简介:面向YOLO系列目标检测算法的下水道缺陷数据集,覆盖关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片等典型缺陷类别,并已划分训练集、验证集与测试集,可直接用于模型训练、验证与测试,适合市政管网检测研发人员及目标检测算法初学者。压缩包共2000个文件,其中1636个XML文件为VOC格式标签、364个TXT文件为YOLO格式标签,两种格式分目录存放,同时提供data.yaml配置文件,能够不经额外转换直接对接YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10及YOLO11等主流版本;整个包体仅7.43MB,已有66人学习下载。标签文件名末尾带有类别名称,方便按缺陷类型筛选样本;YOLO标签采用从0开始的类别索引,目标框中心坐标与宽高均按图像尺寸归一化,格式规范,符合训练脚本默认读取要求。若需了解具体类别标签,可在资源详情页核对。这套数据既适合快速搭建下水道缺陷检测模型,也适合标签格式转换练习、不同YOLO版本对比实验以及数据增强策略验证等场景。
1. 下水道缺陷检测的数据底子:2364张标注图先解决「有没有」的问题
做市政管网缺陷检测的工程师多半经历过这种尴尬:项目立项时甲方要求识别裂纹、障碍物、带扣、洞,真到了训练阶段才发现,内部资料库里全是没标注的原始巡检视频,掏钱买公开数据集又买不到细分类的下水管道场景。这份2364张带标签的下水道缺陷数据集,解决的就是这个「有没有数据」的冷启动问题。它把关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片七类常见缺陷做成了一套YOLO可直接训练的工程包,训练集、验证集、测试集已经划分完毕,data.yaml配置齐全,适用于yolov5到yolo11全系版本。适合正在做YOLO系列目标检测算法验证、需要快速跑通管道缺陷识别流程的算法工程师和研究生,也适合刚上手YOLO的开发者拿它当第一个完整训练范例。
2. 标签格式先看懂:yolo归一化坐标与voc双轨并存
拿到数据集先别急着训练,第一步要搞清楚它的标注组织方式。这套数据最值得肯定的一点是同时提供了 yolo 格式(txt)和 voc 格式(xml),分别存放在两个文件夹里。YOLO系训练直接读txt,而xml可以用于mAP评测时的voc指标计算,或者转成coco格式做横向对比。两种格式并存不是冗余,而是让同一批图在不同框架和评测体系里都能复用。
2.1 txt与xml双轨并存:文件组织与类别索引
文件名形如img_0319_1048.txt,对应同一批图像文件的标注信息。每个txt文件里可能有一行或多行标注,每行代表一个目标框,格式如下:
<class> <x_center> <y_center> <width> <height>前面提到文件名末尾是部分类别名称,但文件内容才是真正的标注依据。读取时不能只看文件名判断类别,要以txt第一列的class索引为准。YOLO格式的类别索引从0开始,和voc格式xml里的对象名称一一对应。下面这段脚本可以快速扫描整个标签目录,统计出当前一共标注了多少目标、每个类别占多少比例:
import os from collections import Counter label_dir = "labels/train" # 按你的实际解压路径修改 counter = Counter() total_boxes = 0 for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue with open(os.path.join(label_dir, fname), "r", encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) == 5: cls = int(parts[0]) counter[cls] += 1 total_boxes += 1 else: print(f"格式异常: {fname} -> {line.strip()}") print("总目标框数:", total_boxes) for cls, cnt in sorted(counter.items()): print(f"class {cls}: {cnt}")这段脚本做了两件事:统计每个类别的目标数量,同时标记出格式异常的行。跑完你会对类别不平衡程度有个直观认识。如果某个类别的框数远低于其他类,说明后续训练时要考虑类别权重或者数据增强策略。注意label_dir路径要改成你本机实际解压出来的标签目录,如果你把train和val的标签分开存放,就分别统计。
结合摘要里给出的类别清单,实际类别索引映射大致是:0代表关节偏移,1代表障碍物,2代表裂纹,3代表带扣,4代表洞,5代表公用设施入侵,6代表碎片。具体索引值可能因打包顺序不同而有差异,保险做法是打开任何一个xml文件确认对象名称和索引的对应关系,再决定data.yaml里怎么配置names列表。
2.2 归一化坐标的数学含义与标注质量检查
txt里存的x_center、y_center、width、height都是相对图像宽度和高度的比例值,范围在0到1之间。实际像素坐标需要乘以图像的宽高才能还原:
- 框左上角x像素 = (x_center - width/2) × 图像宽度
- 框左上角y像素 = (y_center - height/2) × 图像高度
这个转换逻辑是后续做标注可视化和数据增强验证的基础。很多初学者直接拿归一化坐标去画图,结果框的位置完全错位,就是忘了乘回图像尺寸。更隐蔽的坑是:有些标注框的中心点坐标或宽高虽然都在0~1之间,但组合起来框已经超出图像边界,比如x_center=0.95, width=0.2,左边越界了。这类问题不会报错,但会影响训练时的anchor匹配和NMS表现。我习惯在训练前跑一遍边界校验脚本:
import os from PIL import Image img_dir = "images/train" label_dir = "labels/train" img_w, img_h = 640, 640 # 先给默认值,遇到真实图片会覆盖 for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue img_name = fname.replace(".txt", ".jpg") img_path = os.path.join(img_dir, img_name) if os.path.exists(img_path): with Image.open(img_path) as im: img_w, img_h = im.size with open(os.path.join(label_dir, fname), "r", encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls, xc, yc, w, h = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) # 还原像素坐标 x1 = (xc - w / 2) * img_w y1 = (yc - h / 2) * img_h x2 = (xc + w / 2) * img_w y2 = (yc + h / 2) * img_h if x1 < 0 or y1 < 0 or x2 > img_w or y2 > img_h: print(f"越界框: {fname} -> cls={cls}, box=({x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f}), 图像尺寸=({img_w},{img_h})")参数说明:img_w和img_h在循环里动态更新,避免硬编码错误。还原计算时用xc - w/2计算左边界、xc + w/2计算右边界,比较是否小于0或大于图像宽高。如果某个框越界,打印出来的信息里包含文件名、类别和还原后的像素坐标,方便你回去核对原始标注。这类越界框通常来自标注工具的边缘误操作,数量多的话建议过滤掉而不是直接拿来训练。
3. 配置data.yaml与训练启动:yolov8到yolo11都能跑
理解了标签格式,接下来就是把数据组织成YOLO训练工程的标准目录结构,然后启动训练。这一步决定了你后面所有实验是否可复现,建议把train、val、test的图片和标签都放进固定目录,不要散落在桌面或临时文件夹里。
3.1 目录整理与data.yaml配置
理想的项目目录结构应该是:
sewer_defect_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── xml_labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml如果下载解压后的目录结构和上面不完全一致,先手动整理成这个形态再配置yaml。data.yaml是YOLO训练时唯一必须改对的文件,它的路径字段决定了数据加载器去哪里找图。下面是这份数据配套的data.yaml典型配置:
path: D:/sewer_defect_dataset # 数据集根目录,绝对路径更稳妥 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 test: images/test # 测试集图片相对路径,可选 nc: 7 # 类别总数 names: 0: joint_offset 1: obstacle 2: crack 3: buckle 4: hole 5: utility_intrusion 6: debrispath字段是根目录,train、val、test写的是相对路径,两者拼接后就是完整的图片目录。nc必须和names列表长度一致,这是训练程序校验类别数用的。特别提醒:如果你在Windows上训练,path建议用绝对路径,避免相对路径拼接时因为盘符问题找不到文件;如果在Linux服务器上训练,绝对路径同样最省事,省得每次换机器都要改配置。
3.2 yolov8训练命令与关键参数选择
数据目录整理好之后,训练命令本身不长,但几个关键参数直接决定训练结果。拿yolov8来举例,我的习惯是关闭预训练权重对应的数据集假设,用coco预训练模型做迁移学习,同时选择适合小数据量的增强策略。yolov8官方仓库训练入口是yolo命令行工具:
yolo detect train \ model=yolov8m.pt \ data=sewer_defect_dataset/data.yaml \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.005 \ optimizer=AdamW \ patience=30 \ project=sewer_runs \ name=exp_sewer_v8m \ cache=True参数说明:model=yolov8m.pt加载COCO预训练权重,m版本在精度和速度之间比较平衡;epochs=150对于2364张图的中等规模数据集够用;imgsz=640和训练时推理尺寸保持一致;batch=16根据显存调整,8G显存建议降到8;lr0=0.005是从经验值里选的小学习率,避免预训练权重被大步长破坏;patience=30表示连续30轮验证集指标不提升就早停。cache=True可以把数据缓存进内存,第二次训练时加载速度明显加快。
训练完成后在sewer_runs/exp_sewer_v8m/weights/目录下会有best.pt和last.pt,验证和预测都用best.pt,不用last.pt,这是防止过拟合导致的验证集指标虚高。
3.3 yolov5与yolo11的兼容差异
如果你的环境是yolov5,训练命令几乎一样,区别在于yolov5的data.yaml里不需要额外的test字段也能跑,而且yolov5默认用hyp.scratch-low.yaml超参数,学习率策略偏保守。yolov5训练命令参考:
python train.py \ --data sewer_defect_dataset/data.yaml \ --weights yolov5m.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --name sewer_v5m \ --project sewer_runsyolov5和yolov8的主要差别在数据增强策略:yolov5默认开启mosaic和mixup,yolov8的augment参数控制力度。yolo11回归了更简洁的配置方式,训练命令可以直接用yolo detect train接model=yolo11m.pt。无论用哪个版本,data.yaml这个文件是通用的,这也是一份数据集同时声明支持多版本的原因。实际使用中yolov8和yolo11的NMS策略略有差异,但在这种类别较集中的缺陷检测场景下,肉眼可见的差距不大,优先选你环境里依赖最少的版本即可。
4. 训练避坑记录:五条实测踩坑与排查
数据集的标签质量整体不错,但在实际训练和复现过程中,有几个坑是大概率会踩到的,我把排查过程和解决方式记录下来。每条都按现象、原因、解决的顺序写清楚,方便你对号入座。
4.1 训练到一半loss飞升,检查mosaic增强是否越界
现象:训练到20~30轮时,train loss突然从零点几跳到几十,val loss同步飙升,之后再也降不回来。
原因:这类数据里有不少缺陷目标紧贴图像边缘,比如裂纹从图边缘开始延伸。YOLO默认开启mosaic增强,把四张图随机拼接缩放,边缘目标在拼接过程中被裁掉或者框的坐标计算出现偏差,导致训练梯度异常。加上下水道图像本身光照不均,mosaic拼接后对比度差异大,模型学到的是伪特征。
解决:训练命令里加mosaic=0.0关闭mosaic,或者用close_mosaic=10让最后10轮关闭mosaic做微调。yolov8可以用mosaic=0.2保留低概率的mosaic,yolov5则在hyp.scratch-low.yaml里把mosaic参数直接改为0。关闭后loss曲线会明显稳定下来。
4.2 验证集mAP不低但检测视频时漏检碎片
现象:验证集mAP@0.5达到了0.75以上,但跑实际巡检视频时,小碎片目标大量漏检,尤其是尺寸小于32×32像素的碎片。
原因:2364张图里碎片类别的标注框数量虽然不少,但很多碎片目标框边长占整个图片的比例不到5%。模型在640×640输入下经过多次下采样,小目标的特征图分辨率不足以保留足够信息,再加上默认anchor尺寸偏大,小目标匹配不上。
解决:把imgsz从640提高到768或896,小目标检测率会显著提升,代价是训练和推理时间变长。如果显存不够,可以加augment=True配合scale=0.5做多尺度训练。另一个有效手段是单独增大小目标对应的anchor,但yolov8自动anchor机制下通常不需要手动调,优先改imgsz。
4.3 重新划分数据集后结果无法复现,检查随机种子
现象:有人重新划分了train和val,用同样的参数训练,得到的结果和配套实验记录对不上,mAP差了5个点以上。
原因:数据划分本身是随机的,每次划分后训练集和验证集的内容不一样。如果这份数据集已经被划分为固定版本,你重新划分就相当于换了一组数据,结果自然不同。另一个隐藏因素是多卡训练时每张卡的batch分配不同,BN层的统计量会有细微差别。
解决:使用压缩包内已有的划分,不重新划分。训练命令里固定seed=42,如果使用yolov5还需要在启动训练前设置torch.manual_seed(0)。多卡训练时尽量保持batch设置一致,不要随意改batch导致复现困难。
4.4 同时改txt和xml导致标注漂移
现象:发现某个标注框位置不对,手动修改了txt文件,但验证时xml格式的标注还是旧的,导致评测结果为0。
原因:同时维护两套标注格式时,修改只做了一半。这类双格式数据集最忌讳只更新一边,因为后续的v0评测脚本可能读txt,而voc指标计算读xml,两边不一致时结果互相矛盾。
解决:修改标注时以xml为准,改完用脚本重新生成对应的txt,不要手写txt。可以用Ultralytics仓库里的xml2yolo.py脚本批量转换,转换时确认类别索引映射表的顺序和原包一致。每次修改完标注文件,跑一遍前面2.2节里的边界校验脚本,确认两套格式都同步更新。
4.5 中文路径导致dataloader找不到图片
现象:训练启动后立刻报错,提示找不到图片文件,但路径明明存在。仔细看报错信息,路径里的中文目录名变成了乱码。
原因:Windows系统下代码默认编码是GBK,而数据集目录如果包含中文,Ultralytics的dataloader在拼接路径时可能编码不兼容。这种情况在Linux服务器上很少见,主要在Windows本机训练时出现。
解决:把数据集放在纯英文路径下,比如D:/sewer_defect_dataset,不要放在D:/数据集/这种目录下。如果已经放在中文路径里,复制到英文目录重新配置data.yaml的绝对路径即可,不需要改代码。这是最省钱的处理方式。
5. 进阶验证技巧:类别合并、混淆矩阵与迁移微调
训练跑通只是第一步,这类缺陷检测数据集真正的价值在于帮你在真实场景里做决策,「模型能跑」和「模型能用」之间还差几个验证技巧。
第一个技巧是看混淆矩阵找「分不清的类别对」。训练完成后yolov8会在runs/目录下生成confusion_matrix.png,重点关注关节偏移和带扣这一类形态相似的缺陷。下水道管壁的环形结构和破损处的纹理非常接近,模型很容易把关节偏移错判成带扣。如果你的混淆矩阵里这两个类别互相误检率超过20%,建议直接做类别合并——把这两类合成一个「结构破损」类。缺陷检测项目的核心指标是「不漏检」,合并后mAP会上升,漏检率下降,甲方现场验收反而更容易通过。
第二个技巧是用小学习率做迁移微调。如果你手头有自己项目里的少量标注数据,这份数据集可以当预训练语料。做法是先用2364张图训出best.pt,然后加载这个权重在自己的小数据集上继续训练,epochs改成50,lr0从0.005降到0.0005,用freeze=10冻结前10层,只微调检测头。
第三个技巧是验证集做硬负样本补充。把测试集里模型漏检或误检严重的图找出来,单独建一个hard_test目录,每次迭代模型后优先跑这批图,观察改进幅度。这个习惯能快速定位模型在小碎片、低光照场景下的瓶颈,比只看mAP数值有效得多。
这套数据我前后跑了四个版本的YOLO,最大的收获不是精度数字,而是养成了一个习惯:每次拿到新的标注数据集,先花半小时跑一遍标签格式校验和越界检查,再进训练流程。从那以后我每次启动训练前都强制走一遍2.2节里的边界校验脚本,数据本身的坑少了,后面调参的玄学也就少了。希望帮到你。
本文还有配套的精品资源,点击获取