文档

使用深度学习对视频进行分类

此示例演示如何通过结合预训练图像分类模型和LSTM网络来创建视频分类网络。

要创建视频分类的深度学习网络,请执行以下操作:

  1. 使用预先训练的卷积神经网络(如GoogLeNet)将视频转换为特征向量序列,以从每一帧中提取特征。

  2. 在序列上训练LSTM网络以预测视频标签。

  3. 组合一个网络,通过组合两个网络中的层直接对视频进行分类。

下图说明了网络体系结构。

  • 要将图像序列输入网络,请使用序列输入层。

  • 要使用卷积层来提取特征,即独立地将卷积操作应用于视频的每一帧,请使用序列折叠层,然后是卷积层。

  • 要恢复序列结构并将输出重塑为矢量序列,请使用序列展开层和展平层。

  • 要对生成的向量序列进行分类,请包括LSTM层,然后是输出层。

负载预训练卷积网络

要将视频帧转换为特征向量,请使用预训练网络的激活。

使用水壶函数。此函数需要深入学习工具箱™ 模型谷歌网万博1manbetx支持包。如果未安装此支持万博1manbetx包,则该函数提供了下载链接。

netCNN=谷歌网;

加载数据

从下载HBMD51数据集HMDB:大型人类运动数据库并将RAR文件解压缩到名为“hmdb51_组织”.数据集包含大约2 GB的视频数据超过51级,例如“喝”,“跑”, 和“握手”.

提取RAR文件后,使用支持函数万博1manbetxhmdb51files.获取视频的文件名和标签。

datafolder =.“hmdb51_组织”;[files,labels]=hmdb51文件(dataFolder);

使用readvideo.助手函数,在本例末尾定义,并查看视频的大小。这段视频是一部电影H-借-W-借-C-借-s阵列,其中H,W,C, 和s分别是视频的高度、宽度、通道数和帧数。

idx=1;文件名=文件(idx);视频=读取视频(文件名);大小(视频)
ans=1×4240 320 3 409

查看相应的标签。

标签(IDX)
ans=明确的梳头

要查看视频,请使用恳求函数(需要图像处理工具箱™). 此函数需要[0,1]范围内的数据,因此必须先将数据除以255。或者,您可以在各个帧上循环并使用显示图像作用

numFrames=大小(视频,4);图形对于i=1:numFrames frame=video(:,:,:,i);imshow(frame/255);drawnow终止

将帧转换为特征向量

使用卷积网络作为特征提取器,在向网络输入视频帧时获得激活。将视频转换为特征向量序列,其中特征向量是网络的输出激活GoogLeNet网络最后一个池层上的函数(“池5-7x7_s1”).

此图显示了通过网络的数据流。

要读取视频数据并调整其大小以匹配GoogLeNet网络的输入大小,请使用readvideo.CenterCrop.助手函数,在本例末尾定义。此步骤可能需要很长时间才能运行。将视频转换为序列后,将序列保存在坦普迪尔如果MAT文件已经存在,则从MAT文件加载序列,而不重新转换它们。

InputSize = netcnn.layers(1).Putsize(1:2);layername =“池5-7x7_s1”;tempfile = fullfile(tempdir,“hmdb51_org.mat”);如果存在(临时文件,'文件')LOAD(TempFile,“序列”)其他的numfiles = numel(文件);序列= cell(numfiles,1);对于i = 1:numfiles fprintf(“正在读取文件%d,共%d…。\n”,i,numFiles)video=readVideo(files(i));video=centerCrop(video,inputSize);sequences{i,1}=activations(netCNN,video,layerName,'outputas',“专栏”);终止保存(临时文件,“序列”,“-v7.3”);终止

查看前几个序列的大小。每个序列都是一个D-借-s阵列,其中D是功能数量(池池层的输出大小)和s是视频的帧数。

顺序(1:10)
ans=10×1个单元阵列{1024×409单} {1024×395单} {1024×323单} {1024×159单} {1024×137单} {1024×359单} {1024×191单} {1024×439单} {1024×528单}

准备培训数据

通过将数据划分为培训和验证分区并删除任何长序列,为培训准备数据。

创建培训和验证分区

对数据进行分区。将90%的数据分配给培训分区,将10%的数据分配给验证分区。

