简介:163种中草药数据集Chinese-Medicine-163的说明与可运行源码包,面向机器学习、深度学习方向的中草药图像识别研究者。该资源系统介绍了一个包含超过25万张图片、覆盖163种中药材的数据集,明确了训练集约每类1575张、测试集约每类61张的划分细节,数据来自网络爬虫并经人工清洗,可直接用于分类识别算法的训练与评估。资源包共3个文件,以HTML说明文档、InsCode可运行源码和Git配置文件为主,整体仅6KB,轻量而完整地提供了数据集获取方式、参考文章及代码运行入口。已有251人学习下载,适合需要快速了解中草药AI识别数据规模、搭建实验环境的入门开发者。通过该资源可获取到数据集的下载地址和深度学习分类识别的推荐实践,能节省前期资料调研与数据准备的时间。
1. 163 种中草药数据集:不只是图片堆叠,而是一套能直接落地的中药识别训练方案
做中药识别方向的人,多数都卡在同一个地方:网上散落的中草药图片不少,但要么类别太杂、标注混乱,要么图片带水印、带花盆、带手指,训出来的模型一换背景就翻车。这个 163 种中草药数据集说明[可运行源码]项目,解决的正是「有图但没法直接训练」的问题——它把 163 个常见中草药类别整理成统一格式,同时附带一套可运行的分类训练代码。换句话说,你拿到的不只是数据,而是从原始图片到训练脚本、再到模型验证的一整条流水线。这篇文章我从数据组织、源码结构、训练参数、踩坑记录到验证技巧,按一线落地顺序拆开讲。
2. 数据集背后的事:163 类怎么划分、图片形态和标注组织是训练的前提
2.1 163 类的划分逻辑:按中药名还是按入药部位,直接影响模型能学到什么
拿到数据集先别急着解压训练,先想清楚一个问题:这 163 类是按什么维度划分的。常见做法有两种——按药材名划分(如「黄芪」「当归」),按入药部位划分(如「根茎类」「花叶类」)。前者是主流,也是这套数据集大概率采用的方案。按药材名划分时,模型学到的是「这个外形特征对应哪种药」,比如黄芪的切片纹理、枸杞的红色椭球形态。这种划分对做药材识别、中药柜自动抓药、药品溯源这类场景最直接,因为实际需求就是「你给我看一张图,告诉我这是什么药」。
但这里有个容易被忽略的点:同名药材在不同炮制状态下的外观差异巨大。举个例子,生地黄是棕褐色块状,熟地黄是乌黑色软块,如果不做区分,模型很容易被这个「同类不同形」搞晕。好一点的数据集会单独告诉你在 163 类里是否包含炮制品类别,或者同一药材名下是否混入多种形态。我的建议是:拿到数据后先按类别名列表过一遍,把形态差异大的类目标记出来,后续训练时重点看它的准确率和混淆情况。
2.2 图片的采集质量与背景干扰:为什么说「真实场景图」比「白底图」更值钱
中草药数据集的图片来源一般有两类:一类是标本馆或图库的白底专业图,一类是野外或药房实拍图。白底图干净、识别容易,但有明显的落地缺陷——模型会把「白色背景」学进特征里,遇到真实药房桌面的纹理就失效。实拍图的背景复杂,有桌面、手、塑料袋、其他药材,训练难度更高,但泛化能力更好。一套合格的中草药数据集应该是以实拍图为主、白底图为辅,或者明确标注图片来源,让你在训练时能做出取舍。
从工程视角讲,我通常会建议:先不做任何清洗,直接用原始分布训一版基线模型;然后用训练集里 loss 最高的样本做硬样本挖掘,看看模型最难分的是什么。这套数据集的说明里如果能给到图片来源分布,会极大缩短你做这个分析的时间。如果没有给,就自己抽样统计一下,这也花不了多少时间。
2.3 数据集的目录组织:ImageFolder 结构是大多数训练代码的默认前提
不管这个数据集实际打包成什么格式,训练代码能跑通的前提永远是目录结构。PyTorch 的 ImageFolder 是最常见的默认方式,它的要求很朴素:根目录下按类别分子文件夹,文件夹名就是类别名,里面直接放图片。如果数据集解压后是这种结构,恭喜你,大部分源码可以直接跑。如果解压出来是一个 CSV 标注文件加一个大杂烩图片目录,那说明数据组织方式不是为 ImageFolder 准备的,可能需要自己在加载器里按标注文件读路径。
让我把目录结构的事说透。这里有两种常见形态:
# 形态 A:按 ImageFolder 组织(推荐) data/ train/ 黄芪/ img_001.jpg img_002.jpg 当归/ img_001.jpg val/ 黄芪/ img_101.jpg # 形态 B:扁平图片 + 标注 CSV data/ images/ img_0001.jpg labels.csv提示:拿到数据集先确认是形态 A 还是形态 B。形态 A 直接写 data_dir 参数开训;形态 B 要先写一个划分脚本,否则训练代码会报「Found 0 images in subfolders」之类的路径错误。
处理形态 B 的常见做法是写一个转换脚本,把 CSV 标注转成 ImageFolder 目录。我一般会这么做:读 CSV 得到图片路径和类别名,按类别建立子目录,把图片复制过去,再额外做一次训练集和验证集的随机划分。这一步虽然机械,但是值得做——它把数据的物理组织方式统一了,后面换模型、换框架都不用再改数据层。
3. 把数据跑起来:从目录校验到源码运行的最小步骤
3.1 第一步先把数据读进来:校验图片完整性、类别数量、样本分布
很多人拿到数据集直接开训,跑到一半报错说某张图损坏,或者某个类别一张验证图都没有,这种体验很伤。我的习惯是先写一段十几行的检查脚本,把三件事一次做完:图片能不能被解码、类别数量是不是 163、每个类别的样本数。这个脚本不分框架,用纯 Python 加 PIL 就能做。
import os from PIL import Image from collections import Counter data_root = "./data/train" # 改成你的实际路径 categories = [] broken = [] counts = Counter() for cls_name in sorted(os.listdir(data_root)): cls_path = os.path.join(data_root, cls_name) if not os.path.isdir(cls_path): continue categories.append(cls_name) imgs = [f for f in os.listdir(cls_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))] counts[cls_name] = len(imgs) for img_name in imgs: try: with Image.open(os.path.join(cls_path, img_name)) as im: im.verify() except Exception: broken.append(os.path.join(cls_name, img_name)) print(f"类别总数: {len(categories)}") print(f"图片总数: {sum(counts.values())}") print(f"每个类别平均样本数: {sum(counts.values()) / len(categories):.1f}") print(f"最少样本类别: {counts.most_common()[-1]}") print(f"损坏图片数: {len(broken)}")这段脚本的逻辑分三层:先遍历根目录找出所有类别子目录;再统计每个类别的图片文件数,顺便筛出非 jpg/png 的异常格式;最后用Image.verify()打开图片并做完整性检查,不把损坏图片留到训练时才暴露。参数上唯一需要你改的就是data_root指向实际路径。一个值得注意的细节是,verify()只检查文件头部和解码信息,不会完整解码像素数据,所以速度快,适合全量扫描。如果发现某类样本数特别少(比如个位数),后续训练就要考虑类别均衡策略,否则这类直接学不好。
3.2 训练集与验证集的划分:随机划分不够,还要按类别分层
数据集如果只给了你一个 train 目录而没有 val,就需要自己划分。中草药场景下有个特殊性:同一批药材可能在同一背景下连续拍摄,如果纯随机划分,模型可能通过「背景纹理」而不是「药材本身」做识别,验证集正确率高但实际部署很假。更稳的做法是按类别分层划分,再在类别内部适当打乱。
import os import random import shutil source = "./data/all" train_dst = "./data/train" val_dst = "./data/val" val_ratio = 0.15 random.seed(42) os.makedirs(train_dst, exist_ok=True) os.makedirs(val_dst, exist_ok=True) for cls_name in os.listdir(source): cls_path = os.path.join(source, cls_name) if not os.path.isdir(cls_path): continue imgs = os.listdir(cls_path) random.shuffle(imgs) val_count = int(len(imgs) * val_ratio) for i, img in enumerate(imgs): dst_dir = val_dst if i < val_count else train_dst target = os.path.join(dst_dir, cls_name) os.makedirs(target, exist_ok=True) shutil.copy(os.path.join(cls_path, img), os.path.join(target, img)) print("划分完成,验证集比例:", val_ratio)参数说明:val_ratio是验证集比例,中草药数据集如果每类只有几十张图,建议给 0.1~0.15 就够了,给太多验证集训练数据会不够用。random.seed(42)的作用是让每次运行得到相同的划分结果,这样在调整模型参数时能保证训练集和验证集完全一致,比较结果才公平。
提示:划分时用
copy而不是move,保留原始数据。这样后续发现划分不合理还能重新来,算是给自己留一颗后悔药。
3.3 跑通最小训练命令:参数怎么设、日志看什么
数据就绪后,就进入核心环节:跑训练。假设源码入口是train.py,典型的调用长这样:
python train.py \ --data_dir ./data \ -- model_name resnet50 \ --epochs 30 \ --batch_size 32 \ --lr 1e-3 \ --num_classes 163 \ --pretrained True逻辑说明:--data_dir指向包含 train 和 val 子目录的根目录,代码内部用 ImageFolder 自动加载;--model_name是骨干网络,中草药识别第一版用 resnet50 是稳妥选择,兼顾精度与显存消耗;--epochs 30是预热轮数,配合验证集准确率做早停;--batch_size 32适用于 8~11GB 显存的卡,如果显存小就降到 16;--lr 1e-3是预训练权重微调时比较安全的初始学习率;--num_classes 163必须与数据实际类别数一致,不一致时模型最后一层全连接会报维度错误。
训练跑起来之后你要盯两件事:第一,loss 从首轮的 4.5~5.5 缓慢下降说明学习率合理,如果 loss 直接震荡不降,先调低学习率;第二,验证集准确率如果到第 10 轮还在明显上涨,说明还没收敛,可以等一等。多数中草药识别任务,30 轮内能到一个相对可用的水平。
4. 把模型调成可用状态:数据增强、类别不平衡和迁移学习的三个关键参数
4.1 数据增强怎么配:旋转、翻转、色彩抖动怎么选才不会伤到中药特征
数据增强是中药识别里最需要小心的一块。中草药的识别特征有时是颜色(如红花的红色花丝)、有时是纹理(如黄芪切片的放射状纹理)、有时是形状(如枸杞的椭球形态)。增强策略如果一刀切,很容易把关键辨识信息毁掉。比如对颜色类药材做高强度色彩抖动,红花可能变成紫花,模型自然学歪。
我通常使用一组带「克制」的增强参数:
train_transforms = transforms.Compose([ transforms.RandomResizedCrop(224, scale=(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.05), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])参数说明:RandomResizedCrop的 scale 参数从 0.7 起跳,避免过度裁切导致药材主体丢失;hue=0.05是刻意调低的值,因为色调对中药识别太重要了,不能大幅偏移;RandomRotation(15)控制在 15 度以内,因为药材形态在 90 度翻转下可能变成完全不同的东西。验证集不要用任何增强,只做 Resize 和 Normalize,否则验证指标失真。
4.2 类别不平衡:163 类里总有那么十来个类别样本特别少
中草药数据集常见的不平衡问题是:常见药材(黄芪、枸杞、当归)图片量充足,稀有药材(如某些冷门根茎)可能只有十几张图。直接用原始分布训练,模型的整体准确率还行,但稀有类几乎全挂。有两个参数/策略值得优先尝试。
第一个是WeightedRandomSampler,按样本数的倒数给每个样本赋采样权重,每个 epoch 里稀有类被抽到的概率更大。第二个是在 loss 里加类别权重,比如torch.nn.CrossEntropyLoss(weight=class_weights),同样按样本数倒数计算权重,让模型对稀有类分错时给予更大的梯度惩罚。
我自己更偏向先说清楚一个判断标准:如果你的任务是「163 类的整体识别」并且业务关注综合准确率,那不做均衡也能跑;如果你的任务是「每个类别都要可用」,那必须做平衡处理;如果稀有类只有十几张图,做平衡也可能过拟合,这时收集数据比调参更重要。
4.3 微调参数:冻结哪些层、学习率怎么分段,直接决定训练时间
用 ImageNet 预训练权重做迁移学习时,常见做法是「冻结 backbone 早期层 + 微调后期层 + 随机初始化分类头」。具体操作是:先冻结全部 backbone,只训练分类头跑 5 个 epoch;然后解冻部分骨干层,用 1e-4 量级的低学习率训练全模型。这种分阶段微调的好处是避免了随机初始化的分类头在初期给骨干层回传过大梯度、破坏预训练特征。
PyTorch 里控制是否冻结某个层很简单:把该层参数的requires_grad设为False即可。
model = models.resnet50(pretrained=True) # 第一步:冻结全部 backbone,只训练分类头 for param in model.parameters(): param.requires_grad = False model.fc = nn.Linear(model.fc.in_features, 163) for param in model.fc.parameters(): param.requires_grad = True # 第二步(5 轮后用):解冻最后两个 block 继续微调 for name, param in model.named_parameters(): if "layer4" in name or "layer3" in name: param.requires_grad = True这里model.fc.in_features是 PyTorch resnet50 自带的属性,表示全连接层输入维度(2048),不需要硬编码,换模型时也通用。pretrained=True会从缓存或网络加载 ImageNet 权重,如果机上没有缓存会自动下载,注意磁盘空间和网络环境。
提示:如果机器的显存比较大(比如 24GB 以上),没必要解冻部分层,直接全量微调加低学习率效果更省事。冻结策略主要用于显存不足或数据量少、怕过拟合的情况。
5. 中草药数据集训练避坑指南:四个典型翻车现场与排查方法
5.1 翻车现场一:某几个类别准确率是 0,但整体准确率看着不错
现象:训练日志里整体准确率在 70% 以上,但按类别看,总有七八个类别识别率是 0%,预测结果完全随机。原因基本出在样本量上——这些类别可能只有个位数图片,模型在训练中根本没机会学到稳定特征,甚至验证集也可能包含在训练集里没被正确划分出去。解决方法是先按类别统计样本数,把少于 20 张图的类别列为高风险类;然后考虑用采样器或类别权重做均衡;如果条件允许,优先补充这些类别的图片。
5.2 翻车现场二:训练 loss 正常下降,验证集准确率卡在 30% 上不去
现象:loss 从 4 点几一路降到 0.5 左右,但验证准确率始终在 30% 上下浮动,不涨了。这种「训练集拟合、验证集不认」的典型原因是数据划分出了问题。中草药照片经常是同批药材在同样光线下连拍的,如果这些连拍图被同时分进训练集和验证集,模型记住的是拍摄环境;如果恰好被切分到互不重叠的集合,模型看到陌生的拍摄环境就失效。排查方法是抽样看验证集图片,确认是否与训练集来自同一批次、同一背景。解决方法是按「拍摄批次」做分组划分,让同一批次的图片全部落在训练集或验证集,而不是按文件随机划分。
5.3 翻车现场三:预训练权重加载报错,size mismatch 提示 1000 vs 163
现象:代码在加载模型时直接中断,日志提示 fc 层权重尺寸不匹配,1000 对不上 163。这个错误原因非常明确——ImageNet 预训练模型的分类头是 1000 类,而你的任务是 163 类。解决方法几乎所有人都会,就是先替换分类头再加载权重,或者加载 backbone 权重后跳过 fc 层。
state_dict = torch.load("resnet50_weights.pth", map_location="cpu") # 去掉分类头权重,因为它的维度是 1000,不是 163 state_dict = {k: v for k, v in state_dict.items() if not k.startswith("fc.")} model.load_state_dict(state_dict, strict=False)strict=False在这里是安全的,因为被删掉的关键字会跳过检查,剩下的 backbone 层严格匹配。如果你的模型名字不是 resnet50,把删减条件改成对应的分类层名前缀即可。
5.4 翻车现场四:推理时单张图识别正常,批量预测小图时错误率飙升
现象:模型在验证集上表现不错,但实际部署到移动端或批量处理小程序图片时,准确率明显下降。原因通常是输入尺寸不一致。训练时用的是 224x224 的 Resize + Crop,但推理时如果直接喂原始大图、或压缩到很小的尺寸,特征分布会偏移。解决方法是固定推理尺寸,并注意缓存缩略图时不要过度压缩。如果移动端要求 96x96 输入,训练时就应该把训练尺寸降到同样大小,而不是训练用大图部署用小图,这个雷踩过的人不在少数。
6. 验证模型的真实水平:混淆矩阵、难例分析和类别特征可视化
模型训练完,准确率数字不能代表全部。中草药识别里最该做的一件事是画混淆矩阵——它能告诉你模型到底把「谁认成了谁」。如果两类药在混淆矩阵里频繁交叉,通常说明两者的形态特征确实接近,比如不同根茎类药材的切片外观差异小。这时候有两个选择:一是增加训练数据量,让模型见过更多形态变化;二是考虑在业务上合并这两类,因为用户侧也可能分不清。
另一个值得做的验证是「难例分析」:把验证集里预测错误的图片单独导出,按类别分组,用肉眼快速扫一遍。你会发现错误图片往往有共同点——模糊、光线差、药材被遮挡。这些样本不是模型能单靠调参解决的,它们更适合作为补充数据的依据。这类分析工作相当于给模型做一次体检,比单纯看准确率数字更能说明问题。
最后一个技巧是类激活图可视化。用 Grad-CAM 或同类工具把模型关注的区域画出来,叠加在原图上,能直观看到:模型到底是看着药材本身做判断,还是在看桌面纹理、包装袋颜色。我的习惯是每个类别抽 3~5 张图做类激活可视化,如果发现关注的区域跑偏,说明数据里混入了不该有的背景模式,需要回到数据层面清理。这个验证环节虽然不在数据集的说明里,但它是把模型推向可交付状态的最后一道关卡。希望这套流程和踩坑经验帮到你。
本文还有配套的精品资源,点击获取