Yolov5网络结构及其他

网络结构

Yolov5网络包括四部分,输入端,backbone骨干网络,neck颈部网络,Prediction

Yolov5使用了CSPDarknet53作为骨干网络,用于提取图片特征,包括CBL,FOCUS,CSP,SPP模块

CBL

CBL为标准卷积模块,包括普通卷积层Conv、批量归一化层BN和LeakyReLU激活函数层

import torch
import torch.nn as nn

# CBL
def autopad(k, p=None):  # kernel,padding
    # pad to "same":
    # H_out=H_in;W_out=W_in
    # H_out=(H_in+2p-k)/s+1
    # W_out=(W_in+2p-k)/s+1
    if p is None:
        if isinstance(k,int):
            p = k//2 if k%2==1 else k//2 -1
        else:
            p = [x // 2 if x % 2 == 1 else x // 2 - 1 for x in k]
    return p

Standard Convolution

class Conv(nn.Module):
    def __init__(self,c1,c2,k=1,s=1,p=None,g=1,act=True):
        super(Conv,self).__init__()
        self.conv=nn.Conv2d(c1,c2,k,s,autopad(k,p),groups=g,bias=False)
        self.bn=nn.BatchNorm2d(c2)
        self.act=nn.LeakyReLU(0.01) if act 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)) 

Focus

Focus模块是对图片进行切片操作,具体操作是在一张图片中每隔一个像素取一个值,类似于邻近下采样,可以得到四张采样图片,四张图片互补,图片内容差不多,但是整体操作没有信息丢失。Focus将图片H*W空间维度的信息转换到了Channel通道维度,H和W均减为原来一半,Channel扩充为原来四倍,然后将新图片经过一次卷积操作,得到没有信息丢失情况下的二倍下采样特征图

class Focus(nn.Module):
    # ch_in,ch_out,kernel,stride,padding,groups
    def __init__(self,c1,c2,k=1,s=1,p=None,g=1,act=True):
        super(Focus,self).__init__()
        self.conv=Conv(c1*4,c2,k,s,p,g,act)

    def forward(self,x):  # (b,c,w,h)-(b,4c,w/2,h/2)
        return self.conv(torch.cat([x[...,::2,::2],
                                    x[...,1::2,::2],
                                    x[...,::2,1::2],
                                    x[...,1::2,1::2]],1))

CSP

ResUnit模块

class Bottleneck(nn.Module):
    def __init__(self, c1, c2, shortcut=True, g=1,e=0.5):
        super(Bottleneck,self).__init__()
        c_ = int(c2*e)
        self.cv1 = Conv(c1, c_, k=1, s=1)
        self.cv2 = Conv(c_, c2, k=3, s=1, g=1)
        self.add = shortcut and c1 == c2

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

ResUnit模块的shortcut参数设置为True,就是CSP1_X模块的组件;设置成False,就是CSP2_X模块的组件

CSP1_X模块

class BottleneckCSP(nn.Module):
    def __init__(self,c1,c2,n=1,shortcut=True,g=1,e=0.5):
        super(BottleneckCSP,self).__init__()
        c_=int(c2*e)    # hidden channels
        self.cv1=Conv(c1, c_, k=1, s=1)
        self.cv2=nn.Conv2d(c1, c_, k=1, s=1, bias=False)
        self.cv3=nn.Conv2d(c_, c_, k=1, s=1, bias=False)
        self.cv4=Conv(2*c_, c2, k=1, s=1)
        self.bn=nn.BatchNorm2d(2*c_)
        self.act=nn.LeakyReLU(0.1, inplace=True)
        self.m=nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])

    def forward(self,x):
        y1=self.cv3(self.m(self.cv1(x)))
        y2=self.cv2(x)
        return self.cv4(self.act(self.bn(torch.cat((y1,y2),dim=1))))


CSP2_X模块

class BottleneckCSP(nn.Module):
    def __init__(self,c1,c2,n=1,shortcut=False,g=1,e=0.5):
        super(BottleneckCSP,self).__init__()
        c_=int(c2*e)    # hidden channels
        self.cv1=Conv(c1,c_,k=1,s=1)
        self.cv2=nn.Conv2d(c1,c_,k=1,s=1,bias=False)
        self.cv3=nn.Conv2d(c_,c_,k=1,s=1,bias=False)
        self.cv4=Conv(2*c_,c2,k=1,s=1)
        self.bn=nn.BatchNorm2d(2*c_)
        self.act=nn.LeakyReLU(0.1,inplace=True)
        self.m=nn.Sequential(*[Bottleneck(c_,c_,shortcut,g,e=1.0) for _ in range(n)])
    def forward(self,x):
        y1=self.cv3(self.m(self.cv1(x)))
        y2=self.cv2(x)
        return self.cv4(self.act(self.bn(torch.cat((y1,y2),dim=1)))) 

