yolov5分析

0 写在前面

因为近期着手写小论文,改进点是偶然做出来的,对代码原理包括传参还不是很清晰,所以最近需要把每一块内容捋清楚。将之前知识结合代码再看一遍。

1 common.py

1.1 导入文件

import numpy as np #数学函数模块
import pandas as pd #panda数组操作模块
import requests #python的http客户端库
import torch # pytorch深度学习框架
import torch.nn as nn # 专门为神经网络设计的模块化接口
from PIL import Image # 图像基础操作模块
from torch.cuda import amp # 混合精度训练模块

from utils.datasets import letterbox
from utils.general import non_max_suppression, make_divisible, scale_coords, increment_path, xyxy2xywh
from utils.plots import color_list, plot_one_box
from utils.torch_utils import time_synchronized

1.2 autopad

def autopad(k, p=None):  # kernel, padding
    # Pad to 'same'
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p

“”"
用于Conv函数和Classify函数
为same卷积或者same池化作自动扩充(0填充)
k参数:卷积核的kernel_size
return p:自动计算的需要pad值
“”"
1.3 Conv

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # 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)
        self.act = nn.SiLU() 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): #用于Models类的fuse函数,融合conv+bn,加速推理 
        return self.act(self.conv(x))

“”
在Focus、Bottleneck、BottleneckCSP、C3、SPP、DWConv、TransformerBlock等模块中调用
标准的卷积由 卷积层+BN层+激活函数构成

    :params c1: 输入的channel值
    :params c2: 输出的channel值
    :params k: 卷积的kernel_size
    :params s: 卷积的stride
    :params p: 卷积的padding  一般是None  可以通过autopad自行计算需要pad的padding数
    :params g: 卷积的groups数  =1就是普通的卷积  >1就是深度可分离卷积
    :params act: 激活函数类型   True就是SiLU()/Swish   False就是不使用激活函数
                 类型是nn.Module就使用传进来的激活函数类型
""

1.4 Focus

理论上从高分辨率图像中,周期性的抽出像素点重构到低分辨率图像中,即将图像相邻的四个位置进行堆叠,聚焦wh维度信息到c通道空,提高每个点感受野,并减少原始信息的丢失。这个组件并不是为了增加网络的精度的,而是为了减少计算量,增加速度。


class Focus(nn.Module):
    # Focus wh information into c-space
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Focus, self).__init__()
        self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
        # self.contract = Contract(gain=2)

    def forward(self, x):  # x(b,c,w,h) -> y(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))
        # return self.conv(self.contract(x))

