從零開始學習深度學習—水果分類之PyQt5App

一、項目背景?

? ? ? ? 本項目是“從零開始學習深度學習”系列中的第二個實戰項目,旨在實現第一個簡易App(圖像分類任務——水果分類),進一步地落地AI模型應用,幫助初學者初步了解模型落地。

? ? ? ? 基于PyQt5圖形界面的水果圖像分類系統,用戶可以通過加載模型、選擇圖像并一鍵完成圖像識別。

二、項目目標🚀:

? ? ? ? 基于PyQt5圖形界面實現以下功能:

  • 加載本地 .pth 訓練好的模型;

  • 加載本地圖像進行展示;

  • 自動完成圖像預處理(Resize、ToTensor、Normalize);

  • 使用模型完成預測并展示結果;

  • 界面美觀,交互友好。

三、適合人群🫵:

  • 深度學習零基礎或剛入門的學習者
  • 希望通過項目實戰學習BP神經網絡、卷積神經網絡模型搭建的開發者
  • 對圖像識別、分類應用感興趣的童鞋
  • 適用于想學習通過界面實現AI模型推理,

四、項目實戰?:

1.主界面構建

    def initUI(self):# 主窗口設置self.setWindowTitle("水果分類應用")self.setGeometry(100, 100, 800, 600)# 創建主窗口部件central_widget = QWidget()self.setCentralWidget(central_widget)# 創建主布局main_layout = QVBoxLayout()# 模型選擇部分model_layout = QHBoxLayout()model_label = QLabel("模型路徑:")self.model_path_edit = QtWidgets.QLineEdit()model_button = QPushButton("選擇模型")model_button.clicked.connect(self.select_model_path)self.load_model_button = QPushButton("加載模型")self.load_model_button.clicked.connect(self.load_model)self.load_model_button.setEnabled(False)model_layout.addWidget(model_label)model_layout.addWidget(self.model_path_edit)model_layout.addWidget(model_button)model_layout.addWidget(self.load_model_button)main_layout.addLayout(model_layout)# 圖像顯示部分self.image_label = QLabel()self.image_label.setAlignment(QtCore.Qt.AlignCenter)self.image_label.setMinimumSize(600, 400)main_layout.addWidget(self.image_label)# 圖像選擇部分image_layout = QHBoxLayout()image_path_label = QLabel("圖像路徑:")self.image_path_edit = QtWidgets.QLineEdit()image_select_button = QPushButton("選擇圖像")image_select_button.clicked.connect(self.select_image_path)self.predict_button = QPushButton("分類預測")self.predict_button.clicked.connect(self.predict_image)self.predict_button.setEnabled(False)image_layout.addWidget(image_path_label)image_layout.addWidget(self.image_path_edit)image_layout.addWidget(image_select_button)image_layout.addWidget(self.predict_button)main_layout.addLayout(image_layout)# 結果顯示部分self.result_label = QLabel("請先加載模型并選擇圖像")self.result_label.setAlignment(QtCore.Qt.AlignCenter)self.result_label.setStyleSheet("font-size: 20px")main_layout.addWidget(self.result_label)central_widget.setLayout(main_layout)

2.功能輔助函數

    def select_model_path(self):file_path, _ = QFileDialog.getOpenFileName(self,"選擇模型文件","","Pytorch模型 (*.pth);;所有文件(*)")if file_path:self.model_path_edit.setText(file_path)self.load_model_button.setEnabled(True)def load_model(self):model_path = self.model_path_edit.text()if not model_path:returntry:# 模型類型(根據你的模型的時間需求進行修改)self.model = FruitClassificationModelResnet18(4)self.model.load_state_dict(torch.load(model_path, map_location=self.device, weights_only=False))self.model = self.model.to(self.device)self.model.eval()self.result_label.setText("模型加載成功!請選擇圖像進行預測.")self.predict_button.setEnabled(True)except Exception as e:self.result_label.setText(f"模型加載失敗: {str(e)}")self.model = Noneself.predict_button.setEnabled(False)def select_image_path(self):file_path, _ = QFileDialog.getOpenFileName(self,"選擇圖像文件","","圖像文件 (*bmp *.png *.jpg *.jpeg);;所有文件(*)")if file_path:self.image_path_edit.setText(file_path)self.display_image(file_path)def display_image(self, file_path):pixmap = QtGui.QPixmap(file_path)if not pixmap.isNull():scaled_pixmap = pixmap.scaled(self.image_label.size(),QtCore.Qt.KeepAspectRatio,QtCore.Qt.SmoothTransformation)self.image_label.setPixmap(scaled_pixmap)else:self.image_label.setText("無法加載圖像")def preprocess_image(self, image_path):try:# 定義圖像預處理流程transform = transforms.Compose([transforms.Resize((224, 224)),  # 調整圖像大小為224x224transforms.ToTensor(),  # 轉換為Tensor格式transforms.Normalize([0.485, 0.456, 0.406],  # 標準化均值(ImageNet數據集)[0.229, 0.224, 0.225])  # 標準化標準差])# 打開圖像文件image = Image.open(image_path)# 如果圖像不是RGB模式,轉換為RGBif image.mode != "RGB":image = image.convert("RGB")# 應用預處理變換并添加batch維度(unsqueeze(0)),然后移動到指定設備image = transform(image).unsqueeze(0).to(self.device)return imageexcept Exception as e:self.result_label.setText(f"圖像預處理失敗: {str(e)}")return None

