分享一套锋哥原创的基于PyTorch的猫狗图像识别系统(深度学习+PyQt6+ResNet18+ImageNet+迁移学习)

发布时间:2026/9/10 6:04:17

分享一套锋哥原创的基于PyTorch的猫狗图像识别系统(深度学习+PyQt6+ResNet18+ImageNet+迁移学习) 大家好我是Java1234_小锋老师分享一套锋哥原创的基于PyTorch的猫狗图像识别系统(深度学习PyQt6ResNet18ImageNet迁移学习)项目介绍图像分类是计算机视觉领域的基础任务之一在智能安防、宠物管理、内容审核与教学实验等场景中具有广泛的应用价值。针对传统人工鉴别效率低、主观性强以及从零训练深度模型对算力与样本量要求较高等问题本文设计并实现了一套基于PyTorch的猫狗图像识别系统。系统以Python为主要开发语言采用ResNet18卷积神经网络作为核心分类模型并基于ImageNet预训练权重开展迁移学习同时使用PyQt6构建桌面图形界面实现模型训练、单张图像识别与数据集统计分析等功能的一体化集成。在方法层面系统通过冻结骨干网络、仅训练分类头的策略显著降低CPU环境下的训练成本结合随机裁剪、水平翻转与色彩抖动等数据增强手段提升模型泛化能力训练过程中实时绘制损失与准确率曲线并自动保存验证集上表现最优的模型权重。实验结果表明在每类采样约2000张图像、训练5个轮次的设置下系统最优验证准确率可达99.00%能够满足本科毕业设计对功能完整性、可演示性与技术深度的要求。本文从需求分析、总体设计、详细实现到系统测试对课题进行了完整阐述重点介绍了Python语言特性、PyTorch深度学习框架、ImageNet大规模数据集以及ResNet18残差网络等关键技术并给出了各功能模块的核心代码说明。研究成果可为同类图像分类桌面应用的设计与教学实践提供参考。源码下载链接: https://pan.baidu.com/s/1z05iC6wDJmnsAYApQ0tXXQ?pwd1234提取码: 1234系统展示核心代码 模型训练模块 使用 QThread 在后台执行训练通过信号与 UI 通信 import json import os from typing import Optional import torch import torch.nn as nn import torch.optim as optim from PyQt6.QtCore import QThread, pyqtSignal import config from src.dataset import create_dataloaders from src.model import build_model, save_model from src.utils import format_datetime class TrainThread(QThread): 后台训练线程 在独立线程中执行模型训练避免阻塞 UI # 信号定义 log_signal pyqtSignal(str) # 日志消息 epoch_done_signal pyqtSignal(dict) # 每轮训练完成 batch_progress_signal pyqtSignal(int, int) # 批次进度 (current, total) finished_signal pyqtSignal(bool, str) # 训练结束 (success, message) def __init__(self, parentNone): 初始化训练线程 :param parent: 父对象 super().__init__(parent) self._stop_flag False # 训练参数 self.epochs config.DEFAULT_EPOCHS self.batch_size config.DEFAULT_BATCH_SIZE self.lr config.DEFAULT_LR self.img_size config.DEFAULT_IMG_SIZE self.subset_per_class config.DEFAULT_SUBSET_PER_CLASS self.val_split config.DEFAULT_VAL_SPLIT self.freeze_backbone config.DEFAULT_FREEZE_BACKBONE def set_params( self, epochs: int None, batch_size: int None, lr: float None, img_size: int None, subset_per_class: int None, val_split: float None, freeze_backbone: bool None, ): 设置训练超参数 :param epochs: 训练轮数 :param batch_size: 批次大小 :param lr: 学习率 :param img_size: 图像尺寸 :param subset_per_class: 每类子集数量 :param val_split: 验证集比例 :param freeze_backbone: 是否冻结骨干 if epochs is not None: self.epochs epochs if batch_size is not None: self.batch_size batch_size if lr is not None: self.lr lr if img_size is not None: self.img_size img_size if subset_per_class is not None: self.subset_per_class subset_per_class if val_split is not None: self.val_split val_split if freeze_backbone is not None: self.freeze_backbone freeze_backbone def stop(self): 请求停止训练 self._stop_flag True self._emit_log(正在停止训练...) def _emit_log(self, message: str): 发送带时间戳的日志 :param message: 日志内容 timestamp format_datetime() self.log_signal.emit(f[{timestamp}] {message}) def run(self): 执行训练主流程 try: self._stop_flag False device torch.device(config.DEVICE) self._emit_log(f使用设备: {device}) self._emit_log(f训练参数: epochs{self.epochs}, batch_size{self.batch_size}, flr{self.lr}, subset{self.subset_per_class}/类) # 加载数据 self._emit_log(正在加载数据集...) train_loader, val_loader, dataset_info create_dataloaders( batch_sizeself.batch_size, img_sizeself.img_size, subset_per_classself.subset_per_class, val_splitself.val_split, ) self._emit_log(f数据集加载完成: 训练集 {dataset_info[train_size]} 张, f验证集 {dataset_info[val_size]} 张) # 构建模型 self._emit_log(正在构建 ResNet18 模型...) model build_model(num_classes2, freeze_backboneself.freeze_backbone) model.to(device) # 优化器与损失函数 criterion nn.CrossEntropyLoss() optimizer optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lrself.lr ) scheduler optim.lr_scheduler.StepLR(optimizer, step_size3, gamma0.5) # 训练历史 history { train_loss: [], val_loss: [], train_acc: [], val_acc: [], } best_val_acc 0.0 total_batches len(train_loader) # 训练循环 for epoch in range(1, self.epochs 1): if self._stop_flag: self._emit_log(训练已被用户停止) self.finished_signal.emit(False, 训练已停止) return self._emit_log(f--- 第 {epoch}/{self.epochs} 轮 ---) # 训练阶段 model.train() train_loss, train_correct, train_total 0.0, 0, 0 for batch_idx, (images, labels) in enumerate(train_loader): if self._stop_flag: self._emit_log(训练已被用户停止) self.finished_signal.emit(False, 训练已停止) return images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() train_loss loss.item() * images.size(0) _, predicted outputs.max(1) train_correct predicted.eq(labels).sum().item() train_total labels.size(0) self.batch_progress_signal.emit(batch_idx 1, total_batches) train_loss / train_total train_acc train_correct / train_total # 验证阶段 model.eval() val_loss, val_correct, val_total 0.0, 0, 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) val_loss loss.item() * images.size(0) _, predicted outputs.max(1) val_correct predicted.eq(labels).sum().item() val_total labels.size(0) val_loss / val_total val_acc val_correct / val_total scheduler.step() # 记录历史 history[train_loss].append(round(train_loss, 4)) history[val_loss].append(round(val_loss, 4)) history[train_acc].append(round(train_acc, 4)) history[val_acc].append(round(val_acc, 4)) self._emit_log( fEpoch {epoch}: train_loss{train_loss:.4f}, train_acc{train_acc:.2%}, fval_loss{val_loss:.4f}, val_acc{val_acc:.2%} ) # 发送 epoch 完成信号 self.epoch_done_signal.emit({ epoch: epoch, train_loss: train_loss, val_loss: val_loss, train_acc: train_acc, val_acc: val_acc, }) # 保存最优模型 if val_acc best_val_acc: best_val_acc val_acc save_model(model) self._emit_log(f验证准确率提升至 {val_acc:.2%}已保存最优模型) # 保存训练历史 with open(config.HISTORY_PATH, w, encodingutf-8) as f: json.dump(history, f, ensure_asciiFalse, indent2) self._emit_log(f训练完成最优验证准确率: {best_val_acc:.2%}) self.finished_signal.emit(True, f训练完成最优验证准确率: {best_val_acc:.2%}) except Exception as e: self._emit_log(f训练出错: {str(e)}) self.finished_signal.emit(False, f训练出错: {str(e)}) 模型推理预测模块 加载训练好的模型对单张图片进行猫/狗分类 import os from typing import Tuple, Optional import torch import torch.nn.functional as F from PIL import Image import config from src.model import load_model from src.dataset import get_predict_transform class Predictor: 猫狗图像分类预测器 封装模型加载与单图推理逻辑 def __init__(self, model_path: str None): 初始化预测器 :param model_path: 模型权重路径 self.model_path model_path or config.BEST_MODEL_PATH self.model None self.transform get_predict_transform(config.DEFAULT_IMG_SIZE) self.device torch.device(config.DEVICE) self._loaded False def load(self) - bool: 加载模型 :return: 是否加载成功 try: self.model load_model(self.model_path, num_classes2) self._loaded True return True except Exception as e: print(f[预测] 模型加载失败: {e}) self._loaded False return False def is_model_available(self) - bool: 检查模型文件是否存在 :return: 模型是否可用 return os.path.exists(self.model_path) def predict(self, image_path: str) - Tuple[str, float, dict]: 对单张图片进行预测 :param image_path: 图片路径 :return: (预测类别中文名, 置信度, 各类别概率字典) if not self._loaded: if not self.load(): raise RuntimeError(模型未加载请先训练模型) if not os.path.exists(image_path): raise FileNotFoundError(f图片不存在: {image_path}) # 加载并预处理图片 image Image.open(image_path).convert(RGB) input_tensor self.transform(image).unsqueeze(0).to(self.device) # 推理 self.model.eval() with torch.no_grad(): outputs self.model(input_tensor) probabilities F.softmax(outputs, dim1)[0] # 解析结果 pred_idx probabilities.argmax().item() confidence probabilities[pred_idx].item() label config.CLASS_NAMES.get(pred_idx, 未知) prob_dict { config.CLASS_NAMES[i]: probabilities[i].item() for i in range(len(config.CLASS_NAMES)) } return label, confidence, prob_dict def predict_from_pil(self, image: Image.Image) - Tuple[str, float, dict]: 对 PIL Image 对象进行预测 :param image: PIL Image 对象 :return: (预测类别中文名, 置信度, 各类别概率字典) if not self._loaded: if not self.load(): raise RuntimeError(模型未加载请先训练模型) image image.convert(RGB) input_tensor self.transform(image).unsqueeze(0).to(self.device) self.model.eval() with torch.no_grad(): outputs self.model(input_tensor) probabilities F.softmax(outputs, dim1)[0] pred_idx probabilities.argmax().item() confidence probabilities[pred_idx].item() label config.CLASS_NAMES.get(pred_idx, 未知) prob_dict { config.CLASS_NAMES[i]: probabilities[i].item() for i in range(len(config.CLASS_NAMES)) } return label, confidence, prob_dict
延伸阅读

