中建西部建设北方有限公司网站,企业网站建设注意,做网站的,网上网站建设教程物体检测算法
1.R-CNN 首先使用启发式搜索算法来选择锚框#xff0c;使用预训练模型对每个锚框抽取特征#xff0c;训练一个SVM来对类别分类#xff0c;最后训练一个线性回归模型来预测边缘框偏移。
R-CNN比较早#xff0c;所以使用的是SVM
1.1 兴趣区域(RoI)池化…物体检测算法
1.R-CNN 首先使用启发式搜索算法来选择锚框使用预训练模型对每个锚框抽取特征训练一个SVM来对类别分类最后训练一个线性回归模型来预测边缘框偏移。
R-CNN比较早所以使用的是SVM
1.1 兴趣区域(RoI)池化层
给定一个锚框均匀分割成 n × m n\times m n×m块输出每块里的最大值不管锚框多大总是输出 n m nm nm个值。 3 × 3 3\times 3 3×3不好被 2 × 2 2\times 2 2×2均分所以会取一下整。
1.2 Fast RCNN
R-CNN每张图片都要抽取一次特征如果每张图片锚框很多就可能要抽取很多次特征很麻烦Fast RCNN在次基础上做快
使用CNN对整张图片抽取特征再使用RoI池化层对每个锚框生成固定长度的特征 就是先抽取特征后将原图的锚框按比例的在特征图中找出锚框然后再做。CNN这一层不对每个锚框抽取特征而是对整个图片抽取特征那么对于锚框重复的地方就只用抽取一次了变快了很多。
1.3 Faster R-CNN
使用一个区域提议网络来替代启发式搜索来获得更好的锚框 大概就是训练一个神经网络判断这些锚框是不是框住了(一个二分类问题)如果框住了与真实边界框的偏移是多少训练好后会输出比较好的锚框。先做一个粗糙的预测再做一个精准的预测。
1.4 Mask R-CNN 其余部分和Faster R-CNN相同新增了一个对像素的神经网络假设有每个像素的编号可以对像素进行预测。并且将pooling改为了align对像素分类更准确得到的是一个加权而不是简单的切割。
R-CNN是最早、也是最有名的一类基于锚框和CNN的目标检测算法。Faster R-CNN和Mask R-CNN是在追求最高精度场景下的常用算法并且Mask R-CNN需要每个像素的标号会有一些限制
2.单发多框检测 (SSD)
对于每个像素生成多个以它为中心的锚框(上一节的生成锚框方法) 首先使用一个基础网络块来抽取特征然后使用多个卷积层块来减半高宽在每个阶段都生成锚框底部段来拟合小物体顶部段来拟合大物体对每个锚框都预测类别和真实边缘框
接近顶部的多尺度特征图较小但具有较大的感受野它们适合检测较少但较大的物体。 简而言之通过多尺度特征块单发多框检测生成不同大小的锚框并通过预测边界框的类别和偏移量来检测大小不同的目标因此这是一个多尺度目标检测模型。
SSD通过单神经网络来检测模型以每个像素为中心产生多个锚框在多个段的输出上进行多尺度的检测。
2.1 多尺度目标检测
动机是减少图像上的锚框数量可以在输入图像中均匀采样一小部分像素并以它们为中心生成锚框。在不同尺度下我们可以生成不同数量和不同大小的锚框。
因为一般来说较小的物体在图像中出现的可能性更多样例如 1 × 1 , 1 × 2 , 2 × 2 1\times1,1\times2,2\times2 1×1,1×2,2×2的目标分别以4、2和1种可能的方式出现在 2 × 2 2\times 2 2×2图像上。那么当检测较小的物体时可以采样更多的区域对于较大的物体可以采样较少的区域。
import torch
from d2l import torch as d2limg d2l.plt.imread(../img/catdog.jpg)
h, w img.shape[:2]
print(h, w)def display_anchors(fmap_w, fmap_h, s):d2l.set_figsize()# 前两个维度上的值不影响输出fmap torch.zeros((1, 10, fmap_h, fmap_w))# multibox_prior的data是四维的anchors d2l.multibox_prior(fmap, sizess, ratios[1, 2, 0.5]) # 生成多个锚框形状为(1,num_anchors,4)bbox_scale torch.tensor((w, h, w, h))d2l.show_bboxes(d2l.plt.imshow(img).axes,anchors[0] * bbox_scale)#小锚框检测小目标
display_anchors(fmap_w4, fmap_h4, s[0.15]) # 分成4 *4 的区域
d2l.plt.show()
#大锚框检测大目标
display_anchors(fmap_w2, fmap_h2, s[0.4]) # 分成4 *4 的区域
d2l.plt.show()2.2 SSD
具体请看注释
import torch
import torchvision
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l类别预测层索引为i(q1)j的通道代表了索引为i的锚框有关类别索引为j的预测def cls_predictor(num_inputs, num_anchors, num_classes):# num_inputs 是输入的像素点个数对每个像素都要预测return nn.Conv2d(num_inputs, num_anchors * (num_classes 1),kernel_size3, padding1) # 加1是因为还要预测背景类,对于每个锚框都要进行分类所以输出通道有这么多边界框预测层(bound box)为每个锚框预测4个偏移量(x,y,w,h)上的偏移def bbox_predictor(num_inputs, num_anchors):return nn.Conv2d(num_inputs, num_anchors * 4, kernel_size3, padding1)连接多尺度的预测def forward(x, block):return block(x)# 举个例子
# 分别生成5*101 55 个和 3*(101)33个锚框输出形状是(批量大小通道数高度宽度)
Y1 forward(torch.zeros((2, 8, 20, 20)), cls_predictor(8, 5, 10))
Y2 forward(torch.zeros((2, 16, 10, 10)), cls_predictor(16, 3, 10))
print(Y1.shape, Y2.shape)# 把4 D转换成2D的
# permute将维度调整将通道数挪到最后然后从dim1开始拉平即后三维拉平
# 把通道数放到最后是为了让预测值连续好用一些可以想象一下
# 将通道数放在第三维那么纵深就是通道每个平面是(高宽)拉平是每个平面每个平面的拉平这样才是连续的。
def flatten_pred(pred):return torch.flatten(pred.permute(0, 2, 3, 1), start_dim1)# 拉平后连接20 * 20 *55 10 * 10 *33 25300
def concat_preds(preds):return torch.cat([flatten_pred(p) for p in preds], dim1)print(concat_preds([Y1, Y2]).shape)高宽减半块def down_sample_blk(in_channels, out_channels):blk []for _ in range(2):blk.append(nn.Conv2d(in_channels, out_channels,kernel_size3, padding1)) # 高宽不变blk.append(nn.BatchNorm2d(out_channels))blk.append(nn.ReLU())in_channels out_channelsblk.append(nn.MaxPool2d(2)) # 高宽减半return nn.Sequential(*blk)# 示例20*20 减半为 10*10
print(高宽减半块, forward(torch.zeros((2, 3, 20, 20)), down_sample_blk(3, 10)).shape)基本网络块# 该网络块输入图像的形状为256*256,输出的特征图为32*32
def base_net():blk []num_filters [3, 16, 32, 64] # 输入是3个维度然后增加到16,32,64for i in range(len(num_filters) - 1):blk.append(down_sample_blk(num_filters[i], num_filters[i 1])) # 每个块高宽减半有三个减8倍return nn.Sequential(*blk)print(基本网络块, forward(torch.zeros((2, 3, 256, 256)), base_net()).shape)完整的模型# 5个模块每个模块既用于生成锚框又用于预测这些锚框的类别和偏移量
# 第一个是基本网络块第2到4个都是高宽减半块最后一个使用全局最大池化将高度和宽度都降到1
# 可以自己找其他神经网络比如resnet将down_sample_blk换成resnet?
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块前向传播# 与图像分类任务不同此处的输出包括CNN特征图Y当前尺度下根据Y生产的锚框预测的这些锚框的类别和偏移量(基于Y)
def blk_forward(X, blk, size, ratio, cls_predictor, bbox_predictor):Y blk(X)anchors d2l.multibox_prior(Y, sizessize, ratiosratio) # 生成锚框cls_preds cls_predictor(Y) # 类别预测不需要把锚框传进去只需要知道有多少个锚框多少个类就行bbox_preds bbox_predictor(Y) # 边界框预测return (Y, anchors, cls_preds, bbox_preds)# 超参数有5个层size逐渐增加,实际面积 s^2 *原图面积 第二个值是几何平均
# 0.272 \sqrt{0.2 *0.37}
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完整的模型
class TinySSD(nn.Module):def __init__(self, num_classes, **kwargs):super(TinySSD, self).__init__(**kwargs)self.num_classes num_classesidx_to_in_channels [64, 128, 128, 128, 128] # 每个块的输出通道数for i in range(5):# 即赋值语句self.blk_iget_blk(i)# 设定属性值有属性.blk_0,.cls_0,.bbox_0等一系列属性了setattr(self, fblk_{i}, get_blk(i))setattr(self, fcls_{i}, cls_predictor(idx_to_in_channels[i],num_anchors, num_classes))setattr(self, fbbox_{i}, bbox_predictor(idx_to_in_channels[i],num_anchors))def forward(self, X):anchors, cls_preds, bbox_preds [None] * 5, [None] * 5, [None] * 5for i in range(5):# getattr(self,blk_%d%i)即访问self.blk_i获取这一属性的值X, anchors[i], cls_preds[i], bbox_preds[i] blk_forward(X, getattr(self, fblk_{i}), sizes[i], ratios[i],getattr(self, fcls_{i}), getattr(self, fbbox_{i}))anchors torch.cat(anchors, dim1) # 输出的后三个都是三维的第一个都是类似批量大小的# 将预测结果全部连接起来cls_preds concat_preds(cls_preds)# reshape成(输出通道数anchors类别)-1就表示由其他参数决定因为我们想预测类别# 重构成这样方便读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_predsnet TinySSD(num_classes1)
X torch.zeros((32, 3, 256, 256))
anchors, cls_preds, bbox_preds net(X)print(output anchors:, anchors.shape)
print(output class preds:, cls_preds.shape)
print(output bbox preds:, bbox_preds.shape)2.3 训练模型
具体看注释
训练模型# 读取数据和初始化
batch_size 32
train_iter, _ d2l.load_data_bananas(batch_size)device, net torch_directml.device(), TinySSD(num_classes1)
trainer torch.optim.SGD(net.parameters(), lr0.2, weight_decay5e-4)损失和评价函数cls_loss nn.CrossEntropyLoss(reductionnone)
bbox_loss nn.L1Loss(reductionnone) # 当预测特别差时L1也不会特别大如果用L2可能会特别大def calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels, bbox_masks):batch_size, num_classes cls_preds.shape[0], cls_preds.shape[2]cls cls_loss(cls_preds.reshape(-1, num_classes),cls_labels.reshape(-1)).reshape(batch_size, -1).mean(dim1)bbox bbox_loss(bbox_preds * bbox_masks,bbox_labels * bbox_masks).mean(dim1) #mask表示对应背景时取0不算损失了return cls bbox # 损失值就是锚框类别的损失值加上偏移量的损失def cls_eval(cls_preds, cls_labels):# 由于类别预测结果放在最后一维argmax需要指定最后一维。return float((cls_preds.argmax(dim-1).type(cls_labels.dtype) cls_labels).sum())def bbox_eval(bbox_preds, bbox_labels, bbox_masks):return float((torch.abs((bbox_labels - bbox_preds) * bbox_masks)).sum())num_epochs, timer 20, d2l.Timer()
animator d2l.Animator(xlabelepoch, xlim[1, num_epochs],legend[class error, bbox mae])
net net.to(device)
for epoch in range(num_epochs):# 训练精确度的和训练精确度的和中的示例数# 绝对误差的和绝对误差的和中的示例数metric d2l.Accumulator(4)net.train()for features, target in train_iter:timer.start()trainer.zero_grad()X, Y features.to(device), target.to(device)# 生成多尺度的锚框为每个锚框预测类别和偏移量anchors, cls_preds, bbox_preds net(X)# 为每个锚框标注类别和偏移量Y是真实标签bbox_labels, bbox_masks, cls_labels d2l.multibox_target(anchors, Y)# 根据类别和偏移量的预测和标注值计算损失函数l calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels,bbox_masks)l.mean().backward()trainer.step()metric.add(cls_eval(cls_preds, cls_labels), cls_labels.numel(),bbox_eval(bbox_preds, bbox_labels, bbox_masks),bbox_labels.numel()) # 累加器记录(预测正确数总预测数)cls_err, bbox_mae 1 - metric[0] / metric[1], metric[2] / metric[3]animator.add(epoch 1, (cls_err, bbox_mae))
print(fclass err {cls_err:.2e}, bbox mae {bbox_mae:.2e})
print(f{len(train_iter.dataset) / timer.stop():.1f} examples/sec on f{str(device)})d2l.plt.show()
cpu训练(因为是A卡后面会说为什么要用cpu) 使用torch_directml有问题似乎是某个操作不支持(repeat_interleave和AdaptiveMaxPool2d)只能在CPU上计算导致训练结果非常差但应该只影响训练时间啊不是很明白 UserWarning: The operator ‘aten::repeat_interleave.Tensor’ is not currently supported on the DML backend and will fall back to run on the CPU. This may have performance implications. (Triggered internally at D:\a_work\1\s\pytorch-directml-plugin\torch_directml\csrc\dml\dml_cpu_fallback.cpp:17.) out_grid torch.stack([shift_x, shift_y, shift_x, shift_y], 3. YOLO
You Only Look Once
SSD中锚框有大量重叠(生成锚框的方法导致的)因此浪费了很多计算YOLO将图片分成 S × S S\times S S×S个锚框每个锚框预测 B B B个边缘框(如果只预测一个可能会丢失某些物体因为可能有多个物体)