news 2026/10/7 6:28:58

果蔬图像分类实战:4200张标注数据集与PyTorch ResNet训练全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
果蔬图像分类实战:4200张标注数据集与PyTorch ResNet训练全指南

简介:一份面向图像分类任务学习与算法验证的常见果蔬多类别数据集,覆盖香蕉、苹果、梨、葡萄、橙子、胡萝卜、辣椒、洋葱、土豆等36个类别,共约4200张已标注图像。数据经过统一预处理,可直接作为分类网络输入,并已划分训练集、测试集与验证集,同类图片集中存放,便于加载与评估。压缩包内共2000个文件,其中1998张jpg图片构成图像主体,1个json文件提供类别映射,1个Python脚本用于数据集可视化,整体约364.87MB。目前已有119人学习下载。借助资源中的show脚本可快速浏览各类别图像,json文件可查看完整类别清单,适合正在做图像分类毕设或复现CNN模型的开发者使用,能省去标注和整理数据的繁琐环节。

1. 先看清:4200张果蔬图像分类数据集,到底能解决什么问题

手头接到一个果蔬识别需求时,最尴尬的不是模型选型,而是数据从哪来。自己拍几百张不够用,去爬网图又带一堆水印和无关背景,标注更是耗掉两三天。如果你也卡在这,那么这份“常见果蔬多类别图像分类数据集(已标注,约4,200张数据)”就值得花十分钟认真盘一盘。它解决的正是“从零起步做图像分类”的第一道坎:数据集已经完成类别标注,你不需要自己打开标注工具逐张框选,拿过来就能拆训练集、跑模型、看指标。适合的人群很明确:正在做课程设计、竞赛原型验证、农业App MVP,或者想先评估“果蔬分类到底能做到什么精度”的工程师。它不会让你一步登天,但能把从数据到模型的时间压缩到一两天以内。

2. 果蔬分类数据集的长什么样:标签分布、图像规格与三类常见组织方式

2.1 标注格式:ImageNet风格文件夹 vs JSON/CSV vs COCO

拿到一份“已标注”的图像分类数据集,第一件事不是打开图片看美不美,而是确认它的标注格式。常见做法中,约4,200张规模的数据集最爱用两种组织方式:一种是每个类别一个文件夹,图片直接躺在类名文件夹里,PyTorch的ImageFolder、Keras的flow_from_directory都能直接读;另一种是带一个labels.csv或annotations.json,里面记录每张图片的文件名和类别ID。前者叫“暗标注”,因为类别信息写在路径里;后者叫“明标注”,因为你需要自己把映射关系读出来。

少数数据集会提供COCO格式的JSON,里面除了类别还有边界框,那是给目标检测任务用的。做纯图像分类时,COCO格式反而要多写一步“把框裁出来当分类图”的转换逻辑。拿到数据集后我一般会先打印目录树,确认是不是“train/类别A/xxx.jpg”这种结构。如果是一堆散图加CSV,就写个三行脚本把文件移动成文件夹结构,后面所有训练代码都能省掉自定义Dataset的麻烦。

另外一定要看图像规格。有的数据集是统一缩放到224x224的,有的是原始高清图。前者可以直接开训,后者需要你在预处理里加Resize和CenterCrop。4200张图如果每张都是6000x4000的原始照片,那连加载都慢到怀疑人生,最好先离线批量缩到短边256。检查图像尺寸不需要一张张看,用PIL读一张判断就能大致知道风格是否统一,但更稳妥的是用脚本扫一个子集。

2.2 看一眼数据:标签类别与数量分布检查脚本

很多人拿到数据集直接扔进训练脚本,等loss曲线乱飞才回头怀疑数据。正确姿势是先跑一个分布统计。下面这个脚本用pathlib扫描类文件夹,输出每个类别的图片数和总张数,顺便检查是否有图片文件损坏。

