文章目录

  • 一、写在前面
  • 二、相关简介
  • 什么是VGG16?
  • 什么是空洞卷积(dilated convolution)?
  • 三、相关工作
  • CSRNet架构
  • 训练方法
  • 四、实验结果
  • 五、总结


一、写在前面

最近在看人群计数相关的方向,和博主 @Jack和Tim是两个北方男孩 一样,《CSRNet: Dilated Convolutional Neural Networks for Understanding the Highly Congested Scenes》也是我看的第一篇论文。作为一名大二大三学术菜鸡,我花了比较久的时间看完了这篇2018年的论文,有很多细节上的东西还是没有搞明白,模型也因为之前不懂事乱搭环境没跑起来。先记录一下,跑成功应该会更新后续内容。

二、相关简介

该网络主要包括两个部分,前端使用VGG16(改造版)用于 2D 特征提取,后端用空洞卷积网络代替池化操作。two major components: a convolutional neural network(CNN) as the front-end for 2D feature extraction and a dilated CNN for the back-end, which uses dilated kernels to deliver larger reception fifields and to replace pooling operations.

什么是VGG16?

VGG是由 Simonyan 和 Zisserman 在文献《Very Deep Convolutional Networks for Large Scale Image Recognition》中提出卷积神经网络模型,其名称来源于作者所在的牛津大学视觉几何组(Visual Geometry Group)的缩写。其中VGG16属于该模型的D型配置。

下图是VGG16的结构示意图,其中:

  • 224x224x3的彩色图表示3通道的长和宽都为224的图像数据,也是网络的输入层。
  • 白色部分为卷积层,红色部分为池化层(使用最大池化),蓝色部分为全连接层,其中卷积层和全连接层的激活函数都使用relu。
  • 总的来说,VGG16网络为13层卷积层+3层全连接层而组成。卷积层和全连接层具有权重系数,因此也被称为权重层,总数目为13+3=16,这即是VGG16中16的来源。(池化层不涉及权重,因此不属于权重层,不被计数)。


怎么把resnet50做量化_卷积核


图1:VGG16


需要注意的是,CSRNet网络中,修改了VGG16的结构。

具体为:

  • 采用了统一的卷积核大小(3 * 3)降低网络的复杂性。To limit the network complexity, we use the small size of convolution fifilters (like 3 × 3) in all layers.
  • 取消了全连接层的使用,即图1蓝色部分。并且只采用了十层卷积层(见下图4)。By removing the fully-connected layers, we try to determine the number of layers we need to use from VGG-16. We deploy the first 10 layers from VGG-16 as the front-end.
  • 图1红色部分代表池化层,有五层。但在CSRNet模型中,只用了三层池化层。池化层的作用是抑制过拟合,但缺点是会降低空间分辨率(spatial resolution),意味着会损失一些特征图的空间信息。 the first ten layers of VGG-16 with only three pooling layers instead of five to suppress the detrimental effects on output accuracy caused by the pooling operation.

参考链接

VGG16学习笔记

什么是空洞卷积(dilated convolution)?

首先我们先了解一下卷积的概念。

卷积过程是基于一个小矩阵,也就是卷积核,在每层像素矩阵上不断按步长扫过去的,扫到数与卷积核对应位置的数相乘,然后求总和,每扫一次,得到一个值,全部扫完则生成一个新的矩阵。如下图



怎么把resnet50做量化_卷积核_02


图2:卷积


黄色矩阵即为卷积核(3×3),矩阵扫过的结果存入“Convolved Feature”对应矩阵的中心格中,也叫做 “Activation Map” 或 “Feature Map”。

卷积核如何设置可以参考卷积神经网络,卷积核大小、个数,卷积层数如何确定呢?一般取(3,3)的小矩阵,卷积核里面每个值就是我们需要寻找(训练)的神经元参数(权重),开始会随机有个初始值,当训练网络时,网络会通过后向传播不断更新这些参数值,直到寻找到最佳的参数值。如何知道是“最佳”?是通过损失函数去评估。卷积核的步长是指卷积核每次移动几个格子,有横行和纵向两个方向。卷积操作相当于特征提取,卷积核相当于一个过滤器,提取我们需要的特征。

大致了解了卷积相关操作之后,我们来了解一下空洞卷积。

首先观察一下CSRNet论文里的示意图:



怎么把resnet50做量化_卷积_03


图3:空洞卷积示意图


如图3,Kernel size即为卷积核大小,黄色方块的个数始终未变,均为9个(3*3)。通过改变空洞率(Dilation rate)的方式,得到了稀疏的卷积核(sparse kernels)。

这样做的好处是:

  • 在不增加参数和计算量的前提下,增大了感受野。 This character enlarges the receptive field without increasing the number of parameters or the amount of computation.
  • 比其他网络更有效地维持了特征图的分辨率。 Most importantly, the output from dilated convolution contains more detailed information.

参考链接

如何理解空洞卷积

三、相关工作

为了更好的说明CSRNet的相关操作,这一部分会提及上文所述概念。

CSRNet架构

该网络采用VGG16作为前端,因其拥有强壮的迁移学习能力,灵活的架构易于和后端对接。在其它网络中,为生成密度图起辅助作用的VGG16并没有显著提高最后结果的正确率。于是本网络移除了VGG16的全连接层、。

