news 2026/10/5 9:28:50

959张药品盒检测数据集实战:VOC与YOLO格式解析及小样本训练避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
959张药品盒检测数据集实战:VOC与YOLO格式解析及小样本训练避坑指南

简介:这是一份面向目标检测初学者与药品识别应用开发者的感冒药品分类检测数据集,可用于训练和验证药品包装检测模型,适用于零售药房自动盘点、智能货架识别等场景。压缩包共约2000个文件,以960个VOC格式xml标注、962个YOLO格式txt标注和960张jpg图片为主,xml与txt分别对应两种主流标注格式,图片为原始样本,整体约44.17MB,解压后可直接接入YOLO或VOC训练流程。数据集共标注4类,包括999ganmaoling、banlangen、buluofen及drugs,总框数1365,其中buluofen与999ganmaoling样本较多,drugs仅1框,类别分布差异明显,使用labelImg按矩形框规则标注。目前已有395人学习下载,读者可借此快速搭建药品检测基线、验证数据增强与类别不平衡处理策略,并对照标注文件理解VOC与YOLO格式的转换关系。

1. 959 张药品盒检测数据集:从文件名到标注框,这份资源到底能跑什么

拿到一个目标检测数据集,第一件事不是急着train.py,而是先搞清楚它能不能对上你的业务。这份「感冒药品分类检测数据集」给的是 959 张 jpg 图片,配套 Pascal VOC 的 xml 和 YOLO 的 txt 各 960 份,标注工具是 labelImg,类别四个:999ganmaoling、banlangen、buluofen、drugs。框数分布很不均——buluofen657 框、999ganmaoling573 框、banlangen134 框,而drugs只有 1 框。这个分布本身就是信号:前三类是真实可训的药品盒目标,drugs更像一个占位或兜底类,训练时基本可以忽略。它适合谁?做零售货架盘点、药房库存视觉识别、药品分拣机器人视觉模块的团队,拿它当冷启动数据最合适;想直接上产线的,得先接受它只有几百张、单类样本偏少的现实。下面按「先看懂格式 → 再跑通训练 → 再避坑 → 最后做增强」的顺序拆。

2. VOC 与 YOLO 双格式拆解:xml 和 txt 到底怎么对应

2.1 两种格式的字段映射关系

VOC 的 xml 是「一图一文件」,根节点<annotation>下挂<filename>、<size>(宽高通道)、以及若干个<object>,每个 object 里有<name>和<bndbox>(xmin/ymin/xmax/ymax,绝对像素坐标)。YOLO 的 txt 是「一图一文件」,每行一个目标,格式是class_id cx cy w h,全部归一化到 0~1。两者描述的是同一批框,只是坐标系和类别表达方式不同。这份数据集两种都给全了,意味着你可以直接喂给 YOLO 系列,也可以先用 VOC 工具链做可视化核对。

维度VOC xmlYOLO txt
坐标绝对像素 xmin/ymin/xmax/ymax归一化 cx/cy/w/h
类别字符串 name整数 class_id
一图多目标多个 object 节点多行
常用工具labelImg、labelmelabelImg、多数训练框架

2.2 类别名到 id 的映射必须自己定死

YOLO 的 txt 里只有数字,没有类别名。这份数据集的四个类999ganmaoling、banlangen、buluofen、drugs对应哪个 id,取决于生成 txt 时的顺序。如果你直接拿 txt 训练却不核对映射,模型学出来的「0 号类」可能根本不是你以为的感冒灵。常见做法是写一个脚本,从 xml 里反查每个 id 对应的 name,生成一份classes.txt或data.yaml。

import os import xml.etree.ElementTree as ET from collections import defaultdict xml_dir = "annotations" # VOC xml 目录 id2name = defaultdict(set) for f in os.listdir(xml_dir): if not f.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, f)) root = tree.getroot() for obj in root.iter("object"): name = obj.find("name").text.strip() # 这里假设 txt 的 id 顺序与 xml 中类别首次出现顺序一致 # 实际应以生成 txt 的脚本为准,此处仅做反查校验 id2name[name].add(f) for name, files in id2name.items(): print(f"{name}: {len(files)} 张图包含该类")