3.加載模型

    def load_model(self):model_path = self.model_path_edit.text()if not model_path:returntry:# 模型類型(根據你的模型的時間需求進行修改)self.model = FruitClassificationModelResnet18(4)self.model.load_state_dict(torch.load(model_path, map_location=self.device, weights_only=False))self.model = self.model.to(self.device)self.model.eval()self.result_label.setText("模型加載成功!請選擇圖像進行預測.")self.predict_button.setEnabled(True)except Exception as e:self.result_label.setText(f"模型加載失敗: {str(e)}")self.model = Noneself.predict_button.setEnabled(False)

4.預測函數

    def predict_image(self):if not self.model:self.result_label.setText("請先加載模型")returnimage_path = self.image_path_edit.text()if not image_path:self.result_label.setText("請選擇圖像")returninput_tensor = self.preprocess_image(image_path)if input_tensor is None:return# 預測with torch.no_grad():input_tensor = input_tensor.to(self.device)outputs = self.model(input_tensor)_, predicted = torch.max(outputs.data, 1)class_id = predicted.item()# 顯示結果class_names = ['Apple', 'Banana', 'Orange', 'Pinenapple']  # 示例類別  根據你的模型進行修改if class_id < len(class_names):self.result_label.setText(f"預測結果: {class_names[class_id]}")else:self.result_label.setText(f"預測結果: 未知類別 ({class_id})")QtWidgets.QApplication.processEvents()

6.完整實現代碼