from pathlib import Path from PIL import Image data_root = Path("fruits_veg_dataset/train") # 改成你的数据集路径 total = 0 for cls_dir in sorted(data_root.iterdir()): if not cls_dir.is_dir(): continue files = list(cls_dir.glob("*.*")) total += len(files) print(f"{cls_dir.name}: {len(files)} 张") print(f"总张数: {total}") # 抽检前50张图片是否可正常打开 bad = 0 for img_path in list(data_root.glob("*/*"))[:50]: try: with Image.open(img_path) as im: im.verify() except Exception: bad += 1 print(f"损坏图片: {img_path}") print(f"损坏数: {bad}/50")

这段代码的逻辑分两步:先对类别文件夹计数,让你看到每个类有多少张;再抽检50张做verify(),排除半截下载或转码损坏的图片。命令行里跑完你会得到一个关键信息——类别分布是否均匀。如果“香蕉”有800张而“火龙果”只有60张,后期就需要重点做类别平衡处理,否则模型会直接把少见类忽略掉。

参数说明:data_root支持Path对象,比字符串拼接更安全;glob("*.*")能匹配常见的.jpg、.jpeg、.png,如果你有.bmp或.webp就改成glob("*")再加后缀过滤。抽检50张是经验值,能暴露批量坏图问题,又不会太慢。如果抽检就发现好几张损坏,建议写个全量检查脚本,把坏图挪到broken/目录,免得训练时读到一半报错。

2.3 按需切分:训练/验证/测试集划分的常用比例与随机种子

4200张图不算海量,但足够按8:1:1拆成训练、验证、测试。很多开源数据集直接给好了train/和val/目录,但如果你只拿到一个总目录,或者想自己重新划分以做交叉验证,就需要自己动手。拆分的核心是保证随机且可复现,不然下次跑同一脚本结果又变了。

常见做法是用split_folders库,或者用标准库自己写。我不太建议依赖额外库,下面这段脚本用Python自带的random.shuffle完成划分,逻辑可控:

import random import shutil from pathlib import Path src = Path("fruits_veg_dataset") # 所有类别文件夹的根目录 out = Path("fruits_veg_split") train_ratio, val_ratio, test_ratio = 0.8, 0.1, 0.1 random.seed(42) # 固定随机种子 for cls_dir in src.iterdir(): if not cls_dir.is_dir(): continue images = list(cls_dir.glob("*.jpg")) + list(cls_dir.glob("*.png")) random.shuffle(images) n_train = int(len(images) * train_ratio) n_val = int(len(images) * val_ratio) splits = { "train": images[:n_train], "val": images[n_train:n_train + n_val], "test": images[n_train + n_val:], } for split_name, paths in splits.items(): dest = out / split_name / cls_dir.name dest.mkdir(parents=True, exist_ok=True) for p in paths: shutil.copy2(p, dest / p.name)

脚本按类别分别shuffle,避免全部混在一起后某些类别只出现在训练集或测试集。这里我直接copy2而不是move,保留原始数据,方便回滚。random.seed(42)是必须的,否则每次划分结果不同,模型对比就没有意义。

参数说明:比例是硬编码的,如果你数据少,比如某些类只有50张,那训练集只有40张,测试10张,后期评估会抖得厉害。这种情况建议改成stratified k-fold,或者把测试集比例降到5%并把val比例提上去。我一般会先按8:1:1跑一版,看测试集每个类别是否至少有20张,少了就降低比例或干脆不用测试集,把val集当测试用。对于4200张的中等规模数据,8:1:1通常够用,但如果你要发论文或做严谨对比,建议用5折交叉验证。

3. 用ResNet/FastAI在本地跑通果蔬分类:最小训练管线

3.1 环境准备与依赖选择

训练一个42类(假设)果蔬分类模型,主干网络推荐从ResNet50或ResNet18起步。ResNet18训练快,内存占用低,适合先验证数据没问题;ResNet50精度更高,但4200张图容易过拟合,需要更强的正则化。PyTorch是首选框架,因为生态里torchvision.models自带预训练权重,迁移学习只需改最后一层全连接。

