此示例显示如何通过组合预先训练的图像分类模型和LSTM网络来创建用于视频分类的网络。
创建视频分类深度学习网络:
使用预先训练的卷积神经网络(如GoogLeNet)将视频转换为特征向量序列,从每一帧中提取特征。
在序列上训练LSTM网络来预测视频标签。
组装一个网络,通过合并来自两个网络的层直接对视频进行分类。
下图说明了网络架构。
要向网络输入图像序列,使用序列输入层。
使用卷积层提取特征,即对视频的每一帧单独进行卷积操作,先使用序列折叠层,再使用卷积层。
为了恢复序列结构并将输出重塑为向量序列,请使用序列展开层和平坦层。
为了对所得到的矢量序列进行分类,包括LSTM层,然后包括输出层。
要将视频帧转换为特征向量,请使用备用网络的激活。
加载一个预先训练的GoogLeNet模型使用googlenet
功能。此功能需要深度学习工具箱™模型对于Googlenet网络万博1manbetx支持包。如果没有安装此支万博1manbetx持包,则该函数将提供下载链接。
netCNN = googlenet;
下载HMBD51数据集从HMDB:一个大型的人体运动数据库并将RAR文件解压缩到一个名为“hmdb51_org”
.该数据集包含约2gb的视频数据,超过51类7000个片段,例如“喝”
那“跑”
,“shake_hands”
.
提取RAR文件后,使用支持函数万博1manbetxhmdb51Files
获取文件名和视频的标签。
dataFolder =“hmdb51_org”;[文件,标签] = hmdb51files(datafolder);
阅读第一个视频使用readVideo
帮助函数,在此示例结束时定义,并查看视频的大小。视频是一个H——- - - - - -W.——- - - - - -C——- - - - - -S.数组,H那W.那C,S.分别为视频的高度、宽度、频道数和帧数。
idx = 1;文件名=文件(idx);视频= readVideo(文件名);大小(视频)
ans =.1×4240 320 3 409
查看对应的标签。
标签(idx)
ans =.分类brush_hair
若要查看视频,请使用implay
功能(需要图像处理工具箱™)。此函数预计在范围[0,1]中的数据,因此您必须首先将数据划分为255.或者,您可以循环各个帧并使用imshow
功能。
numFrames =大小(视频、4);数字为了i = 1:numframes frame =视频(:,::,i);imshow(框架/ 255);drawn结尾
利用卷积网络作为特征提取器,在输入视频帧时获取激活。将视频转换为特征向量序列,其中特征向量为的输出激活
在GoogLenet网络的最后一个池池(“pool5-7x7_s1”
)。
该图说明了通过网络的数据流。
要读取视频数据并调整其大小以匹配GoogLeNet网络的输入大小,请使用readVideo
和centerCrop
辅助函数,在本例的最后定义。这个步骤可能需要很长时间来运行。将视频转换为序列后,将序列保存在mat -文件中tempdir
文件夹中。如果MAT文件已经存在,则从MAT文件加载序列而不重新转换它们。
inputSize = netCNN.Layers (1) .InputSize (1:2);layerName =“pool5-7x7_s1”;tempFile = fullfile (tempdir,“hmdb51_org.mat”);如果存在(tempFile“文件”)负载(tempFile“序列”)别的numFiles =元素个数(文件);序列=细胞(numFiles, 1);为了i = 1:numFiles fprintf("读取文件%d…\n",i,numfiles)视频= readvideo(文件(i));视频= centerCrop(视频、inputSize);序列{i,1} =激活(netcnn,视频,layername,“OutputAs”那'列');结尾保存(tempFile,“序列”那“-v7.3”);结尾
查看前几个序列的大小。每个序列都是D.——- - - - - -S.数组,D.是特征的数量(池化层的输出大小)和S.是视频的帧数。
序列(1:10)
ans =.10×1单元阵列{1024×409单}{1024×395单}{1024×323单}{1024×246单}{1024×159单}{1024×137单}{1024×359单}{1024×191单}{1024×439单}{1024×528单}
通过将数据划分为训练和验证分区并删除任何长序列,为训练准备数据。
创建培训和验证分区
分区数据。将90%的数据分配给训练分区和10%的验证分区。
numobservations = numel(序列);idx = randperm(numobservations);n =楼层(0.9 * numobservations);idxtrain = IDX(1:n);Sequencestain =序列(Idxtrain);LabelStrain =标签(IDxtrain);IDXValidation = IDX(n + 1:结束);semencesValidation =序列(IDxValidation);LabelSvalidation =标签(IDXValidation);
删除长序列
网络中较典型序列长得多的序列会在训练过程中引入大量的填充。填充过多会对分类精度产生负面影响。
获取训练数据的序列长度,并将其可视化为训练数据的直方图。
numObservationsTrain =元素个数(sequencesTrain);numObservationsTrain sequenceLengths = 0 (1);为了i = 1:numObservationsTrain sequence = sequencesTrain{i};sequenceLengths (i) =(序列,2)大小;结尾图直方图(sequenceLengths)标题(“序列长度”)包含(“序列长度”) ylabel (“频率”)
只有少数序列有超过400个时间步。为了提高分类精度,去除时间步长超过400步的训练序列及其相应的标签。
最大长度= 400;idx = sequenceLengths > maxLength;sequencesTrain (idx) = [];labelsTrain (idx) = [];
接下来,创建一个LSTM网络,可以对代表视频的特征向量序列进行分类。
定义LSTM网络架构。指定以下网络图层。
序列输入层,其输入大小与特征向量的特征维数相对应
一个包含2000个隐藏单元的BiLSTM层,然后是一个dropout层。方法只输出每个序列的一个标签“OutputMode”
选项BiLSTM层'最后的'
一种输出大小与类数对应的全连接层,一种软最大层和一种分类层。
numFeatures =大小(sequencesTrain {1}, 1);numClasses =元素个数(类别(labelsTrain));[sequenceInputLayer(numFeatures,'姓名'那'序列') bilstmLayer (2000,“OutputMode”那'最后的'那'姓名'那“bilstm”) dropoutLayer (0.5,'姓名'那“下降”) fullyConnectedLayer (numClasses'姓名'那“俱乐部”)softmaxlayer('姓名'那“softmax”) classificationLayer ('姓名'那“分类”));
属性指定培训选项trainingOptions
功能。
设置小批量16,初始学习率为0.0001,梯度阈值为2(防止梯度爆炸)。
每纪元播放数据。
每次时代验证网络一次。
在绘图中显示训练进度并抑制冗长的输出。
miniBatchSize = 16;numObservations =元素个数(sequencesTrain);numIterationsPerEpoch = floor(nummobations / miniBatchSize);选择= trainingOptions (“亚当”那...“MiniBatchSize”miniBatchSize,...'italllearnrate'1的军医,...'gradientthreshold'2,...“洗牌”那“every-epoch”那...“ValidationData”{sequencesValidation, labelsValidation},...“ValidationFrequency”numIterationsPerEpoch,...'plots'那“训练进步”那...“详细”、假);
训练网络使用Trainnetwork.
功能。这可能需要很长时间才能运行。
[netLSTM,信息]= trainNetwork (sequencesTrain、labelsTrain层,选择);
计算网络在验证集上的分类精度。使用与培训选项相同的小批量。
ypred = classify(netlstm,semencesvalidation,“MiniBatchSize”, miniBatchSize);YValidation = labelsValidation;精度=平均值(YPred == YValidation)
精度= 0.6647
要创建直接对视频进行分类的网络,请使用来自所有创建的网络的图层组装网络。使用来自卷积网络的图层将视频转换为矢量序列和从LSTM网络的层进行分类矢量序列。
下图说明了网络架构。
要向网络输入图像序列,使用序列输入层。
使用卷积层提取特征,即对视频的每一帧单独进行卷积操作,先使用序列折叠层,再使用卷积层。
为了恢复序列结构并将输出重塑为向量序列,请使用序列展开层和平坦层。
为了对所得到的矢量序列进行分类,包括LSTM层,然后包括输出层。
添加卷积层
首先,创建一个GoogLeNet网络的层图。
cnnlayers = layergraph(netcnn);
删除输入层(“数据”
)和用于激活的池化层之后的层(“pool5-drop_7x7_s1”
那“loss3-classifier”
那“概率”
,“输出”
)。
layerNames = [“数据”“pool5-drop_7x7_s1”“loss3-classifier”“概率”“输出”];cnnLayers = removeLayers (cnnLayers layerNames);
添加序列输入层
创建一个序列输入层,接受包含与Googlenet网络相同的输入大小的图像的图像序列。使用与Googlenet网络相同的平均图像标准化图像,设置“归一化”
序列输入层的选项“zerocenter”
和“的意思是”
选项到Googlenet的输入层的平均图像。
inputSize = netCNN.Layers (1) .InputSize (1:2);averageImage = netCNN.Layers (1) .Mean;inputLayer = sequenceInputLayer([inputSize 3],...“归一化”那“zerocenter”那...“的意思是”averageImage,...'姓名'那'输入');
将序列输入层添加到层图中。为了将卷积层独立应用于序列图像,在序列输入层和卷积层之间加入序列折叠层,去除图像序列的序列结构。将序列折叠层的输出连接到第一卷积层的输入(“conv1-7x7_s2”
)。
[inputLayer sequenceFoldingLayer('姓名'那'折叠'));lgraph = addLayers (cnnLayers层);Lgraph = ConnectLayers(LAPHAGE,“展开”那“conv1-7x7_s2”);
添加LSTM层
通过删除LSTM网络的序列输入层将LSTM层添加到图层图。为了恢复序列折叠层去除的序列结构,包括在卷积层之后的序列展开层。LSTM层预计载体序列。为了将序列展开层的输出重塑为向量序列,在序列展开层之后包括扁平层。
从LSTM网络中取出各层,去掉序列输入层。
lstmLayers = netLSTM.Layers;lstmLayers (1) = [];
将序列折叠层、flatten层、LSTM层添加到层图中。连接最后一个卷积层(“pool5-7x7_s1”
)输入序列展开层(“展开/”)
.
= [sequenceUnfoldingLayer('姓名'那“展开”) flattenLayer ('姓名'那'扁平') lstmLayers);lgraph = addLayers (lgraph层);Lgraph = ConnectLayers(LAPHAGE,“pool5-7x7_s1”那“展开/在”);
若要使展开层恢复序列结构,请连接“迷你atchsize”
序列折叠层的输出到序列展开层的相应输入。
Lgraph = ConnectLayers(LAPHAGE,“折/ miniBatchSize”那“展开/ miniBatchSize”);
组装网络
检查网络是否有效使用analyzeNetwork
功能。
analyzeNetwork (lgraph)
将网络组装起来,以便可以使用assembleNetwork
功能。
net = assemblenetwork(lgraph)
net = DAGNetwork with properties: Layers: [148×1 net.cnn.layer. layer]连接:[175×2 table]
阅读并中间裁剪视频“pushup.mp4”
使用与之前相同的步骤。
filename =.“pushup.mp4”;视频= readVideo(文件名);
若要查看视频,请使用implay
函数(需要图像处理工具箱)。此函数预计在范围[0,1]中的数据,因此您必须首先将数据划分为255.或者,您可以循环各个帧并使用imshow
功能。
numFrames =大小(视频、4);数字为了i = 1:numframes frame =视频(:,::,i);imshow(框架/ 255);drawn结尾
使用组合网络对视频进行分类。这分类
函数需要一个包含输入视频的单元格数组,因此必须输入一个包含视频的1 × 1单元格数组。
视频= centerCrop(视频、inputSize);{视频}YPred =分类(净)
Ypred =分类俯卧撑
这readVideo
函数读取视频文件名
并返回A.H
——- - - - - -W.
——- - - - - -C-
由- - - - - -S.
数组,H
那W.
那C
,S.
分别为视频的高度、宽度、频道数和帧数。
函数视频= readVideo(filename) vr = VideoReader(filename);H = vr.Height;W = vr.Width;C = 3;%预分配视频阵列numFrames =地板(虚拟现实。D.uration * vr.FrameRate); video = zeros(H,W,C,numFrames);%阅读框架我= 0;而hasfame(vr)i = i + 1;视频(:,:,:,i)= ReadFrame(VR);结尾删除未分配的帧如果Size (video,4) > I video(:,:,:, I +1:end) = [];结尾结尾
这centerCrop
函数可以裁剪视频的最长边并调整其大小inputSize
.
函数sz = size(video); / /视频大小如果SZ(1)%视频是横向的Idx = floor((sz(2) - sz(1))/2);视频(:1:(idx-1 ),:,:) = [];视频(:,(深圳(1)+ 1):结束 ,:,:) = [];elseif深圳(2)<深圳(1)%视频是纵向的Idx = floor((sz(1) - sz(2))/2);(1: (idx-1视频 ),:,:,:) = [];视频((深圳(2)+ 1):结束 ,:,:,:) = [];结尾vapleageized = imresize(视频,输入(1:2));结尾
Flattenlayer.
|lstmLayer
|SequenceFoldingLayer.
|sequenceInputLayer
|sequencfoldinglayer.
|trainingOptions
|Trainnetwork.