
简介本资源是面向计算机视觉初学者与中药图像识别研究者的中草药叶片分类数据集专为图像分类任务设计可直接用于YOLOv5等主流模型的训练与验证。数据集包含80类常见中草药叶片图像已严格划分为train4800张和val1400张两个目录结构清晰、开箱即用配套提供类别映射JSON字典文件与可视化Python脚本支持随机加载并展示4张样本图像便于快速验证数据质量与分布。压缩包共2000个文件含1998张JPG格式高清叶片图、1个可视化py脚本含注释及1个JSON类别字典整体大小175MB适配本地快速部署与教学演示。已有118人下载学习特别适合课程设计、毕业课题或轻量级中药AI识别项目起步阶段使用省去数据采集、标注与划分等繁琐环节。 做中草药叶片识别分类这个方向最烦人的其实不是模型本身而是数据。我相信很多刚开始接触计算机视觉的朋友都有体会模型结构可以抄预训练权重可以下但一份干净、划分合理、带类别字典的数据集往往比模型更难搞定。今天分享的这个项目就是围绕这个痛点来的——一套中草药用叶片识别分类的计算机视觉数据集配套资源里面包含划分好的训练集、验证集、测试集一个类别字典文件以及一个用于数据可视化的Python脚本。适合图像分类入门、计算机视觉课程设计、以及想快速验证分类模型效果的朋友拿到手就可以直接开训不用在数据整理上耗时间。这个项目最值得借鉴的地方不是用了多高深的算法而是把“从原始照片到可训练数据集”这条路上最容易踩坑的环节都提前处理好了。接下来我把整体思路、类别字典的设计逻辑、可视化脚本的细节以及实际使用中常见的坑逐个拆开讲。1. 项目整体设计从原始图像到可以训练的干净数据1.1 数据来源与初步清洗中草药叶片的数据来源通常有几种自己拍摄需要场地和大量时间、植物标本馆数字化图像、公开数据集比如PlantVillage、PlntNet的子集、以及网络爬虫采集。这个项目基于的是公开来源的中草药叶片图像但无论数据来自哪里前期都要做一轮清洗把模糊的、重复的、带严重遮挡的图片删掉。这一步绝对不能省。我试过用未经清洗的数据直接训练分类模型结果模型学的是背景的纹理而不是叶片的形态验证集准确率看起来有90%换到真实场景直接崩到50%。叶片识别的核心特征是形状、叶脉、边缘的锯齿形态如果图像里混入大量复杂背景模型很容易偷懒去学背景特征这会严重降低泛化能力。清洗的常见做法是人工快速浏览每个类别的缩略图把明显有问题的删掉也可以写一个脚本用Laplacian方差做模糊检测把过曝或者虚焦的图自动过滤。但最终人工扫一遍缩略图还是最稳的尤其是类别数量不多的时候半小时就能搞定。1.2 数据划分策略分层抽样与目录组织数据划分是整个项目里最有参考价值的部分。很多开源数据集只给一堆按类别分好的文件夹训练集、验证集、测试集要自己分而这个项目直接划分好了而且用的是分层抽样stratified split也就是保证每个类别的图像在训练集、验证集、测试集中的比例大致相等。这里有个常见误区拿到数据直接随机划分不做分层。如果数据集中各类别样本数不均衡比如薄荷有800张而某种冷门药材只有150张随机划分可能导致某个类别的验证集只剩十几张图评估结果波动非常大模型一调参就出现过拟合假象。分层抽样能在根源上规避这个问题。我建议的划分比例是训练集70%、验证集15%、测试集15%。验证集用于调超参和早停测试集留到最终评估时再用。目录结构大概是这样的medicinal_leaf/ ├── train/ │ ├── bo_he/ │ ├── jin_yin_hua/ │ ├── gou_qi_ye/ │ └── ... ├── val/ │ ├── bo_he/ │ ├── jin_yin_hua/ │ ├── gou_qi_ye/ │ └── ... ├── test/ │ ├── bo_he/ │ ├── jin_yin_hua/ │ ├── gou_qi_ye/ │ └── ... ├── label_dict.json └── visualize.py类别目录名用拼音或英文尽量避免直接用中文目录名。原因我在后面“常见问题”会详细说——简单说就是跨平台场景下中文字符串路径非常容易出编码兼容问题。1.3 关于数据泄露的提醒数据划分还有一个容易被忽略的点数据泄露。如果原始数据是同一植株在不同角度、不同光线下的多张照片而这些照片被随机分到了训练集和测试集那测试集的结果会虚高。因为模型在训练阶段已经见过同一个体的不同视图测试时就等于开卷考试准确率参考价值大打折扣。解决思路是按个体或者按拍摄批次分组再进行划分。比如同一个植株采集到的10张照片要全部分到同一个集合里而不是散落到训练、验证、测试三个集合。如果你自己后续扩充数据尤其是用手机连拍的方式采集叶片时这个点一定要留意。2. 类别字典文件让模型和标签准确对应2.1 类别字典的格式与设计逻辑类别字典label dict是整个数据集的“翻译官”。深度学习模型不认“薄荷”“金银花”这种字符串标签它只认整数索引类别字典的作用就是建立字符串类别名到整数标签的映射关系。这个项目里的label_dict.json长这样{ 0: bo_he, 1: jin_yin_hua, 2: gou_qi_ye, 3: huang_lian, 4: dang_shen }这里使用的是字符串形式的数字作为key因为JSON本身的key必须是字符串。实际使用时有两个坑一是有一些库读进来后可以直接用int索引访问有些则不能需要手动转换二是key类型不匹配会造成莫名其妙的报错比如KeyError: 0和KeyError: 0同时出现排查起来很头大。我在代码里一般会做一层适配import json with open(label_dict.json, r, encodingutf-8) as f: label_dict json.load(f) # 字符串key转int同时生成反向映射 id_to_name {int(k): v for k, v in label_dict.items()} name_to_id {v: k for k, v in id_to_name.items()}为什么要同时保留正向和反向映射因为训练时模型输出的是整数索引需要映射回类别名做评估报告而准备数据时又需要把目录名映射为整数标签两个方向都要用到缺一个就得多写不少胶水代码。2.2 类别字典与目录结构的对应关系类别字典里的0、1、2这些序号应该和目录排序后的顺序保持一致。我习惯的做法是先对类别目录名进行排序再按排序结果生成字典这样映射关系是确定性的不会因为文件系统遍历顺序不同而发生变化。这里有一个极其关键的细节如果训练代码用os.listdir()或者glob遍历类别目录两次遍历的顺序可能不同尤其是在不同文件系统挂载方式下。如果每次运行都重新生成label_dict同一张图片在不同次运行中可能被标成不同的类别模型训练的标签分布就会乱套。因此类别字典必须提前固定下来训练代码直接读取这个固定的字典严禁每次运行时重新生成。2.3 类别数量与识别难度评估中草药叶片识别这个任务类别数量直接影响模型选型。如果类别在几十个以内用ResNet、EfficientNet这类常规分类模型就行如果各类别叶片形态高度相似比如薄荷和留兰香同属唇形科叶片大小、锯齿形态接近单靠全局特征很难区分需要考虑更高分辨率的输入或者用多尺度特征融合的方案。标签规范化的程度也会直接影响模型效果。如果“薄荷”和“留兰香”被标成同一个类模型的决策边界就模糊了反过来如果同一个类内部图像差异极大老叶、嫩叶、干叶、鲜叶都混在一起类别内方差可能比类别间方差还大分类任务的难度就上去了。拿到数据后我建议先对类别字典做一次人工核对确认每个标签对应的叶片形态是否有明确可分的视觉特征这决定了后续模型能学到什么程度的区分能力。3. Python数据可视化脚本训练前必须做的一步3.1 环境准备与依赖安装这个项目自带的visualize.py是一个用Python写的可视化脚本。可视化这一步我强烈建议放在任何训练之前因为图像分类任务失败的原因很多时候不是模型不行而是数据跟预期不一致——类别对不上、图片损坏、尺寸混乱、标签错位可视化是发现这些问题最直接的方式。脚本依赖的库非常常规就三个pip install matplotlib opencv-python numpy如果有PyTorch或者TensorFlow环境numpy通常已经装好了。matplotlib用于画图opencv-python用于图像读取和预处理numpy用于数组计算。这里有个小坑如果用conda管理环境建议直接用conda install matplotlib因为conda的matplotlib默认就带中文字体支持如果用pip安装后面画图遇到中文标签大概率要手动设置字体稍后我会详细说。3.2 脚本核心功能一随机采样样本展示可视化脚本的第一个功能是在每个类别下随机抽取若干张图像拼接成一个大网格让你一眼看出每个类别长什么样、图像数量是否充足、有没有混入错误样本。核心代码逻辑大概是这样的import os import random import matplotlib.pyplot as plt import cv2 def show_sample_grid(data_dir, samples_per_class4, grid_cols4): class_names sorted(os.listdir(data_dir)) grid_rows (len(class_names) grid_cols - 1) // grid_cols fig, axes plt.subplots( grid_rows * samples_per_class, grid_cols, figsize(grid_cols * 3, grid_rows * samples_per_class * 3) ) axes axes.flatten() idx 0 for class_name in class_names: class_path os.path.join(data_dir, class_name) images os.listdir(class_path) selected random.sample(images, min(samples_per_class, len(images))) for img_name in selected: img cv2.imread(os.path.join(class_path, img_name)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) axes[idx].imshow(img) axes[idx].set_title(f{class_name}\n{img_name[:10]}) axes[idx].axis(off) idx 1 plt.tight_layout() plt.savefig(sample_grid.png, dpi150) plt.show()这里有两个细节特别值得注意。第一cv2.imread读进来是BGR通道顺序直接用matplotlib显示颜色会明显偏蓝偏红必须先通过cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转换。这是初学者最容易犯的错误很多网上教程都漏了这一步导致可视化颜色失真。第二如果某个类别的图像数量小于samples_per_class要用min(samples_per_class, len(images))兜底否则random.sample会抛ValueError。3.3 脚本核心功能二类别分布统计与可视化第二个功能是统计每个类别的图像数量并画条形图展示。别小看这一步它能直接暴露数据不均衡问题。代码逻辑如下def plot_class_distribution(data_dir): class_names sorted(os.listdir(data_dir)) counts [len(os.listdir(os.path.join(data_dir, c))) for c in class_names] fig, ax plt.subplots(figsize(10, 5)) ax.bar(range(len(class_names)), counts, tick_labelclass_names) ax.set_title(Per-class Sample Count) ax.set_xlabel(Class) ax.set_ylabel(Count) ax.tick_params(axisx, rotation45) plt.tight_layout() plt.savefig(class_distribution.png, dpi150) plt.show() total sum(counts) print(fTotal samples: {total}) for name, count in zip(class_names, counts): ratio count / total * 100 print(f{name}: {count} ({ratio:.1f}%))饼图在类别多的时候标签会严重重叠我一般只看条形图和打印出来的统计信息饼图更适合给不参与项目的人看整体占比。如果类别超过15个建议直接用横向条形图信息密度远高于纵向条形图标签也不会互相挤压。3.4 脚本核心功能三图像尺寸分布与异常检查第三个功能是检查图像尺寸和质量这一步很容易被忽略。深度学习框架里的ImageFolder会自动把图像缩放到统一尺寸所以很多人不看原始尺寸但原始尺寸的差异直接影响预处理策略。如果大部分图像是1000x1000以上的大图而个别图只有200x150统一缩放后大图里的细节会大量丢失小图则会被拉伸变形。如果图像长宽比差异极大直接resize成正方形会造成几何形变这种情况下用随机裁剪的增强策略会温和很多。检查代码可以参考def check_image_sizes(data_dir, sample_limit1000): import numpy as np size_stats [] checked 0 for class_name in sorted(os.listdir(data_dir)): class_path os.path.join(data_dir, class_name) for img_name in os.listdir(class_path): if checked sample_limit: break img_path os.path.join(class_path, img_name) img cv2.imread(img_path) if img is None: print(f[WARN] cannot read: {img_path}) continue h, w img.shape[:2] size_stats.append((w, h)) checked 1 sizes np.array(size_stats) print(fChecked count: {len(sizes)}) print(fWidth - min: {sizes[:, 0].min()}, max: {sizes[:, 0].max()}, mean: {sizes[:, 0].mean():.0f}) print(fHeight - min: {sizes[:, 1].min()}, max: {sizes[:, 1].max()}, mean: {sizes[:, 1].mean():.0f})还有一点需要留意cv2.imread读不出来的图片返回None如果打印出WARN说明有图片文件损坏或者路径中包含特殊字符这类图在训练时会导致DataLoader直接报错。我建议遍历所有图片做一次全量检查把有问题的找出来删掉这个检查虽然简单但能省下后续排查的不少时间。3.5 matplotlib中文显示问题的处理用脚本画图时如果图表的标题、坐标轴标签用了中文matplotlib默认字体不支持中文会全部显示成方框。这个问题有两个解决办法。方法一是显式设置中文字体import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False方法二是图表里不用中文直接用拼音或者英文标签。这也是前面说类别目录用拼音命名的原因之一可视化时直接拿目录名当标签全流程不需要担心中文字体。我个人的建议是两者结合代码里把字体配置写进去同时标签使用拼音。这样即使换到没有中文字体的Linux服务器上图表也不会乱码。3.6 训练集和验证集的可视化对比可视化脚本还可以做一个更高级的检查分别对训练集和验证集调用类别分布统计函数把两张图并排对比。如果两个分布的形态差异很大比如训练集中某个类别占30%验证集中同一类别只占5%那就说明划分质量有问题或者原始数据的采集本身有偏差。具体做法是把plot_class_distribution函数分别对train和val调用再用matplotlib的subplot并排显示即可。这一步的价值在于你能在训练开始前就发现划分问题而不是等到训练完、验证集准确率上不去才回头排查数据。数据问题越早发现时间成本越低。4. 常见问题与排查技巧实录4.1 路径或文件名中的中文编码问题前面反复提到中文路径问题这里展开说一下。Windows默认编码是GBKLinux和macOS是UTF-8如果数据路径或文件名带中文在A机器上能正常读取拷到B机器上可能直接乱码。即便在同一台机器上不同Python库对中文文件名的兼容性也不同比如opencv读取中文路径时在Windows上就容易失败而PIL的兼容性稍好一些这就导致代码在本地能跑换个环境就崩。如果实在无法避开中文路径有一个临时方案用PIL读取图像或者把中文路径转成ASCII别名副本再读取。但最省心的做法还是从一开始就用拼音或英文命名跨平台拷贝、打包、部署都不会出问题。4.2 数据不均衡带来的训练偏差如果数据集里各类别样本数差距明显直接训练分类模型会出现“多数类主导”的问题模型会更倾向于把不确定的样本预测为样本数多的类别以此来压低整体loss。这种情况下总体准确率不低但少数类的召回率可能极低。应对方法按优先级排序如下数据增强对少数类做更强的增强比如随机旋转、翻转、色彩抖动、随机裁剪。类别加权在损失函数里按类别样本数的倒数分配权重让少数类的loss贡献增大。过采样或欠采样对少数类重复采样对多数类随机丢弃。这个方法简单粗暴但过采样容易导致模型在少数类上过拟合属于下策。可视化脚本会输出每个类别的样本数和占比目的就是提示你先看这个再决定要不要做均衡处理。我实测下来用类别加权配合适度增强是性价比最高的方案。4.3 标签错位最隐蔽的错误标签错位这个问题我单独拎出来说因为它最隐蔽。如果类别字典和目录顺序的对应关系处理不好会出现模型训练时准确率很高但实际预测结果完全不对的情况。比如类别字典里0号是薄荷但因为目录遍历顺序不稳定0号实际对应的图片是金银花模型学到的所有特征和标签都对不上。排查方法很简单随机抽几张训练图像用可视化脚本打印出图像和对应的标签名进行人工确认。这个检查在训练前做一次能省掉后面半天排查混乱的时间。标签错位还有一个容易忽略的场景在Windows上文件管理器排序会忽略大小写但Python的sorted()是区分大小写的。如果类别目录有大写有小写比如BoHe和bo_he排序结果在不同平台上可能不同。统一用小写命名就能规避这个问题。4.4 图片损坏或格式异常数据集里偶尔会混入无法解码的图片比如扩展名是.jpg但实际内容损坏或者携带着错误的色彩空间信息。训练过程中DataLoader会突然报错提示“image file is truncated”或者“Found 0 files”就是这类文件导致的。建议用3.4节的check_image_sizes做一轮全量检查把WARN的文件列出来然后批量删除或重新下载。这个检查虽然简单但能避免训练中途崩溃中断。4.5 可视化脚本自身的性能问题如果数据集有几十个类别、每个类别上千张图用可视化脚本展示样本网格时逐个读图会非常慢而且一张图里堆太多小图也看不出细节。我踩过这个坑之后现在的做法是每类抽样不超过6张图太大就分多个网格保存。检查尺寸分布时不要遍历全部图像抽500到1000张就足够看出规律了速度会快很多。5. 从数据到模型这个数据集怎么用起来5.1 与PyTorch的ImageFolder无缝对接目录结构划分好之后配合类别字典这个数据集可以直接和PyTorch的torchvision.datasets.ImageFolder对接使用方式非常标准from torchvision import datasets, transforms transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(medicinal_leaf/train, transformtransform) val_dataset datasets.ImageFolder(medicinal_leaf/val, transformtransform) # 打印自动生成的类别映射和label_dict.json对比 print(train_dataset.class_to_idx)有一个细节ImageFolder会按目录顺序自动生成class_to_idx映射这个生成方式依赖os.listdir的排序结果如果你的label_dict.json是另一种顺序两者就会对不上。拿到数据集后第一件事应该是打印class_to_idx和label_dict做对比确认一致再进入训练流程。5.2 一个最简训练流程参考如果想快速跑通这个数据集我的建议是读取label_dict.json得到id_to_name和name_to_id映射。用ImageFolder加载train、val、test三个集合。用torchvision里的ResNet18或者EfficientNet-B0做迁移学习加载ImageNet预训练权重把最后一层全连接改成类别数。训练时用交叉熵损失SGD或AdamW优化器初始学习率1e-3训练20到30个epoch。每个epoch结束在验证集上评估并记录准确率用验证集上表现最好的模型权重做测试集评估。这个流程在几十个类别的叶片识别任务上通常能跑到85%到95%的准确率。实测下来如果准确率明显低于这个区间问题大概率出在数据侧优先检查数据而不是急着换模型。5.3 数据增强的推荐配置叶片识别这个任务数据增强应该围绕叶片形态特征来设计。我常用的配置是随机水平翻转和随机垂直翻转利用叶片无方向性的特点。随机旋转正负30度。随机缩放裁剪scale设置在0.7到1.0之间。颜色抖动包括亮度、对比度、饱和度的微调。有一个注意点如果后续要利用颜色特征做诊断比如判断叶片枯黄程度、是否有病斑那么颜色抖动幅度不能太大否则会破坏重要特征。增强强度需要配合任务需求来调不是越强越好。5.4 结果评估与错误分析训练完之后除了看总准确率我强烈建议做一个混淆矩阵和错误样本分析。选几个验证集里预测错误的样本把原图、真实标签、预测标签打印出来很多时候你会发现错误是有规律的。比如“薄荷”和“留兰香”总是混淆说明这两个类别的视觉区分度不够可能是标注本身有歧义也可能是图像中叶片占比太小、背景干扰严重。这些分析虽然不在数据集自带的脚本里但可视化脚本的思想可以复用把展示样本网格的函数改成展示错误样本的函数即可。我做了几年图像分类项目一个深刻的体会是模型架构和训练技巧的收益是有上限的数据的质量和组织方式决定了这个上限的起点。这个中草药叶片识别分类数据集把目录结构、类别字典、可视化脚本这些基础工作做到位了你在上面做模型实验时才能真正把精力放在算法迭代上。尤其是在课程设计、毕业设计或快速原型验证这种场景下能省下大量数据整理的时间。如果你自己也在整理图像分类数据集不妨参考这套结构分层划分、固定类别字典、可视化先行这套组合拳能减少很多返工。本文还有配套的精品资源点击获取