当前位置:   article > 正文

ResNet+LSTM实现对视频的时序信息识别_resnet lstm

resnet lstm

一、简介

  如何用神经网络把视频中的时序特征提取出来?比如说某个物体的摆动的频率;或者出现的时间长短;亦或是更高级的信息。
  这篇文章就是把卷积网络ResNet和记忆性网络LSTM结合在了一起,实现了对视频段中的信息识别。方法来源于一篇国外论文[1],但我相信这种应用肯定很多很多人已经做过了。卷积网络提取特征,LSTM负责记忆。如果直接把整个视频所有像素点放到网络里,实现不了长期记忆;如果一张张图片放到LSTM中,数据量又太大,而且冗余信息显然很多,训练很难。因此先使用卷积网络提取图形的“特征”,就达到了降维的效果。目前完整的代码比较少,我写这篇文章的目的就是分享一下代码,帮助大家使用神经网络来完成一些简单的小任务。

背后的原理十分简单,分两步:

1.卷积网络提取特征

  本文使用的是Pytorch上预训练过的ResNet18,其实任何一个训练过的卷积网络应该都可以。卷积网络通过对单帧图片进行卷积和池化运算,得到了"特征"(features),实际上就是把图片的图形特征转化成了浓缩过的数据,体现在长宽减少但深度增加上,最后加上全连接层,实现分类。
  预训练过的ResNet18能在ImageNet数据集上很好的分类,所以自然就可以确定其中的“特征”是有效的。我们把最后的全连接层去掉,就得到了“特征”。

2.LSTM实现记忆

  LSTM作为RNN的进化版,长短期记忆都能够胜任,也就是说能从一段序列中提取信息。最简单的应用就是从01序列中获取0的个数(编程很简单,但如何让计算机自己学习到呢),复杂点的有北京PM2.5预测。可以先从这些简单的开始学习,我就不从原理开始讲了。

二、背景

  提取视频中的时序信息需要一个例子,我的例子是:获得视频中物体的摆动频率。传统方法可能robustness比较差,一有噪音或者偏差就识别错了。但是神经网络可以实现对多个因素的考量。以下是场景展示:在这里插入图片描述
  这个是我自己写的C#窗口程序,只是用来测试算法的正确性的。可以选择方框摆动的频率,四点的坐标都加了一定程度的随机偏移。生成数据后即按照帧率和帧数生成了一堆图片,并把真实的(GroundTruth)频率保存在label.txt中。
  那么问题就是:从视频(时间连续的许多图片)中推测生成时的频率。人眼只能看到大致的快慢,而不能很精确,这也是神经网络的优点之一了。

三、配置

1.样本信息

  帧率15,帧数30,也就是一个样本有30张图片,每张图片生成时就是224大小,如果实际应用的话再加缩放切割就好。附带一个label.txt。文件夹结构:所有样本母文件夹\\样本文件夹\\30张图片和txt。
.\\sample\\2019-8-29-22-36-2\\label.txt
.\\sample\\2019-8-29-22-36-2\\o (1).jpg
……
.\\sample\\2019-8-29-22-36-2\\o (30).jpg

2.网络架构

  卷积使用ResNet18的一部分,具体中间的架构不重要,总之输入是(3,224,224)(通道数、长、宽),层层卷积池化后 (去掉全连接) 的输出是(512,7,7)。明显看到通道数增加,长宽减小。根据论文[1],最后再加一层max pooling,变成(512,3,3)。作用是减少由于relu输出的0的个数,进一步压缩,我觉得这步没有也许也可以?
  卷积完了,把通道铺平,变成3x3x512=4608的一维张量。样本中30张按顺序放好,并且加上批次,变成(batch,30,4608)。放到LSTM (hidden units=100,layers=2) 中运算,获得(batch,30,100)。LSTM实际上每一刻都有输出,但我们只要最终的推测,也就是看完所有图片后LSTM的输出,因此取出30个中的最后一个(batch,100)。
  由于输出是频率大小,是标量,因此增加一个线性映射层Linear(100->1),获得(batch,1)

总之流程是这样的(b的意思是batch):在这里插入图片描述

四、代码

https://github.com/luoye2333/ResNetLSTM
目前master分支是没有详细注释的,在Notes分支下有许多详细注释,但是代码已经落后了。切换分支我就不厌其烦的加张图片
在这里插入图片描述

五、部分代码解释

虽然注释比较完整,但是我还是讲讲一些关键点

