物体检测-系列教程20:YOLOV5 源码解析10 (Model类前向传播、forward_once函数、_initialize_biases函数)

😎😎😎物体检测-系列教程 总目录

有任何问题欢迎在下面留言
本篇文章的代码运行界面均在Pycharm中进行
本篇文章配套的代码资源已经上传
点我下载源码

14、Model类

14.1 构造函数

上篇博客中

14.2 前向传播

    def forward(self, x, augment=False, profile=False):
        if augment:
            img_size = x.shape[-2:]  # height, width
            s = [1, 0.83, 0.67]  # scales
            f = [None, 3, None]  # flips (2-ud, 3-lr)
            y = []  # outputs
            for si, fi in zip(s, f):
                xi = scale_img(x.flip(fi) if fi else x, si)
                yi = self.forward_once(xi)[0]  # forward
                yi[..., :4] /= si  # de-scale
                if fi == 2:
                    yi[..., 1] = img_size[0] - yi[..., 1]  # de-flip ud
                elif fi == 3:
                    yi[..., 0] = img_size[1] - yi[..., 0]  # de-flip lr
                y.append(yi)
            return torch.cat(y, 1), None  # augmented inference, train
        else:
            return self.forward_once(x, profile)  # single-scale inference, train

这段代码是forward方法的实现,它定义了模型的前向传播过程,支持正常和增强两种推理模式:

  1. 前向传播函数,输入x,是否进行数据增强augment,是否分析性能profile
  2. 是否使用数据增强
  3. img_size ,获取输入图像的长宽
  4. s,定义缩放尺度
  5. f,定义翻转模式,这里None表示不翻转,3表示左右翻转
  6. y,初始化输出列表
  7. 使用zip函数将尺度因子列表s和翻转指示列表f组合起来,然后遍历每一对尺度因子和翻转指示
  8. xi,如果fi不为None,先根据fi的值对图像进行翻转,然后调用scale_img函数根据si的值缩放处理图像;否则直接调用scale_img函数根据si的值缩放处理图像
  9. yi,将xi进行一次前向传播,取第一个输出
  10. 对输出yi的前四个维度进行缩放调整,以恢复到原始的尺度。这通常是对边界框坐标的调整
  11. 如果使用了上下翻转
  12. 则调整y的坐标
  13. 如果使用了左右翻转
  14. 则调整x坐标
  15. 将处理后的输出添加到列表
  16. 将list y的所有输出按照第一个维度进行拼接
  17. 如果在当前循环中没有使用数据增强
  18. 直接进行一次正常的前向传播

前向传播方法,包括了一个可选的图像增强步骤。在增强模式下,通过对输入图像应用不同的尺度和翻转,生成多个变体,对每个变体单独进行前向传播,并对输出进行调整以适应原始图像的尺寸和方向,最后将所有变体的输出合并。这种方法可以增加模型的泛化能力,因为它让模型在训练时见到更多的数据变化。如果不进行图像增强,它将执行一次标准的前向传播。通过这种设计,模型可以更灵活地应对不同的输入和训练需求

14.3 forward_once函数

这里每次都执行一次前向传播的时候,是执行一个模块
在这里插入图片描述

    def forward_once(self, x, profile=False):
        y, dt = [], []  # outputs
        for m in self.model:
            if m.f != -1:  # if not from previous layer
                x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f]
            if profile:
                try:
                    import thop
                    o = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2  # FLOPS
                except:
                    o = 0
                t = time_synchronized()
                for _ in range(10):
                    _ = m(x)
                dt.append((time_synchronized() - t) * 100)
                print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type))
            x = m(x)  # run
            y.append(x if m.i in self.save else None)  # save output
        if profile:
            print('%.1fms total' % sum(dt))
        return x
  1. forward_once函数,输入和forward函一样
  2. y, dt ,初始化两个空列表,y用于存储每一层的输出,dt用于在性能分析模式下存储每一层的执行时间
  3. 遍历模型的每一层
  4. 如果当前层的输入不是来自上一层的输出
  5. 如果m.f是整数,则直接从y中获取对应的层输出作为输入。如果m.f是一个列表,则根据列表中的索引从y中选择输入,如果索引为-1,则使用原始输入x
  6. 是否开启性能分析模式
  7. try
  8. 导入thop库,用于计算浮点运算数(FLOPS)
  9. o,使用thop.profile计算当前层m的FLOPS,结果除以1E9转换为GigaFLOPS,并乘以2。这里假设thop.profile返回的是一个元组,其第一个元素是所需的FLOPS
  10. 如果尝试执行失败
  11. 则将o(FLOPS)设置为0
  12. t,调用time_synchronized函数,获取当前精确的时间
  13. 循环10次
  14. 为了稳定测量时间,通过多次执行减少偶然误差
  15. 调用time_synchronized函数计算执行当前层操作的总时间,并将其添加到dt列表中
  16. 打印当前层的FLOPS、参数数量、执行时间和层类型。为性能分析提供详细信息
  17. 执行当前层的前向传播,并更新x为该层的输出
  18. 如果当前层的索引m.i在保存列表self.save中,则将输出x保存到y列表中;否则,保存None. 这样做可以减少内存占用,只保存那些后续步骤中需要的层的输出
  19. 再次检查是否开启了性能分析模式。这个检查是为了在性能分析完成后打印总的执行时间
  20. 如果开启了性能分析,计算所有层执行时间的总和并打印。这提供了整个前向传播过程的总执行时间,帮助了解模型的性能瓶颈
  21. 返回最后一层的输出

