PyQt5调用YOLO图像分类模型进行猫狗图像二分类深度训练和预测示例
引言
- 简单介绍下YOLO模型的图像分类深度训练和预测功能示例,
- 了解Python中的PyQt5库的信号槽,QThread线程类,QCharView图表的简单使用
- 结合Pyqt5与YOLO模型图像分类,实现训练的损失率的图表和预测结果显示。
环境准备
所需的软件和库:PyQt5、YOLO、Python 3.8
涉及库的安装:
#YOLO库
pip install ultralytics
#PyQt5
pip install pyqt5
#QCharView
pip install PyQt5 PyQtChart
YOLO模型 图像分类
通过YOLO模型自带的yolov8n-cls.pt模型进行训练,
通过绑定on_fit_epoch_end回调函数获取训练的损失率和验证的损失率,绘制成报表。
这里介绍常用的训练参数
| 参数名称 | 描述 |
|---|---|
| model | 如果预训练就指向预训练模型 模型文件路径,例如 yolov8n.pt, yolov8n.yaml |
| data | 执行数据集地址 数据文件路径,例如 coco128.yaml |
| epochs | 训练的周期数 |
| patience | 早停训练等待的最大无改善周期数 |
| batch | 每批次的图像数量(-1 表示自动批次) |
| imgsz | 输入图像的尺寸 |
| save | 是否保存训练检查点和预测结果 |
| lr0 | 初始学习率(例如 SGD=1E-2, Adam=1E-3) |
| lrf | 最终学习率(初始学习率 * 最终学习率) |
| weight_decay | 优化器权重衰减 5e-4 |
| project | 项目名称 |
| name | 实验名称 |
| device | 运行设备,例如 cuda device=0 或 device=0,1,2,3 或 device=cpu |
| plots | 训练/验证期间是否保存图表和图像 |
如果没有设置验证数据集,那么设置split=val属性会自动创建20%原有数据作为验证数据
- 代码示例:
# 加载一个预训练的分类模型
model = YOLO('yolov8n-cls.pt')
print("YOLO Model load successfully")
#model.add_callback("on_fit_epoch_end", on_fit_epoch_end)
# 指定设备
tra_device = "cpu"
try:
results = model.train(
data="F://kaggle//train_alittle",
epochs=5,
imgsz=[224,224],
batch=32,
device=tra_device ,
split='val', # 自动创建验证集
# === 数据增强优化 ===
augment=True, # 数据增强(小数据必备,提升泛化)
mosaic=0.5, # 马赛克强度
mixup=0.1, # 启用mixup增强
scale=0.5, # 缩放增强
# === 学习率优化 ===
lr0=0.01, # 初始学习率
lrf=0.01, # 最终学习率
weight_decay=0.0005, # 权重衰减
# === 训练策略优化 ===
patience=20, # 早停(n轮无提升终止,防过拟合)
project="./result",
name="train_exp",
resume=False,
# === 禁用GUI调用 ===
plots=False, # <<< 关键,禁用图表
show=False, # <<< 关键,禁用显示
workers=0 # <<< 关键,禁用子进程加载
)
except KeyboardInterrupt:
print("Training interrupted by user")
#目录结构
##train_alittle
####train
######cat
######dog
####val
######cat
######dog
需要引用的模块:
# 模型和训练模块,核心
from ultralytics import YOLO
from torch import Tensor
import torch
# 设置模块
from ultralytics import settings
from copy import copy, deepcopy
from ultralytics.utils.torch_utils import (
TORCH_2_4,
EarlyStopping,
ModelEMA,
attempt_compile,
autocast,
convert_optimizer_state_dict_to_fp16,
init_seeds,
one_cycle,
select_device,
strip_optimizer,
torch_distributed_zero_first,
unset_deterministic,
unwrap_model,
)
预测就是调用模型,
然后判断每张图片属于各个分类的概率:
- 完整代码:
model = YOLO('E://QINGTUUGUANGDIAN//Example_Demo//MtNas//YOLO_PROJECTCASE//TextModel//classify//result//train_exp-5//weights//best.pt')
image_folder = "F://kaggle//test1//test1"
# 方法1:获取文件夹中的所有图片,用切片限制数量
all_images = list(Path(image_folder).glob("*.jpg"))[:50] # 只取前50张
# 执行预测并配置参数
results = model.predict(
source=all_images, # 可以是图片、文件夹、视频等
save=True, # 保存带有预测标注的图片
save_txt=True, # 将预测结果保存为 .txt 文件
conf=0.25, # 设置置信度阈值(分类任务中常用)
imgsz=224 # 设置输入图片尺寸,应与训练时一致
)
# 遍历所有结果
sum=0;
successed=0
for result in results:
sum+=1
class_name = result.path.split('\\')[-1]
print(class_name[:3])
# 每个 result 包含了单张图片的所有预测信息
print(f"{result.path}:{result.names[result.probs.top1]}") # 打印每个图片的预测类别名
if(class_name[:3]==result.names[result.probs.top1]):
successed+=1
# # probs 是一个包含所有类别概率的张量
# probs_tensor = result.probs.data
#
# # 获取概率最高的前5个类别
# top5_indices = probs_tensor.topk(5).indices.cpu().numpy()
# top5_confidences = probs_tensor.topk(5).values.cpu().numpy()
#
# print("Top-5 预测结果:")
# for i, (idx, conf) in enumerate(zip(top5_indices, top5_confidences)):
# class_name = model.names[int(idx)]
# print(f" 第{i + 1}名: {class_name} (置信度: {conf:.4f})")
print(f"总共{sum}条数据,其中预测正确{successed}条,正确率{successed/sum*100:.2f}%")
总的来说,YOLO的图像分类模型的使用并不复杂,
具体的YOLO模型介绍请看文章后面的参考资料,
接下来就开始设计一个可视化的界面。
PyQt5界面设计
这时候就需要用到Designer 5.15.2 (MSVC 2019 64-bit)工具,具体版本随意,
通过工具创建一个QMainWindow界面文件,设计布局,像这样:

- 在通过pyuic5 命令行,将ui文件转成py文件:
pyuic5 TextModel/ui/YOLO_Classification.ui -o TextModel/ui/YOLO_Classification.py
生成后的py文件需要修改继承类和添加继承类,
同时调用setupUi函数
像这样:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
#super(Ui_MainWindow, self).__init__()
self.setupUi(self)
#其他修改
#self.InitView()
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(789, 596)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.widget = QtWidgets.QWidget(self.centralwidget)
self.widget.setMinimumSize(QtCore.QSize(0, 50))
self.widget.setObjectName("widget")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.pushButton_ToTrain = QtWidgets.QPushButton(self.widget)
self.pushButton_ToTrain.setMinimumSize(QtCore.QSize(60, 35))
self.pushButton_ToTrain.setCheckable(True)
self.pushButton_ToTrain.setObjectName("pushButton_ToTrain")
self.horizontalLayout_3.addWidget(self.pushButton_ToTrain)
self.pushButton_ToVerify = QtWidgets.QPushButton(self.widget)
self.pushButton_ToVerify.setMinimumSize(QtCore.QSize(60, 35))
self.pushButton_ToVerify.setCheckable(True)
self.pushButton_ToVerify.setObjectName("pushButton_ToVerify")
self.horizontalLayout_3.addWidget(self.pushButton_ToVerify)
spacerItem = QtWidgets.QSpacerItem(606, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem)
self.verticalLayout_2.addWidget(self.widget)
self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
self.stackedWidget.setObjectName("stackedWidget")
self.page = QtWidgets.QWidget()
self.page.setObjectName("page")
self.verticalLayout = QtWidgets.QVBoxLayout(self.page)
self.verticalLayout.setObjectName("verticalLayout")
self.widget_tite = QtWidgets.QWidget(self.page)
self.widget_tite.setMaximumSize(QtCore.QSize(16777215, 50))
self.widget_tite.setObjectName("widget_tite")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget_tite)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.widget_tite)
self.label.setMinimumSize(QtCore.QSize(0, 28))
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit_dirpath = QtWidgets.QLineEdit(self.widget_tite)
self.lineEdit_dirpath.setMinimumSize(QtCore.QSize(0, 28))
self.lineEdit_dirpath.setObjectName("lineEdit_dirpath")
self.horizontalLayout.addWidget(self.lineEdit_dirpath)
self.toolButton_choose_dirpath = QtWidgets.QToolButton(self.widget_tite)
self.toolButton_choose_dirpath.setMinimumSize(QtCore.QSize(0, 28))
self.toolButton_choose_dirpath.setObjectName("toolButton_choose_dirpath")
self.horizontalLayout.addWidget(self.toolButton_choose_dirpath)
self.pushButton_StartTrain = QtWidgets.QPushButton(self.widget_tite)
self.pushButton_StartTrain.setMinimumSize(QtCore.QSize(0, 28))
self.pushButton_StartTrain.setObjectName("pushButton_StartTrain")
self.horizontalLayout.addWidget(self.pushButton_StartTrain)
self.verticalLayout.addWidget(self.widget_tite)
self.widget_connect = QtWidgets.QWidget(self.page)
self.widget_connect.setObjectName("widget_connect")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget_connect)
self.horizontalLayout_2.setContentsMargins(15, 15, 15, 15)
self.horizontalLayout_2.setSpacing(15)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.widget_charview = QChartView(self.widget_connect)
self.widget_charview.setStyleSheet("background-color: rgb(246, 246, 246);")
self.widget_charview.setObjectName("widget_charview")
self.horizontalLayout_2.addWidget(self.widget_charview)
self.verticalLayout.addWidget(self.widget_connect)
self.stackedWidget.addWidget(self.page)
self.page_2 = QtWidgets.QWidget()
self.page_2.setObjectName("page_2")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.page_2)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.widget_tite_2 = QtWidgets.QWidget(self.page_2)
self.widget_tite_2.setMaximumSize(QtCore.QSize(16777215, 50))
self.widget_tite_2.setObjectName("widget_tite_2")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget_tite_2)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label_2 = QtWidgets.QLabel(self.widget_tite_2)
self.label_2.setMinimumSize(QtCore.QSize(0, 28))
self.label_2.setObjectName("label_2")
self.horizontalLayout_4.addWidget(self.label_2)
self.lineEdit_imagepath = QtWidgets.QLineEdit(self.widget_tite_2)
self.lineEdit_imagepath.setMinimumSize(QtCore.QSize(0, 28))
self.lineEdit_imagepath.setObjectName("lineEdit_imagepath")
self.horizontalLayout_4.addWidget(self.lineEdit_imagepath)
self.toolButton_choose_file = QtWidgets.QToolButton(self.widget_tite_2)
self.toolButton_choose_file.setMinimumSize(QtCore.QSize(0, 28))
self.toolButton_choose_file.setObjectName("toolButton_choose_file")
self.horizontalLayout_4.addWidget(self.toolButton_choose_file)
self.pushButton_StartVerify = QtWidgets.QPushButton(self.widget_tite_2)
self.pushButton_StartVerify.setMinimumSize(QtCore.QSize(0, 28))
self.pushButton_StartVerify.setObjectName("pushButton_StartVerify")
self.horizontalLayout_4.addWidget(self.pushButton_StartVerify)
self.verticalLayout_3.addWidget(self.widget_tite_2)
self.splitter = QtWidgets.QSplitter(self.page_2)
self.splitter.setOrientation(QtCore.Qt.Horizontal)
self.splitter.setObjectName("splitter")
self.label_3 = QtWidgets.QLabel(self.splitter)
self.label_3.setAlignment(QtCore.Qt.AlignCenter)
self.label_3.setObjectName("label_3")
self.tableWidget = QtWidgets.QTableWidget(self.splitter)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(0)
self.tableWidget.setRowCount(0)
self.verticalLayout_3.addWidget(self.splitter)
self.stackedWidget.addWidget(self.page_2)
self.verticalLayout_2.addWidget(self.stackedWidget)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.stackedWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton_ToTrain.setText(_translate("MainWindow", "训练"))
self.pushButton_ToVerify.setText(_translate("MainWindow", "验证"))
self.label.setText(_translate("MainWindow", "路径设置:"))
self.toolButton_choose_dirpath.setText(_translate("MainWindow", "选择..."))
self.pushButton_StartTrain.setText(_translate("MainWindow", "开始训练"))
self.label_2.setText(_translate("MainWindow", "图片路径:"))
self.toolButton_choose_file.setText(_translate("MainWindow", "选择..."))
self.pushButton_StartVerify.setText(_translate("MainWindow", "验证"))
self.label_3.setText(_translate("MainWindow", "Image"))
不建议在setupUi函数和retranslateUi里面修改参数或类型,
毕竟每次修改了ui文件都会重新生成py文件,这两个函数都会被重新覆盖。
同时也建议把生成后的例如YOLO_Classification.py中的界面复制到新的py文件中,
不建议在原有文件中修改,说起来这都是泪。。。
- 调用主界面窗体
调用窗体时一定要在QtWidgets.QApplication事务中调用,
要不然如果有绑定信号槽的操作,是不会生效的。
像这样:
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
ui = Ui_MainWindow()
ui.show()
sys.exit(qApp.exec_())
运行效果展示:

界面搞完了,接下来就需要对YOLO模块进行调用了,
但是为了训练时不堵塞界面的显示,所以需要加个线程类封装训练的操作。
重写PyQt5 QThread线程类
于是重写Pyqt5 中QThread线程类,基本和写Qt操作类似
将YOLO的深度训练的和预测都放到这个线程类中执行。
设计使用Qt的信号槽来获取训练的损失率数据,
所以也需要同时继承QObject类
然后使用pyqtSignal(参数,参数....)定义信号槽,获取线程的运行状态,和训练的损失率数据:
定义一个ThreadFlags枚举类,用来表示需要进行的操作
添加def run(self)函数,运行上面YOLO模块的训练或预测函数
在PyQt5中def __init__(self): 就相当于QT中类的构造函数。
如下图示:
#线程类
class YOLO_CLS_WORKER(QThread, QObject):
"""最简单的 QObject 子类"""
"""定义一个YOLO训练或预测的线程类"""
# 当前进程处理状态
TreatmentStated = pyqtSignal(int, float, float)
# 当前线程运行状态
IsThreadRun = pyqtSignal(bool)
# 预测图片返回标注图片
Predict_Results = pyqtSignal(str)
def __init__(self, parent=None):
#super().__init__(parent) # 必须调用父类构造器
super(YOLO_CLS_WORKER, self).__init__(parent)
#线程状态
self.__threadFlags = ThreadFlags.NONE
#线程运行
def run(self):
self.IsThreadRun[bool].emit(True)
if(self.__threadFlags & ThreadFlags.THREAD_TRAIN):
self.__Train()
self.__threadFlags &= ~ThreadFlags.THREAD_TRAIN
elif (self.__threadFlags & ThreadFlags.THREAD_PREDICTION):
self.__Predict()
self.__threadFlags &= ~ThreadFlags.THREAD_PREDICTION
else:
print("Jump The Thread")
self.IsThreadRun.emit(False)
#操作状态
class ThreadFlags(IntFlag):
"""操作标志(支持位运算)"""
NONE = 1 << 0
THREAD_TRAIN = 1 << 1
THREAD_PREDICTION = 1 << 2
在PyQt5中通connect绑定信号槽,
通过@pyqtSlot(参数,参数,参数....)定义槽函数。
如下图示:
def InitView(self):
self.resize(800, 600)
self.setWindowTitle("YOLO 图像分类")
self.setWindowIcon(QApplication.style().standardIcon(42))
#初始化线程类
self.Worker=YOLO_CLS_WORKER()
self.Worker.TreatmentStated.connect(self.Train_state)
self.Worker.Predict_Results.connect(self.Predict_Results)
self.Worker.IsThreadRun.connect(self.IsThreadRun)
@pyqtSlot(int,float,float)
def Train_state(self,epoch, total_train_loss, total_val_loss):
print(f"epoch: {epoch}, train_loss: {total_train_loss}, val_loss: {total_val_loss}")
self.series_train.append(epoch,total_train_loss)
self.series_validator.append(epoch, total_val_loss)
self.widget_charview.repaint()
@pyqtSlot(str)
def Predict_Results(self,imagepath):
#self.label_3.setPixmap(QPixmap(imagepath))
pixmap = QPixmap(imagepath)
# 缩放图片以适应标签大小
scaled_pixmap = pixmap.scaled(
self.label_3.size(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
self.label_3.setPixmap(scaled_pixmap)
通过标志的切换,切换训练与预测
#引用
from time import sleep
from PyQt5 import QtCore
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, QTimer, QThread, QFileInfo
from PyQt5.QtWidgets import QApplication
import sys
from enum import IntFlag
#YOLO相关引用
import os
from pathlib import Path
# 模型和训练模块,核心
from ultralytics import YOLO
from torch import Tensor
import torch
# 设置模块
from ultralytics import settings
from copy import copy, deepcopy
from ultralytics.utils.torch_utils import (
TORCH_2_4,
EarlyStopping,
ModelEMA,
attempt_compile,
autocast,
convert_optimizer_state_dict_to_fp16,
init_seeds,
one_cycle,
select_device,
strip_optimizer,
torch_distributed_zero_first,
unset_deterministic,
unwrap_model,
)
#操作状态
class ThreadFlags(IntFlag):
"""操作标志(支持位运算)"""
NONE = 1 << 0
THREAD_TRAIN = 1 << 1
THREAD_PREDICTION = 1 << 2
#线程类
class YOLO_CLS_WORKER(QThread, QObject):
"""最简单的 QObject 子类"""
"""定义一个YOLO训练或预测的线程类"""
# 当前进程处理状态
TreatmentStated = pyqtSignal(int, float, float)
# 当前线程运行状态
IsThreadRun = pyqtSignal(bool)
# 预测图片返回标注图片
Predict_Results = pyqtSignal(str)
def __init__(self, parent=None):
#super().__init__(parent) # 必须调用父类构造器
super(YOLO_CLS_WORKER, self).__init__(parent)
#线程状态
self.__threadFlags = ThreadFlags.NONE
self.__train_Dir="F://kaggle//train_alittle"
self.__predict_file=""
self.__LastWeights="E://QINGTUUGUANGDIAN//Example_Demo//MtNas//YOLO_PROJECTCASE//TextModel//classify//result//train_exp//weights//best.pt"
#获取线程状态
def getThreadFlags(self):
return self.__threadFlags
def To_Train_Start(self,dirpath):
self.__train_Dir=dirpath
self.__threadFlags|=ThreadFlags.THREAD_TRAIN
self.start()
def To_Predict_Start(self,filepath):
self.__predict_file=filepath
self.__threadFlags |= ThreadFlags.THREAD_PREDICTION
self.start()
#线程运行
def run(self):
self.IsThreadRun[bool].emit(True)
if(self.__threadFlags & ThreadFlags.THREAD_TRAIN):
self.__Train()
self.__threadFlags &= ~ThreadFlags.THREAD_TRAIN
elif (self.__threadFlags & ThreadFlags.THREAD_PREDICTION):
self.__Predict()
self.__threadFlags &= ~ThreadFlags.THREAD_PREDICTION
else:
print("Jump The Thread")
self.IsThreadRun.emit(False)
#开始训练
def __Train(self):
def setup_device():
print(f"CUDA available: {torch.cuda.is_available()}")
if torch.cuda.is_available():
print(f"Using GPU: {torch.cuda.get_device_name(0)}")
device = '0'
else:
print("Using CPU")
device = 'cpu'
return device
# 每个拟合 epoch(训练+验证)结束时,验证之后(此时验证指标可用)
def on_fit_epoch_end(trainer):
print("on_fit_epoch_end")
# 1. 获取训练损失
train_losses = {}
total_train_loss = 0.0
if hasattr(trainer, 'loss_items'):
train_losses = trainer.loss_items
total_train_loss = float(trainer.loss_items)
# 2. 获取验证损失(如果执行了验证)
val_losses = {}
total_val_loss = 0
if hasattr(trainer, 'validator') and trainer.validator:
validator = trainer.validator
# 方法1: 从validator获取损失
if hasattr(validator, 'loss'):
total_val_loss =float(validator.loss)
self.TreatmentStated[int,float,float].emit(trainer.epoch,total_train_loss,total_val_loss)
model = YOLO('yolov8n-cls.pt')
print("YOLO Model load successfully")
tra_device = "cpu"
device = setup_device() if tra_device != "cpu" else "cpu"
model.add_callback("on_fit_epoch_end", on_fit_epoch_end)
try:
results = model.train(
data=self.__train_Dir,
epochs=20,
imgsz=[224, 224],
batch=32,
device=device,
split='val', # 自动创建验证集
# split_ratio=0.2, # 关键参数:从训练集中抽取20%作为验证集
# === 数据增强优化 ===
augment=True, # 数据增强(小数据必备,提升泛化)
mosaic=0.5, # 马赛克强度
mixup=0.1, # 启用mixup增强
scale=0.5, # 缩放增强
# === 学习率优化 ===
lr0=0.01, # 初始学习率
lrf=0.01, # 最终学习率
weight_decay=0.0005, # 权重衰减
# === 训练策略优化 ===
patience=20, # 早停(n轮无提升终止,防过拟合)
project="./result",
name="train_exp",
resume=False,
# === 禁用GUI调用 ===
plots=False, # <<< 关键,禁用图表
show=False, # <<< 关键,禁用显示
workers=0 # <<< 关键,禁用子进程加载
)
except KeyboardInterrupt:
print("Training interrupted by user")
#WindowsPath('E:/QINGTUUGUANGDIAN/Example_Demo/MtNas/YOLO_PROJECTCASE/TextModel/classify/result/train_exp-9')
self.__LastWeights= results.save_dir / 'weights' / 'best.pt'
print(f"full_path:{self.__LastWeights}")
print("__Train_Over__")
#开始预测
def __Predict(self):
if self.__predict_file=="" :
return
model = YOLO(self.__LastWeights)
results = model.predict(
source=self.__predict_file, # 可以是图片、文件夹、视频等 这里设置为单张图片路径
save=True, # 保存带有预测标注的图片
save_txt=False, # 将预测结果保存为 .txt 文件
conf=0.25, # 设置置信度阈值(分类任务中常用)
imgsz=224 # 设置输入图片尺寸,应与训练时一致
)
info=QFileInfo(self.__predict_file)
result=results[0].save_dir+"//"+info.fileName()
self.Predict_Results[str].emit(result)
# for result in results:
# print(result)
运行上面的类设计,Pyqthon的脚本模式就是好用。
#*第二种写法
# if __name__ == '__main__':
# app = QApplication(sys.argv) # 必须创建 QApplication
#
# # 不要使用 @pyqtSlot,直接定义普通函数
# def Train_state(epoch, total_train_loss, total_val_loss):
# print(f"epoch: {epoch}, train_loss: {total_train_loss}, val_loss: {total_val_loss}")
#
# thread = YOLO_CLS_WORKER()
# thread.TreatmentStated.connect(Train_state)
# thread.To_Train_Start("F://kaggle//train_alittle")
# # 确保信号连接正确(去掉 @pyqtSlot 装饰器)
# # 或使用默认连接
# # thread.TreatmentStated.connect(Train_state)
# # thread.start()
# # 使用 QTimer 或 app.exec() 而不是 stop_event.wait()
# sys.exit(app.exec_()) # 进入 Qt 事件循环
#*第二种写法 预测
# if __name__ == '__main__':
# app = QApplication(sys.argv) # 必须创建 QApplication
#
# thread = YOLO_CLS_WORKER()
# thread.To_Predict_Start("F://kaggle//test1//test1//2.jpg")
# sys.exit(app.exec_()) # 进入 Qt 事件循环
结果可视化
- 训练:
目前测试的用的数据和训练次数都很小,所以训练效果波动很大。

- 预测:
直接获取模型标注的图像显示。准确率还是很高


总结后记
总得来说,Pyqt与QT实际上的写法/用法基本都改动不大,只要会Qt,那么了解了pyqthon的语法,PyQt使用起来就很容易。
由于近几年没写过PyQt了,就想着在学习YOLO模型的时候复习一下,
于是写了这个案例复习一下PyQt的关键用法,
同时总结下YOLO的图像分类模型。
参考资料
ps: 最近有些懈怠了
(╯°□°)╯︵ ┻━┻
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)