news 2026/10/5 12:17:44

起重机数据集YOLO训练:VOC转YOLO格式与迁移学习实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
起重机数据集YOLO训练:VOC转YOLO格式与迁移学习实战

简介:起重机目标检测数据集以YOLO与VOC两种主流格式整理,共包含689张清晰起重机图片及对应标注文件,面向计算机视觉入门与进阶学习者,可用于目标检测模型的训练、验证与效果对比。压缩包内文件总数约2000个,主要类型为txt标注、xml标注和jpg图片,整体大小34.35MB;其中XML对应VOC格式,TXT可直接适配YOLO系列框架,目录按JPEGImages、Annotations、labels三类划分,便于快速读取。数据集唯一标签为Mobile_crane,共标出764个矩形框,图片未做增强,能较真实反映实际场景,适合用以练习起重机识别模型构建、参数调试及格式转换。目前已有170人学习下载,适合需要规范化起重机械检测数据的开发者与教学场景使用。

1. 起重机目标检测数据集:689张图片到底能不能直接用

这个数据集的压缩包名字很直白:起重机数据集,YOLO+VOC双格式,689张图片。我拆过不少标注数据包,说实话,689张不算多,但它是带完整矩形框标注的——764个Mobile_crane框,意味着平均每张图1.1个目标,属于稀疏检测场景。对于做工业监控、港口调度、工程车辆识别的朋友来说,这种带准确标注的垂直场景数据比通用数据集更难得,因为它不用你自己去筛图、清洗和重新标注。适合谁用?想快速验证YOLO训练流程的新手、正在做工程车辆检测但还没凑够私有数据的工程师、需要一份干净标注做迁移学习起点的研究者。把这份资源当作训练的"启动燃料"是合理的,但指望689张图训出上线级模型不现实。

2. VOC与YOLO双格式的目录结构:先看懂标注再谈训练

2.1 JPEGImages、Annotations、labels 三件套的对应关系

压缩包解压后是三个文件夹,命名对用过VOC的人非常友好:JPEGImages放图片、Annotations放XML标注、labels放YOLO格式的TXT标注。这套结构是从Pascal VOC迁移过来的目录习惯。

关键是对应关系。689张jpg、689个xml、689个txt,文件名主体是同一个,比如sl_images582.jpg对应sl_images582.xml和sl_images582.txt。这意味着你在做任何训练之前,第一步应该核对文件名一致性和数量一致性。

我在拿到这类资源时,第一件事不是直接喂给YOLO,而是先跑一个统计脚本来核对三件套数量是否真的对得上,以及每张图的标注框是否为空。

#!/bin/bash # 核对三件套数量与文件名对应 IMGDIR=JPEGImages XMLDIR=Annotations TXTDIR=labels echo "图片数量: $(ls $IMGDIR | wc -l)" echo "XML数量: $(ls $XMLDIR | wc -l)" echo "TXT数量: $(ls $TXTDIR | wc -l)" # 检查同名文件是否存在 for img in $IMGDIR/*.jpg; do base=$(basename "$img" .jpg) if [ ! -f "$XMLDIR/$base.xml" ]; then echo "缺少XML: $base" fi if [ ! -f "$TXTDIR/$base.txt" ]; then echo "缺少TXT: $base" fi done

这段脚本做的事情就是遍历所有图片,检查对应的XML和TXT文件是否存在,缺哪个就把哪个文件名打出来。先把这一步做了,避免训练到一半才发现有些图片没有任何标注。

严格来说,文件名主体一致是VOC和YOLO生态的共同假设。Ultralytics的YOLO训练流程会自动读取图片路径,然后找同名的txt文件,如果文件名对不上,数据加载器会直接跳过那张图,等于你的训练集悄悄少了几张图,val指标会变得不可解释。

2.2 XML里的关键节点:从 object 到 bounding box 的坐标语义

打开任意一个XML文件,会看到Pascal VOC标准结构。关键节点是<object>,里面包含<name>和<bndbox>。

<name>是类别名,这个数据集里固定是Mobile_crane,只有一个类别;<bndbox>下有四个子节点:xmin、ymin、xmax、ymax,分别表示矩形框左上角和右下角的像素坐标。

<annotation> <folder>JPEGImages</folder> <filename>sl_images582.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>Mobile_crane</name> <bndbox> <xmin>532</xmin> <ymin>287</ymin> <xmax>1241</xmax> <ymax>866</ymax> </bndbox> </object> </annotation>