环境安装不要图新,稳定版本更重要。我常用的组合是Python 3.10、PyTorch 2.x、torchvision 0.17,CUDA版本根据你的显卡驱动定。训练前先用CPU跑一个极小的批次验证代码逻辑通顺,再切回GPU,否则排查bug时还得和显存溢出纠缠。

pip install torch torchvision timm scikit-learn matplotlib

timm不是必需品,但它提供了比torchvision更新更多的骨干网络,比如EfficientNet、ConvNeXt。如果只看重稳定,只用torchvision也够。这里装timm是为后面第6章的进阶对比做准备。

3.2 数据加载与预处理增强参数

数据加载是训练管线最容易出毛病的环节。用ImageFolder读取我们已经按train/类别A/xxx.jpg排好的目录,DataLoader会自动打标签。预处理要严格对齐预训练模型的输入要求:ResNet系列用ImageNet的均值和标准差做归一化,同时先把短边缩放到256,再中心裁剪到224。

数据增强方面,4200张数据不算富裕,我建议训练集用随机水平翻转、随机旋转(±15度)、随机亮度/对比度抖动。不必一上来就上CutMix或AutoAugment,先把基础增强跑通。验证集和测试集只做缩放和中心裁剪,不做随机翻转,否则指标会虚高。

from torchvision import datasets, transforms train_transform = transforms.Compose([ transforms.Resize(256), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) val_transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_dataset = datasets.ImageFolder("fruits_veg_split/train", transform=train_transform) val_dataset = datasets.ImageFolder("fruits_veg_split/val", transform=val_transform)

这段代码里ImageFolder会按文件夹名排序生成类别索引,比如“apple”对应0,“banana”对应1。训练和验证的预处理唯一差别就是RandomCrop和CenterCrop——后者是为了可复现的评估。Resize(256)再RandomCrop(224)比直接Resize(224)多了一点随机裁剪的区域,相当于免费的数据增强。ColorJitter的三个参数都控制在0.2,太大会让果蔬颜色失真,影响像“青苹果和梨”这类靠颜色区分的类别。

3.3 训练脚本:模型选择、学习率与epoch设置

训练脚本的骨架可以复用,关键是几个超参数。迁移学习场景下,我习惯冻结骨干网络的前几层,只微调最后一层和分类头。4200张数据不必从零训练,直接用torchvision的ResNet18预训练权重,把最后的fc层输出改成类别数。

学习率是最大的玄学之一。微调分类头时用1e-3没问题;如果解冻骨干网络,要把学习率降到1e-5到5e-5,否则预训练特征很快被破坏。Epoch数量建议30起步,配合ReduceLROnPlateau,验证loss连续3个epoch不降就把学习率乘0.5。下面是一个最小可跑的训练循环:

import torch import torch.nn as nn from torchvision import models from torch.utils.data import DataLoader model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) num_classes = len(train_dataset.classes) model.fc = nn.Linear(model.fc.in_features, num_classes) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=1e-3, momentum=0.9, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode="min", patience=3, factor=0.5) for epoch in range(30): model.train() running_loss = 0.0 for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * images.size(0) train_loss = running_loss / len(train_dataset) model.eval() val_loss = 0.0 correct = 0 total = 0 with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) val_loss += loss.item() * images.size(0) _, preds = torch.max(outputs, 1) correct += (preds == labels).sum().item() total += labels.size(0) val_loss = val_loss / len(val_dataset) val_acc = correct / total scheduler.step(val_loss) print(f"Epoch {epoch+1:02d} | Train Loss {train_loss:.4f} | Val Loss {val_loss:.4f} | Val Acc {val_acc:.4f}")

这段代码里ReduceLROnPlateau接收val_loss作为监控指标,patience=3意味着连续三个epoch验证loss不降低才降学习率。weight_decay=1e-4是L2正则,对防止小数据集过拟合很关键。batch_size=32在ResNet18下大约占用3GB显存,如果显存不够就降到16。

