当前位置: 首页 > article >正文

yolov5实战拓展

AutoDL服务器部署训练

注册过程不予显示,简单的yolov5其实不需要什么高级卡,毕竟远道1050-4G版,近到4060-8g都可以部署

选择后,看到镜像

不要选择!!!因为训练的分数是0 ,选择基础版本,pytorch1.9 ubantu扩大版本11.1以上

立刻创建!!!jupyter lab的方式打开,直接把github的yolov5的文件夹拖入即可

如果是zip,终端输入 unzip 文件夹名 解压                    cd进入后,和原来一样环境等等等等

conda activate base
conda init

退出后再进入即可

pip install -r requirements.txt

根据信息合理修改txt内容,可以尝试将处理的pt文件上传测试是否正常

python detect.py

同上一章操作方法

可以选择直接上传字体到文件夹中,节省报错

mv Arial.ttf /root/.config/U1tralytics/
python train.py
tar -cvf runs.tar.gz runs/

就可以下载了

pycharm基本使用方法

自己找一个pycharm professional破解版或者为他花点钱

本人下载的是2023.3版本,创建编译器环境,选择你的conda即可

就是可以更直接的找到那个conda的python.exe

有一个NLP常用的包

pip install jieba

在文件中import jieba,发现返回了'没有找到jieba包'

这是因为默认的terminal在windows的powershell运行,和cmd有点问题,将图纸内容改为cmd即可

 如何链接auto服务器?

提前开机auto,有一个SSH登录指令如下填写。      test填写即可

 修改直接在本地修改代码,自动上传在tools----deployment-----Automatic---Update

自动获取tools----deployment-----brows remote host,就可以看到服务器里面文件,download forhere即可

想跑一些参数,就连到终端上面

如果 想调整服务器和本地编译器,右上角

 点击Edit Configurations 直接更换即可

VsCode基本使用方法

直接安装后,选择python的插件,选择解释器,即右下角,选择你的conda环境就OK

由于powershell的影响,在右边有一个选项选择默认配置文件换成cmd

如何建立一个服务器呢?

安装remote-ssh插件,connect to host,add new ssh host,copy一下auto服务器上的ssh,密码即可

vscode其实是直接处理服务器,因此就像网页端的Auto使用即可

第二次打开很容易出来

YOLOV5模型结构和构建原理

结构定义在文件夹中

1.models/yolov5*.yaml              2.models/yolo.py           3.models/commons.py  

比如我们打开yolov5s.yaml的文件,如此代码借助注释和ai可以知道到底是干什么用的

实在不理解提供Tensorboard可视化帮助,,在终端输入如下代码

tensorboard --logdir runs 

