Convolutional Two-Stream Network Fusion for Video Action Recognition
本文的目的是运行并分析双流代码,包括环境配置、数据集和模型准备、代码概览。关于论文的内容概括请看另一篇博客:https://blog.csdn.net/u013588351/article/details/102074562
相关链接
简介:http://www.robots.ox.ac.uk/~vgg/software/two_stream_action/
代码:https://github.com/feichtenhofer/twostreamfusion
数据:http://ftp.tugraz.at/pub/feichtenhofer/tsfusion/data/
数据(百度云):https://pan.baidu.com/s/1Veq9a0n2S_2lebbo8OFZUw 提取码: eed7
环境准备
-
编译matconvnet
- 安装C++编译环境
使用Visual Studio编译【推荐】
使用MinGW编译 - 在matlab中配置mex使用C++编译
- 运行compile.m
- 安装C++编译环境
用GPU编译:
- 错误使用 vl_nnconv
An input is not a numeric array (or GPU support not compiled).
解决:将vl_compilenn.m 502行
mopts = {’-outdir’, fileparts(tgt), src, ‘-c’, mex_opts{:}} ;
改为
mopts = {’-outdir’, fileparts(tgt), src, ‘-c’, mex_opts{:}, ‘-largeArrayDims’} ;
用CPU编译:
-
‘cl.exe’ 不是内部或外部命令,也不是可运行的程序或批处理文件。
警告: CL.EXE not found in PATH. Trying to guess out of mex setup.
解决:在Visual Studio安装目录下搜索cl.exe
,将其所在的目录添加到环境变量中,重启Matlab -
错误使用 mex
data.cpp
error C2027: use of undefined type ‘vl::CudaHelper’
note: see declaration of ‘vl::CudaHelper’
error C2228: left of ‘.getLastCudnnErrorMessage’ must have class/struct/union
解决:将原来的compile.m中的vl_compilenn注释,改成编译为CPU版本
-
错误使用 make_all>search_cuda_devkit (line 442)
Could not find a valid NVCC executable\n
解决:将MexConv3D/make_all.m第19行改为opts.enableGpu = false;
运行代码
以运行cnn_ucf101_spatial为例,运行cnn_ucf101_temporal的方法类似。
-
伪装数据集
注释掉三行,再添加两行就好。 -
下载imagenet预训练模型
模型 | 下载地址 | 大小 | 提出时间 |
---|---|---|---|
vgg-m | imagenet-vgg-m-2048.mat | 329M | 2013 |
vgg-16 | imagenet-vgg-verydeep-16.mat | 491M | 2014 |
res-50 | imagenet-resnet-50-dag.mat | 91.5M | 2015 |
res101 | imagenet-resnet-101-dag.mat | 159M | 2015 |
res152 | imagenet-resnet-152-dag.mat | 215M | 2015 |
默认是res-50,下载完成后放到models目录下,并对应地修改model的值:
一定要用上面的链接下载,不要下载官网上最新版的模型,否则后面运行到net = dagnn.DagNN.loadobj(net)
时会报错:
类dagnn.Conv不存在公共字段dilate
正常情况下,这个时候就可以运行cnn_ucf101_spatial了,只是不能训练。
使用F12添加断点,F5调试,F10单步执行。在左下角工作区可以看到变量值,下方命令行窗口可以在调试过程中实时执行命令。
- 下载时空预训练网络
如果要运行融合网络cnn_ucf101_fusion,就要先下载预训练好的时空网络。res50、res152、vgg16里选一个,最好下vgg16,不然别的模型的层的名字不一样,后面代码要改很多地方。下载好之后放到models目录下,在代码中修改对应地文件名即可:
opts.modelA = fullfile(opts.modelPath, [opts.dataSet '-img-vgg16-split' num2str(opts.nSplit) '.mat']) ;
opts.modelB = fullfile(opts.modelPath, [opts.dataSet '-TVL1flow-vgg16-split' num2str(opts.nSplit) '.mat']) ;
代码阅读
依赖关系
目录结构
程序的入口文件:
cnn_ucf101_spatial:训练空间网络
cnn_ucf101_temporal:训练时间网络
cnn_ucf101_fusion:使用已训练好的模型,并训练最终的融合网络
获取数据的三个文件:
cnn_ucf101_get_frame_batch:获取RGB单帧图像
cnn_ucf101_get_flow_batch:获取光流图像
cnn_ucf101_get_im_flow_batch:获取RGB图像+光流图像
其他文件:
cnn_setup_environment:初始化环境变量
cnn_ucf101_setup_data:初始化数据
cnn_train_dag:训练CNN的,跟具体模型无关
compile:编译matconvnet库
cnn_ucf101_spatial
空间网络实际上就是一个标准的CNN,输入为224x224的RGB图像,输出为UCF-101数据集上的分类,因此其输入维度为[224 224 3],输出维度为[101 1]。中间的网络架构可以使用通用的vgg-m,vgg16,res50,res101,res152等。
文章使用在imagenet上预训练好的网络,这样有几个好处:(1)网络结构的有效性经过大量实验验证,(2)经过预训练后中间的隐藏层已经具有一定提取复杂图像特征的能力,(3)中间的隐藏层的结构不用再定义啦,载入现成模型就好。
将预训练过的网络结构按照实验需要做轻微的修改即可得到空间网络,为了确保输入和输出和实验数据一致,需要修改(1)输入层的权重和偏置(2)最后一个全连接层的权重和偏置;其次为了训练需要(3)设置自己的目标函数loss和derOutputs;最后为了实验,需要添加(4)dropout、top1error和top5error。此外还需要设置numEpochs、epochFactor、learningRate、batchSize等参数。
关于DagNN和SimpleNN:
官方文档:DagNN
官方文档:SimpleNN
这篇文章说了一下DagNN和SimpleNN的一些区别
net
一开始可能是DagNN类型,也可能是SimpleNN类型。如果是SimpleNN类型,会通过dagnn.DagNN.fromSimpleNN(net)
转换成DagNN,最后通过cnn_train_dag
来训练
主要参数
opts.dataSet = 'ucf101';
opts.dropOutRatio = 0 ;
opts.inputdim = [ 224, 224, 3] ;
opts.train.batchSize = 256 ;
opts.train.augmentation = 'borders25';
opts.train.learningRate = [1e-2*ones(1, 2) 1e-2*ones(1, 3) 1e-3*ones(1, 3) 1e-4*ones(1, 3)] ;
imdb = load(opts.imdbPath) ; % 图像数据集
nClasses = length(imdb.classes.name); % 数据集分类数
net = load(opts.model); % 预训练过的网络
(1)输入层
这一步无需修改,因为预训练好的模型的数据就是[224 224 3],和网络一致。
(2)最后一个全连接层
修改最后一个全连接层的两个参数
- fc_filter维度从 [1 1 2048 1000] 变为 [1 1 2048 101] 并随机初始化
- fc_bias维度从 [1000 1] 变为 [101 1] 并初始化为0
% imagenet有1000个分类,所以分类层输出是一个(1000, 1)的向量
% 修改最后一个全连接层的权重和偏置的维度,使得输出维度为(101, 1)
% replace 1000-way imagenet classifiers
for p = 1 : numel(net.params)
sz = size(net.params(p).value);
disp(sz);
if any(sz == 1000)
sz(sz == 1000) = nClasses;
fprintf('replace classifier layer of %s\n', net.params(p).name);
% 将fc1000_filter维度从[1 1 2048 1000]改为[1 1 2048 101]
if numel(sz) > 2
net.params(p).value = 0.01 * randn(sz, class(net.params(p).value));
% 将fc1000_bias维度从[1000 1]改为[101 1]
else
net.params(p).value = zeros(sz, class(net.params(p).value));
end
end
end
% 设置normalization.border = [32 32]
net.meta.normalization.border = [256 256] - net.meta.normalization.imageSize(1:2);
net = dagnn.DagNN.loadobj(net);
if strfind(model, 'bnorm')
net = insert_bnorm_layers(net) ;
end
(3)设置loss和derOutputs
% 移除Softmax层,添加Loss层,并将Loss设置为derOutputs用于反向传播
opts.train.derOutputs = {
} ;
for l=numel(