Two-Stream Convolutional Networks for action Recognition in Video 视频理解领域的开山之作
Posted 肖屁屁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Two-Stream Convolutional Networks for action Recognition in Video 视频理解领域的开山之作相关的知识,希望对你有一定的参考价值。
Two-Stream Convolutional Networks for action Recognition in Video 视频理解领域的开山之作
作者提出视频理解是一个非常有前景的工作,并且指出输入至网络的一定是视频,而不是网上的一张一张的图片,其实双流网络并不是第一篇将深度学习应用到视频分析中的,早在2014年在些的时候CVPR 有一篇叫做deep video 的文章已经用深度学习用在视频分类中了, 但是训练效果差强人意, 甚至还比手工特征的效果还差.
文章结构:
半页摘要
半页引言
大半页相关工作
具体的方法3页多
1页篇幅实现细节
2页多做实验和展现结果
什么是双流网络?
以往我们如果输入的是图片 就往往是采样多个conv,然后全连接最后来个softmax 最后得到一个输出
那么如果输入的是视频呢?
早期是通过视频中一个一个的把视频帧提取出来,然后通过卷积神经网络然后再把它的结果合并起来,要么就是把这些帧叠起来,当作整体的卷积神经输入,然后再这个网络里进行early fusion,或者late fusion(什么是early fusion 什么是late fusion)达到时空学习的效果,但是这些工作的效果都不太好,那么这个作者就发现,一个神经网络不太好处理这些工作,卷积神经网络擅长处理局部的工作,而不太擅长这个物体运动的规律,所以作者就提前提取出Motion-information (运动信息),那么就只需要这个神经网络学从最开始的输入光流到最后的这个结构映射.所以加上下面的这个光流提取后,动作的精度就大大提升了
在篇论文中,作者提出应用深度学习到视频领域中的重要问题,在于如何能同时学到两种信息,一种是从静止的图像中学习到appearance信息,就是图像啊,大小,颜色,以及整体的信息,另一种是移动的信息,作者提出他们想将之前手工提取的信息代入到深度学习的框架中来 从而既能学到appearance信息,又能学到motion信息,然后作者接下来的总结说:他们的论文的贡献有三点,一个是双流网络,包括空间流和时间流,另一个是他们证实了在即便有少量的训练数据情况下,一个直接在光流训练的神经网络也能够取得很好的效果,第三个为了弥补训练数据上的不足,提出了使用 Multitask learning 的方法,在两个数据集上同时去训练一个骨干网络,这样不仅骨干网络训练的更好,在两个数据集上都能提到一个很好的效果,作者说他们在两个最常见的数据集上UCF-101和Hmdb-51上训练,并且效果比之前的视频理解方法好了一大截
视频领域的进展都是被图像领域的进展,推动这走的,一般都是先有图像上的突破,然后大家把图像上的方法移植,或者扩展过来从处理数据,在图像领域中,大家都用这个shift feature, 在视频领域中大家都是用的这个spatio-temporal features 那么基于手工特征最好的方法其实是另外一种方法:就是用了dense point tragectories 就是利用视频帧点和点前后帧之间的联系来的这种轨迹信息 利用光流然后通过光流找到轨迹信息从而进行抽取特征,效果非常的好,下面就很多的改进工作,比如说文献26
前两段是传统的基于手工特征的学习方法,但他也映射了之后用深度学习去做视频的两个方向,那这种局部的时空学习就演变了3d网络
双流网络结构以及如何使用双流
视频可以自然的拆分成时间部分和空间部分,空间部分就是appearance信息主要用来描述视频中的场景以及物体, 时间部分主要用来描述场景中的motion信息,它主要描述视频中的物体是怎么运动的, 空间流去学习空间特征,时间流去学习运动特征,这样两个网络各司其职互不打扰,最后的结构通过late fusion 合并一下 就能得到最后的结构,
那么怎么合并呢?
要么就进行加权平均, 要么就是在已经得到的softmax分数上把它当成特征在SVM上训练做分类
空间上的视频网络是拿图片 一帧一帧的作为网络输入,实际上是一个图片的分类任务,作者说这种静止的appearance信息是一个非常有用的信息,也就是说很多动作其实就是和对应的物体都是牢牢的联系在一起的,比如说弹钢琴,拉小提琴,踢足球, 只要识别到对应的物体,其实最后的判别也是八九不离十了,作者在实验中也证实了基于静止图像的这种视频分类也非常有竞争力,效果一点也不差,还有把这个空间网络单独抽出来做单帧的图像做输入的另外一个好处就是空间流的网络就可以用ImageNet进行预训练,一般能够在大的数据集上进行预训练,然后在小的这种ucf101上进行微调的话效果就会好很多, 空间流的网络实际上就是Alexnet
5层conv 两层fc 最后一个softmax
接下来作者大篇幅讲解如何构建时间流以及如何构建光流,光流长什么样,作者怎么抽取光流,又是怎么预处理光流,以及最后光流的维度又是多少
在图
a,b中就是一个人射箭 将剑从后背拔像前方 将光流可视化出来后就是图c,
图d 是水平方向上的位移.图e就是数值方向上的位移
假如说这个视频帧宽是240,高是320 图像的输入就是240*320*3 那个这个光流图视频帧的维度大小就是 240*320*2(2就代表光流图上水平上和垂直上的两个维度)
注意:一,图像的输入和最后预测出来的光流的大小是一样,因为每个像素点都有可能移动,不移动那么它的值就是0,因此其实每个点都有值,
二,每两张图得到1个光流,四张连续的帧就有3个光流
如何使用光流
最简单的方式就是将视频的前后两个帧提取出来然后算出一个光流,然后将这个光流放入一个2D的网络,但是就相当于图片的分类了只不过把rgb变成了光流图 这样意义就不大了,
作者想使用多张帧,通向时间流神经网络是多个光流图叠加在一起的,
那怎么叠加更好呢
作者讨论出了两种方式,
一种是将这些连续的光流图直接叠加在一起
另一种是根据光流的轨迹,在轨迹上进行光流数值的叠加
但是通过试验左边的效果反而要比右边这种通过轨迹的合理方法的要好,
Bi-derection双向光流,常见的bert,pyramid,cascade这种操作类似的
光流怎么通过叠加,给网络输入
写了两种方法,第一种对应的就是这种简单粗暴的直接连接
2对应的就是轨迹信息去更精确的连接,但是这两种方式都是只做了前向的光流计算
,但是这个光流反过来计算也是合理的, 当然他也可以从下一帧的位置b,回到位置A,作者也计算了一下, 他既计算了前向光流,又计算了后向光流,为了能和之前的两张方式做公平对比,他也选择了l帧这么长度的视频,但是他把前半段用来去算前向光流,后半段用来去算后向光
流,这样最后得到的光流输入呢,还是2l个channel, 具体呢就是L+1帧,那么我们就得到了L个光流图,那么L个光流图的大小呢 就是w*h*2L的张量大小,然后我们就把这个张量送给时间流的网络, 那如果我们再反过来看图1下面的这个事件流网络,那么它具体的结构和时间流是一样的,都是5层conv和两层f,只不过第一个卷积层的那个输入维度和上面不一样了, 具体来说上面那个输入是3个channel, 而下面这个多帧光流的输入维度 2l 在这篇文章里作者使用11帧作为输入 那么输入时间网络的就是10*2,那么这些光流图是按什么顺序进项叠加的呢,其实它先叠加的是水平部分的位移,然后是垂直部分的位移
一开始是x1,x2,x3,x4---xl然后再试竖着的y1,y2,y3--yl,那这个叠加起来最后的输入,以soft max加权平均为例子,如果我们在做ucf101这个数据集,如果说他有101个类的话,最后这个softmax的分数就是1*101的这个向量,那地下这个时间流的输出呢 也是101*1的这个向量,取平均呢,其实就是把这个两项加起来再除2,最后再做一个argmax操作
Uvf,那个分数最高 那他就是那个类,
从网络上来看它并没有做出很大的改动实际上还是沿用alexnet 然后再稍作改动
实现细节
套路:把之前的网络cnn(AlexNet)换掉
1,如何做测试
就是说无论这个视频有多长, 我都从视频里等间距的抽取25帧,比如说有个视频有2000帧那么就是每隔80帧取出一帧,然后对取出来的这些帧,每一帧都去做这个ten crop ,就是说如果下图黄色部分是你取得帧,那么我们就先取4个边角(红色),当然因为crop有点大,所以这四个部分可能有点大 会有重叠的部分 那么我们在取一个中间, 所以这一张图就变成看5个图
然后呢再把这个图像翻转一下,然后再在翻转的图像下再取四个边角1个中心,最后呢这个一张图就变成了10张图 那么对于25帧来说最后就变成了250个crop,那每张图就会通过一个2D这个空间流的神经网络,得到一个结果,直接就去平均来得到预测
对于光流也是一样的,连续的取11帧,然后这11帧抽的光流,然后把这些光流图送入时间流神经网络,然后取平均 然后当空间流和时间流网络都结束后, 将这两个流在做一个late fusion
把他俩加到一起除以2,得到最终的双流网络的预测
对于光流也是一样的,连续的取11帧,然后这11帧抽的光流,然后把这些光流图送入时间流神经网络,然后取平均 然后当空间流和时间流网络都结束后, 将这两个流在做一个late fusion
把他俩加到一起除以2,得到最终的双流网络的预测
2如何预处理光流,预计如何计算光流
14年提出来,沿用至今,其实是从文献2中实现而来的,而且用的是一个GPU的实现,如果去计算前后帧,一帧之间的光流的话,只需要0.06秒,但是如果视频很大的时候,抽取光流还是一件非常耗时的事情,假设说UCF有1万个视频,每个视频有10秒,每个视频的帧率是30fps,那最后大概就会得到300万帧图片,那么两两进行抽取,就会用到几百个小时,那么当前的数据有几百万个视频,而且视频的长度跟长,那么抽取就会成千上万个小时,那光抽取光流都需要抽一个月,也就是说光预处理光流都得一个多月,这也就是后续的工作一直都在攻击光流的一个点,还有一个问题就是,每一个点都有光流的值,也就是它是一个密集的表示,如果我们要把密集的表示存起来的话,它需要的空间就非常大,那么对于ucf101这样的小数据集来说存下来这些数据就需要.1.5t的硬盘空间 那如果把这些换成了sports one million数据集的话那估计就是pb级的空间了,即便是有这么大的空间,那么训练的时候io 的速度也是提不起来的.
作者提出来一个改动:既然这个文件大,是因为dense的这个特性造成的那么能不能该成sparse呢 ,然后他就想了一个巧妙的办法,那我们为什么不像rgb图像一样,给他们做一个压缩呢,所以作者也把光流的这些值rescale了一下到了[0,255]区间,也都把他们变成了整数,然后直接把这个光流存成了jpeg图片,那这样每个光流的图片其实也就十几kb那样小,那么就直接从之前的1.5TB变成了27GB小了快100倍,这样网络起码可以训练起来了,
但是对于大型数据集来说呢,还是很容易就上TB了,所有后续的工作很多都是改进光流了,要么就是直接舍弃光流,投奔3D网络
实验部分
作者拿了UCF-101来做了试验,拿空间流和时间流做了消融实验,空间流就是说有没有使用预训练网络,作者也说之所以把空间和时间流分开,就是想要空间流使用这个预训练模型带来的好处,那这里我们可以看到,如果从随机初始化开始从头训练的话那个效果大概就只有50%左右,无论这个dropout是0.5还是0.9,但是一旦用了这个预训练网络,一下子这个效果就提升到了70%多,作者在使用预训练网络之后呢,还做了两个消融试验,就是说整个网络做微调呢还是只在最后一层上做微调 之前的主干网络都锁住 之所以会这样考虑,是因为当时的数据集太小了,很容易就会过拟合,设置一个非常大的dropout ratio效果就非常好,如果设置0.5的话就会有点低 有点过拟合,但是对于骨干网络锁住,如果只训练最后一层的话,就不太用担心过拟合的问题,这个时候采用0.5就比较好了,如果采用0.9的的Dropout数据丢失的就太多了,可能效果就不太好, 但后面的工作,随着数据集的增加,用了一些训练的式子后,大家往往选择这种pre-trained+fine-tuning的模式.
对于时间流,作者主要对比了到底是简单粗暴的堆叠好,还是 基于轨迹的堆叠好,这种双向的光流到底有没有用,这里的基线模型呢就是采用一张的光流图,也就相当于这里是做图片分类了,只不过输入是一张光流图,不过这时候它的效果只有74,跟后面的80的效果还差了6个点,然后作者逐渐增加光流图,那么随着光流图的增加效果更好,之所以没有再用更多呢,可能是产生了饱和的情况了,也可能是GPU内存不够了,然后确定了长度只有10的时候呢,作者就对比了一下,到底是简单粗暴的stacking好还是根据轨迹去stack好
那么最后结果是直接堆叠的效果要比轨迹堆叠好一个点,最后用这种双向的光流结构(bi-dir)又再好一点,那么这种bi-directional,或者pyramid,或者cascade这种方式一般都会涨点的,要么涨得很多,要么涨得很少,所以可以放心大胆的使用
做完消融实验就基本上的定下来了空间流和时间流的结构
那么双流网络和之前的网络相比如何? 首先前三种方法最好的就是(Improved dense trajectories)手工特征,效果也是很高,第二种就是通过全局的encoding,比如所fisher vector encoding或者VLAD encoding 去让这个特征更加的具有全局性各更适合做视频 所以这个结果更高一点...
Two-Stream Convolutional Networks for Action Recognition in Videos双流网络论文精读
Two-Stream Convolutional Networks for Action Recognition in Videos双流网络论文精读
论文:Two-Stream Convolutional Networks for Action Recognition in Videos
链接:https://arxiv.org/abs/1406.2199
本文是深度学习应用在视频分类领域的开山之作,双流网络的意思就是使用了两个卷积神经网络,一个是Spatial stream ConvNet,一个是Temporal stream ConvNet。此前的研究者在将卷积神经网络直接应用在视频分类中时,效果并不好。作者认为可能是因为卷积神经网络只能提取局部特征,于是作者使用了视频的光流信息,先提取了一遍视频光流特征,再将其送入卷积神经网络中,这样就取得了不错的效果。
双流卷积神经网络的示意图如下,其中上半部分的空间流卷积神经网络输入是静态的单帧图像,该网络输出动作的分类概率;下半部分的事件流卷积神经网络输入的是optical flow,即视频中的光流信息,最后也是通过softmax输出分类概率,最后这两个分类概率取加权平均值,就能得到最终的预测。
OpenCv提取光流的代码在这里:OpenCV: Optical Flow
那么什么是视频中的光流信息呢,作者给出了如下解释。如下图(a)(b)所示,图中人物在抽出弓箭,图©就是相邻两帧的光流信息,图中的箭头就是人物手臂的运动方向,也就是视频的运动特征。因为光流有x,y两个方向,其中图(d)就表示这个视频x方向的光流特征,图(e)表示视频y方向的光流特征。假设图片长度为320,宽度为240,那么输入的相邻两帧视频维度即为(320,240,3),图©中光流图的维度即为(320,240,2)2代表xy两个方向,最后图(d)(e)的维度就是(320,240,1)。
为了得到整个视频的光流特征,作者认为不能直接把视频的光流图像输入2D卷积神经网络,因为这样还是不能得到视频之间的连续信息。作者想出了以下两种方法得到输入的光流信息,分别是optical flow stacking和trajectory stacking,如下图所示。
optical flow stacking的计算公式如下,
I
τ
(
u
,
v
,
2
k
−
1
)
=
d
τ
+
k
−
1
x
(
u
,
v
)
I
τ
(
u
,
v
,
2
k
)
=
d
τ
+
k
−
1
y
(
u
,
v
)
,
u
=
[
1
;
w
]
,
v
=
[
1
;
h
]
,
k
=
[
1
;
L
]
.
\\beginaligned &I_\\tau(u, v, 2 k-1)=d_\\tau+k-1^x(u, v) \\\\ &I_\\tau(u, v, 2 k)=d_\\tau+k-1^y(u, v), \\quad u=[1 ; w], v=[1 ; h], k=[1 ; L] . \\endaligned
Iτ(u,v,2k−1)=dτ+k−1x(u,v)Iτ(u,v,2k)=dτ+k−1y(u,v),u=[1;w],v=[1;h],k=[1;L].
trajectory stacking的计算公式如下:
I
τ
(
u
,
v
,
2
k
−
1
)
=
d
τ
+
k
−
1
x
(
p
k
)
I
τ
(
u
,
v
,
2
k
)
=
d
τ
+
k
−
1
y
(
p
k
)
,
u
=
[
1
;
w
]
,
v
=
[
1
;
h
]
,
k
=
[
1
;
L
]
.
\\beginaligned &I_\\tau(u, v, 2 k-1)=d_\\tau+k-1^x\\left(\\mathbfp_k\\right) \\\\ &I_\\tau(u, v, 2 k)=d_\\tau+k-1^y\\left(\\mathbfp_k\\right), \\quad u=[1 ; w], v=[1 ; h], k=[1 ; L] . \\endaligned
Iτ(u,v,2k−1)=dτ+k−1x(pk)Iτ(u,v,2k)=dτ+k−1y(pk),u=[1;w],v=[1;h],k=[1;L].
其中
p
k
\\mathbfp_k
pk 是轨迹上的第
k
k
k个点, 从第
τ
\\tau
τ帧的位置
(
u
,
v
)
(u, v)
(u,v)开始,且由以下递归关系定义:
p
1
=
(
u
,
v
)
;
p
k
=
p
k
−
1
+
d
τ
+
k
−
2
(
p
k
−
1
)
,
k
>
1.
\\mathbfp_1=(u, v) ; \\quad \\mathbfp_k=\\mathbfp_k-1+\\mathbfd_\\tau+k-2\\left(\\mathbfp_k-1\\right), k>1 .
p1=(u,v);pk=pk−1+dτ+k−2(pk−1),k>1.
其中,这里的每一帧已经被resize为(224,224)的大小。左边表示视频的每一帧都在相同位置取出光流信息,即每次都在P1的位置上取光流的值,右边这种方式表示沿着光流运动的轨迹来取光流的值,即在第T+1帧时第T帧在P1的点运动到了P2,那么就继续从P2的位置开始取光流信息。最终作者发现左边的方式效果更好一些。
那么光流怎么作为网络输入呢?假设给定L+1帧的一个视频,可以得到L个光流图,这L个光流图可以作为(w,h,2L)这么大的一个张量输入网络。因为每个光流图都有L个x方向的光流和L个y方向的光流,所以2L这个channel的叠加方式就是(x1,x2…,xL,y1,y2,…yL)。再看回图1,作者设计的训练网络中Spatial stream ConNet部分有RGB三个channel,然后作者每个视频从中抽样出11帧图片,可以获得10个光流图,因此Temporal stream ConvNet部分有20个channel。
那么最后的结果是怎么通过双流网络获得的呢?对于空间流卷积网络,作者从每个视频中等间隔抽取25帧图片,然后抽取这个图片的四个边角和中心框,再将图像翻转,重复上述操作。这样一张照片就被增强为了10张照片。由于每个视频从中抽取了25帧,增强过后一个视频就变成了250帧,这250张照片分别输入空间流卷积网络,得到动作是每个类别的概率。最后这250个概率值取平均,就可得到这个视频属于每个类别的总概率。对于时间流卷积网络,作者在上述25帧之后都取了连续的11帧,然后将张量送入卷积网络,再取平均,得到每个类别的平均概率。最后将这两个流的结果做一个late fusion,即结果相加再除以2,得到最终的双流网络预测结果。
我从github上看到一份pytorch复现代码,但是目前还没有尝试运行,链接如下:blacknwhite5/pytorch-two-stream-CNN: Two-Stream Convolutional Networks for Action Recognition in Videos (github.com)
pytorch网络实现细节:
class SpatialNet(nn.Module):
def __init__(self):
super(SpatialNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=7, stride=2),
# nn.batchnorm2d(96),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
nn.LocalResponseNorm(2),
nn.Conv2d(96, 256, kernel_size=5, stride=2),
# nn.batchnorm2d(256),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
nn.LocalResponseNorm(2),
nn.Conv2d(256, 512, kernel_size=3),
nn.ReLU(),
nn.Conv2d(512, 512, kernel_size=3),
nn.ReLU(),
nn.Conv2d(512, 512, kernel_size=3),
# nn.batchnorm2d(512),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(2048, 4096),
nn.Dropout(),
nn.Linear(4096, 2048),
nn.Dropout(),
nn.Linear(2048, 5),
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
class TemporalNet(nn.Module):
def __init__(self):
super(TemporalNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=7, stride=2),
# nn.batchnorm2d(96),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
nn.LocalResponseNorm(2),
nn.Conv2d(96, 256, kernel_size=5, stride=2),
# nn.batchnorm2d(256),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
nn.LocalResponseNorm(2),
nn.Conv2d(256, 512, kernel_size=3),
nn.ReLU(),
nn.Conv2d(512, 512, kernel_size=3),
nn.ReLU(),
nn.Conv2d(512, 512, kernel_size=3),
# nn.batchnorm2d(512),
nn.ReLU(),
nn.MaxPool2d(3, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(2048, 4096),
nn.Dropout(),
nn.Linear(4096, 2048),
nn.Dropout(),
nn.Linear(2048, 5),
)
以上是关于Two-Stream Convolutional Networks for action Recognition in Video 视频理解领域的开山之作的主要内容,如果未能解决你的问题,请参考以下文章
动作识别新论文:A Novel Scheme for Training Two-Stream CNNs for Action Recognition
tensorflow 运行 python convolutional.py时
卷积神经网络用于视觉识别Convolutional Neural Networks for Visual Recognition