点击GRAPHS,就可以看见了。
那他现在的话是非常简单的
就是只有一个输入
然后一个detection model
然后一个output
然后我们只要双击这个detection model
它就可以把整个网络结构展开了
然后我的鼠标滚轮的话
拖滚轮滚动的话
它就是放大缩小
然后按住的话拖动它
就会把这个图就可以进行拖动了
O
那我们现现在的话就对着这个图
以及刚才的这个结构
然后来把YOLOV我的整体的结构过一下
那过渡过程中呢
我也会根据tt bot中的这个提示啊
或者这个信息吧
告诉大家
就是这个数据在网络中经过每一层之后
它都是怎么变换的
OK那让我们开始
那首先前面可能比较简单了
在BACKBALL里面的话
你会发现他前面的话就是按照顺序走下来
每一层的这个输入都是一对吧
那其实说白了就是上一层的输出
就是当前层的输入
那我们看一下实际的这个结构
那我们有一个输入input就是原始的这个图片
那它的结构它的维度的话就是1×3乘
640×640
然后紧接着输入第一层CNV0
那这里的话可以看到它的输入是1×3
乘以640×640
然后经过它之后
整个的这个特征就变成了1×32乘三二
0×320
那可以看到它比以前缩小了1/2对吧
那这个怎么在以前在这个压缩文件里面体现呢
所以就在这里面
就他最后除以二
就意思就是告诉我们
我们现在整个特征图变为了原来的1/2
然后我们再往下看
其实它在里面标记了很多处啊
就除以4÷8
除以16
除以32对吧
我们可以看一下
那下一次他说下次又变成除以四
那说明特征图又变小了
那么原始值640×640
然后经过一次除以二
变成320×320
然后再除以四
然后我们再来看一下
就这个CV一点一下可以看到
那这个时候他的这个输入就是1×32乘
320×320
他的这个输出的话就是1×64乘100
6×160对吧
可以看到它确实是变成了呃
原始图像的这个多少分之一
这里的话还要再数点
就看它它前面是什么意思呢
前面这个零的话
大家可以理解就是他是对这个网络的一个编号
为什么他有这么一个编号呢
是因为他后面的话要用到
因为它的虽然前面backbone的这个网络结构
是上一层的输出
就是下一层的输入
但是他在后面的时候
它就不是这样的结构了
它可能会引用前面的那个
他可能会把前几层的输出连接直接连到后面去
就是不经过其他的操作
直接连到后面去
所以说他要对他进行记录
包括后面我们在改结构的时候
这个编号也是非常重要的
我们要把它记清楚
那第一层的话就是从零开始标
然后01234567这样走下来
然后其实可以
我们在这个图上也是可以清晰看到的
那我的这个比如这个第一层卷积
那就是零
第二层卷积就是一
第三层就是第三套
这个C3就是二对吧
就他这里没有标出来的这个二
那紧接着
然后我们继续再往下走
又是一层的这个卷积对吧
那可以看到那这一层的时候
他的输出就是160×160
然后他在输出的时候啊
这里可能它因为它输出了两个tensor
然后他俩就没标出来
我们可以把它嗯取消一下
双击的话就可以打开这个网络的细节啊
我们可以双击它
然后可以看到他这一步有越来越模糊
但是可以看到它最终变成了
就是128×80乘80这样的一个结构
现在就是又往下缩了一层
正规四卷积的话
他就往下
他就做了一次
当于做了一次下采样
然后再继续往下走到C3的这个第四层C3
然后一次一次你会发现
然后到第四层的时候
它就分叉了
那我们先不走它分叉这个路线
我们先按照backbone这个路线把它走下去
就是C3
紧接着后面再接一层卷积
然后到这个第五层
然后再C3到第六层
那这段话
然后再继续
然后到第七层
第八层
然后接下来紧接着过了一个SPPF是第九层
直到这个第九层啊
或者说应该是整个的第十层了啊
就是正好应该是我们这个backbone结束的这一层
那我们可以看啊
就它有几个关键的点
他做了一些特征的变换
就是每次卷积的话
就每一次单独的卷积层
其实他都是做了一次下采样
从第一次卷积
然后让它变成整体的一半
就是就是640
再除以二三百二
然后再一次卷积
然后再除以二变成160
然后再一次是80
再过一次是40
然后经过CNV7之后
它现在就变成了20×20了
对还有个特征图是这样变换的
OK那我们继续往下
继续往下走的话
就到了这个head的部分
head的部分的话呃
其实解析下肯定前面是类似的
就是说他第一层这个head
我们也是按照这个序号走吧
他第一层的这个head其实就是先还是一个卷积
就对应的是图里面的这个位置
然后紧接着一次on sample
就是一次上采样on sample
然后下面的话他做了一次拼接
可以看到它这里的话他告诉你
他要把这个backbone p4的这个地方给他拼过来
那所以说它里的输入的话就变成了一个list
那就是说这里的是一
是指上一层六的话
就是说我第六层的这个输出
然后做了一个拼接
那我们来看一下是不是原始也是这样
就说他在这一次的时候
可以看到
它是把第六层C3的这个输出做了一下拼接
那为什么他们可以拼在一起呢
是因为呃C3之后的就是这个CNV7
就是第七层
这次卷积他又做了一次下采样
但是在我们这里的话
第11层他又做了一次上采样
相于那我的这个就上采样之后
和我做下采样之前的这个维度
他们两个是一样的
所以说我们还可以把它拼起来
然后紧接着再过一层这个C3
然后到实际13层下面的话继续往下走
然后又经过一层卷积
那那这里的卷积我们看一眼
就他这里的卷积
其实你会发现它并没有再去做采样
就是说在做下采样
而是让它维度保持不变
然后我们在这这个基础上再去做一次上采样
就是说在P7的时候还变成了2020
那20×20我们再做一次上采样
变成40×40
那40×40再做上采样之后
现在就会变成80×80
就这里的这个上采样变成80×80
然后这里的这个contact16就是第就这个第16层
他是把第15层的输出
以及第四层的输出拼起来了
就是说我们往前面去倒这个C3对吧
然后把它拼接起来
然后紧接着继续往下走到
然后又到了一层
这个C3C3完之后
他又做了一次卷积
但这次卷积的时候他又做了一次下采样
然后再拼接
然后再C3
然后再CNV
然后把它看开了起来
然后他一直一直到C23
然后就相当于所有的前面的这个
这一部分就都结束了
就不管它怎么拼吧
但是它的原理是一样的
那最后一层到了这个detect层
它是把第17层的这个特征图
还有第20层
还有第23层的特征都都拿出来了
那可以看到他这里有介绍
就是说我第17层的这个输出
其实他是做了八倍下采样
那整个特征图就变成了80×80
然后这个的话是做了16倍下采样
这个第二三层的话
他是做了32倍下采样
那后面这个small和这个media
还有这个large是干什么的
是什么意思呢
就是说我们可以用用这一层
就是说第17层这个800下采样的特征图
是用来检测小目标的
然后这一层40×40的话
它是用来检测这个中等大小的目标的
最后一层32倍下采样的这个特征图
它是用来检测这些比较大的物体的结构的话
大概就是这样的一个结构
那我们其实后续要修改这个模型的话
其实就是在这里面修改了