1.关于Pytorch的ResNet18

  我本来是只会TensorFlow的,但是TF预训练好像只有ResNet50以上的,因此就用了Pytorch。用完之后发现体验异常的好,即时运算。
  如何实现去掉全连接层,我当时搜csdn(pytorch去掉某些层/获取中间某一层输出):一种用勾子,一种是再重新前向传播一遍。想了想还是后一种好,没有冗余计算,下面就是从我的代码ResNetLSTM.py,Line129截取的

ndata=torch.zeros(sampleNum,frames,4608)
with torch.no_grad():
    for i in range(sampleNum):
        input=data[i]
        x=n.conv1(input)
        x=n.bn1(x)
        x=n.relu(x)
        x=n.maxpool(x)
        x=n.layer1(x)
        x=n.layer2(x)
        x=n.layer3(x)
        x=n.layer4(x)
        x=pool(x)
        x=x.flatten(start_dim=1)
        ndata[i]=x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注意这句话 with torch.no_grad():
  作用是取消了resnet18的梯度计算,因为我们的卷积网络相当于只是预处理,在ImageNet上已经训练的十分好了,不用再训练。如果不写这句就会内存占用爆炸

2.定义RMSE

Pytorch原本是只有MSE(mean squared error)的,这种误差平方过,不直观。添加一个开根操作保证和原来的数据是一个量级。

#line32
self.criteria=torch.nn.MSELoss()
#line201
loss=torch.sqrt(self.criteria(pred,y))
loss.backward()
self.opt.step()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
3.保存读取多个网络的参数

既要保存LSTM的参数,又要保存Linear层的参数,怎么办?

保存:
把各个网络的参数用state_dict()拿出来,一起放到一个字典中
torch.save(dict,savePath)
读取:
dict=torch.load(savePath)
按照key从字典中分别读取,key和保存时的对应就行,可以自定义

#line220
state = {'net1':self.LSTM.state_dict(),
         'net2':self.Linear.state_dict(),
         'optimizer':self.opt.state_dict()}
saveName='{}.pth'.format(datetime.now().strftime('%Y-%m-%d-%H-%M-%S'))
torch.save(state,savedir+saveName)

#line261
def load(self,saveName):
    '''
    载入存档:当前路径\\save\\saveName
    '''
    savedir=os.path.dirname(__file__)+os.sep+'save'
    savePath=savedir+os.sep+saveName
    checkpoint = torch.load(savePath)
    self.LSTM.load_state_dict(checkpoint['net1'])
    self.Linear.load_state_dict(checkpoint['net2'])
    self.opt.load_state_dict(checkpoint['optimizer'])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

六、部署训练

  目前写的代码还是cpu版本,没有用gpu运算,后期也许会改,但是因为这个应用目前规模不大,所以不是很必要。
  但是cpu计算如果使用笔记本还是有点吃力的,我就尝试了一下在服务器上训练。结论是部署用了一个多小时,训练用了一个多小时,很快就收敛了。因为本身这个数据集噪音少,比较简单。花费不超过5¥
  我使用的是腾讯云上的计算型,随便挑了个1¥/小时的

1.连接服务器

  怎么购买我就不一步一步细说了:按量计费;镜像centos就可以;网络按使用流量,带宽拉满(反正1和100价格是一样的);免费送公网ip(用来连接服务器的);登录方式设置好密码记下来。
配置总结
开通之后在控制台里打开就是这样的,把公网ip复制下来
在这里插入图片描述
  打开一个ssh连接服务器的软件,我用的是xshell,这方面我也是新手一个。新建一个会话或者临时连接也可以。协议是ssh,主机就是公网ip,端口号22(专门用来ssh的端口,不要填别的)。后面弹出一个密钥警告什么的,一次性接受,因为公网ip我们把服务器换了就改了。用户名root,登录方式Password,密码就是开通的时候设置的密码。
在这里插入图片描述
连接成功就是会出来linux的命令行,按回车有反应就行了

2.样本/文件上传

  需要把本地的样本传到服务器上才能读取训练。我用的软件是FileZilla
  新建一个站点,协议是SFTP,和刚刚xshell差不多,略过
在这里插入图片描述
把源文件ResNetLSTM.py上传
save文件夹是之前训练的模型,无所谓
sample文件夹是放样本,注意文件名要和代码里对应
在这里插入图片描述

3.部署python3和其他环境

  开通的服务器默认只有python2,代码则是python3,同时也需要安装pytorch这些库,所以需要部署一下。下面的是我自己从网上搜来的,仅供参考,如果哪里有问题你们自己百度一下服务器安装python3就可以了。

