如果你去制药车间实地看过,一定会对质检工位印象深刻:成排的药板从传送带上流过,质检员一天要盯上万粒药片,检查有没有漏装、缺角、裂纹,或者混入异物。这个岗位极其枯燥,却直接关系到用药安全,漏掉一粒药的后果谁都担不起。传统视觉方案能做一部分,但遇到药片排列随意、光照变化大、铝箔反光强的场景,规则写起来非常痛苦。这也是为什么越来越多团队开始把目标检测模型引入产线——用AI替代人眼做初检,把质检员从重复劳动里解放出来,把精力放到复核和异常处置上。
这篇文章就从零开始,带你走完一个药片检测AI模型的完整训练流程:数据怎么采、怎么标,标注格式长什么样,YOLO26的训练参数怎么配,训练完怎么评估和部署。全程以我实际跑过的项目为主线,顺手把踩过的坑都标出来。适合有Python基础、想认真落地目标检测的开发者,也适合医药行业里想搞智能化质检的技术人员。
1. 项目价值与方案选型:为什么药片检测值得用AI来做
1.1 医药行业里真正需要“检测”的几个位置
先别急着跑代码,先把场景想清楚。药片检测并不是单一需求,我接触到的主要有这么几类:
- 泡罩包装漏装检测:铝箔封口后看不到里面的药片,通过X光或视觉透射方式检测有没有漏片、空泡。这是制药厂最普遍的质检需求。
- 外观缺陷检测:缺角、裂片、污渍、色差、表面斑点。这类需要高分辨率图像和较细致的特征提取能力。
- 成瓶药片计数:散装药片灌装后统计数量,防止多装或少装。常规做法是数药片轮廓,但药片叠在一起时就要靠检测模型。
- 药房智能核对:拍照识别药片品种和数量,辅助发药人员避免拿错药。这个场景里类别数量可能很多,而且不同批次药片外形差异小,难度更高。
我这次做的项目属于典型的泡罩板漏装检测场景:传送带上有单板、双板、三板交替通过,药片颜色偏深,铝箔反光严重,现场光照会随着早晚班切换发生变化。用传统图像处理的思路做过一轮:通过灰度阈值分割药片区域,再按连通域数量判断是否漏装。结果一到夜班灯光全开,反光区域变得不可控,误检率直接飙升到没法用。后来换用目标检测模型,效果立刻就不一样了。
1.2 为什么选YOLO26这套技术路线
目标检测框架这么多,选型逻辑其实很直接:两阶段检测器虽然精度有优势,但速度慢,产线检测往往要求实时或近实时;传统单阶段检测器速度快,但早期版本对小目标不友好。YOLO系列能成为工业落地的主流选择,就是因为它在精度和速度之间找到了一个非常实用的平衡点。
说回YOLO26,我手头用的这个版本给我的直观感受有三点:一是检测头做了更细粒度的多尺度融合,对药片这类小目标更友好;二是主干网络的特征提取效率更高,同样精度下模型体积和推理延迟明显更小;三是训练过程中的收敛曲线更平滑,对超参数的敏感度降低了,这对新手来说特别重要。整体训练逻辑和之前的YOLO系列一脉相承,官网提供了预训练权重,方便做迁移学习。
当然,这里得说句实在话:如果你之前用过YOLOv8或者YOLO11,这套流程几乎是无缝迁移的。这篇教程虽然以YOLO26为例,但涉及的数据准备、标注格式、训练技巧、评估方法,放到其它版本的YOLO框架上也完全适用。所以不用纠结版本号,核心方法学会了才是自己的。
2. 数据准备与标注:模型效果的上限从这一步决定
2.1 图像采集方案:先搞到一套能用的训练数据
很多初学者上来就找公开数据集,但药片检测这种场景,不同厂家的药片颜色、大小、包装方式差别很大,公开数据很难直接匹配现场环境。所以我强烈建议:有条件就自己采集,而且采集环境越贴近真实产线越好。
我当时的采集方案很简单:用一台工业面阵相机固定在传送带正上方,垂直俯拍,下面配一个封闭的遮光罩,减少环境光干扰。没有工业相机的话,用手机固定在三脚架上俯拍也能用,但要注意拍摄高度和角度保持一致,不然模型会学到奇怪的角度偏差。每个场景至少拍500张以上,不同批次、不同光照条件、不同排列密度都要覆盖到。
这里有一个很多人容易忽略的点:背景不要一成不变。如果训练集的背景全是黑色传送带,模型可能会把背景特征也学进去,到了现场背景变成白色塑料板,检测效果直接崩。所以采集时要刻意加入不同背景、不同托盘材质、不同摆放方式的数据,哪怕每种只增加几十张,都能显著提升模型的泛化能力。
采集完的图片要记得统一分辨率。YOLO训练时虽然会自动做resize,但如果原始图片分辨率差异过大(比如有的是4000x3000,有的是640x480),训练时缩放到统一尺寸的信息损失程度不同,会间接影响检测效果。我的做法是先统一压到与输入分辨率同比例的范围,比如宽边2000像素左右,再送入标注流程。
2.2 标注规范与最容易踩的坑
标注工具我常用的是X-AnyLabeling和LabelImg,两者都支持YOLO格式导出。如果你和团队协作,也可以用Roboflow这类在线平台,直接在浏览器里标注,还自带数据集管理功能。工具本身差别不大,更重要的是标注规范——同一个类别在不同图片里画法不一致,会成为模型困惑的根源。
YOLO格式的标注文件是纯文本txt,每行对应一个目标,格式是:
类别id 中心点x 中心点y 宽度w 高度h
注意x、y、w、h都是归一化坐标,也就是像素值除以图片宽高后的结果。举例来说,一张宽1920、高1080的图片里,一个药片的边界框左上角在(480, 270),右下角在(960, 810),那么中心点x就是(480+960)/2/1920=0.375,中心点y就是(270+810)/2/1080=0.5,宽度w是(960-480)/1920=0.25,高度h是(810-270)/1080=0.5。写进txt就是0 0.375 0.5 0.25 0.5。
标注时最忌惮这么几个问题:
- 边界框画得太随意。框应该紧紧贴合药片的可见轮廓,不要留大片空白,也不要切掉药片边缘。YOLO的回归目标就是边界框,框本身不准,模型永远学不好。
- 严重遮挡的药片要不要标?我的经验是:如果机器都看不出那是完整药片,只有明显超过一半的部分可见才标注,否则宁可漏掉也不要标个四不像的框。标注一致性比数量更重要。
- 类别混淆问题。比如两个厂家生产的药片长得几乎一样,只是颜色深浅略有差异,这类要单独建类别,还是合并成一个类?如果模型只需检测“有没有药片”而不是区分品种,就合并;如果需要区分,一定要保证每个类别的样本量均衡,并且拍摄时要尽量凸显它们的差异特征。
标完一轮后,我建议做一次抽检:把标注框渲染回图片上,目视检查一遍。这一步很枯燥但非常必要,能够拦截大量粗心标错,省得训练完才发现数据有问题。
2.3 数据增强策略:哪些有效,哪些会帮倒忙
YOLO26训练时默认开启Mosaic增强,把四张图拼接成一张,能显著提升模型对目标尺度变化的适应能力。药片虽然大小相对固定,但不同摆放角度、不同拍摄距离下,画面中的尺寸仍然会有波动,所以Mosaic建议保留。
除了Mosaic,我常开的有水平翻转、轻微旋转(±10度以内)、HSV颜色扰动(饱和度±25%、亮度±15%)。这些增强模拟了产线上药片朝向变化和光照波动的真实情况。但有些增强要非常谨慎:
- 垂直翻转:药片是三维刚性物体,上下翻转在真实场景里几乎不会出现,开启后反而会让模型学习到不真实的特征。
- 大幅旋转:超过30度的旋转会改变药片的几何形态,导致误检。除非你的产线上药片是散乱堆叠的,否则不建议开太大。
- 随机擦除或Cutout:有些团队拿它来模拟遮挡,但药片检测场景里遮挡通常来自铝箔或包装膜,形态比较规整,随机擦除反而破坏了训练样本的真实性。
一句话总结:增强策略必须来源于现场真实变化。先统计产线上可能出现哪些扰动,再决定开哪些增强,不要盲目把常用增强全开一遍。
3. 训练流程实操:环境、参数、跑通一个模型
3.1 搭建训练环境与数据目录结构
训练环境建议直接用Linux,配一块NVIDIA显卡,显存8G以上比较舒服。软件方面需要Python 3.10以上版本、PyTorch 2.x版本,以及YOLO26对应的代码仓库。安装依赖时别用最新的CUDA版本,除非你是从零编译,一般直接用仓库默认要求的PyTorch轮子版本最省事。
纯CPU训练不是不行,但药片检测这种小目标任务通常要几百张图、100轮起步,CPU训练一轮可能要几个小时,完全不现实。如果实在没有GPU,可以先用小数据集、小输入分辨率把流程跑通,感受一下全流程,再考虑云GPU方案。
数据目录一般长这样:
datasets/ pills/ images/ train/ val/ labels/ train/ val/images和labels放对应的图片和txt标注文件。需要额外写一个data.yaml配置文件,内容大致是:
path: datasets/pills train: images/train val: images/val names: 0: pill如果有多类药片,就按顺序把类别名列全。这里要特别注意类别id必须和标注txt里的数字一一对应,否则训练不会报错,但评估结果会完全乱掉。
数据划分我习惯按8:1:1拆成train/val/test。val集在整个训练过程中不参与梯度更新,只用于评估;test集是最终训练完后统一测评用的。注意划分时要保证同一片药板的多张照片不要同时出现在train和val里,不然会严重高估模型效果。做法是先按“拍摄批次”分组再划分,而不是逐张随机切。
3.2 核心超参数解析与选择
训练参数里最影响结果的几个是:输入分辨率imgsz、批次大小batch、训练轮数epochs、初始学习率lr0、权重衰减weight_decay、以及Mosaic开关。
我这次用的配置参数如下,供你参考:
| 参数 | 配置值 | 说明 |
|---|---|---|
| imgsz | 640 | 输入分辨率,药片小目标多,不建议低于640 |
| batch | 16 | 根据显卡显存调节,显存不够就降到8 |
| epochs | 200 | 单类别小数据量场景,150-200轮基本够 |
| lr0 | 0.01 | 使用预训练权重时的经验值 |
| weight_decay | 0.0005 | 防止过拟合,数据量小时偏保守 |
| mosaic | 1.0 | 默认开启,前10轮可以先关掉 |
| workers | 4 | 数据加载线程数,视CPU核心数而定 |
输入分辨率的选择值得展开说一下。imgsz越大,模型能看到越多的细节,小目标检测效果越好,但显存占用和推理耗时都会增加。产线检测对实时性要求高,分辨率不是越高越好。我建议先用640跑一版,看小目标的漏检情况再决定是否上调到768或832。药片本身不大,在640分辨率下如果漏检严重,往往不是分辨率问题,而是标注质量或数据量问题。
显存方面有个粗略的估算方法:在640分辨率下,batch size为16时,FP16混合精度训练大约需要12-14G显存;batch降为8则大约7-8G。如果你只有8G显存,直接用batch=8,配合amp=True混合精度,一般能跑起来。显存不够时的第一选择永远是降batch,而不是降imgsz——分辨率对检测精度的影响远大于batch size。
使用预训练权重做迁移学习是官方推荐的做法,也强烈建议这么做。药片检测并不是一个特别独特的任务,从自然图像预训练模型开始,可以让主干网络对边缘、纹理、颜色的基本特征有很好的先验,只需要在医药场景数据上做微调即可。训练脚本里用--weights参数指定预训练权重,初次使用时它会自动下载。
3.3 训练过程监控与权重选择技巧
训练启动后会实时打印每个epoch的损失和评估指标。很多人只看最终mAP,不关心训练过程,这是个坏习惯。训练曲线的形态能告诉你很多信息:
- loss持续下降、val mAP稳步上升:健康状态,继续跑。
- loss快速下降但val mAP停滞甚至下降:开始过拟合,考虑减少epochs或增加数据增强。
- loss在某个值附近反复震荡不下降:可能是学习率太大,或者数据里有大量标错的样本。
训练结束后会在输出目录里生成best.pt和last.pt两个权重文件。best.pt是验证集上表现最好的那一轮,last.pt是最后一轮的。默认选best.pt用,但我习惯再多做一步:用test集把best.pt和last.pt都测一遍,对比一下,因为有时候验证集指标最好的权重并不一定在测试集上表现最好,这在小数据集场景里尤其明显。
训练到一半发现效果不理想怎么办?不要闷头加大epochs,先回到数据层面检查。我个人有个经验法则:如果训练集上检测效果很好但验证集上很差,优先怀疑数据划分有交叉或者过拟合;如果训练集上就检测不出来,那问题基本出在数据标注或者类别定义上。花一小时检查数据,远比多跑几轮训练更有价值。
4. 模型评估与部署落地:从训练完到用起来
4.1 评估指标:mAP不是唯一标准,要结合场景看
YOLO训练完会在验证集上输出mAP50、mAP50-95、precision、recall等指标。很多人看到mAP50到0.95就觉得模型很强了,但如果你的场景对漏检率极其敏感,那就不能只看mAP。
mAP50指的是IoU阈值为0.5时的平均精度均值,mAP50-95是IoU从0.5到0.95取多个阈值后的平均值。前者对框的位置精度要求宽松,更适合有粗检测需求的场景;后者更严格,适合需要精确定位的任务。药片检测下游通常需要定位到具体药片才能判断是否漏装,所以我会更关注mAP50-95。
另外一定要看precision和recall。precision是“模型认为是药片的里面有多少真的是”,recall是“真正的药片里面模型找回来了多少”。漏装检测场景里,漏检一粒药片的后果比多报一次警报严重得多,所以recall优先。如果你的模型precision很高但recall偏低,就适当降低置信度阈值,让模型多给出一些候选框,宁可误报也不要漏报。
YOLO训练时默认用置信度0.25做正负样本划分。这个值可以在评估和部署阶段自行调整。我部署到产线时会把置信度阈值降到0.15左右,同时要求相邻两帧都检测到同一位置药片才确认存在,用一个简单的帧间逻辑过滤掉误报。这一招对现场场景非常有效。
4.2 模型导出与部署集成
训练好的PyTorch权重不能直接拿去产线用,一般要导出为ONNX格式或TensorRT引擎。ONNX是通用中间格式,几乎任何推理框架都支持;TensorRT是NVIDIA显卡上的高性能推理引擎,部署延迟能压到很低。导出代码很简单,在YOLO26仓库里执行:
python export.py --weights best.pt --include onnx --imgsz 640导出后可以用ONNX Runtime加载推理,核心代码大致如下:
import onnxruntime as ort import cv2 import numpy as np session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name img = cv2.imread("test.jpg") img_resized = cv2.resize(img, (640, 640)) blob = img_resized[:, :, ::-1].transpose(2, 0, 1)[None].astype(np.float32) / 255.0 outputs = session.run(None, {input_name: blob}) # 解析outputs得到边界框、置信度、类别有一点容易踩坑:导出的模型期望的输入是RGB归一化张量,而OpenCV读图默认是BGR。如果你忘记做通道翻转,推理结果会非常奇怪,药片位置对不上或者大量误检。每次部署新环境,第一件事就是用一张带标准框的测试图跑一遍原图和预处理图,确认色彩空间一致。
部署端的硬件选择取决于产线速度。如果传送带每秒过3-5块药板,工控机配一块消费级显卡就能胜任;如果速度更快或者检测多个工位,就要考虑Jetson这类边缘设备,配合TensorRT进一步压缩延迟。还有个更省算力的思路:检测区域固定时,可以先裁出药板所在的ROI区域,只在ROI上做检测,输入尺寸缩小后推理速度提升非常明显。
5. 实战问题速查:那些训练和部署时真正会踩的坑
5.1 问题清单与排查思路
把实战中遇到的典型问题整理成一张表,方便你对着排查。
| 现象 | 很可能的原因 | 解决思路 |
|---|---|---|
| 训练时loss降不下去 | 标注缺失或错误,或类别数量与配置不一致 | 渲染标注检查数据,重点看遮挡和模糊样本 |
| mAP很高但现场误检多 | 背景过拟合,训练集背景单一 | 增加不同背景数据,适当关掉不相关的增强 |
| 小药片频繁漏检 | 输入分辨率偏低,或小目标样本占比少 | 提高imgsz到768,检查小目标样本量 |
| 验证集比训练集指标高一截 | 数据划分有泄漏 | 按批次划分数据集,不要按张随机划分 |
| 显存不足OOM | batch过大或图像尺寸过大 | 降低batch,开启混合精度,必要时降分辨率 |
| 推理速度达不到产线要求 | 模型过大或输入分辨率偏高 | 尝试TensorRT引擎,裁剪ROI区域,统一批次推理 |
| 部署后检测结果和训练时差异大 | 色彩空间不一致,或图像压缩格式不同 | 对比预处理流程,检查BGR/RGB顺序和归一化方式 |
5.2 几个值得单独强调的避坑经验
先说反光问题。泡罩板的铝箔反光是个典型难题,训练集里如果不包含反光样本,模型上线后遇到强反光就是漏检重灾区。我记得第一次上线,白班阳光从窗户照进来,有两块药板的铝箔反光特别严重,模型直接漏掉了几粒药片,还好有后续的人工复核环节兜底。这个问题的解法不是硬调模型,而是从源头控制:加遮光罩、调整相机角度避开光源直射。如果反光实在没法消除,就在数据采集时有意识地制造反光,让模型学会在反光区域中分辨药片轮廓。
再说类别不平衡。如果泡罩板上大部分是A药片,只有少量B药片混线,模型很容易把B药片学成背景。处理办法是采样时人为平衡各类别数量,或者对小样本类别做更高的过采样增强。哪怕是多花些时间手工补采B药片的数据,也远比训练完发现B药片检测效果差再返工划算。
最后说标注边界框的贴合度。我在项目里发现,标注时框的松紧程度直接影响模型对药片边缘的定位精度。框太松,模型输出的框会偏大,和药板孔位的对位就不准;框太紧,稍微挡住一点点就会漏检。一个可行的标准是:让边界框紧贴药片主体,但允许包含少量的药片边缘阴影。
写在最后
说实话,训练一个药片检测模型并不难,技术栈非常成熟,难的是把每一个细节都做扎实。数据采集是否贴近现场、标注是否一致、划分是否有泄漏、部署时的色彩空间是否踩坑——随便一个环节没处理好,模型在测试集上的漂亮指标到了产线上就会原形毕露。我个人的体会是,这类工业质检项目的成功与否,七成取决于数据工作的精细程度,剩下的三成才是模型和调参。
如果你正准备上手,我的建议是别一上来就追求完美,先用一小批数据把流程走通,再回头逐步扩充数据和调整策略。YOLO26这套框架给了你很好的起跑线,剩下的路要靠一颗肯耐心打磨数据的心,才能走稳。最后再分享一个小技巧:训练完记得把错误检测的样本逐张翻一遍,你会从中收获比任何调参指南都多的实战经验。