参数调整优先级:先确认验证集没有过拟合(训练loss很低但val loss不降),再去动学习率;如果验证集一开始就高,优先检查数据预处理而不是调模型结构。30个epoch在RTX 3060上大约10分钟能跑完,非常快。如果发现val acc一直卡在某个值,可以考虑换ResNet50,或者回到第2章检查类别分布。

4. 果蔬分类的落地评估:混淆矩阵、Top-1/Top-5与单类召回

4.1 评估指标怎么选

很多教程只看整体准确率,但在果蔬分类场景里,整体准确率会骗人。假设数据集里苹果占40%,模型把所有图都预测成苹果,准确率就有40%,可这显然是个废物模型。对于多类别分类,我至少会看三个指标:Top-1准确率、Top-5准确率和每个类别的召回率。果蔬类别之间存在天然的视觉相似性,比如“富士苹果”和“红蛇果”、“青柠”和“柠檬”,Top-5能反映模型是否把正确答案排进了前五,这对实际应用中的“推荐式”交互更有参考价值。

计算这些指标不需要自己写复杂逻辑,scikit-learn的classification_report和confusion_matrix就够。关键是先保存测试集上每个样本的真实标签和预测结果,再离线分析。

from sklearn.metrics import classification_report, confusion_matrix import numpy as np all_labels = [] all_preds = [] model.eval() with torch.no_grad(): for images, labels in test_loader: images = images.to(device) outputs = model(images) _, preds = torch.max(outputs, 1) all_labels.extend(labels.cpu().numpy()) all_preds.extend(preds.cpu().numpy()) print(classification_report(all_labels, all_preds, target_names=train_dataset.classes, digits=3)) cm = confusion_matrix(all_labels, all_preds)

这里classification_report每行输出一个类别的精确率、召回率、F1和样本数。我一般先看每类召回率,低于0.7的类别就是要优先处理的。confusion_matrix会生成一个num_classes x num_classes的矩阵,横轴是预测,纵轴是真实标签。矩阵里数值最大的非对角线位置,就是最容易混淆的类别对。

4.2 用混淆矩阵定位易混类别

拿到混淆矩阵后,不要只看数字,要画图并回看原始图片。有的混淆是标注本身造成的,比如数据集中“青枣”和“绿苹果”的图像标签可能就有误,模型反而比标注更“正确”。这种情况下不需要强行调模型,而是先清洗错误标注。

画热力图用matplotlib的imshow就能满足,但要注意把类别名显示出来,否则你数格子数到眼花。下面是一个简单可视化脚本:

import matplotlib.pyplot as plt plt.figure(figsize=(12, 10)) plt.imshow(cm, interpolation="nearest", cmap="Blues") plt.colorbar() tick_marks = np.arange(len(train_dataset.classes)) plt.xticks(tick_marks, train_dataset.classes, rotation=90) plt.yticks(tick_marks, train_dataset.classes) plt.xlabel("Predicted") plt.ylabel("True") for i in range(len(train_dataset.classes)): for j in range(len(train_dataset.classes)): if cm[i, j] >= 10: # 只标注数量大于10的格子,防止文字墙 plt.text(j, i, str(cm[i, j]), ha="center", va="center", fontsize=8) plt.tight_layout() plt.savefig("confusion_matrix.png", dpi=200)

阈值设为10,是因为4200张数据切出的测试集每类大约只有10张左右,小于10的格子基本都是噪声。看图时重点找“横向亮、对角线也亮”的类别——说明模型经常把真实类别A预测成B,而B本身预测得还行。这时候我会把那几对类别汇总成一个列表,到训练集里翻几十张原图,看它们到底像不像。如果是真像,那就考虑增加这类样本或做针对性的增强(比如色调抖动),如果是标注错了,就直接修正标签。

4.3 推理部署时的预处理一致性

