2025-05-31 Python深度学习10——模型训练流程

article/2025/6/22 18:55:23

文章目录

  • 1 数据准备
    • 1.1 下载与预处理
    • 1.2 数据加载
  • 2 模型构建
    • 2.1 自定义 CNN 模型
    • 2.2 GPU加速
  • 3 训练配置
    • 3.1 损失函数
    • 3.2 优化器
    • 3.3 训练参数
  • 4 训练循环
    • 4.1 训练模式 (`model.train()`)
    • 4.2 评估模式 (`model.eval()`)
  • 5 模型验证

本文环境:

  • Pycharm 2025.1
  • Python 3.12.9
  • Pytorch 2.6.0+cu124

​ 本文以 CIFAR-10 为例,介绍模型的大致训练流程。相关的 Python 包如下:

import torch
import torchvision
from torch import nn, Tensor
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time

1 数据准备

1.1 下载与预处理

​ 使用torchvision.datasets.CIFAR10下载 CIFAR-10 数据集(32x32 彩色图像,10 类),分为训练集(train=True,5 万张)和测试集(train=False,1 万张)。

# 准备数据集
train_data = torchvision.datasets.CIFAR10(root='./dataset',train=True,transform=torchvision.transforms.ToTensor(),download=True
)test_data = torchvision.datasets.CIFAR10(root='./dataset',train=False,transform=torchvision.transforms.ToTensor(),download=True
)
  • transform=torchvision.transforms.ToTensor():将图像转为 PyTorch 张量(Tensor),并自动归一化到 [0, 1] 范围。
  • download=True:若本地无数据,自动下载。

1.2 数据加载

​ 通过DataLoader分批次加载数据:

# 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
  • batch_size=64:每批次处理 64 张图片,平衡内存占用和训练效率。
  • 训练集默认不打乱(未设置shuffle),测试集可添加shuffle=True以增强评估可靠性。

2 模型构建

2.1 自定义 CNN 模型

MyModel是一个3层卷积神经网络(CNN):

  1. 卷积层:nn.Conv2d(3, 32, 5, 1, 2)

    输入通道 3(RGB),输出通道 32,5×5 卷积核,步长 1,填充 2(保持尺寸不变)。

  2. 池化层:nn.MaxPool2d(2)

    2×2最大池化,尺寸减半。

  3. 全连接层

    • nn.Linear(64 * 4 * 4, 64)

      将展平后的特征(64 通道×4×4尺寸)映射到 64 维。

    • nn.Linear(64, 10)

      最终输出 10 类。

image-20250527161255093
class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.model = nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 64, 5, 1, 2),nn.MaxPool2d(2),nn.Flatten(),nn.Linear(64 * 4 * 4, 64),nn.Linear(64, 10))def forward(self, x):return self.model(x)

2.2 GPU加速

​ 通过.to(device)将模型和数据移至 GPU(若可用),显著加速计算。

# 定义训练的设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")model = MyModel().to(device)  # 使用GPU

3 训练配置

3.1 损失函数

​ 使用nn.CrossEntropyLoss(),适用于多分类任务,计算预测概率与真实标签的交叉熵。

# 损失函数
loss_fn = nn.CrossEntropyLoss().to(device)  # 使用GPU

3.2 优化器

​ 使用torch.optim.SGD,随机梯度下降,学习率lr=1e-2,控制参数更新步长。

# 损失函数
loss_fn = nn.CrossEntropyLoss().to(device)  # 使用GPU

3.3 训练参数

  1. total_train_step:记录训练次数,用于日志和调试。
  2. total_test_step:记录测试次数,用于日志和调试。
  3. epoch=20:遍历完整数据集 20 次。
# 设置训练网络的一些参数
total_train_step = 0  # 记录训练次数
total_test_step = 0  # 记录测试次数
epoch = 20  # 训练的轮数

4 训练循环

数据加载  →  模型初始化  →  训练循环  →  测试评估  →  保存模型↑          ↑                  ↓           ↓└───TensorBoard日志 ←────── 参数更新 ←── 梯度计算

