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

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

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

14、Model类

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函数,传入当前模型实例,用于收集和打印模型的详细信息,如参数数量、层的类型等


http://www.niftyadmin.cn/n/5403125.html

相关文章

《Docker 简易速速上手小册》第10章 朝着 Docker Swarm 和 Kubernetes 迈进(2024 最新版)

文章目录 10.1 Docker Swarm 基础10.1.1 重点基础知识10.1.2 重点案例:Python Web 应用的 Docker Swarm 部署10.1.3 拓展案例 1:微服务架构的 Docker Swarm 部署10.1.4 拓展案例 2:使用 Docker Swarm 进行持续部署 10.2 Kubernetes 与 Docker…

Android AppCompatActivity 方法详解

在 Android 开发中,AppCompatActivity 是一个常用的类,它提供了对新版 Android 特性在旧版 Android 上的兼容支持。作为 Android 支持库的一部分,它通常被用作活动(Activity)的基类。下面我们将介绍 AppCompatActivity…

CSS重点

第一章&#xff1a;CSS类型 1、行内样式 <div style"color:red;font-size:30px;font-weight: 900;font-style: italic;">qcby</div>注意&#xff1a;行内样式&#xff0c;作用力优先级最高&#xff0c;但是不利于html与css的书写以及修改&#xff0c;会…

Java中web的css、js、img等静态资源引入详细操作教程

本篇文章主要讲解&#xff1a;Java中web的css、js、img等静态资源引入详细操作教程 日期&#xff1a;2024年2月27日 作者&#xff1a;任聪聪 java中web单体项目静态资源webjar引入形式说明 说明&#xff1a;通过webjar进行静态资源的导入&#xff0c;使用maven进行版本的管理。…

MySQL篇—执行计划介绍(第二篇,总共三篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

蓝桥每日一题(前缀和差分)

//795 前缀和 #include<bits/stdc.h> using namespace std; //795 前缀和 const int N1e510; int n,m; int a[N]; int main() {cin>>n>>m;for(int i1;i<n;i)cin>>a[i],a[i]a[i-1];for(int i0;i<m;i){int l,r;cin>>l>>r;cout<<…

Docker部署Portainer图形化管理工具

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 前言 Portainer 是一个轻量级的容器管理工具&#xff0c;可以通过 Web 界面对 Docker 容器进行管理和监控。它提供了可…

模拟队列(数组实现)

题目描述&#xff1a; 代码模板&#xff1a; int q[N],hh,tt -1;//插入操作 void push(int x) {q[tt] x; }//弹出操作 void pop() {hh ; }//判断是否为空 bool empty() {if(hh > tt) return true;else return false; }//返回队首元素 int query() {return q[hh]; }AC代…