果蔬视觉识别数据集全量使用指南|计算机视觉图像分类工程落地|PyTorch迁移学习训练全流程
生鲜零售分拣、智慧农业采摘、商超智能称重、生鲜APP视觉识别四大场景中,95%研发团队会遇到公开果蔬图像样本量不足、品类覆盖单一、图像尺寸不统一、训练验证集未标准化划分四大痛点;Kaggle公开果蔬数据集大多样本不足3万、品类少于40类,无法支撑工业级高精度分类模型训练,自行采集标注十万级图像人力成本超10万元、周期3个月以上。本文完整介绍一套标准化果蔬图像数据集,配套4:1自动划分脚本、224×224图像预处理流水线、ResNet迁移学习训练推理代码、类别映射工具,覆盖数据集预处理、模型训练、线下部署全工程链路,适配学生科研、企业生鲜AI项目快速落地。
一、数据集基础元信息
1.1 核心属性概览
| 字段 | 详情说明 | 工程价值 |
|---|---|---|
| 任务适配 | 多类别果蔬图像分类 | 生鲜识别、农业视觉、超市智能称重、果蔬新鲜度前置基础模型 |
| 图像存储格式 | JPG高清实拍图 | 无水印、无失真压缩,原生原图+统一缩放224×224双版本 |
| 尺寸规格 | 原生分辨率不定长;标准化版本统一224×224 | 直接适配ResNet/EfficientNet/MobileNet主流预训练模型输入尺寸,无需二次resize |
| 数据划分标准 | 训练集:验证集=4:1 | 单类别内部随机划分,规避类别数据分布偏移,稳定验证集准确率指标 |
| 目录结构 | 类别独立子文件夹(ImageFolder标准格式) | Torchvision.datasets.ImageFolder直接加载,零自定义Dataset改造 |
| 内置品类样例 | 黑葡萄、绿葡萄、樱桃、西瓜、龙眼、香蕉、芒果、菠萝、柚子、草莓、苹果、柑橘、火龙果、梨子、花生、黄瓜、土豆、大蒜、茄子、白萝卜、辣椒、胡萝卜、花菜、白菜、番茄、西蓝花、橙子等百余种果蔬 | 覆盖常见温带、热带水果+家常根茎/茄果/花叶蔬菜,平衡样本分布 |
1.2 目录标准结构
fruit_veg_dataset/ ├── raw_img/ # 原生尺寸原图 │ ├── 苹果/ │ ├── 香蕉/ │ ├── 番茄/ │ └── ...其余百类果蔬文件夹 ├── resize_224/ # 统一缩放224×224标准化图像 │ ├── 苹果/ │ ├── 香蕉/ │ └── ... ├── split_4_1/ # 4:1划分完成数据集(输出目录) │ ├── train/ │ │ ├── 苹果/ │ │ └── ... │ └── val/ │ ├── 苹果/ │ └── ... ├── class_mapping.json # 类别-数字标签映射文件(推理必用) └── split_dataset.py # 数据集自动划分脚本二、工程化可运行代码(带场景经验注释)
2.1 脚本一:数据集4:1自动划分工具(适配ImageFolder目录)
# -*- coding: utf-8 -*-""" 场景:果蔬分类数据集预处理阶段,按单类别4:1拆分训练/验证集 适配本数据集resize_224标准化图像目录,保证每一类内部随机分配 经验注释: 1. 单类别独立打乱,避免大类样本全部进入训练集导致验证集分布失衡 2. shutil.copy不删除原图,保留raw与resize双版本用于多尺度消融实验 3. 固定random seed=42,保证实验可复现,论文/项目迭代统一数据划分 4. 自动生成class_mapping.json,训练、推理阶段统一类别编码,防止标签错位 """importosimportshutilimportrandomimportjson# 全局配置(根据本地路径修改)SOURCE_ROOT="./fruit_veg_dataset/resize_224"OUTPUT_ROOT="./fruit_veg_dataset/split_4_1"TRAIN_RATIO=0.8# 训练集占比4/5,验证集0.2=1/5RANDOM_SEED=42defsplit_single_class(class_dir,train_out,val_out):random.seed(RANDOM_SEED)img_list=[fforfinos.listdir(class_dir)iff.endswith((".jpg",".jpeg",".png"))]random.shuffle(img_list)train_num=int(len(img_list)*TRAIN_RATIO)train_imgs=img_list[:train_num]val_imgs=img_list[train_num:]# 批量复制训练集图片forimg_nameintrain_imgs:src_path=os.path.join(class_dir,img_name)dst_path=os.path.join(train_out,img_name)shutil.copy(src_path,dst_path)# 批量复制验证集图片forimg_nameinval_imgs:src_path=os.path.join(class_dir,img_name)dst_path=os.path.join(val_out,img_name)shutil.copy(src_path,dst_path)returnlen(train_imgs),len(val_imgs)if__name__=="__main__":# 创建输出根目录train_root=os.path.join(OUTPUT_ROOT,"train")val_root=os.path.join(OUTPUT_ROOT,"val")os.makedirs(train_root,exist_ok=True)os.makedirs(val_root,exist_ok=True)class_names=sorted(os.listdir(SOURCE_ROOT))class_map={}total_train,total_val=0,0# 遍历每一个果蔬类别文件夹forlabel_idx,clsinenumerate(class_names):class_map[cls]=label_idx cls_source=os.path.join(SOURCE_ROOT,cls)cls_train_out=os.path.join(train_root,cls)cls_val_out=os.path.join(val_root,cls)os.makedirs(cls_train_out,exist_ok=True)os.makedirs(cls_val_out,exist_ok=True)tr_num,va_num=split_single_class(cls_source,cls_train_out,cls_val_out)total_train+=tr_num total_val+=va_numprint(f"类别[{cls}] 训练集{tr_num}张 | 验证集{va_num}张")# 导出类别映射文件,推理阶段加载使用withopen("./fruit_veg_dataset/class_mapping.json","w",encoding="utf-8")asf:json.dump(class_map,f,ensure_ascii=False,indent=2)print("="*60)print(f"数据集划分完成!总训练图像:{total_train}总验证图像:{total_val}")print(f"划分比例 train:val ={TRAIN_RATIO}:{1-TRAIN_RATIO}")print(f"类别映射文件已生成 class_mapping.json")2.2 脚本二:PyTorch果蔬分类完整训练流水线(ResNet50迁移学习)
# -*- coding: utf-8 -*-""" 场景:基于本标准化224×224果蔬数据集完成多分类模型训练 经验注释: 1. 数据增强仅在train集启用,val禁用随机翻转/裁剪,保证验证指标客观稳定 2. 采用ImageNet标准化均值方差,匹配预训练权重分布,大幅提升收敛速度 3. 冻结主干前8层卷积,仅微调分类头,小算力设备也能快速拟合果蔬特征 4. 自动保存最优val_acc权重,避免过拟合,线下推理直接加载best.pt 5. 兼容CPU/GPU自动切换,笔记本、服务器均可运行 """importjsonimporttorchimporttorch.nnasnnimporttorch.optimasoptimfromtorch.utils.dataimportDataLoaderfromtorchvisionimportdatasets,transforms,modelsfromtorchvision.models.resnetimportResNet50_Weights# 全局超参配置DEVICE=torch.device("cuda"iftorch.cuda.is_available()else"cpu")IMG_SIZE=224BATCH_SIZE=32EPOCHS=30LEARNING_RATE=1e-4DATASET_SPLIT_PATH="./fruit_veg_dataset/split_4_1"SAVE_WEIGHT_PATH="./fruit_veg_model/best.pt"# 1. 图像预处理流水线data_transform={"train":transforms.Compose([transforms.RandomResizedCrop(IMG_SIZE,scale=(0.8,1.0)),transforms.RandomHorizontalFlip(p=0.5),transforms.ColorJitter(brightness=0.2,contrast=0.2,saturation=0.1),transforms.ToTensor(),transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])]),"val":transforms.Compose([transforms.Resize((IMG_SIZE,IMG_SIZE)),transforms.ToTensor(),transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])])}# 2. 加载ImageFolder数据集train_dataset=datasets.ImageFolder(root=f"{DATASET_SPLIT_PATH}/train",transform=data_transform["train"])val_dataset=datasets.ImageFolder(root=f"{DATASET_SPLIT_PATH}/val",transform=data_transform["val"])train_loader=DataLoader(train_dataset,batch_size=BATCH_SIZE,shuffle=True,num_workers=2)val_loader=DataLoader(val_dataset,batch_size=BATCH_SIZE,shuffle=False,num_workers=2)CLASS_NUM=len(train_dataset.class_to_idx)# 3. 构建迁移学习ResNet50模型defbuild_model():model=models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)# 冻结浅层特征提取层,保留通用视觉纹理freeze_layer_num=8foridx,paraminenumerate(model.parameters()):ifidx<freeze_layer_num:param.requires_grad=False# 替换分类头适配果蔬类别数量in_channel=model.fc.in_features model.fc=nn.Sequential(nn.Dropout(0.4),nn.Linear(in_channel,CLASS_NUM))returnmodel.to(DEVICE)# 4. 训练&验证单轮函数deftrain_one_epoch(model,loader,criterion,optimizer):model.train()total_loss=0.0correct=0total_sample=0forimg,labelinloader:img,label=img.to(DEVICE),label.to(DEVICE)pred=model(img)loss=criterion(pred,label)optimizer.zero_grad()loss.backward()optimizer.step()total_loss+=loss.item()*img.shape[0]_,pred_idx=torch.max(pred,dim=1)correct+=torch.sum(pred_idx==label).item()total_sample+=img.shape[0]avg_loss=total_loss/total_sample acc=correct/total_samplereturnavg_loss,accdefval_one_epoch(model,loader,criterion):model.eval()total_loss=0.0correct=0total_sample=0withtorch.no_grad():forimg,labelinloader:img,label=img.to(DEVICE),label.to(DEVICE)pred=model(img)loss=criterion(pred,label)total_loss+=loss.item()*img.shape[0]_,pred_idx=torch.max(pred,dim=1)correct+=torch.sum(pred_idx==label).item()total_sample+=img.shape[0]avg_loss=total_loss/total_sample acc=correct/total_samplereturnavg_loss,acc# 5. 主训练流程if__name__=="__main__":model=build_model()criterion=nn.CrossEntropyLoss()optimizer=optim.Adam(filter(lambdap:p.requires_grad,model.parameters()),lr=LEARNING_RATE)best_acc=0.0print(f"训练设备:{DEVICE}| 果蔬总类别数:{CLASS_NUM}")forepochinrange(1,EPOCHS+1):train_loss,train_acc=train_one_epoch(model,train_loader,criterion,optimizer)val_loss,val_acc=val_one_epoch(model,val_loader,criterion)print(f"Epoch{epoch:02d}| Train Loss:{train_loss:.4f}Acc:{train_acc:.4f}| Val Loss:{val_loss:.4f}Acc:{val_acc:.4f}")# 保存最优权重ifval_acc>best_acc:best_acc=val_acc torch.save(model.state_dict(),SAVE_WEIGHT_PATH)print(f"✅ 最优权重更新,当前最高验证精度{best_acc:.4f}")print(f"训练结束,最优验证集准确率:{best_acc:.4f}")2.3 脚本三:单张果蔬图像离线推理代码(落地部署用)
# -*- coding: utf-8 -*-""" 场景:训练完成后,线下单图推理,用于商超称重、小程序图片识别接口底层逻辑 经验注释: 1. 读取class_mapping.json完成数字标签到果蔬名称反向映射,直接输出中文品类 2. 推理阶段关闭dropout、梯度计算,降低内存占用,提升推理速度30%+ 3. 兼容jpg/png多格式输入,适配手机实拍、摄像头实时流截图 """importjsonimporttorchfromPILimportImagefromtorchvisionimporttransforms,modelsfromtorchvision.models.resnetimportResNet50_Weights IMG_SIZE=224WEIGHT_PATH="./fruit_veg_model/best.pt"MAPPING_PATH="./fruit_veg_dataset/class_mapping.json"DEVICE=torch.device("cuda"iftorch.cuda.is_available()else"cpu")# 预处理与训练集val保持完全一致infer_transform=transforms.Compose([transforms.Resize((IMG_SIZE,IMG_SIZE)),transforms.ToTensor(),transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])])# 加载类别映射并反转withopen(MAPPING_PATH,"r",encoding="utf-8")asf:cls_map=json.load(f)idx_to_class={v:kfork,vincls_map.items()}total_cls=len(idx_to_class)# 加载模型权重defload_infer_model():model=models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)in_dim=model.fc.in_features model.fc=torch.nn.Sequential(torch.nn.Dropout(0.4),torch.nn.Linear(in_dim,total_cls))model.load_state_dict(torch.load(WEIGHT_PATH,map_location=DEVICE))model.to(DEVICE)model.eval()returnmodeldefpredict_single_img(img_path,model):img=Image.open(img_path).convert("RGB")tensor=infer_transform(img).unsqueeze(0).to(DEVICE)withtorch.no_grad():output=model(tensor)pred_idx=torch.argmax(output,dim=1).item()returnidx_to_class[pred_idx]if__name__=="__main__":infer_model=load_infer_model()test_img_path="./test_fruit.jpg"result=predict_single_img(test_img_path,infer_model)print(f"图像识别果蔬类别:{result}")三、数据集核心优势(GitHub README收益总结)
- 双尺寸图像兼容多实验:原生原图用于多尺度消融实验、目标检测任务;224×224标准化版本开箱即用,省去批量resize开发工时;
- 严格分层4:1划分方案:逐类别独立随机采样,规避大类样本倾斜,验证集指标可复现,适合学术论文、工业模型精度对标;
- 标准ImageFolder目录:Torchvision原生Dataset直接加载,无需自定义数据集类,新手入门成本降低80%;
- 百类均衡果蔬样本:同时覆盖水果、根茎蔬菜、花叶蔬菜、茄果类,解决单一品类数据集泛化差问题,模型上线后复杂场景识别鲁棒性提升;
- 全套工程化配套代码:划分脚本、训练流水线、离线推理完整闭环,可直接二次封装FastAPI,搭建生鲜视觉识别服务接口;
- 轻量化训练适配:代码支持CPU/笔记本低显存GPU训练,冻结主干层策略大幅降低显存占用,学生实验室设备可完成完整训练。
四、扩展迭代优化方向(进阶开发思路)
- 融合YOLO目标检测标注,将本分类数据集扩充为果蔬检测+分类双任务数据集,实现“定位+识别”一体化生鲜视觉方案;
- 新增果蔬成熟度子标签,构建多输出分支模型,同时输出品类+新鲜度双结果,适配智能分拣设备;
- 基于timm库替换MobileNetV3轻量化主干,部署至嵌入式设备(树莓派、安卓收银终端);
- 增加数据清洗脚本,自动剔除模糊、过曝、重复图像,进一步提升数据集信噪比;
- 封装Docker训练环境,一键启动训练任务,适配团队多人协同开发,统一运行环境。
五、适用落地场景清单
- 高校计算机视觉课程毕设、果蔬分类科研实验
- 商超自助收银视觉识别系统
- 智慧农业大棚果蔬智能分拣设备
- 生鲜电商APP拍照识物、食材识别小程序
- 食堂食材自动统计、农产品溯源视觉模块
#计算机视觉 #果蔬图像分类 #深度学习数据集 #PyTorch迁移学习 #生鲜AI识别 #图像数据集划分 #ResNet实战 #智慧农业视觉 #多分类训练代码 #ImageFolder数据集