SSD图像识别代码学习笔记

看代码之前先简述一下SSD的做法:图片被送进网络之后先生成一系列feature map,传统一点的框架会在feature map(或者原图)上进行region proposal提取出可能有物体的部分然后进行分类,这一步可能非常费时,所以SSD就放弃了region proposal,而选择直接生成一系列defaul box(或者叫prior box),然后将这些default box回归到正确的位置上去,整个过程通过网络的一次前向传播就可以完成,非常方便,如下图所示。思路类似的算法还有YOLO,不过精度比SSD差了不少。

 

prior_box_layer

这一层完成的是给定一系列feature map后如何在上面生成prior boxSSD的做法很有意思,对于输入大小是 W×H feature map,生成的prior box中心就是 W×H 个,均匀分布在整张图上,像下图中演示的一样。在每个中心上,可以生成多个不同长宽比的prior box,如[1/3, 1/2, 1, 2,3]。所以在一个feature map上可以生成的prior box总数是 W×H×length_of_aspect_ratio ,对于比较大的feature map,如VGGconv4_3,生成的prior box可以达到数千个。当然对于边界上的box,还要做一些处理保证其不超出图片范围,这都是细节了。

这里,先弄清楚下文所说的 defaultbox 以及 featuremap cell 是什么。看下图:

·        featuremap cell 就是将 feature map 切分成 8×8 或者 4×4 之后的一个个 格子

·         default box 就是每一个格子上,一系列固定大小的 box,即图中虚线所形成的一系列 boxes

这里需要注意的是,虽然priorbox的位置是在 W×H 的格子上,但prior box的大小并不是跟格子一样大,而是人工指定的,原论文中随着featuremap从底层到高层,priorbox的大小在0.20.9之间均匀变化。

这里需要注意的是,虽然priorbox的位置是在 W×H 的格子上,但priorbox的大小并不是跟格子一样大,而是人工指定的,原论文中随着feature map从底层到高层,prior box的大小在0.20.9之间均匀变化。


一开始看SSD的时候很困扰我的一点就是形状的匹配问题:SSD用卷积层做bbox的拟合,输出的不应该是feature map吗,怎么能正好输出4个坐标呢?

这里的做法有点暴力,比如需要输出 W×H×length_of_aspect_ratio×4 个坐标,就直接用 length_of_aspect_ratio×4 channel的卷积层做拟合,这样就得到 length_of_aspect_ratio×4 个大小为 W×H feature map,然后把feature map拉成一个长度为 W×H×length_of_aspect_ratio×4 的向量,用SmoothL1之类的loss去拟合,效果还意外地不错……

multibox_loss_layer

FindMatches

我们已经在图上画出了prior box,同时也有了ground truth,那么下一步就是将prior box匹配到ground truth上,这是在 src/caffe/utlis/bbox_util.cpp  FindMatches 函数里完成的。值得注意的是这里不光是给每个groudtruth box找到了最匹配的prior box,而是给每个prior box都找到了匹配的groundtruth box(如果有的话),这样显然大大增大了正样本的数量。

MineHardExamples

给每个prior box找到匹配(包括物体和背景)之后,似乎可以定义一个损失函数,给每个prior box标记一个label,扔进去一通训练。但需要注意的是,任意一张图里负样本一定是比正样本多得多的,这种严重不平衡的数据会严重影响模型的性能,所以对prior box要有所选择。这一步是在src/caffe/utlis/bbox_util.cpp  MineHardExamples 函数里完成的,我没细看= =

EncodeLocPrediction&& EncodeConfPrediction

因为我们对prior box是有选择的,所以数据的形状在这里已经被打乱了,没办法直接在后面连接一个lossCaffe等框架需要每一层的输入是四维张量),所以需要我们把选出来的数据重新整理一下,这一步是在 src/caffe/utlis/bbox_util.cpp  EncodeLocPrediction  EncodeConfPrediction 两个函数里完成的。

训练

Wei Liu等人在这里的实现方法是在multibox_loss_layer里又实例化了两个loss_layer,前向传播的时候需要先经过前面三步,然后把数据手动喂给loss,反向传播的时候也需要手动把残差从loss取出来写回给前面的卷积层。

