news 2026/9/28 22:32:02

果蔬分类数据集实战:36类4200张图像分类训练与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
果蔬分类数据集实战:36类4200张图像分类训练与避坑指南

简介:本资源为常见果蔬多类别图像分类数据集,面向从事图像分类、分割网络改进及计算机视觉项目实践的学习者与开发者,可用于模型训练、算法验证与课程实验。数据集共36类,涵盖香蕉、苹果、梨、葡萄、橙子、黄瓜、胡萝卜、辣椒、洋葱、土豆等常见果蔬,已完成标注与预处理,可直接作为分类网络输入。压缩包为7z格式,内含约2000个文件,以1998张jpg图像为主体,另附1个py脚本与1个json标注文件,整体约364.87MB,并已划分训练集、测试集与验证集,各类图片分目录存放,便于直接读取。资源中提供show脚本,可快速可视化数据分布与样本情况,帮助检查类别均衡与图像质量。目前已有119人学习下载,适合需要快速搭建果蔬分类基线、开展迁移学习或改进分割网络的读者参考使用。

1. 果蔬分类数据集怎么选:36 类、4200 张、已划分的现成货

做图像分类项目,最耗时的往往不是搭网络,而是找一批干净、标注到位、类别均衡的数据。这份常见果蔬多类别图像分类数据集,约 4200 张图,覆盖 36 个类别,香蕉、苹果、梨、葡萄、橙子、黄瓜、胡萝卜、辣椒、洋葱、土豆这些日常果蔬都在里面,具体类别清单在资源包的 json 文件里能查到。它最大的价值是省掉了从零采集和标注的环节,训练集、验证集、测试集已经按同一类图片分目录放好,预处理也做完了,直接就能喂给分类网络。适合谁?刚入门图像分类想跑通第一个多类别实验的人,以及需要快速验证 backbone 或数据增强策略的熟手。下面从目录结构、加载方式、训练配置到踩坑,一步步拆开讲。

2. 目录结构与数据加载:先看清 json 和三个 split 怎么对应

拿到一个数据集,我习惯先不写模型,先把目录和标注文件摸清楚。这份资源的组织方式对分类任务很友好,但如果不先确认类别索引和文件夹名的映射关系,后面训练出来的 label 很可能整体错位,这种翻车在分类项目里太常见了。

2.1 目录布局与 json 类别映射

典型的结构是这样:根目录下分 train、val、test 三个文件夹,每个文件夹里再按类别名建子目录,图片直接放在对应类别目录下。json 文件里存的是类别列表,顺序就是类别索引 0 到 35。常见做法是用os.listdir拿到子目录名后,一定要和 json 里的顺序对齐,而不是想当然地认为字母序就是索引序。

import os import json data_root = "fruit_veg_dataset" with open(os.path.join(data_root, "classes.json"), "r", encoding="utf-8") as f: class_list = json.load(f) # 形如 ["banana", "apple", ...],顺序即标签索引 for split in ["train", "val", "test"]: split_dir = os.path.join(data_root, split) folders = sorted(os.listdir(split_dir)) print(split, len(folders), folders[:5]) # 关键:确认 folders 的顺序与 class_list 是否一致 assert set(folders) == set(class_list), "文件夹名与 json 类别不匹配"

这段代码先读 json 拿到权威类别顺序,再遍历三个 split 的文件夹做集合比对。assert那行是后悔药,一旦文件夹名和 json 对不上会立刻报错,而不是等到训练完发现准确率异常才回头查。参数上,encoding="utf-8"不能省,类别名里如果有中文或特殊字符,默认编码会读乱。

2.2 用 ImageFolder 还是自定义 Dataset

如果目录结构就是标准的「split/类别名/图片」,PyTorch 的ImageFolder能直接用,配合transforms做预处理。但这份数据已经预处理过,尺寸大概率统一,所以增强策略要克制,别一上来就上重度裁剪。

from torchvision import datasets, transforms train_tf = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p=0.5), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) train_set = datasets.ImageFolder( root=os.path.join(data_root, "train"), transform=train_tf ) print(train_set.class_to_idx) # 确认索引映射

Resize到 224 是给 ResNet、EfficientNet 这类 backbone 用的,如果你换别的输入尺寸,这里要同步改。Normalize用的是 ImageNet 统计量,这是迁移学习的常规操作。class_to_idx打印出来务必和 json 对一遍,ImageFolder默认按文件夹名字母序排索引,和 json 顺序不一定相同,这是最容易埋雷的地方。

2.3 可视化脚本 show 的用法

