深度学习 PyTorch:张量、自动求导与模块
什么是 PyTorch
PyTorch 是一个基于 Python 的深度学习框架,提供了强大的 GPU 加速张量计算和自动求导系统。它的设计理念是灵活、直观、易于调试,非常适合研究和快速原型开发。本教程将带你从零掌握三个核心概念:张量、自动求导 与 模块,为你搭建现代神经网络打下坚实基础。
安装与环境配置
推荐使用 PyTorch 官方命令安装,可自动匹配 CUDA 版本:
# CPU 版本
pip install torch torchvision torchaudio
# CUDA 11.8 版本(示例)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
安装后验证:
import torch
print(torch.__version__)
print(torch.cuda.is_available()) # 检查 GPU 是否可用
张量:深度学习的数据基石
张量(Tensor)是 PyTorch 中最基本的数据结构,你可以把它理解为多维数组,类似 NumPy 的 ndarray,但额外支持 GPU 加速和自动求导。
创建张量
import torch
# 从列表创建
a = torch.tensor([1, 2, 3])
print(a) # tensor([1, 2, 3])
# 全0、全1、未初始化
zeros = torch.zeros(2, 3) # 2行3列
ones = torch.ones(3, 4)
empty = torch.empty(2, 2) # 未初始化,值随机
# 从 NumPy 转换(共享内存)
import numpy as np
arr = np.array([4, 5, 6])
b = torch.from_numpy(arr)
# 生成特定范围的张量
c = torch.arange(0, 10, 2) # tensor([0, 2, 4, 6, 8])
d = torch.linspace(0, 1, 5) # tensor([0.0000, 0.2500, 0.5000, 0.7500, 1.0000])
张量属性
x = torch.randn(2, 3, 4) # 随机张量,形状 (2,3,4)
print(x.shape) # torch.Size([2, 3, 4])
print(x.dtype) # torch.float32
print(x.device) # cpu
print(x.ndim) # 3(维度数)
基本运算
张量支持与 NumPy 相似的向量化运算,且大多数操作都在 GPU 上无缝运行。
x = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([4.0, 5.0, 6.0])
# 加减乘除
print(x + y) # tensor([5., 7., 9.])
print(x * y) # 逐元素乘
print(x @ y) # 点积(内积),等价于 torch.dot(x, y)
# 矩阵乘法
A = torch.randn(2, 3)
B = torch.randn(3, 4)
C = torch.mm(A, B) # 形状 (2,4)
# 或使用 @ 运算符
C = A @ B
# 变形操作
t = torch.arange(12).reshape(3, 4) # 改为3行4列
print(t.view(4, 3)) # view 与 reshape 类似,但需内存连续
print(t.transpose(0, 1)) # 转置
print(t.squeeze()) # 移除尺寸为1的维度
print(t.unsqueeze(0)) # 在第0维插入一个维度
GPU 张量
将张量转移到 GPU 非常简单:
if torch.cuda.is_available():
device = torch.device("cuda")
x = torch.tensor([1.0, 2.0]).to(device)
y = torch.randn(2, 2, device=device)
z = x + y # 运算在GPU上执行
张量是 PyTorch 的通行证,后续所有神经网络中的输入、权重、输出全部以张量形式存在。
自动求导:省去手算梯度的烦恼
PyTorch 的自动求导系统(Autograd)能够自动计算任意张量操作的梯度,这是训练神经网络的核心机制。只要将张量的 requires_grad 属性设为 True,PyTorch 就会追踪该张量上的所有操作,并根据链式法则自动计算梯度。
一个简单的例子
x = torch.tensor(2.0, requires_grad=True)
y = x ** 3 + 5 * x + 2
print(y) # tensor(20., grad_fn=<AddBackward0>)
观察输出中的 grad_fn,它记录了该张量是通过什么运算得到的。调用反向传播:
y.backward() # 计算 dy/dx
print(x.grad) # tensor(17.) 因为 3*x^2 + 5 = 3*4+5=17
多变量与向量梯度
对于多变量函数,通常会对一个标量损失执行 backward(),然后查看每个参数的梯度。
w = torch.tensor([1.0, 2.0], requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)
# 模拟一个简单线性输出
x = torch.tensor([2.0, 3.0])
y = torch.dot(w, x) + b # 点积 + 偏置
y.backward()
print(w.grad) # tensor([2., 3.]) dy/dw = x
print(b.grad) # tensor(1.) dy/db = 1
如果 y 不是标量,需要传入一个与 y 形状相同的梯度参数:
v = torch.tensor([1.0, 0.1], requires_grad=True)
u = v ** 2 # u = [v1^2, v2^2]
u.backward(torch.tensor([1.0, 0.5]))
print(v.grad) # [2*v1*1.0, 2*v2*0.5]
动态计算图与 torch.no_grad()
PyTorch 的计算图是动态的,每次前向都会重新构建,这使得模型可以灵活控制流(如 if 语句、循环)。在评估或推理时,我们不需要计算梯度,可以使用 torch.no_grad() 减少内存消耗并加速计算:
x = torch.randn(100, 10, requires_grad=True)
with torch.no_grad():
y = x @ x.T # 此操作不会被追踪
梯度清零
训练循环中每次反向传播前,必须手动清空梯度,否则梯度会累加:
optimizer.zero_grad() # 常用在优化器中
# 或直接
if w.grad is not None:
w.grad.zero_()
nn.Module:构建神经网络的标准方式
PyTorch 提供了 torch.nn 模块来搭建神经网络。其中 nn.Module 是所有网络层的基类,通过继承并实现 __init__ 与 forward 方法,可以构建任意复杂的模型。
定义一个简单网络
import torch.nn as nn
import torch.nn.functional as F
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size) # 全连接层
self.fc2 = nn.Linear(hidden_size, output_size)
def forward(self, x):
x = F.relu(self.fc1(x)) # 激活函数
x = self.fc2(x)
return x
模型实例化后,可以像函数一样调用,自动执行 forward 方法:
model = SimpleNet(10, 20, 1)
x = torch.randn(5, 10) # 5个样本,每个维度10
output = model(x)
print(output.shape) # torch.Size([5, 1])
常用网络层一览
| 层类型 | 说明 | 示例 |
|---|---|---|
nn.Linear |
全连接层 | nn.Linear(in, out) |
nn.Conv2d |
二维卷积 | nn.Conv2d(in_c, out_c, k) |
nn.LSTM |
长短时记忆网络 | nn.LSTM(input, hidden) |
nn.BatchNorm2d |
批归一化 | nn.BatchNorm2d(num_features) |
nn.Dropout |
随机失活 | nn.Dropout(p=0.5) |
nn.ReLU / nn.Sigmoid |
激活函数 | nn.ReLU() |
训练循环的标准写法
下面是一个极简训练循环,展示了模型、损失函数、优化器如何协作:
# 假数据
X = torch.randn(100, 10)
y = torch.randn(100, 1)
model = SimpleNet(10, 20, 1)
criterion = nn.MSELoss() # 均方误差损失
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
epochs = 50
for epoch in range(epochs):
# 前向
pred = model(X)
loss = criterion(pred, y)
# 反向传播
optimizer.zero_grad() # 清零梯度
loss.backward() # 计算梯度
optimizer.step() # 更新参数
if (epoch+1) % 10 == 0:
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
参数管理与模型保存
model.parameters() 返回所有可学习参数,可以直接用于优化器。模型的保存和加载非常简单:
# 保存
torch.save(model.state_dict(), 'model.pth')
# 加载
model = SimpleNet(10, 20, 1) # 同样结构的模型
model.load_state_dict(torch.load('model.pth'))
model.eval() # 切换到评估模式(影响 dropout/bn 等)
综合示例:线性回归从零实现
让我们用一个完整例子巩固张量、自动求导和模块的知识。使用 nn.Module 拟合一个线性函数。
import torch
import torch.nn as nn
import torch.optim as optim
# 生成数据 y = 3x + 2 + 噪声
torch.manual_seed(42)
X = torch.randn(200, 1) * 10
true_w = 3.0
true_b = 2.0
y = true_w * X + true_b + torch.randn(200, 1) * 2
# 定义模型
class LinearReg(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(1, 1) # 输入1维,输出1维
def forward(self, x):
return self.linear(x)
model = LinearReg()
loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.001)
# 训练
for epoch in range(100):
model.train()
pred = model(X)
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (epoch+1) % 20 == 0:
print(f"Epoch {epoch+1}: loss = {loss.item():.4f}")
# 打印学习到的参数
w, b = model.linear.weight.item(), model.linear.bias.item()
print(f"Learned: w = {w:.3f}, b = {b:.3f}")
print(f"True: w = {true_w}, b = {true_b}")
运行后可以看到模型成功逼近了真实参数。这段代码完美融合了 张量生成、自动求导 和 模块搭建 三大知识点。
下一步学习建议
- 深入阅读 PyTorch 官方文档的
torch.Tensor、torch.autograd、torch.nn部分。 - 尝试构建更复杂的模型(如 CNN、RNN),并应用到图像或文本数据上。
- 学习
torch.utils.data.Dataset和DataLoader实现高效数据加载。 - 探索 PyTorch Lightning 等高级接口,让训练流程更整洁。
掌握张量、自动求导与模块这三柄利器,你已经敲开了深度学习的大门。接下来,就是在真实数据集上不断实践、调优,让模型为你所用。