yolo v5

news2024/11/27 0:30:30

参考

睿智的目标检测56——Pytorch搭建YoloV5目标检测平台

原理

前处理

网络结构

整体思想

思想框架

特征提取-特征加强-预测先验框对应的物体情况。

改进部分

1、主干部分:使用了Focus网络结构,具体操作是在一张图片中每隔一个像素拿到一个值,这个时候获得了四个独立的特征层,然后将四个独立的特征层进行堆叠,此时宽高信息就集中到了通道信息,输入通道扩充了四倍。该结构在yolov5第5版之前有所应用,最新版本中未使用。

2、数据增强:Mosaic数据增强、Mosaic利用了四张图片进行拼接实现数据中增强,根据论文所说其拥有一个巨大的优点是丰富检测物体的背景!且在BN计算的时候一下子会计算四张图片的数据!

3、多正样本匹配:在之前的Yolo系列里面,在训练时每一个真实框对应一个正样本,即在训练时,每一个真实框仅由一个先验框负责预测。YoloV5中为了加快模型的训练效率,增加了正样本的数量,在训练时,每一个真实框可以由多个先验框负责预测。

具体结构

对比参考1中的源码看,效果更佳。这里的输入输出以yolov5-l为例。v5有四种体量,分别为s,m,l,x,区别在于他们中间的维度以及CSP模块的叠加深度,width表示维度例如经过颈部结构输出分别为32,40,64,80;depth为CSP模块的叠加层数,第二个虚线框的叠加层数分别为1,2,3,4。

Backbone

(640, 640, 3)-->(80,80,256), (40,40,512), (20,20,1024)

1.颈部结构

(640, 640, 3)-->(320, 320, 12)-->(320, 320, 64)

(1)说明

yolo v5最初使用了Focus结构来初步提取特征,在改进后使用了大卷积核的卷积来初步提取特征。

(2)内部执行细节

class Focus(nn.Module):
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):
        super(Focus, self).__init__()
        # 这里的Conv表示卷积,归一化,激活函数三件套
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)

    def forward(self, x):
        # (320, 320, 12) => (320, 320, 64)
        return self.conv(
            # (640, 640, 3) => (320, 320, 12)
            torch.cat(
                [
                    x[..., ::2, ::2], 
                    x[..., 1::2, ::2], 
                    x[..., ::2, 1::2], 
                    x[..., 1::2, 1::2]
                ], 1
            )
        )

(3)具体模块

Focus结构 + 1*1卷积

在这里插入图片描述

  2.CSP模块

(320, 320, 64)-->(160, 160, 128)-->(160, 160, 128)

(160, 160, 128)-->(80, 80, 256)-->(80, 80, 256)

(80, 80, 256)-->(40, 40, 512)-->(40, 40, 512)

(1)说明

(2)内部执行细节

class C3(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super(C3, self).__init__()
        # hidden_channel 中间维度的深度为输入的一半,减少参数
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)
        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
        # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])

    def forward(self, x):
        # 将一个Bottleneck处理的张量和原始的张量cat到一起
        # (320, 320, 128)-->(320, 320, 128)
        return self.cv3(torch.cat(
            (
                # (320, 320, 128)-->(320, 320, 64)-->(320, 320, 64)-->(320, 320, 64)--(320, 320, 64)
                self.m(self.cv1(x)),
                # (320, 320, 128)-->(320, 320, 64)
                self.cv2(x)
            )
            , dim=1))
class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):
        super(Bottleneck, self).__init__()
        # hidden channels
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

(3)具体模块

1)步长为2的3*3卷积块

2)CSP模块:Bottleneck内部是1个步长1的1*1和1个步长1的3*3卷积,并且设置了残差连接。注意这里的3*3卷积用了padding,保持输入输出一致。第三,四个虚线框除了降采样,还有的与第二个虚线框不一样的操作是CSP叠加的模块增加,第三,四个框增加第二个框的三倍。第五个框则与第二框深度一致,并且取消残差连接。

3.SPP模块

(40, 40, 512)-->(20, 20, 1024)-->(20, 20, 1024)-->(20, 20, 1024)

(1)说明

SPP原理及实现

Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

SPP的代码如下,每步已显示变量结果,本质是将图像通过不同尺度的池化下采样再进入全连接层。

# coding=utf-8

import math
import torch
import torch.nn.functional as F


