PyTorch深度学习实战01:从环境搭建到模型训练的全流程深度体验
时时彩代理收入是多少

新闻动态

PyTorch深度学习实战01:从环境搭建到模型训练的全流程深度体验

发布日期:2025-11-26 23:48    点击次数:95

PyTorch深度学习实战01:从环境搭建到模型训练的全流程深度体验

摘要

本文以“全流程体验深度学习”为核心,基于《深入浅出PyTorch》教程精髓,系统梳理了PyTorch项目从环境配置到模型训练的全链路实践。内容涵盖PyTorch基础环境搭建、数据加载与预处理、神经网络模型构建、损失函数与优化器配置、训练循环实现及可视化监控等关键环节,并通过30%篇幅的完整实操代码示例(包含MNIST手写数字分类任务的端到端实现),深入解析每个步骤的技术细节与常见问题。无论是深度学习新手还是希望巩固基础的开发者,都能通过本文获得从理论到实践的完整指导。

一、PyTorch环境配置:开启深度学习的第一步

1,1 为什么选择PyTorch?

在深度学习框架的“江湖”中,PyTorch凭借其动态计算图(Dynamic Computation Graph)、直观的Pythonic语法和活跃的社区生态,已成为学术界和工业界的首选工具之一。与TensorFlow的静态图相比,PyTorch的动态图允许开发者在代码中直接操作张量(Tensor),像编写普通Python程序一样构建和调试模型,极大降低了学习门槛。

展开剩余97%

1,2 环境准备:从零搭建PyTorch开发环境

(1)基础依赖:Python与包管理工具

PyTorch基于Python生态,推荐使用Python 3,8+版本(兼容性最佳)。通过conda或pip管理依赖包(本文以conda为例,因其能更好地处理CUDA环境):

# 创建名为pytorch_env的虚拟环境(隔离依赖,避免冲突)

conda create -n pytorch_env python=3,9 -y

conda activate pytorch_env # 激活环境

(2)安装PyTorch:匹配CUDA版本(可选)

PyTorch支持CPU和GPU(NVIDIA CUDA)两种计算模式。若你的电脑有NVIDIA显卡且已安装CUDA驱动(通过nvidia-smi命令查看CUDA版本,如11,8),建议安装GPU版本的PyTorch以加速神经网络训练;否则选择CPU版本。

