1. 为什么“YOLOv5 + 注意力机制”是毕设和论文改进的常见切入点
做目标检测方向的同学,大概率都遇到过这样的困境:用 YOLOv5 跑完 baseline,mAP 停在某个数值上不动了。想换模型,YOLOv8、YOLOv9 的代码复杂度又上来了;想改骨干网络,担心训练不稳定;想加注意力机制,却不知道加在哪一层、加几种、怎么证明有效。
这篇文章想解决的就是这三个问题:加什么、加在哪、怎么证明加了有用。我会围绕 SE、ECA、CBAM、CA 这四种主流注意力机制,给出可以直接复用的 YOLOv5 嵌入代码,并专门讲清楚消融实验怎么做,让改进不只是“玄学调参”,而是能写进毕业论文的创新点。
先说一个值得注意的事实:注意力机制不是越复杂越好。SE 通道注意力实现简单、训练稳定,适合作为第一版改进;ECA 是 SE 的轻量化变体,去掉全连接层后参数更少;CBAM 同时关注通道和空间,适合目标尺度变化大的场景;CA 把位置信息编码进注意力,对小目标和遮挡目标更友好。选哪一种,取决于你的数据集特点,而不是哪个最新用哪个。
在开始动手之前,还需要明确一个观点:嵌入位置比注意力模块本身更影响最终效果。同样一个 SE 模块,放在骨干网络末端和放在检测头前,结果可能差好几个点。因此,这篇文章不仅给出四种模块的代码,还会说明它们的适用位置和嵌入思路。如果你正在写本科或硕士论文,可以把这部分内容直接扩展成“基于改进注意力机制的目标检测算法研究”的方法章节。
2. 注意力机制的核心概念:它在 YOLOv5 里到底做了什么
注意力机制最早火起来是因为 NLP 领域的 Transformer,但它不是 Transformer 的专利。在卷积神经网络中,注意力机制的本质是对特征图的通道或空间位置进行重新加权,让网络更关注对检测任务有用的信息,抑制背景或干扰信息。
2.1 通道注意力和空间注意力
以一张 $H \times W \times C$ 的特征图为例:
- 通道注意力:计算每个通道的重要性权重,把 $C$ 个通道重新标定。它回答的问题是“哪些特征通道值得看”。
- 空间注意力:计算特征图每个位置的重要性权重,回答“图像的哪个区域值得看”。
SE 和 ECA 属于通道注意力,CA 属于带有位置编码的通道注意力,CBAM 则是通道注意力和空间注意力的组合。理解这个分类后,你就能明白为什么 CBAM 的结构看起来比 SE 复杂,因为它多了一个空间维度。
2.2 为什么 YOLOv5 需要注意力机制
YOLOv5 的骨干网络是 CSPDarknet,检测头是耦合的卷积头。默认结构对全局上下文和跨通道关系的建模能力有限,尤其是深层特征图经过多次降采样后,小目标的信息已经比较弱。注意力机制恰好能从两个方向补充:
- 通道维度:强化包含目标特征的通道,弱化背景通道。
- 空间维度:把响应集中到目标区域,减少背景区域对检测结果的干扰。
在实际项目中,注意力机制并不是必须的。如果你的任务简单、baseline 已经很高,加注意力可能只提升 0.3 个点,还增加了训练时间。但如果你做的是小目标检测、遮挡目标检测、或者数据集本身噪声较多,注意力机制带来的收益会明显得多。
2.3 SE、ECA、CBAM、CA 的本质区别
这四种机制很容易让人混淆,我列一个对比表:
| 注意力机制 | 全称 | 核心操作 | 参数量 | 适用场景 |
|---|---|---|---|---|
| SE | Squeeze-and-Excitation | 全局池化 + 全连接 + Sigmoid | 中 | 通用改进,适合第一版 baseline |
| ECA | Efficient Channel Attention | 全局池化 + 一维卷积 | 低 | 轻量化网络,资源受限场景 |
| CBAM | Convolutional Block Attention Module | 通道注意力 + 空间注意力 | 中高 | 目标尺度变化大、复杂背景 |
| CA | Coordinate Attention | 坐标信息嵌入 + 通道注意力 | 中 | 小目标、密集场景、位置敏感任务 |
这里要特别提醒:CBAM 参数量中等,但推理耗时比 SE 高。如果你跑实验的 GPU 不强,或者需要实时推理,ECA 和 SE 是更务实的选择。CA 虽然对位置信息更敏感,但也会改变特征图的拼接方式,嵌入时需要多检查维度对不对。
3. 环境准备与 YOLOv5 项目结构说明
这里以一个常见的 YOLOv5 训练环境为例。如果你使用的是官方仓库,版本建议选择 v6.0 或 v7.0,因为这两个版本在注意力机制嵌入方面社区方案最成熟。以下配置仅做参考,实际以你本机环境为准,但核心代码逻辑与版本无关。
Python >= 3.8 PyTorch >= 1.8 torchvision >= 0.9 ultralytics/yolov5 官方仓库(v6.0 或 v7.0)3.1 获取 YOLOv5 源码
git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt如果你已经有自己的 YOLOv5 项目,也可以直接复用。本文的重点是在这个项目结构上增加注意力模块,因此第一步是确认目录结构。
3.2 了解需要修改的核心文件
YOLOv5 的网络结构主要由三个文件控制:
models/yolo.py:模型解析入口,负责根据 yaml 配置构建网络。models/common.py:存放通用模块,如 Conv、Bottleneck、C3、SPPF 等。models/yolov5s.yaml:YOLOv5s 的网络结构配置文件。
嵌入注意力机制的核心操作就是:先在common.py中定义注意力模块类,然后在yolov5s.yaml(或你自己的 yaml)中将模块插入到指定位置。这个思路对 YOLOv5s、YOLOv5m、YOLOv5l 都通用。
3.3 前置验证:先跑通 baseline
改进之前,一定先训练一次原始 YOLOv5,确保代码环境正常。很多同学直接改完结构再训练,出错了分不清是环境问题还是模型问题。
python train.py --data your_dataset.yaml --weights yolov5s.pt --epochs 10如果这一步能启动训练,说明基本环境没问题。接下来进入正题。
4. 四种注意力机制的代码实现与解析
在动手修改 YOLOv5 之前,需要先写好四个注意力模块。为了降低维护成本,可以把它们统一放到一个文件里,例如在models/common.py末尾追加,或者新建models/attention.py再导入。我更推荐新建独立文件,改回 baseline 时只要注释掉导入语句即可。
4.1 SE 通道注意力模块实现
SE 模块的结构是 Squeeze 和 Excitation,Squeeze 通过全局平均池化把 $H \times W \times C$ 压缩成 $1 \times 1 \times C$,Excitation 通过两个全连接层学习通道权重,最后用 Sigmoid 输出 0 到 1 之间的权重,乘回原特征图。
# 文件路径:models/attention.py import torch import torch.nn as nn class SEAttention(nn.Module): def __init__(self, in_channels, reduction=16): super(SEAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.fc = nn.Sequential( nn.Linear(in_channels, in_channels // reduction, bias=False), nn.ReLU(inplace=True), nn.Linear(in_channels // reduction, in_channels, bias=False), nn.Sigmoid() ) def forward(self, x): b, c, _, _ = x.size() y = self.avg_pool(x).view(b, c) y = self.fc(y).view(b, c, 1, 1) return x * y.expand_as(x)这段代码的关键在于reduction参数,它控制通道压缩比例。默认 16,如果你的特征图通道数很小,比如只有 64,压缩到 4 可能会丢失信息,此时可以调成 8。
4.2 ECA 通道注意力模块实现
ECA 的思路是避免全连接层带来的维度缩减,改用一维卷积直接学习通道权重。卷积核大小 $k$ 通常根据通道数自适应确定。
# 文件路径:models/attention.py import math import torch import torch.nn as nn class ECAAttention(nn.Module): def __init__(self, in_channels, k_size=None): super(ECAAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) if k_size is None: k_size = int(abs((math.log(in_channels, 2) + 1) / 2)) if k_size % 2 == 0: k_size += 1 self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): b, c, _, _ = x.size() y = self.avg_pool(x) y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1) y = self.sigmoid(y) return x * y.expand_as(x)ECA 和 SE 的效果在多数数据集上接近,但 ECA 参数量更小,训练时更省显存。如果你的论文需要强调“轻量化改进”,ECA 是很好的选择。
4.3 CBAM 注意力模块实现
CBAM 包含两个子模块:通道注意力(CAM)和空间注意力(SAM)。通道部分与 SE 类似但使用了最大池化和平均池化并行;空间部分通过通道维度的池化生成二维空间权重。
# 文件路径:models/attention.py import torch import torch.nn as nn class CBAMChannelAttention(nn.Module): def __init__(self, in_channels, reduction=16): super(CBAMChannelAttention, self).__init__() self.max_pool = nn.AdaptiveMaxPool2d(1) self.avg_pool = nn.AdaptiveAvgPool2d(1) self.mlp = nn.Sequential( nn.Conv2d(in_channels, in_channels // reduction, 1, bias=False), nn.ReLU(inplace=True), nn.Conv2d(in_channels // reduction, in_channels, 1, bias=False) ) self.sigmoid = nn.Sigmoid() def forward(self, x): max_out = self.mlp(self.max_pool(x)) avg_out = self.mlp(self.avg_pool(x)) return self.sigmoid(max_out + avg_out) class CBAMAttention(nn.Module): def __init__(self, in_channels, reduction=16, kernel_size=7): super(CBAMAttention, self).__init__() self.channel_attention = CBAMChannelAttention(in_channels, reduction) self.spatial_attention = nn.Sequential( nn.Conv2d(2, 1, kernel_size, padding=kernel_size // 2, bias=False), nn.Sigmoid() ) def forward(self, x): x = x * self.channel_attention(x) x = x * self.spatial_attention(torch.cat( [torch.max(x, dim=1, keepdim=True)[0], torch.mean(x, dim=1, keepdim=True)], dim=1)) return xCBAM 需要注意的细节是:空间注意力部分输入的是通道维度的最大池化和平均池化结果,拼接后通道数为 2。kernel_size=7是论文默认值,实际使用时可以尝试 3 或 5,但收益不一定明显。
4.4 CA 坐标注意力模块实现
CA 把位置信息显式编码进注意力权重。它将特征图沿高度和宽度方向分别做池化,再拼接、卷积、拆分,最后生成两个方向上的注意力权重。
# 文件路径:models/attention.py import torch import torch.nn as nn import torch.nn.functional as F class CAAttention(nn.Module): def __init__(self, in_channels, reduction=32): super(CAAttention, self).__init__() self.pool_h = nn.AdaptiveAvgPool2d((None, 1)) self.pool_w = nn.AdaptiveAvgPool2d((1, None)) hidden_channels = max(8, in_channels // reduction) self.conv1 = nn.Conv2d(in_channels, hidden_channels, kernel_size=1, stride=1, padding=0) self.bn1 = nn.BatchNorm2d(hidden_channels) self.act = nn.Hardswish() self.conv_h = nn.Conv2d(hidden_channels, in_channels, kernel_size=1, stride=1, padding=0) self.conv_w = nn.Conv2d(hidden_channels, in_channels, kernel_size=1, stride=1, padding=0) def forward(self, x): b, c, h, w = x.size() x_h = self.pool_h(x).permute(0, 1, 3, 2) x_w = self.pool_w(x) y = torch.cat([x_h, x_w], dim=2) y = self.conv1(y) y = self.bn1(y) y = self.act(y) x_h, x_w = torch.split(y, [h, w], dim=2) x_h = x_h.permute(0, 1, 3, 2) x_w = x_w.permute(0, 1, 3, 2) a_h = torch.sigmoid(self.conv_h(x_h)) a_w = torch.sigmoid(self.conv_w(x_w)) return x * a_h * a_w这段代码有两个地方容易出错:一是AdaptiveAvgPool2d((None, 1))在部分 PyTorch 版本中可能不支持None,可以等价写成AdaptiveAvgPool2d((1, 1))再转置;二是torch.split的维度要跟前面拼接的h + w严格对应。如果 forward 报维度错误,先打印x_h和x_w的 shape。
5. 将注意力机制嵌入 YOLOv5:两种常用方式
模块写好后,接下来就是把它嵌入网络。这里介绍两种方式:一种是替换 C3 模块内部的 Bottleneck,另一种是作为独立模块插在特征融合层之前。两种方式各有适用场景,下面分别说明。
5.1 方式一:修改common.py并定义新的 C3 变体
这种方式是社区最常见的做法,把注意力模块集成到 C3 结构中,形成 C3_SE、C3_ECA、C3_CBAM、C3_CA 等变体。
首先,在models/common.py中导入刚才的注意力模块:
# 文件路径:models/common.py from models.attention import SEAttention, ECAAttention, CBAMAttention, CAAttention然后定义一个新的 C3 类,以 C3_SE 为例:
# 文件路径:models/common.py class C3_SE(nn.Module): def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): super(C3_SE, self).__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))) self.attention = SEAttention(c_) def forward(self, x): return self.cv3(torch.cat((self.m(self.cv1(x)), self.attention(self.cv2(x))), dim=1))同样,可以定义 C3_ECA、C3_CBAM、C3_CA 类,只需把self.attention = SEAttention(c_)换成对应的模块即可。CBAM 的输入输出通道与c_一致,CA 也一样,因此替换非常方便。
然后在models/yolo.py的parse_model函数中添加映射关系:
# 文件路径:models/yolo.py if m in { Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, C3_SE, C3_ECA, C3_CBAM, C3_CA, # 新增 SEAttention, ECAAttention, CBAMAttention, CAAttention }: c1, c2 = ch[f], args[0] if c2 != no: c2 = make_divisible(c2, 8) args = [c1, c2, *args[1:]]最后在yolov5s.yaml中把某个C3替换成C3_SE。例如把骨干网络最后一个 C3 替换:
# 文件路径:models/yolov5s.yaml backbone: # ... - {1: 1024, 2: 5, 3: C3_SE, 4: 1} # 原 C3 改成 C3_SE5.2 方式二:作为独立模块插入 Neck 前
如果你不想改变 C3 结构,也可以把注意力模块直接插到特征金字塔之前,对整张特征图做重标定。修改yolov5s.yaml在head部分加一行即可。
# 文件路径:models/yolov5s.yaml head: # ... - [-1, 1, SEAttention, [1024]] - [-1, 1, Conv, [512, 1, 1]]这个方式的优点是改动最小,缺点是注意力模块可能没有充分参与多尺度特征提取。从实际效果来看,方式一更推荐,因为它把注意力整合进了 C3 的残差结构中,特征重用和信息流动都更好。
5.3 嵌入位置的选择建议
不是所有层都适合加注意力。根据实践经验,这里给出几个参考方向:
- 骨干网络最后两层:适合加 SE 或 ECA,增强深层语义特征的通道表达。
- Neck 中的 P3、P4、P5 层前:适合加 CBAM,兼顾通道和空间信息。
- 小目标数据集:优先在 P3 层(浅层特征图)附近加 CA,因为小目标的位置信息更重要。
- 轻量化需求:优先选 ECA,参数量和计算量增加最小。
建议先只改一个位置跑通流程,不要一开始就到处加注意力。等验证实验流程没问题后,再设计不同嵌入位置的对比实验,这本身就是很好的消融内容。
6. 消融实验设计:如何证明改进有效
改完代码后,最容易犯的错误是直接全量训练 100 个 epoch,然后只报一个最好的 mAP。这种方式不足以说明注意力机制有效,因为训练超参数、随机种子都可能影响结果。规范的消融实验应遵循“控制变量”原则。
6.1 消融实验的基本原则
核心只有一个:每次只改变一个变量。
例如:
| 实验编号 | 模型结构 | 训练超参数 | 数据增强 | 随机种子 |
|---|---|---|---|---|
| Baseline | YOLOv5s | 相同 | 相同 | 相同 |
| +SE | YOLOv5s + SE | 相同 | 相同 | 相同 |
| +ECA | YOLOv5s + ECA | 相同 | 相同 | 相同 |
| +CBAM | YOLOv5s + CBAM | 相同 | 相同 | 相同 |
| +CA | YOLOv5s + CA | 相同 | 相同 | 相同 |
训练超参数包括 epochs、batch size、优化器、学习率、图像尺寸等。如果不同实验用了不同 batch size,结果对比就没意义了。为了减少偶然性,同一实验可以跑三次取平均,但毕设时间有限的话,至少保证 baseline 和最佳改进模型重复验证一次。
6.2 评价指标的选择
最常见的是 mAP@0.5 和 mAP@0.5:0.95。此外,针对自己的任务还可以补充:
- 小目标 AP:如果你的数据集包含小目标,报告
AP_small。 - 精确率和召回率:判断改进是提高了精度还是召回。
- 参数量和 FLOPs:用
torchsummary或thop统计,作为“轻量化改进”的支撑数据。 - 单张推理耗时:在相同硬件上测量,排除硬件波动。
6.3 消融实验论文写法参考
在论文中,一般会用一张表展示结果,例如:
| 方法 | mAP@0.5 | mAP@0.5:0.95 | 参数量(M) | 推理耗时(ms) |
|---|---|---|---|---|
| YOLOv5s baseline | 0.821 | 0.532 | 7.2 | 3.1 |
| +SE | 0.835 | 0.548 | 7.3 | 3.3 |
| +ECA | 0.832 | 0.545 | 7.2 | 3.1 |
| +CBAM | 0.841 | 0.556 | 7.5 | 3.6 |
| +CA | 0.838 | 0.552 | 7.4 | 3.5 |
表格中的数据仅为演示格式,不建议直接套用,因为不同数据集结果差异很大。写论文时把训练集、验证集、测试集划分、数据量、训练轮数都写清楚,审稿老师会觉得更严谨。
7. 完整训练与验证流程
下面是一个完整的实操流程,从数据准备到输出验证结果。
7.1 准备数据集
假设你的数据集是 VOC 格式或 YOLO 格式。YOLO 格式的目录结构如下:
dataset/ images/ train/ val/ labels/ train/ val/对应的数据集配置文件your_dataset.yaml:
# 文件路径:your_dataset.yaml train: dataset/images/train val: dataset/images/val nc: 2 names: ['defect', 'normal']如果是 PCB 缺陷检测,nc就是缺陷类别数;如果是其他目标检测任务,按实际类别数修改即可。
7.2 修改模型配置
以 SE 嵌入骨干网络最后一个 C3 为例,修改yolov5s.yaml:
# 文件路径:models/yolov5s.yaml nc: 2 # 改成你的类别数 backbone: # ... - [-1, 1, Conv, [1024, 3, 2]] - [-1, 1, C3_SE, [1024, False]] # 原来是 C3 - [-1, 1, SPPF, [1024, 5]]这里False表示不使用残差 shortcut,具体取值参考你使用的 YOLOv5 版本默认配置,不要随意改动。
7.3 启动训练
python train.py \ --data your_dataset.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --img 640 \ --name run_se训练日志中会出现每个 epoch 的 mAP,建议关注最后一轮的mAP@0.5和mAP@0.5:0.95。如果想跟 baseline 对比,把--name分别设为run_baseline、run_se、run_cbam等,方便结果汇总。
7.4 测试与可视化
python detect.py \ --weights runs/train/run_se/weights/best.pt \ --source dataset/images/val/0001.jpg \ --conf-thres 0.25运行后,检测结果图片会输出到runs/detect/exp。除了看检测框,建议额外保存注意力热力图,论文里配一张热力图能更直观地说明模型关注了目标区域。
7.5 判断改进是否有效
一个可靠的判断标准是:在相同环境下,改进模型的 mAP 比 baseline 高 1 个点以上,并且多次运行结果稳定。如果只高 0.2 个点,可能是随机波动,不能写入结论。还可以观察 PR 曲线和 loss 曲线,如果改进模型的 loss 下降更快,说明注意到了有效特征,即使 mAP 提升不大也有分析价值。
8. 常见问题与排查思路
实际操作中,最影响进度的往往是环境或代码层面的小问题。以下是高频问题清单,我整理了排查方式和解决方案。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
训练报错AttributeError: module 'models.common' has no attribute 'C3_SE' | 定义了类但没在yolo.py的解析映射中注册 | 检查parse_model中是否加入了新模块 | 在if m in字典中加入C3_SE |
输入输出 shape 对不上,报错RuntimeError | 注意力模块的输入通道与 C3 分支通道不一致 | 打印 conv1 和 conv2 的输出 shape | 检查c_ = int(c2 * e)是否与注意力模块的in_channels一致 |
| 训练速度变慢很多 | 注意力模块插入位置过多,或 CBAM 的空间卷积耗时较高 | 用thop.profile统计 FLOPs,对比不同实验 | 减少嵌入层数,或换用 ECA |
| 显存不足 | batch size 过大或模型结构改变后占用增加 | 查看nvidia-smi显存占用 | 减小 batch size,或使用梯度累积 |
| 对 baseline 没有提升 | 注意力嵌入位置不合理,或数据集本身特征简单 | 检查特征图分布、尝试不同嵌入位置 | 换到 Neck 层,或在浅层特征图处加 CA |
| 训练结果不稳定 | 随机种子未固定,或数据增强差异 | 固定--seed参数,确保数据集划分一致 | 统一随机种子,多次重复取均值 |
| 加载预训练权重报错 | yaml 结构改变导致模型层数不匹配 | 查看报错中提示的层名称 | 换用--weights ''从头训练,或调整嵌入位置 |
这里要特别提醒:修改结构后,原来的yolov5s.pt预训练权重不一定能完整加载,因为网络层数变了。遇到加载失败时,可以删除--weights参数,改成从零训练。虽然训练时间变长,但结构改进本来就需要重新训练。
9. 最佳实践与工程建议
9.1 实验管理:一次只跑一个变量
建议为每组实验建立独立目录,命名规则用项目名加模块名,例如exp_pcb_se、exp_pcb_cbam。训练完成后把 yaml 文件、训练日志、权重文件和测试图片统一归档。写论文时,这些记录就是最直接的实验依据。
9.2 代码结构:把注意力模块独立成文件
不要在common.py里堆积几十个注意力类,那样后期维护非常麻烦。推荐把注意力模块放在models/attention.py,然后在common.py中导入。这样回滚 baseline 时,只需要注释导入和yolo.py中的注册代码,不用删除网络结构。
9.3 性能评估:不能只看 mAP
在工程落地中,推理速度和显存占用同样重要。建议对每个实验跑一遍推理时间,用相同的输入尺寸、相同的 batch size,在相同 GPU 上测量。如果注意力机制带来 2 个点 mAP 提升但推理时间增加了 30%,就要评估是否值得。
9.4 安全与生产环境提醒
在训练阶段可以任意尝试不同结构,但部署到生产环境时需要注意:模型结构变更可能影响推理平台的算子支持。例如,某些边缘设备对AdaptiveAvgPool2d和Hardswish的支持不完善,CA 模块部署到嵌入式设备时可能报算子不支持的错误。部署前,建议先用 ONNX 导出并在目标平台做单张图片的推理验证。
python export.py --weights runs/train/run_se/weights/best.pt --include onnx如果 ONNX 导出失败,大概率是自定义模块中有不支持的算子,需要简化结构或用等价算子替换。
9.5 论文写作建议
写毕业论文时,建议按以下结构展开:
- 方法部分:先介绍 baseline,再分别介绍 SE、ECA、CBAM、CA 的原理和嵌入位置。
- 实验部分:先给出 baseline 结果,再给 4 种注意力机制的对比结果,最后给出最优模型的消融分析。
- 可视化部分:用热力图展示注意力区域,用 PR 曲线展示性能变化。
核心原则是让读者清楚地看到“改了什么、为什么改、效果如何”。
10. 总结与后续学习方向
这篇文章从 YOLOv5 的实际改进需求出发,完整介绍了 SE、ECA、CBAM、CA 四种注意力机制的原理、代码实现、嵌入方法和消融实验设计。你可以直接复用本文代码,把目标检测的 baseline 改造为带注意力机制的网络,并通过科学对比实验验证改进效果。
下一步可以尝试的方向有三个:一是把注意力机制与 YOLOv8 结合,虽然代码结构不同,但注意力模块的定义基本可以复用;二是研究注意力机制与损失函数的联动,例如在 CIoU 基础上引入注意力权重,进一步提升定位精度;三是针对自己的数据集做更细粒度的超参数调优,注意力模块的位置、通道压缩比例、卷积核大小都可能带来额外收益。
建议先跑通一个最小实验,把 SE 模块嵌入 YOLOv5s,完成一个完整训练流程,再逐步扩展到其他模块。这个过程中如果遇到代码或训练问题,按照文中的排查清单逐项检查,基本都能解决。
收藏这篇文章,下次改进模型时可以直接对照操作。如果你在嵌入过程中遇到新的问题,欢迎在评论区交流。