3输出端
4.1 优化方法
YOLO V5的作者提供了2个优化函数Adam和SGD,并都预设了与之匹配的训练超参数。默认为SGD。YOLO V4使用SGD。
YOLO V5的作者建议是,如果需要训练较小的自定义数据集,Adam是更合适的选择,尽管Adam的学习率通常比SGD低。但是如果训练大型数据集,对于YOLOV5来说SGD效果比Adam好。
实际上学术界上对于SGD和Adam哪个更好,一直没有统一的定论,取决于实际项目情况。
4.2 损失函数
通过上图我们可以看到,对于图中的目标,都会输出class_num+4+1长度的向量,比如针对coco数据集有80个类别,就会输出长度为85的特征向量,其中所包含的内容如下图所示:
图中向量包含4个坐标信息,一个包含目标概率和80个类别得分,换句话解释就是“这个图像中是否有目标(物体出现的概率)?有的话是什么(80类的类别得分)?然后就是这个目标物体在哪里(box坐标位置)?”
其实面对上述的3个输出,也对应YOLOv5的3个分支的,其分别是obj分支、cls分支和box分支。
1、obj分支
obj分支输出的是该anchor中是否含有物体的概率,默认使用BCEWithLogits Loss。
BCEWithLogitsLoss是将BCELoss(BCE:Binary cross entropy)和sigmoid融合了,也就是说省略了sigmoid这个步骤;BCELoss的数学公式如下:
class BCEBlurWithLogitsLoss(nn.Module): # BCEwithLogitLoss() with reduced missing label effects. def __init__(self, alpha=0.05): super(BCEBlurWithLogitsLoss, self).__init__() self.loss_fcn = nn.BCEWithLogitsLoss(reduction='none') # must be nn.BCEWithLogitsLoss() self.alpha = alpha def forward(self, pred, true): loss = self.loss_fcn(pred, true) pred = torch.sigmoid(pred) # prob from logits dx = pred - true # reduce only missing label effects # dx = (pred - true).abs() # reduce missing label and false label effects alpha_factor = 1 - torch.exp((dx - 1) / (self.alpha + 1e-4)) loss *= alpha_factor return loss.mean()
2、cls分支
cls分支输出的是该anchor属于哪一类的概率,也默认使用BCEWithLogits Loss。
class BCEBlurWithLogitsLoss(nn.Module): # BCEwithLogitLoss() with reduced missing label effects. def __init__(self, alpha=0.05): super(BCEBlurWithLogitsLoss, self).__init__() self.loss_fcn = nn.BCEWithLogitsLoss(reduction='none') # must be nn.BCEWithLogitsLoss() self.alpha = alpha def forward(self, pred, true): loss = self.loss_fcn(pred, true) pred = torch.sigmoid(pred) # prob from logits dx = pred - true # reduce only missing label effects # dx = (pred - true).abs() # reduce missing label and false label effects alpha_factor = 1 - torch.exp((dx - 1) / (self.alpha + 1e-4)) loss *= alpha_factor return loss.mean()
例如,对于coco数据集上训练的YOLO的每个anchor的维度都是85,前5个属性是(Cx,Cy,w,h,confidence),confidence对应obj,后80个维度对应cls。
3、box分支
这里的box分支输出的便是物体的具体位置信息了,通过前面对于坐标参数化的分析可以知道,具体的输出4个值为、、以及,然后通过前面的参数化反转方式与GT进行计算loss,对于回归损失,yolov3使用的loss是smooth l1损失。Yolov5的边框(Bounding box)回归的损失函数默认使用的是CIoU,不是GIoU,不是DIoU,是CIoU。
回归损失的发展
下面用一张图粗略看一下IoU,GIoU,DIoU,CIoU:
式中,、,、分别代表候选框的中心点坐标。
下面大概说一下每个IOU损失的局限性:
IoU Loss 有2个缺点:
- 当预测框和目标框不相交时,IoU(A,B)=0时,不能反映A,B距离的远近,此时损失函数不可导,IoU Loss 无法优化两个框不相交的情况。
- 假设预测框和目标框的大小都确定,只要两个框的相交值是确定的,其IoU值是相同时,IoU值不能反映两个框是如何相交的。
GIoU Loss 有1个缺点:
- 当目标框完全包裹预测框的时候,IoU和GIoU的值都一样,此时GIoU退化为IoU, 无法区分其相对位置关系;
DIoU Loss 有1个缺点:
- 当预测框的中心点的位置都一样时, DIoU无法区分候选框位置的质量;
综合IoU、GIoU、DIoU的种种局限性,总结一个好的bounding box regressor包含3个要素:
- 、Overlapping area
- 、Central point distance
- 、Aspect ratio
因此,YOLOv5使用的是CIoU Loss:
iou = bbox_iou(pbox.T, tbox[i], x1y1x2y2=False, CIoU=True) # iou(prediction, target) lbox += (1.0 - iou).mean() # iou loss def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7): # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4 box2 = box2.T # Get the coordinates of bounding boxes if x1y1x2y2: # x1, y1, x2, y2 = box1 b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] else: # transform from xywh to xyxy b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2 # Intersection area inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \ (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0) # Union Area w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps union = w1 * h1 + w2 * h2 - inter + eps iou = inter / union if CIoU or DIoU or GIoU: cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1 c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squared rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center distance squared if CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47 v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2) with torch.no_grad(): alpha = v / (v - iou + (1 + eps)) return iou - (rho2 / c2 + v * alpha) # CIoU return iou - rho2 / c2 # DIoU c_area = cw * ch + eps # convex area return iou - (c_area - union) / c_area # GIoU https://arxiv.org/pdf/1902.09630.pdf return iou # IoU
4、Loss计算
def compute_loss(p, targets, model): # predictions, targets, model device = targets.device lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) tcls, tbox, indices, anchors = build_targets(p, targets, model) # targets h = model.hyp # hyperparameters # Define criteria BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.Tensor([h['cls_pw']])).to(device) BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.Tensor([h['obj_pw']])).to(device) # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 cp, cn = smooth_BCE(eps=0.0) # Focal loss g = h['fl_gamma'] # focal loss gamma if g > 0: BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) 。。。。。。
4.3、后处理之DIoU NMS
在上图重叠的摩托车检测中,中间的摩托车因为考虑边界框中心点的位置信息,也可以回归出来。因此在重叠目标的检测中,DIOU_nms的效果优于传统的nms。
为什么不用CIoU NMS呢?
因为前面讲到的CIOU loss,是在DIOU loss的基础上,添加的影响因子,包含ground truth标注框的信息,在训练时用于回归。但在测试过程中,并没有ground truth的信息,不用考虑影响因子,因此直接用DIOU NMS即可。
4YOLOv5 Lite训练自己的数据集
5.1 git clone仓库代码
clone YOLOv5 Lite代码并下载coco的预训练权重。
$ git clone https://github.com/ppogg/YOLOv5-Lite $ cd YOLOv5-Lite $ pip install -r requirements.txt
5.2 处理数据集格式
这里可以直接参考coco128的数据集形式进行整理:
文件夹目录如下图所示:
5.3 配置超参数
主要是配置data
文件夹下的coco128.yaml
中的数据集位置和种类:
5.4 配置模型
这里主要是配置models
目录下的模型yaml文件,主要是进去后修改nc
这个参数来进行类别的修改。
修改nc
参数
目前支持的模型种类如下所示:
5.3 训练
$ python train.py --data coco.yaml --cfg v5lite-e.yaml --weights v5lite-e.pt --batch-size 128 v5lite-s.yaml --weights v5lite-s.pt --batch-size 128 v5lite-c.yaml v5lite-c.pt 96 v5lite-g.yaml v5lite-g.pt 64
如果您是多卡进行训练,则:
$ python -m torch.distributed.launch --nproc_per_node 2 train.py
5.4 检测结果
$ python path/to/detect.py --weights v5lite-e.pt --source 0 img.jpg # image
检测结果