更多相关文章

2026/9/10 6:01:34

树莓派Pico RTC时间同步:NTP精简实现与硬件补救

1. 为什么树莓派 Pico 的 RTC 不能“开箱即用”——从硬件限制到软件补救的底层逻辑MicroPython 开发者第一次把树莓派 Pico 插上电脑,兴奋地敲下import machine; rtc machine.RTC(),接着调用rtc.datetime(),看到返回的是一串固定值&#xf…

2026/9/10 6:01:34

telegram-node-bot集群部署指南:多进程架构与Webhook配置详解

telegram-node-bot集群部署指南:多进程架构与Webhook配置详解 想要构建高性能、高可用的Telegram机器人应用吗?telegram-node-bot作为一款功能强大的Node.js模块,提供了完整的集群部署方案和灵活的Webhook配置选项。本指南将深入解析telegra…

2026/9/10 6:01:34

diagram-design:前端可视化决策系统实战指南

1. “diagram-design”不是一张图,而是一套前端可视化决策系统“diagram-design”这个词在2024年技术社区里高频出现,但它从来就不是某个具体工具、库或插件的代号——它是一类问题的统称:如何在现代Web环境中,以可控、可维护、可…

2026/9/10 6:01:34

JavaSE初入门·萌新逐步展开视角

i.环境变量配置(非必需)首先右击此电脑选择属性,点击图示箭头的 高级系统设置在高级系统设置中 点击环境变量想用cmd命令提示符打开某程序 得在某程序处在的路径下 删去路径键入cmd回车,如图键入程序名后缀如果想在任意位置用cmd…

2026/9/10 5:56:33

JVM监控与诊断实战:从进程管理到GC分析快速定位线上故障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码