Description
If you shed tears when you miss the sun, you also miss the stars.
下面根据d2l一书中的介绍简单记录一下各个网络模型的特点,由于书中详细介绍了一个简易的SSD,因此这篇笔记主要以SSD为主,R-CNN系列概括其思想,YOLO系列书中未提及,内容主要源于网上的分享。
单发多框检测(single shot multibox detection,SSD)简单、快速,并得到了广泛应用。该模型的一些设计思想和实现细节常适用于其他目标检测模型。
首先特征提取网络选用了VGG-16,后面继续使用卷积来缩小特征图以获得多尺度,此外还设计了6个多尺度检测模块来生成锚框。Conv:3x3x(4x(Classes+4))中,3x3是卷积核大小(d2l中padding为1),4为锚框数量,Classes+4中的4为offsets。由此可以看出,SSD在每个类别的探测锚框数为:
38×38×4+19×19×6+10×10×6+5×5×6+3×3×4+1×1×4=8732
锚框主要有两个参数——scale与ratio。在论文中,作者提到,scale由以下公式计算:
sk=smin+m−1smax−smin(k−1),k∈[1,m]
其中m为用于探测的fmap个数,SSD300(上图)中为6,k为当前的fmap索引。不难发现,一开始scale比较小,fmap也比较大,可以用于探测小物体,后面锚框逐渐变大,用于探测大物体。
由于每个fmap的scale是固定的,因此锚框的个数主要由ratio个数决定。作者选取了5个ratio(1,2,3,1/2,1/3),其中在某些fmap丢弃3与1/3的ratio,对于ratio为1的情况,还增加了一个scale=sksk+1的锚框,总共形成6个锚框(部分4个)。为了使锚框中心位于像素中心,计算坐标时还加了0.5的偏置,公式为:
(∣fk∣i+0.5,∣fk∣j+0.5)
其中∣fk∣是第k个fmap的大小,i,j为像素索引,i,j∈[0,∣fk∣)
锚框的标注仍使用IoU最大的先匹配,优先匹配完所有的gt bbox。随后,对于剩余的锚框,若最大IoU大于0.5,也进行相应匹配。匹配的同时标注类别,计算offset。
在模型预测方面,使用特定fmap33same卷积后的结果,设置一定的通道数。设目标的类别个数为q。每个锚框的类别个数将是q+1,其中类别0表示锚框只包含背景。在某个尺度下,设特征图的高和宽分别为h和w,如果以其中每个单元为中心生成a个锚框,那么我们需要对hwa个锚框进行分类。如果使用全连接层作为输出,很容易导致模型参数过多。因此可以使用3x3的same卷积,类别输出通道数为a×(q+1),对应每个像素a个锚框在q+1类的score,offset输出通道数为a×4,对应每个像素a个锚框的4个偏置。
d2l中使用cls_predictor
来预测类别,bbox_predictor
来预测偏置:
def cls_predictor(num_inputs, num_anchors, num_classes):
return nn.Conv2d(num_inputs, num_anchors * (num_classes + 1),
kernel_size=3, padding=1)
def bbox_predictor(num_inputs, num_anchors):
return nn.Conv2d(num_inputs, num_anchors * 4, kernel_size=3, padding=1)
在编程时,卷积后输出为 (batch size, number of channels, height, width),后三个维度可能不同层输出不一样,无法合并结果。d2l中的策略是,将channels(预测结果)调到最后一个维度,然后将这三个维度合并,只留下两个维度,后期使用结果是只需要reshape成(batch size,-1, num_classes + 1)或者(batch size,-1, 4)。第二个维度自然会变成锚框个数。
d2l中,作者实现了一个简易版的SSD模型,代码如下:
sizes = [[0.2, 0.272], [0.37, 0.447], [0.54, 0.619], [0.71, 0.79],
[0.88, 0.961]]
ratios = [[1, 2, 0.5]] * 5
num_anchors = len(sizes[0]) + len(ratios[0]) - 1
def base_net():
blk = []
num_filters = [3, 16, 32, 64]
for i in range(len(num_filters) - 1):
blk.append(down_sample_blk(num_filters[i], num_filters[i+1]))
return nn.Sequential(*blk)
def down_sample_blk(in_channels, out_channels):
blk = []
for _ in range(2):
blk.append(nn.Conv2d(in_channels, out_channels,
kernel_size=3, padding=1))
blk.append(nn.BatchNorm2d(out_channels))
blk.append(nn.ReLU())
in_channels = out_channels
blk.append(nn.MaxPool2d(2))
return nn.Sequential(*blk)
def get_blk(i):
if i == 0:
blk = base_net()
elif i == 1:
blk = down_sample_blk(64, 128)
elif i == 4:
blk = nn.AdaptiveMaxPool2d((1,1))
else:
blk = down_sample_blk(128, 128)
return blk
def blk_forward(X, blk, size, ratio, cls_predictor, bbox_predictor):
Y = blk(X)
anchors = d2l.multibox_prior(Y, sizes=size, ratios=ratio)
cls_preds = cls_predictor(Y)
bbox_preds = bbox_predictor(Y)
return (Y, anchors, cls_preds, bbox_preds)
class TinySSD(nn.Module):
def __init__(self, num_classes, **kwargs):
super(TinySSD, self).__init__(**kwargs)
self.num_classes = num_classes
idx_to_in_channels = [64, 128, 128, 128, 128]
for i in range(5):
# The assignment statement is self.blk_i = get_blk(i)
setattr(self, f'blk_{i}', get_blk(i))
setattr(self, f'cls_{i}', cls_predictor(idx_to_in_channels[i], num_anchors, num_classes))
setattr(self, f'bbox_{i}', bbox_predictor(idx_to_in_channels[i], num_anchors))
def forward(self, X):
anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5
for i in range(5):
# getattr(self, 'blk_%d' % i) accesses self.blk_i
X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward(
X, getattr(self, f'blk_{i}'), sizes[i], ratios[i],
getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}'))
# In the reshape function, 0 indicates that the batch size remains
# unchanged
anchors = torch.cat(anchors, dim=1)
cls_preds = concat_preds(cls_preds)
cls_preds = cls_preds.reshape(cls_preds.shape[0], -1, self.num_classes + 1)
bbox_preds = concat_preds(bbox_preds)
return anchors, cls_preds, bbox_preds
论文中作者提到,模型训练时使用了不同策略的数据增强以防止过拟合。策略主要有随机裁剪、水平翻转、形变等。
模型的损失函数主要有两部分组成,分别为位置损失(localization loss)与类别损失(confidence loss)。
L(x,c,l,g)=N1(Lconf(x,c)+αLloc(x,l,g))
其中,α设为1。Lconf选用交叉熵损失函数,Lloc使用Smooth L1函数:
Lloc(x,l,g)=i∈Pos∑Nm∈{cx,cy,w,h}∑xijksmoothL1(lim−g^jm)g^jcx=(gjcx−dicx)/diwg^jcy=(gjcy−dicy)/dihg^jw=log(diwgjw)g^jh=log(dihgjh)
在进行锚框标注后,可能会出现大量的负样本,造成负样本数量远大于正样本,因此作者使用了Hard negative mining的策略。
对负样本分类时候,loss比较大(label与prediction相差较大)的那些样本,也可以说是容易将负样本看成正样本的那些样本,例如锚框里没有物体,全是背景,这时候分类器很容易正确分类成背景,这个就叫easy negative;如果锚框里有二分之一个物体,标签仍是负样本,这时候分类器就容易把他看成正样本,这时候就是hard negative。
作者把负样本中的Lconf进行排序,只保留loss较大的负样本,使正负样本比例最多为1:3。
总的来说,SSD具有如下主要特点:
目标检测速度是比较快
从YOLO中继承了将detection转化为regression的思路,一次完成目标定位与分类
基于Faster RCNN中的Anchor,提出了相似的Prior box
运用了多尺度的检测方式
锚框的生成依赖于scale与ratio这两个超参数
下面简要概括d2l中对R-CNN的描述。
R-CNN主要由以下4步构成:
对输入图像使用选择性搜索(selective search)来选取多个(2k个)高质量的具有不同的形状和大小提议区域(Region Proposal),并进行标注。
选取一个预训练的卷积神经网络,并通过前向计算输出抽取的提议区域特征。
将每个提议区域的特征连同其标注的类别作为一个样本,训练多个支持向量机对目标分类。其中每个支持向量机用来判断样本是否属于某一个类别。
将每个提议区域的特征连同其标注的边界框作为一个样本,训练线性回归模型来预测真实边界框。
R-CNN虽然通过预训练的卷积神经网络有效抽取了图像特征,但它的主要缺点是速度慢,因为每个提议区域都必须经过CNN提取特征。
Fast R-CNN对R-CNN的一个主要改进在于只对整个图像做卷积神经网络的前向计算。
其中池化层(region of interest pooling)的输出大小是可以指定的,这样可以将不同形状的Region Proposal转化为统一大小,以便于后续的预测。
Fast R-CNN通常需要在选择性搜索中生成较多的提议区域,以获得较精确的目标检测结果。Faster R-CNN提出将选择性搜索替换成区域提议网络(region proposal network),从而减少提议区域的生成数量,并保证目标检测的精度。
YOLO(You Only Look Once)系列与SSD一样采用一步到位的策略(one-stage),没有先产生region proposal再去分类。YOLO系列v1至v3由Joseph Redmon等人提出,后续作者不再更新网络,由Alexey Bochkovskiy提出了v4,另外v5版本存在一定争议,作者没有发表论文,只提供了代码。由于YOLO版本较多,自己了解不深,这里主要收集展示一些自己认为写得比较好的博客。
v1(划分网格,预测坐标类别等):https://blog.csdn.net/lipengfei0427/article/details/103589165
v2(引入anchor box,将最后的全连接层改为全卷积层)、v3(加深网络、多尺度预测、更换logistics loss):https://blog.csdn.net/lipengfei0427/article/details/103726333
v3、v4(总结并提出大量技巧进行实验得出最佳组合):https://blog.csdn.net/nan355655600/article/details/106246625
v5:https://blog.csdn.net/nan355655600/article/details/107852353
v1-v4的发展史:https://zhuanlan.zhihu.com/p/139128562
v3的pytorch实现:https://zhuanlan.zhihu.com/p/137797393
同时附上Redmon论文中V1的网络结构:
This app can be installed on your PC or mobile device. This will allow this web app to look and behave like any other installed app. You will find it in your app lists and be able to pin it to your home screen, start menus or task bars. This installed web app will also be able to safely interact with other apps and your operating system.
If you shed tears when you miss the sun, you also miss the stars.