SSD的特殊之处主要体现在以下3点:

(1)多尺度的特征图检测(Multi-scale),如SSD同时使用了上图所示的8*8的特征图和4*4特征图。

(2)相比于YOLO,作者使用的是卷积层来代替了YOLO的全连接层做预测。(如下图所示)

(3)SSD使用了默认的边界框+(1,2/1,3/1,1/2,1/3)6个框来做检测(aspectratios)

在训练时,本文的 SSD 与那些用 region proposals + pooling 方法的区别是,SSD 训练图像中的 groundtruth 需要赋予到那些固定输出的 boxes 上。在前面也已经提到了,SSD 输出的是事先定义好的,一系列固定大小的 bounding boxes

如下图中,狗狗的 groundtruth 是红色的 bounding boxes,但进行 label 标注的时候,要将红色的 groundtruth box 赋予图(c)中一系列固定输出的 boxes 中的一个,即图(c)中的红色虚线框。 

事实上,文章中指出,像这样定义的 groundtruth boxes 不止在本文中用到。在 YOLO 中,在 FasterR-CNN中的 region proposal阶段,以及在 MultiBox 中,都用到了。

当这种将训练图像中的 groundtruth 与固定输出的 boxes 对应之后,就可以 end-to-end 的进行 loss function 的计算以及 back-propagation 的计算更新了。

训练中会遇到一些问题:

  • 选择一系列 default boxes
  • 选择上文中提到的 scales 的问题
  • hard negative mining
  • 数据增广的策略

下面会谈本文的解决这些问题的方式,分为以下下面的几个部分。 

Matching strategy

如何将 groundtruth boxes default boxes 进行配对,以组成 label 呢?

在开始的时候,用 MultiBox 中的 best jaccard overlap 来匹配每一个 ground truth box default box,这样就能保证每一个 groundtruth box与唯一的一个 default box 对应起来。

但是又不同于 MultiBox ,本文之后又将 default box 与任何的 groundtruth box 配对,只要两者之间的jaccard overlap 大于一个阈值,这里本文的阈值为 0.5 

Training objective

SSD 训练的目标函数(training objective)源自于 MultiBox 的目标函数,但是本文将其拓展,使其可以处理多个目标类别。用 xpij=1 表示  i  default box  类别 p   j  ground truth box 相匹配,否则若不匹配的话,则 xpij=0

根据上面的匹配策略,一定有 ixpij≥1,意味着对于  j  ground truth box,有可能有多个 default box与其相匹配。

总的目标损失函数(objective loss function)就由 localization lossloc  confidence lossconf 的加权求和: 

L(x,c,l,g)=1N(Lconf(x,c)+αLloc(x,l,g))

其中:

  • N 是与 ground truth box 相匹配的 default boxes 个数
  • localization lossloc 是 Fast R-CNN Smooth L1 Loss,用在 predict boxl 与 ground truth boxg 参数(即中心坐标位置,width、height)中,回归 bounding boxes 的中心位置,以及 width、height
  • confidence lossconf 是 Softmax Loss,输入为每一类的置信度 c
  • 权重项 α,设置为 1

·        Choosing scales and aspect ratios fordefault boxes

·        大部分 CNN 网络在越深的层,feature map 的尺寸(size)会越来越小。这样做不仅仅是为了减少计算与内存的需求,还有个好处就是,最后提取的 feature map 就会有某种程度上的平移与尺度不变性。

·        同时为了处理不同尺度的物体,一些文章,如 ICLR 2014, Overfeat: Integratedrecognition, localization and detection using convolutional networks,还有 ECCV2014, Spatial pyramid pooling in deep convolutional networks for visualrecognition,他们将图像转换成不同的尺度,将这些图像独立的通过 CNN 网络处理,再将这些不同尺度的图像结果进行综合。

·        但是其实,如果使用同一个网络中的、不同层上的 feature maps,也可以达到相同的效果同时在所有物体尺度中共享参数