YOLOv5s 网络结构概览
输入层:

输入尺寸: 1x3x640x640 (批量大小 x 通道数 x 高度 x 宽度)
Backbone (主干网络):

Conv0: 初始卷积层, 将输入从 3x640x640 转换为 32x320x320。此步骤进行了1/2的下采样。
C3模块:
C3_1: 输入 32x320x320 -> 输出 64x160x160 (1/4下采样)
C3_2: 输入 64x160x160 -> 输出 128x80x80 (1/8下采样)
C3_3: 输入 128x80x80 -> 输出 256x40x40 (1/16下采样)
C3_4: 输入 256x40x40 -> 输出 512x20x20 (1/32下采样)
SPPF (Spatial Pyramid Pooling - Fast): 在最后一个C3模块后应用,增强模型对不同尺度特征的捕捉能力。
Neck (颈部网络):

上采样与拼接:
第一次上采样: 512x20x20 -> 256x40x40,与C3_3的输出拼接,形成 512x40x40。
第二次上采样: 256x40x40 -> 128x80x80,与C3_2的输出拼接,形成 256x80x80。
C3模块:
C3_5: 输入 512x40x40 -> 输出 256x40x40
C3_6: 输入 256x80x80 -> 输出 128x80x80
Head (头部网络):

检测层:
第17层: 输入 256x40x40 -> 输出 255x40x40 (用于检测中等大小目标)
第20层: 输入 128x80x80 -> 输出 255x80x80 (用于检测小目标)
第23层: 输入 512x20x20 -> 输出 255x20x20 (用于检测大目标)
每个阶段的输出尺寸反映了不同尺度下的特征图,这些特征图被用于多尺度目标检测,从而提高了模型对于不同大小目标的检测能力。通过上采样和特征图拼接,YOLOv5s能够有效地利用来自多个尺度的信息,这对于提升检测精度至关重要。

我们从train.py看起,直接看Model,第132行就是创建yaml的文件代码,看到创建了yolo.py里面的DetectionModel,下方有178行和nc。

理论写起来太麻烦啦!               鄙人没有基础很难全写出来!此处暂未完工!!!

修改网络结构-以C2f为例

我们借鉴v8的代码https://github.com/ultralytics/ultralytics (YOLOv8)

修改顺序如下
models/commons.py------>加入新增网络结构                                                                      models/yolo.py—---------->设定网络结构的传参细节
models/yolov5*.yaml----->修改现有模型结构配置文件                                                                      train.py---------------------->训练时指定模型结构配置文件

打开v8下的nn网络结构文件夹。我们注意到yolov5里面在common.py中写了这个结构的定义,在v8中,全部写在了module中

我们可以把modules.py的C2f完全复制到commons.py的C3前面:在最新版2023.8中,modules.py以及变成了包含多个文件的文件夹,打开block.py找到C2f