# 构建SPP层(空间金字塔池化层)
class SPPLayer(torch.nn.Module):

    def __init__(self, num_levels = 3, pool_type='max_pool'):
        super(SPPLayer, self).__init__()
        # 划分成多少份
        self.num_levels = num_levels
        # 池化的类型
        self.pool_type = pool_type

    def forward(self, x):
        # (16, 3, 256, 156)
        num, c, h, w = x.size()

        for i in range(self.num_levels):
            # level = 1, 2, 3
            level = i + 1
            # math.ceil返回不小于x的最接近的整数,kernel_size = (256, 256), (128, 128), (86, 86),stride = (256, 256), (128, 128), (86, 86)
            kernel_size = (math.ceil(h / level), math.ceil(w / level))
            stride = (math.ceil(h / level), math.ceil(w / level))
            # math向下取整,pooling用来填充用的,此处是(0, 0), (0, 0), (0, 0)
            pooling = (
            math.floor((kernel_size[0] * level - h + 1) / 2), math.floor((kernel_size[1] * level - w + 1) / 2))

            # 选择池化方式
            if self.pool_type == 'max_pool':
                # tensor = [16, 3], [16, 12], [16, 27]
                tensor = F.max_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
            else:
                tensor = F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)

            # 把所有的张量cat到一起,格式:[batch, channel],[16, 3 + 12 + 27]
            if (i == 0):
                x_flatten = tensor.view(num, -1)
            else:
                x_flatten = torch.cat((x_flatten, tensor.view(num, -1)), 1)
        return x_flatten
x = torch.rand((16, 3, 256, 256))
SPPLayer().forward(x)

【YOLOv5】SPP、SPPF模块及添加ASPP模块

yolo v4中,SPP是用在FPN里面的,在yolo v5中,SPP模块被用在了主干特征提取网络中, 和yolo v3一样。yolo v5借鉴SPP的思想,即采用统一步长但不同尺寸的卷积核和补丁实现SPP。再通过concate按通道拼接后用1x1卷积,实现特征融合。

(2)内部执行细节

class SPP(nn.Module):
    def __init__(self, c1, c2, k=(5, 9, 13)):
        super(SPP, self).__init__()
        c_ = c1 // 2
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])

    def forward(self, x):
        # (20, 20, 1024)-->(20, 20, 1024)
        x = self.cv1(x)
        # (20, 20, 1024), (20, 20, 1024), (20, 20, 1024), (20, 20, 1024)-->((20, 20, 1024))
        return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))

(3)具体模块

1)步长为2的3*3卷积块

2)SPP模块:与CSP的区别在于CSP主要是增加深度,SPP为增加宽度。这里借鉴SPP的思想,并没有搞入后面的全连接层。

3)CSP模块

总结:本质是1*1卷积,3*3卷积与残差连接的递进组合,1*1卷积负责维度对齐,3*3卷积多用于降采样。同时,借鉴CSP的维度加深策略,SPP特征加宽策略。

FPN + Yolo Head

(80,80,256), (40,40,512), (20,20,1024)-->(80,80,255), (40,40,255), (20,20,255)

(1)说明

255分别代表3 * (4 + 1 + num_classes),3代表3个先验框,4代表中心点坐标和宽高,1代表是否有物体,num_classes代表置信度。

(2)内部执行细节

