ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于ResNet18的水果蔬菜识别系统实战(含PyQt5界面与Grad-CAM可视化)

基于ResNet18的水果蔬菜识别系统实战(含PyQt5界面与Grad-CAM可视化) 简介本资源是一套面向计算机相关专业学生的毕业设计与课程设计实践项目基于Python与深度学习CNN构建水果蔬菜图像识别系统涵盖完整训练流程、GUI交互界面及模型评估可视化。资源共78个文件含16个核心Python脚本如train_cnn.py、test_model.py、window.py、40张测试与示例图像png/jpeg格式、2份PDF文档含完整论文《基于卷积神经网络的图像识别设计与实现》及README说明、8个文本配置与日志文件整体压缩包仅12.72MB轻量易部署。已有111人下载学习适合本科生开展课设、毕设或自主深度学习入门实践。用户可直接运行训练与测试代码获取Loss/Accuracy曲线图、热力图及模型性能报告项目模块清晰——数据划分、增强、训练、测试、界面封装全流程覆盖且附带登录主界面与结果展示逻辑便于理解工程化落地细节亦支持二次开发与算法优化。1. 水果蔬菜识别系统一个能跑通、能改、能交毕设的CNN实战项目不是玩具模型你花三天搭好环境配好CUDA下载完ImageNet子集结果训练到第2个epoch就OOM——这种翻车在课设里太常见了。但这个项目不一样它用真实采集的32类水果蔬菜苹果、梨、西红柿、胡萝卜、圣女果、大葱、芒果、大白菜…数据量控制在1200张/类以内ResNet18轻量化结构迁移微调策略单卡GTX1660就能训完带完整PyQt5图形界面支持拖图识别、历史记录、热力图可视化训练曲线、混淆矩阵、准确率/损失图全自动生成附带可直接答辩的论文PDF含算法原理、数据增强细节、消融实验对比所有脚本都加了中文注释和路径容错处理。它不是“教你怎么写CNN”而是“给你一个能立刻运行、改两行代码就能换数据集、加一个按钮就能导出Excel报告”的生产级最小闭环。适合计算机、人工智能、物联网、食品工程等专业本科生做毕业设计或课程设计——尤其适合那种想交差但又怕答辩被问住的同学每个模块都有出处、每张图都有生成逻辑、每个参数都有调整依据。别再找“Python水果识别”搜出一堆Kaggle搬运工了这是真正在实验室拍图、标注、调参、封装、写论文走完全流程的实操包。2. 从解压到首屏五步启动识别系统避开90%新手卡点2.1 解压与路径规范为什么必须重命名中文路径会触发哪些底层报错项目明确强调“项目名字和路径不要有中文”这不是客套话。fruit_vegetables_master文件夹若放在D:\我的毕设\python项目\下train_cnn.py中os.path.join(data, train)会拼出D:\我的毕设\python项目\data\train而cv2.imread()在Windows下对UTF-8路径解析不稳定常返回None更致命的是torchvision.datasets.ImageFolder依赖os.listdir()该函数在Python 3.9中对含中文路径的目录遍历可能抛出UnicodeDecodeError: gbk codec cant decode byte 0xXX—— 这错误不报在train_cnn.py里而藏在DataLoader初始化时debug成本极高。正确做法# 假设你下载到 Downloads 目录 cd ~/Downloads unzip python开发基于深度学习CNN网络的水果蔬菜识别系统论文报告界面和评估曲线.zip mv python开发基于深度学习CNN网络的水果蔬菜识别系统论文报告界面和评估曲线 fruit_veg_cnn_project cd fruit_veg_cnn_project提示fruit_veg_cnn_project是推荐命名避免下划线过多如fruit_vegetable_recognition_system易输错也禁用空格和特殊符号,(,)。所有后续命令均以此为根目录。2.2 环境搭建conda vs pip为什么必须指定torch版本项目未提供environment.yml但train_cnn.py头部明确写了import torch; print(torch.__version__)—— 实测要求torch1.10.0,1.13.0。原因在于torchvision0.11.0项目中requirements.txt隐含版本仅兼容torch 1.10.x的CUDA11.3后端若装torch 2.0torch.nn.functional.interpolate的align_corners默认值变更导致Data_enhancement.py中的随机裁剪尺寸计算偏移训练时RuntimeError: size mismatchPyQt55.15.4与Python 3.9兼容性最佳高版本PyQt6会因信号槽机制变化导致window.py中self.btn_predict.clicked.connect(self.predict_image)失效。执行以下命令推荐conda隔离性强conda create -n fruit_cnn python3.9 conda activate fruit_cnn pip install torch1.11.0cu113 torchvision0.12.0cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install PyQt55.15.4 opencv-python4.7.0.72 numpy1.23.5 scikit-learn1.2.2 matplotlib3.7.1 pandas1.5.3注意cu113表示CUDA 11.3若你用AMD显卡或无GPU请替换为cpu版本torch1.11.0但训练速度将下降5倍以上。2.3 数据准备get_data.py不是万能的手动补漏三原则get_data.py功能是自动从fruit_vegetables_master目录按文件夹名如apple,carrot创建data/train和data/val结构。但它不校验图片质量有些.jpeg实际是损坏的EXIF头cv2.imread返回Nonetest_images中的西红柿.png有透明通道RGBAcv2.imread默认读BGRAlpha层丢失导致颜色失真部分图片分辨率低于224×224CNN输入要求Data_enhancement.py的RandomResizedCrop(224)会放大模糊。补漏操作在fruit_veg_cnn_project根目录执行# check_images.py —— 自查脚本自行创建 import cv2 import os from pathlib import Path root Path(fruit_vegetables_master) for cls_dir in root.iterdir(): if not cls_dir.is_dir(): continue for img_path in cls_dir.glob(*.*): if img_path.suffix.lower() not in [.jpg, .jpeg, .png]: continue try: img cv2.imread(str(img_path)) if img is None: print(f[ERROR] Corrupted: {img_path}) continue if img.shape[0] 224 or img.shape[1] 224: print(f[WARN] Small resolution: {img_path} - {img.shape}) except Exception as e: print(f[EXCEPT] {img_path}: {e})运行后删除报错图片对小图用PIL.Image.resize((224,224), resampleImage.LANCZOS)批量重采样。2.4 启动主界面login_main.py的隐藏入口与调试开关双击login_main.py可能黑窗一闪而过——因为默认启用了登录验证login.py。但项目实际未部署数据库登录逻辑是硬编码校验# login.py 第42行 if username admin and password 123456: self.accept() # 跳转主界面 else: QMessageBox.warning(self, 错误, 用户名或密码错误)快速绕过登录仅调试用打开login_main.py找到if __name__ __main__:下方的login_window LoginWindow()在其后插入# 强制跳过登录临时调试用 from window import MainWindow main_win MainWindow() main_win.show() sys.exit(app.exec_())删掉原login_window.show()行。这样启动即进主界面省去每次输密码。2.5 首次预测验证用test_images里的苹果.jpeg测通整个pipeline主界面点击“选择图片” → 选test_images/苹果.jpeg→ 点“识别” → 应显示左侧原图自动缩放至400×300右侧识别结果“苹果 (置信度: 0.92)”下方热力图heatmap_test.png覆盖在原图上的红色高亮区域标出CNN认为最关键的像素控制台输出Predicted class: apple | Confidence: 0.9231若失败立即检查window.py第187行model torch.load(results/cnn_model.pth, map_locationcpu)—— 确保results/目录下存在该文件首次运行需先训练test_model.py是否被误删它负责生成heatmap_test.pngimages/目录是否有写入权限热力图保存路径。3. 训练自己的CNN模型从数据切分到收敛监控参数怎么设才不玄学3.1 数据切分逻辑data_split.py的7:2:1比例与random_state42的深意data_split.py执行train_test_split时固定random_state42这不是随便选的数字。项目原始数据集共32类每类约1100张图data_split.py将其按7:2:1切分为train770张/类、val220张/类、test110张/类。关键点在于val集用于早停Early Stoppingtest集只用于最终评估绝不参与训练或调参random_state42保证每次运行切分结果一致便于复现实验比如你发现某次val_acc突然下降可回溯是否数据分布偏移切分前已对每类图片按文件名排序random_state实质是打乱顺序再切分避免按拍摄时间集中导致val集全是后期模糊图。修改切分比例如需更多验证样本# data_split.py 第25行 X_train, X_temp, y_train, y_temp train_test_split( all_files, all_labels, test_size0.3, # 原0.3 → 改为0.4则valtest共40% random_state42, stratifyall_labels ) X_val, X_test, y_val, y_test train_test_split( X_temp, y_temp, test_size0.333, # 原0.333 → 改为0.5则val:test1:1 random_state42, stratifyy_temp )3.2 数据增强策略Data_enhancement.py的四步组合为何比单纯RandomHorizontalFlip强项目没用AutoAugment或RandAugment而是手写四步确定性增强RandomResizedCrop(224, scale(0.8,1.0))—— 模拟不同距离拍摄强制CNN学习尺度不变性ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0.1)—— 抵抗光照变化菜市场白炽灯 vs 手机闪光灯RandomRotation(degrees15)—— 解决水果摆放角度随意性GaussianBlur(kernel_size(3,3), sigma(0.1,2.0))—— 模拟手机虚焦提升鲁棒性。为什么不用RandomHorizontalFlip因为蔬菜如大葱、胡萝卜具有明显方向性水平翻转会生成非真实样本倒置的葱看起来不像葱反而降低泛化能力。项目作者在readme.txt中注明“经消融实验关闭HFlip使val_acc提升1.2%”。3.3 CNN架构选择train_cnn.py中的ResNet18vsVGG16实测对比train_cnn.py默认加载torchvision.models.resnet18(pretrainedTrue)而非从头训练。原因很实在模型参数量单卡训练耗时GTX1660val_acc30epoch内存占用ResNet1811.7M22min94.3%3.2GBVGG16138M87min93.1%5.8GB自定义CNN5层卷积2.1M15min89.7%1.9GBResNet18的残差连接有效缓解梯度消失使小数据集上微调更稳定VGG16参数爆炸且无残差在1200张/类数据上容易过拟合。项目在cnn_train.txt中记录第12epoch后ResNet18的val_loss开始平稳下降而VGG16在第8epoch即出现val_loss震荡上升。3.4 训练超参配置train_cnn.py中batch_size32与lr0.001的边界测试batch_size设为32是权衡结果太小16梯度更新噪声大loss曲线抖动剧烈收敛慢太大64显存溢出GTX1660仅6GB或导致BN层统计失效batch太小则mean/var不准32在1660上刚好占满显存85%利用效率最高。学习率lr0.001来自阶梯式搜索lr0.01前5epoch loss暴跌但val_acc停滞过快收敛到局部最优lr0.0001loss缓慢下降30epoch后val_acc仅91.2%lr0.001第10epoch val_acc达93.5%第25epoch达94.3%峰值。动态学习率调整在train_cnn.py中加入# 第120行后插入 scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemax, factor0.5, patience3, verboseTrue ) # 在train loop末尾添加 scheduler.step(val_acc) # 当val_acc连续3轮不升lr×0.53.5 模型评估与可视化results/目录下6个文件的生成逻辑与诊断价值训练完成后results/自动生成6个关键文件每个都是诊断依据文件名生成脚本诊断价值Loss_cnn.pngtrain_cnn.py末尾plt.savefig()观察train/val loss是否收敛、有无过拟合val loss上扬Accuracy_cnn.png同上val_acc是否达平台期峰值是否≥94%cnn_train.txttrain_cnn.py的print()日志记录每epoch的loss/acc定位最佳checkpoint如第23epochmodel_test.txttest_model.py测试集整体acc、每类f1-score暴露长尾类别如“圣女果”易误判为“西红柿”heatmap_cnn.pngtest_model.py的Grad-CAM实现定位CNN关注区域是否合理如识别苹果时高亮果皮而非背景Loss_test.pngtest_model.py测试集loss分布检测异常样本loss2.0的图需人工复查注意heatmap_cnn.png依赖torchcam库需额外安装pip install torchcam否则test_model.py报错ModuleNotFoundError。4. 图形界面深度定制PyQt5控件改造与业务逻辑注入4.1 主界面布局解析window.py中QGridLayout的三层嵌套结构window.py的UI不是用Qt Designer拖出来的而是纯代码构建结构清晰顶层QGridLayoutself.gridLayout划分三大区域——左图区self.label_img、右结果区self.text_result、底部按钮区self.btn_predict等中间层QVBoxLayoutself.layout_right垂直堆叠“识别结果”标签、“置信度”进度条、“热力图”标签底层QHBoxLayoutself.layout_buttons水平排列“选择图片”、“识别”、“清空”按钮保证按钮宽度自适应。改造示例增加“导出结果”按钮# window.py 第132行在 self.layout_buttons.addWidget(self.btn_clear) 后添加 self.btn_export QPushButton(导出结果) self.btn_export.clicked.connect(self.export_result) self.layout_buttons.addWidget(self.btn_export) def export_result(self): if not hasattr(self, pred_class): return import pandas as pd result_df pd.DataFrame([{ filename: self.current_img_path.name, predicted_class: self.pred_class, confidence: f{self.pred_conf:.4f}, timestamp: pd.Timestamp.now().strftime(%Y-%m-%d %H:%M:%S) }]) result_df.to_excel(recognition_report.xlsx, indexFalse) QMessageBox.information(self, 成功, 结果已导出至 recognition_report.xlsx)4.2 图片加载与预处理self.load_image()中的OpenCV-PIL-Tensor三段式转换界面选图后self.load_image()执行严格转换链cv2.imread(path)→ BGR格式NumPy数组cv2.cvtColor(img, cv2.COLOR_BGR2RGB)→ 转RGB适配PIL显示PIL.Image.fromarray(img)→ 创建PIL Image对象self.transform(PIL_img)→ 应用transforms.Compose含ToTensor、Normalize→ 归一化Tensortensor.unsqueeze(0)→ 增加batch维度模型输入要求4D tensor。为什么不用torchvision.io.read_image()因为read_image()对PNG透明通道处理不一致且无法控制色彩空间转换。项目作者在readme.txt中说明“实测cv2PIL链路在Windows/macOS/Linux三端输出Tensor数值完全一致read_image在macOS上偶发Alpha通道残留”。4.3 热力图生成原理test_model.py中Grad-CAM的11行核心实现test_model.py的热力图不是简单叠加而是标准Grad-CAM# test_model.py 第89行 def generate_heatmap(model, img_tensor, target_layer): model.eval() features [] def hook_fn(module, input, output): features.append(output) handle target_layer.register_forward_hook(hook_fn) output model(img_tensor) pred_class output.argmax(dim1).item() score output[0, pred_class] model.zero_grad() score.backward() gradients features[0].grad # [1, C, H, W] weights torch.mean(gradients, dim[2,3], keepdimTrue) # [1, C, 1, 1] cam torch.sum(weights * features[0], dim1, keepdimTrue) # [1, 1, H, W] cam F.relu(cam) # ReLU激活 cam F.interpolate(cam, size(224,224), modebilinear) # 插值回原图尺寸 handle.remove() return cam.squeeze().detach().numpy()此实现确保热力图与模型决策强相关——红色区域确实是CNN分类依据而非伪影。4.4 历史记录功能self.history_list的内存管理与持久化方案当前window.py中历史记录仅存在内存self.history []关程序即丢失。升级为SQLite持久化# window.py 开头导入 import sqlite3 from datetime import datetime # __init__中初始化DB self.db_conn sqlite3.connect(recognition_history.db) self.db_conn.execute(CREATE TABLE IF NOT EXISTS history ( id INTEGER PRIMARY KEY AUTOINCREMENT, filename TEXT, class_name TEXT, confidence REAL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )) # predict_image()末尾插入 self.db_conn.execute( INSERT INTO history (filename, class_name, confidence) VALUES (?, ?, ?), (self.current_img_path.name, self.pred_class, self.pred_conf) ) self.db_conn.commit()再加一个“查看历史”按钮用QTableView展示数据库内容。4.5 避坑PyQt5界面开发的五个血泪经验现象 → 原因 → 解决界面卡顿、拖拽迟滞QLabel.setPixmap()直接传大图如4000×3000PyQt5渲染压力过大。→ 原因setPixmap()不自动缩放强制重绘整图。→ 解决pixmap QPixmap.fromImage(qimage.scaled(400, 300, Qt.KeepAspectRatio))先缩放再传。热力图显示为全黑或全白cv2.applyColorMap()输入值范围非0-255。→ 原因Grad-CAM输出cam是float32范围[0, max_val]applyColorMap要求uint8[0,255]。→ 解决cam_uint8 np.uint8(255 * (cam - cam.min()) / (cam.max() - cam.min()))。多图连续识别后内存泄漏self.label_img.clear()未释放旧Pixmap。→ 原因QLabel持有Pixmap引用clear()只清显示不释放内存。→ 解决self.label_img.setPixmap(QPixmap())强制置空。Windows下按钮文字乱码.ui文件或代码中使用了非系统字体。→ 原因PyQt5默认用系统字体中文Win10用微软雅黑但某些精简版系统缺失。→ 解决self.btn_predict.setFont(QFont(SimSun, 10))显式指定宋体。打包成exe后找不到results/cnn_model.pthpyinstaller未自动包含results/目录。→ 原因--add-data参数未指定。→ 解决pyinstaller --add-data results;results --onefile login_main.py。5. 毕设答辩高频问题预演从原理到落地的六道硬核问答5.1 “为什么用CNN而不是Transformer”——直击模型选型本质这个问题90%的答辩老师必问。不能答“因为CNN流行”要拆解任务特性输入规模水果蔬菜图平均分辨率1280×720ViT需将图切块如16×16生成5625个token显存占用是ResNet18的3.2倍数据规模总图数4万ViT在小数据上易过拟合参考ICML 2022《When Do Vision Transformers Generalize?》实时性要求毕设演示需1s响应ResNet18在GTX1660上推理耗时83msViT-base需320ms可解释性答辩需展示热力图CNN的Grad-CAM成熟稳定ViT的Attention Rollout在小模型上效果差。回答模板“我们对比了ResNet18和ViT-TinyViT在val_acc上仅高0.7%但推理慢3.8倍且热力图噪声大。CNN在此任务中是更务实的选择。”5.2 “数据增强是否引入了虚假样本”——用消融实验数据反证老师可能质疑ColorJitter过度扭曲颜色。拿出cnn_train.txt中的消融数据增强策略train_accval_acctest_acc无增强99.2%87.3%86.1%仅RandomResizedCrop98.1%91.5%90.2%全增强含ColorJitter97.4%94.3%93.7%关键结论ColorJitter单独贡献2.8% val_acc因为它让模型学会忽略光照干扰——实测test_images/西红柿.png在暗光下识别率从63%升至89%。5.3 “界面如何保证跨平台”——指出PyQt5的三个兼容性锚点字体渲染QApplication.setFont(QFont(Microsoft YaHei))→ Windows用微软雅黑macOS自动fallback为PingFang SCLinux用Noto Sans路径分隔符os.path.join(results, model.pth)→ 自动适配/或\图像加载cv2.imread()QPixmap.fromImage()链路绕过PyQt5对.jpeg格式的Linux兼容性问题某些发行版缺libjpeg-turbo。5.4 “模型精度94.3%是否足够”——用混淆矩阵定位改进点打开model_test.txt找到混淆矩阵片段apple: [92 3 0 ...] # 92张苹果正确3张误判为pear pear: [1 88 2 ...] # pear误判apple 1张误判apple 2张 tomato: [0 0 95 ...] # 西红柿几乎无误判 cherry_tomato: [5 0 0 ...] # 圣女果5张全判为tomato回答重点精度够用但长尾问题明显——圣女果和西红柿外观相似需增加二者间的对抗样本如合成圣女果西红柿混合图或在损失函数中为cherry_tomato类加权重weight1.5。5.5 “能否识别没见过的水果”——坦诚边界并给出升级路径不能。CNN是监督学习只认识训练集32类。但可延伸零样本扩展接入CLIP模型用文本描述如“紫色表皮、椭圆形、表面有绒毛”匹配新水果增量学习冻结ResNet18前4层只微调最后两层分类头用10张新水果图即可适配主动学习界面加“不确定”按钮当max(confidence)0.7时存入uncertain/目录供人工标注。答辩话术“当前系统聚焦于32类高频果蔬这是农业质检的实际需求。扩展能力已在README.md的‘Future Work’章节说明。”5.6 “论文中‘消融实验’具体做了什么”——用train_cnn.py的注释行证明指向train_cnn.py第58行注释# 消融实验配置答辩时展示此段代码 # ABALATION_MODE no_jitter # 关闭ColorJitter # ABALATION_MODE no_crop # 关闭RandomResizedCrop # ABALATION_MODE no_blur # 关闭GaussianBlur ABALATION_MODE full # 全增强默认再展示cnn_train.txt中三组实验的val_acc对比表格——这就是最硬的证据。6. 从交差到真懂我坚持的三个“强制动作”让毕设变成技术资产做完这个项目我养成了三个雷打不动的习惯它们让“交差作业”变成了真正属于我的技术资产6.1 每次修改代码前先跑通test_model.py的单元测试test_model.py不只是生成热力图的脚本它本身就是一个微型测试框架。我在test_model.py开头加了这段def test_model_integrity(): 验证模型加载、推理、热力图生成全流程 model torch.load(results/cnn_model.pth, map_locationcpu) assert model.training False, Model must be in eval mode dummy_input torch.randn(1, 3, 224, 224) with torch.no_grad(): out model(dummy_input) assert out.shape (1, 32), Output shape mismatch # 测试热力图生成不保存只验证不崩溃 cam generate_heatmap(model, dummy_input, model.layer4[-1]) assert cam.shape (224, 224), CAM shape error print(✅ All integrity tests passed) if __name__ __main__: test_model_integrity() # 后续原有逻辑...现在无论我改train_cnn.py的损失函数还是动window.py的按钮逻辑第一件事就是python test_model.py。它能在1秒内告诉我模型结构没崩、推理接口没断、热力图引擎还活着。这比等训练完再看results/里有没有图高效十倍。6.2 所有实验结果必须用git tag固化拒绝“最后一次commit就是最好的”我给每次关键实验打taggit tag -a v1.0_baseline -m Baseline: ResNet18, lr0.001, no scheduler git tag -a v1.1_lr_scheduler -m Added ReduceLROnPlateau, val_acc 0.8% git tag -a v1.2_data_aug -m Added ColorJitter, val_acc 2.1%答辩前git describe --tags输出v1.2_data_aug-3-gabc123老师问“你说加了ColorJitter效果在哪”我直接git checkout v1.1_lr_scheduler跑一遍train_cnn.py再git checkout v1.2_data_aug两份Accuracy_cnn.png并排一放——证据链闭环。没有tag你的“优化”就是一句空话。6.3 论文图表全部从代码生成拒绝PS手动调色train_cnn.py末尾的绘图代码我强制加了三行plt.figure(figsize(12, 4)) # ... 绘制loss/acc曲线 plt.savefig(fresults/Loss_cnn_{datetime.now().strftime(%Y%m%d_%H%M%S)}.png, bbox_inchestight, dpi300) # 300dpi保证印刷清晰 plt.close() # 同时生成CSV供论文引用 pd.DataFrame({ epoch: list(range(len(train_losses))), train_loss: train_losses, val_loss: val_losses, train_acc: train_accs, val_acc: val_accs }).to_csv(results/training_log.csv, indexFalse)答辩PPT里的每张曲线图都对应results/里一个带时间戳的PNG和一个CSV。老师问“第15epoch的val_acc是多少”我不用翻论文cat results/training_log.csv | sed -n 16p—— 秒回。这种确定性是“我调了好久觉得差不多”给不了的底气。从那以后我每次开新项目都强制走一遍这三步跑单元测试、打git tag、代码生成图表。它不增加工作量却把“做完”和“做好”划出了清晰的界线。希望帮到你。本文还有配套的精品资源点击获取
返回列表