简介:这份资源是面向目标检测初学者与药品包装质检场景的布洛芬检测数据集,适合需要快速验证模型、练习标注流程或搭建药品识别原型的开发者与研究人员。数据集共476张jpg图片,每张均配有对应的VOC格式xml标注与YOLO格式txt标注,标注工具为labelImg,采用矩形框方式,类别仅含buluofen一类,共657个标注框,样本分布相对集中,便于直接用于单类别检测任务。压缩包内文件总数为1430个,其中txt文件478个、xml文件476个、jpg图片476个,整体大小约19.62MB,体积轻量,下载与解压成本低。目前已有175人学习下载,说明该数据集在药品检测方向具有一定参考价值。读者可借助VOC与YOLO双格式标注,灵活适配Faster R-CNN、YOLO系列等主流框架,省去自行采集与标注的时间,快速投入模型训练、数据增强与评估实验,也可作为药品包装缺陷检测或相似单类别任务的迁移学习起点。
1. 476张药品布洛芬检测数据集:小样本目标检测的试金石
手里只有几百张图,却要跑通一个药品检测模型,这是很多做工业视觉和医药分拣的工程师都会遇到的真实开局。药品布洛芬检测数据集476张VOC+YOLO格式,本质上就是这样一个“小而精”的起点:476张标注好的药品图像,同时提供VOC的XML和YOLO的TXT两套标注,让你不用在格式转换上浪费时间,直接把精力放在模型能不能收敛、小目标能不能召回上。它适合三类人:一是刚接触目标检测、想找一个真实场景练手的新手;二是做药品分拣、药房自动化、智能药柜的从业者,需要快速验证一个检测方案是否可行;三是想研究小样本、类别不均衡问题的熟手,因为药品包装的类间差异小、类内差异大,天然是个有挑战的测试集。别小看这476张,它足够让你把YOLO训练、验证、部署的整条链路走一遍,也能让你踩到小数据集最典型的坑。
2. 拆开这个数据集:VOC与YOLO双格式到底怎么用
2.1 VOC格式的目录结构与XML字段含义
拿到压缩包解压后,常见做法是看到两个顶层目录:VOC2007和YOLO(或者labels和images分开)。VOC格式的核心是Annotations文件夹里每张图对应一个XML,文件名与图片同名。XML里真正影响训练的是这几个字段:filename记录图片名,size里的width和height是原图尺寸,object下的name是类别名,bndbox里的xmin、ymin、xmax、ymax是左上角和右下角坐标。注意,VOC的坐标是绝对像素值,且原点在左上角,xmax和ymax是包含边界的,转换时不要习惯性减一,否则框会整体偏小一个像素。对于药品布洛芬这种单类或少数类场景,name字段通常就是ibuprofen或者pill,你要先确认类别名是否统一,有没有写成Ibuprofen、ibuprofen、布洛芬混用的情况,这会导致训练时类别数对不上。
2.2 YOLO格式的归一化坐标与类别索引
YOLO格式的标注是每张图一个TXT,每行一个目标,格式为class_id x_center y_center width height。这里的坐标全部是归一化到0到1之间的浮点数,x_center和y_center是框中心点相对于图宽和图高的比例,width和height是框宽高相对于图宽和图高的比例。class_id是从0开始的整数索引,对应一个classes.txt或data.yaml里的类别列表。很多新手直接拿VOC的绝对坐标除以图片尺寸,却忘了VOC的xmax是包含边界的,正确做法是x_center = (xmin + xmax) / 2.0 / width,w = (xmax - xmin) / width,不要额外加一或减一。如果你拿到的YOLO标注里出现了大于1的值,说明转换脚本写错了,或者原图尺寸记录有误,这种数据直接训练会导致损失爆炸。
2.3 用Python脚本做一次格式自检与可视化
在投入训练之前,我一般会写一个短脚本把标注画到图上,肉眼确认框的位置和类别是否正确。下面这段代码同时读取VOC和YOLO两种格式,把框画出来保存到check目录,你只需要改img_dir和label_dir两个路径。
import os import cv2 import xml.etree.ElementTree as ET # 配置路径:图片目录和标注目录(VOC用Annotations,YOLO用labels) img_dir = "dataset/images" voc_dir = "dataset/Annotations" yolo_dir = "dataset/labels" save_dir = "check" os.makedirs(save_dir, exist_ok=True) # 读取YOLO格式并画框 def draw_yolo(img_path, label_path, save_path): img = cv2.imread(img_path) h, w = img.shape[:2] if os.path.exists(label_path): with open(label_path, "r") as f: for line in f.readlines(): parts = line.strip().split() if len(parts) != 5: continue cls_id, xc, yc, bw, bh = map(float, parts) # 反归一化到像素坐标 x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls_id)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(save_path, img) # 读取VOC格式并画框 def draw_voc(img_path, xml_path, save_path): img = cv2.imread(img_path) if os.path.exists(xml_path): tree = ET.parse(xml_path) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text bbox = obj.find("bndbox") x1 = int(bbox.find("xmin").text) y1 = int(bbox.find("ymin").text) x2 = int(bbox.find("xmax").text) y2 = int(bbox.find("ymax").text) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, name, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(save_path, img) # 遍历图片,同时检查两种格式 for img_name in os.listdir(img_dir): if not img_name.lower().endswith((".jpg", ".png", ".jpeg")): continue img_path = os.path.join(img_dir, img_name) base = os.path.splitext(img_name)[0] # YOLO检查 yolo_label = os.path.join(yolo_dir, base + ".txt") draw_yolo(img_path, yolo_label, os.path.join(save_dir, "yolo_" + img_name)) # VOC检查 voc_xml = os.path.join(voc_dir, base + ".xml") draw_voc(img_path, voc_xml, os.path.join(save_dir, "voc_" + img_name)) print("可视化完成,请检查check目录")这段代码的逻辑很直接:对每张图分别用YOLO和VOC的解析方式画框,YOLO用绿色,VOC用红色,保存到check目录。参数上你只需要改三个路径,img_dir指向图片文件夹,voc_dir指向XML文件夹,yolo_dir指向TXT文件夹。跑完之后重点看三件事:框有没有明显偏移、有没有漏标的目标、类别文字是不是你预期的那个。如果发现YOLO的框整体偏小一圈,大概率是转换时把xmax减了1;如果VOC的框跑到图像外面去了,检查XML里的size和实际图片尺寸是否一致。这一步花十分钟,能省掉后面几个小时的训练排错。
3. 用YOLOv8跑通476张药品检测:从data.yaml到训练命令
3.1 组织数据集目录与编写data.yaml
YOLOv8对目录结构有固定要求,常见做法是建一个datasets文件夹,里面放images和labels,再各自分train、val、test。对于476张图,我一般按7:2:1切分,也就是训练集333张、验证集95张、测试集48张。切分时要注意同一类药品的不同角度、不同光照尽量均匀分布,不要把所有正面图都放进训练集,否则验证集指标会虚高。切分脚本可以用sklearn.model_selection.train_test_split,也可以用简单的随机打乱后按比例复制。目录建好后,在datasets同级写一个ibuprofen.yaml,内容如下:
path: ./datasets train: images/train val: images/val test: images/test nc: 1 names: ['ibuprofen']nc是类别数,药品布洛芬数据集如果只有布洛芬一种药,就写1;如果还包含其他药品,按实际类别数改。names列表的顺序必须和YOLO标注里的class_id一致,否则模型会把布洛芬学成别的类别。这个文件是YOLOv8训练的唯一入口,路径写错会直接报Dataset not found。
3.2 训练命令与关键参数怎么设
YOLOv8的训练命令很简洁,但参数设不对,476张小数据集很容易过拟合或者不收敛。我常用的起手命令是:
yolo detect train \ data=ibuprofen.yaml \ model=yolov8n.pt \ epochs=200 \ imgsz=640 \ batch=16 \ lr0=0.001 \ lrf=0.01 \ patience=50 \ augment=True \ cache=True \ device=0逐项说明:model=yolov8n.pt用nano版本,因为数据量小,大模型反而容易过拟合;epochs=200配合patience=50,如果50轮验证指标不升就早停,避免浪费时间;imgsz=640是默认输入尺寸,药品包装上的文字和图案通常在这个分辨率下能看清,如果药盒很小,可以提到imgsz=960,但显存要够;batch=16在单卡12G显存下比较稳,显存小就降到8;lr0=0.001是初始学习率,小数据集不要用默认的0.01,容易震荡;lrf=0.01是最终学习率因子,让学习率在训练末期降到初始值的1%;augment=True开启Mosaic、HSV等增强,对476张图来说,增强是防止过拟合的关键;cache=True把图片缓存到内存,加快训练速度,但内存小于16G就别开。训练过程中重点看mAP50和mAP50-95,如果mAP50在0.8以上,说明模型基本可用;如果一直在0.3以下,先回去检查标注。
3.3 训练日志里必须盯住的三个指标
训练启动后,控制台会打印每个epoch的损失和指标。第一个要盯的是box_loss,它衡量预测框和真实框的差距,正常应该从1.0左右稳步下降到0.3以下。如果box_loss不降反升,检查学习率是不是太大,或者标注里有没有宽高为0的无效框。第二个是cls_loss,分类损失,单类场景下它应该很快降到接近0,如果一直很高,说明类别索引对不上,比如标注里写了class_id=1但nc=1只允许0。第三个是mAP50,这是最直观的指标,476张图训练集上mAP50到0.9以上不稀奇,但验证集能到0.75以上才算真的学到了东西。如果训练集mAP50很高、验证集很低,那就是过拟合,需要加增强、减模型复杂度或者早停。另外注意val/box_loss和val/cls_loss,它们和训练损失差距太大也是过拟合的信号。
4. 小数据集训练避坑:476张图最容易翻车的五个地方
4.1 标注类别名不一致导致类别数错乱
现象:训练启动时报AssertionError: nc mismatch,或者训练完发现模型把所有药都预测成同一个类别。原因:VOC的XML里name字段有的写ibuprofen,有的写Ibuprofen,有的写布洛芬,而YOLO的classes.txt只定义了一个类别。解决:在转换前统一类别名,用脚本把所有XML里的name替换成同一个字符串,再重新生成YOLO的TXT和data.yaml。我一般会先跑一遍grep -r "<name>" Annotations | sort | uniq -c,看看有多少种写法。
4.2 图片与标注文件名不匹配
现象:训练时提示No labels found,或者某张图被跳过。原因:图片是IMG_001.jpg,标注是IMG_001.xml或IMG_001.txt,但中间多了空格、大小写不一致,或者图片是.jpeg而标注是.jpg。解决:写一个重命名脚本,把所有图片和标注的后缀统一成小写,去掉文件名里的空格和特殊字符,确保os.path.splitext出来的base完全一致。这个坑在Windows和Linux之间拷贝文件时尤其常见。
4.3 验证集里出现训练集图片导致指标虚高
现象:验证集mAP50高得离谱,比如0.98,但拿新图片测试时一塌糊涂。原因:切分数据集时用了随机切分,但同一张图的不同增强版本或者同一批次连拍的图片被分到了训练集和验证集。解决:按图片的原始来源分组切分,比如同一盒药的不同角度算一组,整组进训练集或验证集。如果数据里没有分组信息,至少用hash去重,确保验证集的图片没有在训练集里出现过。
4.4 小目标漏检严重
现象:大药盒能检测到,小药板或者远处药盒漏检。原因:476张图里小目标占比高,而YOLOv8默认的imgsz=640下,小目标经过下采样后特征太弱。解决:把imgsz提到960或1280,同时开启mosaic增强让模型多见小目标在不同位置的情况。如果显存不够,用yolov8s代替yolov8n,稍微增加模型容量。另外检查标注里小目标的框是不是太小,比如宽高只有几个像素,这种框在训练时会被忽略,需要合并或剔除。
4.5 训练到一半loss突然变成NaN
现象:前几十轮正常,突然box_loss变成nan,训练中断。原因:学习率太大导致梯度爆炸,或者标注里有坐标超出图片范围的异常框。解决:先把lr0降到0.0005,加warmup_epochs=3让学习率慢慢升上去。然后检查所有YOLO标注,确保x_center、y_center、width、height都在0到1之间,如果有大于1的值,定位到具体图片重新标注或修正。另外batch太大也可能导致NaN,降到8试试。
5. 从476张到可用模型:验证、导出与一个提点技巧
训练完不是终点,你得知道模型到底能不能用。我一般会做三件事:第一,用yolo detect val在测试集上跑一遍,看mAP50和mAP50-95,同时生成混淆矩阵,确认有没有把布洛芬误判成背景;第二,拿几张训练时没见过的真实药盒照片,用yolo detect predict跑一下,看置信度分布,如果大部分框的置信度在0.5以下,说明模型还没学好;第三,用yolo export导出ONNX或TensorRT,测一下推理速度,药品分拣场景通常要求单张图在50毫秒以内,如果超过这个数,考虑换更小的模型或者量化。
这里分享一个我常用的提点技巧:用训练好的模型对训练集做一次推理,把置信度低但实际正确的框挑出来,人工确认后加入训练集。476张图里总有一些目标因为角度、遮挡导致模型学不好,通过这种“模型挖矿”的方式,往往能再提升2到3个点的mAP50。具体操作是先用yolo detect predict生成预测结果,然后写脚本对比预测框和真实框的IoU,把IoU大于0.5但置信度小于0.4的图片挑出来,这些就是模型的“困难样本”,把它们复制一份到训练集,重新训练一轮。注意不要直接把预测框当标注,一定要人工核对,否则会引入噪声。
导出ONNX的命令很简单:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640导出后可以用onnxruntime加载,测一下单张图的推理时间。如果部署在边缘设备上,还可以用format=engine导出TensorRT,速度能再快一倍,但需要设备支持。最后说一句血泪经验:小数据集训练,数据质量比模型结构重要十倍。476张图如果标注得干净、类别统一、切分合理,YOLOv8n就能跑出可用的效果;如果标注乱七八糟,换再大的模型也是白搭。希望帮到你。
本文还有配套的精品资源,点击获取