·        之前的工作,如 CVPR 2015, Fully convolutionalnetworks for semantic segmentation,还有 CVPR 2015, Hypercolumns forobject segmentation and fine-grained localization 就用了 CNN 前面的 layers,来提高图像分割的效果,因为越底层的 layers,保留的图像细节越多。文章 ICLR 2016, ParseNet: Lookingwider to see better 也证明了以上的想法是可行的。

·        因此,本文同时使用 lower feature mapsupper feature maps predict detections。下图展示了本文中使用的两种不同尺度的 feature map8×8 feature map,以及 4×4  feature map 

一般来说,一个 CNN 网络中不同的 layers 有着不同尺寸的感受野(receptive fields。这里的感受野,指的是输出的 feature map 上的一个节点,其对应输入图像上尺寸的大小

所幸的是,SSD 结构中,default boxes 不必要与每一层 layer receptive fields 对应。本文的设计中,feature map 中特定的位置,来负责图像中特定的区域,以及物体特定的尺寸。加入我们用 m  feature maps 来做 predictions,每一个 feature map default box 的尺寸大小计算如下: 

sk=smin+smaxsminm−1(k−1),          k[1,m]


其中,smin 取值 0.2smax 取值 0.95,意味着最低层的尺度是 0.2,最高层的尺度是 0.95,再用不同 aspect ratio default boxes,用 ar 来表示:ar={1,2,3,12,13},则每一个 default boxes widthheight 就可以计算出来: 

wak=skar−−√hak=sk/ar−−√


对于 aspect ratio 1 时,本文还增加了一个 default box,这个 box scale  sk=sksk+1−−−−−√。所以最终,在每个 feature map location 上,有 6 default boxes

每一个 default box 的中心,设置为:(i+0.5|fk|,j+0.5|fk|),其中,|fk| 是第 k  feature map 的大小,同时,i,j[0,|fk|)

在结合 feature maps 上,所有不同尺度、不同 aspect ratios default boxes,它们预测的 predictions 之后。可以想见,我们有许多个 predictions,包含了物体的不同尺寸、形状。如下图,狗狗的 ground truth box  4×4 feature map 中的红色 box 吻合,所以其余的 boxes 都看作负样本。 

Hard negativemining

在生成一系列的 predictions 之后,会产生很多个符合 ground truth box predictions boxes,但同时,不符合 ground truth boxes 也很多,而且这个 negative boxes,远多于 positive boxes。这会造成 negative boxespositive boxes 之间的不均衡。训练时难以收敛。

因此,本文采取,先将每一个物体位置上对应 predictionsdefault boxes)是 negative boxes 进行排序,按照 default boxes confidence 的大小。选择最高的几个,保证最后 negativespositives 的比例在 3:1

本文通过实验发现,这样的比例可以更快的优化,训练也更稳定。 

Model

