news 2026/10/10 23:33:38

果蔬识别项目实战:12类标签与CNN/MobileNet训练脚本解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
果蔬识别项目实战:12类标签与CNN/MobileNet训练脚本解析

简介:这是一套面向深度学习入门者与计算机视觉方向学生的YOLOv5果蔬识别完整实践资源,围绕土豆、圣女果、大白菜、大葱、梨、胡萝卜、芒果、苹果、西红柿、韭菜、香蕉、黄瓜等十余类常见果蔬的检测任务展开,可用于课程设计、毕业项目或算法练手。压缩包共56个文件,约94.07MB,包含14个Python脚本、12个PNG与9个JPG及6个JPEG图像、6个TXT说明、4个XML标注、2个H5权重文件,以及Markdown文档和工程配置,覆盖数据读取、训练、测试与实时摄像头推理等环节。资源内提供CNN与MobileNet两套训练流程、训练过程记录、测试记录、热力图与结果图,并附readme说明,便于对照复现与调参。目前已有3770人学习下载,适合希望快速跑通果蔬检测全流程、理解数据划分与模型评估的读者参考。

1. 果蔬识别项目拆包:从 12 类标签到两套可跑通的训练脚本

前阵子帮一个做智慧食堂的朋友看需求,他们想在结算台自动认出餐盘里的果蔬品类,减少人工点选。市面上现成的商用 SDK 报价不低,而且对「大葱」「韭菜」这种细长叶菜识别率一般。翻到这个yolov5果蔬识别数据集系统+代码+教程.zip时,我第一反应是标题有点「标题党」——里面其实同时塞了 TensorFlow 2.3 的 CNN/MobileNet 分类方案和一套 YOLOv5 相关的数据组织思路,不是纯 YOLOv5 工程。但拆完发现,它把「数据集 + 两套训练脚本 + PyQt 界面 + 实时摄像头推理」这条链路走通了,标签覆盖土豆、圣女果、大白菜、大葱、梨、胡萝卜、芒果、苹果、西红柿、韭菜、香蕉、黄瓜共 12 类,对想快速搭一个果蔬识别 demo 或做课程设计的人来说,省掉了从零攒数据、搭界面的时间。这篇就把包里的目录结构、训练参数、界面调用和几个容易翻车的点讲清楚,适合刚接触图像分类、想拿一个完整小项目练手的人。

2. 先看清包里到底有什么:目录结构与两套模型的分工

2.1 从文件清单反推工程结构

拿到压缩包先别急着解压跑代码,把文件清单过一遍能省很多事。这个包大致分四块:数据与模型产物、训练脚本、界面脚本、工具脚本。

类别代表文件作用
模型权重cnn_fv.h5、mobilenet_fv.h5训练好的 CNN 与 MobileNet 权重,可直接加载推理
训练脚本train_cnn.py、train_mobilenet.py、old_train_mobilenet.py两套分类模型的训练入口
界面脚本window.py、window_new.py、window_up_camera.py、window_realtime.pyPyQt 主界面、上传图片、摄像头实时识别
工具脚本data_split.py、get_data.py、data_read.py、remove_wrong_image.py、jpeg2jpg.py数据划分、读取、清洗、格式转换
训练记录cnn训练过程.txt、mobilenet训练过程.txt、测试记录.txt准确率、loss 变化留痕
结果图results_cnn.png、results_mobilenet.png、heatmap_cnn.png、heatmap_mobilenet.png混淆矩阵与热力图

这里有个关键认知:cnn_fv.h5和mobilenet_fv.h5是图像分类权重,不是 YOLOv5 的检测权重。也就是说,这套代码默认的推理方式是「一张图里主体是什么果蔬」,而不是「一张图里框出多个果蔬并标位置」。如果你的场景是餐盘里多个菜品混在一起要分别定位,得自己补检测标注,这点后面避坑章会细说。

2.2 为什么同时给 CNN 和 MobileNet 两套

包里两套模型不是冗余,是给你做精度和速度的取舍。普通 CNN 结构简单、参数量大,在果蔬这种类间差异明显的任务上容易训到较高准确率,但推理慢;MobileNet 用深度可分离卷积压参数量,适合往边缘设备或实时摄像头上放。