• GPU版本安装命令(以CUDA 11,8为例,通过PyTorch官网https://pytorch,org/get-started/locally/获取最新适配命令):

conda install pytorch torchvision torchaudio pytorch-cuda=11,8 -c pytorch -c nvidia

• CPU版本安装命令(无GPU时使用):

conda install pytorch torchvision torchaudio cpuonly -c pytorch

(3)验证安装:检查PyTorch与CUDA是否可用

打开Python交互环境(或Jupyter Notebook),运行以下代码:

import torch

# 检查PyTorch版本

print(f"PyTorch版本: {torch,__version__}") # 示例输出: 2,0,1

# 检查CUDA是否可用(GPU支持)

print(f"CUDA是否可用: {torch,cuda,is_available()}") # 若为True则支持GPU加速

if torch,cuda,is_available():

print(f"当前GPU设备: {torch,cuda,get_device_name(0)}") # 示例输出: NVIDIA GeForce RTX 3060

print(f"CUDA版本: {torch,version,cuda}") # 示例输出: 11,8

常见问题:若torch,cuda,is_available()返回False,请检查:① NVIDIA显卡驱动是否安装(通过nvidia-smi查看);② CUDA Toolkit版本是否与PyTorch版本匹配(如PyTorch 2,0,1通常需要CUDA 11,7/11,8);③ 虚拟环境中是否安装了pytorch-cuda(GPU版本)。

二、数据加载与预处理:深度学习的“燃料”准备

2,1 数据:深度学习的核心输入

深度学习模型的性能高度依赖数据质量。在实战中,我们需要完成以下数据相关任务:

• 数据加载:从本地文件(如CSV、图片)或公开数据集(如MNIST、CIFAR-10)中读取原始数据。

• 数据预处理:对原始数据进行标准化(如像素值缩放到[0,1])、归一化(如减去均值除以标准差)、数据增强(如旋转、翻转图片)等操作,提升模型泛化能力。

• 数据封装:将处理后的数据封装为PyTorch的Dataset和DataLoader对象,支持批量加载(batch)、随机打乱(shuffle)和多线程加速(num_workers)。

2,2 实战案例:MNIST手写数字数据集

MNIST是深度学习的“Hello World”级数据集,包含60,000张训练图片和10,000张测试图片,每张图片是28×28像素的手写数字(0-9)。

(1)使用torchvision加载MNIST

PyTorch的torchvision库提供了常用数据集的直接加载接口(自动下载到本地):

import torch

from torchvision import datasets, transforms

# 定义数据预处理流程:转换为Tensor并归一化到[-1,1](均值0,5,标准差0,5)

transform = transforms,Compose([

transforms,ToTensor(), # 将PIL图片或numpy数组转换为Tensor(形状[1,28,28],值范围[0,1])

transforms,Normalize((0,5,), (0,5,)) # 对单通道(灰度图)做归一化:(x - 0,5)/0,5 → 范围[-1,1]

])

# 下载并加载训练集(root指定存储路径,train=True表示训练集)

train_dataset = datasets,MNIST(

root=',/data',

train=True,

download=True, # 首次运行自动下载数据集

transform=transform

# 下载并加载测试集

test_dataset = datasets,MNIST(

root=',/data',

train=False,

download=True,

transform=transform

print(f"训练集样本数: {len(train_dataset)}") # 输出: 60000

print(f"测试集样本数: {len(test_dataset)}") # 输出: 10000

(2)封装DataLoader:支持批量与随机化

DataLoader是PyTorch的数据加载器,可将Dataset对象封装为支持批量迭代、随机打乱和多线程加载的迭代器:

from torch,utils,data import DataLoader

# 定义批量大小(batch_size)和是否随机打乱(shuffle)

batch_size = 64

train_loader = DataLoader(

dataset=train_dataset,

batch_size=batch_size,

shuffle=True # 训练时随机打乱数据,避免模型学习到顺序偏差

test_loader = DataLoader(

dataset=test_dataset,

batch_size=batch_size,

shuffle=False # 测试时通常不需要随机化

# 查看一个批次的数据形状

for images, labels in train_loader:

print(f"单批次图片形状: {images,shape}") # [batch_size, channels, height, width] → [64, 1, 28, 28]

print(f"单批次标签形状: {labels,shape}") # [batch_size] → [64](每个元素是0-9的数字)

break # 仅查看第一个批次

关键参数说明:

- transforms,ToTensor():将图片数据转换为PyTorch的Tensor类型,并自动将像素值从[0,255]缩放到[0,1]。

- transforms,Normalize(mean, std):对数据进行标准化,公式为(x - mean) / std。对于MNIST的灰度图(单通道),mean和std均为标量(若为RGB三通道图片,需传入长度为3的元组,如(0,5, 0,5, 0,5)和(0,5, 0,5, 0,5))。

- DataLoader的shuffle=True:训练时随机打乱数据顺序,防止模型因数据顺序固定而学习到偏差(如总是先看到数字“0”的样本)。

三、模型构建:从全连接网络到神经网络设计

3,1 PyTorch模型定义:继承nn,Module

PyTorch中所有神经网络模型均需继承torch,nn,Module基类,并实现__init__(定义网络层)和forward(定义数据流向)两个核心方法。

(1)全连接网络(MLP)实现MNIST分类

针对MNIST的28×28像素图片,首先将其展平为784维向量(28×28=784),然后通过多个全连接层(Linear)提取特征,最后用Softmax输出10个类别的概率分布。

import torch,nn as nn

import torch,nn,functional as F

class MNISTNet(nn,Module):

def __init__(self):

super(MNISTNet, self),__init__()

# 定义网络层:输入784维(展平后的图片),输出10维(10个数字类别)

self,fc1 = nn,Linear(28 * 28, 128) # 第一层全连接:784 → 128

self,fc2 = nn,Linear(128, 64) # 第二层全连接:128 → 64

self,fc3 = nn,Linear(64, 10) # 输出层:64 → 10(对应数字0-9)

def forward(self, x):

# 展平输入图片(从[batch_size, 1, 28, 28] → [batch_size, 784])

x = x,view(-1, 28 * 28) # -1表示自动推断batch_size

# 逐层前向传播(激活函数用ReLU)

x = F,relu(self,fc1(x)) # 第一层输出:ReLU(128维)

x = F,relu(self,fc2(x)) # 第二层输出:ReLU(64维)

x = self,fc3(x) # 输出层:10维(不经过激活函数,后续用CrossEntropyLoss自带Softmax)

return x

# 实例化模型

model = MNISTNet()

print(model) # 打印模型结构

代码解析:

- nn,Linear(in_features, out_features):定义一个全连接层,输入维度为in_features,输出维度为out_features(内部包含权重矩阵W和偏置b)。

- F,relu():ReLU激活函数(Rectified Linear Unit),公式为max(0, x),用于引入非线性能力(避免多层线性变换退化为单层线性变换)。

- view(-1, 28*28):将输入的4D张量([batch_size, 1, 28, 28])展平为2D张量([batch_size, 784]),-1表示自动计算该维度的大小(根据总元素数和已知维度推断)。

(2)模型参数初始化(可选)

PyTorch默认会对线性层的权重进行初始化(通常为均匀分布或正态分布),但为了提升训练稳定性,可以手动初始化(例如用Xavier初始化):

def init_weights(m):

if isinstance(m, nn,Linear):

nn,init,xavier_uniform_(m,weight) # Xavier初始化权重

nn,init,zeros_(m,bias) # 偏置初始化为0

model,apply(init_weights) # 对模型的所有子模块应用初始化函数

四、损失函数与优化器:指导模型学习的“指挥棒”

4,1 损失函数:衡量预测与真实的差距

对于多分类任务(如MNIST的10个数字),常用交叉熵损失(Cross Entropy Loss),它结合了Softmax函数和负对数似然损失(NLL Loss),公式为:

\[ \text{Loss} = -\log\left(\frac{e^{z_y}}{\sum_{j=1}^{C} e^{z_j}}\right) \]

其中 z_y 是真实类别对应的输出logit, C 是类别总数(10)。

PyTorch中直接使用nn,CrossEntropyLoss(注意:该函数内部已包含Softmax,因此模型的输出层不需要手动添加Softmax):

criterion = nn,CrossEntropyLoss() # 定义损失函数

4,2 优化器:更新模型参数的算法

优化器的核心任务是根据损失函数的梯度,调整模型的参数(权重和偏置)以最小化损失。常用的优化器包括:

• SGD(随机梯度下降):基础优化器,通过学习率(lr)控制参数更新步长。

• Adam:自适应学习率优化器(结合动量和自适应调整),通常收敛更快且效果更好。

本文选择Adam优化器(学习率lr=0,001):

import torch,optim as optim

optimizer = optim,Adam(model,parameters(), lr=0,001) # 传入模型的所有参数(通过model,parameters()获取)

关键参数说明:

- model,parameters():返回模型中所有需要训练的参数(即nn,Module子类中通过nn,Parameter定义的张量,如nn,Linear的权重和偏置)。

- lr(learning rate):学习率,控制参数更新的步长(过大可能导致震荡,过小可能导致收敛缓慢)。

五、训练循环:模型的“成长之路”

5,1 训练流程的核心步骤

一个完整的训练循环包含以下步骤(以一个epoch为例):

1, 前向传播:输入数据通过模型得到预测输出。

2, 计算损失:用损失函数对比预测输出和真实标签。

3, 反向传播:计算损失对模型参数的梯度(通过loss,backward())。

4, 参数更新:优化器根据梯度更新参数(通过optimizer,step())。

5, 清零梯度:避免梯度累积(通过optimizer,zero_grad())。

此外,通常还会监控训练过程中的损失值和准确率,以便调试模型。

5,2 完整训练代码(含验证)

配置示例(mongod,conf):

storage:

wiredTiger:

engineConfig:

cacheSizeGB: 16 # 设置 WiredTiger 缓存为 16GB

游戏循环与事件处理

游戏开发的核心是主循环机制,典型结构包含三个关键组件:

def main_game_loop():

clock = pygame,time,Clock()

running = True

while running:

# 1, 事件处理阶段

for event in pygame,event,get():

if event,type == pygame,QUIT:

running = False

elif event,type == pygame,KEYDOWN:

handle_key_press(event,key)

# 2, 游戏逻辑更新

update_game_state(delta_time)

# 3, 渲染绘制阶段

screen,fill((0, 0, 0)) # 黑色背景

draw_game_objects()

pygame,display,flip() # 双缓冲交换

clock,tick(60) # 限制60FPS

事件类型对照表:

事件类型 触发条件 典型用途

QUIT 点击窗口关闭按钮 游戏安全退出

KEYDOWN/KEYUP 键盘按键动作 角色移动控制

MOUSEMOTION 鼠标移动 射击瞄准系统

USEREVENT 自定义定时器 子弹自动发射

高级事件处理技巧:

# 组合键检测示例

keys = pygame,key,get_pressed()

if keys[pygame,K_LEFT] and keys[pygame,K_SPACE]:

player,move_left_with_boost()

# 自定义事件系统

SHOOT_EVENT = pygame,USEREVENT + 1

pygame,time,set_timer(SHOOT_EVENT, 200) # 每200ms触发一次

2,2 坐标系与基础图形

Pygame采用左手坐标系(原点(0,0)在左上角,x向右递增,y向下递增),所有绘图操作需明确指定颜色参数(RGB/RGBA格式)。

核心绘图函数:

# 基本几何图形

pygame,draw,rect(screen, (255,0,0), (x,y,width,height), border_radius=5)

pygame,draw,;ep.bnnb29.cn@163.com;circle(screen, (0,255,0), (center_x,center_y), radius)

pygame,draw,line(screen, (255,255,0), start_pos, end_pos, width=3)

# 图像渲染优化

player_img = pygame,image,load("spaceship,png"),convert_alpha()

screen,;ep.bnnh26.com@163.com;blit(player_img, (x,y)) # 支持透明通道

坐标变换实战:

# 实现旋转动画

angle = (angle + 5) % 360

rotated_img = pygame,transform,rotate(original_img, angle)

new_rect = rotated_img,get_rect(center=original_rect,center)

screen,;ep.bnnb28.com@163.com;blit(rotated_img, new_rect,topleft)

第三章 实战项目:太空射击游戏(字 + 完整代码)

3,1 游戏架构设计

本项目采用面向对象设计模式,核心类结构如下:

├── GameEngine (游戏主控制器)

├── Player (玩家飞船类)

├── Enemy (敌机生成系统)

├── Bullet (子弹管理系统)

└── GameUI (界面渲染模块)

完整项目文件结构:

space_shooter/

│── main,py # 程序入口

│── config,py # 游戏配置常量

│── game_objects/ # 游戏实体类

│ ├── __init__,py

│ ├── player,py

│ ├── enemy,py

│ └── projectile,py

│── utils/ # 工具模块

│ ├── collision,py

│ └── assets,py

└── assets/ # 资源文件

├── images/

└── sounds/

3,2 核心代码实现(50%篇幅)

3,2,1 游戏初始化模块(config,py)

# 游戏基础配置

SCREEN_WIDTH = 1024

SCREEN_HEIGHT = 768

FPS = 60

BACKGROUND_COLOR = (0, 0, 20)

# 玩家设置

PLAYER_SPEED = 5

PLAYER_HEALTH = 100

PLAYER_IMG_PATH = "assets/images/player_ship,png"

# 敌机配置

ENEMY_SPAWN_RATE = 0,02 # 每帧生成概率

ENEMY_SPEED_RANGE = (2, 4)

ENEMY_TYPES = {

'basic': {'health': 30, 'speed': 2, 'score': 10},

'elite': {'health': 80, 'speed': 1, 'score': 30}

}

# 子弹系统

BULLET_SPEED = 8

BULLET_DAMAGE = 25

MAX_BULLETS = 10

3,2,2 玩家控制系统(player,py)

import pygame

from pygame,math import Vector2

class Player(pygame,sprite,Sprite):

def __init__(self, x, y):

super(),__init__()

self,image = pygame,image,load(config,PLAYER_IMG_PATH),convert_alpha()

self,rect = self,;ep.bnnb29.com@163.com;mage,get_rect(center=(x, y))

self,velocity = Vector2(0, 0)

self,health = config,PLAYER_HEALTH

self,;ep.okxhup25.com@163.com;shoot_cooldown = 0

def update(self):

# 处理移动输入

keys = pygame,key,get_pressed()

self,velocity,x = 0

self,velocity,y = 0

if keys[pygame,K_LEFT] or keys[pygame,K_a]:

self,velocity,x = -config,PLAYER_SPEED

if keys[pygame,;top.bnnb28.cn@163.com;K_RIGHT] or keys[pygame,K_d]:

self,velocity,x = config,PLAYER_SPEED

if keys[pygame,K_UP] or keys[pygame,K_w]:

self,velocity,y = -config,PLAYER_SPEED

if keys[pygame,;top.bnnb29.cn@163.com;K_DOWN] or keys[pygame,K_s]:

self,velocity,y = config,PLAYER_SPEED

# 对角线移动速度修正

if self,velocity,length() > 0:

self,velocity,scale_to_length(config,PLAYER_SPEED)

self,rect,x += int(self,velocity,x)

self,rect,y += int(self,velocity,y)

# 边界检测

self,rect,clamp_ip(pygame,Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT))

# 射击冷却计时

if self,shoot_cooldown > 0:

self;top.bnnh26.com@163.com;shoot_cooldown -= 1

def shoot(self):

if self,shoot_cooldown <= 0:

bullet = Bullet(self,rect,centerx, self,rect,top, -config,BULLET_SPEED)

return bullet

return None

3,2,3 敌机生成系统(enemy,py)

import random

from pygame,;top.bnnb28.com@163.com;math import Vector2

class Enemy(pygame,sprite,Sprite):

def __init__(self, enemy_type='basic'):

super(),__init__()

self,type = enemy_type

self,config = config,ENEMY_TYPES,get(enemy_type, config,ENEMY_TYPES['basic'])

# 根据类型加载不同图像

img_path = f"assets/images/enemy_{enemy_type},png"

try:

self,image = pygame,image,load(img_path),convert_alpha()

except:

self,image = pygame,Surface((40, 40))

self,image,;top.bnnb29.com@163.com;fill((255, 0, 0))

self,rect = self,image,get_rect()

self,rect,x = random,randint(0, SCREEN_WIDTH - self,rect,width)

self,rect,y = -self,rect,height

self,health = self,config['health']

self,speed = random,uniform(*config,ENEMY_SPEED_RANGE)

self,score_value = self,config['score']

def update(self):

self,rect,;top.okxhup25.com@163.com;y += int(self,speed)

# 超出屏幕边界自动销毁

if self,rect,top > SCREEN_HEIGHT:

self,kill()

def take_damage(self, damage):

self,health -= damage

if self,;top.bka1.cn@163.com;health <= 0:

return True # 标记为需要销毁

return False

3,2,4 碰撞检测系统(utils/collision,py)

def check_collision(sprite1, sprite2):

"""精确像素级碰撞检测"""

return pygame,sprite,collide_mask(sprite1, sprite2)

def check_rect_collision(rect1, rect2):

"""基础矩形碰撞检测(性能优化版)"""

return rect1,;top.bka2.cn@163.com;colliderect(rect2)

def group_collision(sprite, group):

"""精灵与精灵组的碰撞检测"""

return pygame,sprite,spritecollide(sprite, group, False, check_rect_collision)

3,2,5 主游戏循环(main,py)

import pygame

import sys

from game_objects,player import Player

from game_objects,enemy import Enemy

from game_objects,projectile import Bullet

from utils,collision import check_collision

class SpaceShooterGame:

def __init__(self):

pygame,init()

self,screen = pygame,display,set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

pygame,display,set_caption("Python太空射击游戏")

self,;top.bka3.cn@163.com;clock = pygame,time,Clock()

self,running = True

self,score = 0

self,font = pygame,font,Font(None, 36)

# 游戏对象组

self,all_sprites = pygame,sprite,Group()

self,enemies = pygame,sprite,Group()

self,bullets = pygame,sprite,Group()

self,particles = pygame,sprite,Group()

# 创建玩家

self,player = Player(SCREEN_WIDTH//2, SCREEN_HEIGHT - 100)

self,all_sprites,add(self,player)

def handle_events(self):

for event in pygame,event,get():

if event,;top.bak4.cn@163.com;type == pygame,QUIT:

self,running = False

elif event,type == pygame,KEYDOWN:

if event,key == pygame,K_SPACE:

bullet = self,player,shoot()

if bullet:

self,;top.bak5.cn@163.com;bullets,add(bullet)

self,all_sprites,add(bullet)

def update_game(self):

# 生成敌机

if random,random() < config,ENEMY_SPAWN_RATE:

enemy_type = random,choice(list(config,ENEMY_TYPES,keys()))

enemy = Enemy(enemy_type)

self,enemies,add(enemy)

self,;top.bak6.cn@163.com;all_sprites,add(enemy)

# 更新所有精灵

self,all_sprites,update()

# 子弹与敌机碰撞

for bullet in self,bullets:

hit_enemies = pygame,sprite,spritecollide(bullet, self,enemies, False)

for enemy in hit_enemies:

if enemy,;top.bak7.cn@163.com;take_damage(config,BULLET_DAMAGE):

self,score += enemy,score_value

enemy,kill()

bullet,kill()

# 玩家与敌机碰撞

if pygame,sprite,spritecollide(self,player, self,enemies, False):

self,player,health -= 10

if self,player,health <= 0:

self,;ep.bnnb25.cn@163.com;game_over()

def render(self):

self,screen,fill(config,BACKGROUND_COLOR)

self,all_sprites,draw(self,screen)

# 绘制UI信息

score_text = self,font,render(f"Score: {self,score}", True, (255, 255, 255))

health_text = self,font,render(f"Health: {self,player,health}", True, (255, 255, 255))

self,screen,blit(score_text, (10, 10))

self,screen,blit(health_text, (10, 50))

pygame,;ep.bnnb26.cn@163.com;display,flip()

def game_over(self):

game_over_text = self,font,render("GAME OVER - Press R to Restart", True, (255, 0, 0))

text_rect = game_over_text,get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))

self,screen,blit(game_over_text, text_rect)

pygame,display,flip()

waiting = True

while waiting:

for event in pygame,event,get():

if event,;ep.bnnb28.cn@163.com;type == pygame,QUIT:

self,running = False

waiting = False

elif event,type == pygame,KEYDOWN:

if event,key == pygame,K_r:

self,__init__() # 重置游戏状态

waiting = False

def run(self):

while self,running:

self,handle_events()

self,update_game()

self,render()

self,clock,tick(FPS)

pygame,quit()

sys,exit()

if __name__ == "__main__":

game = SpaceShooterGame()

game,run()

import torch

# 训练函数

def train(model, train_loader, criterion, optimizer, epoch):

model,train() # 设置模型为训练模式(启用Dropout/BatchNorm的训练行为)

total_loss = 0

correct = 0

total = 0

for batch_idx, (images, labels) in enumerate(train_loader):

# 1, 将数据移动到GPU(如果可用)

if torch,cuda,is_available():

images, labels = images,cuda(), labels,cuda()

# 2, 前向传播

outputs = model(images)

loss = criterion(outputs, labels) # 计算损失

# 3, 反向传播与参数更新

optimizer,zero_grad() # 清零梯度(避免累积)

loss,backward() # 计算梯度

optimizer,step() # 更新参数

# 4, 统计损失和准确率

total_loss += loss,item() * images,size(0) # 累计损失(loss,item()是当前批次的平均损失)

_, predicted = torch,max(outputs,data, 1) # 获取预测的类别(取概率最大的索引)

total += labels,size(0) # 累计样本总数

correct += (predicted == labels),sum(),item() # 累计正确预测数

# 每100个批次打印一次进度

if batch_idx % 100 == 0:

print(f'Train Epoch: {epoch} [{batch_idx * len(images)}/{len(train_loader,dataset)} '

f'({100, * batch_idx / len(train_loader):,0f}%)]\tLoss: {loss,item():,6f}')

# 计算平均损失和准确率

avg_loss = total_loss / total

accuracy = 100, * correct / total

print(f'\nTrain Epoch: {epoch} \tAverage Loss: {avg_loss:,4f}, Accuracy: {accuracy:,2f}%\n')

return avg_loss, accuracy

# 验证函数(测试集评估)

def test(model, test_loader):

model,eval() # 设置模型为评估模式(禁用Dropout/BatchNorm的训练行为)

test_loss = 0

correct = 0

total = 0

with torch,no_grad(): # 禁用梯度计算(节省内存和计算资源)

for images, labels in test_loader:

if torch,cuda,is_available():

images, labels = images,cuda(), labels,cuda()

outputs = model(images)

test_loss += criterion(outputs, labels),item() * images,size(0)

_, predicted = torch,max(outputs,data, 1)

total += labels,size(0)

correct += (predicted == labels),sum(),item()

test_loss /= total

accuracy = 100, * correct / total

print(f'Test set: Average Loss: {test_loss:,4f}, Accuracy: {correct}/{total} ({accuracy:,2f}%)\n')

return test_loss, accuracy

# 主训练流程

num_epochs = 5 # 训练轮数

for epoch in range(1, num_epochs + 1):

train_loss, train_acc = train(model, train_loader, criterion, optimizer, epoch)

test_loss, test_acc = test(model, test_loader)

代码解析:

- model,train() 和 model,eval():分别设置模型的训练模式和评估模式。训练模式下,Dropout层会随机丢弃部分神经元,BatchNorm层会使用当前批次的统计量;评估模式下,Dropout层不丢弃神经元,BatchNorm层使用训练阶段统计的全局均值和方差。

- torch,no_grad():上下文管理器,禁用梯度计算(验证时不需要反向传播,可节省资源)。

- torch,max(outputs,data, 1):返回每行(每个样本)的最大值及其索引,索引即为预测的类别(如输出为[0,1, 0,8, 0,1],则预测类别为1)。

六、可视化与模型保存:让训练过程“看得见”

6,1 使用TensorBoard可视化训练指标

TensorBoard是PyTorch官方推荐的可视化工具,可实时监控损失值、准确率等指标的变化趋势。

(1)安装与启动

pip install tensorboard

tensorboard --logdir=runs # 启动服务(默认端口6006,浏览器访问http://localhost:6006)

(2)在代码中记录指标

from torch,utils,tensorboard import SummaryWriter

writer = SummaryWriter('runs/mnist_experiment') # 指定日志保存目录

for epoch in range(1, num_epochs + 1):

train_loss, train_acc = train(model, train_loader, criterion, optimizer, epoch)

test_loss, test_acc = test(model, test_loader)

# 记录损失和准确率到TensorBoard

writer,add_scalar('Loss/train', train_loss, epoch)

writer,add_scalar('Accuracy/train', train_acc, epoch)

writer,add_scalar('Loss/test', test_loss, epoch)

writer,add_scalar('Accuracy/test', test_acc, epoch)

writer,close() # 关闭写入器

6,2 保存与加载模型

训练完成后,通常需要保存模型的参数(或整个模型),以便后续推理或继续训练。

(1)保存模型参数(推荐)

仅保存模型的state_dict(包含所有可学习参数的权重和偏置),体积小且灵活(可加载到不同结构的模型中,只要参数名匹配):

torch,save(model,state_dict(), 'mnist_model,pth') # 保存到当前目录

# 加载模型参数(需先实例化相同结构的模型)

loaded_model = MNISTNet()

loaded_model,load_state_dict(torch,load('mnist_model,pth'))

loaded_model,eval() # 设置为评估模式

(2)保存整个模型(包含结构和参数)

直接保存整个模型对象(包括网络结构定义),但灵活性较低(若模型代码修改后可能无法加载):

torch,save(model, 'mnist_model_full,pth') # 保存整个模型

loaded_model = torch,load('mnist_model_full,pth') # 直接加载整个模型

loaded_model,eval()

七、总结与进阶方向

7,1 本文核心知识点总结

• 环境配置:通过conda安装PyTorch(匹配CUDA版本),验证GPU可用性。

• 数据加载:使用torchvision,datasets和DataLoader实现MNIST数据的高效加载与预处理。

• 模型构建:继承nn,Module定义全连接网络,通过forward方法指定数据流向。

• 训练循环:结合损失函数(CrossEntropyLoss)和优化器(Adam),实现前向传播、反向传播与参数更新。

• 可视化与保存:通过TensorBoard监控训练指标,保存模型参数供后续使用。

7,2 进阶学习方向

• 卷积神经网络(CNN):针对图像数据,用卷积层(Conv2d)替代全连接层,提升特征提取能力(MNIST的准确率可从95%+提升到99%+)。

• 迁移学习:使用预训练模型(如ResNet、VGG)处理更复杂的任务(如CIFAR-10、ImageNet分类)。

• 超参数调优:通过网格搜索或贝叶斯优化调整学习率、批量大小、网络层数等参数。

通过本文的全流程实践,你已经掌握了PyTorch深度学习项目的核心流程。接下来,可以尝试更复杂的数据集(如CIFAR-10)或模型结构(如CNN),开启真正的深度学习之旅!

发布于:广东省