受到一些网络的启发,研究人员在后端部署了空洞卷积层来提取更深层的显著性信息,同时保持输出的分辨率。

通过反复的实验比较(包括训练时间,内存消耗,参数数量等),本网络结构如下图:



怎么把resnet50做量化_深度学习_04


图4:CSRNet网络结构示意图


可见,本网络的前端结构VGG16,有10个卷积层,三个池化层,对所有输入均保持一致。而对于后端结构,本网络设置了四个不同的配置,卷积层数量均为6层,但相关的参数有所变化。注:卷积层如 conv3-256-1表示的意思为:卷积核大小为(3 X 3),滤波器数量为256,空洞率为1。

因为经过了三次池化层,本网络输出的密度图比较小,宽高均为原图的1/8。如果设置更多的卷积层,输出大小会进一步的下降,意味着很难生成高质量的密度图。通过双线性插值的方法来将密度图放大至与原图分辨率大小相同。对应源代码如下:

#image.py
 #长宽缩小为原图的1/8,*64保证分辨率大小不变
 target = cv2.resize(target(target.shape[1]/8,target.shape[0]/8),
                   interpolation = cv2.INTER_CUBIC) * 64
训练方法
  1. 生成GT图 (Ground truth generation)
    采用了高斯卷积的方式生成GT图。对于每个目标点 怎么把resnet50做量化_怎么把resnet50做量化_05,找到距它最近的 怎么把resnet50做量化_卷积核_06个邻点,通过相关计算得到平均距离为 怎么把resnet50做量化_卷积核_07 ,再通过高斯卷积生成密度图。公式如下:
    怎么把resnet50做量化_深度学习_08
    可见,其中的方差 σ 值具有几何自适应的性质。
  2. 数据增强
    通过图片裁剪以及图片镜像的方法得到更多的数据。思路是将原图裁成9份,每份都是1/4大小,其中四份是按照田字格的方式将图片分割,其余五份为随机裁剪。源代码中默认为False,如果要启用数据增强则需要修改相关代码。
    对应源代码如下:
#image.py
#(需修改)
if False:
        #切割图片,size[0]代表长,1为宽,各占一半
        crop_size = (img.size[0]/2,img.size[1]/2)
        #(需修改)
        if random.randint(0,9)<= -1:
            dx = int(random.randint(0,1)*img.size[0]*1./2)
            dy = int(random.randint(0,1)*img.size[1]*1./2)
        else:
            dx = int(random.random()*img.size[0]*1./2)
            dy = int(random.random()*img.size[1]*1./2)
        #切割,参数对应左上右下
        img = img.crop((dx,dy,crop_size[0]+dx,crop_size[1]+dy))
        #没看懂这步操作
        target = target[dy:crop_size[1]+dy,dx:crop_size[0]+dx]
        #利用随机翻转对图像进行预处理来增加训练数据
        if random.random()>0.8:
            #左右翻转矩阵
            target = np.fliplr(target)
            #图片左右翻转
            img = img.transpose(Image.FLIP_LEFT_RIGHT)
  1. 训练细节
    采用了固定的学习率(learning rates)为怎么把resnet50做量化_卷积核_09。并利用欧几里得距离来测量生成的密度图和GT图之间的差异。
    该模型的损失函数如下:


怎么把resnet50做量化_深度学习_10


图5:损失函数


由于本人水平有限,损失函数这部分不做深入讲解。具体可参考如下链接。

参考链接

损失函数

四、实验结果

研究人员设置了四组实验,四个CSRNet模型对应的参数均相同,但具有不同的空洞率。其中CSRNet A对应的空洞率r为1,CSRNet B为2,CSRNet D为4,而CSRNet C则介于2-4之间。研究人员发现在空洞率 r = 2时,模型得到的平均绝对误差(MAE),均方误差(MSE)最小,即该条件下人群计数效果最好。对应结果如下图:



怎么把resnet50做量化_深度学习_11


图6:四组CSRNet模型结果对比(基于上海数据集A)


研究人员采用了DropOut算法防止过拟合(弥补池化层数的减少),但发现对结果并没有显著提升,于是最后并未加入该算法。

根据实验结果,研究人员最终采用了模型B来进行接下来的实验。

实验结果十分可观,在各个数据集中表现良好超过了很多当时一流水平的网络。下面是基于上海数据集(ShanghaiTech dataset)与其它神经网络对比的实验结果,以及密度图质量对比:



怎么把resnet50做量化_卷积核_12


图7:各种神经网络估计结果对比(基于上海数据集)



怎么把resnet50做量化_神经网络_13


图8:密度图质量对比(基于上海数据集)


参考链接

DropOut

五、总结

CSRNet通过空洞卷积并减少池化层的相关操作,大大地提高了神经网络对图像细节的提取并提升了图像显著性特征,本网络比较容易部署和训练,并且网上有相关的源代码。缺点是算法可能相对较旧,如今有很多优秀的人群计数方法已经超越了CSRNet的性能。

对于CSRNet的部署和源代码解读,可参考如下链接:


最后,这是我写下的第一篇博客,对我来说意义非凡。本人深度学习方面的基础比较薄弱,如果有疏漏或者错误希望大家理解同时欢迎大家在评论区指出。

😉