この論文のコードは当初 Caffe バージョンでのみ利用可能でしたが、pytorch バージョンがここに添付されています。
JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorch: アクション認識のためのオプティカル フロー ガイド付き機能-Pytorch (github.com) https://github.com/JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorchは、いくつかの理由により必要です本稿で紹介したコードのOFF部分を分解していきますので、まずは論文の該当部分を取り上げてから解説していきますが、レベルが限られており、表現が不正確な点もありますが、ご容赦ください。
ペーパーポータル:
オプティカル フロー ガイド付き機能: ビデオ アクション認識のための高速かつ堅牢なモーション表現 (thecvf.com) https://openaccess.thecvf.com/content_cvpr_2018/papers/Sun_Optical_Flow_Guided_CVPR_2018_paper.pdf従来の CNN の場合、ビデオから特徴を抽出する場合はより困難ですなぜなら、ビデオは画像とは異なり、空間的特徴と時間的特徴がビデオをよりよく表しているからです。この論文は TSN のネットワーク構造に基づいています. これに基づいて, 著者は時間次元の特徴を抽出するための OFF ユニットを設計しました. ネットワークの全体的な構造に興味がある場合は, 論文を確認してください. ここでは紹介するだけです. OFFユニット部の構造とコード。
まず、ネットワーク全体の構造における OFF ユニットの位置を見てみましょう。
図1 紙のネットワーク構造
上図では、異なる時間帯の特徴を抽出する特徴抽出用のサブネットワークが2つあり、OFFユニットで構成されるOFFサブネットワークは、上記2つのサブネットワークを通じて時間情報を抽出し、最終的にそれぞれのクラススコアの融合により分類を行っています。サブネットワーク。次に、OFF ユニットの構造図を詳しく見てみましょう。
図2 OFFユニットの構造
論文では、Sobel 演算子と Subtract の組み合わせを OFF と呼び、前の 1*1 畳み込みと合わせて OFF 層を形成します。特徴は OFF ユニットを通じて 2 回畳み込まれます。1 つのブランチでは Sobel 演算子を使用して空間特徴を抽出し、要素ごとの減算 (Subtract) 演算を使用して時間情報を抽出します。図 1 と組み合わせると、OFF ユニットから得られる情報が得られます。ユニットは ReseNet を通じて次のモジュールに入ります。
OFF モジュールの基本的なコードがコードに反映されています. OFF をクラスとして個別に実装するわけではありません. 論文には複数の OFF が含まれています. いくつかの OFF は畳み込みを実行する際の入力チャンネル数とサイズが異なります. ここでは例として motion_3a を取り上げます.他のネットワーク構造を結合する方法については、内部の形状をさらに計算して設計する必要があります。そうしないと、寸法が一致しないなどの問題が発生します。ここでは、OFF はクラスとして実装されています。関連するコードは次のとおりです。修正してください。間違いがある場合は私に連絡してください。
from __future__ import print_function, division, absolute_import
import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import os
import sys
from torch.autograd import Variable
from util import SobelFilter, SobelFilter_Diagonal
from basic_ops import *
import pdb
class OFFNet(nn.Module):
def __init__(self, batch, length, in_channels, h, w):
super(OFFNet, self).__init__()
self.batch = batch
self.length = length
self.motion_conv_gen = nn.Conv2d(in_channels[0], 128, kernel_size=(1, 1), stride=(1,1))
self.motion_spatial_down = nn.Conv2d(in_channels[1], 32, kernel_size=(1,1), stride=(1,1))
self.motion_spatial_grad = nn.Conv2d(in_channels[2], 32, kernel_size=(3,3), stride=(1,1), padding=(1,1), groups=32, bias=True)
self.motion_relu_gen = nn.ReLU()
self.dropout = nn.Dropout(p=0.8)
def forward(self, x):
# print(x.shape)
# motion operating on [batch * length, c, h, w] level
# motion_conv_gen = self.motion_conv_gen(x)
motion_conv_gen = self.motion_conv_gen(x)
motion_relu_gen = self.motion_relu_gen(motion_conv_gen)
channel_size = motion_relu_gen.shape[1] #
reshape_rgb_frames = motion_relu_gen.view(self.batch, -1, self.h, self.w)
# print(reshape_rgb_frames.shape)
last_frames = reshape_rgb_frames[:, channel_size:, :, :]
# print(last_frames.shape)
first_frames = reshape_rgb_frames[:, :-channel_size, :, :]
# print(first_frames.shape)
eltwise_motion = last_frames - first_frames
# print(eltwise_motion.shape)
# convert back to [batch * (time - 1), c, h, w]
temporal_grad = eltwise_motion.view(-1, channel_size, self.h, self.w)
spatial_frames = x[:self.batch * (self.length - 1), :, :, :]
# downgrade dimension to 32
spatial_down = self.motion_spatial_down(spatial_frames)
spatial_grad = self.motion_spatial_grad(spatial_down)
spatial_grad = self.dropout(spatial_grad)
# print(spatial_grad.shape)
# concatenate temporal and spatial dimension
# import pdb;pdb.set_trace()
# print(spatial_grad.shape)
# print(temporal_grad.shape)
motion = torch.cat((spatial_grad, temporal_grad), dim=1)
return motion
#in_channels[motion_3a:[256,256,32],
# motion_3b:[320,320,32],
# motion_3c:[576,576,32],
# motion_4a:[576,576,32],
# motion_4b:[576,576,32],
# motion_4c:[608,608,32],
# motion_4d:[608,608,32],
# motion_5a:[1024,1024,32],
# motion_5b:[1024,1024,32]
# ]
]
コード内にコメントがあり、OFF層の構造に対応して読み取れますが、論文では畳み込みにOFFを使用した場合の入力チャンネル数が一定ではないため、ここでは入力チャンネル数をパラメータとして渡していますモジュールを他のモジュールに追加しやすくするためです. この論文では出力チャネル数は同じであり、それ以上の処理は実行されません. パラメータとして変更することもできます. 一番下は、すべての OFF 操作に対する入力チャネル数ですこの紙。sobelオペレーター等については、さらに詳しく知りたい場合はご自身で関連情報をご確認ください。