14.4 _initialize_biases函数

    def _initialize_biases(self, cf=None):
        m = self.model[-1]  # Detect() module
        for mi, s in zip(m.m, m.stride):  # from
            b = mi.bias.data.view(m.na, -1).clone()
            obj_add = math.log(8 / (640 / s) ** 2)  # 计算obj层需要增加的值
            cls_add = math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum())
            b[:, 4] = b[:, 4] + obj_add
            b[:, 5:] = b[:, 5:] + cls_add
            mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
  1. 初始化偏执的函数,接受一个可选的参数,这个参数用于根据数据集中各类别出现的频率来调整分类(cls)层的偏置
  2. m,获取模型中的最后一个模块,检测层(Detect模块),用于目标检测
  3. 遍历检测层中的每个子模块mi及其对应的步长stride,这里的步长是指输入图像被缩减的尺度,对目标尺寸预测非常关键
  4. b,获取子模块mi的偏置项,并将其重塑(reshape)成(m.na, -1)的形状,其中m.na是每个特征图位置预测的锚框数量。.clone()确保在修改b时不会影响原始的偏置值
  5. obj_add ,计算对象(obj)层偏置需要增加的值。这个公式基于假设每640像素的图像中有8个对象,并根据特征图的尺度(通过步长s计算)来调整。目的是调整检测层对于不同尺寸特征图上对象数量预测的偏置
  6. cls_add ,计算分类(cls)层偏置需要增加的值。如果没有提供类频率(cf为None),则使用一个基于类数量m.nc的固定公式。如果提供了类频率,那么使用类频率来计算每个类的偏置调整值,以此反映数据集中类别的分布
  7. 将计算出的对象层偏置调整值加到b的第4列上,这是因为在目标检测中,偏置项通常包括4个坐标偏置和一个对象存在的偏置,后者位于第5个位置(索引为4)
  8. 将计算出的分类层偏置调整值加到b的第5列及之后的所有列上,对应于每个类别的偏置
  9. 将调整后的偏置b重塑回原始形状并设置为mi的偏置,确保这些偏置在训练过程中可以被进一步调整(requires_grad=True)

14.5 其他辅助函数

    def _print_biases(self):
        m = self.model[-1]  # Detect() module
        for mi in m.m:  # from
            b = mi.bias.detach().view(m.na, -1).T  # conv.bias(255) to (3,85)
            print(('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean()))
  1. 获取模型的最后一个模块,这里假设是一个目标检测模块(Detect模块)
  2. 遍历检测模块中的每个子模块mi
  3. 取得当前子模块mi的偏置,通过.detach()确保不会影响梯度计算,.view(m.na, -1)调整形状以匹配锚点数量m.na和偏置的其它维度,最后进行转置以便于处理
  4. 打印当前子模块卷积层的输入通道数和偏置的统计信息,包括前五个偏置的平均值和之后所有偏置的平均值

fuse函数,用于融合模型中的卷积层(Conv2d)和批归一化层(BatchNorm2d)

    def fuse(self):  # fuse model Conv2d() + BatchNorm2d() layers
        print('Fusing layers... ')
        for m in self.model.modules():
            if type(m) is Conv:
                m._non_persistent_buffers_set = set()  # pytorch 1.6.0 compatability
                m.conv = fuse_conv_and_bn(m.conv, m.bn)  # update conv
                m.bn = None  # remove batchnorm
                m.forward = m.fuseforward  # update forward
        self.info()
        return self
  1. 遍历模型中的所有模块
  2. 检查当前模块是否为卷积层
  3. 为了兼容PyTorch 1.6.0,清空非持久性缓冲区集合
  4. 使用fuse_conv_and_bn函数来融合当前卷积层和其后的批归一化层
  5. 将批归一化层设为None,表示移除批归一化层
  6. 更新模块的前向传播函数为融合后的版本
  7. 在完成融合后,调用info方法打印模型信息
  8. 返回更新后的模型实例
    def info(self):  # print model information
        model_info(self)

调用一个model_info函数,传入当前模型实例,用于收集和打印模型的详细信息,如参数数量、层的类型等

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Yolov5前向传播是通过调用PyTorch提供的API实现的,主要包括以下几个步骤: 1. 加载模型:使用PyTorch提供的API加载训练好的Yolov5模型,可以选择不同的模型版本,如Yolov5s、Yolov5m、Yolov5l和Yolov5x等。 ```python import torch model = torch.hub.load('ultralytics/yolov5', 'yolov5s') ``` 2. 加载图像:使用OpenCV等库加载待检测的图像,并进行预处理,如调整图像大小、归一化等。 ```python import cv2 img = cv2.imread('image.jpg') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) img = img / 255.0 img = torch.from_numpy(img).float().unsqueeze(0) ``` 3. 前向传播:将处理后的图像输入到模型中,并进行前向传播,得到检测结果。 ```python with torch.no_grad(): output = model(img) ``` 4. 后处理:对前向传播得到的检测结果进行后处理,包括滤除低置信度的边界框、去除重复的边界框、使用非极大值抑制(NMS)算法等。 ```python from utils.general import non_max_suppression detections = non_max_suppression(output, conf_thres=0.25, iou_thres=0.45) ``` 5. 可视化:将处理后的检测结果绘制到原图像上,以便进行人工检查和分析。 ```python from utils.plots import plot_one_box for det in detections[0]: plot_one_box(det[:4], img[0], color=(0, 255, 0), line_thickness=3) cv2.imshow('result', img[0].numpy()) cv2.waitKey(0) ``` 总的来说,Yolov5前向传播主要包括模型加载、图像加载、前向传播、后处理和可视化等步骤,需要注意参数设置、预处理和后处理等细节,以提高检测的准确率和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机器学习杨卓越

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值