新手必看!PyTorch基础知识全面解析 – wiki基地


新手必看!PyTorch基础知识全面解析:从入门到实践

前言:AI时代,为何选择PyTorch?

在人工智能浪潮席卷全球的今天,深度学习已成为推动技术进步的核心引擎。而要驾驭这股力量,选择一个强大而灵活的深度学习框架至关重要。PyTorch,由Facebook AI研究院(FAIR)开发并维护,凭借其Pythonic的风格、动态计算图、易于调试等特点,迅速在学术界和工业界占据了一席之地,成为研究人员和开发者构建、训练神经网络的首选工具之一。

对于初学者而言,PyTorch的上手体验非常友好。它更贴近Python的编程习惯,让你能够以更直观的方式理解和实现复杂的深度学习模型。本文旨在为完全没有PyTorch经验的新手提供一份全面、深入的基础知识指南,帮助你从零开始,逐步掌握PyTorch的核心概念和操作,为未来的深度学习探索打下坚实的基础。

我们将从PyTorch最基本的数据结构——张量(Tensor)讲起,深入探索自动求导(Autograd)的奥秘,然后逐步构建起神经网络模型、理解优化器与损失函数,并最终掌握数据加载、模型训练以及GPU加速等实践技巧。准备好了吗?让我们一起踏上PyTorch的学习之旅!

第一章:安装与环境配置

万事开头难,但PyTorch的安装却异常简单。良好的开始是成功的一半。

1.1 选择合适的版本

在PyTorch官网 (pytorch.org) 你可以找到详细的安装指令。选择时需要注意以下几点:
* 操作系统: Windows、macOS、Linux。
* 包管理器: pip 或 conda (推荐,更易管理环境)。
* Python版本: 通常支持较新的Python版本。
* CUDA版本: 如果你的电脑有NVIDIA显卡并希望使用GPU加速,需要安装与你的显卡驱动兼容的CUDA版本。如果没有,可以选择CPU版本。

1.2 安装示例 (使用conda)

假设我们要在Linux系统上安装支持CUDA 11.8的PyTorch,推荐使用Anaconda或Miniconda来管理Python环境。

“`bash

创建一个新的conda环境 (可选,但推荐)

conda create -n my_pytorch_env python=3.9
conda activate my_pytorch_env

从PyTorch官网获取的安装命令

例如,如果你的CUDA版本是11.8

conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia

如果你不需要GPU加速,只安装CPU版本

conda install pytorch torchvision torchaudio cpuonly -c pytorch

“`

安装完成后,可以通过Python交互式环境进行验证:

python
import torch
print(torch.__version__)
print(torch.cuda.is_available()) # 检查GPU是否可用

如果 torch.cuda.is_available() 返回 True,恭喜你,你的GPU加速环境已配置成功!

第二章:PyTorch的基石——张量(Tensor)

张量是PyTorch中最基本的数据结构,它是一个多维数组,与NumPy的 ndarray 非常相似,但张量还支持GPU加速和自动求导功能。你可以把它想象成:
* 0维张量:标量(Scalar),一个独立的数值。
* 1维张量:向量(Vector),一串数值。
* 2维张量:矩阵(Matrix),一个二维表格。
* N维张量:高维数组。

2.1 张量的创建

PyTorch提供了多种创建张量的方法:

a. 直接从数据创建

“`python
import torch

从Python列表创建

data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(“直接从数据创建的张量:\n”, x_data)

输出:

直接从数据创建的张量:

tensor([[1, 2],

[3, 4]])

从NumPy数组创建 (张量与NumPy数组共享内存,修改其中一个会影响另一个)

import numpy as np
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(“从NumPy数组创建的张量:\n”, x_np)

输出:

从NumPy数组创建的张量:

tensor([[1, 2],

[3, 4]])

“`

b. 创建特定形状和值的张量

“`python

创建一个2×3的全1张量

x_ones = torch.ones(2, 3)
print(“全1张量:\n”, x_ones)

输出:

全1张量:

tensor([[1., 1., 1.],

[1., 1., 1.]])

创建一个2×3的全0张量

x_zeros = torch.zeros(2, 3)
print(“全0张量:\n”, x_zeros)

输出:

全0张量:

tensor([[0., 0., 0.],

[0., 0., 0.]])

创建一个2×3的随机张量 (均匀分布在[0,1)之间)

x_rand = torch.rand(2, 3)
print(“随机张量 (rand):\n”, x_rand)

输出:

随机张量 (rand):

tensor([[0.8206, 0.5057, 0.5061],

[0.6033, 0.4578, 0.4632]])

创建一个2×3的随机张量 (标准正态分布,均值为0,方差为1)

x_randn = torch.randn(2, 3)
print(“随机张量 (randn):\n”, x_randn)

输出:

随机张量 (randn):

tensor([[-0.2081, 1.1396, -0.6358],

[ 1.3435, 1.4243, -0.6433]])

创建一个张量,其形状和数据类型与另一个张量相同,但值为随机

x_like = torch.rand_like(x_data, dtype=torch.float)
print(“形状和数据类型与x_data相同,但值为随机的张量:\n”, x_like)

输出:

形状和数据类型与x_data相同,但值为随机的张量:

tensor([[0.2033, 0.8164],

[0.6074, 0.4468]])

“`

2.2 张量的属性

张量有三个核心属性,它们描述了张量的形状、数据类型和存储设备:

“`python
tensor = torch.rand(3, 4)
print(f”张量的形状 (Shape): {tensor.shape}”)
print(f”张量的数据类型 (Dtype): {tensor.dtype}”)
print(f”张量存储的设备 (Device): {tensor.device}”)

输出:

张量的形状 (Shape): torch.Size([3, 4])

张量的数据类型 (Dtype): torch.float32

张量存储的设备 (Device): cpu

``
*
shape:张量的维度信息,例如(3, 4)表示3行4列的矩阵。
*
dtype:张量中元素的数据类型,例如torch.float32(单精度浮点数)、torch.int64(长整型)等。
*
device:张量所存储的设备,cpu表示中央处理器,cuda:0` 表示第一个GPU。

2.3 张量的操作

张量支持各种常见的数学运算、索引、切片和形状变换。

a. 算术运算

“`python
tensor = torch.ones(4, 4)
tensor[:,1] = 0 # 将第二列设置为0
print(“原始张量:\n”, tensor)

输出:

原始张量:

tensor([[1., 0., 1., 1.],

[1., 0., 1., 1.],

[1., 0., 1., 1.],

[1., 0., 1., 1.]])

逐元素相加

tensor_add = tensor + tensor
print(“逐元素相加:\n”, tensor_add)

矩阵乘法

tensor_mul = tensor.matmul(tensor.T) # .T 是转置
print(“矩阵乘法:\n”, tensor_mul)

逐元素乘法

tensor_elem_mul = tensor * tensor
print(“逐元素乘法:\n”, tensor_elem_mul)

也可以使用函数形式

torch.add(tensor, tensor)
torch.mul(tensor, tensor)
torch.matmul(tensor, tensor.T)
“`

b. 索引和切片

与NumPy类似,可以使用索引和切片访问张量的子集:

“`python
tensor = torch.arange(16).reshape(4, 4)
print(“原始张量:\n”, tensor)

输出:

原始张量:

tensor([[ 0, 1, 2, 3],

[ 4, 5, 6, 7],

[ 8, 9, 10, 11],

[12, 13, 14, 15]])

访问第一行

print(“第一行:”, tensor[0])

访问第二列

print(“第二列:”, tensor[:, 1])

访问第一行第二列的元素

print(“第一行第二列的元素:”, tensor[0, 1])

访问从第二行到第三行(不包括第四行)的所有列

print(“部分行切片:\n”, tensor[1:3, :])

访问所有行,从第二列到第三列(不包括第四列)

print(“部分列切片:\n”, tensor[:, 1:3])
“`

c. 形状变换

“`python
tensor = torch.ones(4, 4)

使用 .view() 或 .reshape() 改变张量形状 (元素数量必须一致)

reshaped_tensor = tensor.view(2, 8)
print(“reshape后:\n”, reshaped_tensor)

-1 表示该维度大小由其他维度推断

reshaped_tensor_auto = tensor.view(-1, 2) # 自动推断行数
print(“自动推断reshape后:\n”, reshaped_tensor_auto)

使用 .squeeze() 和 .unsqueeze() 移除或添加维度

x = torch.zeros(1, 3, 1, 5)
print(“原始形状:”, x.shape) # torch.Size([1, 3, 1, 5])
y = x.squeeze() # 移除所有维度大小为1的维度
print(“squeeze后形状:”, y.shape) # torch.Size([3, 5])
z = y.unsqueeze(0) # 在第0维添加一个维度
print(“unsqueeze后形状:”, z.shape) # torch.Size([1, 3, 5])
“`

d. CPU与GPU之间传输

python
if torch.cuda.is_available():
tensor = tensor.to("cuda") # 将张量移动到GPU
print(f"张量在GPU上: {tensor.device}")
tensor = tensor.to("cpu") # 将张量移回CPU
print(f"张量在CPU上: {tensor.device}")
else:
print("CUDA不可用,张量仍在CPU上。")

张量是PyTorch深度学习的基石,熟练掌握其创建、属性和操作是迈向高级应用的第一步。

第三章:自动求导的魔力——Autograd

深度学习的核心是反向传播(Backpropagation),它通过计算损失函数相对于模型参数的梯度来更新参数。PyTorch的Autograd(自动求导)系统正是幕后的“魔术师”,它能够自动为任何计算图中的张量计算梯度。

3.1 核心概念:requires_grad

默认情况下,张量的requires_grad属性是False。只有当一个张量的requires_grad设置为True时,PyTorch才会跟踪其上的所有操作,以便在后续计算梯度。

“`python
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x * 2
z = y.sum()

print(“x:”, x) # tensor([1., 2., 3.], requires_grad=True)
print(“y:”, y) # tensor([2., 4., 6.], grad_fn=)
print(“z:”, z) # tensor(12., grad_fn=)

对z进行反向传播

z.backward()

访问x的梯度

print(“x的梯度:”, x.grad) # tensor([2., 2., 2.])
``
在这个例子中,
z = (x_02) + (x_12) + (x_2*2)
*
dz/dx_0 = 2*dz/dx_1 = 2*dz/dx_2 = 2因此,x.grad的结果是[2., 2., 2.]`。

3.2 梯度清零:zero_grad()

在神经网络训练中,每次迭代(或每个批次)都需要计算新的梯度。PyTorch会将梯度累加到.grad属性中。因此,在每次反向传播之前,我们必须手动将模型的梯度清零,以避免旧梯度的干扰。

“`python
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x * 2
z = y.sum()
z.backward()
print(“第一次反向传播后的x.grad:”, x.grad) # tensor([2., 2.])

再次进行计算

z2 = (x * 3).sum()
z2.backward()
print(“第二次反向传播后的x.grad:”, x.grad) # tensor([5., 5.]) (2+3累加了)

正确的做法是每次迭代前清零

x = torch.tensor([1.0, 2.0], requires_grad=True)
x.grad = None # 或者通过optimizer.zero_grad()来清零
y = x * 2
z = y.sum()
z.backward()
print(“清零后第一次反向传播后的x.grad:”, x.grad) # tensor([2., 2.])

x.grad.zero_() # 手动清零梯度
y2 = x * 3
z2 = y2.sum()
z2.backward()
print(“清零后第二次反向传播后的x.grad:”, x.grad) # tensor([3., 3.])
``
通常,我们通过优化器对象来清零梯度:
optimizer.zero_grad()`。

3.3 禁用梯度计算:torch.no_grad()

在模型评估或推理阶段,我们不需要计算梯度,这不仅可以节省内存,还能提高计算速度。可以使用torch.no_grad()上下文管理器来临时禁用梯度计算。

“`python
x = torch.tensor([1.0, 2.0], requires_grad=True)

with torch.no_grad():
y = x * 2
print(“在no_grad上下文中,y是否需要梯度:”, y.requires_grad) # False

退出no_grad上下文,梯度计算恢复

z = x * 3
print(“在no_grad上下文外,z是否需要梯度:”, z.requires_grad) # True
“`

理解Autograd是理解神经网络训练过程的关键,它极大地简化了深度学习框架的实现难度。

第四章:构建神经网络——nn.Module

PyTorch的torch.nn模块提供了构建神经网络所需的各种层(layers)和模块。所有神经网络模型都应该继承自nn.Module类。

4.1 nn.Module 的基本结构

每个自定义的神经网络模型都必须:
* 继承 nn.Module
* 在 __init__ 方法中定义网络的层。
* 在 forward 方法中定义数据如何在这些层之间流动(即前向传播)。

“`python
import torch.nn as nn
import torch.nn.functional as F

class SimpleNeuralNetwork(nn.Module):
def init(self):
super(SimpleNeuralNetwork, self).init()
# 定义网络层
# nn.Linear(in_features, out_features) 是全连接层
self.fc1 = nn.Linear(784, 128) # 输入784个特征(例如28×28图像展平),输出128个特征
self.relu = nn.ReLU() # 激活函数,非线性变换
self.fc2 = nn.Linear(128, 10) # 输出10个特征(例如10个类别的分类)

def forward(self, x):
    # 定义数据的前向传播路径
    x = self.fc1(x)
    x = self.relu(x)
    x = self.fc2(x)
    return x

实例化模型

model = SimpleNeuralNetwork()
print(model)

输出:

SimpleNeuralNetwork(

(fc1): Linear(in_features=784, out_features=128, bias=True)

(relu): ReLU()

(fc2): Linear(in_features=128, out_features=10, bias=True)

)

随机生成一个输入数据 (批大小为64,784个特征)

dummy_input = torch.randn(64, 784)
output = model(dummy_input)
print(“模型输出形状:”, output.shape) # torch.Size([64, 10])
“`

4.2 常用的神经网络层

torch.nn提供了丰富的层类型:
* 线性层 (Linear Layers): nn.Linear (全连接层)。
* 卷积层 (Convolutional Layers): nn.Conv1d, nn.Conv2d, nn.Conv3d (用于图像、序列数据等)。
* 池化层 (Pooling Layers): nn.MaxPool1d, nn.MaxPool2d, nn.AvgPool2d (用于降采样)。
* 激活函数 (Activation Functions): nn.ReLU, nn.Sigmoid, nn.Tanh, nn.LeakyReLU 等。这些也可以通过 torch.nn.functional 调用,如 F.relu()
* 循环神经网络层 (Recurrent Layers): nn.RNN, nn.LSTM, nn.GRU (用于序列数据)。
* 批归一化 (Batch Normalization): nn.BatchNorm1d, nn.BatchNorm2d (用于加速训练和提高稳定性)。
* Dropout 层: nn.Dropout (用于防止过拟合)。

4.3 序列化容器:nn.Sequential

对于简单的、按顺序堆叠的层,可以使用nn.Sequential来更简洁地定义模型:

“`python
model_sequential = nn.Sequential(
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10)
)
print(model_sequential)

输出:

Sequential(

(0): Linear(in_features=784, out_features=128, bias=True)

(1): ReLU()

(2): Linear(in_features=128, out_features=64, bias=True)

(3): ReLU()

(4): Linear(in_features=64, out_features=10, bias=True)

)

``nn.Sequential`的优点是代码简洁,但缺点是灵活性较低,无法实现复杂的网络结构(如分支、跳跃连接等)。

第五章:优化器与损失函数

构建完模型后,我们需要定义如何衡量模型预测的“好坏”(损失函数),以及如何调整模型参数来最小化这个“坏”(优化器)。

5.1 损失函数(Loss Functions)

损失函数(或称成本函数、误差函数)衡量模型预测值与真实值之间的差异。PyTorch的nn模块也提供了多种常用的损失函数。

  • 分类问题:

    • nn.CrossEntropyLoss():常用于多分类问题,它结合了nn.LogSoftmaxnn.NLLLoss。输入是模型的原始输出(logits),目标是类别索引。
    • nn.BCELoss():用于二分类问题,通常与nn.Sigmoid激活函数一起使用。
    • nn.BCEWithLogitsLoss()BCELoss的稳定版本,集成了Sigmoid,直接接收模型的原始输出。
  • 回归问题:

    • nn.MSELoss() (Mean Squared Error Loss):均方误差损失,适用于回归任务。
    • nn.L1Loss() (Mean Absolute Error Loss):平均绝对误差损失,对异常值不敏感。

“`python

示例:交叉熵损失 (分类)

loss_fn_ce = nn.CrossEntropyLoss()
predictions = torch.randn(3, 5) # 3个样本,5个类别
targets = torch.tensor([1, 0, 4]) # 真实类别索引
loss = loss_fn_ce(predictions, targets)
print(“交叉熵损失:”, loss)

示例:均方误差损失 (回归)

loss_fn_mse = nn.MSELoss()
predictions_reg = torch.randn(5, 1) # 5个样本,1个输出
targets_reg = torch.randn(5, 1)
loss_reg = loss_fn_mse(predictions_reg, targets_reg)
print(“均方误差损失:”, loss_reg)
“`

5.2 优化器(Optimizers)

优化器的任务是根据损失函数计算出的梯度,更新模型的权重和偏置,使损失函数最小化。PyTorch的torch.optim模块提供了各种优化算法。

  • torch.optim.SGD (Stochastic Gradient Descent): 随机梯度下降,最基础的优化器,可以添加动量(momentum)来加速收敛。
  • torch.optim.Adam: 自适应矩估计,广泛使用的优化器,通常收敛速度更快,性能更好。
  • torch.optim.Adagrad:
  • torch.optim.RMSprop:

“`python
import torch.optim as optim

实例化模型

model = SimpleNeuralNetwork()

实例化优化器

参数1: model.parameters() 告诉优化器需要更新哪些参数

参数2: lr (learning rate) 学习率,控制每次更新的步长

optimizer_sgd = optim.SGD(model.parameters(), lr=0.01)
optimizer_adam = optim.Adam(model.parameters(), lr=0.001)

在训练循环中的使用:

optimizer.zero_grad() # 清零梯度

loss.backward() # 计算梯度

optimizer.step() # 根据梯度更新参数

“`
优化器是深度学习训练的核心,它的选择和学习率的设置对模型的性能至关重要。

第六章:数据处理——DatasetDataLoader

在实际的深度学习任务中,我们通常需要处理大量数据,并且以小批量(mini-batches)的形式进行训练。PyTorch提供了torch.utils.data.Datasettorch.utils.data.DataLoader两个工具来高效地管理数据。

6.1 Dataset:数据样本的集合

Dataset是一个抽象类,表示一个数据集,其中包含样本及其对应的标签。你需要自定义一个继承自Dataset的类,并重写两个方法:
* __len__:返回数据集中的样本总数。
* __getitem__:根据给定的索引返回一个样本及其标签。

“`python
from torch.utils.data import Dataset, DataLoader
import pandas as pd

class CustomDataset(Dataset):
def init(self, csv_file, transform=None):
“””
Args:
csv_file (string): 包含数据和标签的CSV文件路径。
transform (callable, optional): 应用于样本的A函数/变换。
“””
self.data_frame = pd.read_csv(csv_file)
self.transform = transform

def __len__(self):
    return len(self.data_frame)

def __getitem__(self, idx):
    # 假设CSV文件第一列是特征,第二列是标签
    features = torch.tensor(self.data_frame.iloc[idx, 0].split(',')).float() # 示例:如果特征是逗号分隔的字符串
    label = torch.tensor(self.data_frame.iloc[idx, 1]).long()

    if self.transform:
        features = self.transform(features)

    sample = {'features': features, 'label': label}
    return sample

假设你有一个名为 ‘my_data.csv’ 的文件

示例数据格式 (my_data.csv):

“1.0,2.0,3.0”,0

“4.0,5.0,6.0”,1

(实际使用时需要根据数据格式调整__getitem__方法)

创建一个虚拟csv文件用于演示

with open(‘my_data.csv’, ‘w’) as f:
f.write(“1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0,41.0,42.0,43.0,44.0,45.0,46.0,47.0,48.0,49.0,50.0,51.0,52.0,53.0,54.0,55.0,56.0,57.0,58.0,59.0,60.0,61.0,62.0,63.0,64.0,65.0,66.0,67.0,68.0,69.0,70.0,71.0,72.0,73.0,74.0,75.0,76.0,77.0,78.0,79.0,80.0,81.0,82.0,83.0,84.0,85.0,86.0,87.0,88.0,89.0,90.0,91.0,92.0,93.0,94.0,95.0,96.0,97.0,98.0,99.0,100.0,101.0,102.0,103.0,104.0,105.0,106.0,107.0,108.0,109.0,110.0,111.0,112.0,113.0,114.0,115.0,116.0,117.0,118.0,119.0,120.0,121.0,122.0,123.0,124.0,125.0,126.0,127.0,128.0″,0\n”)
f.write(“1.0,2.0,3.0,…,128.0″,1\n”) # 仅为示例,实际应有完整128个特征

dataset = CustomDataset(csv_file=’my_data.csv’)

print(“数据集大小:”, len(dataset))

sample = dataset[0]

print(“第一个样本特征形状:”, sample[‘features’].shape, ” 标签:”, sample[‘label’])

为了避免文件操作的复杂性,这里简化`CustomDataset`为直接生成数据:python
class SimpleDataset(Dataset):
def init(self, num_samples=100, feature_dim=784, num_classes=10):
self.features = torch.randn(num_samples, feature_dim)
self.labels = torch.randint(0, num_classes, (num_samples,))

def __len__(self):
    return len(self.labels)

def __getitem__(self, idx):
    return self.features[idx], self.labels[idx]

dataset = SimpleDataset()
print(“数据集大小:”, len(dataset))
sample_features, sample_label = dataset[0]
print(“第一个样本特征形状:”, sample_features.shape, ” 标签:”, sample_label)
“`

6.2 DataLoader:批量加载数据

DataLoader包装了Dataset,并提供以下功能:
* 批量处理 (batching): 将多个样本组合成一个批次。
* 数据混洗 (shuffling): 在每个epoch开始时打乱数据,减少模型对数据顺序的依赖。
* 并行加载 (multi-process data loading): 使用多进程来加速数据加载(num_workers参数)。

“`python

实例化自定义数据集

train_dataset = SimpleDataset(num_samples=1000, feature_dim=784, num_classes=10)

实例化DataLoader

train_loader = DataLoader(dataset=train_dataset,
batch_size=64, # 每个批次64个样本
shuffle=True, # 每个epoch开始时打乱数据
num_workers=2) # 使用2个子进程加载数据 (Windows上设为0避免多进程问题)

遍历DataLoader

for batch_idx, (features, labels) in enumerate(train_loader):
print(f”批次 {batch_idx+1}:”)
print(” 特征形状:”, features.shape) # torch.Size([64, 784]) (最后一个批次可能小于64)
print(” 标签形状:”, labels.shape) # torch.Size([64])
if batch_idx == 2: # 只打印前3个批次
break
``DatasetDataLoader`是处理大规模数据集的关键工具,它们能够大大提高数据加载的效率和训练的便捷性。

第七章:完整的训练循环

现在,我们已经掌握了构建模型、定义损失函数和优化器,以及加载数据的所有基础知识。是时候把它们组合起来,创建一个完整的模型训练循环了。

“`python

1. 准备数据

train_dataset = SimpleDataset(num_samples=1000, feature_dim=784, num_classes=10)
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True, num_workers=0) # num_workers=0 for simple demo

2. 定义模型

class MyClassifier(nn.Module):
def init(self, input_dim, hidden_dim, output_dim):
super(MyClassifier, self).init()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_dim, output_dim)

def forward(self, x):
    out = self.fc1(x)
    out = self.relu(out)
    out = self.fc2(out)
    return out

input_dim = 784
hidden_dim = 128
output_dim = 10 # 10个类别

model = MyClassifier(input_dim, hidden_dim, output_dim)

3. 定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

4. 设置训练设备 (GPU或CPU)

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
model.to(device) # 将模型移动到指定设备

5. 训练循环

num_epochs = 10
print(f”开始在 {device} 上训练模型…”)

for epoch in range(num_epochs):
model.train() # 设置模型为训练模式
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
inputs = inputs.to(device) # 将数据移动到指定设备
labels = labels.to(device)

    # 前向传播
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    # 反向传播和优化
    optimizer.zero_grad() # 清零梯度
    loss.backward()       # 计算梯度
    optimizer.step()      # 更新模型参数

    running_loss += loss.item() * inputs.size(0)

epoch_loss = running_loss / len(train_dataset)
print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}")

print(“训练完成!”)

6. (可选) 模型评估

假设我们有一个测试数据集 test_loader

model.eval() # 设置模型为评估模式 (禁用Dropout等)

correct = 0

total = 0

with torch.no_grad(): # 在评估时禁用梯度计算

for inputs, labels in test_loader:

inputs = inputs.to(device)

labels = labels.to(device)

outputs = model(inputs)

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

total += labels.size(0)

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

print(f’测试集上的准确率: {100 * correct / total:.2f}%’)

“`
这个训练循环包含了深度学习模型训练的全部核心步骤:数据加载、前向传播、损失计算、反向传播、参数优化。

第八章:模型的保存与加载

训练好的模型通常需要保存起来,以便后续使用(如推理、继续训练或部署)。PyTorch提供了灵活的模型保存和加载机制。

8.1 仅保存和加载模型参数 (推荐)

这是最常见和推荐的方法。它只保存模型的state_dict(包含模型所有学习到的参数,如权重和偏置)。
优点:更灵活,可以在不同模型架构之间加载参数子集,也更小。
缺点:加载时需要先实例化模型架构。

“`python

保存模型参数

PATH = “my_model_state_dict.pth”
torch.save(model.state_dict(), PATH)
print(f”模型参数已保存到 {PATH}”)

加载模型参数

1. 首先需要重新实例化模型架构

loaded_model = MyClassifier(input_dim, hidden_dim, output_dim)
loaded_model.to(device) # 确保模型也在正确的设备上

2. 加载state_dict

loaded_model.load_state_dict(torch.load(PATH, map_location=device)) # map_location确保加载到正确设备
loaded_model.eval() # 加载后通常设置为评估模式

print(“模型参数已加载。”)

示例:使用加载的模型进行预测

dummy_input_eval = torch.randn(1, input_dim).to(device)
with torch.no_grad():
output_eval = loaded_model(dummy_input_eval)
print(“加载模型后的预测输出:”, output_eval)
“`

8.2 保存和加载整个模型

直接保存整个模型对象。
优点:方便,加载时无需重新实例化模型架构。
缺点:强依赖于模型定义的代码,如果代码结构改变,可能无法加载;文件大小通常更大。

“`python

保存整个模型

PATH_FULL = “my_model_full.pth”
torch.save(model, PATH_FULL)
print(f”整个模型已保存到 {PATH_FULL}”)

加载整个模型

注意:这需要模型定义的代码(MyClassifier)在当前环境中可用

loaded_full_model = torch.load(PATH_FULL, map_location=device)
loaded_full_model.eval()
print(“整个模型已加载。”)
``
对于初学者和大多数场景,推荐使用保存和加载
state_dict`的方式。

第九章:GPU加速

利用NVIDIA GPU进行并行计算是深度学习能够快速发展的重要原因之一。PyTorch对GPU支持非常好,只需几行代码就能将计算转移到GPU上。

“`python

检查CUDA是否可用

if torch.cuda.is_available():
print(“CUDA可用!”)
device = torch.device(“cuda”)
print(f”当前使用的GPU设备: {torch.cuda.get_device_name(0)}”) # 假设有多个GPU,使用第一个
else:
print(“CUDA不可用,将使用CPU。”)
device = torch.device(“cpu”)

将模型移动到GPU

model = MyClassifier(input_dim, hidden_dim, output_dim)
model.to(device)

将数据移动到GPU

x = torch.randn(64, 784)
x = x.to(device)

进行计算

output = model(x)
print(f”模型输出的设备: {output.device}”) # cuda:0 或 cpu
``
**重要提示:**
* **模型和数据必须在同一个设备上。** 如果模型在GPU上,那么输入数据也必须在GPU上,反之亦然。否则会报错。
* **输出结果也在同一个设备上。**
* 当你需要将GPU上的张量转换为NumPy数组或进行一些CPU特定的操作时,需要先使用
.cpu()将张量移回CPU,例如tensor.cpu().numpy()`。

总结与展望

恭喜你,已经完成了PyTorch基础知识的全面学习!我们从张量的操作开始,深入理解了自动求导的原理,学会了如何构建神经网络模型、选择合适的损失函数和优化器,掌握了数据加载的最佳实践,并实现了完整的模型训练循环和模型保存加载。最后,我们还探讨了GPU加速的关键技巧。

回顾重点:
* 张量 (Tensor): PyTorch的核心数据结构,多维数组,支持GPU和自动求导。
* 自动求导 (Autograd): 实现了反向传播,通过requires_grad.backward()自动计算梯度。
* nn.Module: 神经网络的基类,定义层和前向传播。
* 损失函数 (Loss Functions): 衡量模型预测与真实值差异。
* 优化器 (Optimizers): 根据梯度更新模型参数。
* Dataset & DataLoader: 高效处理和加载数据。
* 训练循环: 将所有组件整合,实现模型训练。
* 模型保存/加载: 推荐使用state_dict
* GPU加速: 通过.to(device)将模型和数据移至GPU。

接下来的学习之路:
1. 实践项目: 动手尝试构建和训练一个简单的图像分类(如MNIST)或文本分类模型。
2. 深入理解: 学习更复杂的层(如卷积层、循环层),更高级的优化器。
3. 数据预处理: 探索torchvision.transforms等工具进行数据增强和预处理。
4. 模型部署: 了解如何将训练好的模型部署到实际应用中。
5. 阅读官方文档: PyTorch官方文档是最好的学习资源,包含丰富的教程和API参考。

PyTorch的生态系统强大且活跃,掌握了这些基础知识,你就已经具备了进入深度学习世界并解决实际问题的能力。祝你在未来的AI学习和实践中取得丰硕的成果!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部