import cv2
import sys
import numpy as np
import torch
import torchvision.transforms as transforms
from PIL import Image
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QFileDialog, QLabel, QPushButton, QVBoxLayout, QWidget, QHBoxLayout
from model import FruitClassificationModelResnet18class FruitClassificationApp(QtWidgets.QMainWindow):def __init__(self):super().__init__()self.model = Noneself.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")self.initUI()def initUI(self):# 主窗口設置self.setWindowTitle("水果分類應用")self.setGeometry(100, 100, 800, 600)# 創建主窗口部件central_widget = QWidget()self.setCentralWidget(central_widget)# 創建主布局main_layout = QVBoxLayout()# 模型選擇部分model_layout = QHBoxLayout()model_label = QLabel("模型路徑:")self.model_path_edit = QtWidgets.QLineEdit()model_button = QPushButton("選擇模型")model_button.clicked.connect(self.select_model_path)self.load_model_button = QPushButton("加載模型")self.load_model_button.clicked.connect(self.load_model)self.load_model_button.setEnabled(False)model_layout.addWidget(model_label)model_layout.addWidget(self.model_path_edit)model_layout.addWidget(model_button)model_layout.addWidget(self.load_model_button)main_layout.addLayout(model_layout)# 圖像顯示部分self.image_label = QLabel()self.image_label.setAlignment(QtCore.Qt.AlignCenter)self.image_label.setMinimumSize(600, 400)main_layout.addWidget(self.image_label)# 圖像選擇部分image_layout = QHBoxLayout()image_path_label = QLabel("圖像路徑:")self.image_path_edit = QtWidgets.QLineEdit()image_select_button = QPushButton("選擇圖像")image_select_button.clicked.connect(self.select_image_path)self.predict_button = QPushButton("分類預測")self.predict_button.clicked.connect(self.predict_image)self.predict_button.setEnabled(False)image_layout.addWidget(image_path_label)image_layout.addWidget(self.image_path_edit)image_layout.addWidget(image_select_button)image_layout.addWidget(self.predict_button)main_layout.addLayout(image_layout)# 結果顯示部分self.result_label = QLabel("請先加載模型并選擇圖像")self.result_label.setAlignment(QtCore.Qt.AlignCenter)self.result_label.setStyleSheet("font-size: 20px")main_layout.addWidget(self.result_label)central_widget.setLayout(main_layout)def select_model_path(self):file_path, _ = QFileDialog.getOpenFileName(self,"選擇模型文件","","Pytorch模型 (*.pth);;所有文件(*)")if file_path:self.model_path_edit.setText(file_path)self.load_model_button.setEnabled(True)def load_model(self):model_path = self.model_path_edit.text()if not model_path:returntry:# 模型類型(根據你的模型的時間需求進行修改)self.model = FruitClassificationModelResnet18(4)self.model.load_state_dict(torch.load(model_path, map_location=self.device, weights_only=False))self.model = self.model.to(self.device)self.model.eval()self.result_label.setText("模型加載成功!請選擇圖像進行預測.")self.predict_button.setEnabled(True)except Exception as e:self.result_label.setText(f"模型加載失敗: {str(e)}")self.model = Noneself.predict_button.setEnabled(False)def select_image_path(self):file_path, _ = QFileDialog.getOpenFileName(self,"選擇圖像文件","","圖像文件 (*bmp *.png *.jpg *.jpeg);;所有文件(*)")if file_path:self.image_path_edit.setText(file_path)self.display_image(file_path)def display_image(self, file_path):pixmap = QtGui.QPixmap(file_path)if not pixmap.isNull():scaled_pixmap = pixmap.scaled(self.image_label.size(),QtCore.Qt.KeepAspectRatio,QtCore.Qt.SmoothTransformation)self.image_label.setPixmap(scaled_pixmap)else:self.image_label.setText("無法加載圖像")def preprocess_image(self, image_path):try:# 定義圖像預處理流程transform = transforms.Compose([transforms.Resize((224, 224)),  # 調整圖像大小為224x224transforms.ToTensor(),  # 轉換為Tensor格式transforms.Normalize([0.485, 0.456, 0.406],  # 標準化均值(ImageNet數據集)[0.229, 0.224, 0.225])  # 標準化標準差])# 打開圖像文件image = Image.open(image_path)# 如果圖像不是RGB模式,轉換為RGBif image.mode != "RGB":image = image.convert("RGB")# 應用預處理變換并添加batch維度(unsqueeze(0)),然后移動到指定設備image = transform(image).unsqueeze(0).to(self.device)return imageexcept Exception as e:self.result_label.setText(f"圖像預處理失敗: {str(e)}")return Nonedef predict_image(self):if not self.model:self.result_label.setText("請先加載模型")returnimage_path = self.image_path_edit.text()if not image_path:self.result_label.setText("請選擇圖像")returninput_tensor = self.preprocess_image(image_path)if input_tensor is None:return# 預測with torch.no_grad():input_tensor = input_tensor.to(self.device)outputs = self.model(input_tensor)_, predicted = torch.max(outputs.data, 1)class_id = predicted.item()# 顯示結果class_names = ['Apple', 'Banana', 'Orange', 'Pinenapple']  # 示例類別  根據你的模型進行修改if class_id < len(class_names):self.result_label.setText(f"預測結果: {class_names[class_id]}")else:self.result_label.setText(f"預測結果: 未知類別 ({class_id})")QtWidgets.QApplication.processEvents()if __name__ == '__main__':app = QtWidgets.QApplication(sys.argv)window = FruitClassificationApp()window.show()sys.exit(app.exec_())

五、學習收獲🎁:

通過本次 PyTorch 與 PyQt5 的項目實戰,不僅鞏固了深度學習模型的使用方法,也系統地學習了如何將模型部署到圖形界面中。以下是我的一些具體收獲:

1?? 深度學習模型部署實踐

  • 學會了如何將 .pth 格式的模型加載到推理環境;

  • 熟悉了圖像的預處理流程(如Resize、ToTensor、Normalize);

  • 掌握了 torch.no_grad() 推理模式下的使用,避免梯度計算加速推理。