这段脚本不直接产出 id,而是统计每个类别出现在多少张图里。跑完你会看到drugs只出现在 1 张图,banlangen出现在约 130 张左右,和框数对得上。逻辑说明:ET.parse逐文件解析,root.iter("object")遍历所有目标框,name.text取类别字符串。参数上唯一要改的是xml_dir路径。跑完这一步,你心里对类别分布就有底了,再决定要不要把drugs合并或剔除。

2.3 用一条命令把 VOC 转成 YOLO 做交叉验证

虽然数据集已经给了 YOLO txt,但自己转一遍能验证两份标注是否一致。常见做法是用voc2yolo类脚本,核心是把绝对坐标归一化:

import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, classes): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text.strip() if name not in classes: continue cls_id = classes.index(name) box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") return lines classes = ["999ganmaoling", "banlangen", "buluofen", "drugs"] print(voc_to_yolo("999ganmaoling_xyxr_334.xml", classes))

逻辑说明:先读图片宽高,再把每个框的中心点和宽高除以宽高做归一化,保留 6 位小数足够。参数说明:classes列表的顺序就是最终 id 顺序,必须和训练时的data.yaml完全一致,否则类别全乱。转完拿自己生成的 txt 和数据集自带的 txt 做 diff,如果行数和数值基本一致,说明标注可信;如果差很多,优先信 xml,因为 xml 里类别名是明文,不容易被 id 映射坑到。

3. 从 959 张图到可训练集:划分、配置与首轮训练

3.1 训练集验证集划分要按类别分层

959 张图直接随机 8:2 划分,drugs那一张大概率进验证集或训练集之一,导致某一侧完全没有该类。更稳的做法是按类别分层抽样,保证每个 split 里前三类都有。常见做法是用sklearn.model_selection.train_test_split的stratify参数,但多标签场景要自己构造分层键。简单点:以「图片包含的主要类别」为键做分层。

import os import random from collections import defaultdict img_dir = "images" all_imgs = [f for f in os.listdir(img_dir) if f.endswith(".jpg")] # 按文件名前缀粗分,实际应按标注内容分层 buckets = defaultdict(list) for f in all_imgs: if f.startswith("999ganmaoling"): buckets["999ganmaoling"].append(f) elif f.startswith("banlangen"): buckets["banlangen"].append(f) elif f.startswith("buluofen"): buckets["buluofen"].append(f) else: buckets["other"].append(f) train, val = [], [] for k, files in buckets.items(): random.shuffle(files) split = int(len(files) * 0.8) train += files[:split] val += files[split:] print(f"train: {len(train)}, val: {len(val)}")

逻辑说明:按文件名前缀分桶,是因为这份数据集的命名里带了类别线索(如buluofen_xyxr_390.jpg)。参数说明:0.8是训练比例,样本少的类可以调到0.9让训练集多留一点。跑完把文件名写进train.txt和val.txt,YOLO 训练时直接读这两个列表。

3.2 data.yaml 的四个字段别写错

YOLO 系列训练入口通常吃一个 yaml,字段就四个关键项:

path: ./dataset train: train.txt val: val.txt nc: 4 names: ["999ganmaoling", "banlangen", "buluofen", "drugs"]

逻辑说明:path是数据集根目录,train/val是相对路径的列表文件,nc是类别数,names顺序必须和 txt 里的 id 一致。参数说明:如果你决定剔除drugs,要把nc改成 3,names删掉最后一项,同时所有 txt 里 id 为 3 的行要么删要么重映射,否则训练时索引越界直接报错。这一步是新手翻车高发区,改类别数不改 txt,报错信息往往只给一个 index 越界,看不出根因。