# 步长为1的1*1卷积 20, 20, 1024 -> 20, 20, 512
        P5          = self.conv_for_feat3(feat3)
        # 最邻近上采样2倍大小 20, 20, 512 -> 40, 40, 512
        P5_upsample = self.upsample(P5)
        # cat 40, 40, 512 -> 40, 40, 1024
        P4          = torch.cat([P5_upsample, feat2], 1)
        # 叠加depth为第二个虚线框的CSP模块,且维度减半了 40, 40, 1024 -> 40, 40, 512
        P4          = self.conv3_for_upsample1(P4)

        # feat2,feat1和feat3,feat2执行相同的操作
        # 40, 40, 512 -> 40, 40, 256
        P4          = self.conv_for_feat2(P4)
        # 40, 40, 256 -> 80, 80, 256
        P4_upsample = self.upsample(P4)
        # 80, 80, 256 cat 80, 80, 256 -> 80, 80, 512
        P3          = torch.cat([P4_upsample, feat1], 1)
        # 80, 80, 512 -> 80, 80, 256
        P3          = self.conv3_for_upsample2(P3)
        
        # 步长为2的3*3卷积 80, 80, 256 -> 40, 40, 256
        P3_downsample = self.down_sample1(P3)
        # 40, 40, 256 cat 40, 40, 256 -> 40, 40, 512
        P4 = torch.cat([P3_downsample, P4], 1)
        # 叠加depth为第二个虚线框的CSP模块,维度不减半 40, 40, 512 -> 40, 40, 512
        P4 = self.conv3_for_downsample1(P4)

        # P4,P5执行相同的操作
        # 40, 40, 512 -> 20, 20, 512
        P4_downsample = self.down_sample2(P4)
        # 20, 20, 512 cat 20, 20, 512 -> 20, 20, 1024
        P5 = torch.cat([P4_downsample, P5], 1)
        # 20, 20, 1024 -> 20, 20, 1024
        P5 = self.conv3_for_downsample2(P5)

        # 以下均为1*1卷积,将得到的特征维数映射到回归层和类别层
        # 80, 80, 256 => 80, 80, 3 * (5 + num_classes) => 80, 80, 3 * (4 + 1 + num_classes)
        # 40, 40, 512 => 40, 40, 3 * (5 + num_classes) => 40, 40, 3 * (4 + 1 + num_classes)
        # 20, 20, 1024 => 20, 20, 3 * (5 + num_classes) => 20, 20, 3 * (4 + 1 + num_classes)
        #---------------------------------------------------#
        #   第三个特征层
        #   y3=(batch_size,255,80,80)
        #---------------------------------------------------#
        out2 = self.yolo_head_P3(P3)
        #---------------------------------------------------#
        #   第二个特征层
        #   y2=(batch_size,255,40,40)
        #---------------------------------------------------#
        out1 = self.yolo_head_P4(P4)
        #---------------------------------------------------#
        #   第一个特征层
        #   y1=(batch_size,255,20,20)
        #---------------------------------------------------#
        out0 = self.yolo_head_P5(P5)

(3)具体模块

后处理

(80,80,255), (40,40,255), (20,20,255)-->(25200, 85)-->((num_box_filter, 7))

预测框与得分

(80,80,255), (40,40,255), (20,20,255)-->( 3, 80, 80, 4), ( 3, 40, 40, 4), ( 3, 20, 20, 4);( 3, 80, 80, 1), ( 3, 40, 40, 1), ( 3, 20, 20, 1);( 3, 80, 80, 80), ( 3, 40, 40, 80), ( 3, 20, 20, 80)-->(19200, 85), (4800, 85), (1200, 85)-->(25200, 85)

(1)说明

yolo准备了9个不同大小的预测框10,13, 16,30, 33,23,  30,61, 62,45, 59,119,  116,90, 156,198, 373,326。前三个对应(20,20,255),中间三个对应(40,40,255),后三个对应(80,80,255)

(2)内部执行细节

1)将85中的5剖开分别进入激活函数

2)得到每个特征图的坐标位置和3个映射先验框,与x,y,w,h适配调节增大范围

a)进行中心预测点的计算,利用Regression预测结果前两个序号的内容对特征点的三个先验框中心坐标进行偏移。
b)进行预测框宽高的计算,利用Regression预测结果后两个序号的内容求指数后获得预测框的宽高。