SSD 是基于一个前向传播 CNN 网络,产生一系列 固定大小(fixed-size  bounding boxes,以及每一个 box 中包含物体实例的可能性,即 score。之后,进行一个 非极大值抑制(Non-maximum suppression 得到最终的 predictions

 

SSD模型的最开始部分,本文称作 base network,是用于图像分类的标准架构。在 base network 之后,本文添加了额外辅助的网络结构:

·        Multi-scalefeature maps for detection 
在基础网络结构后,添加了额外的卷积层,这些卷积层的大小是逐层递减的,可以在多尺度下进行 predictions

·        Convolutionalpredictors for detection 
每一个添加的特征层(或者在基础网络结构中的特征层),可以使用一系列 convolutional filters,去产生一系列固定大小的 predictions,具体见 Fig.2。对于一个大小为 m×n,具有 p 通道的特征层,使用的convolutional filters 就是 3×3×p  kernels。产生的 predictions,要么就是归属类别的一个得分,要么就是相对于 default box coordinate shape offsets 
在每一个 m×n 的特征图位置上,使用上面的 3×3  kernel,会产生一个输出值。boundingbox offset 值是输出的 default box 与此时 feature map location 之间的相对距离(YOLO 架构则是用一个全连接层来代替这里的卷积层)。

·        Defaultboxes and aspect ratios 
每一个 box 相对于与其对应的feature map cell 的位置是固定的。在每一个 feature map cell 中,我们要 predict 得到的 box  default box 之间的 offsets,以及每一个 box 中包含物体的 score(每一个类别概率都要计算出)。 
因此,对于一个位置上的 k boxes 中的每一个 box,我们需要计算出 c 个类,每一个类的score,还有这个 box 相对于 它的默认 box  4 个偏移值(offsets)。于是,在 featuremap 中的每一个feature map cell 上,就需要有 (c+4k  filters。对于一张 m×n 大小的 feature map,即会产生 (c+4k×m×n 个输出结果。

这里的 default box 很类似于 FasterR-CNN 中的 Anchor boxes,关于这里的 Anchor boxes,详细的参见原论文。但是又不同于 Faster R-CNN 中的,本文中的 Anchor boxes 用在了不同分辨率的 feature maps 上。

Experimental Results

Base network and hole filling algorithm

本文的 Base network 是基于 ICLR 2015, VGG16 来做的,在 ILSVRC CLS-LOC 数据集上进行了预训练。

 ICLR 2015, DeepLab-LargeFOV 的工作类似,本文将 VGG 中的 FC6 layerFC7 layer 转成为卷积层,并从模型的 FC6FC7 上的参数,进行采样得到这两个卷积层的 parameters

还将 Pool5 layer 的参数,从 2×2−s2 转变成 3×3−s1,外加一个 pad1),如下图: 


但是这样变化后,会改变感受野(receptive field)的大小。因此,采用了 atrous algorithm 的技术,这里所谓的 atrousalgorithm,我查阅了资料,就是 holefilling algorithm(孔填充算法)

DeepLab 的主页上:http://liangchiehchen.com/projects/DeepLab.html,有一张如下的图: 

博客 1http://www.cnblogs.com/jianyingzhou/p/5386222.html

最早用的就是 deeplab 的文章了,Semantic Image Segmentation with Deep Convolutional Nets and FullyConnected CRFS 这篇文章和 fcn 不同的是,在最后产生 score map 时,不是进行upsampling,而是采用了 hole algorithm,就是在 pool4 pool 5层,步长由 2 变成 1,必然输出的 score map 变大了,但是 receptive field 也变小了,为了不降低 receptive field,怎么做呢?利用 hole algorithm,将卷积 weights 膨胀扩大,即原来卷积核是 3x3,膨胀后,可能变成 7x7 了,这样 receptive field 变大了,而 score map 也很大,即输出变成 dense(密集)的了。

这么做的好处是,输出的 scoremap 变大了,即是 dense 的输出了,而且 receptive field 不会变小,而且可以变大。这对做分割、检测等工作非常重要。

 

keras卷积补零相关的border_mode的选择以及padding的操作

1、 keras卷积操作中border_mode的实现

总结:如果卷积的方式选择为same,那么卷积操作的输入和输出尺寸会保持一致。如果选择valid,那卷积过后,尺寸会变小。

 代码解读

num_priors = 6

x =Dense(num_priors * 4, name='pool6_mbox_loc_flat')(net['pool6'])

即为x= Dense(24,name='pool6_mbox_loc_flat')(net['pool6'])

24为该层输出维度,模型中非首层的全连接层其输入维度可以自动推断,在此处为首层全连接层

num_classes=21

name ='pool6_mbox_conf_flat'

x =Dense(num_priors * num_classes, name=name)(net['pool6'])

即为:

num_classes=21

name ='pool6_mbox_conf_flat'

x = Dense(126,name=name)(net['pool6'])

Flatten

keras.layers.core.Flatten()

Flatten层用来将输入压平,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。Flatten不影响batch的大小。

例子

model= Sequential()

model.add(Convolution2D(64, 3, 3,

            border_mode='same',

            input_shape=(3, 32, 32)))

# now: model.output_shape == (None, 64, 32, 32)

 

model.add(Flatten())

# now: model.output_shape == (None, 65536)


Reshape

keras.layers.core.Reshape(target_shape)

Reshape层用来将输入shape转换为特定的shape

参数

·        target_shape:目标shape,为整数的tuple,不包含样本数目的维度(batch大小)

输入shape

任意,但输入的shape必须固定。当使用该层为模型首层时,需要指定input_shape参数

输出shape

(batch_size,)+target_shape

例子

# as first layer in a Sequential model

model= Sequential()

model.add(Reshape((3, 4), input_shape=(12,)))

# now: model.output_shape == (None, 3, 4)

# note: `None` is the batch dimension

 

# as intermediate layer in a Sequential model

model.add(Reshape((6, 2)))

# now: model.output_shape == (None, 6, 2)

 

# also supports shape inference using `-1` as dimension

model.add(Reshape((-1, 2, 2)))

# now: model.output_shape == (None, 3, 2, 2)

数据增强

主要函数:ImageDataGenerator 实现了大多数上文中提到的图像几何变换方法.

·        rotation_range: 旋转范围, 随机旋转(0-180);

·        width_shift and height_shift: 随机沿着水平或者垂直方向,以图像的长宽小部分百分比为变化范围进行平移;

·        rescale: 对图像按照指定的尺度因子, 进行放大或缩小, 设置值在0 - 1之间,通常为1 / 255;

·        shear_range: 水平或垂直投影变换参考这里 https://keras.io/preprocessing/image/

·        zoom_range: 按比例随机缩放图像尺寸;

·        horizontal_flip: 水平翻转图像;

·        fill_mode: 填充像素, 出现在旋转或平移之后.

Input=imag  ->output:为Box coordinate(4 numbers)这是卷积神经网络预测的

                     Correct output (4 numbers)

两者比较,得出Loss为欧氏距离去算差异值

有准确的label值,groundtruth是给定的标签,它有(X,Y,W,H).使得卷积神经网络预测出的框不断拟合给定输入,我们做的事,就是不断减小loss值。

SGD反向传播,不断改变参数,直到最后,神经网络预测的框非常接近groudtruth的值。

这样使得框能准确框。

以上过程叫做location(回归)的训练过程

Classification和

最终的final conv feature map要利用到高维特征,把它给到两个模块上

一个是用于class scores ,概率

一个是Box coordinate,坐标

网络训练时,分类用softmax计算loss

计算坐标loss用欧氏距离

Regression head(回归模块)加在哪里?

1种加在最后一,积之后,VGG

一种加在全连接层之后,R-CNN

实际试一下,进行对比,才知道那个位置最合适。

Sliding window(滑动窗口)

不断滑动窗口,来进行拟合

Network input为3*221*221,滑动拟合Largerimage为3*257*257

Scale变换之后,相当于金字塔

Region proposal(区域建议)

可以用selective search进行先框出一部分,即纹理相同的框起来,进行合并,框出一部分典型的框,对于R-CNN一幅图可以框出2000个框,对固化的特征进行分类。

R-CNN将最后一层改变了,将池化层最后获取的特征固化到磁盘上

R-CNN训练,用SVM训练分类器

FastRCNN改进:实现了权重共享,缺点:找框太费时间了

FasterRCNN改进主要在test上时间的提升,在卷积之后找框,采用Region proposal network(RPN)的方法,所有的训练分类都是在卷积上面完成的。

最大的改进,在测试时对于一个3*3大小的滑动框,可以生成9个anchor(候选框),在卷积之后提取框了之后进行分类或者回归。

不管是任何时候的机器学习,它的特征图大小要一样,采用Rol pooling将9个框弄成一样大小的。

Data augmentation

Horizon flips水平翻转、Random crops/scales(随机裁剪、缩放)、translation(平移)、rotation(角度变换,对整张图片进行进行小角度的旋转)、stretching、shearing

在做数据增强时,所有的方法一起进行,随机进行组合 ,使得数据没有特征可循

数据量大约是原来的10倍

Transfer learning

当时数据量达不到要求时,把别人训练好的模型拿过来借用一下。

猜你喜欢

转载自blog.csdn.net/tumi678/article/details/77335992