简介:这是一份面向中草药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 crop(scale取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_classes=163,然后运行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 = "cuda"image_size选224是ResNet系列的标准输入,也是速度和精度的平衡点。batch size尽量在显存允许范围内调大,我用24GB显存跑64,显存小就降到32或16。学习率1e-3配合CosineAnnealingLR,比固定学习率稳定很多。训练时损失函数用CrossEntropyLoss,如果类别不平衡问题明显,可以给loss传入类别权重。优化器我建议用AdamW,在图像分类任务上表现比SGD更稳,尤其是对预训练模型做微调时,收敛过程更平滑。
下面的表格总结了几个核心参数的作用,方便你对照修改:
| 参数 | 推荐值 | 作用 |
|---|---|---|
| image_size | 224 | 控制输入尺寸,影响速度和精度 |
| batch_size | 32~64 | 影响梯度稳定性和显存占用 |
| lr | 1e-3 | 初始学习率,太高发散,太低收敛慢 |
| epochs | 50~100 | 训练轮数,视验证集效果决定 |
| weight_decay | 1e-4 | L2正则化,缓解过拟合 |
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_location="cpu")) 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_type="cuda", dtype=torch.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_samples=len(sample_weights), replacement=True)需要注意的是,用了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开始,先把流程跑通,把混淆样本梳理清楚,再考虑换成更大的模型或更复杂的方案。这个顺序走下来,你会发现后面所有优化工作都有据可依,不会再瞎试碰运气。
本文还有配套的精品资源,点击获取