3.3 首轮训练命令与关键超参

以常见的 YOLO 训练脚本为例,首轮建议小 epoch 跑通再放大:

python train.py \ --data data.yaml \ --img 640 \ --batch 16 \ --epochs 100 \ --weights yolov8n.pt \ --device 0

逻辑说明:--img 640是输入分辨率,药品盒目标通常不大,640 够用;--batch 16在 8G 显存上比较稳;--epochs 100是首轮试探,看 loss 曲线再决定要不要加到 300;--weights用预训练权重能明显加快收敛。参数说明:如果显存不够,把 batch 降到 8 并开--amp;如果banlangen这类小样本类 recall 一直上不去,优先考虑过采样或 mosaic 增强,而不是盲目加 epoch。跑完看results.csv里的mAP50和每类instances,drugs那 1 个框基本不会贡献有效指标,别被它拉低整体判断。

4. 避坑与排查:这份数据集最容易翻车的五个点

4.1 现象:训练 loss 正常但验证 mAP 为 0

原因:data.yaml里names顺序和 txt 里的 id 对不上,模型学的是「错位类别」,验证时按正确名字算指标自然全错。解决:用第 2.2 节的脚本反查每个 id 对应的 xml 类别名,确认顺序一致后再训。这个坑最隐蔽,因为 loss 会正常下降,只有看混淆矩阵才发现类别全串了。

4.2 现象:报错IndexError: index 3 is out of bounds

原因:剔除了drugs类但没改 txt,或者nc写成了 3 而 txt 里还有 id=3 的行。解决:全局搜一遍所有 txt,把 id 大于等于nc的行删掉或重映射,再确认data.yaml的nc和names长度一致。血泪经验是改类别一定要「yaml + txt + 可视化」三处同步。

4.3 现象:banlangen类几乎检不出

原因:该类只有 134 框,且可能集中在少数图片里,模型见得太少。解决:先统计该类出现在多少张图,如果少于 100 张,考虑对含该类的图做复制过采样,或调低该类在损失里的权重惩罚。不要直接调高学习率,小样本类调学习率往往更糟。

4.4 现象:图片能读但标注框整体偏移

原因:VOC 的 xmin/ymin 是左上角,YOLO 的 cx/cy 是中心点,转换时如果忘了除以 2 或忘了归一化,框会整体偏移甚至超出 1。解决:转完抽 5 张图用可视化脚本画框,肉眼看框是否贴合药品盒。常见做法是用PIL画矩形,比对着原图看。

4.5 现象:训练到一半显存爆掉

原因:batch设太大,或 mosaic 增强把 4 张图拼成一张导致等效分辨率翻倍。解决:先把 batch 降到 8,再关掉 mosaic 试一轮。如果还爆,把--img从 640 降到 512。药品盒检测对分辨率没那么敏感,512 通常够用,别为了 640 硬撑爆显存。

5. 小样本药品检测的增强技巧:从 959 张里榨出更多有效样本

959 张图、1365 个框,放在目标检测里属于小数据集。想让它训出能用的模型,增强策略比模型结构更关键。我一般会按「先保类别平衡,再保尺度多样,最后保背景真实」的顺序做。

第一,针对banlangen只有 134 框的问题,做类别感知的过采样。不是简单复制图片,而是把含banlangen的图在训练列表里重复 2~3 次,同时配合 mosaic 让它们和其他类拼在一起,增加共现场景。这样模型不会因为该类样本少而直接忽略。

第二,尺度增强要克制。药品盒在货架上通常占图比例中等,mosaic 的scale参数别开太大,常见做法是scale=0.5,避免把盒子缩得太小导致标注框和实际目标错位。如果你发现验证时小目标 recall 明显低,优先检查是不是增强把目标缩过头了。

