目录

一、实验

1、实验目的

2、相关知识点

本实验的网络结构

实验结果

3、错误及解决方案

4、总结

二、代码实现

1、前期准备

2、构建网络

3、训练模型

4、结果可视化

5、对模型进行评估和验证


一、实验

1、实验目的

①了解YOLOv5算法中的backbone部分

②了解网络是如何搭建起来的,每个参数的含义是什么

2、相关知识点

① SiLU函数

SiLU 是一种连续、平滑且可导的激活函数,全名是 Sigmoid Linear Unit。

可以表示为:SiLU(x) = x · sigmoid(x)

它也被称为 Swish-1(Swish 激活的一种特例,β=1)。在很多现代检测网络(比如 YOLO 系列后期版本)和部分卷积/Transformer混合骨干里,SiLU 逐渐取代传统的 ReLU,成为默认激活函数之一,函数图形如下

本实验的网络结构

本实验实现的是简化版YOLOv5骨干网络(Backbone),并将原本用于目标检测的YOLOv5核心特征提取模块改造为一个图像分类网络,最终输出4类分类结果

实验结果

3、错误及解决方案

A module that was compiled using NumPy 1.x cannot be run in NumPy 2.0.2 as it may crash. To support both 1.x and 2.x versions of NumPy, modules must be compiled with NumPy 2.0. Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.

解决方法:安装1.26.4,这个版本比较稳定

executing.executing.NotOneValueFound: Expected one value, found 0

原因:核心是executing这个库在解析代码(通常是AST抽象语法树)时,预期能找到一个有效值,但实际只找到了0个

这个错误很少是executing库本身的问题,几乎都是它的调用方(比如调试工具、代码分析工具、IPython/Jupyter、或者rich/pdb等依赖库)在解析你的代码时,某个本该有值的位置是空的

解决方案:问了ai,给的测试代码如下:

# 在代码最开头添加这几行,禁用所有异常美化工具
import sys

# 禁用rich的traceback
if "rich.traceback" in sys.modules:
    from rich.traceback import uninstall
    uninstall()

# 强制使用Python标准异常格式
sys.excepthook = sys.__excepthook__

# 接下来写你的业务代码(示例)
import executing
source = ""
tree = executing.Source.exec(source)
try:
    tree.value_at(0)
except Exception as e:
    print(f"原始异常:{type(e).__name__}: {e}")  # 现在能看到真正的错误了

但其实我根本没运行成功,是安装了rich包就可以正常运行了,深度学习反正就是玄学,可能因为换了个电脑新环境没配置吧

4、总结

这两次实验都感觉网络的搭建非常困难,虽然看框架图感觉比较好理解,但是在如何转化成代码上面感觉很懵,代码还是得多理解吧,回头看其实最开始那些非常简单的网络我也不理解,到现在居然觉得比较简单了,完事开头难,理解不了的代码都丢给AI让它一句一句给我解释哈哈哈哈,其实还蛮有用的

二、代码实现

1、前期准备

* 前期准备和之前实验一模一样,这里不摆代码了,只放一些结果图

数据集结构以及数据的形状

2、构建网络

* 重点只放在网络如何搭建上面,其余代码一致就略过

       ①了解网络架构

本实验实现的是简化版YOLOv5骨干网络(Backbone),并将原本用于目标检测的YOLOv5核心特征提取模块改造为一个图像分类网络,最终输出4类分类结果

       ②实现模型

辅助函数autopad:目的是为了保证卷积操作后特征图的尺寸不发生变化(仅步长为1时)

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

核心卷积模块Conv类:该类封装基础卷积单元,就是一次卷积操作+归一化+激活处理,是所有特征提取层的基础

参数说明:c1:输入通道数,c2:输出通道数;k:卷积核尺寸,s:步长;g:分组卷积的组数;act:激活函数(默认 SiLU)

class Conv(nn.Module):
    def __init__(self,c1,c2,k=1,s=1,p=None,g=1,act=True):
        super().__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)))

瓶颈层Bottleneck 类:实现残差连接的瓶颈结构,减少计算量的同时保留特征(YOLOv5 的核心残差单元)

class Bottleneck(nn.Module):
    def __init__(self,c1,c2,shortcut=True,g=1,e=0.5):
        super().__init__()
        c_=int(c2*e)
        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))

C3类:YOLOv5 的核心特征提取模块,通过多分支融合增强特征表达能力

class C3(nn.Module):
    def __init__(self,c1,c2,n=1,shortcut=True,g=1,e=0.5):
        super().__init__()
        c_=int(c2*e)
        self.cv1=Conv(c1,c_,1,1)
        self.cv2=Conv(c1,c_,1,1)
        self.cv3=Conv(2*c_,c2,1)
        self.m=nn.Sequential(*(Bottleneck(c_,c_,shortcut,g,e=1.0) for _ in range(n)))

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

SPPF(Spatial Pyramid Pooling Fast):快速空间金字塔池化,融合多尺度特征(解决不同尺寸输入的问题)

class SPPF(nn.Module):
    def __init__(self,c1,c2,k=5):
        super().__init__()
        c_=c1//2
        self.cv1=Conv(c1,c_,1,1)
        self.cv2=Conv(c_*4,c2,1,1)
        self.m=nn.MaxPool2d(kernel_size=k,stride=1,padding=k//2)

    def forward(self,x):
        x=self.cv1(x)
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            y1=self.m(x)
            y2=self.m(y1)
            return self.cv2(torch.cat([x,y1,y2,self.m(y2)],1))

完整骨干网络:

  • 特征提取:通过 4 次 Conv(步长 2)下采样(特征图尺寸每次减半),搭配 C3 模块提取特征,最后 SPPF 融合多尺度特征;

  • 分类头:将特征图展平后,通过两层全连接层输出 4 类分类结果(in_features=65536对应特征展平后的维度,需匹配输入图像尺寸,如输入 64x64 时,102488=65536)

class YOLOv5_backbone(nn.Module):
    def __init__(self):
        super(YOLOv5_backbone, self).__init__()
        self.Conv_1=Conv(3,64,3,2,2)
        self.Conv_2=Conv(64,128,3,2)
        self.C3_3=C3(128,128)
        self.Conv_4=Conv(128,256,3,2)
        self.C3_5=C3(256,256)
        self.Conv_6=Conv(256,512,3,2)
        self.C3_7=C3(512,512)
        self.Conv_8=Conv(512,1024,3,2)
        self.C3_9=C3(1024,1024)
        self.SPPF=SPPF(1024,1024,5)

        #全连接层,用于分类
        self.classifier=nn.Sequential(
            nn.Linear(in_features=65536,out_features=100),
            nn.ReLU(),
            nn.Linear(in_features=100,out_features=4),
        )

    def forward(self,x):
        x = self.Conv_1(x)
        x = self.Conv_2(x)
        x = self.C3_3(x)
        x = self.Conv_4(x)
        x = self.C3_5(x)
        x = self.Conv_6(x)
        x = self.C3_7(x)
        x = self.Conv_8(x)
        x = self.C3_9(x)
        x = self.SPPF(x)
        x=torch.flatten(x,start_dim=1)
        x=self.classifier(x)

最后的网络结构如图所示:

       ③打印模型

3、训练模型

设置超参数并编写训练函数,优化器选择Adam,选择SGD会导致无法训练

训练轮次为60,得到的每个epoch训练结果如下(部分展示),精度比预期低,还在探索原因中

4、结果可视化

5、对模型进行评估和验证

可以看到训练效果最好的精度和损失

更多推荐