<size>节点里的width和height最好和实际图片分辨率核对一下,我遇到过标注尺寸和原图分辨率不一致的数据包,这种情况在转YOLO格式时会导致框全部偏移。值得注意的一点是:这里面的坐标是绝对像素坐标,单位是像素,不是归一化值。你在写转换脚本时,xmin/ymin这些值直接除以图片宽度/高度即可得到YOLO归一化坐标。

2.3 YOLO 格式的 txt 是怎么从 XML 转出来的(含转换脚本)

TXT文件的格式是YOLO生态的标准写法:每行代表一个目标框,五个值按空格分隔,依次是class_id x_center y_center width height。class_id从0开始,这个数据集只有一个类,所以所有行的class_id都是0;四个坐标值全部归一化到[0,1]区间。

由于资源已经同时提供了TXT文件,你可以直接拿一只文件来肉眼检查,比如看某个TXT里坐标是否越界。如果你想从XML重新生成TXT(比如想统一做数据清洗),常见做法是用一小段Python脚本完成。

import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, txt_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_names: continue class_id = class_names.index(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) x_center = (xmin + xmax) / 2 / img_w y_center = (ymin + ymax) / 2 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") with open(txt_path, 'w') as f: f.write('\n'.join(lines)) class_names = ['Mobile_crane'] xml_to_yolo('Annotations/sl_images582.xml', 'labels/sl_images582.txt', class_names)

脚本核心逻辑不算复杂:解析XML里的size拿到图像宽高,遍历object节点,把xmin、ymin、xmax、ymax转成中心点加宽高的归一化四元组。注意class_names列表的顺序决定了class_id,如果你后续要加第二类数据,务必保持同一个列表顺序,否则标注类别会全部错位。

这里有个坐标语义的细节:VOC的bbox四个值都是像素级整数,而YOLO的四元组是浮点归一化。中间任何一步用错了除数,比如用width去归一化y坐标,框会歪得离谱。我自己一般会写一个反向脚本,把TXT再转回XML,用OpenCV画框对比原图,视觉验证一遍坐标是否合理。

3. 在 YOLOv8 上把 689 张图跑起来:数据配置与训练参数

3.1 数据集目录整理与 yaml 配置

拿到数据集后,直接把它放到YOLOv8项目里用,目录结构需要稍微调整。YOLOv8默认的布局是images/train、images/val、labels/train、labels/val这样的结构,但当前数据集只有一个总体文件夹,所以需要自己划分训练集和验证集。

常见做法是用脚本做比例划分。689张图是小规模数据,我一般按 8:1:1 划成 train/val/test,但test集可以暂时不参与调参,留到最后验证用。下面这段脚本把图片和对应的标注同步移动,确保图片train和标注train严格对应。

import os import shutil import random random.seed(42) base_dir = 'crane_dataset' img_files = sorted(os.listdir(os.path.join(base_dir, 'JPEGImages'))) txt_files = sorted(os.listdir(os.path.join(base_dir, 'labels'))) assert len(img_files) == len(txt_files) == 689 random.shuffle(img_files) train_num = int(len(img_files) * 0.8) val_num = int(len(img_files) * 0.1) splits = { 'train': img_files[:train_num], 'val': img_files[train_num:train_num + val_num], 'test': img_files[train_num + val_num:] } for split, files in splits.items(): os.makedirs(f'data/{split}/images', exist_ok=True) os.makedirs(f'data/{split}/labels', exist_ok=True) for img in files: base = os.path.splitext(img)[0] shutil.copy(os.path.join(base_dir, 'JPEGImages', img), f'data/{split}/images/{img}') shutil.copy(os.path.join(base_dir, 'labels', base + '.txt'), f'data/{split}/labels/{base}.txt')

划分完目录之后,训练前的最后一步是写YOLOv8的数据配置yaml。这里面的路径建议写相对路径,配合path字段指向项目根目录,这样换机器跑不需要改动配置。

# crane.yaml path: ./data train: train/images val: val/images test: test/images nc: 1 names: 0: 'Mobile_crane'

yaml里的nc是类别数,这个数据集只有一类,所以是1;names要和TXT里的class_id顺序完全一致。这里有个容易翻车的点:names是一个字典,key必须是整数0,顺序不能乱。如果你的标注txt里类别id是0,但yaml里names的0对应名字写错了,训练能跑,但推理输出和可视化class名字会全错。

3.2 训练命令与关键超参数说明

目录和yaml都就绪后,在YOLOv8环境下运行训练命令:

