简介:这是一份面向目标检测入门学习与模型训练验证的共享单车检测数据集。样本来源于真实道路与停车区域,共包含一百三十六张图片,标注类别为bicycle,数据集共标注了三百一十八个目标框,图片场景多样,涵盖不同拍摄角度、光照条件和环境背景,有助于提升模型的泛化能力。每张图片均同时提供Pascal VOC格式的xml标注文件与YOLO格式的txt标注文件,可直接用于YOLOv5、YOLOv8、Faster R-CNN等主流框架,省去格式转换步骤;标注使用labelImg工具逐张完成,按矩形框规则准确绘制,类别清晰,适合初学者理解数据集结构,也便于开发者快速扩充到自有项目中。压缩包共四百一十个文件,含一百三十六张jpg原图、一百三十六个xml标注文件及一百三十八个txt文件,整体大小约八十九点九五MB,文件组织清晰,解压后可依据文件名与目录结构快速定位。该资源已有二百零三人浏览学习,适合需要获取带标注共享单车样本的科研人员、竞赛选手和算法爱好者用于练手、毕业设计或项目验证。
1. 共享单车检测数据集VOC+YOLO格式136张1类别.7z:这份数据到底能干什么
先说结论:这是一份只包含1 个类别(共享单车)、136 张标注图片的检测数据集,同时提供VOC 和 YOLO 两种格式。换句话说,它不是为了让你直接训练出一个能上生产的模型,而是为「跑通目标检测全流程」准备的最小可用样本。刚入门 YOLO 的开发者、要做毕设或课程设计的在校生、以及想快速验证检测框架是否安装正确的工程师,拿它来练手最合适。
这类小数据集最大的特点是「麻雀虽小,五脏俱全」:VOC 格式为主的 XML 标注方便人读,YOLO 格式为主的 txt 标注方便模型直接吃,两者并存意味着你不需要为格式转换额外折腾,解压后就能看到标准的数据集目录长什么样。但注意,136 张图在深度学习里属于极小规模,用它训练出来的模型精度只能作为流程验证,不能当作品质标杆。接下来我从数据组织、格式转换、训练验证、常见坑四个维度,把这份数据能怎么用、边界在哪里讲透。
2. VOC 与 YOLO 格式的数据组织:136 张图里到底装了什么
拿到.7z压缩包的第一件事不是急着解压,而是先确认内部目录结构。标准 VOC 格式数据集(VOC2007/2012 风格)通常包含Annotations、JPEGImages、ImageSets三个核心目录;YOLO 格式数据集则通常包含images和labels两个平级目录,外加一个data.yaml配置文件。这份数据既然同时提供两种格式,解压后大概率是两套独立目录并列——要么是VOCdevkit/和YOLODataset/两个根目录,要么是images/、labels/、Annotations/混在同一个根下。
点开Annotations里的 XML 文件,你能看到一个目标检测标注文件的标准骨架:<object>标签里嵌套<name>、<bndbox>,<bndbox>里是xmin、ymin、xmax、ymax四个整数坐标。而 YOLO 格式的同名 txt 文件,每一行只有五个数字:class_id x_center y_center width height,前四个是相对图片宽高的归一化浮点数,最后一个才是类别编号。两类格式的根本差异在于绝对坐标与相对坐标,这也是格式转换时要重点处理的数学关系。
2.1 目录结构自检:解压后如何快速确认数据没损坏
7z 压缩包最好用官方 7-Zip 解压,命令行环境下常见做法是用7z x保留目录结构:
# 安装 7zip 后,解压到当前目录 7z x 共享单车检测数据集VOC+YOLO格式136张1类别.7z -o./bike_dataset # 查看顶层目录结构 find ./bike_dataset -maxdepth 2 -type d | sort-o参数指定输出目录,x命令会保留压缩包内部的相对路径。解压完后,重点核对三个数字:图片数量(136 张)、XML 数量(VOC 侧应有 136 个)、txt 数量(YOLO 侧应有 136 个)。如果 XML 或 txt 少于 136,说明标注文件缺失;如果多于 136,可能是压缩包内包含备份文件或.gitkeep之类的占位文件,不影响使用但要心里有数。
提示:如果不确定 7z 命令是否安装,Linux 下先执行
sudo apt install p7zip-full,macOS 下执行brew install sevenzip。Windows 用户直接装 7-Zip 图形版即可,右键解压就能看到完整目录。
2.2 标注文件核对:用脚本统计类别与坐标边界
光看目录结构还不够,我一般会写一个临时脚本把所有 XML 扫一遍,确认类别名只有一个、坐标没有越界、图片分辨率没有异常。这一步能提前过滤掉「图片是横的但标注按竖的算」这类低级错误:
import os import xml.etree.ElementTree as ET from collections import Counter xml_dir = "./bike_dataset/VOCdevkit/Annotations" class_counter = Counter() error_files = [] for xml_name in os.listdir(xml_dir): if not xml_name.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_name)) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text class_counter[name] += 1 bbox = obj.find("bndbox") xmin = int(float(bbox.find("xmin").text)) ymin = int(float(bbox.find("ymin").text)) xmax = int(float(bbox.find("xmax").text)) ymax = int(float(bbox.find("ymax").text)) if xmin >= xmax or ymin >= ymax or xmin < 0 or ymin < 0: error_files.append(xml_name) print("类别统计:", class_counter) print("疑似坐标异常文件:", error_files)逻辑很简单:遍历 XML,用xml.etree.ElementTree解析<object>标签,统计类别名的出现次数,同时检查 bbox 坐标是否满足「左上角小于右下角」的基本合法性。跑完后如果class_counter里出现多个类别名,说明标注口径不统一;如果error_files非空,说明存在倒挂坐标,这类文件在训练时会导致 loss 异常甚至直接报错。
注意:VOC 格式的坐标是整数像素值,YOLO 格式的坐标是归一化浮点数。校验 YOLO 侧 txt 时,规则变为检查五个数字是否都在合理区间——
class_id应为 0,其余四个数应在 0~1 之间。
3. 把 VOC 转成 YOLO 格式:转换脚本与四个边界坑
虽然压缩包声称同时提供两种格式,但实际项目中经常遇到只有 VOC 格式、需要自己转 YOLO 的情况,或者反过来。更常见的是:你想用 Ultralytics YOLOv8 训练,官方接口只认images+labels的 YOLO 格式。所以即便这份数据已经自带 YOLO 版,我也建议你亲手把 VOC 转一遍——只有自己转过,遇到其他数据集时才不会卡在格式上。
转换脚本的核心逻辑只有两步:从 XML 里读出xmin、ymin、xmax、ymax,再除以图片宽高得到归一化的x_center、y_center、width、height。注意宽高要从 XML 的<size>标签里取,而不是从图片文件里用 PIL 现读,因为后者在数据集很大时会拖慢速度,而且部分数据集的图片尺寸与标注基准可能不一致。
3.1 最小可用的 VOC 转 YOLO 脚本
import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt_path, class_list): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_list: continue class_id = class_list.index(name) bbox = obj.find("bndbox") xmin = int(float(bbox.find("xmin").text)) ymin = int(float(bbox.find("ymin").text)) xmax = int(float(bbox.find("xmax").text)) ymax = int(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(out_txt_path, "w") as f: f.write("\n".join(lines)) class_list = ["bicycle"] xml_dir = "./bike_dataset/VOCdevkit/Annotations" out_dir = "./bike_dataset/YOLODataset/labels" for xml_name in os.listdir(xml_dir): if not xml_name.endswith(".xml"): continue txt_name = xml_name.replace(".xml", ".txt") voc_to_yolo(os.path.join(xml_dir, xml_name), os.path.join(out_dir, txt_name), class_list)几个关键参数说明:class_list的索引顺序决定了class_id的数值,这必须与后续data.yaml里的names列表完全一致;xml.find("size/width")用 XPath 语法直接取嵌套标签,避免多层find的冗余代码;输出坐标保留 6 位小数,足够满足 YOLO 训练要求。跑完脚本后用wc -l抽查几个 txt,确保不是空文件。
3.2 跨平台排查:解码编码与路径分隔符差异
亲测最容易翻车的不是转换算法,而是文件系统层面的琐碎问题。
- 中文路径:如果项目路径里带中文,Windows 下 Python 默认编码可能不是 UTF-8,
os.listdir能列出来但open()时偶发UnicodeDecodeError。解决:在脚本头部加import sys; sys.setrecursionlimit没用,正确做法是给open()显式指定encoding="utf-8"。 - 路径分隔符:Linux 用
/,Windows 用\,脚本里拼接路径时用os.path.join而不是字符串相加,否则换机器跑必炸。 - XML 引号:部分标注工具导出的 XML 属性值带单引号,
ET.parse对这种非标准 XML 会抛ParseError。解决:用open(xml_path, "r", encoding="utf-8").read()读成字符串后先做replace("'", "\"")再交给ET.fromstring。 - 空标注文件:一张图片里如果没有目标,
lines列表为空,写入的 txt 是 0 字节。YOLO 训练允许空标注,但有些数据清洗脚本会误删,记得转换前先建好labels目录,转换后统一检查 0 字节文件数量。
提示:如果压缩包里已经提供了 YOLO 格式,不需要再转一遍。自己写脚本的唯一目的是理解数学关系,顺带验证官方转换是否遗漏了某张图。
4. 用 YOLOv8 训练 136 张单类别数据:最小命令与必调参数
数据格式搞定后,下一步就是训练。当前社区最主流的框架是 Ultralytics YOLOv8,接口封装得很干净,但也正因为干净,很多人忽略了底层参数的实际含义。136 张单类别数据集不是拿来训练大模型的,它的正确用法是:验证环境、跑通流程、观察不同超参对收敛速度的影响。
开始之前需要先建一个data.yaml,内容如下:
path: ./bike_dataset/YOLODataset train: images val: images test: images names: 0: bicycle这个文件是 YOLO 训练的数据集描述文件。path是数据集根目录,train、val是相对path的图片文件夹路径。这里我把val直接指向images,因为 136 张图如果再划分训练集和验证集,每边都太小,看不出任何统计规律,不如先全部拿来验证「数据能不能被模型吃进去」。
4.1 训练命令与几个容易忽略的参数
# 用 yolov8n 预训练权重训练 50 轮 yolo detect train data=./bike_dataset/YOLODataset/data.yaml \ model=yolov8n.pt \ epochs=50 \ imgsz=640 \ batch=8 \ device=0 \ project=./runs/bike_train \ name=exp_bike参数含义:model=yolov8n.pt会先下载 COCO 预训练权重,n 后缀代表 nano 版本,模型最小、显存占用最低,适合验证环境;imgsz=640是 YOLO 系列默认输入尺寸,136 张图的原始分辨率大概率不是 640,但训练时会自动 resize;batch=8在小显存显卡上比较稳妥,如果显存 8G 以上可以调到 16;device=0指定使用第一张 GPU,如果没有 GPU 就删掉这个参数,CPU 训练也能跑,但速度会慢很多。
注意:
yolo detect train会自动把data.yaml里的val分割出来做验证。如果像我一样把val指向images,训练中途打印的val指标(mAP50 等)实际上是「在训练集上做回代验证」,只能说明模型记住了数据,不能说明泛化能力。
4.2 训练完怎么验收:看results.png和weights/
训练结束后,Ultralytics 会在runs/bike_train/exp_bike/下生成一堆文件,其中三个最值得关注:
results.png:训练曲线,横轴是 epoch,纵轴是 loss 和 mAP。如果 loss 曲线一路下滑后趋于平缓,说明流程没问题。weights/best.pt:验证集指标最好的权重,后续推理用这个。weights/last.pt:最后一轮的权重,理论上不应该比best.pt好,但如果你只跑了一两个 epoch,两者差别不大。
快速验证模型是否有效,跑一次推理看结果图最直观:
yolo detect predict model=./runs/bike_train/exp_bike/weights/best.pt \ source=./bike_dataset/YOLODataset/images \ save=True \ conf=0.25conf=0.25表示置信度阈值设为 0.25,低于这个值的检测框会被过滤掉。所有图片的推理结果会保存在runs/detect/predict下,按x键可以切到下一张图(如果用的是有界面的环境)。打开结果图重点看两件事:框有没有把共享单车完整包住、有没有把旁边的行人或电线杆误判成自行车。136 张图上如果肉眼能看到一半以上的正确检测框,这套流程已经算是闭环了。
5. 常见问题排查与避坑:136 张图训练前后最容易踩的 5 个坑
小数据集训练大模型,坑比想象中多。这里整理了我自己反复踩过的五类问题,按「现象 → 原因 → 解决」三要素写清楚,方便你遇到时直接对照。
5.1 解压后图片能看但训练时报AssertionError: Label not found
现象:解压后目录结构完整,images里确实有 136 张 jpg,但运行训练命令后马上报错,提示某个 label 找不到。
原因:data.yaml里的path是相对路径,而训练进程的工作目录不在数据集根目录下。Ultralytics 解析path: ./bike_dataset/YOLODataset时,会把相对路径拼接到当前进程的cwd上,如果cwd是项目代码目录而不是数据集上级目录,路径就对不上。
解决:把data.yaml里的path改成绝对路径,例如/home/yourname/bike_dataset/YOLODataset。或者在运行训练命令前先用cd到data.yaml所在目录,再执行yolo train data=data.yaml。我更推荐前者,因为绝对路径在重启终端后依然有效。
5.2 训练 loss 从第一轮开始就是nan
现象:loss 曲线显示nan,或终端日志里出现nan关键字,训练没有正常下降。
原因:最常见的是imgsz=640时某些图片里目标太小,归一化后 width 和 height 趋近于 0;或者 XML 转 YOLO 时某张图的img_w读成了 0,导致除以零。另一个可能的原因是预训练权重和类别数不匹配——YOLOv8 在 COCO 上预训练时是 80 类,你的数据集是 1 类,Ultralytics 会自动调整最后一层,但如果转换脚本输出的class_id大于 0 而data.yaml里names只有一个类,映射就会错位。
解决:第一步用脚本扫描所有 txt,检查有没有 0 值或大于 1 的数值;第二步把data.yaml的names数量和 txt 里的class_id对齐;第三步把imgsz从 640 降到 320,小目标比例会改善。
5.3 训练正常但验证 mAP 为 0
现象:训练过程中 loss 在下降,但每轮结束打印的mAP50和mAP50-95始终是 0,一轮都没动过。
原因:验证集的标注文件路径不对。Ultralytics 验证时会去val对应的 labels 目录找同名的 txt,如果val: images但labels目录名不是labels而是Labels(首字母大写),Linux 下会由于大小写敏感直接找不到,导致所有预测框都视为误检。
解决:用ls确认目录名精确匹配,labels目录必须放在与images平级的位置,并且在data.yaml里不用指定val_labels字段——Ultralytics 默认自动找labels目录,目录名写错只能自己查。
5.4 136 张图训练出来的模型把路灯杆全认成自行车
现象:推理时输出大量置信度 0.6 以上的检测框,但框里的目标明显是路灯杆、树干、消防栓等竖条状物体。
原因:数据量太小,模型没有见过足够多的「负样本」(也就是没有共享单车的图片)。136 张全是正样本,模型学到的是「竖条状物体 = 自行车」这个粗糙的规则,而不是「两个轮子 + 车架 + 车把」的组合特征。
解决:这不是超参能救回来的,必须补充数据。常见做法是从开放数据集的 COCO 里把bicycle类的图片筛出来,合并进训练集;或者把当前 136 张图随机裁剪出一些「无目标」的负样本,让模型学会输出「无目标」。如果两者都不想做,那就接受这个模型的定位——只能跑通流程,不能做实际检测。
5.5 7z 压缩包解压到一半提示CRC Failed
现象:解压过程中弹出错误,提示某个文件 CRC 校验失败,压缩包无法完整解压。
原因:压缩包在下载或拷贝过程中损坏,或者磁盘空间不足导致写入错误。7z 格式自带 CRC32 校验,解压时发现文件内容与压缩头记录不一致就会报错。
解决:先确认磁盘剩余空间大于压缩包体积的 2 倍,再重新下载一次压缩包,下载完比对文件大小是否一致。如果多次下载都 CRC 失败,大概率是存储介质有问题,换个目录或换个硬盘再试。这里多说一句,136 张图的压缩包本身不大,重传成本很低,不值得花时间修复损坏分卷。
6. 把这 136 张图用出价值:评估、增强与自制数据集的迁移技巧
小数据集最大的价值不是训练出最终模型,而是帮你建立一套「数据质量 → 模型表现」的直觉。我自己习惯的做法是:先不训练,直接对 136 张图做一次「标注质量体检」,然后人为制造一些困难样本,观察模型在什么情况下崩,再决定要不要并入更大的公开数据集。
具体来说,我会先用yolo detect val做一次回代验证,拿best.pt在训练集自己身上测,记录 mAP50。如果这个值连 95% 都不到,说明标注质量有问题或模型结构太弱;如果这个值接近 1,说明模型已经把训练集背下来了。后者的下一步是拷贝一份训练集,把图片随机旋转 30 度、加高斯噪声、调亮度对比度,生成一个「增强版测试集」,再跑一次推理看 mAP 掉到多少。这个衰减幅度就是模型真实泛化能力的第一手估计。
至于数据增强的参数,我一般会避开 YOLO 自带的hsv_h、hsv_s、degrees等超参调参黑洞,而是直接用 OpenCV 写一个小脚本做离线增强:
import cv2 import os import numpy as np src_dir = "./bike_dataset/YOLODataset/images" dst_dir = "./bike_dataset/augmented/images" label_dir = "./bike_dataset/YOLODataset/labels" dst_label_dir = "./bike_dataset/augmented/labels" # 让 x 坐标跟随图片一起水平翻转,标签里每行第一个数字是 class_id,后四个是归一化坐标 # 水平翻转后 x_center 变成 1 - x_center for img_name in os.listdir(src_dir): if not img_name.endswith(".jpg"): continue img = cv2.imread(os.path.join(src_dir, img_name)) flipped = cv2.flip(img, 1) cv2.imwrite(os.path.join(dst_dir, img_name.replace(".jpg", "_flip.jpg")), flipped) txt_name = img_name.replace(".jpg", ".txt") with open(os.path.join(label_dir, txt_name), "r") as f: lines = f.read().strip().split("\n") new_lines = [] for line in lines: parts = line.split() cls_id, x_center, y_center, w, h = parts new_x = 1 - float(x_center) new_lines.append(f"{cls_id} {new_x:.6f} {y_center} {w} {h}") with open(os.path.join(dst_label_dir, txt_name.replace(".txt", "_flip.txt")), "w") as f: f.write("\n".join(new_lines))这段脚本只做了水平翻转,但它演示了增强中最容易出错的一点:图片变了,标签也必须同步变。翻转后x_center变成1 - x_center,y_center和宽高不变。如果你后续要加旋转、裁剪、缩放,对应的坐标变换必须按同一套数学规则同步计算,否则模型学到的就是错位标注,越增强越糟糕。
我早年做巡检项目时,把 300 张图用翻转和 HSV 抖动扩到 2400 张,训练完发现 mAP 反而比不增强低了 3 个点,排查到最后才明白是旋转增强的标签映射算错了一行代码。从那以后我给自己定了个规矩:增强脚本写完先画 10 张图叠加标注框做人工检查,再投入批量生成。这个习惯帮我省下了无数次「训练完才发现数据坏了」的返工时间,建议你把这个验证动作固定成流程,然后再决定是继续扩数据还是合并开放数据集——希望这些方法帮你在自己的数据集上少走几趟弯路。
本文还有配套的精品资源,点击获取