“”"
在yolo.py的parse_model函数中被调用
理论:从高分辨率图像中,周期性的抽出像素点重构到低分辨率图像中,即将图像相邻的四个位置进行堆叠,聚焦wh维度信息到c通道空,提高每个点感受野,并减少原始信息的丢失,该模块的设计主要是减少计算量加快速度。
Focus wh information into c-space 把宽度w和高度h的信息整合到c空间中
先做4个slice 再concat 最后再做Conv
slice后 (b,c1,w,h) -> 分成4个slice 每个slice(b,c1,w/2,h/2)
concat(dim=1)后 4个slice(b,c1,w/2,h/2)) -> (b,4c1,w/2,h/2)
conv后 (b,4c1,w/2,h/2) -> (b,c2,w/2,h/2)

    :params c1: slice后的channel
    :params c2: Focus最终输出的channel
    :params k: 最后卷积的kernel
    :params s: 最后卷积的stride
    :params p: 最后卷积的padding
    :params g: 最后卷积的分组情况  =1普通卷积  >1深度可分离卷积
    :params act: bool激活函数类型  默认True:SiLU()/Swish  False:不用激活函数
    """

1.5 Bottleneck

class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansion
        super(Bottleneck, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2

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

在BottleneckCSP和yolo.py的parse_model中调用
Standard bottleneck Conv+Conv+shortcut

    :params c1: 第一个卷积的输入channel
    :params c2: 第二个卷积的输出channel
    :params shortcut: bool 是否有shortcut连接 默认是True
    :params g: 卷积分组的个数  =1就是普通卷积  >1就是深度可分离卷积
    :params e: expansion ratio  e*c2就是第一个卷积的输出channel=第二个卷积的输入channel

1.6 BottleneckCSP

class BottleneckCSP(nn.Module):
    # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(BottleneckCSP, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
        self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
        self.cv4 = Conv(2 * c_, c2, 1, 1)
        self.bn = nn.BatchNorm2d(2 * c_)  # applied to cat(cv2, cv3)
        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))))

在C3模块和yolo.py的parse_model模块调用CSPBottleneckhttps://github.com/WongKinYiu/CrossStagePartialNetworks

    :params c1: 整个BottleneckCSP的输入channel
    :params c2: 整个BottleneckCSP的输出channel
    :params n: 有n个Bottleneck
    :params shortcut: bool Bottleneck中是否有shortcut,默认True
    :params g: Bottleneck中的3x3卷积类型  =1普通卷积  >1深度可分离卷积
    :params e: expansion ratio c2xe=中间其他所有层的卷积核个数/中间所有层的输入输出channel数

1.7 C3

是 BottleneckCSP简化版本可以减少参数。

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super(C3, self).__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)
        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
        # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

在C3TR模块和yolo.py的parse_model模块调用
CSP Bottleneck with 3 convolutions

    :params c1: 整个BottleneckCSP的输入channel
    :params c2: 整个BottleneckCSP的输出channel
    :params n: 有n个Bottleneck
    :params shortcut: bool Bottleneck中是否有shortcut,默认True
    :params g: Bottleneck中的3x3卷积类型  =1普通卷积  >1深度可分离卷积
    :params e: expansion ratio c2xe=中间其他所有层的卷积核个数/中间所有层的输入输出channel数

1.8 SPP

将更多不同分辨率的特征进行融合,得到更多信息


class SPP(nn.Module):
    # Spatial pyramid pooling layer used in YOLOv3-SPP
    def __init__(self, c1, c2, k=(5, 9, 13)):
        super(SPP, self).__init__()
        c_ = c1 // 2  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 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)
        return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))

在yolo.py的parse_model模块调用
空间金字塔池化 Spatial pyramid pooling layer used in YOLOv3-SPP

    :params c1: SPP模块的输入channel
    :params c2: SPP模块的输出channel
    :params k: 保存着三个maxpool的卷积核大小 默认是(5, 9, 13)

1.9 concat


class Concat(nn.Module):
    # Concatenate a list of tensors along dimension
    def __init__(self, dimension=1):
        super(Concat, self).__init__()
        self.d = dimension

    def forward(self, x):
        return torch.cat(x, self.d)

在yolo.py的parse_model模块调用
Concatenate a list of tensors along dimension

    :params dimension: 沿着哪个维度进行concat

1.10 Contract & Expand

这两个函数是用于改变feature map的维度,并不是很常用。Contract函数改变输入特征的shape,将feature map的w和h维度(缩小)的数据收缩到channel维度上(放大)。如:x(1,64,80,80) to x(1,256,40,40)。Expand函数也是改变输入特征的shape,不过与Contract的相反, 是将channel维度(变小)的数据扩展到W和H维度(变大)。如:x(1,64,80,80) to x(1,16,160,160)。

class Contract(nn.Module):# 改变输入特征的shape将wh维度(缩小)的数据收缩到channel维度上(放大)
    # Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
    def __init__(self, gain=2):
        super().__init__()
        self.gain = gain

    def forward(self, x):
        N, C, H, W = x.size()  # assert (H / s == 0) and (W / s == 0), 'Indivisible gain'
        s = self.gain
        x = x.view(N, C, H // s, s, W // s, s)  # x(1,64,40,2,40,2)
        x = x.permute(0, 3, 5, 1, 2, 4).contiguous()  # x(1,2,2,64,40,40)
        return x.view(N, C * s * s, H // s, W // s)  # x(1,256,40,40)


class Expand(nn.Module):# contract的反向操作
    # Expand channels into width-height, i.e. x(1,64,80,80) to x(1,16,160,160)
    def __init__(self, gain=2):
        super().__init__()
        self.gain = gain

    def forward(self, x):
        N, C, H, W = x.size()  # assert C / s ** 2 == 0, 'Indivisible gain'
        s = self.gain
        x = x.view(N, s, s, C // s ** 2, H, W)  # x(1,2,2,16,80,80)
        x = x.permute(0, 3, 4, 1, 5, 2).contiguous()  # x(1,16,80,2,80,2)
        return x.view(N, C // s ** 2, H * s, W * s)  # x(1,16,160,160)

你可能感兴趣的:(pytorch,深度学习,python)