道路坑洼识别这个题,在课程设计和毕业设计里出现的频率相当高。表面上看,它就是一个标准的计算机视觉目标检测任务,但真正动手之后你会发现,从数据、模型、训练到演示系统,每一环都有不少坑。这篇文章按我自己完整做过一遍的课程设计流程来拆解,尽量把每一步的“为什么”讲清楚,顺便把踩过的坑都列出来。
这套方案的核心链路是:收集道路图像 → 标注坑洼区域 → 用深度学习目标检测模型训练 → 导出权重 → 搭一个能上传图片并返回检测结果的演示系统。它解决的实际问题很简单:过去道路病害排查靠人工巡路,效率低、主观性强;用模型自动识别坑洼的位置和数量,至少能辅助养护部门做初步筛查。对课程设计来说,这个题目的优势在于技术栈集中、见效快、答辩时演示效果好,而且方便扩展成裂缝识别、破损等级评估等方向。
适合参考这篇内容的人,主要是正在纠结课程设计或毕业设计选题的同学,尤其是那种“想用深度学习但不知道从哪下手”的情况。如果你只是想把项目跑通拿个学分,按本文的流程走基本够了;如果你想把分数往上冲,文中关于实验对比和答辩问答的部分也能派上用场。
1. 项目整体拆解:题目背后藏着哪些隐形要求
1.1 先看题目里的三个关键词
很多同学看到“机器学习、python、深度学习”串在一起,第一反应是“又要做图像分类了吧”。其实不对。题目真正落点在后半句——“道路坑洼识别”,这是一个目标检测问题,要回答的是三件事:图像里有没有坑、坑在哪个位置、坑大概多大。如果只把它当成分类问题,输出一个“有坑/没坑”的标签,实现会简单很多,但答辩时很容易被追问:“那你怎么知道坑在哪里?怎么判断严重程度?”所以绝大多数课程设计都会首选目标检测路线。
“深度学习”决定了技术路线,要搭网络、做训练;“python”决定了实现语言,整个数据科学和机器学习生态都在这边,找资料、装环境都方便;“机器学习”更像是一个背景词,意味着整个项目要包含数据、模型、训练、评估这一套完整流程,而不只是调用一个现成接口。换句话说,题目真正考察的是你能不能把一套标准的深度学习工程链路走通。
1.2 除了模型,课程设计还隐形要求你交付这些
一个能跑的模型只是及格线。课程设计通常还要交设计报告、源码和演示,报告里至少要讲清楚:数据怎么来的、为什么选这个模型、训练过程如何调参、结果指标怎么解读。很多同学栽在“只跑通了代码,但说不清原理”上。答辩老师最爱问的一句话是:“你这个权重文件是怎么来的,训练过程发生了什么?”这个我在第五部分专门说。
所谓“隐形要求”还包括对比实验。拿一个baseline和一个检测模型对比,或者对比不同训练策略,会让报告显得有分量。实际做的时候不用贪多,一两个对比就够。我见过最有效的报告结构是:先说明为什么路面坑洼检测不能靠传统图像处理,再给出深度学习的思路,然后用数据证明深度学习确实有效。这样逻辑闭环,答辩老师基本挑不出大毛病。
1.3 为什么传统图像处理方案在这里行不通
有人会想:我用边缘检测、阈值分割,再找连通区域,不也能找坑吗?能,但只在非常理想的光照和路况下能。道路场景里,阴影、落叶、水渍、修补痕迹、沥青纹理都会产生大量边缘,传统方法很难把这些和真实坑洼区分开。我当年用传统边缘检测算法试过,晴天直射光下的效果勉强能看,一到阴天加树影,误检率直接爆炸。
深度学习方案的本质是把“特征”交给模型从样本里学。坑洼没有统一的颜色、形状和纹理——有的浅,有的深,有的边缘整齐,有的碎裂成一片——这种“不好描述但能举例子”的问题,恰恰是深度神经网络擅长的。再加上迁移学习,用预训练权重初始化,即使是几千张图片的小数据集也能训练出可用的检测模型。这是整套方案的底层逻辑,也是答辩时可以讲清楚的核心卖点。
2. 数据准备:决定模型上限的环节
2.1 数据规模和来源怎么定:2000张是个舒服的区间
先说数据规模。我完整跑下来大概用了2000张左右,其中带坑洼的约1500张,正常路面负样本500张。对课程设计来说,1500到2500张是一个比较舒服的区间:再少容易过拟合,再多标注耗时急剧上升,性价比不高。你不需要追求数据集越大越好,而是要保证质量,让每一张样本都有效。
来源主要有三个:公开的道路损伤数据集、自采数据、以及在遵守平台规则的前提下收集的公开街景素材。很多公开数据集直接标好了框,能省掉大量时间;自采数据的价值在于答辩时你可以说“模型在我自己采集的数据上也有效果”,这句话比任何指标都管用。我当时的做法是晴天和阴天各拍一批,用手机蹲在路边拍井盖附近和人行道边缘,注意不要拍车牌和有明显人脸的路段,既省事又避免了隐私问题。
类别设计也别上来就贪多。最基础的做法是只设一个坑洼类,稳妥;如果想加亮点,可以拆成两类:坑洼和裂缝。但类别一多,标注成本和混淆程度都会上升,建议先把单类跑通,再考虑加类。这个顺序放在整个项目里同样成立:先做减法,再做加法。
2.2 标注规范和几个容易翻车的细节
标注工具选开源框选式工具,导出格式就选YOLO的txt格式。每一行的结构是:类别编号、归一化中心x、归一化中心y、归一化宽w、归一化高h。这个格式对初学者有点反直觉,很多人会问“为什么不直接存矩形四个角的像素坐标?”因为模型输入图像会被缩放到固定尺寸,归一化坐标可以避免训练时换算出错。
标注规范上,我给自己定的原则是:框要贴合坑洼可见边缘,但不强求精确到像素级;坑洼有重叠时按完整可见部分分别标注;特别小、面积小于整体图像1%的坑统一不标,保持一致即可。原因很简单,标注的“一致性”比“精细度”重要得多。如果两个人标同一个坑,一个框得很紧,一个松一大圈,模型学到的边界就是乱的。
标注完一定要做可视化检查。把标注框画回原图,随机抽查200张,重点看有没有标漏、标反、类别标错。这一步省不得,踩过的人都懂。我第一版数据集就是因为有十几张图标签和图片对不上,训练出来mAP死活上不去,后来才发现是导出时文件配对错位了。
注意:x_center、y_center、width、height都是除以图像宽高后的0到1之间的数值,手填很容易错,务必用标注工具导出,不要手动编辑txt。
2.3 数据增强到底该加哪些
道路图像的主要变量是光照、天气、拍摄角度。数据增强建议围绕这几类做:亮度扰动模拟不同时段,高斯模糊模拟雨天或快速拍摄虚焦,随机水平翻转扩充样本,多图拼接增强对中小目标的检出能力。这些操作在常见检测框架里都有现成开关,按文档打开对应参数即可,不用自己从头写。
有一个坑要提醒:水平翻转数据增强时,如果用的是YOLO格式,x_center要同步换算成1减去原值。很多框架会自动处理,但如果自己写数据增强脚本,这步漏了会导致框整体偏移,训练时loss看似正常下降,实际预测结果全歪。
数据划分建议按8:1:1拆成训练、验证、测试。验证集用于调参,测试集只在最终评估时用一次,避免“对着测试集调参”把指标做得虚高。负样本很重要,500张正常路面图相当于告诉模型“这些不是坑”,没有它们,模型会把坑洼周边的新旧沥青色差全当成目标。
3. 模型训练实操:从环境配置到得到可用权重
3.1 环境与项目结构:先花十分钟确认GPU可用
建议用虚拟环境管理依赖,创建一个Python 3.8环境,再安装深度学习框架和对应CUDA版本。版本匹配最常见的坑是框架和显卡驱动不匹配,装完先跑一句命令确认:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"输出True说明GPU可用,False就老老实实调驱动,或者装CPU版本。CPU训练不是不行,但150轮要跑到天荒地老,强烈建议用带显卡的机器或云端的免费GPU平台。数据集目录结构我习惯这样组织:
dataset/ images/ train/ # 1600张 val/ # 200张 test/ # 200张 labels/ train/ val/ test/配套一个数据配置文件,内容大概是这样:
path: ./dataset train: images/train val: images/val nc: 1 names: ['pothole']path这一项容易踩坑,相对路径和绝对路径在不同机器上表现不一样。建议所有命令都在项目根目录下运行,路径尽量统一,换机器时少报错。
3.2 训练参数怎么定:先跑20轮验证再跑全量
参数取值可以参考下面的表格,这是我做过几轮后觉得比较稳的起点:
| 参数 | 我的取值 | 说明 |
|---|---|---|
| 输入图片尺寸 | 640 | 速度和精度的折中,太大会爆显存 |
| 批大小 | 16 | 显存不够就降到8或4 |
| 训练轮数 | 150 | 数据量在2000张附近够用,可加可减 |
| 优化器 | SGD或AdamW | 前者传统稳定,后者收敛更快 |
| 初始学习率 | 0.01或0.001 | 配SGD常用0.01,配AdamW常用0.001 |
| 预训练权重 | 使用 | 迁移学习的关键,小数据集必备 |
640这个尺寸是大多数检测模型默认的配置,识别小坑洼需要保留足够细节,太小了目标糊成一团。批大小16在中等显存上能跑,同时保持收敛稳定。轮数150轮在2000张图上足够,如果加了更多数据可以加到300。预训练权重一定要用,训练出来的效果和随机初始化差距很大,尤其是在数据量不足的情况下。
实操上有一个小技巧:先把轮数设成20,跑通全流程,确认数据能读进去、loss有波动、能输出权重文件,再启动完整150轮。这个小实验能省掉大量无效等待。我见过同学直接跑全量,跑了一晚上发现标签路径配错,那一夜基本白费。另外,显存吃紧时可以开启混合精度训练,一般一句话就能打开,能让显存占用明显下降。
3.3 训练过程中怎么判断训练是否正常
训练过程中每轮会输出loss和mAP等指标。正常情况是分类损失和定位损失稳步下降,前20轮降得最快,然后逐渐平缓;准确率、召回率、mAP会在震荡中上升。如果loss直接发散、越训越大,优先怀疑学习率太大;如果loss降不下来,回头检查标注格式和类别编号,八成是早期的数据问题。
训练结束后,工作目录下会有best和last两个权重文件。best是按照验证集效果自动保存的最优结果,后续推理直接用best。训练完拿一张没有见过的图片快速验证,命令大概是这样:
python detect.py --weights runs/train/exp/best.pt --source test_images/001.jpg --conf-thres 0.3能正常画出框就算链路通了。到这里,一个可用的检测模型就算拿到了,后面就是评估和演示系统的事。
4. 结果评估与答辩演示系统
4.1 指标该看哪几个:别只盯着mAP
课程设计里最常见的误区是只报一个mAP就完事。答辩老师更希望听到的是你“看懂了”这些指标。我习惯看这五个:
| 指标 | 含义 | 课程设计参考水平 |
|---|---|---|
| 精确率 | 预测为坑的结果里确实为坑的比例 | 85%以上 |
| 召回率 | 真实坑洼被找出来的比例 | 80%以上 |
| mAP@0.5 | 在交并比0.5下的平均精度 | 85%-90% |
| mAP@0.5:0.95 | 更严格的平均精度 | 60%以上 |
| 单帧推理时间 | 处理一张图耗时 | 演示可用即可 |
只看mAP的风险在于它掩盖了“漏检”和“误检”的差异。召回率低说明很多坑没被找出来,这对道路巡检来说是严重的问题;精确率低说明正常路面频繁报警,系统没法用。答辩时能说出一句“我在精确率和召回率之间做了权衡,把推理置信度阈值调到0.3来优先保证召回”,这句话比任何指标数字都加分,因为它证明你真的理解检测系统的调优逻辑。
4.2 演示系统可以这样搭:上传图片返回结果
课程设计演示最重要的是让老师两分钟内看懂它在干嘛。最省事的方案是做一个网页式上传界面或者桌面小工具,上传一张图片,立刻显示检测框和坑洼数量。推理核心部分大概是这样:
import cv2 import torch # 加载本地训练好的自定义权重 model = torch.hub.load('你的项目根目录', 'custom', path='runs/train/exp/best.pt', source='local') def infer(image_path, output_path): img = cv2.imread(image_path) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = model(img_rgb, size=640) result_img = results.render()[0] cv2.imwrite(output_path, cv2.cvtColor(result_img, cv2.COLOR_RGB2BGR)) return len(results.xyxy[0])return返回的是检测到的坑洼数量,框已经画在输出图里。演示时可以顺便加一个批处理模式,把测试集图片全部跑一遍,统计平均耗时,并在界面上显示出来。这个小细节会让整套系统看起来更完整。
视频检测其实就是把视频帧一帧一帧地过这个推理函数,注意不需要每帧都算,隔几帧抽一帧也能带来流畅的演示效果。答辩现场网络不稳定,尽量不要依赖在线服务,所有推理都在本地完成,提前把示例图片和视频准备好,存在本地文件夹里,一点就出结果,比现场打开摄像头稳定得多。
5. 常见问题与排查技巧实录
5.1 训练效果不理想的排查顺序
出现训练效果差的情况,我的排查顺序永远是:先检查数据,再检查参数,最后才考虑改网络结构。按下面的顺序走一遍,九成问题都能定位:
| 现象 | 优先排查方向 |
|---|---|
| loss不降或发散 | 学习率太大、标注格式错误、标签编号对不上 |
| 训练集效果好但验证集差 | 过拟合,先加增强、开早停、补数据 |
| 几乎全部漏检 | 预训练权重没加载、标注框是否正常、图片尺寸异常 |
| 误检一堆 | 负样本不足、置信度阈值太低 |
还有一个我常用的操作:训练中途把当前权重的预测结果直接可视化到验证集上,看一眼框的位置对不对。如果框整体偏移,优先怀疑标注或增强环节;如果框位置对但置信度很低,那就是样本难度问题,需要补充同类样本,这比盲调参数高效得多。
5.2 环境与工程问题:中文路径、显存、内存
工程层面有几个高频问题,基本每个人都遇过。第一是中文路径导致图像读取失败,cv2.imread遇到中文路径直接返回空值,用下面的方式解决:
import cv2 import numpy as np def imread_unicode(path): return cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)第二是GPU显存溢出,先把batch size降到8或4,再把输入图尺寸降到480,最后开混合精度,基本能解决。第三是训练到一半进程被杀,优先检查系统内存是不是爆了,把数据加载线程数调低,关掉后台无关程序。第四是换机器就报错,模型路径、数据集路径尽量写相对路径并固定工作目录,别依赖本机绝对路径,这一步能省很多沟通成本。
5.3 答辩高频问题怎么应答
总结几个我被问到过、也见过老师反复提问的问题,答题思路分享出来:
为什么用深度学习不用传统算法?回答思路:坑洼没有统一颜色和纹理,传统特征在阴影、水渍下误检率高;深度网络自动学习层级特征,配合迁移学习,小数据集也能训出可用模型。
为什么选这个检测模型?回答思路:单阶段检测,速度和精度均衡,生态成熟、学习资料多,适合课程设计快速落地,也方便后续换更轻量的模型对比。
换个城市或光线条件效果还稳定吗?回答思路:模型效果由数据边界决定,拿到新场景数据做微调和数据增强即可,同时可以配合置信度阈值调节。
答辩时千万不要夸大效果。说“在验证集上mAP到了90%”没毛病,说“能直接取代人工巡查”就等着被问倒。老师也是做过项目的,你几斤几两一聊就知道,不如诚实一点,把学到的原理讲透。
注意:答辩演示前,一定要把虚拟环境和依赖完整记录下来,写进README。现场临时装环境是最容易出现意外的环节,提前准备好能让你从容很多。
6. 写在最后:给正在做这个题的同学几句实在话
最后分享一些我实际做下来的体会。第一,先花半天把20个小轮数跑通,确认整个链路正常,再投入完整训练。这个习惯帮我避开了很多无效等待,每次改数据集或改配置,都先小规模验证一下再说。第二,数据质量是项目的生命线,宁可减少样本量也要保证标注正确,因为后续所有指标都建立在这项工作之上。第三,把每个环节的记录留好,训练日志、指标曲线、测试结果都保存下来,写设计报告的时候会发现这些都是现成的素材,不用临时补。
道路坑洼识别这个题目上限不低、下限也很明确,认真做完一套,你对目标检测、数据工程、模型迭代这一整条链路就不会再发怵了。希望这篇记录能帮你少走一点弯路,答辩顺利。