第三,背景要真实。这份数据集的图片命名带xyxr后缀,推测是同一批采集场景。如果全是白底或单一货架,模型换到真实药房会崩。常见做法是加mixup或随机裁剪,但 mixup 对小数据集容易产生不真实叠加,我一般只在 epoch 后 30% 开启,前期先让模型学干净特征。

第四,验证阶段别只看 mAP。小数据集上 mAP 波动大,建议同时看每类的 precision 和 recall,尤其是banlangen的 recall。如果 recall 长期低于 0.5,说明该类样本量或增强还不够,这时候加 epoch 没用,得回去补数据或调采样。

最后说个习惯:我每次拿到新数据集,都会先跑一遍「可视化 20 张 + 统计类别分布 + 核对 id 映射」这三件事,再动训练脚本。这份数据集的双格式给了很大便利,xml 用来核对,txt 用来训练,两边对不上就以 xml 为准。从那以后我每次接新数据集都强制走一遍这个流程,省下的返工时间远比那十分钟多。希望帮到你。

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

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

Agent结构化输出实战:用LangChain与Pydantic打通最后一公里

在Agent开发这条路上摸爬滚打了一段时间之后&#xff0c;我发现一个特别有意思的现象&#xff1a;很多人能把Agent跑起来&#xff0c;能调工具、能对话、能查资料&#xff0c;但一到要拿它的输出对接下游系统&#xff0c;就全乱套了。模型返回一段洋洋洒洒的自然语言&#xff0…

作者头像 李华
网站建设 2026/10/5 9:28:32

基于YOLOv5+OpenPose的人体姿态识别算法工程实践与优化

简介&#xff1a;一套结合OpenPose与YOLOv5的人体姿态识别完整项目&#xff0c;面向具备计算机视觉与机器学习基础的开发者、研究者&#xff0c;用于解决多人关键点检测、实时姿态估计及工程化部署等实际问题。压缩包共716个文件、约85.59MB&#xff0c;内部以C头文件/源文件&a…

作者头像 李华
网站建设 2026/10/5 9:28:18

通达信主力吸筹猛攻指标:源码拆解与实战用法

很多人拿到所谓“主力吸筹猛攻指标”&#xff0c;第一反应是看它能不能让自己买在起爆点。我的看法很直接&#xff1a;这类指标真正的价值&#xff0c;不在于那个红红绿绿的信号箭头&#xff0c;而在于它背后对“量、价、资金”三者关系的刻画方式。今天我把自己多年折腾通达信…

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

从闭源到开源:本地部署NeoHorse-Jev-4B决策模型实战指南

最近圈子里讨论最多的模型&#xff0c;除了各种Chat类大模型&#xff0c;还有一个叫Jev的决策模型。它不写诗、不聊天&#xff0c;专干“判断”这活&#xff0c;比如“这条工单该分给哪个组”“这笔交易要不要人工审核”。老实说&#xff0c;第一次看到Demo的时候我也觉得只是又…

作者头像 李华
网站建设 2026/10/5 9:27:16

让AI编程从“快”到“可靠”:Superpowers工作流全解析

在AI编程工具越来越普及的圈子里&#xff0c;Superpowers这个名字最近被频繁提起。它不是某个大厂发布的新IDE&#xff0c;也不是又一款“AI编程软件”&#xff0c;而是一套针对AI编程代理设计的开源技能与工作流集合。我最初接触它&#xff0c;是因为自己用命令行AI写代码时觉…

作者头像 李华
网站建设 2026/10/5 9:27:02

AI Native团队落地手册:Agent编排、CLAUDE.md与安全边界实战

1. 从“人写代码”到“人管意图”&#xff1a;AI Native 团队到底在变什么“AI Native”这个词最近被喊得很响&#xff0c;但真正落到一个研发团队里&#xff0c;它到底意味着什么&#xff1f;我见过太多团队把 AI Native 理解成“给每个人发一个 AI 编程助手账号”&#xff0c…

作者头像 李华