numObservations=numel(序列);idx=randperm(numObservations);N=floor(0.9*numObservations);idxTrain=idx(1:N);sequencesTrain=sequences(idxTrain);labelsTrain=labels(idxTrain);idxValidation=idx(N+1:end);sequencesValidation=序列(idxValidation);labelsValidation=标签(idxValidation);

删除长序列

比网络中的典型序列长得多的序列会在训练过程中引入大量填充。填充过多会对分类精度产生负面影响。

获取培训数据的序列长度并在培训数据的直方图中可视化它们。

numObservationsTrain=numel(sequencesTrain);SequenceLength=0(1,numObservationsTrain);对于i = 1:numobservationstrain序列= sequencestain {i};Sequencelengths(i)=大小(序列,2);终止图直方图(序列长度)标题(“序列长度”)xlabel(“序列长度”)ylabel(“频率”)

只有少数序列具有超过400个时间步。为了提高分类精度,请删除具有超过400个时间步的训练序列及其相应的标签。

最大长度=400;idx=序列长度>最大长度;序列菌株(idx)=[];标签序列(idx)=[];

创建LSTM网络

接下来,创建一个LSTM网络,该网络可以对代表视频的特征向量序列进行分类。

定义LSTM网络体系结构。指定以下网络层。

  • 序列输入层,其输入大小与所述特征向量的特征维数相对应

  • 具有2000个隐藏单元的BiLSTM层,之后带有一个退出层。通过设置“输出模式”Bilstm层的选项“最后”

  • 一个完全连接的层,其输出大小对应于类别,软MAX层和分类层的数量。

numFeatures=size(sequencesTrain{1},1);numClasses=numel(categories(labelsTrain));layers=[sequenceInputLayer(numFeatures,“姓名”,“顺序”)Bilstmlayer(2000年,“输出模式”,“最后”,“姓名”,“bilstm”)dropoutLayer(0.5,“姓名”,“下降”)全连接列(numcrasses,“姓名”,'fc')软MaxLayer(“姓名”,“softmax”scassificationlayer(“姓名”,'分类')];

指定培训选项

使用培训选项作用

  • 设置迷你批量16,初始学习率为0.0001,梯度阈值为2(以防止梯度爆炸)。

  • 将每个迷你批处理中的序列截断与最短序列相同的长度。

  • 每个历元对数据进行洗牌。

  • 每个历元验证一次网络。

  • 在绘图中显示训练进度并抑制详细输出。

minibatchsize = 16;numobservations = numel(Sequencestain);numiterationsperepoch = bloor(numobservations / minibatchsize);选项=培训选项(“亚当”,......“MiniBatchSize”,小批量,......“初始学习率”,1e-4,......“梯度阈值”2.......“洗牌”,'每个时代',......'vightationdata',{序列验证,标签验证},......'验证职业',numIterationsPerEpoch,......“情节”,'培训 - 进步',......“冗长”,错误的);

火车LSTM网络

使用列车网络函数。这可能需要很长时间才能运行。

[netLSTM,info]=列车网络(sequencesTrain,labelsTrain,layers,options);

计算验证集上网络的分类准确性。使用与培训选项相同的百分比尺寸。

YPred=分类(netLSTM,序列验证,“MiniBatchSize”,miniBatchSize);YValidation=标签验证;准确度=平均值(YPred==YValidation)
精度=0.6647

组装视频分类网络

要创建直接对视频进行分类的网络,请使用两个创建的网络中的层组装网络。使用卷积网络中的层将视频转换为矢量序列,使用LSTM网络中的层对矢量序列进行分类。

下图说明了网络体系结构。

  • 要将图像序列输入网络,请使用序列输入层。

  • 要使用卷积层来提取特征,即独立地将卷积操作应用于视频的每一帧,请使用序列折叠层,然后是卷积层。

  • 要恢复序列结构并将输出重塑为矢量序列,请使用序列展开层和展平层。

  • 要对生成的向量序列进行分类,请包括LSTM层,然后是输出层。

添加卷积层

首先,创建GoogLeNet网络的图层图。

cnnLayers=层图(netCNN);

移除输入层(“数据”)和用于激活的汇集层之后的层(“池5-drop_7x7_s1”,“loss3分类器”,“问题”, 和“输出”).

层名称=[“数据”“池5-drop_7x7_s1”“loss3分类器”“问题”“输出”];CNNLayers = RemoveLayers(CNNLayers,Layernames);

添加序列输入层

创建一个序列输入层,该层接受包含与GoogLeNet网络输入大小相同的图像的图像序列。要使用与GoogLeNet网络相同的平均图像对图像进行规格化,请设置“正常化”序列输入层的选项'Zerocenter'“中庸”GoogLeNet输入层的平均图像选项。

InputSize = netcnn.layers(1).Putsize(1:2);普生图像= netcnn.layers(1).AverageImage;InputLayer = sequenceInputLayer([输入3],......“正常化”,'Zerocenter',......“中庸”,平均,......“姓名”,“输入”);

将序列输入层添加到图层图。为了独立地将卷积层应用于序列的图像,通过在序列输入层和卷积层之间包括序列折叠层来除去图像序列的序列结构。将序列折叠层的输出连接到第一卷积层的输入(“conv1-7x7_s2”).

layers=[inputLayer sequenceFoldingLayer(“姓名”,“折叠”)];Lgraph = Addlayers(CNNLayers,层);lgraph=连接层(lgraph,“折叠/展开”,“conv1-7x7_s2”);

添加LSTM层

通过移除LSTM网络的序列输入层,将LSTM层添加到层图中。若要恢复序列折叠层移除的序列结构,请在卷积层之后包含序列展开层。LSTM层期望向量序列。若要将序列展开层的输出重塑为向量序列,请例如,在序列展开层之后包括展平层。

从LSTM网络中获取层,并移除序列输入层。

lstmlayers = netlstm.layers;lstmlayers(1)= [];

将序列折叠层、展平层和LSTM层添加到图层图中。连接最后一个卷积层(“池5-7x7_s1”)到序列展开层的输入(“展开/插入”).

层=[SequenceUnfolingLayer](“姓名”,'展开')压扁层(“姓名”,“扁平化”)lstmlayers];Lgraph = Addlayers(LAGHAGH,层);lgraph=连接层(lgraph,“池5-7x7_s1”,“展开/插入”);