首先,安装python3之前需要安装一些环境(因为linux上python是编译安装的,和windows上不太一样),总之一股脑把下面所有的命令右键粘帖到xshell里就可以安装了,ctrl+V粘帖不了。复制的不好可能会把最后一句的回车漏掉,自己按下。

yum -y install libffi-devel 
yum -y install zlib zlib-devel
yum -y install bzip2 bzip2-devel
yum -y install ncurses ncurses-devel
yum -y install readline readline-devel
yum -y install openssl openssl-devel
yum -y install openssl-static
yum -y install xz lzma xz-devel
yum -y install sqlite sqlite-devel
yum -y install gdbm gdbm-devel
yum -y install tk tk-devel
yum -y install libSM-1.2.2-2.el7.x86_64 --setopt=protected_multilib=false
yum -y install libXext

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述
第二步,安装python3。建议在windows上从官网下载linux的安装包然后上传到服务器上。(也可以直接命令行安装,我也忘了当时为什么要这样做)
https://www.python.org/downloads/source/
在这里插入图片描述
然后把它用filezilla上传到服务器上,下面的命令可能会因为版本问题名字不太一样,自己注意下。

(解压刚刚上传的压缩包) tar -xvf (按tab键自动补全)
(进入解压出来的文件夹) cd Py(tab补全)
(配置一下)           ./configure --prefix=/usr/local/Python3.7.4(这里版本号自己改,其实就是安装路径)
(编译然后安装)        make&&make install
  • 1
  • 2
  • 3
  • 4

运行完了应该会有
在这里插入描述
在/usr/local文件夹下应该会有Python3.7.4这个文件夹,和configure命令执行的一样

第三步,配置环境变量

(添加软连接什么的)        
ln -s /usr/local/Python3.7.4/bin/python3 /usr/bin/python3
这步非常重要,作用就是输入python3命令可以直接运行/usr/local/Python3.7.4/bin/python3的程序

(用vim打开某个厉害的文件)
vim ~/.bash_profile
在最后添加(按i进入插入模式,然后移到最后一行,按end,按回车,右键粘帖)
export PYTHON_HOME=/usr/local/Python3.7.4
export PATH=$PYTHON_HOME/bin:$PATH
保存退出(按esc,直接输入:wq,然后回车)

(使修改过的文件生效) 
source ~/.bash_profile
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
python3就安装好了,输入python3看能不能进命令行
如果不行,报错no such file or directory 检查软链接的前面一个路径对不对,大小写啊啥的
再次创建的时候报错failed to create symbolic link ‘/usr/bin/python3’: File exists
说明之前错的还在,
rm /usr/bin/python3
rm: remove symbolic link ‘/usr/bin/python3’? y
注意输入yes/y

第四步,安装python库pytorch和opencv,使用了清华的镜像,下载快一点

pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple torch torchvision opencv-python
  • 1

如果报错-bash: pip3: command not found,说明vim修改~/.bash_profile那步有错,注意路径
第五步,下载ResNet18预训练模型
在python3命令行下

import torchvision as tv
n=tv.models.resnet18(pretrained=True)
  • 1
  • 2

输完就可以看到下载开始,完成以后就可以按windows上的步骤训练了。具体使用方法在文件ResNetLSTM.py中最后有写

七、感想

  我做这个的时候,一点头绪都没有,每一步都是我摸索出来的,参考了许多其他人的代码、博客。论文里面写了用的是Matlab,可我终究是接受不了。一开始也不知道Pytorch,只知道TensorFlow,看了tf.slim一头雾水。

首先,一定要沉下心来,不能遇到难题就怕,不敢解决;遇到难懂的东西,要一点一点去理解。
第二,如果有一个老师在旁边指导,帮你走入门槛,效率要高不知道多少,所以希望这篇文章能帮到大家吧。

  有问题可以在评论区问,我会慢慢修改初稿。欢迎大家在我的代码的基础上开发一些有意思的应用~~。写完所有的代码,大概用了两个星期?我觉得效率太低了,实在比不上日码10k的那些人,可能是我花了太多时间看bilibili了吧= =。过几天我会再写一篇用卷积网络识别实现自动玩扫雷游戏的文章。

[1] JenniferL.Cardona, MichaelF.Howland, JohnO.Dabiri.
Seeing the Wind: Visual Wind Speed Prediction with a Coupled Convolutional and Recurrent Neural Network

See more details at:https://arxiv.org/abs/1905.13290?context=cs.LG

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/118724
推荐阅读
相关标签
  

闽ICP备14008679号