我一般这么选:如果是离线批量识别图片、追求准确率,先跑train_cnn.py;如果要接摄像头做实时,优先train_mobilenet.py。包里results_cnn.png和results_mobilenet.png就是两套的验证结果对比,heatmap_*.png能看出哪些类别容易混——比如大葱和韭菜这种细长绿叶菜,热力图上经常互相串。

2.3 环境依赖与版本约束

requirements.txt里锁的是 TensorFlow 2.3 时代的组合。这个版本号很关键,TF 2.3 对 CUDA 和 cuDNN 有明确对应关系,装错了就是一堆DLL load failed。CPU 版相对省心,.idea里那个trash_tf2.3_cpu.iml也暗示作者主要用 CPU 环境跑。

# 建议单独建虚拟环境,别和系统 Python 混 python -m venv venv_fv # Windows 激活 venv_fv\Scripts\activate # Linux / macOS 激活 source venv_fv/bin/activate # 按 requirements 安装,注意 TF 版本 pip install -r requirements.txt

逻辑说明:虚拟环境隔离是为了避免 TF 2.3 和你机器上其他项目的 TF 2.x 冲突。参数上,如果你有 NVIDIA 显卡且想用 GPU,需要把tensorflow换成tensorflow-gpu==2.3.0,并确认驱动、CUDA 10.1、cuDNN 7.6 三者匹配;没把握就直接用 CPU 版,训练慢但不会卡在环境上。

3. 数据准备与训练:从 data_split 到两个 train 脚本

3.1 数据目录怎么摆、怎么划分

分类任务的数据组织比检测简单,常见做法是按类别建文件夹,每个文件夹放该类图片。包里data_split.py负责把原始数据切成训练集和验证集,get_data.py和data_read.py负责读取。

# data_split.py 的核心逻辑示意(按实际脚本调整路径) import os import shutil import random RAW_DIR = "dataset/raw" # 原始数据,按类别分子文件夹 TRAIN_DIR = "dataset/train" VAL_DIR = "dataset/val" SPLIT_RATIO = 0.8 # 训练集占比 random.seed(42) # 固定随机种子,保证可复现 for cls in os.listdir(RAW_DIR): cls_path = os.path.join(RAW_DIR, cls) if not os.path.isdir(cls_path): continue imgs = [f for f in os.listdir(cls_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))] random.shuffle(imgs) n_train = int(len(imgs) * SPLIT_RATIO) for i, img in enumerate(imgs): dst_root = TRAIN_DIR if i < n_train else VAL_DIR dst_dir = os.path.join(dst_root, cls) os.makedirs(dst_dir, exist_ok=True) shutil.copy(os.path.join(cls_path, img), os.path.join(dst_dir, img))

逻辑说明:按类别分别划分,而不是把所有图混在一起随机切,这样能保证每个类别在训练集和验证集里都有。SPLIT_RATIO设 0.8 是分类任务的常见起点,数据量小可以调到 0.7。random.seed(42)是后悔药——不固定种子,每次划分结果不同,复现实验时对不上账。

3.2 训练 CNN 与 MobileNet 的参数差异

两个训练脚本结构类似,差别在模型构建部分。train_cnn.py用普通卷积堆叠,train_mobilenet.py用 MobileNet 骨干。

# train_mobilenet.py 关键参数示意 import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.applications import MobileNetV2 from tensorflow.keras import layers, models IMG_SIZE = (224, 224) BATCH_SIZE = 32 EPOCHS = 30 LR = 1e-4 NUM_CLASSES = 12 # 数据增强,缓解小数据集过拟合 train_gen = ImageDataGenerator( rescale=1./255, rotation_range=20, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True, zoom_range=0.1 ) train_data = train_gen.flow_from_directory( "dataset/train", target_size=IMG_SIZE, batch_size=BATCH_SIZE, class_mode="categorical" ) # 加载预训练骨干,去掉顶层 base = MobileNetV2(input_shape=(224, 224, 3), include_top=False, weights="imagenet") base.trainable = False # 先冻结,只训分类头 model = models.Sequential([ base, layers.GlobalAveragePooling2D(), layers.Dense(128, activation="relu"), layers.Dropout(0.5), layers.Dense(NUM_CLASSES, activation="softmax") ]) model.compile(optimizer=tf.keras.optimizers.Adam(LR), loss="categorical_crossentropy", metrics=["accuracy"]) model.fit(train_data, epochs=EPOCHS) model.save("mobilenet_fv.h5")