模型在测试集上跑出95%的准确率,一上线就掉到80%,最常见原因就是部署时的预处理和训练不一致。训练时用的是Resize(256) + CenterCrop(224) + Normalize(ImageNet均值方差),部署时如果直接把原始图Resize((224,224))再丢进模型,虽然输入尺寸一样,但图像内容和分布已经变了。特别是果蔬图片经常带着叶子、标签贴纸、包装盒,训练时靠中心裁剪把背景裁掉一部分,部署时全图缩放就会把干扰物一起放大。

解决方案是把预处理逻辑封装成和训练完全相同的函数,并在部署环境里跑单张测试图做对比。我一般会在测试集里挑三张图,分别用训练管线和部署管线跑一遍,比较输出的logits是否接近。如果差异大于1e-3,就说明哪一步没对齐。常见的坑是ToTensor的归一化放错了位置,或者用了cv2.resize的默认插值而训练用的是PIL的双线性插值,这两者输出有细微差别,累积起来就能影响置信度。

def preprocess_for_inference(image_path): from PIL import Image image = Image.open(image_path).convert("RGB") image = transforms.Resize(256)(image) image = transforms.CenterCrop(224)(image) image = transforms.ToTensor()(image) image = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])(image) return image.unsqueeze(0).to(device)

这段代码直接复用训练时的val_transform里前三步,保证一致性。注意Image.open后有convert("RGB"),因为有些图片是RGBA或灰度模式,如果不转换,模型会报通道数错误。部署时不要在这里加任何随机增强,推理必须确定性。

5. 果蔬图像分类避坑与常见问题排查:从标注错误到过拟合

5.1 标注错误:现象、原因与清洗

训练loss很难降到0.2以下,但验证集准确率一直在80%左右徘徊,打开预测错误的样本一看,发现某些图压根就是错标签——这就是典型的标注错误。原因很直接:约4,200张数据的人工标注很难保证100%正确,尤其像“杏鲍菇”和“白蘑菇”、“柠檬”和“青柠”这类相似品类,标注员也可能混淆。解决方法是把训练集里每个类别的图按“预测置信度从高到低”排序,人工抽查置信度最低的一批。我习惯写个脚本,批量输出“模型预测为X但标签为Y”的样例图,拼成一张大图快速扫一遍。如果发现某个类别有超过5%的错误标签,就该考虑修正或删除这些样本。很多情况下,删掉噪音样本比增加几千张新图更能提升精度。

5.2 类别不平衡:小类别被吃掉

如果统计后发现有类别只有40张,而其他类别有150张,那么模型大概率会在训练中忽略这个小类别。现象是训练loss正常下降,但小类别的召回率接近0,混淆矩阵里那一行几乎全部分散到其他类别。原因是交叉熵损失在小样本类别上贡献的梯度太小,被大类别淹没。解决方法有三个层次:先做上采样——在DataLoader里用WeightedRandomSampler给小类别更高采样概率;再不行就做数据增强的强化版,比如对罕见类额外做RandomResizedCrop;最后考虑用类别平衡损失函数,比如FocalLoss。

WeightedRandomSampler的权重一般按类别样本数的倒数设置,还要注意replacement=True,否则采样不到足够多样本。修完代码后再看小类别的召回率有没有上来,如果还是不行,就需要补充数据而不是硬调。

5.3 背景干扰与拍摄环境差异

同一类果蔬,在菜市场灯光下、在超市包装盒里、在自家厨房砧板上的表现差异巨大。训练集里如果大量图片都是同一种背景,模型会把背景特征学进去。现象是测试集里“带红色塑料袋的西红柿”预测为“苹果”,因为训练集里的苹果图很多也配了红色塑料袋。排查方法:随机抽一个类别,用matplotlib画一个网格图,一眼扫过去看背景是否单一。

