前言
一时兴起和好朋友一起组队参加了Datawhale的OD小项目,索性也算是第一次做CV的项目了,顺便练练pytorch和神经网络的内容。这是第二次任务的博客。
一、锚框
锚框就是先验框。之前提到目标检测的任务分为两步,识别+标定。我们需要遍历图片上每一个可能的目标框,在对这些框进行分类和微调。
1. 设置不同尺度的先验框
为了覆盖更多的情况,在图中的同一位置,会设置几个不同尺度的先验框。这个很好理解,增大覆盖面。那么什么样的框叫做良好的先验框呢,很简单,回到上一节讲到的一个OD任务中非常重要的指标,就是交并比IoU了,交并比越高,说明我们的先验框的匹配度越高。
2. 先验框与特征图的对应
通过先验框和特征图这种一一对应的映射关系,可以通过特征图一次性的输出所有先验框的类别信息以及坐标信息。
3. 先验框类别信息的确定
设定一个IoU阈值,与图片中目标的IoU小于阈值的先验框划分为背景,大于阈值的划分为目标先验框
4. 先验框生成算法
- 编译特征图上每一个cell,并进行归一化,这叫做基准框
- 根据在每个cell上设置的长宽比为1:1的基准框,几个三种设hi真的尺度和三种长宽比可以得到9个先验框
- 返回这些先验框
来看一看代码,VGG16网络最后生成的特征图的尺寸为 7 × 7 7 \times 7 7×7,每一个cell上有9种不同的候选框。
def create_prior_boxes():
fmap_dims = 7
obj_scales = [0.2, 0.4, 0.6]
aspect_ratios = [1., 2., 0.5]
prior_boxes = []
for i in range(fmap_dims):
for j in range(fmap_dims):
cx = (j + 0.5) / fmap_dims #将坐标移到中心
cy = (i + 0.5) / fmap_dims
for obj_scale in obj_scales:
for ratio in aspect_ratios:
prior_boxes.append([cx, cy, obj_scale * sqrt(ratio), obj_scale / sqrt(ratio)])
prior_boxes = torch.FloatTensor(prior_boxes).to(device) # (441, 4)
prior_boxes.clamp_(0, 1) # (441, 4)
return prior_boxes
来看看代码生成的效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
需要注意,刚刚我们使用了prior.clamp_(0,1)
是为了防止越界问题,比如上图中的蓝框,所以我们需要将其截断。
二、模型结构
这部分我们就来看看这次的OD任务种的网络和模型。
1. 特征提取模块
我们采用VGG16作为特征提取的模块,模型参数如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKBK1saJ-1608375237954)(3-17.png)]
我们可以用Pytorch定义出这个网络,pytorch中,所有的神经网络都继承自nn.Module
类,定义类的时候,可以在__init__()
函数中定义网络结构,在forward()
函数中定义前向传播的过程
import torch
import torch.nn as nn
import torch.nn.functional as F # 激活函数
class VGGBase(nn.Module):
def __init__(self):
#首先找到VGGBase的父类(nn.module),然后把类VGGBase的对象self转换为类nn.module的对象,然后“被转换”的类nn.module对象调用自己的init函数
super(VGGBase,self).__init__()
###### 下面逐层定义这个网络 ######
'''
输入为224 * 224 * 3
'''
## Conv2d(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True))
self.conv1_1 = nn.Conv2d(3,64,3,1)
self.conv1_2 = nn.Conv2d(64, 64, kernel_size=3, padding=1) # 224 * 224 * 64
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2) # 112 * 112 * 64
self.conv2_1 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.conv2_2 = nn.Conv2d(128, 128, kernel_size=3, padding=1) # 112 * 112 * 128
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) # 56 * 56 * 128
self.conv3_1 = nn.Conv2d(128, 256, kernel_size=3, padding=1)
s