class C2f(nn.Module):
    """Faster Implementation of CSP Bottleneck with 2 convolutions."""

    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5):
        """Initializes a CSP bottleneck with 2 convolutions and n Bottleneck blocks for faster processing."""
        super().__init__()
        self.c = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv((2 + n) * self.c, c2, 1)  # optional act=FReLU(c2)
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))

    def forward(self, x):
        """Forward pass through C2f layer."""
        y = list(self.cv1(x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

    def forward_split(self, x):
        """Forward pass using split() instead of chunk()."""
        y = list(self.cv1(x).split((self.c, self.c), 1))
        y.extend(m(y[-1]) for m in self.m)
        return self.cv2(torch.cat(y, 1))

其中 由于k参数未定义,我们需要copy定义地方Bottleneck,改名为C2f...,同时C2f中也要改

class C2fBottleneck(nn.Module):
    """Standard bottleneck."""

    def __init__(self, c1, c2, shortcut=True, g=1, k=(3, 3), e=0.5):
        """Initializes a standard bottleneck module with optional shortcut connection and configurable parameters."""
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, k[0], 1)
        self.cv2 = Conv(c_, c2, k[1], 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        """Applies the YOLO FPN to input data."""
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

那么在原先传入C3的地方加入C2f就好了:在yolo.py parse_model下319行加C2f,325行同理

if m in {BottleneckCSP, C3, C3TR, C3Ghost, C3x,C2f}:

接下来修改 yolov5s.yaml,把backbone中的C3全改成C2f,head部分可改可不改

修改train.py的435行,可以把yolov5s.yaml改为自定义名字

def parse_opt(known=False):
    parser = argparse.ArgumentParser()
    parser.add_argument('--weights', type=str, default=ROOT / 'yolov5s.pt', help='initial weights path')
    parser.add_argument('--cfg', type=str, default=ROOT / 'models/yolov5s.yaml', help='model.yaml path')

直接python  train.py即可

可能会好奇,我们这里传了CONFIG,是这个cf
,但是我们这个位置传的yolov5s.pt,他这两个结构也不一样,还能不能就是说预训练,或者说借用之前的权重?

其实是可以的,我们可以先简单来看一下,这里其实这个参数也比较关键,就是说当我们传这个位置的时候,他就会去尝试从这个权重里面进行加载权重,那如果说我们不传,那它就相当于就是完全的从头开始训练这部分的逻辑,就是我们上次课讲的那个模型创建的一部分

他这里有一个参数,有一个判断叫这个PRETRAIN,如果pt结尾的话,说明是一个预训练权重,然后就会尝试去加载然后把它给拷贝过去,会做一下迁移,那如果说你不传就没有了 

引入注意力机制-以SE为例

ZhugeKongan/Attention-mechanism-implementation: Self-attention、Non-local、SE、SK、CBAM、DANet (github.com)

其中SE-block.py就是代码部分了,把他放到common.py 的C2f前,并且import

import torch.nn as nn
import torch.nn.functional as F

class SE(nn.Module):

    def __init__(self, in_chnls, ratio):
        super(SE, self).__init__()
        self.squeeze = nn.AdaptiveAvgPool2d((1, 1))
        self.compress = nn.Conv2d(in_chnls, in_chnls // ratio, 1, 1, 0)
        self.excitation = nn.Conv2d(in_chnls // ratio, in_chnls, 1, 1, 0)

    def forward(self, x):
        out = self.squeeze(x)
        out = self.compress(out)
        out = F.relu(out)
        out = self.excitation(out)
        return x*F.sigmoid(out)

这个SE它到底是做了什么?

它里面定义了三个模块
一个是适应的那种平均值化
一个是然后再加上两个卷积
一个是把这就是输入通道数
然后卷积之后把它压缩一点,压缩完之后,然后再把压缩再返回,再卷成之前的输入通道数.
那具体做的话就是先进行这个直画
然后进行一次压缩
然后再进行一次
就是怎么说呢
再涨回来
然后紧接着我们把涨回来的那个结果
进行一个用
用那个sigmoid参数进行激活
然后再去乘上原始这个x

就是它的输入通道
输入通道以及它的这个参数
那可以看到在这个SE的这个结构里面
其实它只有两个参数
一个是in channels
一个是这个ratio in channelse的话
其实它就是输入通道了
只不过在SE1这个里面它比较特殊一点
他的这个输入通道和输入通道其实是一样的
对吧
就是他这个先压缩
后来又给它转回去是一个值
这跟我们上次这个cf它有两个通道
这个其实是有些差异的对吧
然后接下来的话它还有个ritual
这个值就是说我中间这个银层
就是我第一次卷积之后
他的那个输入通道数
应该是我这个in china除以这个ratio
就是那个输入通道数
相当于整个的这个SE的这个结构
我们一共需要传递两个参数对吧
OK我们要把这个记清楚

接下来在yolov5s.yaml中backbone最后一行添加

[-1,1,SE,[1024,2]]

那么这一层变成第10层,接下来head每一行都要加一层 ,其中大于10的都要进行修改+1

head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 15], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 11], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[18, 21, 24], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]
 

 在yolo.py中添加343行

elif m is SE:
    c1= ch[f]#这里要注意,为什么要设定C2,而不是用C1,因为parse_model中,c2是用来记录输出维度的,最后会将c2存入ch中,所以一定对c2的值进行设定
    c2= args[0]
    if c2 != no: # if not output
        c2 = make_divisible(c2 * gw, 8)
    args =[c1, args[1]

替换主干网络-以MobileNet为例

新建demo.ipynb文件并且pip install torchinfo

此处缺少部分完善

在替换主干网络的部分需要考虑的一点

其实是很有考究的对吧
一定要有它经过八倍采样以及16倍采样32倍的采样的输出
为什么呢
因为首先啊你的这个八倍的采样会在这儿来用
会在就是第17层前面来用
然后以16倍的采样会在head的第一部分来用
然后你要想用它的话
你首先你保证你的这个进head之前
它一定是一个20×20
就是32倍下采样的一个结构

我们的主干网络替换完成之后,一定也是有经过这几次采样的,一定要有一层,它的那个结构对应的是八倍下采样,一定要有一层对应的是16倍下采样,还有一层要对应的是32倍下采样
只有这样的话才能完美的去继承整个结构,可以让这个high的部分直接就去复用.

import torchvision.models as models
from torchinfo import summary
model = models.mobilenet_v3_small(pretrained=True, progress=True)
summary(model.features, input_size=(1, 3, 640, 640))
model
type(model.features)
model.features[:4]
model.features[4:9]
model.features[9:]
import fiftyone as fo
import fiftyone.zoo as foz
dataset = foz.load_zoo_dataset(
    "open-images-v7",
    split="validation",
    label_types=["detections"],
    classes=["Mouse"],
    max_samples=100,
    seed=51,
    shuffle=True,
    dataset_name="mouse"
)
fo.launch_app(dataset)

在common.py中添加如下

class MobileNetV3(nn.Module):

    def init(self, slice):
        super(MobileNetV3, self). init()
        self.model =None
        if slice == 1:
            self.model= models.mobilenet_v3_small(pretrained=True).features[:4]
        elif slice == 2:
            self.model=models.mobilenet v3 small(pretrained=True).features[4:9]
        else:
            self.model=models.mobilenet_v3_small(pretrained=True).features[9:]

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

修改yaml

 yolo.py添加Mobile的elif

实现了模型轻量化的工作 ---------数据702w到373w的轻量化


http://www.kler.cn/a/315574.html

相关文章:

  • STM32嵌入式闹钟系统设计与实现
  • 算法演练----24点游戏
  • 【p2p、分布式,区块链笔记 DAM】GUN/SEA(Security, Encryption, Authorization) 模块genkey
  • 使用HTML、CSS和JavaScript创建动态圣诞树
  • git之 revert和rebase
  • Python 小高考篇(2)字符串
  • 使用git命令
  • 基于SpringBoot+Vue的时尚美妆电商网站系统
  • Web APIs 1:基础介绍+DOM+定时器
  • 饭局礼仪:以下这7种动作,特容易被视为没教养,不要犯
  • Vue学习记录之三(ref全家桶)
  • 今日leetCode 1. 两数之和
  • (转载)智能指针shared_ptr从C++11到C++20
  • SpringSecurity6.x整合手机短信登录授权
  • 2024 硬盘格式恢复软件大揭秘
  • 《论分布式存储系统架构设计》写作框架,软考高级系统架构设计师
  • 无限边界:现代整合安全如何保护云
  • 怀庄之醉是勾兑酒吗?
  • YOLOv10改进,YOLOv10替换主干网络为PP-HGNetV2(百度飞桨视觉团队自研,独家手把手教程,助力涨点)
  • re题(38)BUUCTF-[FlareOn6]Overlong
  • 在vue中嵌入vitepress,基于markdown文件生成静态网页从而嵌入社团周报系统的一些想法和思路
  • 【GMNER】Grounded Multimodal Named Entity Recognition on Social Media
  • 负载均衡服务由几部分组成?分别是什么
  • vue3 中后台系统中,复杂表单的开发优化技巧
  • Spring框架总体结构
  • 无人机之航线规划篇