2?? PyQt5 圖形界面開發

  • 掌握了 PyQt5 中常用的控件如 QLabelQPushButtonQLineEdit 等;

  • 學會了如何使用 QFileDialog 實現文件選擇;

  • 了解了如何通過 QPixmap 加載并展示圖像;

  • 熟悉了 QVBoxLayoutQHBoxLayout 進行界面布局。

3?? 端到端流程整合

  • 實現了從模型加載 → 圖像讀取 → 圖像預處理 → 推理 → 展示結果 的完整流程;

  • 初步理解了如何將 AI 模型變成一個用戶可交互的軟件;

  • 為后續構建更復雜的推理系統(如視頻流識別、多模型切換)打下了基礎。

注:完整代碼,請私聊,免費獲取。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88743.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88743.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88743.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

小架構step系列13:測試用例的加載

1 概述測試用例的編寫要有一些基礎的規范&#xff0c;在本文先定義文件名稱和測試用例方法名的規范。2 文件加載原理先從源碼來看一下測試用例的文件加載原理。2.1 文件的匹配主要是通過注解來掃描測試用例。// 在IDEA測試用例啟動時&#xff0c;調用junit-platform-launcher-x…

K8S的CNI之calico插件升級至3.30.2

前言宿主機ping不通K8S的pod&#xff0c;一直存在丟包的現象&#xff0c;排查了防火墻、日志、詳細信息等沒發現什么問題&#xff0c;最后搜索發現&#xff0c;是因為把K8S的版本升級之后&#xff0c;舊版本的CNI插件不適配原因導致的&#xff0c;于是就把calico也一并升級并且…

Spring Boot RESTful API 設計指南:查詢接口規范與最佳實踐

Spring Boot RESTful API 設計指南&#xff1a;查詢接口規范與最佳實踐 引言 在 Spring Boot 開發中&#xff0c;查詢接口的設計直接影響著系統的可用性、可維護性和性能。本文將深入探討如何規范設計查詢接口&#xff0c;包括 GET/POST 的選擇、參數定義、校驗規則等&#xff…

ctfshow萌新題集

記錄一下前半部分是能自己寫出來的&#xff0c;后半部分是需要提示的&#xff0c;感覺自己歸來兩年仍是萌新 misc部分 知識點 base家族密文特征 Base16 (Hex) 字符集&#xff1a;0-9, A-F&#xff08;不區分大小寫&#xff09;。特征&#xff1a; 長度是 2 的倍數&#xff…

2025年語言處理、大數據與人機交互國際會議(DHCI 2025)

&#x1f310;&#x1f916;&#x1f9e0; 語言處理、大數據與人機交互&#xff1a;探索智能未來 —— DHCI 2025國際會議2025年語言處理、大數據與人機交互國際會議&#xff08;DHCI 2025&#xff09; 將于2025年在中國重慶市召開。這次盛會將匯聚全球頂尖專家、學者及行業領袖…

RIP實驗以及核心原理

RIP&#xff08;Routing Information Protocol&#xff0c;路由信息協議&#xff09;是一種內部網關協議&#xff0c;基于距離矢量算法&#xff0c;用于在自治系統內交換路由信息。RIP 核心原理距離矢量算法&#xff1a;RIP 使用跳數作為路徑選擇的唯一度量標準。每經過一個路由…

基于大數據的電力系統故障診斷技術研究

摘要本文提出了一種創新性的基于大數據技術的電力系統故障診斷方法&#xff0c;該方法通過整合先進的機器學習算法和交互式可視化技術&#xff0c;實現了對電力系統各類故障的智能化識別與深度分析。該系統采用隨機森林算法作為核心分類器&#xff0c;構建了高精度的故障分類模…

MySQL 分區功能應用專門實現全方位詳解與示例

MySQL 分區功能允許將表的數據分散存儲在不同的物理分區中,同時保持邏輯上的單一表結構。下面我將從基礎概念到高級應用,全面講解 MySQL 分區實現。 一、分區核心作用 1. 性能提升 分區剪枝(Partition Pruning):查詢時自動跳過不相關的分區,減少數據掃描量 并行處理:不…

汽車功能安全-嵌入式軟件測試(軟件合格性測試)【目的、驗證輸入、集成驗證要求】11

