Faster-RCNN 虽然在效果上做到了 State-Of-The-Art,但效率问题无法做到实时,YOLO 在此基础上提出了改进:
一. Region Proposal
Region Proposal 过程的优点是能够初步检测有效的 Candidate,缺点是带来效率的降低(Faster构造了两级网络)。
YOLO去掉了 Faster的 RPN 过程,直接预测物体的种类和位置。
这是一个里程碑似的 Idea!
二. 直接训练
YOLO 将目标的分类与定位进行合并,将对应位置的信息 通过网络,对应到最后面的 7*7的格子上,每个格子对应一个30维的向量,分别代表 分类与定位信息。对应图中 30维向量 = (B*5 + C),B是每个Grid对应Bound Box数量,Paper中值为2,C为分类数,值为20。
YOLO 网络图参考如下:
三. SSD 剖析
SSD全称是:Single Shot MultiBox Detector,作者是 UNC教堂山的 Wei Liu
论文下载地址:http://www.cs.unc.edu/~wliu/papers/ssd.pdf
1. 开山绝技 - 多尺度
这是对YOLO最大的改进,也是本算法的核心。
YOLO基于最后一张 Feature Map(特征图)进行提取和分类,某些细节特征的丢失导致精度下降和小目标遗漏。
与YOLO不同的是,SSD 在多个尺度(Feature Map)上进行了回归,低层的 feature map 蕴含更多的信息,有利于细节保留 及 训练误差回传,提高了精度及小目标的适应能力。
对于上图(b)(c)所对应 不同分辨率的 Feature Map,同样 3*3 的区域 代表了原图(a)上的目标,蓝色与红色框 的对应关系。
通过多尺度,对检测效果由很大的提升,在不使用 conv4_3的情况下,mAP 下降到了 68.1%。
2. 拿来主义 - 宽高比和Anchor
SSD 借鉴了 Faster的锚 ,假定以目标中心作为Location,以该中心以不同宽高比进行 Rect 扩展(1:1 1:2 2:1),再结合三种不同尺度,这样我们就得到了9种不同的Scale(不考虑太特殊的比例)。
不同的Aspect Ratio & Scale 对预测目标进行覆盖,对于每一个feature map来讲,每个Grid Cell对应多个 Predict Boxes,我们假设为K(对应上面K=9,3种尺度*3种宽高比),所有中心落在该Grid的目标,都由该Grid进行回归Predict。
So 我们需要预测的结果就是:这K个Box每个对应的 分类(概率,C种)和 相对默认Rect的偏移(Offset,4个):
Fature Map Grid => (C+4)*K
对于一张 m*n 大小的 Feature Map,能够得到 m*n * (C+4)*K个输出结果(SSD 中默认使用了 6 个 default boxes)。
3. 各司其责 - Ground Truth与训练样本采集
3.1 Ground Truth 映射
这是与YOLO类似的方法,通过将Ground Truth映射到对应的Grid(参考下图),得到与之最接近的一个Default Box(K个选一个):
我们得到了该目标在对应 Feature Map的Location,以及对应Box的偏移(Offset)。
下图最后一幅即是我们得到的输出结果信息。每一个位代表一个信息(1-20表示20种Class每种类别的Score,Last4 表示当前Rect偏移),是端到端训练的依据。
3.2 加权的目标函数
确定了端到端的对应,我们再来看目标函数。
有个问题要明确:对于 每个 ground truth box,有可能有多个 default box与其相匹配(根据 IOU>0.5 )。
PS:不熟悉IOU者请自行补充知识。
总的目标损失函数(objective loss function)是由 分类误差(confidence loss)和定位误差( localization loss)加权求和得到:
有几个点要说明:
1)L(conf)为分类误差,采用多分类Softmax实现;
“c” for Ground Truth Type。
2)L(loc)为回归定位误差(Rect对应的Offset),类似Fast R-CNN 中 Smooth L1 Loss;
“l” for predict & “g” for Ground Truth。
3)权值α作为两个误差的均衡系数,并没有完全解决分类与定位的问题,还是与YOLO一致;
4)N 即是上面提到的与 ground truth相匹配的 default boxes 个数。
3.3 负样本选取
训练过程中副样本的选取是个技术活,针对Ground Truth的预测结果中,有许多Box是与Ground Truth不match的,通常可以选择这些Box作为副样本。
实际上 负样本的数量要远远多于正样本,容易造成样本比例比均衡,导致训练时难以收敛。针对这个问题,通常将每一个物体位置上对应的负样本进行排序,
只保留最前面的一部分,根据经验值,负样本、正样本的配比通常在 3:1。
4. 奇技淫巧 - Data Augmentation(数据增广)
数据增广 是对训练数据进行某些自定义加工,以提高网络对数据的适应能力,特别是在数据不足的时候,可以借此来避免过拟合的问题。
通常的数据增广的做法是对样本 添加噪声、旋转拉伸、光照明暗、色度处理等。
本例中的做法是对每一张训练图像,随机的进行如下几种选择:
a)使用原始的图像;
b)采样一个 patch,与物体之间最小的 jaccard overlap 为:0.1,0.3,0.5,0.7 与 0.9
c)随机采样一个 patch
采样的 patch 占原始图像大小范围[0.1,1],aspect ratio范围是[0.5,2]。
当 groundtruth box 的 中心(center)在采样的 patch 中时,保留重叠部分。
在这些采样步骤之后,每一个采样的 patch 被 resize 到固定的大小,并且以 0.5 的概率随机的 水平翻转(horizontally flipped)
通过数据增广,本文将 mAP 从 65.4% 提升到了 72.1%,提升了 6.7%。
5. 事实胜于雄辩 - 实验效果
给出性能参考:
下面是根据caffe生成的网络图,可以作为参考,重点看从 conv7_2 到 conv7_2_mbox_loc 和 conv7_2_mbox_conf。
完整的网络图也可以从下面下载:
三. 训练自定义数据集
训练时需要准备以下数据:
1)demo中预训练好的VGGnet model, VGG_ILSVRC_16_layers_fc_reduced.caffemodel
在 $CAFFE_ROOT/models/VGGNet 下。
2)建立原始数据集
a)按照 VOC格式, 采用 labelImage 进行标注; 【Github】
b)在 data/VOCdevkit 目录下创建 自己的目录 zz_test,存放上面的标注数据;
注:需要保留 Annotations(标注) 、ImageSets(图像集合) JPEGImages(图片)三个文件夹。
ImageSets下面只需要 Main文件夹,存放后面训练要用到的 trainval.txt 和 test.txt。
c)在 examples 目录下创建 zz_test 目录,存放指向生成的 lmdb 数据的软链接;
d)在 data 目录下创建 自己的目录 zz_test,存放数据转换命令;
将 data/VOC0712 下的 create_list.sh, create_data.sh, labelmap_voc.prototxt 这三个文件copy到 zz_test 目录下,重命名为:
create_list_zz.sh,create_data_zz.sh, labelmap_zz.prototxt
e)对上面三个文件进行修改;
将 create_list_zz.sh 里的 for name in VOC2007 VOC2012 修改为 for name in zz_test,
修改 create_data_zz.sh
dataset_name="zz_test" mapfile="$root_dir/data/$dataset_name/labelmap_zz.prototxt"修改 labelmap_zz.prototxt,假设只检测车和人。
item { name: "none_of_the_above" label: 0 display_name: "background" } item { name: "car" label: 1 display_name: "car" } item { name: "person" label: 2 display_name: "person" }e)执行创建命令;./data/zz_test/create_list_zz.sh # 对应生成3个文件 trainval.txt test.txt 和 test_name_size.txt ./data/zz_test/create_data_zz.sh到对应文件夹下(examples/zz_test)查看文件是否生成。3)训练脚本ssd_pascal.py
在 $CAFFE_ROOT/examples/SSD 下,可以copy一份,重命名为 ssd_pascal_zz.py,有几个地方要修改:
a) train_data和test_data修改成指向自己的数据集LMDB;
train_data = "examples/zz_test/zz_test_trainval_lmdb"
test_data = "examples/zz_test/zz_test_test_lmdb"
b)修改 name_size_file 和 label_map_file 的路径;
name_size_file = "data/zz_test/test_name_size.txt"
label_map_file = "data/zz_test/labelmap_zz.prototxt"c)修改 num_classes(标签数量+1) 和 num_test_image(测试数据个数);
d)修改 batch_size 为自己的 16,根据自己的GPU性能;
4)执行训练命令python examples/ssd/ssd_pascal_zz.py