逻辑说明:weights="imagenet"是迁移学习的关键,果蔬数据集通常几千张级别,从零训容易过拟合,借 ImageNet 预训练权重能明显稳住。base.trainable = False先冻结骨干只训分类头,等 loss 平稳后可以解冻部分层做微调,学习率要再降一个量级。Dropout(0.5)和增强参数是防过拟合的常规手段。EPOCHS=30不是死的,看mobilenet训练过程.txt里验证准确率什么时候不再涨就停。

3.3 训练过程怎么看、结果图怎么读

包里留了cnn训练过程.txt和mobilenet训练过程.txt,这是血泪经验——很多人训完只看最后一行准确率,不看曲线。正确做法是盯验证集 loss:如果训练 loss 一直降、验证 loss 先降后升,就是过拟合,该加增强或早停。

results_*.png是混淆矩阵,重点看对角线外的格子。果蔬里最容易混的是大葱/韭菜、苹果/梨(颜色接近时),如果这两类互相误判多,说明特征区分度不够,可以考虑加颜色直方图特征或补更多难例。heatmap_*.png是 Grad-CAM 类热力图,能看出模型到底在看图片哪个区域——如果热力集中在背景而不是果蔬主体,说明数据里背景太单一,模型学到了捷径。

4. 界面与实时推理:window 系列脚本怎么串起来

4.1 PyQt 界面加载模型的基本流程

window.py和window_new.py是主界面,window_up_camera.py管上传图片识别,window_realtime.py管摄像头实时。核心都是「加载 h5 权重 → 预处理输入 → 前向推理 → 显示类别」。

# window_up_camera.py 推理核心示意 import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing import image CLASS_NAMES = ['土豆', '圣女果', '大白菜', '大葱', '梨', '胡萝卜', '芒果', '苹果', '西红柿', '韭菜', '香蕉', '黄瓜'] model = load_model("mobilenet_fv.h5") def predict(img_path): img = image.load_img(img_path, target_size=(224, 224)) arr = image.img_to_array(img) / 255.0 arr = np.expand_dims(arr, axis=0) # 加 batch 维度 preds = model.predict(arr)[0] idx = int(np.argmax(preds)) return CLASS_NAMES[idx], float(preds[idx])

逻辑说明:target_size必须和训练时一致,训练用 224 推理用 299 会直接报维度错。/255.0归一化也要和训练时的rescale对齐,训练归一化了推理不归一化,结果会离谱。np.expand_dims加 batch 维度是因为model.predict要的是(N, H, W, C)。CLASS_NAMES顺序必须和训练时flow_from_directory的类别索引一致,否则标签全错位——这是最常见的翻车点。

4.2 摄像头实时推理的帧率与线程

window_realtime.py比上传图片复杂,因为要持续读摄像头帧。常见做法是把推理放子线程,主线程只管显示,否则界面会卡死。

# 实时推理线程示意 import cv2 import threading class InferThread(threading.Thread): def __init__(self, model, class_names): super().__init__() self.model = model self.class_names = class_names self.frame = None self.running = True def run(self): while self.running: if self.frame is None: continue img = cv2.resize(self.frame, (224, 224)) arr = img[..., ::-1] / 255.0 # BGR 转 RGB arr = arr[None, ...] preds = self.model.predict(arr, verbose=0)[0] idx = int(preds.argmax()) # 把结果回传主线程显示,这里省略信号机制

逻辑说明:OpenCV 读进来是 BGR,训练时用的是 RGB,img[..., ::-1]这步不能省,省了颜色通道反了,准确率会掉。verbose=0关掉每帧打印,不然控制台刷屏拖慢速度。实时场景下 MobileNet 比普通 CNN 合适,普通 CNN 每帧几百毫秒,摄像头画面会一顿一顿。