3)归一化,聚合

        for i, input in enumerate(inputs):
            #-----------------------------------------------#
            #   输入的input一共有三个,他们的shape分别是
            #   batch_size = 1
            #   batch_size, 3 * (4 + 1 + 80), 20, 20
            #   batch_size, 255, 40, 40
            #   batch_size, 255, 80, 80
            #-----------------------------------------------#
            batch_size      = input.size(0)
            input_height    = input.size(2)
            input_width     = input.size(3)

            #-----------------------------------------------#
            #   输入为640x640时
            #   特征层相对于原图的变化 640/20, 640/40, 640/80
            #   stride_h = stride_w = 32、16、8
            #-----------------------------------------------#
            stride_h = self.input_shape[0] / input_height
            stride_w = self.input_shape[1] / input_width
            #-------------------------------------------------#
            #   此时获得的scaled_anchors大小是相对于特征层的
            #-------------------------------------------------#
            #   原图上的anchor映射到特征图上,这里求的是这个映射的尺度是多少
            scaled_anchors = [(anchor_width / stride_w, anchor_height / stride_h) for anchor_width, anchor_height in self.anchors[self.anchors_mask[i]]]

            #-----------------------------------------------#
            #   输入的input一共有三个,他们的shape分别是
            #   batch_size, 3, 20, 20, 85
            #   batch_size, 3, 40, 40, 85
            #   batch_size, 3, 80, 80, 85
            #-----------------------------------------------#
            #   将最后映射出来的张量做提前的格式转换,拿20*20的特征图举例(batch_size, 255, 20, 20)-->(batch_size, 3, 85, 20, 20)-->(batch_size, 3, 20, 20, 85)
            prediction = input.view(batch_size, len(self.anchors_mask[i]),
                                    self.bbox_attrs, input_height, input_width).permute(0, 1, 3, 4, 2).contiguous()

            #-----------------------------------------------#
            #   先验框的中心位置的调整参数
            #-----------------------------------------------#
            #   prediction[..., 0]为prediction前面所有维度保持不变,只把最后一维度切分
            #   (batch_size, 3, 20, 20, 85)-->(batch_size, 3, 20, 20)
            x = torch.sigmoid(prediction[..., 0])  
            y = torch.sigmoid(prediction[..., 1])
            #-----------------------------------------------#
            #   先验框的宽高调整参数
            #-----------------------------------------------#
            w = torch.sigmoid(prediction[..., 2]) 
            h = torch.sigmoid(prediction[..., 3]) 
            #-----------------------------------------------#
            #   获得置信度,是否有物体
            #-----------------------------------------------#
            conf        = torch.sigmoid(prediction[..., 4])
            #-----------------------------------------------#
            #   种类置信度
            #-----------------------------------------------#
            pred_cls    = torch.sigmoid(prediction[..., 5:])

            FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor
            LongTensor  = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor

            #----------------------------------------------------------#
            #   生成网格,先验框中心,网格左上角 
            #   batch_size,3,20,20
            #----------------------------------------------------------#
            # 得到间距为1的x,y[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.]-->
            # 在行上重复得到的shape为[20, 20]-->[batch_size*3, 20, 20]-->[batch_size, 3, 20, 20]
            grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat(
                batch_size * len(self.anchors_mask[i]), 1, 1).view(x.shape).type(FloatTensor)
            grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_width, 1).t().repeat(
                batch_size * len(self.anchors_mask[i]), 1, 1).view(y.shape).type(FloatTensor)

            #----------------------------------------------------------#
            #   按照网格格式生成先验框的宽高
            #   [batch_size,3,20,20]
            #----------------------------------------------------------#
            anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0]))
            anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1]))
            anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape)
            anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape)

            #----------------------------------------------------------#
            #   利用预测结果对先验框进行调整
            #   首先调整先验框的中心,从先验框中心向右下角偏移
            #   再调整先验框的宽高。
            #   x 0 ~ 1 => 0 ~ 2 => -0.5, 1.5 => 负责一定范围的目标的预测
            #   y 0 ~ 1 => 0 ~ 2 => -0.5, 1.5 => 负责一定范围的目标的预测
            #   w 0 ~ 1 => 0 ~ 2 => 0 ~ 4 => 先验框的宽高调节范围为0~4倍
            #   h 0 ~ 1 => 0 ~ 2 => 0 ~ 4 => 先验框的宽高调节范围为0~4倍
            #----------------------------------------------------------#
            pred_boxes          = FloatTensor(prediction[..., :4].shape)
            pred_boxes[..., 0]  = x.data * 2. - 0.5 + grid_x
            pred_boxes[..., 1]  = y.data * 2. - 0.5 + grid_y
            pred_boxes[..., 2]  = (w.data * 2) ** 2 * anchor_w
            pred_boxes[..., 3]  = (h.data * 2) ** 2 * anchor_h

            #----------------------------------------------------------#
            #   将输出结果归一化成小数的形式
            #----------------------------------------------------------#
            _scale = torch.Tensor([input_width, input_height, input_width, input_height]).type(FloatTensor)
            output = torch.cat((pred_boxes.view(batch_size, -1, 4) / _scale,
                                conf.view(batch_size, -1, 1), pred_cls.view(batch_size, -1, self.num_classes)), -1)
            outputs.append(output.data)

(3)具体模块

得分筛选与非极大值抑制

(25200, 85)-->(num_box, 85)-->(num_box, 7)(将那80转为类别和置信度)-->(num_box_filter, 7)

(1)说明

得分筛选就是筛选出得分满足confidence置信度的预测框。
非极大抑制就是筛选出一定区域内属于同一种类得分最大的框。

得分筛选-->得分过滤(85-->7)-->同一种类进行最大值抑制-->将得到的框的坐标值映射回原图

非极大值抑制:

1)是否有物体的置信度评分*该种类的置信度评分,降序排序

2)依次将现在的框与后面的框求IOU,利用IOU阈值筛选掉不满足阈值的情况

(2)内部执行细节

        for i, image_pred in enumerate(prediction):
            #----------------------------------------------------------#
            #   对种类预测部分取max。
            #   class_conf  [num_anchors, 1]    种类置信度
            #   class_pred  [num_anchors, 1]    种类
            #----------------------------------------------------------#
            class_conf, class_pred = torch.max(image_pred[:, 5:5 + num_classes], 1, keepdim=True)

            #----------------------------------------------------------#
            #   利用置信度进行第一轮筛选
            #----------------------------------------------------------#
            conf_mask = (image_pred[:, 4] * class_conf[:, 0] >= conf_thres).squeeze()

            #----------------------------------------------------------#
            #   根据置信度进行预测结果的筛选
            #----------------------------------------------------------#
            image_pred = image_pred[conf_mask]
            class_conf = class_conf[conf_mask]
            class_pred = class_pred[conf_mask]
            if not image_pred.size(0):
                continue
            #-------------------------------------------------------------------------#
            #   detections  [num_anchors, 7]
            #   7的内容为:x1, y1, x2, y2, obj_conf, class_conf, class_pred
            #-------------------------------------------------------------------------#
            detections = torch.cat((image_pred[:, :5], class_conf.float(), class_pred.float()), 1)

            #------------------------------------------#
            #   获得预测结果中包含的所有种类
            #------------------------------------------#
            unique_labels = detections[:, -1].cpu().unique()

            if prediction.is_cuda:
                unique_labels = unique_labels.cuda()
                detections = detections.cuda()

            for c in unique_labels:
                #------------------------------------------#
                #   获得某一类得分筛选后全部的预测结果
                #------------------------------------------#
                detections_class = detections[detections[:, -1] == c]

                #------------------------------------------#
                #   使用官方自带的非极大抑制会速度更快一些!
                #   筛选出一定区域内,属于同一种类得分最大的框
                #------------------------------------------#
                keep = nms(
                    detections_class[:, :4],
                    detections_class[:, 4] * detections_class[:, 5],
                    nms_thres
                )
                max_detections = detections_class[keep]
                
                # # 按照存在物体的置信度排序
                # _, conf_sort_index = torch.sort(detections_class[:, 4]*detections_class[:, 5], descending=True)
                # detections_class = detections_class[conf_sort_index]
                # # 进行非极大抑制
                # max_detections = []
                # while detections_class.size(0):
                #     # 取出这一类置信度最高的,一步一步往下判断,判断重合程度是否大于nms_thres,如果是则去除掉
                #     print(detections_class[0].unsqueeze(0).shape)
                #     max_detections.append(detections_class[0].unsqueeze(0))
                #     if len(detections_class) == 1:
                #         break
                #     ious = bbox_iou(max_detections[-1], detections_class[1:])
                #     detections_class = detections_class[1:][ious < nms_thres]
                # # 堆叠
                # max_detections = torch.cat(max_detections).data
                #
                # Add max detections to outputs
                output[i] = max_detections if output[i] is None else torch.cat((output[i], max_detections))
            
            if output[i] is not None:
                output[i]           = output[i].cpu().numpy()
                box_xy, box_wh      = (output[i][:, 0:2] + output[i][:, 2:4])/2, output[i][:, 2:4] - output[i][:, 0:2]
                output[i][:, :4]    = self.yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape, letterbox_image)

(3)具体模块

损失函数(train)

(80,80,255), (40,40,255), (20,20,255)-->loss

(1)说明

1)损失函数用作训练,取Yolo Head之后的张量作为输入。

2)正样本匹配策略计算出适合这个真实框预测的先验框。

正样本匹配,就是寻找哪些先验框被认为有对应的真实框,并且负责这个真实框的预测

a)宽高比匹配先验框

b)最邻近网格匹配特征点

在这里插入图片描述

3)包含三个部分的损失

a)Reg部分,IOU损失
b)Obj部分,所有真实框对应的先验框都是正样本,剩余的先验框均为负样本,根据正负样本和特征点的是否包含物体的预测结果计算交叉熵损失。
c)Cls部分,根据真实框的种类和先验框的种类预测结果计算交叉熵损失。

(2)内部执行细节

1)与后处理预测框与得分处理一致

