YOLOv1

作者: leon_tly | 来源:发表于2022-12-12 11:27 被阅读0次

    YOLOv1

    1. 实现原理
    • 将图像划分为SxS个网格(grid cell),如果某个object的中心点落在这个网格上 ,那么这个网格就负责预测该object。
    • 每个网格要预测B个bounding box,每个bounding box除了要回归自身的位置之外,还要附带预测一个confidence值。
      这个confidence代表了所预测的box中含有object的置信度和这个box预测的有多准两重信息,其值是这样计算的: Pr(object)*IoU_{pred}^{truth}
      其中如果有object落在一个grid cell里,第一项取1,否则取0。 第二项是预测的bounding box和实际的groundtruth之间的IoU值
    • 每个bounding box要预测(x, y, w, h)和confidence共5个值,每个网格还要预测一个类别信息,记为C类。则SxS个网格,每个网格要预测B个bounding box还要预测C个类别。输出就是S x S x (5*B+C)的一个tensor。
      注意:class信息是针对每个网格的,confidence信息是针对每个bounding box的。
    1. YOLOv1的损失函数
      \lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}L_{ij}^{obj}[(x_i - x_{i}^{'})^2+(y_i - y_{i}^{'})^2 ]
      +\lambda_{coord}\sum_{i=0}^{S^2}\sum_{j=0}^{B}L_{ij}^{obj}[(\sqrt{w_i} - \sqrt{w_{i}^{'}})^2 + \sqrt{h_i} - \sqrt{h_{i}^{'}})^2]
      + \sum_{i=0}^{S^2}\sum_{j=0}^{B}L_{ij}^{obj}[(c_i - c_{i}^{'})^2]
      + \lambda_{noobj}\sum_{i=0}^{S^2}\sum_{j=0}^{B}L_{ij}^{noobj}[(c_i - c_{i}^{'})^2]
      + \sum_{i=0}^{S^2}L_{i}^{obj}\sum_{c=0}^{classes_num}(p_i(c) - p_{i}(c)^{'})^2

    \lambda_{coord} = 5, \lambda_{noobj} = 0.5, S=7, B=2
    L_{ij}^{obj} 表示第i个grid cell,第j个bounding box中是否存在目标,存在为1,不存在为0
    L_{ij}^{noobj} 表示第i个grid cell,第j个bounding box中是否存在目标,存在为0,不存在为1
    x_i, y_i, w_i, h_i 预测值 x_{i}^{'}, y_{i}^{'},w_{i}^{'}, h_{i}^{'}真实值
    p_i(c)预测为类别i的概率值, p_{i}(c)^{'} 真实类别为i则为1, 否则为0
    c_i预测confidence, c_{i}^{'} 预测框与ground truth的iou
    L_{i}^{obj}第i个grid cell中是否包括物体,即真实ground truth的中心点是否落在该grid cell中

    1. pytorch loss实现
    import torch
    from torch import  nn
    
    class Loss_yolov1(nn.Module):
        def __init__(self):
            super(self).__init__()  # 修改过
        def forward(self, pred, labels):
            """
            :param pred: (batchsize,30,7,7)的网络输出数据
            :param labels: (batchsize,30,7,7)的样本标签数据
            :return: 当前批次样本的平均损失
            """
            num_gridx, num_gridy = labels.size()[-2:]  # 划分网格数量 取最后两列数据    7*7
            num_b = 2  # 每个网格的bbox数量
            num_cls = 20  # 类别数量
            noobj_confi_loss = 0.  # 不含目标的网格损失(只有置信度损失)
            coor_loss = 0.  # 含有目标的bbox的坐标损失
            obj_confi_loss = 0.  # 含有目标的bbox的置信度损失
            class_loss = 0.  # 含有目标的网格的类别损失
            n_batch = labels.size()[0]  # batchsize的大小
    
            # 可以考虑用矩阵运算进行优化,提高速度,为了准确起见,这里还是用循环
            for i in range(n_batch):  # batchsize循环
                for n in range(7):  # x方向网格循环
                    for m in range(7):  # y方向网格循环
                        if labels[i,4,m,n]==1:# 如果包含物体
                            # 将数据(px,py,w,h)转(换为x1,y1,x2,y2)
                            # 先将px,py转换为cx,cy,即相对网格的位置转换为标准化后实际的bbox中心位置cx,xy
                            # 然后再利用(cx-w/2,cy-h/2,cx+w/2,cy+h/2)转换为xyxy形式,用于计算iou
                            bbox1_pred_xyxy = ((pred[i,0,m,n]+m)/num_gridx - pred[i,2,m,n]/2,(pred[i,1,m,n]+n)/num_gridy - pred[i,3,m,n]/2,
                                               (pred[i,0,m,n]+m)/num_gridx + pred[i,2,m,n]/2,(pred[i,1,m,n]+n)/num_gridy + pred[i,3,m,n]/2)
                            bbox2_pred_xyxy = ((pred[i,5,m,n]+m)/num_gridx - pred[i,7,m,n]/2,(pred[i,6,m,n]+n)/num_gridy - pred[i,8,m,n]/2,
                                               (pred[i,5,m,n]+m)/num_gridx + pred[i,7,m,n]/2,(pred[i,6,m,n]+n)/num_gridy + pred[i,8,m,n]/2)
                            bbox_gt_xyxy = ((labels[i,0,m,n]+m)/num_gridx - labels[i,2,m,n]/2,(labels[i,1,m,n]+n)/num_gridy - labels[i,3,m,n]/2,
                                            (labels[i,0,m,n]+m)/num_gridx + labels[i,2,m,n]/2,(labels[i,1,m,n]+n)/num_gridy + labels[i,3,m,n]/2)
                            iou1 = calculate_iou(bbox1_pred_xyxy,bbox_gt_xyxy)
                            iou2 = calculate_iou(bbox2_pred_xyxy,bbox_gt_xyxy)
                            # 选择iou大的bbox作为负责物体
                            if iou1 >= iou2:
                                coor_loss = coor_loss + 5 * (torch.sum((pred[i,0:2,m,n] - labels[i,0:2,m,n])**2) \
                                            + torch.sum((pred[i,2:4,m,n].sqrt()-labels[i,2:4,m,n].sqrt())**2)) # 含有目标的bbox的坐标损失
                                obj_confi_loss = obj_confi_loss + (pred[i,4,m,n] - iou1)**2
                                # iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou2
                                noobj_confi_loss = noobj_confi_loss + 0.5 * ((pred[i,9,m,n]-iou2)**2)
                            else:
                                coor_loss = coor_loss + 5 * (torch.sum((pred[i,5:7,m,n] - labels[i,5:7,m,n])**2) \
                                            + torch.sum((pred[i,7:9,m,n].sqrt()-labels[i,7:9,m,n].sqrt())**2))
                                obj_confi_loss = obj_confi_loss + (pred[i,9,m,n] - iou2)**2
                                # iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou1
                                noobj_confi_loss = noobj_confi_loss + 0.5 * ((pred[i, 4, m, n]-iou1) ** 2)
                            class_loss = class_loss + torch.sum((pred[i,10:,m,n] - labels[i,10:,m,n])**2)
                        else:  # 如果不包含物体
                            noobj_confi_loss = noobj_confi_loss + 0.5 * torch.sum(pred[i,[4,9],m,n]**2)
    
            loss = coor_loss + obj_confi_loss + noobj_confi_loss + class_loss
            # 此处可以写代码验证一下loss的大致计算是否正确,这个要验证起来比较麻烦,比较简洁的办法是,将输入的pred置为全1矩阵,再进行误差检查,会直观很多。
            return loss/n_batch
    def calculate_iou(bbox1,bbox2):
        """计算bbox1=(x1,y1,x2,y2)和bbox2=(x3,y3,x4,y4)两个bbox的iou"""
        intersect_bbox = [0., 0., 0., 0.]  # bbox1和bbox2的交集
        if bbox1[2]<bbox2[0] or bbox1[0]>bbox2[2] or bbox1[3]<bbox2[1] or bbox1[1]>bbox2[3]:
            pass
        else:
            intersect_bbox[0] = max(bbox1[0],bbox2[0])
            intersect_bbox[1] = max(bbox1[1],bbox2[1])
            intersect_bbox[2] = min(bbox1[2],bbox2[2])
            intersect_bbox[3] = min(bbox1[3],bbox2[3])
    
        area1 = (bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1])  # bbox1面积
        area2 = (bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1])  # bbox2面积
        area_intersect = (intersect_bbox[2] - intersect_bbox[0]) * (intersect_bbox[3] - intersect_bbox[1])  # 交集面积
        # print(bbox1,bbox2)
        # print(intersect_bbox)
        # input()
    
        if area_intersect>0:
            return area_intersect / (area1 + area2 - area_intersect)  # 计算iou
        else:
            return 0
    

    来源 https://github.com/qiujunlin/yolov1/blob/master/loss.py

    相关文章

      网友评论

          本文标题:YOLOv1

          本文链接:https://www.haomeiwen.com/subject/atnqqdtx.html