资源里带了 show 脚本,用来快速看一批图长什么样。跑之前先确认它依赖的库装了没有,常见是 matplotlib 和 PIL。可视化不是为了好看,是为了在训练前发现脏图、错标、重复图。我一般会随机抽每个类别若干张拼成网格,重点看有没有把胡萝卜标成辣椒这种低级错误。如果 show 脚本读的是固定路径,记得把路径参数改成你解压后的实际目录,否则会报文件找不到。

3. 训练配置与迁移学习:36 类分类的 backbone 和超参怎么定

数据摸清楚了,接下来是把它跑起来。36 类、4200 张,平均每类一百多张,属于小样本多分类,直接从头训容易过拟合,迁移学习是更稳的选择。

3.1 backbone 选型与冻结策略

小数据集上,ResNet18 或 ResNet50 预训练权重是性价比很高的起点。做法是先冻结主干,只训最后的全连接层几个 epoch,让分类头先适应新类别,再解冻整体微调。这样收敛快,也不容易把预训练学到的特征一开始就冲垮。

import torch import torch.nn as nn from torchvision import models model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) for param in model.parameters(): param.requires_grad = False # 先冻结主干 model.fc = nn.Linear(model.fc.in_features, 36) # 替换为 36 类 criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-3)

requires_grad = False冻结主干,model.fc换成 36 维输出。第一阶段学习率可以给 1e-3,因为只训分类头。第二阶段解冻后,学习率要降到 1e-4 甚至更低,否则预训练权重会被大步长破坏。这个两段式策略是我在多个小数据集上验证过的稳妥打法。

3.2 数据增强与类别不均衡处理

4200 张分 36 类,类别之间数量大概率不完全均衡。先统计每类样本数,差距大的类别要在采样或损失上做补偿。增强方面,水平翻转、轻微颜色抖动够用了,别上随机裁剪加旋转的组合拳,预处理过的图经不起折腾。

from collections import Counter targets = [s[1] for s in train_set.samples] counts = Counter(targets) print(counts) # 看每类样本数 # 若明显不均衡,用带权重的采样器 from torch.utils.data import WeightedRandomSampler weights = [1.0 / counts[t] for t in targets] sampler = WeightedRandomSampler(weights, num_samples=len(weights), replacement=True)

Counter统计每类数量,WeightedRandomSampler让少样本类别被抽到的概率更高。replacement=True表示有放回采样,这是处理不均衡的标准做法。如果类别还算均衡,这一步可以跳过,别为了用而用。

3.3 训练循环与验证指标

训练循环里,每个 epoch 结束在验证集上算准确率,保存验证集表现最好的权重,而不是最后一个 epoch 的。测试集只在最后评估一次,中途反复看测试集等于变相泄露。

best_acc = 0.0 for epoch in range(20): model.train() for imgs, labels in train_loader: optimizer.zero_grad() loss = criterion(model(imgs), labels) loss.backward() optimizer.step() model.eval() correct = total = 0 with torch.no_grad(): for imgs, labels in val_loader: preds = model(imgs).argmax(1) correct += (preds == labels).sum().item() total += labels.size(0) acc = correct / total if acc > best_acc: best_acc = acc torch.save(model.state_dict(), "best.pth")

model.eval()和torch.no_grad()在验证阶段必须加,前者关掉 dropout 和 BN 的训练行为,后者省显存。保存best.pth而不是覆盖式保存,是为了防止后期过拟合把好权重冲掉。20 个 epoch 是起步值,实际看验证曲线早停。

4. 避坑与排查:分类数据集落地最常见的五个问题

这一章是我踩过的坑集中营,每一条都按现象、原因、解决来写,照着排查能省不少时间。

4.1 训练准确率一直上不去

现象:loss 不降,准确率卡在 1/36 附近,等于随机猜。原因:标签索引和模型输出对不上,或者 json 类别顺序和文件夹顺序错位。解决:回到 2.1 的assert那步,打印class_to_idx和 json 逐项比对,确认索引一致再训。

4.2 验证集准确率远高于测试集

现象:验证集 95%,测试集只有 70%。原因:验证集和测试集分布不一致,或者验证集被反复用来调参导致过拟合。解决:检查三个 split 是不是同一批数据随机划分的,确认没有图片重复出现在不同 split;调参只看验证集,测试集留到最后。

4.3 显存爆了或者训练极慢

现象:batch size 稍微调大就 OOM。原因:图片分辨率没统一,个别大图拖垮整个 batch;或者 num_workers 设太大导致 IO 争抢。解决:在 Dataset 里强制 Resize 到固定尺寸,num_workers从 4 起步按机器核数调,别一上来就设 16。

4.4 某些类别几乎全错

现象:整体准确率还行,但某几类混淆严重。原因:这几类视觉上太像,比如不同品种的辣椒,或者样本量太少。解决:看混淆矩阵定位具体是哪几类,针对性加样本或做类内增强,必要时用 focal loss 压制易分样本。

