资讯中心

372张VOC+YOLO双格式数据训练目标检测模型实战

📅 2026/9/28 6:21:15
372张VOC+YOLO双格式数据训练目标检测模型实战
简介面向药品识别与目标检测场景一款999感冒灵检测数据集可为计算机视觉学习者、算法工程师提供可直接投入训练的标注数据。资源围绕单一目标类别“999ganmaoling”构建共372张jpg原图每张图片都同时包含VOC格式xml与YOLO格式txt标注总计573个真实标注框便于进行单类别检测模型的训练、验证与迁移学习。包体共1119个文件压缩后约20.88MB其中jpg图像372张、xml标注373个、txt标注374个整体体积适中下载和搬运都很方便。数据集已用labelImg完成标注同时提供两种主流格式免去格式转换环节可直接接入Pascal VOC或YOLO系列训练流程也可按需做数据划分或数据增强。已有248人浏览学习适合想快速上手目标检测实战、补充药品类样本或做数据增强实验的开发者使用。1. 372张的999感冒灵检测数据集怎么做才能训练出能用的目标检测模型感冒药盒子这类外观稳定、拍摄背景却从药店货架换到柜台和批发箱的检测目标372张图像看着不大恰恰是练目标检测流程的合适样本量。这套999感冒灵检测数据集图片内容集中在药品包装同时交付VOC和YOLO两种标注格式等于把最费时的标注环节前置拿到之后可以直接走训练流程。它适合三类人想快速跑通一个货架商品检测链路的新手在VOC与YOLO两个框架之间反复横跳的老手以及先拿小样本验证YOLO训练流程、随后再去扩数据的产品团队。真正决定成败的不是372这个数字而是两套格式的坐标怎么对齐、372张怎么划分和增强、以及训练时那一堆指标到底该信谁。2. 拿到压缩包先别急着跑VOC与YOLO两套格式的坐标体系解压后第一件事不是训练而是确认标注格式长什么样。只要做一次完整流程就会明白目标检测数据集最常见的拦路虎不是模型而是标签和样本不匹配。下面按一个VOCYOLO双格式数据集最常见的目录结构来看。2.1 解压后先做三件事数图片、列目录、看标注类别先解压再把目录结构列出来数一遍。先用这些命令unzip 999感冒灵检测数据集372张VOCYOLO格式.zip find . -maxdepth 2 -type d | sortfind . -name *.xml | wc -l find . -name *.txt | wc -l第一段命令是看压缩包内部组织有的压缩包把VOC和YOLO分两个根目录放有的是混在一起再靠后缀区分。第二段命令统计标注文件数量用于确认XML和txt文件数量是否接近图片数量。如果txt数量比图片少几十个说明部分空样本没有标注文件这在YOLO格式里是正常现象空图片本来就不需要生成txt。下一步需要统计类别名。写一行Python把所有XML里的name字段打出来确认类别是999、999ganmaoling还是带空格的变体。LabelImg、CVAT这些目标检测常用标注工具导出的XML类别名就藏在name节点里不先看清楚后面YAML文件里的names列表很容易写错。2.2 VOC的XML标注绝对像素坐标和object节点VOC格式的标注文件是XML和图片同名放在Annotations目录下。一个典型的XML片段长这样annotation filenameIMG_0001.jpg/filename size width640/width height480/height /size object name999ganmaoling/name bndbox xmin120/xmin ymin96/ymin xmax312/xmax ymax268/ymax /bndbox /object /annotation这段XML表示在640x480的图上框住一个药盒左上角是(120,96)右下角是(312,268)。VOC坐标是绝对像素值给定一张图框的位置不会因为运行设备、部署分辨率变化而改变这也是它适合作为中间交换格式的原因。标注工具导出VOC格式时通常会带上size节点但要注意这个节点有时并不等于图片真实尺寸后面转换时很容易在这里踩坑。2.3 YOLO的txt标注归一化坐标一行五个数YOLO格式的标注文件是txt和图片同名后缀不同。每一行代表一个目标五个数值按顺序是类别ID、中心点x、中心点y、框宽、框高。后四个值全部归一化到0到1之间用坐标值除以图片实际宽度或高度得到。拿上面XML里的框来算一笔账中心点x (120312)/2/640 0.3375中心点y (96268)/2/480 0.3792框宽 (312-120)/640 0.3000框高 (268-96)/480 0.3583对应的txt行0 0.3375 0.3792 0.3000 0.3583这就是同一张图在两套格式下的真实关系。VOC转YOLO没有玄学就是除以图片宽高反过来YOLO转VOC则是乘以宽高再还原边角坐标。核心只有一点分母必须是图片真实尺寸不能用XML里写的size替代。2.4 VOC转YOLO的Python脚本一边转一边把边界问题拦下来很多数据集虽然标了VOCYOLO两种格式但偶尔会碰到只有XML没有txt的情况需要自己转换。下面这段脚本是常见做法可以直接保存成voc2yolo.pyimport os import glob import xml.etree.ElementTree as ET from PIL import Image def voc2yolo(xml_path, img_w, img_h, class_names): 把一个VOC XML文件转换成YOLO txt的一行行标注。 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: # 类别名对不上就跳过避免把未知类别硬塞进训练集 print(f跳过未知类别: {name}) continue class_id class_names.index(name) b obj.find(bndbox) xmin float(b.find(xmin).text) ymin float(b.find(ymin).text) xmax float(b.find(xmax).text) ymax float(b.find(ymax).text) # 核心换算绝对坐标 - 归一化中心点 宽高 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h # 越界框截断到0-1避免训练时出现负坐标或超界坐标 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) lines.append(f{class_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) return lines if __name__ __main__: # 改成解压后实际统计到的类别名 class_names [999ganmaoling] xmls glob.glob(Annotations/*.xml) for xml_path in xmls: # 这里直接读图片真实尺寸别迷信XML里的size标签 img_path xml_path.replace(Annotations, JPEGImages).replace(.xml, .jpg) if not os.path.exists(img_path): print(f缺图跳过: {img_path}) continue img Image.open(img_path) w, h img.size base_name os.path.splitext(os.path.basename(img_path))[0] lines voc2yolo(xml_path, w, h, class_names) if not lines: print(f没有有效标注: {xml_path}) continue out_txt flabels/{base_name}.txt with open(out_txt, w) as f: f.write(\n.join(lines) \n)这段代码里class_names的顺序就是训练YAML里names的顺序改顺序会导致类别ID错位所以转换前先确认这个列表。img_w和img_h直接用PIL读图拿真实尺寸不读XML里size节点这是避免坐标整体偏移最有效的一招。最后那组min(max(...))截断是把越界框拉回0到1区间很多标注边缘目标时会标出负坐标不截断的话YOLO训练容易直接崩。3. 372张图怎么组织才不浪费目录划分、数据自检和增强参数数据集只有372张每一张都不能浪费。如果划分随意比如连续编号的前100张全是同一时间段拍的验证集就会失真。这一章把标准目录、划分脚本和自检脚本一次讲清楚。3.1 建目录并固定划分随机种子训练验证集划分的先决条件YOLOv8训练时默认按images/train和images/val两个目录找图标签目录自动找并排的labels目录。先建标准目录mkdir -p datasets/ganmaoling/images/train datasets/ganmaoling/images/val mkdir -p datasets/ganmaoling/labels/train datasets/ganmaoling/labels/val然后写一个划分脚本。假设解压后的图片全在images/alltxt标注在labels/allimport os import random import shutil all_files [f for f in os.listdir(images/all) if f.endswith((.jpg, .png))] all_files.sort() random.seed(42) # 固定随机种子保证下次跑还能得到同一份划分 random.shuffle(all_files) split int(len(all_files) * 0.8) train_files, val_files all_files[:split], all_files[split:] def move(files, img_dir, lbl_dir): for f in files: base os.path.splitext(f)[0] # 图片和txt标注同名复制时同步搬运避免训练时找不到label shutil.copy(os.path.join(images/all, f), os.path.join(img_dir, f)) shutil.copy(os.path.join(labels/all, base .txt), os.path.join(lbl_dir, base .txt)) move(train_files, datasets/ganmaoling/images/train, datasets/ganmaoling/labels/train) move(val_files, datasets/ganmaoling/images/val, datasets/ganmaoling/labels/val)这段脚本先把文件名读出来、排序再用固定随机种子打乱最后按8:2切分。372张按这个比例训练集约298张验证集约74张。关键点是图片和txt同步复制训练集和验证集之间不能有同名图片混合。如果数据集采集时存在多场景最好先按场景目录分组再从每个场景各自抽20%进验证集这样验证分布更贴近真实环境。3.2 训练前的数据自检把坏图、空标注、越界坐标一次查完数据量小的时候最容易出现一个脏文件拖垮整个训练。训练前跑一遍自检脚本能在几分钟内发现问题省下训练到一半才发现tensor崩溃的时间import os from PIL import Image image_dir datasets/ganmaoling/images/train label_dir datasets/ganmaoling/labels/train for f in sorted(os.listdir(label_dir)): if not f.endswith(.txt): continue base f[:-4] img_path os.path.join(image_dir, base .jpg) # 检查1图片是否存在 if not os.path.exists(img_path): print(f缺图: {base}) continue # 检查2图片能不能正常解码 try: with Image.open(img_path) as im: im.load() except Exception: print(f坏图: {img_path}) # 检查3空标注 with open(os.path.join(label_dir, f)) as fh: lines [ln.strip() for ln in fh if ln.strip()] if not lines: print(f空标注: {base}.txt) # 检查4格式和坐标范围 for ln in lines: parts ln.split() if len(parts) ! 5: print(f格式错误: {base}.txt - {ln}) continue cid int(parts[0]) if cid ! 0: # 只有0一个类别时出现其他ID直接报错 print(f类别越界: {base}.txt - {ln}) _, cx, cy, bw, bh map(float, parts) if max(cx, cy, bw, bh) 1.0: print(f坐标越界: {base}.txt - {ln})自检覆盖四类高频问题图片不存在、图片损坏、标注文件为空、坐标或类别ID越界。其中图片损坏和坐标越界最容易引起训练中BN统计异常后续出现NaN时往回排查十有八九是这里出问题。注意脚本里找图片只认.jpg后缀如果原数据里有PNG图要把base .jpg改成同时尝试.png或者用glob匹配否则会误报缺图。3.3 小数据集的在线增强Mosaic、翻转和HSV各自该开多大372张图硬扛100轮训练必然过拟合在线增强是性价比最高的应对方式。YOLOv8把增强参数直接暴露成训练参数不必离线生成增强图就能在每轮训练时实时变换yolo detect train ... \ mosaic1.0 mixup0.2 \ scale0.5 translate0.2 \ fliplr0.5 flipud0.0 \ hsv_h0.015 hsv_s0.7 hsv_v0.4mosaic1.0表示每张训练图都有一半概率由四张图拼接而成对于想在同一个货架上同时检出多个药盒的场景特别有利。mixup0.2是两张图按比例融合能让模型在目标重叠时依然保持定位稳定但开太大容易让药盒纹理糊成一团。flipud0.0值得一提药盒文字是竖向排列还是横向排列取决于拍摄角度如果数据里不存在倒置摆放的样本就别用上下翻转否则会凭空造出大量不符合真实分布的假样本。HSV三个参数控制颜色扰动药店灯光偏暖偏冷很正常给色相一点偏移空间能提升抗干扰能力但hsv_h超过0.02会让蓝色包装直接变调。在线增强不改变硬盘上的文件只在每次训练迭代时从原图临时变换这样存储成本低也能在每轮训练动态看到不同的组合。如果后面发现验证集和训练集之间光照差异太大优先调HSV而不是急着加离线增强副本。4. 用YOLOv8训练自己的999感冒灵检测从预训练权重到参数调整数据准备好之后接下来走一条最小训练链路。前提是你已经有Python环境机器上有没有GPU都能跑只是速度和batch大小不同。4.1 环境准备与预训练权重不需要从零训练YOLOv8用ultralytics这一个包就能覆盖训练、验证和推理。先建虚拟环境并安装python -m venv .venv source .venv/bin/activate pip install -U ultralytics环境装好之后第一轮训练不需要手动找预训练模型modelyolov8n.pt这个参数会让程序自动下载官方权重。对372张的小数据集从yolov8n起步是理智选择yolov8s或更大的模型容量高小样本下更容易把训练集细节背下来验证集分数反而不升。预训练权重带来的好处是模型已经见过大量自然图像特征药盒的边角、纹理和文字都算常见特征微调几百张就能收敛。如果机器没有GPU且内存紧张训练时把batch降到8imgsz降到480训练时间会增加但流程不变。不要在CPU机器上盲目跑大模型等会训练到一半内存不够返回去再改参数才是真浪费时间。4.2 训练命令与必调参数epochs、imgsz、batch和lr0最简训练命令如下yolo detect train \ modelyolov8n.pt \ datadatasets/ganmaoling/dataset.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.005 \ patience20 \ seed42 \ projectruns/detect name999g这里的dataset.yaml是上一章建好的数据集配置文件内容类似path: datasets/ganmaoling train: images/train val: images/val names: 0: 999ganmaoling几个参数按小样本场景分别说。epochs100看似多实际YOLOv8有早停机制patience20表示验证集指标连续20轮不涨就自动停对372张数据集来说通常40到60轮就到顶了。imgsz640是输入分辨率药盒在图片里占的比例变化很大640能兼顾小目标和显存开销。batch16在显存允许时不用刻意调大预训练微调阶段batch大小对最终精度影响有限。lr00.005比官方默认的0.01小一半原因是小数据集上较大的学习率容易让预训练权重在前几轮就被冲坏尤其当标注框本身不太完美时大学习率会放大噪声。训练输出在runs/detect/999g目录下best.pt是验证集指标最好的权重last.pt是最后一轮权重。4.3 训练中途NaN、BN崩溃或断电resume恢复和results.csv排查训练到一半出现NaN或者train loss飙升是常见翻车现场。原因往往有两种一是数据里有坏图或坐标越界的标注BN统计被异常张量污染二是学习率相对数据量太大梯度在个别batch上爆炸。前者用上一章的自检脚本查出来删掉后者把lr0降到0.001再重训。如果只是断点续训不需要从头再来yolo detect train \ modelruns/detect/999g/weights/last.pt \ datadatasets/ganmaoling/dataset.yaml \ resumeTrue这里resumeTrue会从last.pt继续把训练状态恢复到中断时的epoch而不是重新跑。注意不要用best.pt去resume因为best.pt是验证指标最好的状态但它所在的epoch不一定和训练state匹配续训会丢掉一部分优化器状态。判断训练是否真的出了问题可以看runs/detect/999g/results.csvtail -20 runs/detect/999g/results.csv这个文件每行一个epoch包含train loss、val loss、mAP等指标。如果train loss降但val loss连续20轮不动那就是过拟合信号训练会在patience触发时自动停不必手动中断。5. 372张级别避坑记录格式转换、类别名和过拟合这一章是血泪经验汇总。每个问题都按现象、原因、解决的顺序写处理完这四条基本能排除大部分常见坑。5.1 类别名不一致导致标注被吞现象训练时日志显示的实例数量明显少于预期或者train loss正常但验证mAP始终为0。原因XML里标注的name字段和YAML里的names列表不一致。比如XML里写的是999感冒灵YAML里写的是ganmaoling转换脚本看到未知类别名就直接跳过等于一张图里的目标全被丢弃。更隐蔽的情况是类别名中间有全角空格肉眼看不出来。解决先遍历所有XML统计真实类别名再写YAMLimport xml.etree.ElementTree as ET import glob names set() for xml in glob.glob(Annotations/*.xml): root ET.parse(xml).getroot() for obj in root.findall(object): names.add(obj.find(name).text) print(names)统计结果里出现几个名字YAML的names就写几个类别顺序一旦确定就不要中途改。转换后还要抽查几个txt文件确认里面的类别ID和预期一致。5.2 坐标转换后所有框都偏别信XML的size要读原图现象转换完用可视化工具查看检测框位置整体偏移有的框超出图片边界有的框比目标大一圈。原因转换脚本用了XML里size节点记录的分辨率这个值和图片真实像素不一致。常见于数据集被resize过但XML没同步更新或者标注工具自动填充了错误参数。宽度差几个像素时看不太出来一旦宽高差到几十像素所有框都会整体跑偏验证集mAP直接被拖下去。解决转换脚本里用PIL或OpenCV直接读图片真实尺寸作为除数。转换完成之后随手写一行可视化代码把标注框画回原图上肉眼抽查十张。框和药盒边缘偏差超过两个像素就要回头检查图片加载路径和标注文件的对应关系。5.3 train loss走低但val mAP上不去小样本过拟合现象训练到第40轮train loss一路降到0.5以下但验证集mAP卡在60%左右一直不涨甚至后续轮次还掉几个点。原因372张对YOLOv8n来说依然偏少模型在反复看训练集的过程中记住了药盒的具体摆放和背景纹理而不是泛化出在任意货架角度都能找到药盒的能力。train loss低只代表模型对训练集拟合好验证指标才能反映真实泛化水平。解决先确认早停参数patience20已经生效不要硬跑满100轮。然后增强力度往上调尤其mosaic和mixup这两个参数对小样本最有效。再不行就换更小的模型从yolov8n换到yolov8n其实已经是nano不要再降如果用的是yolov8s先退回n版本。最后一条路是补充数据比调参更直接有效。5.4 验证指标还行但真图漏检阈值和验证分布现象验证集mAP有75%但把训练好的模型放到一张自然光下的药店货架图上漏检了一半药盒。原因验证集划分太干净随机打乱时把同一场景下拍摄角度几乎一致的图片同时放进了训练集和验证集模型在验证集上高分一遇到真实场景的货架透视角度、反光和遮挡就熄火。另一层原因是推理阈值设太高验证时用的是置信度0.5真实检测时还把阈值保留在0.5以上很多低置信度的真目标被过滤掉。解决推理阶段先用conf0.25跑一批真实图片看看漏检目标落在哪个置信度区间。如果漏检目标大多是0.3到0.5之间就把阈值降到0.25同时用iou0.45做NMS避免同一个药盒出多个框。验证集划分改成按拍摄场景分组后抽取才能让mAP有参考意义。6. 验证模型并反哺数据集372张只是一个起点模型训练完成后最后一段路是拿真实场景图片验证并筛选错误样本。先把best.pt放到一批没参与训练的真实图片上跑一遍yolo detect predict \ modelruns/detect/999g/weights/best.pt \ sourcereal_test/ \ conf0.25 iou0.45 \ saveTrue输出图片会标注框、置信度和类别名。先挑那些低置信度的漏检目标看再挑误检目标看。如果漏检主要发生在药盒重叠、盒面反光或包装有塑料袋遮挡时不要急着调参把这些图片找出来补标加进训练集重训一轮。常见做法是每轮增加50到100张新增样本连续三轮增量训练之后模型对这个场景的稳定性通常会有明显改善。我自己在这个方向吃过亏第一次迭代只看验证集mAP感觉很稳结果部署到真实柜台照片上漏检率接近三成。后来养成的习惯是每个项目都保留一批固定的真实场景测试图不参与训练也不参与验证每轮训练后跑一遍通过错误图片决定下一轮补数据方向。对372张这种规模的数据集来说增量数据永远比花时间调增强参数更值得投入。希望帮到你。本文还有配套的精品资源点击获取

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案