ssd是经典的one-stage目标检测算法,作者是基于caffe来实现的,这需要加入新的层来完成功能,caffe自定义层可以使用python和c++,faster rcnn既使用了c++定义如smoothl1layer,又使用了python定义,如proposaltargetlayer、roidatalayer等。而ssd完全使用c++来定义层,包括:
1)annotateddatalayer数据读取层,用于读取图像和标签数据,并且支持数据增强
2)permutelayer用于改变blob的读取顺序
3)priorboxlayer用于生成defalutbox
4)multiboxlosslayer,边框回归集成了l2loss、smoothl1,于分类集成了softmax和logistic,并且支持困难样本挖掘、defaultbox匹配等功能。
name: "mbox_loss"
type: "MultiBoxLoss"
bottom: "mbox_loc"
bottom: "mbox_conf"
bottom: "mbox_priorbox"
bottom: "label"
top: "mbox_loss"
整个网络比较繁杂,不过相对于fasterrcnn算简单一些,我们从loss顺藤摸瓜,首先查看label的存储格式,这需要查看annotateddatalayer层是如何对数据进行读取的,查看网络定义文件发现数据读取层的输入要求是lmdb或leveldb格式,那么问题来了,如何将voc格式的数据制作成lmdb呢?原生caffe好像只支持分类的数据制作,github上说通过./data/VOC0712/create_list.sh和./data/VOC0712/create_data.sh来制作数据,查看create_data.sh文件,里面又调用了$root_dir/scripts/create_annoset.py,继续查看create_annoset.py 发现最终调用的是/build/tools/convert_annoset进而查看tools/convert_annoset.cpp,里面实现了将分散的文件转换为lmdb格式,但是依然还没找到哪里解析了voc格式标注的xml文件,convert_annoset.cpp调用了ReadRichImageToAnnotatedDatum函数,终于在utils/io.cpp中找到了读取xml标注文件的实现。数据层的输出有data和label,data的格式是n×3×300×300,而label的格式是n*1×nofboxes*8。每个物体包含了8个信息[item_id, group_label, instance_id, xmin, ymin, xmax, ymax, diff]
,含义是batchsize
个图像中的第
item_id
幅图像中的第
group_label
个类别下的第
instance_id
个目标的坐标为
[xmin, ymin, xmax, ymax]
。
loss
层的
label
输入格式清楚了,接下来解析
mbox_priorbox
的格式,
mbox_priorbox
是由多个
priorbox
层
concat
起来的,
ssd300
在
6
个
featuremap
上生成了
priorbox
,例如在
conv4_3
的
featuremap
上的
priorbox
数量为
w×h×numprior,
其存储方式为
chw
,维度为
2×1*(w×h×numprior*4)
,可以看出是
3
维的,而不是
nchw
,这是因为一个
batch
的图像大小是相同的,为它们生成的
priorbox
也是完全相同的,所以不需要
batch
这个维度,另外注意
channel
维度是
2,
是存入了对应位置的方差
varirance
。多层的
priorbox
数据
concat
起来时,
axis
设为
2,
也就是在
w
的维度将
bloob
接起来形成一个如图所示长条。
接着解析
mbox_loc
的维度,以大小为
38×38
的
conv4_3
的
fmap
为例,
3*3
的卷积在
fmap
上卷积得到
bsize×16×38×38
的
map
,注意通道数量是
16,
在
38*38
的空间位置上,每个空间位置对应
16
个数,而这
16
个数就是对
4
个
priorbox
也就是
16
个点的回归,但是麻烦的是
priorbox
是被拉成了
1
维向量(不考虑
variance
的话),暂时不考虑
bsize
的话,那如何将
16×38×38
的
blob
拉成与
priorbox
对应的
1
维向量呢?直接
flatten
的话将按照
whc
从低到高的顺序
flatten
,这是不行的,需要按照
cwh
的顺序
flatten
才能与
priorbox
的存储方式对应得上,于是
ssd
定义了
permutelayer
将
blob
的
nchw
顺序改为
nhwc
,然后进行
flatten
即可。这里需要注意
flatten
的用法,文件指定从
1
号
axis
开始
flatten
,也就是从
permute
过后
hwc
维
flatten
成
1
维,整体
flatten
过后的维度为
n×(h*w*c)
在
conv4
也就是
bsize×23104,
四维
blob
变成了
2
维,如果对
flatten
有疑惑可直接查看源码。各层的二维
blob
再继续
concat
,第
1
维是
bsize
,那么肯定在第
2
维上进行
concat
,所以
mbox_loc
和
mbox_conf
层拼接的
axis
都设为
1
。所以
mbox_loc
层
blob
的维度最终为
bsize×27130
。
mbox_conf
层的维度与
mbox_loc
层维度的推算类似,
3*3
的卷积在
fmap
上卷积得到
bsize×84×38×38
的
map,
其中
84=4×21,4
是每个位置
4
个不同的
priorbox
,
21
代表了卷积网络预测出该
priorbox
属于
21
类的概率。后续计算方式参考
mbox_loc
即可
。
p { margin-bottom: 0.1in; line-height: 120% }
code.cjk { font-family: "Nimbus Mono L", monospace }
原文地址:https://www.cnblogs.com/anti-tao/p/9991334.html