4.1 训练模式 (model.train())

  1. 前向传播:输入图像imgs,模型输出预测outputs
  2. 计算损失loss = loss_fn(outputs, targets),衡量预测误差。
  3. 反向传播
    • optimizer.zero_grad():清空梯度,避免累积。
    • loss.backward():计算梯度(链式法则)。
    • optimizer.step():更新模型参数。
  4. 日志记录:每 100 次训练记录损失和时间到 TensorBoard 中。
for i in range(epoch):print(f"------------第 {i + 1} 轮训练开始------------")# 训练步骤开始model.train()for data in train_dataloader:imgs, targets = dataimgs = imgs.to(device)  # 使用GPUtargets = targets.to(device)  # 使用GPUoutputs = model(imgs)loss = loss_fn(outputs, targets)# 反向传播optimizer.zero_grad()loss.backward()optimizer.step()total_train_step += 1if total_train_step % 100 == 0:end_time = time.time()print(f"第 {total_train_step} 次训练,Loss:{loss.item()},Time:{end_time - start_time}")writer.add_scalar("train_loss", loss.item(), total_train_step)start_time = time.time()

4.2 评估模式 (model.eval())

  1. 关闭梯度计算with torch.no_grad(),节省内存并加速。
  2. 测试指标
    • 总损失:累加所有批次的损失total_test_loss
    • 准确率:统计预测正确的样本数(outputs.argmax(dim=1) == targets)。
  3. 日志记录:每轮测试后保存损失和准确率到 TensorBoard。
  4. 保存模型:通过torch.save()方法将模型的 state_dict 保存到本地文件中。
    # 测试步骤开始model.eval()total_test_loss = 0total_accuracy = 0accuracy_rate = 0with torch.no_grad():for data in test_dataloader:imgs, targets = dataimgs = imgs.to(device)  # 使用GPUtargets = targets.to(device)  # 使用GPUoutputs: Tensor = model(imgs)loss = loss_fn(outputs, targets)total_test_loss += lossaccuracy = outputs.argmax(dim=1) == targetstotal_accuracy += accuracy.sum()total_test_step += 1accuracy_rate = total_accuracy / test_data_sizeprint(f"第 {i + 1} 轮测试,Loss:{total_test_loss},Accuracy:{total_accuracy} ({accuracy_rate})")writer.add_scalar("test_loss", total_test_loss, total_test_step)writer.add_scalar("test_accuracy", total_accuracy, total_test_step)writer.add_scalar("accuracy_rate", accuracy_rate, total_test_step)torch.save(model.state_dict(), f"model/my_model.pth")  # 保存模型writer.close()
image-20250531123058128

说明

  1. 训练与评估模式切换

    • model.train():启用 Dropout 和 BatchNorm 的训练行为(如随机丢弃神经元)。

    • model.eval():固定 Dropout 和 BatchNorm 的统计量,确保评估一致性。

  2. GPU 数据迁移

    需将输入数据 imgs 和标签 targets 均移至 GPU,否则会报错。

  3. 梯度清零

    避免梯度累加导致参数更新错误。

完整代码