文章目錄1 嵌入式軟件測試&#xff08;Testing of the embedded Software&#xff09;2 測試輸入3 驗證要求和建議3.1 測試環境3.2 測試方法3.2.1 基于需求的測試3.2.2 故障注入測試3.2.3 兩種方法的區別與聯系總結3.3 測試用例導出方法4 嵌入式軟件的測試結果評價5 測試輸出物…

【webrtc】gcc當前可用碼率1:怎么決策的

【webrtc】當前最大碼率是怎么決策的1 看日志,跟蹤代碼最大碼率 是probe的上限 默認值很大 外部設置的較小,調用堆棧 無限大作為默認值 默認是無限大,所以使用預設值 【webrtc】碼率設定中的 int64_t 的無限大

UE5 C++計時器

UE5 C計時器 計時器一&#xff1a; .h文件 FTimerHandle TimerHandle_BetweenShot;//定義時間句柄 void StartFire();void EndFire();.cpp文件 #include “TimerManager.h” void ASpaceShip::StartFire() {GetWorldTimerManager().SetTimer(TimerHandle_BetweenShot, this, &a…

【hivesql 已知維度父子關系加工層級表】

這里寫自定義目錄標題1. 維度表示例1.1清單表1.2層級表2.從清單表加工層級表2.1 注意點2.2 加工方式&#xff08;join&#xff09;2.3 使用函數3.清單表字段加工3.1通過上級編碼信息加工級別信息3.2 通過級別信息&#xff0c;加工上級編碼信息4.創建維度表的一般注意點1. 維度表…

Ubuntu重裝系統后ssh連接不上(遇到 ??“Unit ssh.service not found“?? 錯誤)

重裝系統時不知道為什么SSH 服務未安裝&#xff0c;以下是解決方案&#xff1a;先檢查ssh服務安裝沒安裝 sudo systemctl status ssh # Ubuntu/Debian如果 systemctl 找不到服務&#xff0c;可能是 SSH 未安裝&#xff1a;sudo apt update sudo apt install openssh-serve…

2025社交電商新風口:推客小程序的商業邏輯與技術實現

一、推客小程序市場前景與商業價值在當今社交電商蓬勃發展的時代&#xff0c;推客小程序已成為連接商家與消費者的重要橋梁。推客模式結合了社交傳播與電商變現的雙重優勢&#xff0c;通過用戶自發分享帶來裂變式增長&#xff0c;為商家創造了全新的營銷渠道。推客小程序的核心…

Go 單元測試進階:AI 加持下的高效實踐與避坑指南

單元測試的必要性與基礎單元測試不僅是保障代碼質量的手段&#xff0c;也是優秀的設計工具和文檔形式&#xff0c;對軟件開發具有重要意義。另一種形式的文檔&#xff1a;好的單元測試是一種活文檔&#xff0c;能清晰展示代碼單元的預期用途和行為&#xff0c;有時比注釋更有用…

VScode SSH遠程連接Ubuntu(通過SSH密鑰對的方式)

我們都知道在VScode上通過SSH插件的方式可以遠程連接到虛擬機的Ubuntu系統&#xff0c;這樣開發者就可以在Windows下的Vscode編譯器下直接遠程連接Ubuntu&#xff0c;這種方式是 “用 Windows 的便捷性操作 Linux 的專業性”—— 既保留了Windows系統的易用性和VS Code的強大功…

學術繪圖(各種神經網絡)

23種神經網絡設計&可視化工具匯總 下面做簡要羅列&#xff0c;具體請看相關鏈接 1.draw_convnet Github: https://github.com/gwding/draw_convnet? star 數量&#xff1a;1.7k? 這個工具最后一次更新是2018年的時候&#xff0c;一個Python腳本來繪制卷積神經網絡的工…

Redis的高可用性與集群架構

Redis的高可用性與集群架構 引言&#xff1a;解釋高可用性的重要性及Redis如何實現主從復制&#xff08;Replication&#xff09; 原理&#xff1a;異步復制&#xff0c;主從數據同步配置方法優缺點分析 哨兵模式&#xff08;Sentinel&#xff09; 功能&#xff1a;監控、通知、…

TCP的連接

TCP 三次握手過程是怎樣的&#xff1f;TCP 是面向連接的協議&#xff0c;所以使用 TCP 前必須先建立連接&#xff0c;而建立連接是通過三次握手來進行的。三次握手的過程如下圖&#xff1a;一開始&#xff0c;客戶端和服務端都處于 CLOSE 狀態。先是服務端主動監聽某個端口&…