163种中草药数据集背后的中药识别训练全流程解析 简介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_okTrue) os.makedirs(val_dst, exist_okTrue) 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_okTrue) shutil.copy(os.path.join(cls_path, img), os.path.join(target, img)) print(划分完成验证集比例:, val_ratio)参数说明val_ratio是验证集比例中草药数据集如果每类只有几十张图建议给 0.10.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.55.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(brightness0.2, contrast0.2, saturation0.2, hue0.05), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])参数说明RandomResizedCrop的 scale 参数从 0.7 起跳避免过度裁切导致药材主体丢失hue0.05是刻意调低的值因为色调对中药识别太重要了不能大幅偏移RandomRotation(15)控制在 15 度以内因为药材形态在 90 度翻转下可能变成完全不同的东西。验证集不要用任何增强只做 Resize 和 Normalize否则验证指标失真。4.2 类别不平衡163 类里总有那么十来个类别样本特别少中草药数据集常见的不平衡问题是常见药材黄芪、枸杞、当归图片量充足稀有药材如某些冷门根茎可能只有十几张图。直接用原始分布训练模型的整体准确率还行但稀有类几乎全挂。有两个参数/策略值得优先尝试。第一个是WeightedRandomSampler按样本数的倒数给每个样本赋采样权重每个 epoch 里稀有类被抽到的概率更大。第二个是在 loss 里加类别权重比如torch.nn.CrossEntropyLoss(weightclass_weights)同样按样本数倒数计算权重让模型对稀有类分错时给予更大的梯度惩罚。我自己更偏向先说清楚一个判断标准如果你的任务是「163 类的整体识别」并且业务关注综合准确率那不做均衡也能跑如果你的任务是「每个类别都要可用」那必须做平衡处理如果稀有类只有十几张图做平衡也可能过拟合这时收集数据比调参更重要。4.3 微调参数冻结哪些层、学习率怎么分段直接决定训练时间用 ImageNet 预训练权重做迁移学习时常见做法是「冻结 backbone 早期层 微调后期层 随机初始化分类头」。具体操作是先冻结全部 backbone只训练分类头跑 5 个 epoch然后解冻部分骨干层用 1e-4 量级的低学习率训练全模型。这种分阶段微调的好处是避免了随机初始化的分类头在初期给骨干层回传过大梯度、破坏预训练特征。PyTorch 里控制是否冻结某个层很简单把该层参数的requires_grad设为False即可。model models.resnet50(pretrainedTrue) # 第一步冻结全部 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不需要硬编码换模型时也通用。pretrainedTrue会从缓存或网络加载 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_locationcpu) # 去掉分类头权重因为它的维度是 1000不是 163 state_dict {k: v for k, v in state_dict.items() if not k.startswith(fc.)} model.load_state_dict(state_dict, strictFalse)strictFalse在这里是安全的因为被删掉的关键字会跳过检查剩下的 backbone 层严格匹配。如果你的模型名字不是 resnet50把删减条件改成对应的分类层名前缀即可。5.4 翻车现场四推理时单张图识别正常批量预测小图时错误率飙升现象模型在验证集上表现不错但实际部署到移动端或批量处理小程序图片时准确率明显下降。原因通常是输入尺寸不一致。训练时用的是 224x224 的 Resize Crop但推理时如果直接喂原始大图、或压缩到很小的尺寸特征分布会偏移。解决方法是固定推理尺寸并注意缓存缩略图时不要过度压缩。如果移动端要求 96x96 输入训练时就应该把训练尺寸降到同样大小而不是训练用大图部署用小图这个雷踩过的人不在少数。6. 验证模型的真实水平混淆矩阵、难例分析和类别特征可视化模型训练完准确率数字不能代表全部。中草药识别里最该做的一件事是画混淆矩阵——它能告诉你模型到底把「谁认成了谁」。如果两类药在混淆矩阵里频繁交叉通常说明两者的形态特征确实接近比如不同根茎类药材的切片外观差异小。这时候有两个选择一是增加训练数据量让模型见过更多形态变化二是考虑在业务上合并这两类因为用户侧也可能分不清。另一个值得做的验证是「难例分析」把验证集里预测错误的图片单独导出按类别分组用肉眼快速扫一遍。你会发现错误图片往往有共同点——模糊、光线差、药材被遮挡。这些样本不是模型能单靠调参解决的它们更适合作为补充数据的依据。这类分析工作相当于给模型做一次体检比单纯看准确率数字更能说明问题。最后一个技巧是类激活图可视化。用 Grad-CAM 或同类工具把模型关注的区域画出来叠加在原图上能直观看到模型到底是看着药材本身做判断还是在看桌面纹理、包装袋颜色。我的习惯是每个类别抽 3~5 张图做类激活可视化如果发现关注的区域跑偏说明数据里混入了不该有的背景模式需要回到数据层面清理。这个验证环节虽然不在数据集的说明里但它是把模型推向可交付状态的最后一道关卡。希望这套流程和踩坑经验帮到你。本文还有配套的精品资源点击获取