SPP模块

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_,k=1,s=1)
        self.cv2=Conv(c_*(len(k)+1),c2,k=1,s=1)
        self.m=nn.ModuleList([nn.MaxPool2d(kernel_size=x,stride=1,padding=x//2) for x in k])
        
    def forward(self,x):
        x=self.cv1(x)
        # [x,m1(x),m2(x),m3(x)]
        return self.cv2(torch.cat([x]+[m(x) for m in self.m],1))

Yolov5的改进

1.RFP(Recursive Feature Pyramid)递归特征金字塔

∀ i = 1 , . . . , S , ∀i = 1, ..., S, i=1,...,S,
f i = F i ( f i + 1 , X i ) , X i = B i ( X i − 1 , R i ( f i ) ) f_i=F_i(f_{i+1} ,X_i ),X_i=B_i(X_{i-1},R_i(f_i)) fi=Fi(fi+1,Xi),Xi=Bi(Xi1,Ri(fi))
增加递归循环操作:
∀ i = 1 , . . . , S , t = 1 , . . . T , ∀i = 1, ..., S,t = 1, ...T, i=1,...,S,t=1,...T,
f i t = F i t ( f i + 1 t , X i t ) , X i t = B i t ( X i − 1 t , R i ( f i t − 1 ) ) f_i^t=F_i^t(f_{i+1}^t ,X_i^t),X_i^t=B_i^t(X_{i-1}^t,R_i(f_i^{t-1})) fit=Fit(fi+1t,Xit),Xit=Bit(Xi1t,Ri(fit1))
不同于传统的FPN,PAN,RFP使用几个(T=2,即2个)递归循环,加深了特征与图像的融合,保证了细节

2.ASPP(Atrous Spatial Pyramid Pooling)膨胀空间金字塔池化

ASPP在卷积神经网络的最后一个卷积层后添加多个并行的池化分支,每个分支具有不同的采样率(或称为膨胀率)来捕获不同尺度的上下文信息。这样可以允许模型在不同层次上感知和分析图像的语义信息。

3.SAP(Switchable Atrous Convolution)

SAC通过引入不同的膨胀率的空洞卷积和权重锁定机制,使得模型能够根据不同的感受野需求自适应地调整卷积核。这样可以在减少参数和计算量的同时,提高模型的灵活性和适应性。

Yolov5损失函数

Yolov5损失函数分为三部分

1.类别损失(classes loss)

采用交叉熵损失计算类比损失
有N个类别,则预测结果是一个N维的概率分布向量

L = -sum(y_true * log(y_pred))

其中,y_true表示真实标签向量,y_pred表示模型的预测概率分布向量,log表示自然对数运算。

2.置信度损失(confidence loss)

采用二值交叉熵损失计算置信度损失
标签只有是和否两种,显然

L=-PlogP-(1-P)log(1-P)

3.定位损失

Yolov5使用CIOU loss来衡量矩形框的损失

GIoU

GIoU=IoU-(Ac-U)/Ac  
# Ac是包含两张图片的最小矩形面积,U是并集,I是交集,IoU即交并比
GIoULoss=1-GIoU=2-I/U-U/Ac   # 取值[0,2)

DIoU

DIoU损失函数是对GIoU的改进,引入了框的中心点距离。
定义如下:
D I o U = I o U − d 2 C 2 DIoU = IoU - \frac{{d^2}}{{C^2}} DIoU=IoUC2d2 #取值范围[-1,1]
d表示两个框的中心点之间的欧氏距离。
d = ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 d = \sqrt{{(x2 - x1)^2 + (y2 - y1)^2}} d=(x2x1)2+(y2y1)2
C 表示两个框的对角线长度。
C = max ⁡ ( w 1 , w 2 ) 2 + max ⁡ ( h 1 , h 2 ) 2 C = \sqrt{{\max(w1, w2)^2 + \max(h1, h2)^2}} C=max(w1,w2)2+max(h1,h2)2
DIoU损失函数在GIoU的基础上加入了中心点距离的项,能够更好地处理框的尺度不匹配和位置偏移的问题。
DIoULoss=1-DIoU

CIoU

CIoU损失函数是对DIoU的改进,进一步考虑了框的长宽比例。
定义如下:
C I o U = D I o U − α v CIoU = DIoU - \alpha v CIoU=DIoUαv # CIOU取值范围[-1.5,1]
α = v 1 − I o U + v \alpha=\frac{v}{1 -IoU+v} α=1IoU+vv
v = 4 π 2 ( arctan ⁡ w 1 h 1 − arctan ⁡ w p h p ) 2 v = \frac{4}{\pi^2}(\arctan\frac{w_1}{h_1}-\arctan\frac{w_p}{h_p})^2 v=π24(arctanh1w1arctanhpwp)2
v为框A、框B的宽高比相似度,α为v的影响因子
CIoULoss=1-CIoU

其他知识

LeakyReLU激活函数

LeakyReLU激活函数是ReLu的变种,解决了Relu输入负值梯度为0造成神经元死亡

import torch
import torch.nn as nn

leaky_relu=nn.LeakyReLU(negative_slope=0.001,inplace=False)
#带泄露线性整流函数,负斜率为0.001,即输入负值时斜率固定为小值,
#inplace:是否进行原地操作(in-place operation)。如果设置为True,则会将激活函数应用于输入张量本身,而不会创建新的张量。默认值为False

input_tensor = torch.randn(1,3,50,50)  # 3个通道数,图像的尺寸是50X50

output = leaky_relu(input_tensor)

卷积计算公式:

H_out=(H_in+p*2-k)/s+1
W_out=(W_in+p*2-k)/s+1

p是填充,输入特征图周围填充零值元素,常见的有valid(不填充),same(填充至相同输入输出大小)
s,步长,k,卷积核大小

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值