Pytorch神器

大家好,今天我们来聊一下深度学习中CV领域的另一个有趣应用目标检测。

说到目标检测,可能有的朋友还不熟悉,我们就看看。

看上去这么乱乱的图就是目标检测应用的输出了,简单说,目标检测的任务就是输入一张图片或者一帧图像,通过一系列计算,使得输出是这样的一个可视化结果或一个等同于这种可视化结果的数据结果。也就是让计算机把图片上哪里有物体,物体是什么,多少概率。图上我们看到的其实就是这样一个内容的输出了。

这种应用还是有非常宽的应用场景的,不管是在安防领域、自动驾驶领域、娱乐领域等各个领域都会有应用。那么这种应用是用什么技术来实现的呢?其实很多朋友如果一直关注我们的系列分享一定能够猜出来,应该用的是卷积网络。没错,确实是使用卷积网络作为其中重要的实现部分。

用于实现这样一种应用的方法其实已经研究了很多年了,从RCNN、Fast RCNN、Faster RCNN到近年来应用比较火的YOLO和SSD这些模型来看,结构越来越简单,效率越来越高。那么我们今天就来看看其中的一个比较简单的模型SSD。

SSD的全称是Single Shotmultibox Detector,中文咱就不翻译了。简单说就是可以通过一次扫描图片就能在把图片上的物体位置和分类都标记出来。很厉害是吧。那么它是怎么实现的呢?其实并不太难理解,虽然学术上的推导和试验阶段确实显得比较复杂,但是在我们了解了深度学习的原理后,我们就可以以我们理解的一些概念为基础来研究和理解这个模型。

首先这个模型仍旧有Encoder-Decoder模型的特性——也就是说整个大的网络仍然从结构和功能上分成了两个部分,一个是Encoder的部分一个是Decoder的部分。下面我们就来逐步理解一下这里面各个部分的功能。

1、输入

不必多说,这个部分的输入就是一张图片。

2、输出

输出的内容应该是我们画出来的那个带有框的图片。图片上的每个框有坐标位置信息和分类信息。

需要说明的是,一个框的坐标没必要记录其四个点的坐标,因为在这个模型中我们设定的位置信息描述是一个矩形,矩形只需要记录左上角和右下角的坐标信息就可以描述其位置了。这样也就是左上角的x,y和右下角的x,y两个点的信息,一共4个整数值就可以了。除此之外,每个框还有一个分类信息,这个分类信息的描述和MNIST识别模型中的分类模型没有什么区别,就用独热向量表示就可以了。

3、网络结构

网络结构是这个模型的重点,但其实不算太难,因为我们毕竟见过不止一个Encoder-Decoder的模型。在这个模型中也有吗?答案是肯定的。

这个模型的Encoder部分其实一个在分类模型上做过训练的网络,用VGG也可以,用RESNET也可以,但一定是训练好的模型。照旧,这个部分需要把网络的最后一层的分类层去掉,只保留从第一层到倒数第二层的结构。这样,这个部分的网络就具备了特征提取的能力,并把一张图片的内容提取成了一个几千维的Code信息。

在分类训练的过程中,由于损失函数的限制,那么这个Code信息中其实包涵了对于物体位置、物体大小、物体轮廓、物体颜色等丰富信息的描述。这个提取出来的Code人是读不懂的,但是由于计算机在大量物体的识别中做了甄别,所以这个Code它能读懂。这是从训练任务中学到的能力。

从Code到后面输出内容的信息比较有趣。

刚刚我们也说过,Code中蕴含了丰富的大小、轮廓、颜色等信息。现在我们期望的是用这些信息来解码成对应的物体位置和分类的信息。

如果这个Code中真的包含这样的信息,那么通过足够多的样本训练应该是可以映射出来的——训练复杂映射关系是卷积网络的基本功了,人家最擅长的就是这个。

因此,在输出层我们要想办法用一个矩阵来描述这样的信息。

为了完成一次性的扫描,在精细度问题上,其实是做了一定的妥协的。将GT box先match到某个cell的一个或多个default box——图中的虚线框;假设每个cell有k个default box(在这个示例中给的是3),则对于每个default box预测c个类别score(就是类似于置信度一样的得分)和4个offset;因此对于一个m*n个cell的map,就有(c+4)*k*m*n个输出,输出的矩阵尺寸就可以从下图的示意图看出来了。

那就应该是m*n*k*(c+4)这么多个“维度”。而且在这里需要强调一下的是,有不同尺寸的feature map就可以适应不同大小的物体。比如8*8这种尺寸的就可以用来识别较小的物体,而4*4的则用来识别比较大的物体。

这样一来就好了,只要有这个东西,误差就可以计算了,对不对。