要使展开层恢复序列结构,请连接“最小批量大小”序列折叠层的输出到序列展开层的相应输入。

lgraph=连接层(lgraph,“折叠/小批量大小”,“展开/小批量大小”);

装配网络

检查网络是否有效使用分析网络作用

分析(LGROPH)

组装网络,以便使用装配网络作用

net=汇编网络(lgraph)
net=DAG网络,具有以下属性:层:[148×1 nnet.cnn.layer.layer]连接:[175×2表]

使用新数据进行分类

读取和中心裁剪视频“pushup.mp4”使用与以前相同的步骤。

文件名=“pushup.mp4”;Video = ReadVideo(文件名);

要查看视频,请使用恳求功能(需要图像处理工具箱)。此函数预计在范围[0,1]中的数据,因此您必须首先将数据划分为255.或者,您可以循环各个帧并使用显示图像作用

numFrames=大小(视频,4);图形对于i=1:numFrames frame=video(:,:,:,i);imshow(frame/255);drawnow终止

使用组合网络对视频进行分类分类函数需要一个包含输入视频的单元格数组,因此必须输入一个包含视频的逐1单元格数组。

Video = CenterCrop(视频,输入);ypred =分类(net,{video})
伊普雷德=明确的伏地挺身

辅助函数

这个readvideo.功能读取视频文档名称并返回一个H-借-W-借-C-借-s阵列,其中H,W,C, 和s分别是视频的高度、宽度、通道数和帧数。

功能Video = ReadVideo(文件名)VR = Videoreader(文件名);h = vr.height;w = vr.width;C = 3;%preal分配视频阵列numframes =楼层(vr.duration * vr.framerate);Video = Zeros(H,W,C,NumFrames);%读取框架i=0;尽管hasFrame(vr)i=i+1;video(:,:,:,i)=readFrame(vr);终止%删除未分配的帧如果大小(视频,4)>i视频(:,:,:,i+1:end)=[];终止终止

这个CenterCrop.功能作弊视频的最长边缘并调整它具有大小的大小输入大小.

功能videoResized=中心裁剪(视频,输入大小)sz=大小(视频);如果sz(1)%视频是景观idx=楼层((sz(2)-sz(1))/2;视频(:,1:,:,:)=[];视频(:,(sz(1)+1):结束,:,:)=[];eleesifsz(2)%视频是肖像idx=楼层((sz(1)-sz(2))/2;视频(1:(idx-1),:,:,:)=[];视频((sz(2)+1):结束,:,:,:)=[];终止videoResized=imresize(视频,输入大小(1:2));终止