解决这类问题靠数据层面最有效:对训练集做随机背景替换增强,或者加更强的RandomCrop让模型更关注果蔬主体。如果要求不高,在预处理里把图片随机裁掉边缘10%的区域,也能逼迫模型放弃背景信息。另外,采集真实场景图时要有意识地覆盖不同光源角度,不要都在同一天同一地点拍。

5.4 模型过拟合与欠拟合的调参顺序

小数据集上最典型的过拟合现象是:训练loss稳步降到0.01,验证loss从第10个epoch开始反弹,验证准确率停滞。原因是模型容量太大,4200张图不够ResNet50记住所有真实特征。我踩过最深的坑是一上来就换大模型,结果过拟合更严重。正确调参顺序应该是:先确认数据分布没问题,再调数据增强强度,然后调weight_decay,最后才是换模型架构。对于这个规模的数据,ResNet18搭配强增强已经能打,如果还过拟合,就把weight_decay从1e-4提到5e-4,同时把Dropout层加在全连接前面。

欠拟合的表现为训练loss和验证loss都高,模型容量不够或学习率太低。这时候先调学习率,用1e-3跑10个epoch看训练loss有没有下降趋势;没有就升到3e-3,但注意大学习率配合SGD容易震荡。还有一种情况是batch size太小导致收敛慢,可以先加到64试一下。记住,调参不能一次改多个变量,否则翻车了都不知道是哪一步改坏的。

6. 把4200张用出12000张的效果:数据增强、迁移学习与半自动标注

6.1 迁移学习骨干网络选型

4200张图想训练一个高精度模型,最划算的方案是使用更强的预训练骨干。ResNet18适合第一版跑通,但如果最终精度要冲95%以上,我会换EfficientNet-B3或ConvNeXt-Tiny。timm库里这些模型的预训练权重都是在ImageNet-21k上训过的,特征提取能力比ResNet18强一截。选型时注意输入分辨率:EfficientNet-B3默认是320x320,比224x224更吃显存,但小模型结构在果蔬纹理识别上反而占优势。

换模型只需改一行代码:

import timm model = timm.create_model("efficientnet_b3", pretrained=True, num_classes=num_classes)

num_classes会自动替换分类头。但要注意,timm的预处理统计量不一定和torchvision一样,需要从timm.data.create_transform获取正确的均值方差。我一般直接用timm.data.create_model配合timm.data.create_transform,省去手动对齐的麻烦。

6.2 强增强策略与CutMix

基础增强只能缓解过拟合,想真正把数据“变多”,就要用强增强。CutMix就是把两张训练图按块拼在一起,标签也跟着变成两块区域的混合比例。它天然适合果蔬分类,因为果蔬大多是圆形或椭圆,裁剪块不会割裂语义主体。torchvision从1.13开始内置了CutMix配合MixUp,通过torchvision.transforms.v2.MixUp和CutMix实现。引用时需要把数据集输出的(image, label)变成(image, target),因为CutMix要求标签是One-Hot或软标签。

如果不想升级到v2接口,也可以用timm里的mixup函数,它直接接收batch输出。我建议只上CutMix,不上MixUp,因为果蔬图像混合后会产生“半苹果半香蕉”的怪异图,模型虽然能学到鲁棒特征,但调试时很难解释预测结果。CutMix的alpha参数设成1.0,mixup的alpha设成0.2,两个一起用容易让训练loss下降变慢,需要更长epoch才能收敛。

6.3 用预训练模型做伪标注扩展数据集

当4200张确实不够用时,不要盲目去爬图,先用现有模型做一轮伪标注。具体做法是:用训练好的模型对一批无标签果蔬图片做预测,只保留置信度大于0.95的样本,然后人工抽检后加入训练集。这里的关键是“置信度阈值”和“类别均衡”。如果你有1000张无标签图,模型可能对其中500张都给出很高的置信度,但可能全部集中在苹果和香蕉上,导致新增样本加剧不平衡。因此伪标注时要按类别分别设置阈值,对原本样本少的类别,把阈值降到0.9,对样本多的类别提到0.97。