因为不管你的Decoder部分的网络怎么设计,一开始甚至到结束肯定会有误差存在。这个误差就是每个单元格经过正向传播所产生的拟合值和GT值之间的差距。

这个差距包含两个部分:

第一、分类的差距。

如果你判断n类物体,那一定是n+1位的独热编码。这n位的编码自不必说,一个分类一个,还有一个类就是Background类,也就是说背景——哪类都不算。这样一来,如果分类错误,那么就是一种重要的误差了。尤其是那些本来没物体的,给识别成有物体的,或者反过来。其次就是那些傻傻分不清把物体之间分类搞混的。这些都算是误差损失。

第二、距离的偏移。

这些框是事先画出来的,所以极大概率是和你给的GT对不上的,多少都有偏差。为了纠正这个东东,需要在每个框上有4个值的偏移量值分别是x,y,w,h,也就是坐标位置和宽、高的偏移。一旦给了GT之后,这些值其实是确定的,但是正向传播中有误差,而这个偏差当然也应该尽可能小,这就是训练要做的事情了。

4、损失函数

损失函数的样子就是这样,前后两项而已。

x就是图片,l是预测出来的box,c是分类,g是Ground Truth。前面的1/N表示平均每一个样本的损失,后面的前一项是分类损失项,简单说就是分错了的损失。后面的一项是一个回归损失项,指的是映射出来的方框中的位置偏移和Ground Truth的差距。优化方向就是让整个值减小,又要分对类,又要标准确位置。两种损失其实就是交叉熵损失函数和平方损失函数的变种了,具体的逻辑可以从代码里直接读,都不复杂。

5、代码工程

在Github上我找到一个还不错的Pytorch SSD项目:

https://github.com/amdegroot/ssd.pytorch

下载下来:

cd ~/

git clone https://github.com/amdegroot/ssd.pytorch

这个项目支持MSCOCO数据集,也支持VOC数据集,我用的是VOC数据集进行的训练。

cd ~/ssd.pytorch

sh data/scripts/VOC2007.sh

sh data/scripts/VOC2012.sh

对于预训练权重也是有现成的文件的,就是指Encoder的那个部分。

mkdir weights

cd weights

wget https://s3.amazonaws.com/amdegroot-models/vgg16_reducedfc.pth

这些都准备好就可以开始训练了。

python train.py --batch_size 12–learning-rate=1e-5

这个地方learning-rate要给的小一些,如果用原来默认的1e-3的话很可能还没有几轮就loss=nan了,也就没办法训练下去了。前面的batch_size主要是看自己的GPU显存大小如何,我这个4GB的老爷机用12已经很大了,太大了就会引发OOM的错误。

也是非常脏花时间的,大概要大半宿吧,默认12万轮训练。

这个时候你会在weights目录下得到一个VOC.pth的文件

如果你想做验证的话,可以使用

python –trained_model wieghts/VOC.pth

这个命令来验证。

这里验证完毕会把每个类别验证的结果写在这个文件夹下面:

ssd300_120000/test/

这种.pkl文件你直接是读不了的,不过根据eval.py文件的信息你可以判断出来,这些文件保存的就是每个图片分类的测试数据。

如果你想把这个测试过程可视化的话,可以参考写在jupyter里面的提示信息:

先下载和安装

https://files.pythonhosted.org/packages/45/4d/79a9a1f71de22fbc6c6433ac135f68d005de72fbe73e2137d2e77da9252c/testresources-2.0.1-py2.py3-none-any.whl

sudo pip3 installtestresources-2.0.1-py2.py3-none-any.whl

sudo pip3 install --upgrade--force-reinstall jupyter

demo/demo.ipynb

我认为这个项目其实只要模型的意义基本理解了。代码看起来就会非常轻松,虽然内容看上去好像挺多的。

关于代码,咱们都已经看了这么多的工程了,阅读代码的能力想必也是有了相当的提高。从一行一行阅读现在也应该可以进化成一段一段阅读了吧。这个工程中最复杂的部分是Loss函数的定义,函数定义在layers/modules/multibox_loss.py里面的48行到117行,分别计算了conf损失和loc损失,也就是分类损失和关于偏移位置的距离损失。

经过交叉检验,alpha设置成1。这个alpha主要是为了调节两个不一样量纲来源项目的比例的因子,在很多项目中都会用到。

损失函数的每一项具体就写在这里了,公式是令人头痛的。具体计算的因由如果一时不能理解也没关系,就按照前面的原理梗概理解就可以了。理解输入和输出的内容,网络结构的两个部分,损失函数中所做的限制条件究竟是如何做的限制就可以了。

好了,今天我们就分享到这里了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180604G0C4V100?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券