# train_gpu_2.pyimport torch
import torchvision
from torch import nn, Tensor
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import time# 定义训练的设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 准备数据集
train_data = torchvision.datasets.CIFAR10(root='./dataset',train=True,transform=torchvision.transforms.ToTensor(),download=True
)test_data = torchvision.datasets.CIFAR10(root='./dataset',train=False,transform=torchvision.transforms.ToTensor(),download=True
)# 数据集大小
train_data_size = len(train_data)
test_data_size = len(test_data)print(f"训练集数量:{train_data_size}")
print(f"测试集数量:{test_data_size}")# 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)# 创建网络模型
class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.model = nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 32, 5, 1, 2),nn.MaxPool2d(2),nn.Conv2d(32, 64, 5, 1, 2),nn.MaxPool2d(2),nn.Flatten(),nn.Linear(64 * 4 * 4, 64),nn.Linear(64, 10))def forward(self, x):return self.model(x)model = MyModel().to(device)  # 使用GPU# 损失函数
loss_fn = nn.CrossEntropyLoss().to(device)  # 使用GPU# 优化器
lr = 1e-2
optimizer = torch.optim.SGD(model.parameters(), lr=lr)# 设置训练网络的一些参数
total_train_step = 0  # 记录训练次数
total_test_step = 0  # 记录测试次数
epoch = 20  # 训练的轮数# 添加 tensorboard
writer = SummaryWriter("../logs_train")for i in range(epoch):print(f"------------第 {i + 1} 轮训练开始------------")start_time = time.time()# 训练步骤开始model.train()for data in train_dataloader:imgs, targets = dataimgs = imgs.to(device)  # 使用GPUtargets = targets.to(device)  # 使用GPUoutputs = model(imgs)loss = loss_fn(outputs, targets)# 反向传播optimizer.zero_grad()loss.backward()optimizer.step()total_train_step += 1if total_train_step % 100 == 0:end_time = time.time()print(f"第 {total_train_step} 次训练,Loss:{loss.item()},Time:{end_time - start_time}")writer.add_scalar("train_loss", loss.item(), total_train_step)start_time = time.time()# 测试步骤开始model.eval()total_test_loss = 0total_accuracy = 0accuracy_rate = 0with torch.no_grad():for data in test_dataloader:imgs, targets = dataimgs = imgs.to(device)  # 使用GPUtargets = targets.to(device)  # 使用GPUoutputs: Tensor = model(imgs)loss = loss_fn(outputs, targets)total_test_loss += lossaccuracy = outputs.argmax(dim=1) == targetstotal_accuracy += accuracy.sum()total_test_step += 1accuracy_rate = total_accuracy / test_data_sizeprint(f"第 {i + 1} 轮测试,Loss:{total_test_loss},Accuracy:{total_accuracy} ({accuracy_rate})")writer.add_scalar("test_loss", total_test_loss, total_test_step)writer.add_scalar("test_accuracy", total_accuracy, total_test_step)writer.add_scalar("accuracy_rate", accuracy_rate, total_test_step)torch.save(model.state_dict(), f"model/my_model.pth")  # 保存模型writer.close()

5 模型验证

​ 准备待验证的图片,放在 imgae 目录下。

image-20250531123319819

​ 编写 test.py 文件,用于验证模型。

# test.pyimport torch
import torchvision
from PIL import Image
from torch import nn# 定义图片路径
image_path = "image/dog.png"# 打开图片并转换为RGB格式
image = Image.open(image_path).convert('RGB')# 定义图片转换操作
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),  # 将图片大小调整为32x32torchvision.transforms.ToTensor()  # 将图片转换为张量
])# 对图片进行转换操作
image = transform(image).reshape(1, 3, 32, 32)# 定义模型类
class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()# 定义模型结构self.model = nn.Sequential(nn.Conv2d(3, 32, 5, 1, 2),  # 第一个卷积层,输入通道数为3,输出通道数为32,卷积核大小为5,步长为1,填充为2nn.MaxPool2d(2),  # 最大池化层,池化核大小为2nn.Conv2d(32, 32, 5, 1, 2),  # 第二个卷积层,输入通道数为32,输出通道数为32,卷积核大小为5,步长为1,填充为2nn.MaxPool2d(2),  # 最大池化层,池化核大小为2nn.Conv2d(32, 64, 5, 1, 2),  # 第三个卷积层,输入通道数为32,输出通道数为64,卷积核大小为5,步长为1,填充为2nn.MaxPool2d(2),  # 最大池化层,池化核大小为2nn.Flatten(),  # 展平操作nn.Linear(64 * 4 * 4, 64),  # 全连接层,输入维度为64*4*4,输出维度为64nn.Linear(64, 10)  # 全连接层,输入维度为64,输出维度为10)def forward(self, x):return self.model(x)# 加载模型参数
model_dict = torch.load('model/my_model.pth')
model = MyModel()
model.load_state_dict(model_dict)
model.to('cuda')# 设置模型为评估模式
model.eval()
# 关闭梯度计算
with torch.no_grad():# 将图片转换为GPU格式image = image.to('cuda')# 进行模型推理output = model(image)# 打印输出结果
print(output)
# 打印输出结果中最大值的索引
print(output.argmax(1))