pred_probs = torch.softmax(outputs, dim=1) max_probs, pred_cls = torch.max(pred_probs, dim=1) keep_mask = max_probs > 0.95 # 再把保留的样本按类别去重,避免单一类别刷屏 selected_paths = [paths[i] for i in torch.nonzero(keep_mask).flatten()]

这段逻辑里softmax得到的概率才是有效的置信度,直接用outputs的最大值会被logits尺度影响。人工抽检时我会每类随机抽20张新图,看看伪标签是否靠谱。如果某类别的误标率超过3%,就把该类别的阈值上调或者直接放弃该类的伪标注。用这种方式,4200张基础数据通常能扩展到6000到7000张,精度提升肉眼可见。

最后说一个我自己的教训:最早拿到这类数据集时,我图省事跳过分布检查直接开训,结果因为类别目录里混入了一个损坏的文件夹,训练Loss在某个epoch直接变成NaN,排查了一个小时才发现是某张图片是0字节。后来学乖了,不管数据来源多可靠,第一件事永远是统计和清洗。图像分类项目的成败,八成在数据准备,模型只是放大镜。希望这个数据集能让你少走我这些弯路,也祝你一版就跑到90%以上的准确率。

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

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

S7-1200控制步进电机实战:从选型接线到博途组态与调试

去年接了一个传感器装配工装的项目,转盘旁要加一台步进电机驱动的推料机构:正转把物料推到检测位,检测完再反转退回来,转速要在触摸屏上能调。用西门子S7-1200做PLC控制步进电机,听起来无非是梯形图加脉冲输出&#xf…

作者头像 李华
网站建设 2026/10/7 6:27:55

Flink读取Kafka实战:从环境搭建到参数调优与避坑指南

简介:一份基于 Apache Flink 的实时数据处理工程包,完整演示从 Kafka 消费数据、执行流式计算、再将结果写入 Redis 集群与 MySQL 的链路。面向大数据开发初学者及有实时数仓落地需求的工程师,适合用于学习 Flink Connector 配置、算子应用和…

作者头像 李华
网站建设 2026/10/7 6:27:51

JSP+SQL实现交通信息管理系统实战指南

简介:本资源是一套完整的基于JSP与SQL技术的本科毕业设计项目材料,面向计算机专业学生、Web开发初学者及Java后端入门学习者,聚焦智能交通信息管理这一典型B/S架构应用场景。压缩包共含项目报告、源代码、开题报告、答辩PPT与外文翻译等核心文…

作者头像 李华
网站建设 2026/10/7 6:27:21

FPGA实现10G/25G UDP线速网络栈的工程落地路径

1. 这不是“又一个UDP demo”,而是一套能跑满线速的FPGA网络栈落地路径你有没有试过在Vivado里拖一个UDP IP核,写几行Verilog发个包,然后用Wireshark抓到数据——心里一热,以为成了?结果一上真实流量,ping延…

作者头像 李华
网站建设 2026/10/7 6:26:56

Jetson Orin DLA调优实战:从TensorRT部署到INT8量化与低功耗推理

1. GPU把活都干了,为什么还要把DLA单独拎出来很多刚拿到NVIDIA Jetson Orin开发板的开发者,第一反应都是把模型导成ONNX,丢给TensorRT,然后在GPU上跑得飞快。这套流程没毛病,CUDA生态成熟、资料多、踩坑记录全网都是&a…

作者头像 李华
网站建设 2026/10/7 6:26:27

AI焦虑干预系统架构:多模态情绪识别与认知拆解技术链路

1. 从“情绪识别”到“认知拆解”:AI焦虑干预的完整技术链路焦虑情绪干预这件事,过去十几年一直是心理咨询领域的专属阵地。一个来访者坐在咨询室里,咨询师通过面部表情、语音语调、用词习惯、停顿节奏来判断对方的焦虑水平,再用认…

作者头像 李华