目录
Shortcut 层用于执行残差连接,将前一层的特征图与当前层的特征图相加。
不多说一句话,直接开撕!!!
一:读取数据
读取coco.data文件
二:初始化模型
接下里继续搭建模型,
卷积后紧跟BN层,V3版本已经丢掉了Dropout层了
Route 层用于将来自不同层的特征图进行连接或拼接。
Shortcut 层用于执行残差连接,将前一层的特征图与当前层的特征图相加。
CBL(conv+BN+Leaky relu)+Res+ResX+上采样下采样
#当前层与前面的第几层做操作。如果layers是第四层,那么就第四层做拼接操作————————做特征融合的(做乘法法)
elif module_def["type"] == "route": # 输入1:26*26*256 输入2:26*26*128 输出:26*26*(256+128)
layers = [int(x) for x in module_def["layers"].split(",")]
filters = sum([output_filters[1:][i] for i in layers])
modules.add_module(f"route_{module_i}", EmptyLayer())
#跟上面的route很像————————做残差网络的(做加法)
elif module_def["type"] == "shortcut":
filters = output_filters[1:][int(module_def["from"])]
modules.add_module(f"shortcut_{module_i}", EmptyLayer())
最重要的一层 yolo层:
这表示只选择了锚框列表中索引为6、7、8的三个锚框用于预测目标框。这个选择是根据训练数据集和目标类别的特点进行的。 yolov3是kmean聚类,他们通过经验和实验,对锚框的尺寸进行了调整和优化,以便在特定任务和数据集上获得更好的性能。这种自定义的锚框设置是基于作者的经验和实践,可能是为了在特定任务中获得更好的结果。
提前设置好了输入的图片是416
进入yolo层的定义,初始化需要的参数
三:初始化完所有有网络层后,开始处理数据
collate_fn是一个函数,用于在数据加载器中将多个样本组合成一个批次。它定义了如何对样本进行分组和处理,以确保组合成的批次具有一致的形状和大小。
collate_fn函数
这个就是利用collate_fn分组后的结果
四:开始训练
首先先加入Darknet的forward函数,将图片传入,输入到定义好的各个网络层,其中重点看yolo层 。
每一层做完后,都需要append到layer_outputs里面存储。
直接跳到看yolo层的训练
打断点,进入到yolo层的forward哈函数:
第一次yolo层,传入的是11*11的图片
4+1+80 grid_size网格的大小(x+y+w+h+confidence+predictor(80个列别的概率 )=85),后面用sigmoid把self.num_classes + 5个类别转成概率,虽然可以直接在 x.view 中完成维度交换,但是将其分成两个步骤可以更好地表达代码的意图,提高可读性和可维护性。这样的做法也更加符合代码编写的规范和风格。
获取这6个变量的信息
最后取出数据保存到pred_boxes中
还原到原始图中,上面先把高宽成了下采样后的,不过这个是保存在output中的。注意:下面传入的pred_boxes值没有改变
先筛选一些iou,再拿这些锚框去跟真实框进行iou计算, 也就是说iou是随便生成的,然后才真正的用筛选好的锚框去跟真实框做iou,
def build_targets(pred_boxes, pred_cls, target, anchors, ignore_thres):
ByteTensor = torch.cuda.ByteTensor if pred_boxes.is_cuda else torch.ByteTensor
FloatTensor = torch.cuda.FloatTensor if pred_boxes.is_cuda else torch.FloatTensor
nB = pred_boxes.size(0)
nA = pred_boxes.size(1)
nC = pred_cls.size(-1)
nG = pred_boxes.size(2)
obj_mask = ByteTensor(nB, nA, nG, nG).fill_(0)
noobj_mask = ByteTensor(nB, nA, nG, nG).fill_(1)
class_mask = FloatTensor(nB, nA, nG, nG).fill_(0)
iou_scores = FloatTensor(nB, nA, nG, nG).fill_(0)
tx = FloatTensor(nB, nA, nG, nG).fill_(0)
ty = FloatTensor(nB, nA, nG, nG).fill_(0)
tw = FloatTensor(nB, nA, nG, nG).fill_(0)
th = FloatTensor(nB, nA, nG, nG).fill_(0)
tcls = FloatTensor(nB, nA, nG, nG, nC).fill_(0)
target_boxes = target[:, 2:6] * nG
gxy = target_boxes[:, :2]
gwh = target_boxes[:, 2:]
ious = torch.stack([bbox_wh_iou(anchor, gwh) for anchor in anchors])
print(ious.shape)
best_ious, best_n = ious.max(0)
b, target_labels = target[:, :2].long().t()
gx, gy = gxy.t()
gw, gh = gwh.t()
gi, gj = gxy.long().t()
obj_mask[b, best_n, gj, gi] = 1
print(obj_mask.shape)
noobj_mask[b, best_n, gj, gi] = 0
for i, anchor_ious in enumerate(ious.t()):
noobj_mask[b[i], anchor_ious > ignore_thres, gj[i], gi[i]] = 0
tx[b, best_n, gj, gi] = gx - gx.floor()
print(tx.shape)
ty[b, best_n, gj, gi] = gy - gy.floor()
tw[b, best_n, gj, gi] = torch.log(gw / anchors[best_n][:, 0] + 1e-16)
print(tw.shape)
th[b, best_n, gj, gi] = torch.log(gh / anchors[best_n][:, 1] + 1e-16)
tcls[b, best_n, gj, gi, target_labels] = 1
print(tcls.shape)
hyt = pred_cls[b, best_n, gj, gi].argmax(-1)
class_mask[b, best_n, gj, gi] = (pred_cls[b, best_n, gj, gi].argmax(-1) == target_labels).float()
iou_scores[b, best_n, gj, gi] = bbox_iou(pred_boxes[b, best_n, gj, gi], target_boxes,
x1y1x2y2=False)
tconf = obj_mask.float()
return iou_scores, class_mask, obj_mask, noobj_mask, tx, ty, tw, th, tcls, tconf
得出来这些值后:
iou_scores:真实值与最匹配的anchor的IOU得分值 class_mask:分类正确的索引 obj_mask:目标框所在位置的最好anchor置为1 noobj_mask obj_mask那里置0,还有计算的iou大于阈值的也置0,其他都为1 tx, ty, tw, th, 对应的对于该大小的特征图的xywh目标值也就是我们需要拟合的值 tconf 目标置信度
五:损失
六:完整代码
视频,笔记和代码,以及注释都已经上传网盘,放在主页置顶文章