PyTorch 基础:快速了解深度学习框架 – wiki基地


PyTorch 基础:快速了解深度学习框架

引言:深度学习的浪潮与 PyTorch 的崛起

在当今人工智能飞速发展的时代,深度学习(Deep Learning)无疑是最受瞩目的技术之一。它在图像识别、自然语言处理、语音识别、推荐系统等众多领域取得了突破性进展,深刻地改变着我们的生活和工作方式。而支撑这些进步的基石,正是各种强大的深度学习框架。

TensorFlow、Keras、Caffe、MXNet 等框架曾是主流,但近年来,一个后起之秀——PyTorch,以其独特的优势和灵活的设计,迅速赢得了学术界和工业界的广泛青睐,成为了许多研究人员和工程师进行深度学习开发的首选工具。

PyTorch 由 Facebook(现 Meta)人工智能研究院(FAIR)开发并维护,它提供了一个强大的张量计算(Tensor computation)库,支持 GPU 加速,并且构建于一个基于 Autograd 系统的深度神经网络。其“Python First”的设计理念、动态计算图(Dynamic Computation Graph)特性以及简洁易用的 API,使得原型开发和实验变得异常高效和直观。

本文将带领大家快速入门 PyTorch,深入了解其最核心的基础概念:张量(Tensor)、自动微分(Autograd)以及构建神经网络的基本模块(nn.Module)。通过掌握这些基石,你将能够理解 PyTorch 的工作原理,并为后续更复杂的深度学习模型的学习和开发打下坚实的基础。

第一部分:张量(Tensor)——PyTorch 的数据基石

在深度学习中,所有的数据,无论是输入图像的像素值、文本数据的词向量、模型的权重和偏置,还是中间计算结果,都被表示为多维数组。在 PyTorch 中,这种多维数组就被称为张量(Tensor)。

张量在概念上类似于 NumPy 数组,但 PyTorch 的张量具备一个 NumPy 数组所不具备的关键能力:它们可以在 GPU 上运行,从而大幅提升计算速度,并且它们是构建自动微分系统的核心。

理解张量,是掌握 PyTorch 的第一步。

1. 张量的基本概念

  • 维度(Dimension/Rank): 张量的维度是其轴的数量。例如,一个标量(Scalar)是 0 维张量,一个向量(Vector)是 1 维张量,一个矩阵(Matrix)是 2 维张量。一个包含多张图片的集合可以表示为一个 4 维张量(数量 x 颜色通道 x 高 x 宽)。
  • 形状(Shape): 张量的形状是一个描述每个维度大小的元组。例如,一个 3×4 的矩阵的形状是 (3, 4)
  • 数据类型(Data Type): 张量存储的数据的类型,如浮点数 (torch.float32torch.float)、整数 (torch.int64torch.long)、布尔值 (torch.bool) 等。深度学习中最常用的是浮点数类型。
  • 设备(Device): 张量可以存储在 CPU 或 GPU 上。device 属性指示了张量所在的设备。

2. 创建张量

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

  • 从 Python 列表或 NumPy 数组创建:
    “`python
    import torch
    import numpy as np

    从列表创建

    data = [[1, 2], [3, 4]]
    x_data = torch.tensor(data)
    print(f”从列表创建的张量:\n{x_data}”)

    从 NumPy 数组创建

    np_array = np.array(data)
    x_np = torch.from_numpy(np_array)
    print(f”从 NumPy 数组创建的张量:\n{x_np}”)
    ``torch.tensor()是最常用的创建方法,它可以根据输入数据自动推断形状和数据类型。torch.from_numpy()` 可以将 NumPy 数组转换为 PyTorch 张量,并且它们共享内存,修改一个会影响另一个。

  • 创建具有特定属性的张量:
    “`python
    # 创建形状为 (2, 3) 的全 1 张量,指定数据类型
    x_ones = torch.ones(2, 3, dtype=torch.float32)
    print(f”全 1 张量:\n{x_ones}”)

    创建形状为 (2, 3) 的全 0 张量

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

    创建形状为 (2, 3) 的随机张量 (服从均匀分布)

    x_rand = torch.rand(2, 3)
    print(f”随机张量 (均匀分布):\n{x_rand}”)

    创建形状为 (2, 3) 的随机张量 (服从标准正态分布)

    x_randn = torch.randn(2, 3)
    print(f”随机张量 (正态分布):\n{x_randn}”)

    创建形状为 (2, 3) 的未初始化张量

    x_empty = torch.empty(2, 3)
    print(f”未初始化张量:\n{x_empty}”) # 内容是随机的,取决于内存状态

    创建形状和数据类型与现有张量相同的张量 (如全 1)

    x_like_ones = torch.ones_like(x_data)
    print(f”类似 x_data 的全 1 张量:\n{x_like_ones}”)
    “`

3. 张量的属性

创建张量后,可以通过其属性获取信息:

“`python
tensor = torch.rand(3, 4)

print(f”形状 (Shape): {tensor.shape}”)
print(f”数据类型 (Data type): {tensor.dtype}”)
print(f”设备 (Device): {tensor.device}”)
“`

4. 张量的操作

张量支持各种数学运算、索引、切片、变形等操作,这些操作与 NumPy 非常相似。

  • 数学运算:
    “`python
    x = torch.tensor([[1, 2], [3, 4]])
    y = torch.tensor([[5, 6], [7, 8]])

    逐元素相加

    z1 = x + y
    z2 = torch.add(x, y)
    print(f”逐元素相加:\n{z1}”)

    逐元素相乘

    z3 = x * y
    z4 = torch.mul(x, y)
    print(f”逐元素相乘:\n{z3}”)

    矩阵乘法

    要求 x 的列数等于 y 的行数

    matrix_mul = torch.matmul(x, y) # 或使用 @ 运算符: x @ y
    print(f”矩阵乘法:\n{matrix_mul}”)

    inplace 操作 (操作结果直接修改原张量,通常以 _ 结尾)

    print(f”修改前的 x:\n{x}”)
    x.add_(y) # x = x + y
    print(f”修改后的 x (inplace add):\n{x}”)
    “`

  • 索引和切片:
    “`python
    tensor = torch.ones(4, 4)
    print(f”原始张量:\n{tensor}”)

    获取第一行

    row_0 = tensor[0]
    print(f”第一行:\n{row_0}”)

    获取第一列

    col_0 = tensor[:, 0]
    print(f”第一列:\n{col_0}”)

    获取子区域 (前两行,后两列)

    sub_tensor = tensor[0:2, 2:4]
    print(f”子区域:\n{sub_tensor}”)

    修改元素 (inplace)

    tensor[0, 0] = 100
    print(f”修改后的张量:\n{tensor}”)
    “`

  • 改变形状(Reshaping):
    “`python
    tensor = torch.arange(12) # 创建一个包含 0-11 的 1维张量
    print(f”原始 1维张量:\n{tensor}”)

    改变形状为 3×4 的矩阵

    reshaped_tensor = tensor.view(3, 4) # view 要求数据在内存中是连续的

    或者使用 reshape (更灵活,不要求连续)

    reshaped_tensor = tensor.reshape(3, 4)

    print(f”改变形状为 3×4:\n{reshaped_tensor}”)

    改变形状为 4×3 的矩阵

    reshaped_tensor_2 = tensor.view(4, 3)
    print(f”改变形状为 4×3:\n{reshaped_tensor_2}”)

    展平 ( Flatten )

    flattened_tensor = reshaped_tensor.view(-1) # 使用 -1 让 PyTorch 自动推断维度大小
    print(f”展平后的张量:\n{flattened_tensor}”)

    增加维度 (如从 2D 矩阵变成 3D 张量)

    unsqueeze_tensor = reshaped_tensor.unsqueeze(0) # 在第 0 维增加一个维度
    print(f”增加维度:\n{unsqueeze_tensor}, Shape: {unsqueeze_tensor.shape}”)

    移除维度 (如从 3D 张量移除大小为 1 的维度)

    squeezed_tensor = unsqueeze_tensor.squeeze(0) # 移除第 0 维

    或者使用 squeeze() 移除所有大小为 1 的维度

    squeezed_tensor = unsqueeze_tensor.squeeze()

    print(f”移除维度:\n{squeezed_tensor}, Shape: {squeezed_tensor.shape}”)
    “`

  • 连接(Concatenation):
    “`python
    tensor1 = torch.ones(2, 3)
    tensor2 = torch.zeros(2, 3)

    按行连接 (dim=0)

    concat_rows = torch.cat([tensor1, tensor2], dim=0)
    print(f”按行连接:\n{concat_rows}, Shape: {concat_rows.shape}”)

    按列连接 (dim=1)

    concat_cols = torch.cat([tensor1, tensor2], dim=1)
    print(f”按列连接:\n{concat_cols}, Shape: {concat_cols.shape}”)
    “`

5. CPU 与 GPU

PyTorch 张量可以在 CPU 或 GPU 上进行计算。将张量移动到 GPU 可以显著加速深度学习模型的训练过程。

首先,检查是否有可用的 GPU:
python
if torch.cuda.is_available():
device = torch.device("cuda")
print("CUDA is available! Using GPU.")
else:
device = torch.device("cpu")
print("CUDA not available. Using CPU.")

将张量移动到指定设备:
“`python
tensor = torch.rand(3, 4)

将张量移动到 GPU (如果可用) 或 CPU

tensor_on_device = tensor.to(device)
print(f”张量所在的设备: {tensor_on_device.device}”)

将模型的所有参数移动到 GPU

model.to(device)

“`
注意: 只有位于同一设备上的张量才能进行相互操作。如果一个张量在 CPU 上,另一个在 GPU 上,直接进行数学运算会导致错误,需要先将它们移到同一设备上。

6. 张量与 NumPy 的转换

PyTorch 张量和 NumPy 数组之间可以方便地相互转换:
“`python

PyTorch 张量转 NumPy 数组

tensor = torch.ones(5)
numpy_array = tensor.numpy()
print(f”PyTorch 张量转 NumPy 数组:\n{numpy_array}, Type: {type(numpy_array)}”)

NumPy 数组转 PyTorch 张量

numpy_array = np.array([1, 2, 3, 4, 5])
tensor_from_numpy = torch.from_numpy(numpy_array)
print(f”NumPy 数组转 PyTorch 张量:\n{tensor_from_numpy}, Type: {type(tensor_from_numpy)}”)
“`
注意: CPU 上的张量和 NumPy 数组共享底层内存位置。修改其中一个,另一个也会改变。GPU 上的张量则不共享内存,转换时会进行数据拷贝。

第二部分:自动微分(Autograd)——PyTorch 的核心魔法

深度学习模型的训练过程本质上是一个优化问题,目标是找到一组模型参数,使得模型在训练数据上的损失函数达到最小值。这个优化过程通常依赖于梯度下降及其变种算法,而这些算法的核心是计算损失函数关于模型参数的梯度。

手动计算复杂模型的梯度既繁琐又容易出错。PyTorch 的 Autograd 系统正是为了解决这个问题而设计的,它能够自动计算任何张量操作的梯度。这是 PyTorch 作为深度学习框架最强大和最重要的特性之一。

1. Autograd 的基本原理

Autograd 的核心是构建一个计算图(Computation Graph)。当你对张量进行操作时,PyTorch 会在后台记录下这些操作,形成一个有向无环图(DAG,Directed Acyclic Graph)。图的节点是张量,边是操作函数。

当调用张量的 .backward() 方法时,Autograd 会沿着这个计算图从输出张量(通常是损失函数)开始,反向遍历图中的边,利用链式法则(Chain Rule)计算出所有需要梯度的叶子节点张量(通常是模型的参数)的梯度。

2. requires_grad 属性

并非所有张量都需要计算梯度。例如,模型的输入数据和标签就不需要计算梯度。只有那些需要在训练过程中通过梯度下降进行更新的参数(如权重和偏置)才需要计算梯度。

通过设置张量的 requires_grad=True 属性,可以告诉 PyTorch 需要跟踪这个张量的计算历史并计算其梯度。默认情况下,张量的 requires_gradFalse

“`python

创建一个需要梯度的张量 (例如,模拟模型参数)

x = torch.tensor(2.0, requires_grad=True)
print(f”x requires_grad: {x.requires_grad}”)

创建一个不需要梯度的张量 (例如,模拟输入数据)

y = torch.tensor(3.0)
print(f”y requires_grad: {y.requires_grad}”)

对需要梯度的张量进行操作,结果张量默认也需要梯度

z = x * 2
print(f”z (x*2) requires_grad: {z.requires_grad}”) # True

对不需要梯度的张量进行操作,结果张量不需要梯度

w = y * 2
print(f”w (y*2) requires_grad: {w.requires_grad}”) # False

混合操作:如果操作涉及至少一个 requires_grad=True 的张量,结果张量也 requires_grad=True

v = x + y
print(f”v (x+y) requires_grad: {v.requires_grad}”) # True
“`

3. 计算梯度 (.backward())

当计算图构建完成后,可以通过调用作为计算图最终输出的张量(通常是损失张量)的 .backward() 方法来启动反向传播,计算梯度。

“`python
x = torch.tensor(2.0, requires_grad=True)
y = x*2 + 3x + 1 # 构建计算图: y = f(x)

计算 y 关于 x 的梯度

y.backward() # 如果 y 是一个标量,直接调用 backward()

print(f”梯度 dy/dx: {x.grad}”) # x.grad 会存储梯度值

``
在这个例子中,
y = x2 + 3x + 1。对x求导得到dy/dx = 2x + 3。当x=2时,dy/dx = 2*2 + 3 = 7`。

“`python

实际运行

x = torch.tensor(2.0, requires_grad=True)
y = x*2 + 3x + 1
y.backward() # 计算梯度
print(f”梯度 dy/dx @ x=2: {x.grad}”) # 输出 tensor(7.)
“`

注意:
* backward() 方法通常只用于标量输出(损失函数通常是一个标量)。如果输出是一个非标量张量,需要为其指定一个 gradient 参数,表示需要计算其关于输入的雅可比矩阵的向量-积(Jacobian-vector product)。对于大多数深度学习场景,我们关注的是损失函数关于参数的梯度,损失函数是标量,所以直接调用 .backward() 即可。
* 梯度会累加到叶子节点的 .grad 属性上。因此,在每次反向传播之前,需要清零之前的梯度,以避免梯度累积带来的错误。这通常通过 optimizer.zero_grad()tensor.grad.zero_() 来实现。

4. 梯度清零

在典型的训练循环中,我们需要在每次迭代开始时清零梯度,然后在计算损失后进行反向传播计算新的梯度,最后使用优化器更新参数。

“`python

假设有一个简单的线性模型 y = wx + b

w = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(0.5, requires_grad=True)
x_data = torch.tensor(2.0)
target = torch.tensor(5.0)

训练迭代 1

前向传播

y_pred = w * x_data + b # y_pred = 1.0 * 2.0 + 0.5 = 2.5

计算损失 (MSE)

loss = (y_pred – target)2 # loss = (2.5 – 5.0)2 = (-2.5)**2 = 6.25

print(f”迭代 1 前的梯度 w.grad: {w.grad}, b.grad: {b.grad}”) # None 或上次的值

反向传播

loss.backward()
print(f”迭代 1 后的梯度 w.grad: {w.grad}, b.grad: {b.grad}”)

loss = (wx + b – target)^2

d(loss)/dw = 2 * (wx + b – target) * x

d(loss)/db = 2 * (wx + b – target) * 1

@ w=1, b=0.5, x=2, target=5:

d(loss)/dw = 2 * (1*2 + 0.5 – 5) * 2 = 2 * (2.5 – 5) * 2 = 2 * (-2.5) * 2 = -10

d(loss)/db = 2 * (1*2 + 0.5 – 5) * 1 = 2 * (-2.5) * 1 = -5

清零梯度 (在下一次迭代前必须执行)

w.grad.zero_()
b.grad.zero_()
print(f”清零后的梯度 w.grad: {w.grad}, b.grad: {b.grad}”)

训练迭代 2 (省略参数更新部分,只展示梯度清零的必要性)

前向传播 (假设 w, b 保持不变,实际训练中会更新)

y_pred = w * x_data + b # 仍然 2.5
loss = (y_pred – target)**2 # 仍然 6.25

反向传播

loss.backward() # 如果不清零,这里的梯度会累加到 w.grad 和 b.grad 上
print(f”迭代 2 (未清零前) 后的梯度 w.grad: {w.grad}, b.grad: {b.grad}”)

如果不清零,w.grad 会变成 -10 + (-10) = -20,b.grad 会变成 -5 + (-5) = -10

因为我们清零了,所以现在 w.grad 和 b.grad 应该再次是 -10 和 -5

“`

5. 停止梯度跟踪

在某些情况下,你可能希望停止 Autograd 跟踪计算历史,例如:
* 在模型评估或推理阶段,不需要计算梯度,这可以提高性能和减少内存消耗。
* 在训练循环中,有时需要对张量进行操作,但不希望这些操作被记录在计算图中(例如,计算损失函数的值用于监控)。

有两种主要方式可以停止梯度跟踪:

  • torch.no_grad() 上下文管理器:with torch.no_grad(): 块中执行的操作,其结果张量将不具有 requires_grad=True,即使输入张量具有此属性。
    python
    x = torch.tensor(2.0, requires_grad=True)
    with torch.no_grad():
    y = x * 2
    print(f"y (在 no_grad 中): {y}, requires_grad: {y.requires_grad}") # False

  • .detach() 方法: 从计算图中分离出一个张量,返回一个新的张量,该张量与原张量共享数据,但不再是计算图的一部分,因此不需要梯度。
    python
    x = torch.tensor(2.0, requires_grad=True)
    y = x * 2
    z = y.detach()
    print(f"z (从 y detach): {z}, requires_grad: {z.requires_grad}") # False
    print(f"y requires_grad: {y.requires_grad}") # y 仍然是 True

    detach() 常用于需要将计算结果作为 NumPy 数组使用(因为 NumPy 数组不支持梯度)或需要将结果用于不希望被记录在计算图中的后续操作。

理解并熟练使用 Autograd 是进行深度学习训练的关键。它让 PyTorch 能够自动完成复杂的梯度计算,使开发者能够专注于模型的设计和实验。

第三部分:构建神经网络——使用 nn.Module

在 PyTorch 中,构建神经网络的核心是 torch.nn 模块。它提供了各种预定义的神经网络层(如线性层、卷积层、激活函数等)、损失函数以及一个基础类 nn.Module,用于构建更复杂的模型。

1. nn.Module 的作用

nn.Module 是所有神经网络模块的基类。一个模块可以包含其他模块(构成嵌套结构),也可以包含模型参数(张量,如权重和偏置)。

继承自 nn.Module 的好处包括:
* 参数管理: 它会自动跟踪模块中定义的所有 nn.Parameter 或其他 nn.Module 中的参数,方便通过 .parameters().named_parameters() 方法访问。
* GPU 支持: 可以方便地使用 .to(device) 方法将整个模块及其所有参数移动到 GPU 或其他设备上。
* 训练/评估模式: 可以使用 .train().eval() 方法来设置模块的模式,这会影响一些特殊层(如 Dropout 和 BatchNorm)的行为。
* 保存/加载: 提供了方便的方法来保存和加载模块的状态字典 (.state_dict(), .load_state_dict())。

2. 构建一个简单的神经网络

构建一个神经网络通常涉及以下几个步骤:

  • 继承 nn.Module 基类。
  • __init__ 方法中定义网络的层或其他子模块。这些层通常是 nn.Linear, nn.Conv2d, nn.ReLU, nn.MaxPool2dnn.Module 的实例。注意: 参数层(如 nn.Linear)的权重和偏置都是 requires_grad=True 的张量,会自动被 nn.Module 跟踪。
  • forward 方法中定义数据在网络中的前向传播路径。这是模型的核心逻辑,描述了输入数据如何依次经过各个层得到输出。

让我们构建一个简单的多层感知机(MLP):

“`python
import torch
import torch.nn as nn
import torch.nn.functional as F # 常用的激活函数、池化等操作也可以在这里找到

定义设备

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)

定义一个简单的 MLP 类

class SimpleMLP(nn.Module):
def init(self, input_size, hidden_size, output_size):
super(SimpleMLP, self).init() # 调用父类的构造函数
# 定义线性层
self.fc1 = nn.Linear(input_size, hidden_size) # 全连接层 1: input_size -> hidden_size
self.relu = nn.ReLU() # ReLU 激活函数
self.fc2 = nn.Linear(hidden_size, output_size) # 全连接层 2: hidden_size -> output_size

def forward(self, x):
    # 定义前向传播过程
    out = self.fc1(x)      # 数据通过第一个全连接层
    out = self.relu(out)   # 应用 ReLU 激活
    out = self.fc2(out)      # 数据通过第二个全连接层
    return out

创建模型实例

input_dim = 784 # 例如 MNIST 图像展平后的维度
hidden_dim = 128
output_dim = 10 # 例如 10 个类别

model = SimpleMLP(input_dim, hidden_dim, output_dim)

将模型移动到设备

model.to(device)

print(“模型结构:”)
print(model)

检查模型参数 (参数会自动注册)

print(“\n模型参数:”)
for name, param in model.named_parameters():
if param.requires_grad: # 只打印需要梯度的参数
print(f”参数名: {name}, 形状: {param.shape}”)

模拟一个输入张量 (批量大小为 64, 输入维度 784)

dummy_input = torch.randn(64, input_dim).to(device)

进行一次前向传播

output = model(dummy_input)
print(f”\n输入形状: {dummy_input.shape}”)
print(f”输出形状: {output.shape}”)
“`

在这个例子中:
* __init__ 中定义了两个线性层 fc1fc2,以及一个 ReLU 激活函数。
* forward 方法接收输入张量 x,依次通过 fc1relufc2 层,最后返回输出。
* 当我们调用 model(dummy_input) 时,实际上是调用了 model.forward(dummy_input) 方法。

3. 常用的层和函数

torch.nn 模块提供了大量的预定义层和函数:

  • 线性层: nn.Linear(in_features, out_features)
  • 卷积层: nn.Conv1d, nn.Conv2d, nn.Conv3d
  • 池化层: nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d, nn.AvgPool2d
  • 激活函数: nn.ReLU, nn.Sigmoid, nn.Tanh, nn.LeakyReLU, nn.Softmax (也可以在 torch.nn.functional 中找到对应的函数版本,如 F.relu)
  • 归一化层: nn.BatchNorm1d, nn.BatchNorm2d
  • Dropout 层: nn.Dropout (用于防止过拟合)
  • 循环神经网络层: nn.RNN, nn.LSTM, nn.GRU
  • 嵌入层: nn.Embedding (用于将离散数据如词索引映射到连续向量)

这些层都是 nn.Module 的子类,可以像乐高积木一样组合起来构建复杂的网络结构。

第四部分:损失函数(Loss Functions)和优化器(Optimizers)

1. 损失函数(Loss Functions)

损失函数衡量了模型预测输出与真实标签之间的差距。在训练过程中,我们希望最小化这个损失。torch.nn 模块也提供了多种常用的损失函数。

一些常见的损失函数:
* 均方误差(MSE)损失: nn.MSELoss() – 用于回归问题,计算预测值和目标值之间差的平方的平均值。
* 交叉熵损失(Cross-Entropy Loss): nn.CrossEntropyLoss() – 常用于多类别分类问题。它结合了 LogSoftmax 和 NLLLoss(负对数似然损失),通常用于模型的输出是原始分数(logits)而非概率的情况。
* 二元交叉熵损失(Binary Cross-Entropy Loss): nn.BCELoss()nn.BCEWithLogitsLoss() – 用于二元分类问题。BCEWithLogitsLoss 更稳定,并且将 Sigmoid 层集成在了损失函数中。

使用损失函数:
“`python

创建一个损失函数实例

loss_fn = nn.CrossEntropyLoss() # 用于分类问题

模拟模型的预测输出 (logits) 和真实标签

predictions = torch.randn(10, 5, requires_grad=True) # 批量大小 10, 5个类别
targets = torch.empty(10, dtype=torch.long).random_(5) # 10个样本的类别索引 (0-4)

计算损失值

loss = loss_fn(predictions, targets)
print(f”计算的损失值: {loss.item()}”) # 使用 .item() 获取标量张量的 Python 数值
“`
注意: 大多数 PyTorch 损失函数期望模型的原始输出(如线性层的输出)作为输入,而不是经过 Softmax 或 Sigmoid 转换后的概率。查阅文档以确认具体损失函数的输入要求。

2. 优化器(Optimizers)

优化器的作用是根据计算得到的梯度来更新模型参数,以使损失函数最小化。torch.optim 模块提供了各种优化算法。

一些常见的优化器:
* 随机梯度下降(SGD): torch.optim.SGD(params, lr=...) – 最基本的优化器,通过学习率控制更新步长。可以添加动量(momentum)。
* Adam: torch.optim.Adam(params, lr=...) – 一种自适应学习率优化算法,通常在实践中表现良好。
* Adagrad, RMSprop, AdamW 等: 其他各种优化算法。

创建优化器:
“`python

假设 model 是前面定义的 SimpleMLP 实例

创建 SGD 优化器,需要传入模型参数和学习率

model.parameters() 会返回一个迭代器,包含模型中所有 requires_grad=True 的参数

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

创建 Adam 优化器

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

“`

优化器的工作流程:
在每个训练迭代中:
1. 清零梯度: 调用 optimizer.zero_grad() 清除之前计算的梯度。
2. 前向传播: 将输入数据通过模型得到预测输出。
3. 计算损失: 使用损失函数计算预测输出和真实标签之间的损失。
4. 反向传播: 调用 loss.backward() 计算损失关于模型所有 requires_grad=True 参数的梯度。这些梯度会存储在对应参数的 .grad 属性中。
5. 参数更新: 调用 optimizer.step() 根据存储在 .grad 中的梯度和优化器的算法更新模型参数。

第五部分:将所有组件整合:一个简单的训练示例

现在我们已经了解了 PyTorch 的核心组件:张量、Autograd、nn.Module、损失函数和优化器。是时候将它们组合起来,看看如何训练一个简单的模型了。

我们将使用一个简单的线性回归问题作为例子:找到最佳的 wb,使得 y = w*x + b 能够最好地拟合给定的数据。

“`python
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt # 用于可视化

1. 准备数据

生成一些线性关系的数据 y = 2*x + 1 + 噪声

torch.manual_seed(42) # 设置随机种子,以便结果可复现
X_train = torch.randn(100, 1) * 10 # 100个样本,特征维度 1
y_train = 2 * X_train + 1 + torch.randn(100, 1) * 2 # 加上一些噪声

2. 定义模型

使用 nn.Linear 构建一个简单的线性模型

class LinearRegressionModel(nn.Module):
def init(self):
super(LinearRegressionModel, self).init()
self.linear = nn.Linear(1, 1) # 输入维度 1, 输出维度 1

def forward(self, x):
    return self.linear(x)

model = LinearRegressionModel()

将模型移动到设备 (如果可用 GPU 会快很多,但这里数据量小影响不大)

device = torch.device(“cuda” if torch.cuda.is_available() else “cpu”)
model.to(device)
X_train = X_train.to(device)
y_train = y_train.to(device)

print(“初始模型参数:”)
for name, param in model.named_parameters():
print(f”{name}: {param.data}”)

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

criterion = nn.MSELoss() # 均方误差损失,适合回归问题
optimizer = optim.SGD(model.parameters(), lr=0.01) # SGD 优化器,学习率 0.01

4. 训练模型

num_epochs = 100 # 训练迭代次数

print(“\n开始训练…”)
for epoch in range(num_epochs):
# 前向传播
outputs = model(X_train)

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

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

# 打印训练信息
if (epoch + 1) % 10 == 0:
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

print(“\n训练结束.”)
print(“最终模型参数:”)
for name, param in model.named_parameters():
print(f”{name}: {param.data}”)

5. 结果可视化

将数据移回 CPU 进行可视化

predicted = model(X_train).detach().cpu().numpy() # 使用 detach() 停止梯度跟踪并转为 NumPy
X_train_np = X_train.cpu().numpy()
y_train_np = y_train.cpu().numpy()

plt.plot(X_train_np, y_train_np, ‘bo’, label=’Original data’, markersize=5)
plt.plot(X_train_np, predicted, ‘r-‘, label=’Fitted line’)
plt.legend()
plt.xlabel(‘X’)
plt.ylabel(‘y’)
plt.title(‘Linear Regression with PyTorch’)
plt.grid(True)
plt.show()
“`
这个示例代码展示了一个完整的 PyTorch 训练流程:数据准备、模型定义、损失函数和优化器选择、以及最重要的训练循环。训练循环中的四个核心步骤——清零梯度、前向传播、计算损失、反向传播和更新参数——是所有 PyTorch 模型训练的基础模式。

第六部分:PyTorch 的特点与优势回顾

通过前面的学习,我们已经接触到了 PyTorch 的几个核心优势:

  1. Pythonic 设计: PyTorch 的 API 设计非常符合 Python 的习惯,易于学习和使用。
  2. 动态计算图: PyTorch 使用动态计算图,这意味着计算图是在前向传播过程中实时构建的。这使得调试更加容易(可以直接使用 Python 的 pdb 调试器),并且可以处理变长的输入序列等复杂情况。这与 TensorFlow 1.x 的静态图形成鲜明对比(尽管 TensorFlow 2.x 也引入了动态图)。
  3. 易于调试: 由于动态图特性,当出现错误时,可以直接在运行过程中定位到出错的代码行。
  4. 强大的社区和生态: PyTorch 拥有一个活跃的社区,提供了丰富的教程、文档和第三方库(如 torchvision, torchaudio, torchtext, transformers 等),覆盖了计算机视觉、自然语言处理、音频处理等多个领域。
  5. 研究友好: 其灵活性和易用性使得 PyTorch 在学术研究领域非常受欢迎,许多最新的研究成果都首先在 PyTorch 中实现。

第七部分:超越基础:接下来可以学习什么?

掌握了张量、Autograd 和 nn.Module 这些基础知识后,你已经具备了继续深入学习 PyTorch 的能力。接下来可以探索的主题包括:

  • 数据加载: 使用 torch.utils.data.DatasetDataLoader 高效地加载和批量处理数据。
  • 更多层类型: 学习更复杂的网络层,如卷积层、池化层、循环层、Transformer 层等,以及如何将它们组合起来构建现代深度学习模型。
  • 模型保存与加载: 学习如何保存模型的参数或整个模型,以及如何在需要时重新加载它们。
  • GPU 加速: 更深入地理解如何在 GPU 上高效地进行训练。
  • 训练技巧: 学习学习率调度、正则化(Dropout, Weight Decay)、梯度裁剪、优化器选择等训练过程中的常用技巧。
  • 可视化: 使用 TensorBoard 或 matplotlib 等工具可视化训练过程、模型结构和结果。
  • 预训练模型与迁移学习: 学习如何利用他人训练好的大型模型进行迁移学习。
  • 分布式训练: 学习如何在多台机器或多个 GPU 上训练模型以加速。
  • 部署: 了解如何将训练好的 PyTorch 模型部署到服务器、移动设备或边缘设备上(如使用 TorchScript, ONNX, PyTorch Mobile)。
  • 特定领域库: 深入学习 torchvision, torchtext, torchaudio 等 PyTorch 官方库,或 Hugging Face 的 transformers 等第三方库。

结论

PyTorch 作为一个强大、灵活且易于使用的深度学习框架,为研究人员和开发者提供了构建、训练和部署深度学习模型的坚实基础。本文详细介绍了 PyTorch 的核心概念:张量是数据的载体,Autograd 是梯度计算的魔法,nn.Module 是构建网络结构的基石,而损失函数和优化器则是驱动模型学习的关键。

通过一个简单的线性回归示例,我们展示了这些组件如何在实际训练流程中协同工作。掌握这些基础知识,你已经迈出了使用 PyTorch 进行深度学习实践的第一步。

深度学习是一个充满活力和快速发展的领域,持续学习和实践是掌握它的关键。鼓励你在 PyTorch 官方文档、教程以及各种在线资源中继续探索,动手实践,构建自己的模型,解决实际问题。

祝你在 PyTorch 的世界里学习顺利,取得丰硕成果!

发表评论

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

滚动至顶部