yolo train data=crane.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0

我用的是yolov8n(nano版)作为起始权重,原因很简单:数据集只有689张图,模型太大反而容易过拟合,nano参数量小,训练速度快,先跑通流程再说精度。上面的命令行里epochs=100对这个小数据集来说偏多,通常跑到50轮左右val损失就已经到底了;batch=16在显存足够的情况下可以往大调,小数据集每个batch的样本多样性有限,batch大一点会更早稳定。

参数理解上要区分两个概念:训练轮次epochs和早停机制。YOLOv8默认开着早停,patience默认是100,实际上如果val指标连续多轮不涨,训练会自动中断。对689张图这种规模的数据,50-80轮通常是甜点区间,太多轮只会让模型在训练集上背答案。

3.3 损失函数与评价指标怎么对应到这个小数据集

YOLOv8的损失函数由三部分组成:box损失(CIoU)、分类损失(BCE)、DFL损失(Distribution Focal Loss)。小数据集下这三个损失的比重表现和COCO大训练集有差异。

我训练完后会先看损失曲线:如果box_loss和cls_loss都在稳步下降,说明标注质量没问题;如果loss震荡剧烈,优先怀疑标注里混入了异常框。项目里给的描述说标签是矩形框,764个框分布在689张图中,意味着有部分图片是空图(没有目标)或者一张图多个目标。处理空图时,对应的TXT文件是空文件,这是合法的,不需要删除,但要注意空图片在训练时只参与背景学习。

关于评价指标,小数据集上的mAP往往虚高得厉害。因为验证集小,任何一次随机初始化差异都可能让mAP波动5个点以上。我的习惯是看mAP@0.5的同时,强制关注mAP@0.5:0.95,后者能真实反映框的定位精度。如果两者差距很大,说明框偏得厉害但大致位置对。

4. 标注质量核查脚本:用 Python 验证 764 个框有没有问题

4.1 统计脚本:图片数、XML 数、TXT 数逐项核对

数据集简介里写了689张图、689份xml、689份txt,总框数764。我拿到数据后不会直接信这句话,先跑脚本把框数统计出来,和764这个基准值对齐。这是因为标注数据集在压缩、上传、下载的过程中,偶尔会有文件缺失,文件名乱码也会让某些标注无法被正确解析。

import os import glob def count_labels(txt_dir): total_boxes = 0 class_count = {} empty_files = [] for txt_path in glob.glob(os.path.join(txt_dir, '*.txt')): with open(txt_path, 'r') as f: lines = [line.strip() for line in f if line.strip()] boxes = len(lines) total_boxes += boxes if boxes == 0: empty_files.append(os.path.basename(txt_path)) for line in lines: parts = line.split() cls_id = int(parts[0]) class_count[cls_id] = class_count.get(cls_id, 0) + 1 return total_boxes, class_count, empty_files total, class_count, empty_files = count_labels('data/labels') print(f"总框数: {total}") print(f"类别分布: {class_count}") print(f"空标注文件数: {len(empty_files)}") print(f"空文件列表: {empty_files[:10]}")

这段脚本逐行读取每个TXT文件,统计总框数和每个类别id的框数,同时把空标注文件单独列出来。如果你的统计结果和简介里写的764有出入,就要警惕数据在传输过程中出了问题,而不是盲目开训。

4.2 坐标越界与空标注检查

坐标越界是标注数据里最常见也最隐蔽的问题。YOLO格式的归一化坐标理论上应该在[0,1]区间内,但标注工具偶尔会把框的一边拉出图像边缘,导致x_center + width/2 > 1这种越界情况。这类框在训练时会让损失函数异常波动。