​ 验证结果如下,表明 dog.png 图片的预测结果索引为 5,即第 6 类预测。

image-20250531123550636

​ 依据分类规则,预测结果为 dog,是正确的。

image-20250531123902927

http://www.hkcw.cn/article/OmoaVLGAnw.shtml

相关文章

十五、STM32的TIM(六)(PWM驱动舵机)

介绍:本章节主要讲解如何在 STM32C8T6 上使用 PWM 驱动舵机。通过按键输入控制,输出以 PWM 信号调整舵机转动角度,从而实现对舵机的精准控制。 目录 一、接线图 二、相关参数的计算 三、相关代码的编写 四、程序现象 一、接线图 二、相关…

C语言指针完全指南:从入门到精通(上)

目录 一、内存和指针 1.1 指针的使用场景 二、指针变量和地址 2.1 取地址符(&) 2.2指针变量和解引用操作符(*) 2.2.1 指针变量 2.3 指针变量的大小 三、指针变量类型的意义 3.2 指针-整数 ​编辑 四、指针计算 五、const修饰指针 5.1 const修饰变量 1.2 const修饰…

Kafka数据怎么保障不丢失

在分布式消息系统中,数据不丢失是核心可靠性需求之一。Apache Kafka 通过生产者配置、副本机制、持久化策略、消费者偏移量管理等多层机制保障数据可靠性。以下从不同维度解析 Kafka 数据不丢失的核心策略,并附示意图辅助理解。 一、生产者端&#xff1a…

Win10秘笈:两种方式修改网卡物理地址(MAC)

Win10秘笈:两种方式修改网卡物理地址(MAC) 在修改之前,可以先确定一下要修改的网卡MAC地址,查询方法有很多种,比如: 1、在设置→网络和Internet→WLAN/以太网,如下图所示。 2、在控…

Angularjs-Hello

1 关于Angularjs 最近因为项目需要又要做这个,所以简单复习下。其实这个大概7,8年前就用过,当时做了几个简单页面觉得太简单就还是回去做嵌入式了。按照互联网技术的进化速度,本来以为早死在 沙滩上了,没想到现在还在坚…

红外遥控(外部中断)

目录 1.红外遥控简介 通信方式: 红外LED波长: 通信协议标准: 2.硬件电路 发送部分1: 内部元件介绍: 工作原理: 为什么要以38KHZ亮灭? 电路图: 发送部分2: 电…

leetcode hot100刷题日记——33.二叉树的层序遍历

解题总结二维vector的初始化方法 题目描述情况1:不确定行数和列数情况2:已知行数和列数情况3:已知行数但不知道列数情况4:已知列数但不知道行数 题目描述 解答:用队列 思路都差不多,我觉得对于我自己来说&a…

GitToolBox 插件安装与配置指南

GitToolBox 插件安装与配置指南 GitToolBox GitToolBox IntelliJ plugin 项目地址: https://gitcode.com/gh_mirrors/gi/GitToolBox 1. 项目基础介绍和主要编程语言 项目基础介绍 GitToolBox 是一个专为 JetBrains 家族 IDE(如 IntelliJ IDEA、PyCharm 等&…

开源模型应用落地-qwen模型小试-Qwen3-8B-推理加速-vLLM-结构化输出(三)

一、前言 在人工智能技术迅猛发展的今天,高效推理框架与强大语言模型的结合正不断突破应用边界。vLLM作为新一代高性能推理引擎,凭借其创新的PagedAttention技术和内存优化能力,为大规模语言模型部署提供了全新可能。 本文将聚焦vLLM框架与QWen3-8B这一国产开源大模型的深度…