4.5 可视化脚本报路径错误

现象:跑 show 脚本提示文件不存在。原因:脚本里写死了作者本地的绝对路径。解决:把路径参数改成相对路径或命令行传入,解压后先cd到数据根目录再跑。

5. 进阶技巧:用混淆矩阵和 t-SNE 验证这份数据到底学没学到东西

训练完一个模型,准确率只是一个数字,真正能说明问题的是它错在哪、特征学得怎么样。我习惯在测试集上跑一遍混淆矩阵,再用 t-SNE 把倒数第二层的特征降维画出来,看同类是否聚拢、异类是否分开。这一步能暴露很多准确率掩盖的问题。

混淆矩阵用 sklearn 几行就能出:

from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt model.eval() all_preds, all_labels = [], [] with torch.no_grad(): for imgs, labels in test_loader: preds = model(imgs).argmax(1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) cm = confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annot=False, cmap="Blues") plt.savefig("confusion.png", dpi=150)

对角线越亮越好,非对角线的亮点就是混淆对。如果发现苹果和梨互相错,说明模型对形状和颜色相近的类别区分力不够,可以考虑换更强的 backbone 或加注意力模块。t-SNE 则更直观:

from sklearn.manifold import TSNE feats = [] with torch.no_grad(): for imgs, _ in test_loader: f = model.avgpool(model.layer4(model.layer3(model.layer2(model.layer1( model.conv1(imgs)))))) feats.append(f.flatten(1)) feats = torch.cat(feats).numpy() emb = TSNE(n_components=2, perplexity=30).fit_transform(feats) plt.scatter(emb[:, 0], emb[:, 1], c=all_labels, cmap="tab20", s=5) plt.savefig("tsne.png", dpi=150)

perplexity=30是常用起点,样本少可以调小。图上同类点聚成一团、不同类分开,说明特征有判别力;如果混成一锅粥,那准确率再高也要怀疑。这两个图我每次训完分类模型都会跑一遍,比盯着 loss 曲线有用得多。从那以后我每次拿到新数据集,都强制先跑可视化再开训,省得训到一半才发现数据本身有问题。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 22:31:36

React Native鸿蒙跨平台开发实战:积分商城页面实现记录

最近在做React Native的鸿蒙跨平台开发,手头的第一个实战任务就是积分商城页面。功能看起来直白——积分商品列表、兑换、记录——但一旦要把React Native跑在鸿蒙设备上,页面只是表象,背后是环境搭建、鸿蒙适配、状态同步、真机调试这一连串…

作者头像 李华
网站建设 2026/9/28 22:30:58

C语言课程设计实战:控制台版球球大作战开发全解析

C语言课程设计,最愁人的往往不是题目本身,而是题目无聊。我们班交上去的选题,十个里有八个是学生成绩管理系统、图书管理系统,剩下两个是计算器。我做的是“伪版球球大作战”——控制台里一张10028的地图上,玩家用表示…

作者头像 李华
网站建设 2026/9/28 22:30:45

Sealos:把200万运维成本省下来投入产品研发

如果你所在的技术团队有10个人,有没有认真算过:一年下来,花在“让系统不崩、环境不坏、部署不卡”上面的时间,占了多少比例?我见过不少从初创期走过来的团队,业务其实做得不错,但组里最忙的永远…

作者头像 李华
网站建设 2026/9/28 22:28:41

AI编程助手落地终端:Codex CLI与Claude CLI协同工作流实战

前阵子我把自己的终端工作流彻底重写了一遍,项目名叫 CLI-Anything。名字有点狂,但用下来是真顺手:Codex CLI 负责在仓库里跑代码任务、Claude CLI 负责多文件改造和代码解释,再配合 jq、rg 这些老伙计,我在终端里能完…

作者头像 李华
网站建设 2026/9/28 22:28:09

Unity AI Navigation新导航系统:动态烘焙与寻路实战解析

1. 从旧版导航到 AI Navigation:这次升级到底动了什么先说结论:如果你还在用 Unity 内置的 NavMesh(Navigation 旧版组件),那你这几年做的地图寻路其实一直处于“能用,但不好扩展”的状态。Unity 从 2022 L…

作者头像 李华
网站建设 2026/9/28 22:27:45

RHEL 9启动过程全解析:从UEFI到systemd的排错指南

网上聊stm32的启动过程、聊项目管理里那个启动过程组,能搜出一大堆,但真正问 rhel9 的启动过程,很多运维兄弟能说出“GRUB 加载、内核起来、systemd 接管”三步,再往下就含糊了。我最早也是在服务器起不来的时候,才下决…

作者头像 李华