import glob import os def check_out_of_bounds(txt_dir, eps=0.001): problems = [] for txt_path in glob.glob(os.path.join(txt_dir, '*.txt')): base = os.path.basename(txt_path) with open(txt_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: problems.append(f"{base}: 字段数量异常 {parts}") continue _, cx, cy, w, h = map(float, parts) if w <= 0 or h <= 0: problems.append(f"{base}: 宽或高非正数") if cx < -eps or cx > 1 + eps or cy < -eps or cy > 1 + eps: problems.append(f"{base}: 中心点越界 cx={cx}, cy={cy}") if w > 1 + eps or h > 1 + eps: problems.append(f"{base}: 宽高超界 w={w}, h={h}") return problems out = check_out_of_bounds('data/labels') for p in out: print(p)

我一般用宽松的上限1.001来检查,因为个别标注工具在导出时会有极小浮点舍入误差。如果越界数值达到了1.5这种程度,那肯定不是舍入问题,而是转换脚本写错了,比如没除以图像宽度就直接拼接。

4.3 数据增强与类别不平衡的现实考量

这个数据集只有一个类,不存在类别不平衡问题,但689张只有764个框,平均每张图只有1.1个目标,大量图片可能只有单目标甚至空背景。这种分布会让模型训练时对小目标、多目标场景泛化明显不足。

数据增强是缓解小数据集过拟合的主要手段。YOLOv8自带一系列增强参数:hsv_h、hsv_s、hsv_v控制颜色扰动,translate控制平移,scale控制缩放,fliplr控制水平翻转。对起重机这种目标,颜色扰动和安全别太激进。

我的建议预算:fliplr=0.5可以放心开,起重机左右对称翻转不改变语义;scale=0.3让模型适应目标尺度变化;translate=0.1不能太大,因为很多起重机车体在图中占的比例不小,平移多了容易让目标主体出画。如果训练时发现loss曲线有周期性尖峰,多半是增强参数太激进,把标注框连同目标一起移出了图像边界。

5. 常见问题与避坑指南:小数据集训练的三个深坑

5.1 坑一:压缩包解压后中文路径和空格导致训练报错

现象:数据集解压到某个带中文或空格的目录后,yolo train命令刚启动就报文件找不到,或者报RuntimeError: Dataset not found。

原因:Ultralytics在处理路径时对中文路径和空格支持不完整,特别是一些Windows环境下从zip解压出来的路径带着中文字符,数据加载器在拼接路径时出现编码错乱。

解决:把数据集全部放到纯英文路径下,目录名不要带空格,例如D:\datasets\crane_dataset。压缩包内文件夹名如果是中文,解压后先整体重命名成英文再做划分。我在处理zip文件时习惯先看一遍压缩包内根目录,确认没有中文目录结构再解压。

5.2 坑二:类别名不一致导致训练静默出错

现象:训练能跑,loss也能降,但最终验算时发现模型把所有目标都当成背景,mAP接近0。

原因:XML里<name>是Mobile_crane,但转换脚本里的类别列表写成了mobile_crane或mobile-crane,大小写或连字符不一致导致object被直接跳过,生成的TXT全是空标注。

解决:训练前一定要跑一遍第2.1节的核验脚本,再单独检查TXT文件的非空比例。经验法则:764个框除以689张图,平均每图1.1个框,如果统计出来的非空TXT比例低于90%,大概率有标注没被正确解析。我从那以后每次处理新数据集都会先打印一次类别分布,跟数据集说明里给出的框数对齐再开训。

5.3 坑三:小数据集过拟合严重,val指标虚高

现象:训练到后期train loss持续下降,val loss却在某轮之后开始反弹,mAP曲线看起来特别漂亮但实际推理效果很差。

原因:689张图的验证集规模太小,比如10%就是69张,模型在训练集上记住了一些背景纹理和固定构图,验证集刚好在分布偏差范围内,导致指标虚高。

解决:三个手段一起用。第一,降低epoch数,我前面建议过50-80轮;第二,开启更积极的数据增强,hsv_h=0.015、scale=0.5、fliplr=0.5这类配置可以让小数据集变得更"大";第三,用K折交叉验证代替单次train/val划分,拿5折的平均mAP作为可信指标。K折在小数据集上能从数据利用率上缓解val虚高,代价是训练时间翻几倍,但689张图的数据量,5轮训练也就几十分钟的事。

5.4 坑四:VOC与YOLO坐标换算出现偏移

现象:训练完用yolo predict测试,检测框和实际目标错位半个身位,或者框明显偏向目标的一侧。

原因:XML转TXT时,有人把x_center直接写成了(xmin + xmax) / 2没有除以图像宽度,也有的人把xmin和ymin直接当成了归一化坐标,导致整体偏移。

解决:写一个可视化脚本,用OpenCV把TXT里的bbox画回图片,抽查20张图人眼确认。

import cv2 def draw_yolo_boxes(img_path, txt_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(txt_path, 'r') as f: for line in f: cls_id, cx, cy, bw, bh = map(float, line.strip().split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f'cls{int(cls_id)}', (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imshow('check', img) cv2.waitKey(0)

这段脚本把归一化坐标乘回图片宽高,画框展示。坐标语义如果错得离谱,这里一眼就能看出来。这个操作也被我列为数据集验收的固定动作之一,不再跳过。

6. 用迁移学习把小数据集训练到可用的最后一步

689张图训出来模型,直接用于生产很可能翻车,但配合迁移学习可以把它推到接近可用的程度。预训练权重里已经包含了丰富的通用特征,尤其是COCO里的卡车、公交车等类别和起重机在形状上有部分共享特征。我在用这个数据集时,默认从yolov8n.pt或yolov8s.pt开始,而不是随机初始化。固定主干层数或者只微调检测头的做法,在这个数据规模下值得优先尝试。

训练完成后的验证动作也有讲究,不只看mAP图。我习惯在抽30张未见过的起重机图片跑一遍推理,观察框的稳定性:同一个目标在不同帧之间的框是否抖动明显,远距离小目标的框是否经常丢失。配合第4.2节的绘制脚本把结果叠加到原图上,人眼判断比任何指标都直观。

如果发现数据量确实不足导致泛化不够,下一步是从生产环境里自己补标注,常见做法是用当前模型做半自动预标注,把置信度高的框直接采纳,置信度低的框人工修正,这样补几百张标注只需要几个小时。这批689张标注数据恰好可以作为半自动标注的种子模型。

想充分利用这个数据集,我的习惯是把它作为垂直场景的底座,而不是终极训练数据。从yolov8n开始跑通流程后,再用yolov8s或m型号在大一点的私有数据上续训,这样迭代经济性最高。从那以后我每次做小数据集训练,都强制走一遍框数核对、坐标可视化、迁移学习三步,再匆忙也会把这套流程跑完。这批689张起重机的标注框质量在同类资源里算规整的,正适合拿来当这个过程的练手数据。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 12:16:18

SpringBoot+Vue 心理疏导防控微信小程序的设计与实现

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 1. 引言 随着社会节奏加快与生活压力增大&#xff0c;心理健康问题日益受到关注。传统心理疏导服务存在资源分布不均、预约流程繁琐、隐私顾虑较高等痛点。本文设计并实…

作者头像 李华
网站建设 2026/10/5 12:15:12

从需求拆解到上线:多AI协作Agent工作流自建Linkly AI链接工具

从需求拆解到上线&#xff1a;用多AI协作Agent工作流自建了一个"Linkly AI"链接工具前阵子一直泡在各种AI Agent工作流里&#xff0c;突然冒出个念头&#xff1a;与其天天用别人的SaaS链接工具&#xff0c;不如自己拿AI全流程搭一个"Linkly AI"出来——一个…

作者头像 李华
网站建设 2026/10/5 12:12:58

AI代理大战:本地模型与代理助手的实战指南

1. 这场“代理大战”到底在打什么1.1 从“聊天机器人”到“数字员工”&#xff1a;AI助手的关键一跃过去两年我接触了大量AI产品&#xff0c;从最早的新鲜感到现在的日常依赖&#xff0c;最大的感受是&#xff1a;个人AI助手已经不再是单纯的“聊天机器人”。以前问一句“帮我写…

作者头像 李华
网站建设 2026/10/5 12:12:57

个人AI代理进阶指南:从云端到本地模型的自建助手实践

1. 个人AI助手代理大战&#xff0c;到底在抢什么 这两年AI圈最热闹的赛道之一&#xff0c;就是个人AI助手代理&#xff08;AI Agent&#xff09;。从“能聊天的机器人”到“能帮你干活的下属”&#xff0c;这个转变看着只是一小步&#xff0c;背后却是整个AI应用形态的大洗牌。…

作者头像 李华
网站建设 2026/10/5 12:11:00

PyTorch图像去雨实战:从数据处理到模型训练的完整教程

1. 图像去雨到底在解决什么问题先聊点实际的。图像去雨&#xff0c;就是给定一张带雨纹的图&#xff0c;让模型学会把这层“干扰”剥掉&#xff0c;恢复出干净背景。这个任务听起来简单&#xff0c;但做起来比想象中麻烦得多——雨不是均匀撒在画面上的&#xff0c;它有方向、有…

作者头像 李华
网站建设 2026/10/5 12:06:41

SUMO路网XML构建原理与工业级实践指南

1. 为什么非得用XML写路网&#xff1f;——从“点选拖拽”到“精准控制”的思维切换 你打开SUMO的netedit&#xff0c;拖几条路、拉几个交叉口、点几下鼠标&#xff0c;5分钟就能画出一个像模像样的十字路口。这很爽&#xff0c;对吧&#xff1f;但当你需要建一个包含237个信号…

作者头像 李华