2)计算IOU损失,有无样本的交叉熵损失,类别的交叉熵损失

    def forward(self, l, input, targets=None, y_true=None):
        #----------------------------------------------------#
        #   l               代表使用的是第几个有效特征层
        #   input的shape为  bs, 3*(5+num_classes), 20, 20
        #                   bs, 3*(5+num_classes), 40, 40
        #                   bs, 3*(5+num_classes), 80, 80
        #   targets         真实框的标签情况 [batch_size, num_gt, 5]
        #----------------------------------------------------#
        #--------------------------------#
        #   获得图片数量,特征层的高和宽
        #   20, 20
        #--------------------------------#
        bs      = input.size(0)
        in_h    = input.size(2)
        in_w    = input.size(3)
        #-----------------------------------------------------------------------#
        #   计算步长
        #   每一个特征点对应原来的图片上多少个像素点
        #   [640, 640] 高的步长为640 / 20 = 32,宽的步长为640 / 20 = 32
        #   如果特征层为20x20的话,一个特征点就对应原来的图片上的32个像素点
        #   如果特征层为40x40的话,一个特征点就对应原来的图片上的16个像素点
        #   如果特征层为80x80的话,一个特征点就对应原来的图片上的8个像素点
        #   stride_h = stride_w = 32、16、8
        #-----------------------------------------------------------------------#
        stride_h = self.input_shape[0] / in_h
        stride_w = self.input_shape[1] / in_w
        #-------------------------------------------------#
        #   此时获得的scaled_anchors大小是相对于特征层的
        #-------------------------------------------------#
        scaled_anchors  = [(a_w / stride_w, a_h / stride_h) for a_w, a_h in self.anchors]
        #-----------------------------------------------#
        #   输入的input一共有三个,他们的shape分别是
        #   bs, 3 * (5+num_classes), 20, 20 => bs, 3, 5 + num_classes, 20, 20 => batch_size, 3, 20, 20, 5 + num_classes

        #   batch_size, 3, 20, 20, 5 + num_classes
        #   batch_size, 3, 40, 40, 5 + num_classes
        #   batch_size, 3, 80, 80, 5 + num_classes
        #-----------------------------------------------#
        prediction = input.view(bs, len(self.anchors_mask[l]), self.bbox_attrs, in_h, in_w).permute(0, 1, 3, 4, 2).contiguous()
        
        #-----------------------------------------------#
        #   先验框的中心位置的调整参数
        #-----------------------------------------------#
        x = torch.sigmoid(prediction[..., 0])
        y = torch.sigmoid(prediction[..., 1])
        #-----------------------------------------------#
        #   先验框的宽高调整参数
        #-----------------------------------------------#
        w = torch.sigmoid(prediction[..., 2]) 
        h = torch.sigmoid(prediction[..., 3]) 
        #-----------------------------------------------#
        #   获得置信度,是否有物体
        #-----------------------------------------------#
        conf = torch.sigmoid(prediction[..., 4])
        #-----------------------------------------------#
        #   种类置信度
        #-----------------------------------------------#
        pred_cls = torch.sigmoid(prediction[..., 5:])
        #-----------------------------------------------#
        #   self.get_target已经合并到dataloader中
        #   原因是在这里执行过慢,会大大延长训练时间
        #-----------------------------------------------#
        # y_true, noobj_mask = self.get_target(l, targets, scaled_anchors, in_h, in_w)

        #---------------------------------------------------------------#
        #   将预测结果进行解码,判断预测结果和真实值的重合程度
        #   如果重合程度过大则忽略,因为这些特征点属于预测比较准确的特征点
        #   作为负样本不合适
        #----------------------------------------------------------------#
        pred_boxes = self.get_pred_boxes(l, x, y, h, w, targets, scaled_anchors, in_h, in_w)

        if self.cuda:
            y_true          = y_true.type_as(x)
        
        loss    = 0
        n       = torch.sum(y_true[..., 4] == 1)
        if n != 0:
            #---------------------------------------------------------------#
            #   计算预测结果和真实结果的giou,计算对应有真实框的先验框的giou损失
            #                         loss_cls计算对应有真实框的先验框的分类损失
            #----------------------------------------------------------------#
            giou        = self.box_giou(pred_boxes, y_true[..., :4]).type_as(x)
            loss_loc    = torch.mean((1 - giou)[y_true[..., 4] == 1])
            loss_cls    = torch.mean(self.BCELoss(pred_cls[y_true[..., 4] == 1], self.smooth_labels(y_true[..., 5:][y_true[..., 4] == 1], self.label_smoothing, self.num_classes)))
            loss        += loss_loc * self.box_ratio + loss_cls * self.cls_ratio
            #-----------------------------------------------------------#
            #   计算置信度的loss
            #   也就意味着先验框对应的预测框预测的更准确
            #   它才是用来预测这个物体的。
            #-----------------------------------------------------------#
            tobj        = torch.where(y_true[..., 4] == 1, giou.detach().clamp(0), torch.zeros_like(y_true[..., 4]))
        else:
            tobj        = torch.zeros_like(y_true[..., 4])
        loss_conf   = torch.mean(self.BCELoss(conf, tobj))
        
        loss        += loss_conf * self.balance[l] * self.obj_ratio
        # if n != 0:
        #     print(loss_loc * self.box_ratio, loss_cls * self.cls_ratio, loss_conf * self.balance[l] * self.obj_ratio)
        return loss