4.3 工具脚本的用途别忽略

remove_wrong_image.py用来清理损坏或标注错的图,jpeg2jpg.py统一扩展名,remove_func.py是界面里删功能的辅助。这些脚本看着不起眼,但数据清洗阶段能省大量手工活。我一般先跑jpeg2jpg.py统一格式,再跑remove_wrong_image.py清掉打不开的图,最后才data_split.py,顺序反了会把坏图带进训练集。

5. 避坑与排查:这几处不处理准翻车

5.1 现象:加载 h5 报「Unknown layer」或维度不匹配

原因:TensorFlow 版本和保存权重时的版本不一致,或者推理时target_size和训练不一致。TF 2.3 存的模型用 TF 2.10 加载,自定义层可能认不出来。

解决:严格按requirements.txt装 TF 2.3;确认推理输入尺寸和训练一致;如果必须换版本,用tf.keras.models.load_model(path, compile=False)先跳过编译再手动 compile。

5.2 现象:界面能跑但识别结果全是同一类

原因:CLASS_NAMES顺序和训练时类别索引对不上,或者归一化没做。flow_from_directory是按文件夹名字母序生成索引的,你手写的列表顺序如果和它不一致,argmax 出来的索引映射就全错。

解决:训练完打印train_data.class_indices,把那个字典的键值对固化下来,界面里直接用同一份映射,别手敲。

5.3 现象:摄像头实时画面卡顿、延迟越来越高

原因:推理和显示在同一线程,或者每帧都重新加载模型。模型加载一次要几百毫秒到几秒,放循环里必卡。

解决:模型在程序启动时加载一次,全局持有;推理放子线程;适当跳帧,比如每 3 帧推理一次,中间帧复用上次结果。

5.4 现象:训练准确率高但实际拍照识别差

原因:数据集背景单一,模型学到了背景捷径。比如训练图全是白底,实际拍照是餐桌背景,直接崩。

解决:数据增强里加背景替换或随机裁剪;补拍真实场景图;看heatmap_*.png确认模型关注区域是不是果蔬主体。

5.5 现象:想检测多个果蔬但模型只给一个类别

原因:这套权重是分类模型,一张图只输出一个类别,不是检测。标题里的 YOLOv5 更多是数据组织思路的借名,包里没有 YOLOv5 的检测权重和yolov5*.pt文件。

解决:如果确实要框多个目标,得用 YOLOv5 重新标注检测数据集(每张图给 bbox 和类别),再走 YOLOv5 训练流程;这套分类代码可以作为预训练分类头或数据清洗的参考。

6. 进阶技巧:把分类模型接进 YOLOv5 检测链路的一个实用做法

如果你手上已经有这套 12 类果蔬分类权重,又想做「先检测再分类」的两阶段方案,有个省事的做法:用 YOLOv5 只训一个「果蔬」大类做检测,把框裁出来送进这里的 MobileNet 分类。这样检测模型不用区分 12 类,标注压力小很多,分类模型又能复用现成权重。

具体操作上,先拿 YOLOv5 官方流程训一个单类检测器,推理时对每个 bbox 裁剪并 resize 到 224,批量送进mobilenet_fv.h5。

# 两阶段推理:YOLOv5 检测 + MobileNet 分类 import cv2 import numpy as np from tensorflow.keras.models import load_model clf = load_model("mobilenet_fv.h5") CLASS_NAMES = ['土豆', '圣女果', '大白菜', '大葱', '梨', '胡萝卜', '芒果', '苹果', '西红柿', '韭菜', '香蕉', '黄瓜'] def classify_crop(crop_bgr): img = cv2.resize(crop_bgr, (224, 224)) arr = img[..., ::-1] / 255.0 arr = np.expand_dims(arr, axis=0) preds = clf.predict(arr, verbose=0)[0] return CLASS_NAMES[int(preds.argmax())], float(preds.max()) # boxes 来自 YOLOv5 推理结果,格式 [x1, y1, x2, y2] def two_stage(frame, boxes): results = [] for box in boxes: x1, y1, x2, y2 = map(int, box) crop = frame[y1:y2, x1:x2] if crop.size == 0: continue label, score = classify_crop(crop) results.append((box, label, score)) return results

