资讯动态

基于深度学习的道路坑洼识别:从数据集构建到模型部署的完整实践

发布时间:2026/10/18 10:09:50 来源:尧图企业网站定制
1. 毕设选题与整体设计思路1.1 核心需求这个项目到底在做什么道路坑洼识别说白了就是让计算机从图片或者视频里自动找出路面上的凹坑、破损区域。放在计算机毕业设计里这属于典型的“计算机视觉 深度学习”应用方向比单纯做一个分类任务有意思也比纯算法研究更容易做出实际效果。很多同学第一次听到这个题目会以为只是“判断一张图里有没有坑”其实不然。一次合格的毕设项目需要回答三个问题第一能不能把坑洼从正常路面里区分出来第二能不能大致标出坑洼的位置第三这套东西脱离训练环境后换个场景还能不能用。把这三个问题想清楚项目的技术路线自然就出来了。我当时选这个题目的时候考虑得很直白数据好找、模型可深可浅、展示效果直观。路面坑洼图片在网上有很多公开来源哪怕自己拿手机去校园周边拍也能凑一批模型方面可以用经典的卷积神经网络做分类也可以进一步用目标检测网络做定位最后做一个上传图片就能出结果的小网页答辩时演示效果拉满。1.2 技术方案对比为什么选择深度学习路线传统的路面破损检测多用图像处理的办法比如边缘检测、阈值分割、纹理分析。路边上那种道路检测车早期就是这么做的。这些方法的问题很明显对光照变化太敏感阴影一多就分不清楚对裂缝、坑洼、水渍等不同病害的区分能力也弱换个摄像头、换个路面材质参数基本要重新调。深度学习的优势在于不需要手工设计特征。模型自己从数据里学习“坑洼长什么样”而且随着数据量增加泛化能力会越来越强。用预训练模型微调训练成本也不高一张普通显卡甚至CPU都能跑通小模型。对毕设来说使用基于Python的深度学习方案还能顺带展示出你掌握了数据加载、模型构建、训练调参、模型导出这一整套工程流程比单纯贴一个OpenCV函数要加分得多。看起来最省事的方案可能是直接套用某个开源检测框架把权重下载下来跑个demo。但你只要在答辩时被问“模型的骨干网络为什么选这个”“训练集怎么划分的”“mAP是怎么算的”很容易露馅。所以这篇博文里我会按照一套完整的项目流程来拆解从数据到模型到部署演示每一步都给出可落地的方案。1.3 整体架构与模块划分一个完整的道路坑洼识别项目我建议拆成五个模块数据模块图像采集、清洗、标注、增强、数据集划分。模型模块选择网络结构设计损失函数加载预训练参数。训练模块配置训练超参数执行训练并保存最优权重。评估模块用验证集/测试集计算指标输出混淆矩阵和可视化结果。展示模块用Flask或Streamlit封装一个简单页面上传图片返回检测结果。数据模块决定模型上限模型模块决定性能下限展示模块决定最终观感。论文里写的“系统总体架构图”其实就是把这几个模块画成流程。真正动手的时候也要按这个顺序推进切忌上来就写训练代码。2. 数据集构建与预处理2.1 数据获取公开数据与自采数据的搭配做坑洼识别第一步卡人的往往是数据量。公开的路面病害数据集不算少常见的有裂缝检测类的也有坑洼类别相对完整的。你可以去学术数据集平台搜“pothole detection dataset”找带bounding box标注或者只带类别标签的都可以。如果找不到完整数据集也可以退一步用“正常路面”和“含有坑洼的路面”两类图片做分类训练数据量需求会低很多。我想提醒一点纯靠公开数据集训练出来的模型测试时不一定好用。公开数据集往往场景单一而真实路面有沥青、水泥、砖石坑洼大小不一还有积水、阴影、树叶遮挡。我当时的做法是“公开数据集打底自采数据补盲区”。找一块旧手机加上固定支架绕着学校周边和附近路况差的小路拍了大概一千五百张图挑出清晰的、有代表性的再配合公开数据混合使用。自采图片不需要多但能显著提升模型在本地场景下的鲁棒性。2.2 数据清洗与标注格式拿到原始图片后第一件事不是训练而是清洗。模糊的、过曝的、几乎看不清路面的图片直接删掉。连续拍摄的视频抽帧后相邻帧相似度太高需要按时间间隔抽帧我一般每三十帧取一帧然后再人工筛选。如果做目标检测每一张图还要画框标注。标注工具有很多我常用labelImg它是图形界面画矩形框导出XML文件格式和VOC数据集一致。标注时有个原则框要紧贴坑洼边缘宁可稍微大一点也不要框进去过多正常路面对于密集的坑洼区域可以拆成多个框不需要一个框包含所有破损。标注是很費时间的事一千张图大概要花一个周末但质量直接影响后面的模型效果不能偷懒。2.3 数据增强让有限的图片发挥更大价值深度模型需要大量数据但毕设项目很难搞到上万张图。数据增强是扩充样本最直接的手段。常见增强操作包括随机水平翻转、垂直翻转、旋转角度范围±15度。调整亮度、对比度、饱和度模拟不同天气和光照。随机裁剪和缩放模拟远近视角变化。添加少量高斯噪声增加模型对画面噪点的耐受度。我用PyTorch自带的torchvision.transforms就能完成大部分增强。有一点要注意增强操作不能太夸张。比如旋转角度超过30度、亮度调成黑白极端会让模型学到不真实的特征。增强不是越多越好我测试下来亮度扰动和随机翻转作用最明显裁剪稍加一点就好。如果做的是目标检测增强不是只在数据加载时做还要同步修改标注框的坐标。比如旋转时标注框的顶点也需要随之旋转否则框和物体就错位了。这个细节容易被新手忽略。如果你用的是主流检测框架框架内部已经集成了带标注增强的逻辑直接用即可如果是自己写数据管线就要特别小心。2.4 数据集划分不只看比例常规做法是按7:2:1或者8:1:1划分训练集、验证集、测试集。但分类任务里有个小坑不同来源的图片不能随便混在一起随机切。比如公开数据集中光照偏亮自采数据光照偏暗随机划分会让同源图片同时出现在训练集和测试集里导致测试指标虚高。正确做法是先按来源分组再在组内随机划分保证训练集和测试集的来源不重叠。这一点我在写论文数据说明时也专门提了评审老师会认为你想过泛化问题。还有一个是因为坑洼类别天然不均衡。如果做多分类正常、轻微破损、严重坑洼严重破损样本通常偏少。我会在划分时采用分层抽样让每一类在训练集和测试集中的比例都接近整体比例避免测试集里某一类一张都没有。3. 模型选型与训练配置3.1 选分类模型还是目标检测模型坑洼识别有两种解题思路图像分类和目标检测。分类只需要回答“这幅图里有没有坑洼”检测则需要回答“坑洼在哪个位置”。如果目标是把项目做得完整强烈建议做检测因为答辩效果更好技术含量也更高。但如果你想快速跑通流程先做分类也可以理解后面再切换检测模型并不复杂。分类任务我用的是ResNet18。它是很经典的卷积网络结构清晰源码容易读懂而且相对轻量。用一个在ImageNet上预训练过的ResNet18把最后的全连接层改成2输出正常/坑洼或者3输出就能开始迁移学习。即便只用两千张图也能取得不错的效果。检测任务我更推荐直接基于YOLOv8或者带backbone的检测框架来微调。因为这类框架已经把所有细节处理好了数据加载、锚框计算、损失计算、后处理都封装在接口里。对毕设来说重点是学会怎么调参和评估而不是从零手写检测头。3.2 PyTorch数据加载与模型定义示例我先写一段分类模型的完整骨架语言用PyTorch。这个框架对毕设来说最友好调试也直观。环境建议用Python 3.9以上版本配合CUDA的PyTorch安装包。import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader from torchvision import datasets, transforms from torchvision.models import resnet18 # 数据路径train/ 和 val/ 下各放置 normal/ 和 pothole/ 两个子文件夹 transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.3, contrast0.3), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 使用 ImageFolder 自动按子文件夹生成标签 train_dataset datasets.ImageFolder(rootdata/train, transformtransform) val_dataset datasets.ImageFolder(rootdata/val, transformtransform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue) # 加载预训练模型替换最后一层 model resnet18(pretrainedTrue) num_features model.fc.in_features model.fc nn.Linear(num_features, 2) device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device)这里有几件事要解释一下。pretrainedTrue表示加载模型在大型公开数据集上的预训练权重。fc是分类头原来输出1000类我们改成2类这一步叫迁移学习。迁移学习的底层逻辑是网络前面几层学到的是边缘、纹理、颜色斑块这些通用特征这些特征在路面图片里同样有用我们只需要让后面几层重新学习“坑洼的组合特征”就行。训练时不能上来就整个网络一起大改否则容易把预训练权重冲掉。常见策略是先冻结前面的所有层只训练最后一层跑几个epoch后再解冻全部层用小学习率微调。不过我实测下来ResNet18本身不大数据集也不多直接全网络训练学习率调低一点也能正常收敛。这里不用太纠结。3.3 损失函数、优化器与训练参数分类任务损失函数直接用交叉熵没什么可改的。优化器我习惯用AdamW权重衰减设0.01初始学习率设为1e-4。如果你用SGD加动量也能达到差不多的效果但AdamW对学习率不敏感调参压力小。batch size并不是越大越好。毕设电脑可能是普通笔记本显存有限。ResNet18在224×224输入下batch size设32大约需要4GB显存没有显卡的话就设8到16用CPU慢慢跑也能跑完就是时间要翻几倍。我的建议是优先用小模型小输入确保模型能跑起来等代码流程通顺后再考虑增加输入分辨率或换更大模型。训练epoch数怎么定我习惯先设置30个epoch同时在每个epoch结束时在验证集上计算准确率并保存当前最优模型。判断最优的标准不是训练集loss而是验证集loss或者验证集准确率。这样即使后面出现过拟合手里也攥着最佳权重。一个非常精简的训练循环可以写成下面这样criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-2) best_acc 0.0 epochs 30 for epoch in range(epochs): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() # 验证 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() acc 100.0 * correct / total print(fEpoch {epoch1}/{epochs}, Loss: {running_loss/len(train_loader):.4f}, Val Acc: {acc:.2f}%) if acc best_acc: best_acc acc torch.save(model.state_dict(), best_model.pt)3.4 用检测框架跑通目标检测如果做检测建议直接用YOLO系列框架接口非常简洁。安装对应的包调用model.train()就能训练。关键是把数据集组织成它要求的格式每行一张图片的路径后面跟类别和归一化的框坐标或者直接用它的标注格式读XML。from ultralytics import YOLO # 先训练 model YOLO(yolov8n.pt) model.train(datapothole.yaml, epochs50, imgsz640, batch16)其中的pothole.yaml需要写清楚训练集、验证集路径和类别名。训练结束后它会自动输出最优权重和一系列图表包括训练loss曲线、验证集PR曲线、混淆矩阵等。这些图表可以直接放进毕业论文的“实验结果”部分非常省事。之所以推荐用框架而不是自己写检测网络是因为检测比分类复杂太多。损失函数要处理前景背景不平衡还要计算预测框和真实框的匹配后处理要做NMS去重。这些内容不是一个月内新手能独立优化好的。用成熟框架把精力花在数据和质量分析上反而更容易做出高质量的毕设。4. 模型评估与结果分析4.1 评估指标准确率之外还要看什么很多人把“准确率高”挂在嘴边但坑洼识别里准确率并不能说明全部问题。如果训练集里80%是正常路面20%是坑洼模型全都预测成正常准确率也有80%。可它一条坑洼都识别不出来这项目基本等于废了。对分类任务我建议核心指标看四类准确率、精确率、召回率、F1值。精确率衡量“模型标定为坑洼的图里有多少真是坑洼”召回率衡量“所有真实坑洼图里有多少被找出来了”。对道路养护场景漏报坑洼的代价远高于误报所以应该重点提高召回率。实际操作中可以适当降低分类阈值让模型敢于输出“坑洼”类别即使误报多一些也能接受。4.2 混淆矩阵与可视化分析训练结束后把测试集图片全部过一遍模型生成混淆矩阵。矩阵对角线上的数字越集中越好看非对角线也会知道模型常把哪两类混淆。比如“轻微破损”经常被预测成“正常路面”说明这一类样本的区分度还不够或者图像数量偏少。此时应该回到数据集多补充轻微破损样本而不是盲目调模型结构。可视化还有一个关键动作把预测错误的图片单独挑出来拼接成一张大图逐张分析原因。常见错误类型有影子被当成坑洼、积水反光被当成坑洼、小坑被漏检。把这些错误案例按原因分组列在论文里能体现出你真的做了错误分析这是答辩加分项。4.3 过拟合判断与处理毕设数据集普遍不大过拟合是常客。判断方法很简单训练集loss一直下降验证集loss却率先上升或者波动说明开始过拟合了。我之前有一次训练到第20个epoch训练准确率接近100%验证准确率却停留在85%这种就是典型过拟合。处理方法按照效果优先级排序增加数据量或者加大数据增强强度这是最推荐的一招。增加Dropout或者正则化权重让模型不能死记硬背。使用更小的模型比如从ResNet50换到ResNet18。提前停止训练直接取验证集最优时刻的权重。记住不要一过拟合就换更大的模型。数据量小时大模型只会学得更快过拟合得更严重。小模型加数据增强才是最稳的组合。4.4 检测模型的评估与阈值调节检测模型的评估指标主要是mAP它是不同置信度阈值下精度和召回率综合的结果。主流检测框架训练结束会在验证集上算mAP50和mAP50-95。mAP50指的是IoU阈值0.5下的平均精度对毕设来说mAP50达到0.8以上就算不错。调试检测模型时有个很容易忽略的操作调整后处理置信度阈值。推理时检测框的置信度默认过滤掉不少低分框如果发现坑洼漏检可以往下调阈值如果发现大量误检就往上调。我建议做一个小工具用同一批测试图遍历多个阈值把所有检测结果可视化出来对比肉眼挑一个最适中的值。5. 系统展示与部署5.1 展示方案怎么选毕设不能只交一堆训练日志和准确率图表还需要一个可交互的演示系统。最简单的方案是用Flask写一个网页用户上传一张道路图片页面返回这张图是否含有坑洼或者返回画好检测框的图片。Flask足够轻量代码量少而且答辩时在本地跑不依赖网络。如果你想更省事一点也可以用Streamlit几行代码就能生成带上传组件的页面。但对于计算机专业的毕设我还是建议把Flask写清楚因为答辩老师更认可你理解HTTP请求、路由、模板渲染这些内容。5.2 用Flask封装模型推理接口下面是一个极简的实现。假设你已经用PyTorch训练好了分类模型并且把最优权重保存为best_model.pt。from flask import Flask, request, render_template from PIL import Image import torch import torchvision.transforms as transforms from torchvision.models import resnet18 app Flask(__name__) class_names [正常, 坑洼] device torch.device(cuda if torch.cuda.is_available() else cpu) model resnet18(pretrainedFalse) model.fc torch.nn.Linear(model.fc.in_features, 2) model.load_state_dict(torch.load(best_model.pt, map_locationdevice)) model.to(device) model.eval() transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) app.route(/, methods[GET, POST]) def index(): if request.method POST: file request.files[image] img Image.open(file.stream).convert(RGB) img_t transform(img).unsqueeze(0).to(device) with torch.no_grad(): outputs model(img_t) _, pred torch.max(outputs, 1) result class_names[pred.item()] return render_template(index.html, resultresult) return render_template(index.html, resultNone) if __name__ __main__: app.run(debugTrue)这段代码虽然短但是能把整个推理流程串起来读图、预处理、模型前向传播、输出结果。如果你做的是检测模型也不难先把模型推理得到的结果框画到图上再用save保存到临时文件夹最后把这个临时图片传到网页模板里展示即可。5.3 前端页面不用花哨但要完整网页部分不需要复杂的框架一个干净的HTML页面加一个文件上传控件就够了。我见过不少同学花大量时间调CSS最后答辩时屏幕分辨率一变布局又乱了得不偿失。重点应该放在识别效果上界面只要简洁可用。可以在页面中增加两张图片的对比左侧是上传的原图右侧是标注后的结果图。这样可以直观展示模型的定位能力。再附上一行识别结果文本比如“检测到坑洼区域置信度0.92”给人很专业的感觉。除了Web界面也可以准备一个本地批处理工具对某个文件夹里的图片批量识别并自动把结果按类别移动到不同文件夹。这个工具可以整合进演示流程用于说明“系统不仅能看单张还能批量处理”。5.4 部署注意事项与模型导出本地写好的Flask应用论文里可以写成“基于B/S架构的坑洼识别系统”。如果是想在宿舍的电脑上演示不用部署到服务器。但要注意依赖环境答辩演示的电脑如果没有Python环境最好提前用PyInstaller把整个Flask应用打包成exe或者准备一个配置好的Anaconda环境。别小看这一步我见过有人到答辩现场才发现缺依赖包浪费时间又影响心态。模型导出时用torch.save保存state_dict最方便但是得在加载模型时重新定义网络结构。如果想跨平台可以再导出一份ONNX格式用ONNX Runtime做推理速度更快部署也更灵活。导出ONNX只需要一行dummy_input torch.randn(1, 3, 224, 224).to(device) torch.onnx.export(model, dummy_input, best_model.onnx, input_names[input], output_names[output])6. 常见问题与避坑指南6.1 环境配置问题PyTorch安装是最常见的拦路虎。安装CPU版还是GPU版要看电脑有没有NVIDIA独立显卡。没有显卡的同学不必强求训练慢就慢一点可以适当降低图片尺寸和batch size或者用Google Colab在线训练。版本冲突也很烦人。比如numpy版本和PyTorch不兼容torchvision和torch版本不一致导致模型加载报错。我的经验是直接创建新的虚拟环境使用venv或conda在里面重新安装依赖不跟系统原有环境混在一起。每次复现项目都新建环境能省大量时间。6.2 道路坑洼图像的特殊干扰坑洼识别的世界远比公开数据集复杂。树影投射在路面上会被模型当成坑洼积水反光会被当成坑洼施工区域深色修补痕迹也会被当成坑洼。解决这些误检不能只靠换模型要从数据层面下手。专门收集一些带树影、带积水、带修补痕迹的“干扰图片”作为负样本放进训练集里让模型学会区分。我最后一批训练数据里负样本的比例大约占百分之四十误检率明显下降。另外同一个坑洼故障白天和晚上看起来完全不同。如果场景允许尽量收集不同光线下的图片。Night-time图像在公开数据集里很少需要自己补。这一步虽然费劲但对模型鲁棒性提升极大。6.3 训练速度与显存不足模型训练到一半显存爆掉是常事。快速方案是降低batch size同步降低学习率因为小batch需要更平滑的更新。或者减小输入图片分辨率比如从640降到416检测模型依然可以工作。如果还不行就把pin_memory关掉减少GPU内存占用。如果时间很紧还有一个技巧先用小数据集和少量epoch验证整个流程能不能跑通再全量训练。比如先拿每个类别二十张图跑一个epoch确认代码不会中途报错然后再正式开训。这能避免你等了三十分钟才发现数据加载路径写错的尴尬。6.4 答辩演示时的注意事项答辩时间有限不要现场训练模型那是最不稳的行为。提前录制一段视频展示Web页面操作流程再现场跑一两次预测。录制视频作为兜底现场演示作为加分项。准备一个“失败案例”也非常有效。主动拿出一张模型预测错的图片解释为什么错、后面怎么改进。答辩老师看到你有分析能力而不是只会跑通代码通常会给更高评价。这也正好用上前面可视化错误分析的内容。最后再聊点实在的道路坑洼识别听起来不难但要做到稳定、能落到实际场景确实需要反复打磨数据。我个人最大的体会是与其花大量时间调模型结构不如多花时间看数据。模型结构用成熟方案数据里缺什么就补什么效果提升往往是最明显的。如果你正在做这个毕设我建议按“先跑通、后优化、再包装”的顺序来。第一天先把最简单的分类模型跑通哪怕准确率只有70%也不要慌后面所有改进都是在让这个70%变成80%、85%。不要一上来就追求90%以上很容易被打击。最后分享一个小技巧把每一版的模型参数、训练时间、验证集指标和错误案例截图都记录下来。这不仅是写论文的原始素材还能在调参过程中帮你找到哪些改动真正有效。你会发现记录实验本身就是提升项目质量最好的方式。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