(3)具体模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615857.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

118. 杨辉三角解题思路

文章目录 题目解题思路 题目 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: num…

【Java基础】集合List/Set/Map

零、重点 初始大小加载因子扩容大小底层机制ArrayList101(存满)1.5倍动态数组&#xff0c;Arrays.copyof()HashMap160.752.0倍 jdk1.7及其之前&#xff1a;数组单向链表&#xff08;有扩容死锁问题&#xff09; jdk1.8及其之后&#xff1a;Node 数组 单向Node链表 红黑树 …

大专苦学 37 天,5 轮面试,6 小时灵魂拷问,含泪拿下阿里 offer~

先纠正一下大家的错误认知&#xff0c;大专学历不是不能进大厂&#xff0c;只不过很难罢了&#xff0c;比如阿里就有很多大专学历的人&#xff1b;学历低想进大厂很难的&#xff0c;不光需要的技术足够厉害&#xff0c;还需要有合适的内推人&#xff08;人脉&#xff09;以及恰…

机器的眼睛:漫谈机器视觉(文末送书)

眼睛是人心灵的窗户&#xff0c;我们可以通过凝视对方的眼神来理解他人。当有一天你走过一道需要刷脸才能通过的大门&#xff0c;突然间看到一个冰冷的摄像头在凝视着你的时候&#xff0c;你是否也曾若有所思地看着它&#xff0c;心中充满了疑惑——它是如何工作的&#xff1f;…

苹果颠覆性新品取代iPhone,5万元新电脑告别 Intel

今日凌晨苹果正式举行了 WWDC23 Apple 全球开发者大会。 和往年相比 WWDC23 可以说是倍受关注&#xff0c;在召开前就有号称革命性产品问世的消息。 场面如此壮观&#xff0c;今年到底苹果又带来了什么引领时代的玩意&#xff1f; 革命性产品又是否能接力乔布斯时代&#xff…

day06——朴素贝叶斯算法

朴素贝叶斯算法 一、什么是朴素贝叶斯分类方法二、概率基础知识1&#xff0c;联合概率2&#xff0c;条件概率 三、贝叶斯公式1&#xff0c;公式2&#xff0c;拉普拉斯平滑系数 四、API五、实操案例&#xff1a;20类新闻分类代码 六、总结 一、什么是朴素贝叶斯分类方法 朴素贝…

漫谈大数据 - 如何设计业务埋点方案与数据采集应用

业务埋点和数据分析是在用户行为和业务数据上进行跟踪、收集和分析的关键方法&#xff0c;用于了解用户行为模式、改进产品和服务&#xff0c;并做出数据驱动的决策。 全文1.5万字&#xff0c;建议阅读时间35min。 目录 业务埋点 埋点的重要性 埋点的类型 全埋点 代码埋点…

springboot3自动装配机制原理(小白也看得懂)

