163种中草药图像分类实战:从数据清洗到模型部署

163种中草药图像分类实战:从数据清洗到模型部署 简介这是一份面向中草药AI识别研究者的说明材料配套可运行源码聚焦163种中草药图像数据集Chinese-Medicine-163。该数据集包含超过25万张中药材图片按训练集与测试集划分每类平均约1575张训练图和61张测试图数据来自网络爬虫检索并经过人工清洗与整合整体质量稳定可直接用于图像分类等深度学习任务解决中草药样本分散、标注成本高的痛点。资源包共3个文件以html格式的说明文档和inscode可运行代码为主另含gitignore配置文件整体仅6KB轻量而聚焦便于快速阅读与复用。目前已有237人学习使用。通过说明文档可清晰掌握数据集构成、采集流程与训练测试划分细节配套源代码则帮助研究者快速理解或构建中草药分类识别基线模型缩短从数据到实验的部署路径适合刚进入该领域的研究者或开发者用于验证算法效果。 中草药识别这个方向看着简单真做起来数据坑特别多。最近我在帮朋友整理中药材图像分类的baseline正好拿到一套163种中草药数据集还带了可运行源码。这套数据对想快速上手图像分类、做中草药识别demo、或者给移动端识药功能做预研的人都很友好尤其适合那些刚接触深度学习、需要一个真实数据集练手的朋友。下面我会从数据清洗、模型训练、源码跑通到问题排查把我实际走过的流程和踩过的坑一次性说清楚给你一条可以直接参考的路线。1. 中草药识别项目的数据逻辑与方案选型1.1 为什么是163种这个规模带来的取舍163种药材覆盖了市面上常见的中药饮片和部分原植物不是什么随随便便的数字。类别太少模型学不到足够的区分度很容易过拟合训练出来的模型换个环境基本不能用类别太多比如上千类数据采集和标注成本会直线上升对算力的要求也高很多普通单卡GPU训练起来会很吃力。163类刚好处于一个不错的中间位置既能体现细粒度识别的难度又不会让训练成本高到普通人没法复现。这套数据集的另一个价值点就是标题里写的“可运行源码”。我拿到手之后最大的感受是它把很多重复劳动做完了——数据加载、训练循环、评估脚本都有现成代码你不需要从零写一个训练框架。对很多做药材算法的人来说可能他不是专职算法工程师而是研究生、药学院的学生或者想做个识别demo的爱好者能把流程先跑通比什么都重要。源码的意义就是降低这个门槛让你把精力集中在调参和业务理解上。1.2 为什么用图像分类而不是目标检测或实例分割很多人看到“识别中草药”这个需求第一反应是“图片里不是有植物吗是不是该用目标检测”这里的判断要看真实使用场景。这套数据集的图片大多是单一主体、背景相对干净比如药材饮片铺在白色桌面上或者一两片叶子占据画面主要区域这种情况下图像分类是最合适的方案。分类网络只需要输出“这张图是当归”或“这张图是黄芪”不需要把药材位置框出来训练和推理成本都低得多。如果后续要做“田间/野外中草药识别”照片里可能出现多株植物混在一起甚至药材只占画面一角那就需要检测分类两阶段方案比如先用YOLO把每个植物区域框出来再对每个区域做分类。但从baseline角度来说先用分类把类别特征学好再叠加检测模块是更稳妥的路径。我给我朋友的建议也是不要一上来就上检测先把分类网络跑通后面再加复杂度效率会高很多。2. 数据集结构与预处理细节2.1 目录结构与标签规范拿到数据以后第一步不是急着训练而是要把目录结构看清楚。目前大多数公开中草药数据集都采用“按类别分文件夹”的组织方式这套163类数据集也类似典型结构是这样的dataset/ ├── train/ │ ├── 人参/001.jpg │ ├── 人参/002.jpg │ ├── 当归/001.jpg │ └── ... ├── val/ │ └── ... └── test/ └── ...这种结构对PyTorch的ImageFolder非常友好标签就是文件夹名不需要额外维护一份标签映射表。不过这里有个容易忽略的点文件夹名字用的是中文还是拼音会直接影响后边代码的兼容性。如果文件夹名是中文在处理路径时要确保系统编码没问题同时最好额外维护一份“中文名-拉丁名-英文名”的映射表因为后面做模型输出、前端展示时经常会用到多语言名称单独建一个CSV文件管理会省很多事。如果你拿到的数据是图片加一个label.csv也不是不能用自定义Dataset读取就行。关键是要保证图片路径和标签一一对应不能出现多一张或少一张的情况。我习惯在训练前专门统计一下每个类别的图片数量先确认163个类别是不是都齐了再进入下一阶段。这个检查看起来基础但能避免后面训练到一半才发现缺了某个类别导致模型输出维度对不上的问题。2.2 数据清洗与类别均衡处理中草药数据集最大的麻烦是混入脏图片。常见的典型问题包括手机拍摄时带的水印或日期戳、不同光照条件下产生的严重色差、同一味药材被放进错误文件夹、背景过于杂乱导致药材主体占比太小。这些脏数据如果不处理任你再好的模型也训练不出理想效果。我在拿到这套数据后会先做一轮人工抽样检查每个类别随机抽5到10张图片快速浏览一遍有没有明显错标。这是个体力活但非常必要。等错标问题处理完再统计类别样本数很容易发现分布不平衡——某个类别只有30张另一个类别却有300张。针对类别不平衡我一般用三种方法一是对少样本类做更强的数据增强扩大样本量二是在DataLoader里使用WeightedRandomSampler让少样本类被采样到的概率提高三是在损失函数中按类别数量设置权重。如果只是做baseline我建议先用WeightedRandomSampler改动最小而且不会破坏原始数据分布。2.3 数据增强策略与参数建议中草药图像分类里颜色、纹理、边缘形态都是重要特征数据增强不能盲目加狠否则药材颜色失真模型学的特征就偏了。我常用的组合是随机水平翻转50%概率、随机旋转±15度、随机resized cropscale取0.8到1.0、颜色抖动brightness和contrast都取0.2再根据实际情况加一个RandomErasing模拟部分区域被遮挡的情况。需要提醒的是CutMix、MixUp这类强增强在中草药识别上要慎用。它们会把两张药材图片混合模型不得不学习更鲁棒的特征但同时会破坏药材本身精细的纹理和边缘信息反而增加训练难度。如果做细粒度识别建议只保留轻微的空间变换和颜色扰动让模型专注学习药材本身的区分性特征。3. 可运行源码的技术栈与核心实现3.1 框架选择与目录结构这套可运行源码用的是PyTorch这个选择很合理。PyTorch在图像分类领域生态最成熟动态图调试方便社区里能参考的代码也最多。源码的主要目录结构一般类似下面这样project/ ├── config.py ├── data_loader.py ├── train.py ├── predict.py ├── models/ │ └── classifier.py ├── utils/ │ └── metrics.py └── requirements.txttrain.py负责训练和验证predict.py负责单张图片推理config.py集中管理所有超参数。这种“集中配置”的设计对新手特别友好你不用在代码里到处找参数改一个地方就行。我第一次跑的时候只需要修改config.py里的数据路径和num_classes163然后运行train.py整个流程就通了。requirements.txt里固定了torch、torchvision、opencv-python、tqdm、scikit-learn等依赖版本。很多项目跑不起来就是因为torch版本和CUDA版本对不上所以强烈建议按照requirements.txt安装而不是自己装最新版。3.2 训练脚本的关键配置训练脚本的核心逻辑并不复杂但有几个参数要重点理解。下面这段是我实际跑通过的配置# config.py num_classes 163 image_size 224 batch_size 64 epochs 50 lr 1e-3 weight_decay 1e-4 model_name resnet50 pretrained True device cudaimage_size选224是ResNet系列的标准输入也是速度和精度的平衡点。batch size尽量在显存允许范围内调大我用24GB显存跑64显存小就降到32或16。学习率1e-3配合CosineAnnealingLR比固定学习率稳定很多。训练时损失函数用CrossEntropyLoss如果类别不平衡问题明显可以给loss传入类别权重。优化器我建议用AdamW在图像分类任务上表现比SGD更稳尤其是对预训练模型做微调时收敛过程更平滑。下面的表格总结了几个核心参数的作用方便你对照修改参数推荐值作用image_size224控制输入尺寸影响速度和精度batch_size32~64影响梯度稳定性和显存占用lr1e-3初始学习率太高发散太低收敛慢epochs50~100训练轮数视验证集效果决定weight_decay1e-4L2正则化缓解过拟合3.3 推理脚本与模型导出训练结束后predict.py会加载best_model.pth输入一张图片输出top-5的预测结果和置信度。推理阶段有一个细节特别重要transform必须和训练阶段保持一致尤其是标准化用的均值和方差不能漏。很多人训练时效果挺好一到了推理环节准确率暴跌大概率就是transform不对齐导致的。如果要部署到手机或边缘设备可以把模型导出成ONNX格式然后用ONNX Runtime或者TensorRT做推理加速。导出代码很简单import torch model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) model.eval() dummy torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy, herb_model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}})导出之后可以接入ONNX Runtime做CPU推理也可以在移动端用MNN或NCNN。这一步对落地很关键因为在服务器上训练完不等于能在手机或边缘盒子上跑。4. 实操过程与训练结果分析4.1 环境准备与快速验证第一次跑代码我建议不要直接全量训练而是先拿小数据子集验证流程。比如从dataset里复制两个类、每类20张图跑5个epoch确认代码能通、loss能正常下降再切换到全量数据。这样做能帮你把“代码写错了”和“模型欠拟合”两类问题分开节省大量调试时间。环境层面Python 3.8以上、PyTorch 1.10以上基本都能跑。Windows用户要特别注意路径问题数据目录建议用绝对路径而不是相对路径否则在IDE里运行和命令行运行时当前工作目录可能不一样很容易报“文件不存在”的错误。真遇到路径问题优先用os.path.join拼接路径不要手写反斜杠或正斜杠。4.2 训练过程记录我这次用ResNet50预训练权重做微调163类数据训练集每类有几十到几百张不等。实际跑下来第5个epoch后loss开始明显下降第20个epoch后准确率提升放缓最终50轮训练结束后验证集Top-1准确率大概在92%上下。这个结果对中草药识别这个粒度来说已经够用。如果你在训练过程中发现准确率一直不涨先别急着改模型优先检查数据加载是否正常。我之前遇到过数据路径配置错误导致每个batch加载到的都是同一类图片训练loss看起来正常验证集却完全崩溃。所以在训练开始后建议先打印一个batch的标签和图片形状确认数据顺序和标签分布没问题再放心跑完整训练。4.3 评估指标不只是准确率准确率是最直观的指标但在中草药识别里只看准确率会掩盖很多问题。因为不同药材的样本量不同小样本类的recall可能很低但总体准确率还是很高。所以我每次训练完都会计算每个类别的precision、recall和F1并输出混淆矩阵重点看容易混淆的类别对。混淆矩阵里最容易出现的是相似药材之间的相互误判比如薄荷和留兰香、黄芪和甘草。如果发现某对类别反复混淆单纯调模型可能效果有限更有效的办法是补充这些混淆样本的训练数据或者把几类合并成一个大类再让用户在应用里做二次确认。这个思路在真实项目里非常实用能显著改善用户体验。5. 常见问题与排查技巧实录5.1 数据加载慢或内存溢出数据加载慢首先看DataLoader的num_workers是不是设得太小机器性能允许的话可以调到8或16。Windows上还要注意num_workers设太大容易报BrokenPipeError建议不要超过8。内存溢出多半是图片分辨率太高导致可以在Dataset里先resize到256再转成tensor训练时随机裁剪到224这样能明显降低内存压力。如果GPU显存不够除了减小batch size还可以开启PyTorch的混合精度训练AMP改动很小但显存占用能降一半左右scaler torch.cuda.amp.GradScaler() with torch.autocast(device_typecuda, dtypetorch.float16): output model(images) loss criterion(output, labels) scaler.scale(loss).backward()这个技巧对精度影响很小我基本每次都会用强烈建议显存紧张的人试试。5.2 类别不均衡导致准确率偏斜如果发现某些类别准确率接近100%另外一些类别却接近0%大概率是类别不均衡造成的。我用WeightedRandomSampler后小样本类的recall会明显提升。代码也很直接from torch.utils.data import WeightedRandomSampler class_counts torch.bincount(dataset.dataset.targets) weights 1.0 / class_counts.float() sample_weights weights[dataset.dataset.targets] sampler WeightedRandomSampler(sample_weights, num_sampleslen(sample_weights), replacementTrue)需要注意的是用了sampler之后每个epoch里少样本类被采样到的概率更均匀但总样本数基本不变所以训练时间不会大幅增加效果却更稳。5.3 相似药材类别容易混淆中草药有很多同科同属植物叶片形状和颜色非常接近这是数据本身带来的难度。如果模型反复混淆某几类我建议先检查这部分训练样本是否真的存在明显差异。实在区分不了可以换用EfficientNet或ConvNeXt这类更强的模型它们在细粒度特征提取上通常比ResNet好一些。另外数据增强里的颜色抖动可能让原本就接近的类别更难区分。遇到混淆问题可以尝试关掉颜色抖动只保留空间变换看有没有改善。我试验过在某些相似类对上关掉颜色增强后F1反而涨了2到3个百分点。这种细节往往比换网络结构更有效。5.4 源码换环境跑不通的常见原因把源码从一个环境迁到另一个环境常见报错主要集中在这几类一是ModuleNotFoundError缺依赖用requirements.txt完整安装即可二是CUDA版本不匹配torch版本和显卡驱动对不上需要按官方文档重新安装对应CUDA的torch三是路径分隔符差异Windows下用反斜杠Linux下用正斜杠最稳妥的方式是统一用os.path.join四是类别数量不一致如果数据集不是163类记得同步修改num_classes参数。遇到报错不要慌先看Traceback最下面一行绝大多数问题都是环境问题而不是代码问题。把环境对齐、路径修正基本就能跑通。6. 落地到真实场景的几个建议6.1 从baseline到移动端推理的路径训练好分类模型之后离真正落地还有一段距离。移动端推理一般有两条路一是把模型导出为ONNX再用ONNX Runtime、MNN或NCNN推理二是用TensorFlow Lite转换。建议优先用ONNX因为从PyTorch导出最方便而且支持动态batch。导出之后可以做量化。我实测把模型从FP32量化到INT8后体积缩小到原来的四分之一推理速度提升2到3倍准确率只掉了1%左右。对中草药识别这种场景来说这个误差完全可接受而且换来了实时性和更小的安装包性价比很高。6.2 数据持续迭代与人工复核没有哪个数据集一次就能覆盖所有真实场景。落地后要不断收集用户拍摄的、模糊的、背光的数据并定期做人工复核。你可以记录模型预测置信度比较低的样本交给药师或相关专业人员做二次标注再把这些新样本增量加入训练集。这个流程比一次性追求更高精度更有价值因为它能让模型在真实环境中越用越准。建议给系统加一个“人工复核”的后台页面把模型置信度低于某个阈值的图片单独列出来由人工确认后入库。一个月积累下来你会发现自己手上多了一套比原始数据更贴近真实场景的私有数据集这才是真正的护城河。6.3 我的一点实操体会这套163种中草药数据集配合可运行源码最大的价值不是那个92%的准确率而是给了你一个可以复现、可以修改、可以继续迭代的起点。我个人的经验是拿到任何数据集都不要急着把模型训到极致先把数据看明白把错误样本分析透比调十几个超参数都管用。如果你正准备做中草药识别建议从这套baseline开始先把流程跑通把混淆样本梳理清楚再考虑换成更大的模型或更复杂的方案。这个顺序走下来你会发现后面所有优化工作都有据可依不会再瞎试碰运气。本文还有配套的精品资源点击获取