逻辑说明:crop.size == 0是防御性判断,YOLO 偶尔会给出越界框,不判会崩。分类置信度低于阈值(比如 0.6)的可以标成「未知」,避免硬报错。这个两阶段方案的好处是检测和分类解耦,哪边效果差就单独换哪边,不用整体重训。

验证这套链路是否靠谱,我一般固定三步:先用测试记录.txt里提到的测试图跑一遍,看分类结果和记录是否一致;再拿手机拍几张真实餐桌图,看背景变化后准确率掉多少;最后看单帧总耗时,检测加分类如果超过 200ms,实时场景就得考虑量化或换更小的骨干。从那以后我每次接分类模型进检测链路,都强制先跑一遍「训练集外真实场景图」的验证,不然实验室准确率再高也不敢上线。希望帮到你。

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

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

RSNA肺炎检测数据集:VOC+YOLO双格式目标检测实战指南

简介&#xff1a;该数据集围绕RSNA肺炎检测场景构建&#xff0c;提供VOC与YOLO两种流行的目标检测标注格式&#xff0c;适配主流训练框架&#xff0c;方便算法工程师、科研人员以及医学影像智能分析方向的学习者使用&#xff0c;也可迁移至其他单类别目标检测任务。数据规模包含…

作者头像 李华
网站建设 2026/10/10 23:32:24

金融机器学习实践:分数差分、三重屏障与Purged K-Fold避免时间泄漏

简介&#xff1a;这份代码包围绕《金融机器学习进展》&#xff08;英文名Advances in Financial Machine Learning&#xff09;一书的精选练习&#xff0c;面向具备一定Python语言和机器学习基础的量化研究者、金融数据爱好者&#xff0c;提供了一套可在Jupyter环境下直接运行的…

作者头像 李华
网站建设 2026/10/10 23:26:38

烟雾检测数据集21578张图像YOLO实战:从XML标注到模型训练

简介&#xff1a;面向烟雾检测目标识别任务&#xff0c;提供一套适用于YOLO算法的数据标注资源&#xff0c;主要服务于计算机视觉学习者、AI算法工程师以及智慧消防、森林防火等场景的开发者。压缩包内共包含2000个XML标注文件&#xff0c;文件类型全部为XML&#xff0c;整体体…

作者头像 李华
网站建设 2026/10/10 23:24:59

后端开发第一课:从HTTP、接口到数据库的完整链路入门

后端开发这个方向&#xff0c;几乎每年都被拿出来讨论一遍。我见过不少刚转行或者刚入学的朋友&#xff0c;第一周还兴致勃勃&#xff0c;第二周就开始被各种名词轮番轰炸&#xff1a;接口、数据库、缓存、部署、框架、中间件……每个字都认识&#xff0c;连在一起就不知道在说…

作者头像 李华
网站建设 2026/10/10 23:19:18

论文提交前降AI率攻略:一周紧急改写技巧

论文提交前一周紧急降AI率攻略&#xff1a;时间紧也能搞定离提交论文还剩一周&#xff0c;打开学校检测系统一看&#xff0c;AI疑似率37%&#xff0c;旁边写着“疑似人工智能生成内容比例较高”。这一幕我见过太多次&#xff0c;每年毕业季都有学生半夜发消息问怎么办。先说明&…

作者头像 李华
网站建设 2026/10/10 23:17:19

AI数学论文撤稿事件:一个符号错误如何摧毁整篇证明

1. 事件还原&#xff1a;三篇论文被撤回的现场细节与时间线事情发生在上周三晚间&#xff0c;OpenAI官网的arXiv预印本页面突然出现三条状态更新——原本标注为“Submitted”&#xff08;已提交&#xff09;的三篇论文&#xff0c;状态悄然变更为“Retracted”&#xff08;已撤…

作者头像 李华