YOLO V3 SPP ultralytics 第四节:YOLO V3 SPP网络的搭建

news2024/11/18 16:40:06

目录

1. 介绍

2. 代码介绍

2.1 create_modules 部分

2.1.1 不同层的处理

2.1.2 信息的融合

2.1.3 yolo 层的处理

2.2 get_yolo_layers

2.3 前向传播

3. 完整代码


1. 介绍

根据 上一节 解析的cfg文件,本章将配置文件cfg 搭建YOLO V3 SPP网络

本章的代码经过了相应的更改

 搭建网络的代码在models py文件下

YOLO V3 SPP 网络如下:


 

2. 代码介绍

因为搭建网络的代码较长,并且调用比较多,所以这里只介绍重点部分

 

2.1 create_modules 部分

首先,传入的参数是 解析的cfg配置文件 ,self.module_defs 是字典的形式,如下:

 

2.1.1 不同层的处理

首先,cfg中 [net] 的部分不需要,弹出就行了

 遍历解析好的cfg配置文件字典,然后根据不同 [] 里面的key 去获取即可

 

例如卷积层:

注 :卷积 + BN + 激活函数 

 

2.1.2 信息的融合

在yolo v3 spp中,信息的融合有两个:shortcut 和 spp 模块

其中,shortcut 是 高维和低维信息的add

spp 是高维和低维信息在channel维度 的concatenate 

 

其中,FeatureConcat 为spp中的特征层拼接

spp 模块如下:

 

WeightedFeatureFusion 为shortcut 的add

 

2.1.3 yolo 层的处理

这里的yolo 层是对yolo网络输出进行后处理的操作,没有包括在网络中

 

YOLOLayer 大概就是训练的时候,怎么产生预测框,然后计算定位损失;在测试的时候,怎么将不同尺度的信息,还原回原来的图像上等等

具体的可以看这部分代码:

# yolo 的预测进行处理,不是yolo v3 spp的输出层
class YOLOLayer(nn.Module):
    def __init__(self, anchors, nc, stride):
        super(YOLOLayer, self).__init__()
        self.anchors = torch.Tensor(anchors)        # anchors
        self.stride = stride                        # layer stride 特征图上一步对应原图上的步距 [32, 16, 8]
        self.na = 3                                 # 每一个cell里面预测3个 anchors
        self.nc = nc                                # 预测类别的个数
        self.no = nc + 5                            # 每一个anchor预测的参数个数 ,(x,y,w,h+置信度+ nc)
        self.nx, self.ny, self.ng = 0, 0, (0, 0)    # initialize number of x, y gridpoints
        self.anchor_vec = self.anchors / self.stride    # 将anchors大小缩放到grid尺度
        self.anchor_wh = self.anchor_vec.view(1, self.na, 1, 1, 2)  # batch_size,3,grid_h, grid_w, anchor的w和h
        self.grid = None

    def create_grids(self, ng=(13, 13), device="cpu"):
        self.nx, self.ny = ng
        self.ng = torch.tensor(ng, dtype=torch.float)

        # build xy offsets 构建每个cell处的anchor的xy偏移量(在feature map上的)
        if not self.training:  # 训练模式不需要回归到最终预测boxes
            yv, xv = torch.meshgrid([torch.arange(self.ny, device=device),
                                     torch.arange(self.nx, device=device)])
            # batch_size, na, grid_h, grid_w, wh
            self.grid = torch.stack((xv, yv), 2).view((1, 1, self.ny, self.nx, 2)).float()

        if self.anchor_vec.device != device:
            self.anchor_vec = self.anchor_vec.to(device)
            self.anchor_wh = self.anchor_wh.to(device)

    def forward(self, p):
        bs, _, ny, nx = p.shape  # p为预测值,batch_size, predict_param(75), grid(13), grid(13)
        print(p.shape)
        if (self.nx, self.ny) != (nx, ny) or self.grid is None:  # fix no grid bug
                self.create_grids((nx, ny), p.device)

        # view: (batch_size, 75, 13, 13) -> (batch_size, 3, 75, 13, 13)
        # permute: (batch_size, 3, 75, 13, 13) -> (batch_size, 3, 13, 13, 75)
        # [bs, anchor, grid, grid, xywh + obj + classes]
        p = p.view(bs, self.na, self.no, self.ny, self.nx).permute(0, 1, 3, 4, 2).contiguous()  # permute 内存是不连续的,所以调用contiguous方法

        if self.training:
            return p

        else:  # inference
            # p = [bs, anchor, grid, grid, xywh + obj + classes]
            io = p.clone()  # inference output
            io[..., :2] = torch.sigmoid(io[..., :2]) + self.grid     # sigmoid(x,y) + cell坐标
            io[..., 2:4] = torch.exp(io[..., 2:4]) * self.anchor_wh  # exp(w,h) * anchor (w,h)
            io[..., :4] *= self.stride                  # 将cell坐标,换算映射回原图尺度
            torch.sigmoid_(io[..., 4:])
            return io.view(bs, -1, self.no), p  # view [1, 3, 13, 13, 25] as [1, 507, 25]

2.2 get_yolo_layers

这一层主要获得 yolo 层

 

 

2.3 前向传播

如下:

 

3. 完整代码

代码如下:

import math
import torch.nn as nn
import torch
from build_utils.parse_config import parse_model_cfg    # 解析 cfg 的函数


# 将多个特征矩阵在channel维度进行concatenate拼接
class FeatureConcat(nn.Module):
    def __init__(self, layers):
        super(FeatureConcat, self).__init__()
        self.layers = layers  # layer indices
        self.multiple = len(layers) > 1  # multiple layers flag

    def forward(self, x, outputs):      # x 不能删
        return torch.cat([outputs[i] for i in self.layers], 1) if self.multiple else outputs[self.layers[0]]


# 将多个特征矩阵的值进行融合(add操作)
class WeightedFeatureFusion(nn.Module):
    def __init__(self, layers):
        super(WeightedFeatureFusion, self).__init__()
        self.layers = layers  # layer indices
        self.n = len(layers) + 1  # number of layers 融合的特征矩阵个数

    def forward(self, x, outputs):
        for i in range(self.n - 1):
            a = outputs[self.layers[i]]  # feature to add
            x = x + a

        return x


# 根据解析的cfg 配置信息,逐层搭建yolo v3 spp网络
def create_modules(modules_defs: list):
    modules_defs.pop(0)         # 将第一个 [net] 信息删除,这里使用不到
    output_filters = [3]        # 对应卷积核的个数,第一个为输入的rgb 3通道
    module_list = nn.ModuleList()   # 网络的模块

    routs = []                 # 统计哪些特征层的输出会被后续的层使用到(可能是特征融合,也可能是拼接)
    yolo_index = -1

    for i, mdef in enumerate(modules_defs):         # 遍历搭建每个层结构
        modules = nn.Sequential()

        # 卷积层
        if mdef["type"] == "convolutional":
            bn = mdef["batch_normalize"]        # bn = 1使用 BN层,0为不启用BN层
            filters = mdef["filters"]           # 卷积核的个数
            k = mdef["size"]                    # 卷积核大小
            stride = mdef["stride"]             # stride 步距

            modules.add_module("Conv2d", nn.Conv2d(in_channels=output_filters[-1],
                                                    out_channels=filters,
                                                    kernel_size=k,
                                                    stride=stride,
                                                    padding=k // 2 if mdef["pad"] else 0,
                                                    bias=not bn))

            if bn:      # 使用BN的话,卷积层后面要接BN层
                modules.add_module("BatchNorm2d", nn.BatchNorm2d(filters))
            else:       # 如果该卷积操作没有bn层,意味着该层为 yolo的 predictor
                routs.append(i)

            if mdef["activation"] == "leaky":
                modules.add_module("activation", nn.LeakyReLU(0.1, inplace=True))
            else:       # 除了 yolo的 predictor,都是leaky激活
                pass

        # 最大池化层
        elif mdef["type"] == "maxpool":
            k = mdef["size"]            # kernel size
            stride = mdef["stride"]
            modules = nn.MaxPool2d(kernel_size=k, stride=stride, padding=(k - 1) // 2)

        # 上采样层
        elif mdef["type"] == "upsample":
            stride = mdef["stride"]
            modules = nn.Upsample(scale_factor=stride)

        # route
        elif mdef["type"] == "route":  # [-2],  [-1,-3,-5,-6], [-1, 61]
            layers = mdef["layers"]
            filters = sum([output_filters[l + 1 if l > 0 else l] for l in layers])      # 距离特征层的个数
            routs.extend([i + l if l < 0 else l for l in layers])
            modules = FeatureConcat(layers=layers)      # 特征层拼接

        # shortcut 结构
        elif mdef["type"] == "shortcut":
            layers = mdef["from"]               # 相对索引
            filters = output_filters[-1]
            routs.append(i + layers[0])
            modules = WeightedFeatureFusion(layers=layers)   # residual 的add操作

        # yolo 层
        elif mdef["type"] == "yolo":
            yolo_index += 1         # 记录是第几个yolo_layer [0, 1, 2]
            stride = [32, 16, 8]    # 不同尺度输出的下采样倍数

            # 对yolo的预测进行后处理
            modules = YOLOLayer(anchors=mdef["anchors"][mdef["mask"]],      # anchor list
                                nc=mdef["classes"],                         # number of classes
                                stride=stride[yolo_index])

            try:
                j = -1
                # bias: shape(255,) 索引0对应Sequential中的Conv2d
                # view: shape(3, 85)
                b = module_list[j][0].bias.view(modules.na, -1)
                b.data[:, 4] += -4.5  # obj
                b.data[:, 5:] += math.log(0.6 / (modules.nc - 0.99))  # cls (sigmoid(p) = 1/nc)
                module_list[j][0].bias = torch.nn.Parameter(b.view(-1), requires_grad=True)
            except Exception as e:
                print('WARNING: smart bias initialization failure.', e)

        else:
            print("Warning: Unrecognized Layer Type: " + mdef["type"])

        # 添加模块
        module_list.append(modules)
        output_filters.append(filters)

    # 相对索引找到信息融合的层
    routs_binary = [False] * len(modules_defs)
    for i in routs:
        routs_binary[i] = True
    return module_list, routs_binary


# yolo 的预测进行处理,不是yolo v3 spp的输出层
class YOLOLayer(nn.Module):
    def __init__(self, anchors, nc, stride):
        super(YOLOLayer, self).__init__()
        self.anchors = torch.Tensor(anchors)        # anchors
        self.stride = stride                        # layer stride 特征图上一步对应原图上的步距 [32, 16, 8]
        self.na = 3                                 # 每一个cell里面预测3个 anchors
        self.nc = nc                                # 预测类别的个数
        self.no = nc + 5                            # 每一个anchor预测的参数个数 ,(x,y,w,h+置信度+ nc)
        self.nx, self.ny, self.ng = 0, 0, (0, 0)    # initialize number of x, y gridpoints
        self.anchor_vec = self.anchors / self.stride    # 将anchors大小缩放到grid尺度
        self.anchor_wh = self.anchor_vec.view(1, self.na, 1, 1, 2)  # batch_size,3,grid_h, grid_w, anchor的w和h
        self.grid = None

    def create_grids(self, ng=(13, 13), device="cpu"):
        self.nx, self.ny = ng
        self.ng = torch.tensor(ng, dtype=torch.float)

        # build xy offsets 构建每个cell处的anchor的xy偏移量(在feature map上的)
        if not self.training:  # 训练模式不需要回归到最终预测boxes
            yv, xv = torch.meshgrid([torch.arange(self.ny, device=device),
                                     torch.arange(self.nx, device=device)])
            # batch_size, na, grid_h, grid_w, wh
            self.grid = torch.stack((xv, yv), 2).view((1, 1, self.ny, self.nx, 2)).float()

        if self.anchor_vec.device != device:
            self.anchor_vec = self.anchor_vec.to(device)
            self.anchor_wh = self.anchor_wh.to(device)

    def forward(self, p):
        bs, _, ny, nx = p.shape  # p为预测值,batch_size, predict_param(75), grid(13), grid(13)
        if (self.nx, self.ny) != (nx, ny) or self.grid is None:  # fix no grid bug
                self.create_grids((nx, ny), p.device)

        # view: (batch_size, 75, 13, 13) -> (batch_size, 3, 75, 13, 13)
        # permute: (batch_size, 3, 75, 13, 13) -> (batch_size, 3, 13, 13, 75)
        # [bs, anchor, grid, grid, xywh + obj + classes]
        p = p.view(bs, self.na, self.no, self.ny, self.nx).permute(0, 1, 3, 4, 2).contiguous()  # permute 内存是不连续的,所以调用contiguous方法

        if self.training:
            return p

        else:  # inference
            # p = [bs, anchor, grid, grid, xywh + obj + classes]
            io = p.clone()  # inference output
            io[..., :2] = torch.sigmoid(io[..., :2]) + self.grid     # sigmoid(x,y) + cell坐标
            io[..., 2:4] = torch.exp(io[..., 2:4]) * self.anchor_wh  # exp(w,h) * anchor (w,h)
            io[..., :4] *= self.stride                  # 将cell坐标,换算映射回原图尺度
            torch.sigmoid_(io[..., 4:])
            return io.view(bs, -1, self.no), p  # view [1, 3, 13, 13, 25] as [1, 507, 25]


# 获取网络中三个"YOLOLayer"模块对应的索引
def get_yolo_layers(self):
    return [i for i, m in enumerate(self.module_list) if m.__class__.__name__ == 'YOLOLayer']  # [89, 101, 113]


# Darknet 网络
class Darknet(nn.Module):
    def __init__(self, cfg):        # 需要传入yolo v3 spp 的配置文件
        super(Darknet, self).__init__()
        self.module_defs = parse_model_cfg(cfg)                # 解析网络对应的.cfg文件
        self.module_list, self.routs = create_modules(self.module_defs)   # 根据解析的网络结构一层一层去搭建
        self.yolo_layers = get_yolo_layers(self)    # 获取所有YOLOLayer层的索引

    def forward(self, x):
        return self.forward_once(x)

    def forward_once(self, x):
        yolo_out, out = [], []      # yolo_out收集每个yolo_layer层的输出,out收集每个模块的输出,作信息融合
        for i, module in enumerate(self.module_list):
            name = module.__class__.__name__
            if name in ["WeightedFeatureFusion", "FeatureConcat"]:  # sum, concat
                 x = module(x, out)  # WeightedFeatureFusion(), FeatureConcat()
            elif name == "YOLOLayer":
                yolo_out.append(module(x))
            else:  # run module directly, i.e. mtype = 'convolutional', 'upsample', 'maxpool', 'batchnorm2d' etc.
                x = module(x)

            out.append(x if self.routs[i] else [])

        if self.training:  # train
            return yolo_out

        else:              # inference or test
            x, p = zip(*yolo_out)  # inference output, training output
            x = torch.cat(x, 1)  # cat yolo outputs

            return x, p


# net = Darknet(cfg='./cfg/my_yolov3.cfg')        # 建立yolo v3 spp网络
# from torchsummary import summary
# print(summary(model=net.cuda(),input_size=(3,512,512)))

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

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

相关文章

VuePress V1 添加 Vssue 评论功能

文章目录 前言选型集成Vssue安装创建 Github OAuth App配置插件使用 Vssue 组件自动创建 Issue 小结参考文献 前言 我的第二本开源电子书《后台开发命令 365》上线啦。 使用 VuePress 将之前记录的后台常用 Linux 命令博文整理成一个系统的开源电子书&#xff0c;方便阅读&am…

LeetCode 周赛 346(2023/05/21)仅 68 人 AK 的最短路问题

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 LeetCode 单周赛第 345 场 体验一题多解的算法之美 单周赛 345 概览 T1. 删除子串后的字符串最小长度&#xff08;Easy&#xff09; 标签&#xff1a;栈 T2. 字典序最小回…

Sui基金正在招聘亚太地区市场经理,期待您的加入

Sui基金会致力于支持Sui网络的开发、增长和推广使用。Sui是基于第一原理重新设计和构建而成的L1公有链&#xff0c;旨在为创作者和开发者能够构建从游戏到金融的独特Web3体验。 Sui基金会三大去中心化原则&#xff1a;拥抱透明且公平的竞争环境、公开沟通以建立信任的文化&…

传染病学模型 | Matlab实现SIR传染病学模型 (SIR Epidemic Model)

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现SIR传染病学模型 (SIR Epidemic Model) 模型介绍 SIR模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIR模型假设每个人可以被感染,感染后会进入恢复…

Kibana:创建你的第一个仪表板

了解从你自己的数据创建仪表板的最常用方法。 本教程将从分析师查看网站日志的角度使用示例数据&#xff0c;但这种类型的仪表板适用于任何类型的数据。 完成后&#xff0c;你将全面了解示例 Web 日志数据。 在本次的展示中&#xff0c;我将使用最新的 Elastic Stack 8.7.1 来…

百分比图:解读数据,驱动业务增长

在当今信息爆炸的时代&#xff0c;数据成为了各行各业决策的重要依据。而在数据展示的众多形式中&#xff0c;百分比图凭借其简洁直观的表达方式和强大的信息传递能力&#xff0c;成为了企业和组织不可或缺的工具。本文将带您一同探索百分比图的魅力&#xff0c;揭示其在决策智…

介绍一下全链路压测平台的相关内容

随着互联网技术的不断发展&#xff0c;越来越多的企业开始依赖互联网来实现业务的发展和增长。而对于这些企业而言&#xff0c;如何保证他们的业务在高并发、高负载的情况下依然能够正常运行&#xff0c;是非常重要的一个问题。为了解决这个问题&#xff0c;企业可以使用全链路…

Vue3+ElementPlus报错集锦

目录 1、导入TS类型报错 2、使用类型报错 3、Vue3引入文件爆红且不提示 4、为defineAsyncComponent引入的component子组件设置类型 1、导入TS类型报错 &#xff08;1&#xff09;报错信息 import type { FormInstance, FormRules } from element-plus 模块 ""e…

精彩回顾 | Fortinet Accelerate 2023·中国区巡展杭州站

Fortinet Accelerate 2023中国区巡展 5月18日&#xff0c;Fortinet Accelerate 2023中国区巡展来到杭州&#xff0c;Fortinet携手太平洋电信、亚马逊云科技等云、网、安合作伙伴&#xff0c;与各行业典型代表客户&#xff0c;就网安融合、网安协同、工业互联网安全、云安全、网…

LC-1080. 根到叶路径上的不足节点(递归DFS)

1080. 根到叶路径上的不足节点 难度中等126 给你二叉树的根节点 root 和一个整数 limit &#xff0c;请你同时删除树中所有 不足节点 &#xff0c;并返回最终二叉树的根节点。 假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit&#xff0c;则该…

网络安全有什么学习误区?

一、网络安全学习的误区 1.不要试图以编程为基础去学习网络安全 不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;且过渡到网络安全用到编程的用到的编程的关键点不多。一般人如果想要把编程学好再开始学习网络安全往…

【STM32系列】基础操作及LED测试

【STM32系列】基础操作及LED测试 资源常用网站整理基本操作恢复出厂设置 欢迎收看由咸鱼菌工作室出品的STM32系列教程。本篇内容主要是开发板的基础操作 资源 首先给大家推荐一些学习micropython的资源网站&#xff0c;文字版直接去我的博客里面翻一下 以下是一些Micropyth…

redis问题汇总

redis的优点 读写性能优异。十万/s的量级&#xff1b; 支持数据持久化。AOF,RDB 支持丰富的数据类型&#xff1b; 支持集群&#xff0c;可以实现主从复制&#xff0c;哨兵机制迁移&#xff0c;扩容等 缺点&#xff1a; 因为是基于内存的&#xff0c;所以虽然redis本身有key过期…

单片机如何通过PWM脉冲控制电机转速?

通过单片机实现对电机自动化控制已经在各行各业得到广泛应用&#xff0c;电机转速灵活使用方便&#xff0c;控制性能好&#xff0c;易于大范围调速。单片机通过PWM脉冲控制电机转速&#xff0c;在现代化生产中起到重要作用。 单片机是一种集成电路芯片&#xff0c;包括处理器、…

传染病学模型 | Matlab实现SIS传染病学模型 (SIS Epidemic Model)

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现SIS传染病学模型 (SIS Epidemic Model) 模型介绍 SIS模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIS模型假设每个人都可以被感染,即没有免疫力,…

PHP中常见的错误与异常处理总结大全

前言 当我们开发程序时&#xff0c;程序出现问题是很常见的&#xff0c;当出现了异常与错误我们该如何处理呢&#xff1f;本文将详细给大家介绍PHP错误与异常处理的相关内容&#xff0c;分享出来供大家参考学习&#xff0c;下面话不多说了&#xff0c;来一起看看详细的介绍&am…

【发电厂用JDHF-1010 合闸(分闸)监测继电器(220V/110V) JOSEF约瑟】

■JDHF-1000合闸(分闸)监测继电器主要用于各种保护和自动控制装置中&#xff0c;作为断路器操作运行状态的监测继电器。■交直流两用■监测继电器具有高内阻特性&#xff0c;可适应各种框架式断路器的合分回路。■快速导轨安装结构&#xff0c;适合各种导轨安装。■螺钉压接式端…

软件详细设计总复习(三)【太原理工大学】

题型及分值&#xff1a; 选择 30 分&#xff0c;填空 20 分&#xff0c; 判断 10 分&#xff0c;简答 20 分&#xff0c;综合设计 20 分。 文章目录 三、行为型模式1. 命令模式2. 迭代器模式3. 观察者模式4. 状态模式5. 策略模式 三、行为型模式 1. 命令模式 举个例子&#x…

面试踩坑合集

文章目录 前言一、String1、String的常用方法 二、多线程1、有几种线程池 三、集合1、hashmap和hashtable的区别2、红黑树转链表的条件 四、SpringMvc1、springMVC的处理流程 五、Sql1、把班级看做一张表&#xff0c;男女平均年纪和人数总数&#xff0c;根据性别分组2、Mysql事…

Kelvin和Rossby波 Part-1(简要介绍)

Equatorial Kelvin and Rossby Waves 赤道Kelvin和Rossby波&#xff1b;在该部分简要介绍 Kelvin waves和Rossby waves是海洋对西风突发等外界作用力变化的调整方式。这种调整是通过受重力、科氏力f以及科氏力的南北变化 ∂ f / ∂ y β \partial f/\partial yβ ∂f/∂yβ影响…