简介:这是一份面向计算机科学与技术等专业本科生的毕业论文参考文档,主题为基于AC-YOLO的路面落叶检测方法,适合正在准备目标检测方向毕业设计、需要完整论文框架与算法改进思路的同学。文档围绕YOLO算法的自适应聚类改进展开,涵盖研究背景与意义、目标检测技术综述、AC-YOLO算法改进、数据集采集标注与预处理、网络模型与损失函数设计、实验设置与结果分析,以及结论与展望等完整章节,并针对路面落叶这类小目标检测场景给出了多尺度特征融合与数据增强方案。资源包共1个docx文件,约33KB,为完整一万字论文正文,目录结构清晰,便于按章节查阅与借鉴写作逻辑。目前已有321人学习下载,可作为毕业论文选题、算法改进论证与实验章节撰写的实用参考资料。
1. 路面落叶检测为什么值得单独做一个改进模型:从 AC-YOLO 的注意力与卷积混合说起
城市道路清扫车和环卫巡检车每天都要判断路面落叶的堆积程度,决定是否加派清扫班次。这件事听起来简单,真放到车载摄像头里跑实时检测,问题就来了:落叶颜色和沥青路面接近、形状随风吹散、密集时互相遮挡、雨后贴地反光,通用 YOLO 直接拿来用,漏检和误检都不少。AC-YOLO 这个方向,核心思路是在 YOLO 检测头或主干里引入注意力机制(Attention)与卷积(Convolution)的混合结构,让模型对落叶这种低对比度、非刚性、密集小目标更敏感。这篇面向本科毕业论文一万字体量的实战笔记,讲的是怎么把 AC-YOLO 从概念落到能跑通训练、能出对比实验、能写进论文的完整路径。适合正在做目标检测方向毕设、需要一份可复现改进方案的同学,也适合想把 YOLO 用到市政巡检场景的工程师。下面按数据、模型改造、训练调参、避坑、论文级验证五块展开,每一步都给到能直接抄的命令和参数。
2. 数据准备与落叶数据集构建:从采集到 YOLO 格式的完整链路
2.1 落叶检测的数据从哪来,为什么不能直接用公开数据集
路面落叶检测没有现成的标准大规模数据集,这是做这个题目第一个要面对的现实。常见做法有三条路:一是自己用手机或行车记录仪在不同路段、不同天气下拍摄视频,抽帧成图;二是从公开的垃圾检测、道路异物检测数据集里筛选含落叶的类别;三是用数据增强合成落叶贴图。我一般会以自采为主,因为落叶的形态、颜色、堆积密度跟本地树种和季节强相关,公开数据集里的落叶跟你要检测的场景分布差太远,直接训出来的模型换个城市就翻车。
自采时要注意几个硬指标:拍摄高度尽量模拟车载摄像头,离地 1.2 到 1.5 米;覆盖晴天、阴天、雨后三种光照;每类场景至少 200 张有效图;落叶密集、稀疏、单叶、成堆四种形态都要有。视频抽帧别按固定间隔抽,相邻帧太像会导致训练集和验证集泄漏,建议按场景分段,每段只抽 3 到 5 帧。
2.2 标注规范与 YOLO 格式转换脚本
标注用 LabelImg 或 X-AnyLabeling 都行,导出 VOC 格式的 XML,再转成 YOLO 的 txt。落叶这种目标边界模糊,标注时统一规则很重要:单叶按可见轮廓框,成堆落叶按整堆外接矩形框,被遮挡超过 70% 的不标。下面这个转换脚本处理 VOC 到 YOLO 的坐标归一化,同时做一轮尺寸过滤。
import os import xml.etree.ElementTree as ET # 类别映射,落叶场景一般就一类,多类时按需扩展 CLASSES = ["fallen_leaf"] # 过滤掉宽或高小于 8 像素的框,落叶小目标太多会拖累训练 MIN_SIZE = 8 def convert_voc_to_yolo(xml_dir, out_dir, img_w, img_h): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text if cls_name not in CLASSES: continue cls_id = CLASSES.index(cls_name) bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) w = xmax - xmin h = ymax - ymin if w < MIN_SIZE or h < MIN_SIZE: continue # YOLO 格式:类别 中心x 中心y 宽 高,全部归一化到 0-1 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h nw = w / img_w nh = h / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}") out_path = os.path.join(out_dir, xml_file.replace(".xml", ".txt")) with open(out_path, "w") as f: f.write("\n".join(lines)) if __name__ == "__main__": convert_voc_to_yolo("./annotations", "./labels", 1920, 1080)这段代码的关键参数是MIN_SIZE和img_w/img_h。MIN_SIZE设太小会把标注噪声当正样本,设太大会丢掉远处的小落叶,8 像素是 1080p 下的经验值。img_w和img_h必须和实际图片分辨率一致,否则归一化坐标全错,训练时 loss 会异常震荡。转换完一定要抽查几张,用可视化脚本把框画回原图确认。
2.3 数据集划分与增强策略
划分比例按 8:1:1 走训练、验证、测试。落叶检测的增强不要无脑上,翻转和亮度调整安全,Mosaic 和 MixUp 要慎用,因为落叶成堆时 Mosaic 拼接会造出不符合物理规律的堆积形态,模型学到假特征。我一般只开水平翻转、随机亮度、轻微高斯噪声,Mosaic 概率压到 0.3 以下。数据量少于 1500 张时,可以用离线增强把训练集扩到 3000 张左右,但验证集和测试集必须保持原始分布,不能增强。
3. AC-YOLO 模型改造:注意力与卷积怎么嵌进 YOLO 才不掉点
3.1 为什么是注意力加卷积,而不是纯 Transformer
纯 Transformer 检测器在小数据集上容易过拟合,落叶这种场景样本量通常就几千张,直接上纯注意力结构,训练 loss 降得好看,验证集 mAP 却上不去。AC-YOLO 的思路是保留 YOLO 的卷积主干做特征提取,在 Neck 和检测头之间插入注意力模块,让模型在通道和空间两个维度上重新加权特征。卷积负责局部纹理和边缘,注意力负责长距离依赖和密集目标间的区分,两者互补。这个组合在雾天、逆光这类低对比度场景下提升最明显,因为注意力能把被压制的小目标响应拉回来。
3.2 注意力模块的插入位置与代码实现
插入位置有三个候选:主干末端、Neck 的 PAN 结构里、检测头前。我的经验是插在 Neck 的每个上采样融合之后,也就是 P3、P4、P5 三个尺度各加一个轻量注意力模块,参数量增加控制在 5% 以内。下面是一个通道加空间的混合注意力实现,可以直接嵌进 YOLOv5 或 YOLOv8 的 Neck 代码里。
import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction=16): super().__init__() # 全局平均池化和最大池化并行,比只用平均池化更稳 self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.fc = nn.Sequential( nn.Conv2d(channels, channels // reduction, 1, bias=False), nn.ReLU(inplace=True), nn.Conv2d(channels // reduction, channels, 1, bias=False) ) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = self.fc(self.avg_pool(x)) max_out = self.fc(self.max_pool(x)) return x * self.sigmoid(avg_out + max_out) class SpatialAttention(nn.Module): def __init__(self, kernel_size=7): super().__init__() # 沿通道维做平均和最大,拼成 2 通道再卷积 self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size // 2, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = torch.mean(x, dim=1, keepdim=True) max_out, _ = torch.max(x, dim=1, keepdim=True) concat = torch.cat([avg_out, max_out], dim=1) return x * self.sigmoid(self.conv(concat)) class ACBlock(nn.Module): def __init__(self, channels): super().__init__() self.ca = ChannelAttention(channels) self.sa = SpatialAttention() def forward(self, x): x = self.ca(x) x = self.sa(x) return xreduction=16是通道注意力的压缩比,通道数小于 64 时改成 8,否则中间层太窄会丢信息。kernel_size=7是空间注意力的卷积核,落叶这种不规则形状用 7 比 3 的感受野更合适。ACBlock 先通道后空间,顺序不要反,反了在密集落叶场景下空间注意力会先被背景主导。把 ACBlock 接到 Neck 的每个 C3 或 C2f 模块后面,改完用model.info()看参数量和 GFLOPs,涨幅超过 10% 就要考虑减层。
3.3 损失函数与正负样本分配
落叶检测的正负样本极不均衡,背景占绝大多数。YOLOv8 默认的 TaskAlignedAssigner 已经比早期版本好很多,但落叶密集时仍会出现正样本被背景淹没。我一般会把分类损失的权重适当调高,或者在损失里加一个针对小目标的加权项。具体做法是在loss.py里对分类分支的 BCE 损失按目标面积做加权,面积越小权重越大,上限设 3 倍,避免小目标权重失控。这个改动对 mAP 的提升通常在 1 到 2 个点,但会让训练前期 loss 波动变大,属于正常现象。
4. 训练调参与环境配置:让 AC-YOLO 在单卡上跑稳
4.1 环境搭建与依赖版本
环境这块血泪经验最多。CUDA、PyTorch、ultralytics 三者版本必须对齐,否则训练中途 BN 崩溃或者 loss 变 NaN 是常事。我一般用 conda 建独立环境,PyTorch 选 2.0 以上,ultralytics 用 8.x 系列。下面这套命令在单卡 V100 和 3090 上都验证过。
conda create -n acyolo python=3.10 -y conda activate acyolo # 按 CUDA 版本装 PyTorch,这里以 CUDA 11.8 为例 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics==8.1.0 pip install opencv-python matplotlib seaborn装完先跑python -c "import torch; print(torch.cuda.is_available())"确认 GPU 可用。如果返回 False,八成是 CUDA 版本和 PyTorch 不匹配,别急着改代码,先把环境理顺。
4.2 训练命令与关键参数设置
训练用 ultralytics 的 CLI 或 Python API 都行,我习惯写 Python 脚本方便记录参数。下面是一个针对落叶数据集的训练配置。
from ultralytics import YOLO # 加载预训练权重,小数据集必须用预训练,从零训基本没戏 model = YOLO("yolov8s.pt") # 替换 Neck 中的模块为 ACBlock,这一步在自定义模型 yaml 里完成 model = YOLO("ac_yolov8s.yaml").load("yolov8s.pt") results = model.train( data="leaf.yaml", # 数据集配置,指向 train/val 路径和类别 epochs=200, imgsz=640, batch=16, # 显存 12G 以上可上 16,8G 用 8 lr0=0.01, # 初始学习率,预训练微调用 0.01 足够 lrf=0.01, # 最终学习率系数,余弦退火到 lr0*lrf momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, # 前 3 轮预热,防止初期梯度爆炸 mosaic=0.3, # 落叶场景压低 Mosaic 概率 fliplr=0.5, hsv_v=0.4, # 亮度增强幅度,模拟不同光照 patience=30, # 30 轮无提升就早停 device=0, project="runs/leaf", name="ac_yolov8s_exp1" )lr0=0.01是预训练微调的稳妥值,从零训才用 0.1 量级。warmup_epochs=3对加了注意力模块的模型尤其重要,注意力层初始权重随机,没有预热容易在头几轮把主干带偏。patience=30配合 200 轮上限,实际有效训练通常在 120 到 150 轮。batch=16在 640 分辨率下大约占 10G 显存,显存不够就降 batch 并等比调小学习率。
4.3 训练过程监控与指标解读
训练时重点盯三个东西:loss 曲线、mAP50、混淆矩阵。loss 正常是前期快速下降后缓慢收敛,如果分类 loss 一直高位不降,检查标注里是不是有大量空 txt 或者类别 id 写错。mAP50 在落叶数据集上,基线 YOLOv8s 大概能到 0.75 到 0.82,加了 ACBlock 后目标提升 2 到 4 个点,如果反而掉了,先查注意力模块插入位置是不是在检测头之后,那样会破坏原始特征。混淆矩阵里如果背景被大量误判为落叶,说明正负样本分配或置信度阈值需要调,不是模型结构问题。
5. 避坑与排查:AC-YOLO 训练中最容易翻车的五个点
5.1 现象:训练到一半 loss 突然变 NaN,BN 层报错
原因通常是学习率过大或 batch 太小导致 BN 统计量不稳定,加了注意力模块后梯度路径变长,这个问题更容易触发。解决方法是把lr0降到 0.005,batch提到 16 以上,或者在注意力模块后加一层 GroupNorm 替代 BN。如果已经 NaN,从上一个正常 epoch 的权重恢复,别从头训。
5.2 现象:验证集 mAP 比基线还低,但训练 loss 正常
原因多半是注意力模块插入位置不对,或者参数量增加后过拟合。先确认 ACBlock 是插在 Neck 融合之后而不是检测头之后;再把weight_decay从 0.0005 提到 0.001,并开大dropout(如果模型支持)。数据量少于 1000 张时,注意力模块的通道压缩比从 16 改成 8,减少参数量。
5.3 现象:小目标落叶大量漏检,大堆落叶检测正常
原因是 P3 小尺度特征图上的注意力权重被背景压制。解决方法是单独对 P3 分支的 ACBlock 调大空间注意力核到 9,或者在损失里对小目标加权。另一个检查点是输入分辨率,640 对远处小落叶不够,可以试 800 或 960,但显存和速度要重新评估。
5.4 现象:混淆矩阵总和与验证集样本数对不上
这是 ultralytics 版本差异导致的统计口径问题,不是模型 bug。不同版本对置信度阈值和 NMS 的处理不同,混淆矩阵的行列和可能不等于图片总数。解决办法是固定一个版本,论文里写清楚用的 ultralytics 版本号,对比实验全部在同一版本下跑,别混用。
5.5 现象:推理速度比基线慢很多,达不到实时
注意力模块带来的计算量增加是主因。先看 GFLOPs 涨幅,超过 15% 就要精简:把三个尺度的 ACBlock 减到只在 P3 和 P4 加,P5 不加;或者把通道注意力换成更轻的 ECA 结构。实测在 3090 上,YOLOv8s 加三个 ACBlock 后 FPS 从 120 降到 85 左右,仍然满足车载实时要求,但如果你的部署平台是边缘设备,这个降幅要提前评估。
6. 论文级验证与消融实验:把 AC-YOLO 的改进讲成可复现的结论
6.1 消融实验怎么设计才有说服力
本科毕业论文的消融实验不需要太复杂,但必须干净。基线用原始 YOLOv8s,实验组依次是:只加通道注意力、只加空间注意力、通道加空间都加、再加损失加权。四组在同一个数据集、同一套超参下跑,只改模型结构。每组跑三个随机种子取平均,报告 mAP50、mAP50-95、参数量、FPS 四个指标。表格里把提升幅度标出来,提升小于 0.5 个点的组别要说明原因,不能只报好看的数字。
| 实验组 | 通道注意力 | 空间注意力 | 损失加权 | mAP50 | 参数量(M) | FPS |
|---|---|---|---|---|---|---|
| 基线 | 否 | 否 | 否 | 0.782 | 11.2 | 120 |
| A | 是 | 否 | 否 | 0.801 | 11.6 | 102 |
| B | 否 | 是 | 否 | 0.795 | 11.5 | 108 |
| C | 是 | 是 | 否 | 0.814 | 11.9 | 85 |
| D | 是 | 是 | 是 | 0.826 | 11.9 | 85 |
这张表是论文里最有价值的部分,它把每个模块的贡献拆开了。注意 FPS 的测试条件要写清楚:显卡型号、batch size、是否开半精度。不同条件下 FPS 差很多,不写清楚审阅老师会追问。
6.2 可视化验证:热力图和检测结果对比
光有数字不够,论文里放两组图:一组是基线模型和 AC-YOLO 在同一张密集落叶图上的检测结果对比,另一组是注意力热力图。热力图用 Grad-CAM 生成,看模型关注区域是不是更集中在落叶上而不是路面纹理。生成热力图的代码可以直接用 pytorch-grad-cam 库,指定目标层为最后一个 ACBlock,跑几张典型场景图就行。这一步能直观说明注意力机制确实起了作用,比单纯堆指标更有说服力。
6.3 我踩过的论文写作坑
最后一个具体技巧:论文里的实验数据一定要和代码、日志对得上。我见过太多人表格里的数字是手填的,跟实际跑出来的差一点,答辩时被问到就说不清。我的习惯是训练脚本自动把 results.csv 存下来,写论文时用脚本从 csv 里读指标生成表格,保证数字来源可追溯。另外,AC-YOLO 这个名字在论文里第一次出现时要给出完整定义,说明 A 和 C 分别指什么,别让审阅老师猜。改进点要写成「在 Neck 的 P3/P4/P5 融合后插入通道与空间混合注意力模块」,而不是「加了注意力」,具体到层和位置才显得是真正做过。希望帮到你。
本文还有配套的精品资源,点击获取