很多时候我们理解了深度学习的基本概念与原理,但阅读相关代码时候还是一头雾水,主要是因为往往是因为代码中隐藏了大量框架特有的设计模式和工程化操作,简单总结一下 Pytorch 中常见操作,帮助读者轻松阅读深度学习代码
张量 Tensor
张量是 PyTorch 中最基础的数据结构,类似于 NumPy 的多维数组,但具备支持深度学习任务的特殊功能,如 GPU 加速和自动求导等
本质上张量是标量、向量、矩阵的高维拓展,能够表示任意维度的数据
- 0 维张量(标量):如
tensor(5)
,表示单个数值 - 1 维张量(向量):如
tensor([1,2,3])
,表示一维数组 - 2 维张量(矩阵):如
tensor([[1,2],[3,4]])
,表示二维表格 - 更高维张量:如 3D 张量(图像数据)、4D 张量(视频或批量图像)等
定义张量
python
a = torch.tensor([1, 2, 3]) # 1D 张量
b = torch.tensor([[1,2], [3,4]]) # 2D 张量
c = torch.zeros(2, 3) # 全 0 张量
d = torch.ones(3) # 全 1 张量
e = torch.from_numpy(numpy_array) # 将 NumPy 数组转为张量
f = torch.rand(2, 2) # 均匀分布随机数
g = torch.randn(3, 3) # 标准正态分布随机数
h = torch.tensor([1,2], device="cuda") # 在GPU上创建张量
CUDA(Compute Unified Device Architecture)是 NVIDIA 开发的并行计算平台和编程模型,旨在利用 GPU 的并行计算能力加速通用计算任务
张量属性
python
print(a.shape) # 张量形状[行数, 列数]
print(b.dtype) # 数据类型
# 将张量移动到 GPU
device = torch.device("cuda")
c = c.to(device)
基本操作
python
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
y = torch.tensor([[7, 8, 9], [10, 11, 12]])
z = x + y
print(x + y) # tensor([[8, 10, 12],[14, 16, 18]])
print(x * y) # tensor([[7, 16, 27],[40, 55, 72]])
concatenated = torch.cat((x, y), dim=0) # 沿第 0 维拼接
print(concatenated)
"""
tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
"""
print(x[0, 1]) # tensor(2)
print(x[:, 1]) # tensor([2, 5])
# [0,2) 行,[0, 1) 列
print(x[0:2, 0:1]) # tensor([[1], [4]])
# 转置
print(x.t()) # tensor([[1, 4], [2, 5], [3, 6]])
自动求导
梯度是一个向量,表示多元函数在某一点处所有偏导数构成的向量。
自动求导机制是 PyTorch 的核心特性之一,它可以自动计算张量的梯度,这对于训练神经网络非常重要
python
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # tensor(7.)
- 当张量 x 设置 requires_grad=True 时,PyTorch 会跟踪其上的所有操作,构建计算图。
- 计算梯度时,数学上 y = x² + 3x + 1 的导数是 dy/dx = 2x +3。
- 代入 x=2,结果应该是 2*2+3 = 7,所以 x.grad 的值是 7。
神经网络模块
PyTorch 提供了 nn.Module
类,用于构建神经网络模型,所有神经网络组件(层、激活函数、损失函数等)均继承自 nn.Module
自定义模型
通过继承 nn.Module 可以定义自己的模型
python
import torch.nn as nn
import torch.nn.functional as F
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc1 = nn.Linear(10, 50) # 全连接层1:输入10维→输出50维
self.relu = nn.ReLU() # 非线性激活层
self.fc2 = nn.Linear(50, 1) # 全连接层2:输入50维→输出1维
def forward(self, x):
x = self.fc1(x) # 线性变换:10D→50D
x = self.relu(x) # 非线性激活(过滤负值)
x = self.fc2(x) # 线性变换:50D→1D
return x
model = MyModel()
print(model)
使用容器
nn.Sequential
是 PyTorch 里的一个容器类,其作用是按顺序排列一系列的神经网络层。在这个容器中前一层的输出会直接作为后一层的输入。我们可以使用 nn.Sequential 实现刚才自定义的模型
python
# 使用 nn.Sequential 快速构建模型
sequential_model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
print(sequential_model)
损失函数
torch.nn
模块提供了多种可用于不同类型的机器学习任务的损失函数
python
# 均方误差损失
mse_loss = nn.MSELoss()
input = torch.randn(3, requires_grad=True)
target = torch.randn(3)
loss = mse_loss(input, target)
print(loss)
损失函数名称 | 功能描述 | 适用任务 |
---|---|---|
MSELoss均方误差 | 计算预测值与真实值误差平方的平均值,对异常值较敏感。 | 回归任务 |
L1Loss平均绝对误差,MAE | 计算预测值与真实值绝对误差的平均值,对异常值的鲁棒性优于均方误差。 | 回归任务 |
SmoothL1Loss平滑 L1 损失 | 结合 L1 和 L2 损失的优点,误差较小时梯度更稳定,对异常值鲁棒。 | 回归任务(如目标检测) |
CrossEntropyLoss交叉熵损失 | 直接计算多分类任务中模型输出(未归一化)与真实类别的差异,内置 softmax 处理。 | 多分类任务 |
BCEWithLogitsLoss带 logits 的二元交叉熵 | 二分类专用损失,内置 sigmoid 函数,避免数值不稳定问题,直接接受未归一化输出。 | 二分类任务 |
KLDivLossKL 散度损失 | 衡量两个概率分布的差异,常用于模型输出与目标分布的匹配(如知识蒸馏)。 | 分布匹配任务 |
正则化
Dropout 是深度学习中一种常用的正则化技术,用来防止模型过拟合,在神经网络的训练过程中,Dropout 会随机 "丢弃"(即暂时忽略)一部分神经元。具体做法是,在每次前向传播时,每个神经元都有一定的概率(通常记为 p
)被置为 0,这个概率 p
是超参数,需要根据具体情况进行调整。被置为 0 的神经元在本次训练中不参与计算,也不会对后续的梯度计算和参数更新产生影响。
python
import torch
import torch.nn as nn
# 定义一个包含 Dropout 的简单神经网络
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(10, 20)
self.dropout = nn.Dropout(p=0.5) # 设置 Dropout 概率为 0.5
self.fc2 = nn.Linear(20, 1)
def forward(self, x):
x = self.fc1(x)
x = torch.relu(x)
x = self.dropout(x) # 在隐藏层之后应用 Dropout
x = self.fc2(x)
return x
# 创建模型实例
model = SimpleNet()
print(model)
优化器
优化器作用是通过迭代更新参数(如权重、偏置),使模型的损失函数值逐步降低,从而让模型输出更接近真实结果,是深度学习中用于调整模型参数的核心组件,PyTorch 通过optim
模块提供了多个优化器
python
import torch.optim as optim
model = MyModel()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 前向传播
output = model(torch.randn(1, 10))
loss = mse_loss(output, torch.randn(1, 1))
# 反向传播与优化
optimizer.zero_grad() # 清除梯度
loss.backward() # 反向传播
optimizer.step() # 更新参数
优化器名称 | 作用 | 适用任务 |
---|---|---|
SGD(Stochastic Gradient Descent)随机梯度下降 | 最基础的优化算法,随机选取一个或一小批样本计算梯度并更新参数 | 适用于大多数机器学习任务,尤其是数据量较大的情况 |
Adam(Adaptive Moment Estimation) | 结合了 AdaGrad 和 RMSProp 的优点,自适应地为不同参数调整学习率 | 通用性很强,在各种深度学习任务中都表现良好,是很多场景下的默认选择 |
RMSprop(Root Mean Square Propagation) | 自适应地调整每个参数的学习率,通过计算梯度平方的指数移动平均来调整学习率的大小,使得不同参数的学习率可以根据其梯度的变化情况自适应调整 | 适合处理非平稳目标和梯度稀疏的情况,常用于循环神经网络(RNN)及其变体 |
Adagrad(Adaptive Gradient Algorithm) | 根据每个参数的历史梯度平方和来调整学习率,使得频繁更新的参数学习率变小,不频繁更新的参数学习率变大 | 适用于数据稀疏的场景,能够自动调整不同特征的学习率,但可能会导致学习率过早衰减 |
Adadelta | 对 Adagrad 进行改进,不需要手动设置全局学习率,通过计算梯度和参数更新的指数移动平均来动态调整学习率 | 可以避免 Adagrad 中学习率过早衰减的问题,适用于各种深度学习任务 |
梯度管理
在模型训练时通过前向传播计算损失值,再通过反向传播计算损失对每个参数的梯度,最终用优化器(如 SGD、Adam)根据梯度更新参数。但在验证集、测试集或实际部署中,模型仅需进行前向传播生成预测结果,无需更新参数,此时禁用梯度可减少内存占用并加速计算
python
# 禁用梯度计算
with torch.no_grad():
z = x + y
print(z.requires_grad) # False
PyTorch 在多次调用 backward() 时默认会累积梯度,手动清零可确保每次反向传播基于当前批次的梯度
python
# 清除梯度
x.grad.zero_()
print(x.grad) # 0.0
数据集和数据加载器
torch.utils.data.Dataset
是一个抽象类,用于表示数据集,用户可以自定义数据集类。DataLoader
用于批量加载数据,支持多线程、数据打乱等功能。
python
import torch
from torch.utils.data import Dataset, DataLoader
# 自定义数据集类
class MyDataset(Dataset):
def __init__(self):
self.data = torch.randn(100, 10)
self.labels = torch.randint(0, 2, (100,))
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx], self.labels[idx]
# 创建数据集和数据加载器
dataset = MyDataset()
dataloader = DataLoader(dataset, batch_size=10, shuffle=True)
# 遍历数据加载器
for inputs, labels in dataloader:
print(inputs.shape, labels.shape)
模型保存与加载
保存模型
python
# 保存模型参数
torch.save(model.state_dict(), 'model.pth')
# 保存整个模型
torch.save(model, 'full_model.pth')
加载模型
python
# 加载模型参数
model = MyModel()
model.load_state_dict(torch.load('model.pth'))
model.to(device)
# 加载整个模型
model = torch.load('full_model.pth')
model.to(device)
训练与循环验证
训练循环指的是多次迭代遍历训练数据集即多个 "epoch"),每个 epoch 中都进行前向传播、计算损失、反向传播和参数更新等步骤。
实际训练中,数据通常按 "批次"(batch)输入模型,一个 epoch 包含多个批次,每个批次更新一次参数。多次循环就像 "反复练习",让模型逐渐修正错误,逼近能够最小化损失的最优参数。
python
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# 生成训练数据
x_train = torch.linspace(-5, 5, 100).view(-1, 1) # 生成 100 个样本
y_train = 2 * x_train ** 2 - 3 # 用规则生成标签,预测这个规则
# 定义一个简单的神经网络模型
class SimpleNet(nn.Module):
def __init__(self):
super(SimpleNet, self).__init__()
self.fc1 = nn.Linear(1, 10)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(10, 1)
def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out
# 初始化模型
model = SimpleNet()
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# 训练循环
num_epochs = 1000
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) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 测试模型
x_test = torch.linspace(-5, 5, 50).view(-1, 1)
y_test = 2 * x_test ** 2 - 3
with torch.no_grad():
predicted = model(x_test)
# 可视化结果
plt.scatter(x_test.numpy(), y_test.numpy(), label='True data')
plt.plot(x_test.numpy(), predicted.numpy(), color='r', label='Predicted data')
plt.legend()
plt.show()
输出
plain
Epoch [100/1000], Loss: 61.7129
Epoch [200/1000], Loss: 36.9103
Epoch [300/1000], Loss: 21.3151
Epoch [400/1000], Loss: 11.8180
Epoch [500/1000], Loss: 6.6703
Epoch [600/1000], Loss: 4.1226
Epoch [700/1000], Loss: 2.8500
Epoch [800/1000], Loss: 2.1080
Epoch [900/1000], Loss: 1.6015
Epoch [1000/1000], Loss: 1.2224
Loss 从第 100 个 Epoch 的 61.71 逐步下降到第 1000 个 Epoch 的 1.22,呈单调递减趋势,说明模型在训练过程中能够有效学习,参数更新方向正确,正在逐步逼近真实函数关系。真实预测结果确实比较吻合

小结
了解这些知识之后再去读使用了 PyTorch 的深度学习代码就会顺畅很多了