springbootApplication注解 实现自动装配的核心就是EnableAutoConfiguration。(springboot就是通过此注解实现自动装配机制&#xff0c;主要作用就是让springboot去扫描默认的配置类&#xff0c;如果没有这个类的话&#xff0c;springboot只会默认扫描启动类下包中各个类的注解…

nuxt3项目在宝塔上使用pm2放到服务器

最近在将nuxt3项目扔到服务器上的时候遇到了一些问题&#xff0c;发现打包后的nuxt项目与正常的vue文件有些差别&#xff0c;缺失了index.html文件。 这边就记录下nuxt3项目是怎么放到服务器上的&#x1f914; 一、打包nuxt3项目 执行对应的打包命令 yarn build 二、找…

Electron详解(一):基本介绍

文章目录 一、electron简介二、发展历史三、electron优缺点四、electron和Qt的对比五、使用electron开发的实际案例六、electron 的原理 一、electron简介 Electron&#xff08;官网&#xff1a;https://www.electronjs.org/zh/&#xff09;是由Github开发&#xff0c;用HTML&…

17:00面试,还没10分钟就出来了,问的实在是太...

从外包出来&#xff0c;没想到死在另一家厂子 自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到8月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有个兄弟内推我去…

CVE-2022-30887

文章目录 CVE-2022-30887一、漏洞介绍二、渗透步骤1、打开网站2、登录网站3、shell4、文件上传5、查看flag CVE-2022-30887 一、漏洞介绍 多语言药房管理系统 (MPMS) 是用 PHP 和 MySQL 开发的, 该软件的主要目的是在药房和客户之间提供一套接口&#xff0c;客户是该软件的主要…

MLC Chat App上架苹果App Store可在本地运行语言模型;谷歌推出AI设计工具StyleDrop

&#x1f989; AI新闻 &#x1f680; 开源项目MLC Chat App上架苹果App Store&#xff0c;可在本地运行语言模型 摘要&#xff1a;CMU助理陈天其教授表示&#xff0c;开源项目MLC LLM的独立聊天应用程序MLC Chat App已上架苹果App Store&#xff0c;允许将任何语言模型本地部…

3.用图神经网络进行图分类

在本教程中&#xff0c;我们将更深入地了解如何将图神经网络&#xff08;GNN&#xff09;应用于图分类任务。图分类是指在给定图的数据集的情况下&#xff0c;基于一些结构图的属性对整个图&#xff08;与节点相反&#xff09;进行分类的问题。在这里&#xff0c;我们希望嵌入整…

uniapp:HbuildeX项目实现jenkins自动化打包上传

前序: 基于HBuilderX创建的项目。 配置uniapp 指令式打包APP、微信小程序、H5。 ubuntu系统上的jenkins部署基础指令,发送给windows服务器上,windows服务器使用HBuilderX软件的cli.exe执行相关cli命令打包,然后jenkins再调用接口下载打包后的文件。 可实现多线程同时打…

社会主义核心价值观解码

今天随机刷题时&#xff0c;遇到了一个蛮有意思的题&#xff0c;浅浅记录一下&#xff0c;挺好玩的。 题目&#xff1a; [鹤城杯 2021]easy_crypto 难度&#xff1a;很小啦&#xff0c;差不多签到题 文件下载的内容如下&#xff1a; 最开始我以为是把每个词按社会主义那个顺序…

Java基础学习+面向对象

一&#xff0c;基础概念介绍 1.1Java跨平台原理&#xff08;一次编译&#xff0c;处处运行&#xff09; Java 源代码经过编译&#xff0c;生成字节码文件&#xff0c;交由 Java 虚拟机来执行&#xff0c;不同得系统有不同得JVM,借助JVM 实现跨平台。就比如说我们在 Windows 下…

Redis内存优化——内存淘汰及回收机制

文章目录 Redis内存优化——内存淘汰及回收机制内存淘汰策略LRU和LFULRU和Redis的近似LRU什么是LRURedis的近似LRU LFU如何选择 内存回收策略惰性删除定期删除 配置文件说明总结 系列文章目录 本文是系列文章&#xff0c;为了增强您的阅读体验&#xff0c;已将系列文章目录放入…

Yakit: 集成化单兵安全能力平台使用教程·MITM交互式劫持篇

Yakit: 集成化单兵安全能力平台使用教程MITM交互式劫持篇 1.免配置抓包2.拦截HTTPS的消息3.代理和浏览器设置4.内容规则:标记/替换流量5.过滤流量6.网站树视角1.免配置抓包 点击手工测试--MITM交互式劫持即可进入MITM劫持页面,点击免配置启动进入免配置启动设置界面(这类似…

ESP32-C2-12模组 使用乐鑫AT固件示例

ESP32C2 AT固件使用 ESP32 C2模组&#xff0c;如图1-1所示 图1-1 ESP32 C2模组 ESP32 C2开发板&#xff0c;如图1-2所示 图1-2 ESP32 C2开发 方案亮点 1、完整的 WiFi 子系统&#xff0c;符合 IEEE 802.11b/g/n 协议&#xff0c;具有 Station 模式、SoftAP 模式、SoftAP Stat…