史上最全 Git 图文教程(非常详细)零基础入门到精通,收藏这一篇就够了

戳上方蓝字“Java知音”关注我 Git安装 安装 1.先去官网下载这个软件, 准备安装到本电脑中 https://git-scm.com/ 2.根据自己电脑系统下载此软件到本机 Windows 系统直接下载 .exe 文件即可,macOS 系统使用 Homebrew 命令行安装,终端输入 git --versi…

GitHub学生认证申请

想要免费使用Copilot,申请学生认证可以免费使用。在申请过程中,踩了些坑。记录一下供大家参考 认证有效期 默认时长:首次认证成功后,学生权益(如 GitHub Pro 权限和 Student Developer Pack)的有效期一般为…

最新Spring Security实战教程(十五)快速集成 GitHub 与 Gitee 的社交登录

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Micro麦可乐的博客 🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战 🌺《RabbitMQ》…

Vue3 开源UI 框架推荐 (总有一款适合你)

一 、前言 💥这篇文章主要推荐了支持 Vue3 的开源 UI 框架,包括 web 端和移动端的多个框架,如 Element-Plus、Ant Design Vue 等 web 端框架,以及 Vant、NutUI 等移动端框架,并分别介绍了它们的特性和资源地址。&#…

Java 版 Manus 实现来了,Spring AI Alibaba 发布开源 OpenManus 实现

大家好,我是玄姐。 此次官方发布的 Spring AI Alibaba OpenManus 实现,涵盖了完整的多智能体任务规划、思考与执行流程。这一版本专为 Java 开发者设计,能够让开发者亲身体验多智能体协同工作的强大效果。它具备根据用户问题进行深度分析、操…

Linux之基础开发工具二(makefile,git,gdb)

目录 一、自动化构建-make/makefile 1.1、背景 1.2、基本使用 1.3、推导过程 1.4、语法拓展 二、进度条小程序 2.1、回车与换行 2.2、行缓冲区 2.3、练手-倒计时程序 2.4、进度条程序 三、版本控制器-Git 3.1、版本控制器 3.2、gitee的使用 3.2.1、如何创建仓库 …

如何使用gitee进行代码管理(常见的两种私人令牌-HTTPS和公钥SSH)

Getee平台提供了四种方式管理代码,如下图所示: 一、使用私人令牌(HTTPS)管理代码 优点:账户下所有项目都可以操作,并且使用快捷,过程简单,可以选择令牌的权限范围,HTTPS…

@PathVariable注解-补充

这段代码是 Spring MVC 框架中使用 RESTful 风格的请求处理方法,详细解释其功能和注解: 代码功能概述 这段 Java 代码定义了一个 Spring MVC 控制器方法,用于处理 RESTful 风格的 URL 请求。它可以从 URL 路径中提取参数,并将这…

Canvas实例篇:十二星座之天秤座

Canvas实例篇:十二星座之天秤座 前言效果预览代码实现代码说明星座特定星 结语 前言 星座总给人浪漫而神秘的感觉,如何用代码还原星空中的浪漫?本文将通过 Canvas 技术,讲述如何实现一个可交互的天秤座星空图,包含星星…

VIP》》IP地址漂移

IP地址漂移,就是一个虚拟的IP地址,能够在不同的物理服务器或网络接口之家来回转换,所以当你或者其他的网络设备跟这个虚拟IP地址连接的时候,并不会察觉到设备的转换。这对于网络流量调度,服务器负载均衡的使用意义重大…

【C语言】讲解 程序分配的区域(新手)

目录 代码区 数据区 堆区 栈区 常量区 重点比较一下堆区与 栈区 总结: 前言: C语言程序的内存分配区域是理解其运行机制的重要部分。根据提供的多条证据,我们可以总结出C语言程序在运行时主要涉及以下五个关键内存区域: 代…