摘要:本案例将为大家介绍视频动作识别领域的经典模型并进行代码实践。
本文分享自华为云社区《视频动作识别》,作者:HWCloudAI。实验目标
通过本案例的学习:
- 掌握 C3D 模型训练和模型推理、I3D 模型推理的方法;
注意事项
- 本案例推荐使用TensorFlow-1.13.1,需使用 GPU 运行,请查看《ModelArts JupyterLab 硬件规格使用指南》了解切换硬件规格的方法;
- 如果您是第一次使用 JupyterLab,请查看《ModelArts JupyterLab使用指导》了解使用方法;
- 如果您在使用 JupyterLab 过程中碰到报错,请参考《ModelArts JupyterLab常见问题解决办法》尝试解决问题。
实验步骤
案例内容介绍
视频动作识别是指对一小段视频中的内容进行分析,判断视频中的人物做了哪种动作。视频动作识别与图像领域的图像识别,既有联系又有区别,图像识别是对一张静态图片进行识别,而视频动作识别不仅要考察每张图片的静态内容,还要考察不同图片静态内容之间的时空关系。比如一个人扶着一扇半开的门,仅凭这一张图片无法判断该动作是开门动作还是关门动作。
视频分析领域的研究相比较图像分析领域的研究,发展时间更短,也更有难度。视频分析模型完成的难点首先在于,需要强大的计算资源来完成视频的分析。视频要拆解成为图像进行分析,导致模型的数据量十分庞大。视频内容有很重要的考虑因素是动作的时间顺序,需要将视频转换成的图像通过时间关系联系起来,做出判断,所以模型需要考虑时序因素,加入时间维度之后参数也会大量增加。
得益于 PASCAL VOC、ImageNet、MS COCO 等数据集的公开,图像领域产生了很多的经典模型,那么在视频分析领域有没有什么经典的模型呢?答案是有的,本案例将为大家介绍视频动作识别领域的经典模型并进行代码实践。
1. 准备源代码和数据
这一步准备案例所需的源代码和数据,相关资源已经保存在 OBS 中,我们通过ModelArts SDK将资源下载到本地,并解压到当前目录下。解压后,当前目录包含 data、dataset_subset 和其他目录文件,分别是预训练参数文件、数据集和代码文件等。
上一节课我们已经介绍了视频动作识别有 HMDB51、UCF-101 和 Kinetics 三个常用的数据集,本案例选用了 UCF-101 数据集的部分子集作为演示用数据集,接下来,我们播放一段 UCF-101 中的视频:
2. 视频动作识别模型介绍
在图像领域中,ImageNet 作为一个大型图像识别数据集,自 2010 年开始,使用此数据集训练出的图像算法层出不穷,深度学习模型经历了从 AlexNet 到 VGG-16 再到更加复杂的结构,模型的表现也越来越好。在识别千种类别的图片时,错误率表现如下:
在图像识别中表现很好的模型,可以在图像领域的其他任务中继续使用,通过复用模型中部分层的参数,就可以提升模型的训练效果。有了基于 ImageNet 模型的图像模型,很多模型和任务都有了更好的训练基础,比如说物体检测、实例分割、人脸检测、人脸识别等。
那么训练效果显著的图像模型是否可以用于视频模型的训练呢?答案是 yes,有研究证明,在视频领域,如果能够复用图像模型结构,甚至参数,将对视频模型的训练有很大帮助。但是怎样才能复用上图像模型的结构呢?首先需要知道视频分类与图像分类的不同,如果将视频视作是图像的集合,每一个帧将作为一个图像,视频分类任务除了要考虑到图像中的表现,也要考虑图像间的时空关系,才可以对视频动作进行分类。
为了捕获图像间的时空关系,论文 I3D 介绍了三种旧的视频分类模型,并提出了一种更有效的 Two-Stream Inflated 3D ConvNets(简称 I3D)的模型,下面将逐一简介这四种模型,更多细节信息请查看原论文。
旧模型一:卷积网络 + LSTM
模型使用了训练成熟的图像模型,通过卷积网络,对每一帧图像进行特征提取、池化和预测,最后在模型的末端加一个 LSTM 层(长短期记忆网络),如下图所示,这样就可以使模型能够考虑时间性结构,将上下文特征联系起来,做出动作判断。这种模型的缺点是只能捕获较大的工作,对小动作的识别效果较差,而且由于视频中的每一帧图像都要经过网络的计算,所以训练时间很长。
旧模型二:3D 卷积网络
3D 卷积类似于 2D 卷积,将时序信息加入卷积操作。虽然这是一种看起来更加自然的视频处理方式,但是由于卷积核维度增加,参数的数量也增加了,模型的训练变得更加困难。这种模型没有对图像模型进行复用,而是直接将视频数据传入 3D 卷积网络进行训练。
旧模型三:Two-Stream 网络
Two-Stream 网络的两个流分别为 1 张 RGB 快照和 10 张计算之后的光流帧画面组成的栈。两个流都通过 ImageNet 预训练好的图像卷积网络,光流部分可以分为竖直和水平两个通道,所以是普通图片输入的 2 倍,模型在训练和测试中表现都十分出色。
光流视频 optical flow video
上面讲到了光流,在此对光流做一下介绍。光流是什么呢?名字很专业,感觉很陌生,但实际上这种视觉现象我们每天都在经历,我们坐高铁的时候,可以看到窗外的景物都在快速往后退,开得越快,就感受到外面的景物就是 “刷” 地一个残影,这种视觉上目标的运动方向和速度就是光流。光流从概念上讲,是对物体运动的观察,通过找到相邻帧之间的相关性来判断帧之间的对应关系,计算出相邻帧画面中物体的运动信息,获取像素运动的瞬时速度。在原始视频中,有运动部分和静止的背景部分,我们通常需要判断的只是视频中运动部分的状态,而光流就是通过计算得到了视频中运动部分的运动信息。
下面是一个经过计算后的原视频及光流视频。
原视频
光流视频
新模型:Two-Stream Inflated 3D ConvNets
新模型采取了以下几点结构改进:
- 拓展 2D 卷积为 3D。直接利用成熟的图像分类模型,只不过将网络中二维 $ N × N 的 filters 和 pooling kernels 直接变成的 filters 和 poolingkernels 直接变成 N × N × N $;
- 用 2D filter 的预训练参数来初始化 3D filter 的参数。上一步已经利用了图像分类模型的网络,这一步的目的是能利用上网络的预训练参数,直接将 2D filter 的参数直接沿着第三个时间维度进行复制 N 次,最后将所有参数值再除以 N;
- 调整感受野的形状和大小。新模型改造了图像分类模型 Inception-v1 的结构,前两个 max-pooling 层改成使用 $ 1 × 3 × 3 kernels and stride 1 in time,其他所有 max-pooling 层都仍然使用对此的 kernel 和 stride,最后一个 average pooling 层使用 kernelsandstride1intime,其他所有 max−pooling 层都仍然使用对此的 kernel 和 stride,最后一个 averagepooling 层使用 2 × 7 × 7 $ 的 kernel。
- 延续了 Two-Stream 的基本方法。用双流结构来捕获图片之间的时空关系仍然是有效的。
最后新模型的整体结构如下图所示:
好,到目前为止,我们已经讲解了视频动作识别的经典数据集和经典模型,下面我们通过代码来实践地跑一跑其中的两个模型:C3D 模型( 3D 卷积网络)以及 I3D 模型(Two-Stream Inflated 3D ConvNets)。
C3D 模型结构
我们已经在前面的 “旧模型二:3D 卷积网络” 中讲解到 3D 卷积网络是一种看起来比较自然的处理视频的网络,虽然它有效果不够好,计算量也大的特点,但它的结构很简单,可以构造一个很简单的网络就可以实现视频动作识别,如下图所示是 3D 卷积的示意图:
a) 中,一张图片进行了 2D 卷积, b) 中,对视频进行 2D 卷积,将多个帧视作多个通道, c) 中,对视频进行 3D 卷积,将时序信息加入输入信号中。
ab 中,output 都是一张二维特征图,所以无论是输入是否有时间信息,输出都是一张二维的特征图,2D 卷积失去了时序信息。只有 3D 卷积在输出时,保留了时序信息。2D 和 3D 池化操作同样有这样的问题。
如下图所示是一种 C3D 网络的变种:(如需阅读原文描述,请查看 I3D 论文 2.2 节)
C3D 结构,包括 8 个卷积层,5 个最大池化层以及 2 个全连接层,最后是 softmax 输出层。
所有的 3D 卷积核为 $ 3 × 3 × 3$ 步长为 1,使用 SGD,初始学习率为 0.003,每 150k 个迭代,除以 2。优化在 1.9M 个迭代的时候结束,大约 13epoch。
数据处理时,视频抽帧定义大小为:$ c × l × h × w,,c 为通道数量,为通道数量,l 为帧的数量,为帧的数量,h 为帧画面的高度,为帧画面的高度,w 为帧画面的宽度。3D 卷积核和池化核的大小为为帧画面的宽度。3D 卷积核和池化核的大小为 d × k × k,,d 是核的时间深度,是核的时间深度,k 是核的空间大小。网络的输入为视频的抽帧,预测出的是类别标签。所有的视频帧画面都调整大小为是核的空间大小。网络的输入为视频的抽帧,预测出的是类别标签。所有的视频帧画面都调整大小为 128 × 171 $,几乎将 UCF-101 数据集中的帧调整为一半大小。视频被分为不重复的 16 帧画面,这些画面将作为模型网络的输入。最后对帧画面的大小进行裁剪,输入的数据为 $16 × 112 × 112 $
3.C3D 模型训练
接下来,我们将对 C3D 模型进行训练,训练过程分为:数据预处理以及模型训练。在此次训练中,我们使用的数据集为 UCF-101,由于 C3D 模型的输入是视频的每帧图片,因此我们需要对数据集的视频进行抽帧,也就是将视频转换为图片,然后将图片数据传入模型之中,进行训练。
在本案例中,我们随机抽取了 UCF-101 数据集的一部分进行训练的演示,感兴趣的同学可以下载完整的 UCF-101 数据集进行训练。
UCF-101 下载
数据集存储在目录 dataset_subset 下
如下代码是使用 cv2 库进行视频文件到图片文件的转换
此时,视频逐帧转换成的图片数据已经存储起来,为模型训练做准备。
4. 模型训练
首先,我们构建模型结构。
C3D 模型结构我们之前已经介绍过,这里我们通过 keras 提供的 Conv3D,MaxPool3D,ZeroPadding3D 等函数进行模型的搭建。
通过 keras 提供的 summary () 方法,打印模型结构。可以看到模型的层构建以及各层的输入输出情况。
此处输出较长,省略
通过 keras 的 input 方法可以查看模型的输入形状,shape 分别为 (batch size, width, height, frames, channels) 。
可以看到模型的数据处理的维度与图像处理模型有一些差别,多了 frames 维度,体现出时序关系在视频分析中的影响。
接下来,我们开始将图片文件转为训练需要的数据形式。
参数定义
数据集划分,随机抽取 4/5 作为训练集,其余为验证集。将文件信息分别存储在 train_list 和 test_list 中,为训练做准备。
此处输出较长,省略
接下来开始进行模型的训练。
首先定义数据读取方法。方法 process_data 中读取一个 batch 的数据,包含 16 帧的图片信息的数据,以及数据的标注信息。在读取图片数据时,对图片进行随机裁剪和翻转操作以完成数据增广。
定义 data generator, 将数据批次传入训练函数中。
定义方法 preprocess, 对函数的输入数据进行图像的标准化处理。
5. 模型测试
接下来我们将训练之后得到的模型进行测试。随机在 UCF-101 中选择一个视频文件作为测试数据,然后对视频进行取帧,每 16 帧画面传入模型进行一次动作预测,并且将动作预测以及预测百分比打印在画面中并进行视频播放。
首先,引入相关的库。
构建模型结构并且加载权重。
定义函数 arrayshow,进行图片变量的编码格式转换。
进行视频的预处理以及预测,将预测结果打印到画面中,最后进行播放。
6.I3D 模型
在之前我们简单介绍了 I3D 模型,I3D 官方 github 库提供了在 Kinetics 上预训练的模型和预测代码,接下来我们将体验 I3D 模型如何对视频进行预测。
首先,引入相关的包
进行参数的定义
定义参数:
- imagenet_pretrained :如果为 True,则调用预训练权重,如果为 False,则调用 ImageNet 转成的权重
构建 RGB 部分模型
构建光流部分模型
将模型联合,成为完整的 I3D 模型
开始模型预测,获得视频动作预测结果。
预测数据为开篇提供的 RGB 和光流数据: