目标检测
`提示:以下是本篇文章正文内容,仅仅是自我学习,提高自我理解
一、YOLOV7模型结构
yolov7是在2022年7月6日正式提出的
论文地址:
https://link.csdn.net/?target=https%3A%2F%2Farxiv.org%2Fpdf%2F2207.02696.pdf
源代码地址:
https://github.com/WongKinYiu/yolov7
模型优势:
1、模型重参数化
yolov7的网络架构中是引入模型中参数化,用梯度传播路径的概念分析了适用于不同网络中各层结构重参化策略;
2、标签分配策略
当使用动态标签分配策略时,多输出层的模型在训练时会产生新的问题,比如怎样才能为不同分支更好的输出分配动态目标。
yolov7的标签分配策略采用yolov5的跨网格搜索,和yolox的匹配搜索;
3、ELAN高效网络架构
yolov7中提出了一个新的网络架构,主打一个高效;
4、带辅助头的训练
深度监督是一种常用于训练深度网络的技术,其主要概念是在网络的中间层增加额外的辅助头,以及以辅助损失为指导的浅层网络权重。yolov7是由辅助头的训练方法,可以通过增加训练成本,提升精度,但是不影响预测的时间,辅助头是出现在训练过程中。
1、总体模型架构
可以看到,yolov7的网络结构如上图所示,可以分为3个部分:Backbone,FPN和Yolo Head。
Backbone:对输入图片进行特征提取,yolov7的backbone可以输出3种不同尺寸的特征层;
Neck:加强特征提取,可以利用上采样和下采样进行实现Backbone中提取的3个不同尺寸的特征词进行特征融合;
Yolo Head:对anchor进行分类和回归,最终输出预测框;
2、Backbone
yolov7的backbone主要是由Multi_Concat_Block和Transition_Block组成;
2.1 Multi_Concat_Block
Multi_Concat_Block是一个高效的网络结构,通过控制最短和最长的梯度路径,能学习到更多特征,并且具有更强的鲁棒性;
Multi_Concat_Block结构如上图所示,由2D卷积+标准+激活函数(Con2D_BN_SiLU)构成,一共有4个链路通道;
- 第一个通道经过1次Con2D_BN_SiLU函数(1x1),主要是为了通道数变化;
- 第二个通道也是经过1次Con2D_BN_SiLU(1x1),也是为了做通道变化;
- 第三个通道经过1次Con2D_BN_SiLU(1x1),再经过2次Con2D_BN_SiLU(3x3),目的为特征提取;
- 第四个通道经过1次Con2D_BN_SiLU(1x1),再经过2次Con2D_BN_SiLU(5x5),目的为特征提取;
这四个通道的特征层Concat堆叠后,再由Con2D_BN_SiLU函数进行特征整合;
2.2 Transition_Block
Transition_Block的模型结构由上图所示,这是一个过度模块,此过度模块由两个分支通道组成:
- 左侧通道为的最大池化层+Con2D_BN_SiLU,最大池化的作用为下采样,再经过1x1的卷积进行通道数改变;
- 右侧通道为Con2D_BN_SiLU+Con2D_BN_SiLU;先进性1x1的卷积做通道数变化,再经过3x3卷积,步长为2的卷积核进行下采样;
这左右两个通道再进行Concat堆叠,这两个通道堆叠在一起就得到了超级下采样的结果;
3、Neck
Yolov7的Backbone提取了3个不同尺寸的特征层,输入的尺寸固定为640x640x3,三个特征层尺寸输出为80x80x512,40x40x1024,20x20x1024。这三个特征层都会进入FPN进行加强特征提取。
FPN结构,将不同尺度的特征进行融合,充分利用Backbone提取的特征信息。
Neck层包括SPPCSPC, ELANPAN
1、最底层特征的尺寸大小为20x20x1024,本层特征会经过SPPCSPC进行特征提取,本结构可以提升YoloV7的感受野;
以上为SPPCSPC的模型结构,更加丰富了特征信息;
3、Yolo Head
YoloV7是Yolo Head前使用了RepConv的结构,在训练时引入一个经过特殊设计的残差结构,在实际预测的时候,该结构等效于一个3x3的卷积;
Rep的模块分为2个,一个时Train,一个是deploy
训练模块有3个分支:上层分支为3x3的卷积,用于特征提取;中层分支为1x1的卷积,用于平滑特征;底层分支为BN,不做卷积操作,最后将其相加
推理模块包含一个3x3的卷积,步长为1,由训练模块重参数化转换而来。
经过Neck(FPN特征金字塔),我们可以得到3个加强特征:(N,20,20,255),(N,40,40,255),(N,80,80,255),N为通道数。将这三层加强特征传入Yolo Head,获得预测结果
每个输出的通道数255都可以分解为3个85,对应这3个先验框的85个参数。
先验框的85个参数可以拆解为4+1+80,4代表每个特征点的回归参数,用于获得调整后的预测框,1代表判断特征点是否含有物体,80用以判断每个特征点所包含的物体种类;
计算步骤:
- 进行中心点预测,利用回归预测(前两位)的结果对中心点偏移进行计算;
- 进行预测框宽高预测,利用回归预测(后两位)的结果计算预测框的宽高;
- 将预测框绘制在图片上
模型重参数化:
模型重参数化的技术在推理阶段将多个计算模块合并为1个,是一个集成技术,可以将其分为2类,即模块级集成和模型级集成。为了获得最终的推理模型,有2中常见的模型级重参数化时间;一种是利用不同额训练数据训练多个相同的模型,然后平均多个训练模型的权重,二是对不同迭代次数的模型权重进行加强平均
二、使用步骤
1.引入库
代码如下(示例):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
2.模块代码
2.1 backbone
import torch
import torch.nn as nn
def autopad(k, p=None):
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k]
return p
class SiLU(nn.Module):
@staticmethod
def forward(x):
return x * torch.sigmoid(x)
class Conv(nn.Module):
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=SiLU()): # ch_in, ch_out, kernel, stride, padding, groups
super(Conv, self).__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2, eps=0.001, momentum=0.03)
self.act = nn.LeakyReLU(0.1, inplace=True) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
def forward(self, x):
return self.act(self.bn(self.conv(x)))
def fuseforward(self, x):
return self.act(self.conv(x))
class Multi_Concat_Block(nn.Module):
def __init__(self, c1, c2, c3, n=4, e=1, ids=[0]):
super(Multi_Concat_Block, self).__init__()
c_ = int(c2 * e)
self.ids = ids
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = nn.ModuleList(
[Conv(c_ if i ==0 else c2, c2, 3, 1) for i in range(n)]
)
self.cv4 = Conv(c_ * 2 + c2 * (len(ids) - 2), c3, 1, 1)
def forward(self, x):
x_1 = self.cv1(x)
x_2 = self.cv2(x)
x_all = [x_1, x_2]
# [-1, -3, -5, -6] => [5, 3, 1, 0]
for i in range(len(self.cv3)):
x_2 = self.cv3[i](x_2)
x_all.append(x_2)
out = self.cv4(torch.cat([x_all[id] for id in self.ids], 1))
return out
class MP(nn.Module):
def __init__(self, k=2):
super(MP, self).__init__()
self.m = nn.MaxPool2d(kernel_size=k, stride=k)
def forward(self, x):
return self.m(x)
class Transition_Block(nn.Module):
def __init__(self, c1, c2):
super(Transition_Block, self).__init__()
self.cv1 = Conv(c1, c2, 1, 1)
self.cv2 = Conv(c1, c2, 1, 1)
self.cv3 = Conv(c2, c2, 3, 2)
self.mp = MP()
def forward(self, x):
# 160, 160, 256 => 80, 80, 256 => 80, 80, 128
x_1 = self.mp(x)
x_1 = self.cv1(x_1)
# 160, 160, 256 => 160, 160, 128 => 80, 80, 128
x_2 = self.cv2(x)
x_2 = self.cv3(x_2)
# 80, 80, 128 cat 80, 80, 128 => 80, 80, 256
return torch.cat([x_2, x_1], 1)
class Backbone(nn.Module):
def __init__(self, transition_channels, block_channels, n, phi, pretrained=False):
super().__init__()
#-----------------------------------------------#
# 输入图片是640, 640, 3
#-----------------------------------------------#
ids = {
'l' : [-1, -3, -5, -6],
'x' : [-1, -3, -5, -7, -8],
}[phi]
# 640, 640, 3 => 640, 640, 32 => 320, 320, 64
self.stem = nn.Sequential(
Conv(3, transition_channels, 3, 1),
Conv(transition_channels, transition_channels * 2, 3, 2),
Conv(transition_channels * 2, transition_channels * 2, 3, 1),
)