PyTorch基础回顾:张量操作、自动求导机制、nn.Module基类、forward方法定义
说实话,每次带新人做项目,我都会先问一句:「PyTorch 基础扎实吗?」
不是故意刁难。而是我踩过太多坑了——有人把张量形状搞错,调试三天找不到 bug;有人不会用自动求导,手写反向传播写到崩溃。嗯,这些我都经历过。
今天咱们就把这四个核心概念捋一遍。别嫌基础,SwiGLU 的实现全靠它们撑着呢。
1. 张量操作:PyTorch 的「乐高积木」
张量是什么?说白了就是多维数组。但 PyTorch 的张量比 NumPy 的 ndarray 多了一个杀手锏——自动求导。
创建张量,我常用的几种方式:
import torch
# 从列表创建
t1 = torch.tensor([[1, 2], [3, 4]])
# 全零张量
t2 = torch.zeros(3, 4)
# 随机张量
t3 = torch.randn(2, 3) # 标准正态分布
# 单位矩阵
t4 = torch.eye(5)
我个人习惯用 torch.randn 初始化权重,效果比均匀分布好一些。不过要注意,SwiGLU 里的门控机制对初始化挺敏感,后面咱们会细说。
形状操作,这里容易翻车:
x = torch.randn(2, 3, 4)
# 重塑形状
x.view(2, 12) # 注意:view 要求内存连续
x.reshape(2, 12) # reshape 更灵活,不连续也能用
# 转置
x.T # 二维转置
x.permute(2, 0, 1) # 多维转置,指定维度顺序
# 拼接
a = torch.randn(2, 3)
b = torch.randn(2, 4)
c = torch.cat([a, b], dim=1) # 沿着列拼接,形状变成 (2, 7)
广播机制,新手必看:
a = torch.randn(3, 1) # 形状 (3, 1)
b = torch.randn(1, 4) # 形状 (1, 4)
c = a + b # 结果形状 (3, 4),自动广播
广播的规则很简单:从最后一个维度往前比,要么相等,要么一个是 1。你想想看,这跟 NumPy 完全一致,所以 NumPy 玩得溜的人上手 PyTorch 特别快。
2. 自动求导机制:PyTorch 的「魔法」
自动求导,英文叫 Autograd。说白了就是框架帮你算梯度,你不用手写链式法则了。
核心就三个东西:
requires_grad=True:告诉 PyTorch 这个张量需要计算梯度.backward():从某个标量开始反向传播.grad:存着计算好的梯度
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward()
print(x.grad) # 输出 tensor([7.]),因为 dy/dx = 2x + 3 = 7
嗯,这里要注意:backward() 只能对标量调用。如果你的 y 不是标量,得传一个同形状的 gradient 参数。我在做多任务学习时经常用到这个技巧。
optimizer.zero_grad() 清空梯度。不然梯度会累加,结果就乱套了。我曾经因为这个 bug 调了一整个下午...
计算图是怎么工作的?
PyTorch 会动态构建一个计算图。每次前向传播时,它记录下所有操作;反向传播时,沿着图往回走,用链式法则算梯度。
你想想看,这跟 TensorFlow 1.x 的静态图完全不一样。动态图的好处是:你可以用 if 语句、for 循环,想怎么调试就怎么调试。SwiGLU 的实现里,我们会用到这个特性来做条件计算。
3. nn.Module 基类:所有模型的「祖宗」
在 PyTorch 里,所有神经网络模块都要继承 nn.Module。这不是规定,而是为了方便——它帮你管理参数、处理设备迁移、支持序列化。
一个最简单的自定义模块:
import torch.nn as nn
class MyLinear(nn.Module):
def __init__(self, in_features, out_features):
super().__init__()
self.weight = nn.Parameter(torch.randn(out_features, in_features))
self.bias = nn.Parameter(torch.zeros(out_features))
def forward(self, x):
return x @ self.weight.T + self.bias
看到 nn.Parameter 了吗?它告诉 PyTorch:「这个张量是模型参数,需要被优化器更新。」如果你用普通的 torch.tensor,优化器根本不会理它。
nn.Module 帮我做了哪些事?
.parameters():自动收集所有可训练参数.to(device):一键迁移到 GPU.state_dict()/.load_state_dict():保存和加载模型.train()/.eval():切换训练/评估模式
4. forward 方法定义:模型的前向逻辑
forward 方法是模型的核心。它定义了输入怎么变成输出。你想想看,整个神经网络就是个函数,forward 就是函数体。
写 forward 的几个原则:
- 保持简洁:复杂的逻辑拆成子模块,forward 只做组装
- 支持动态图:可以用 if、for,PyTorch 会帮你记录计算图
- 注意设备一致性:输入和参数要在同一个设备上
class SimpleMLP(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(256, 10)
def forward(self, x):
# x 的形状: (batch_size, 784)
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
我个人习惯在 forward 里加一些断言,检查输入形状。比如:
def forward(self, x):
assert x.dim() == 2, f"Expected 2D input, got {x.dim()}D"
assert x.size(1) == 784, f"Expected 784 features, got {x.size(1)}"
# ... 后续逻辑
这样调试起来特别快。一旦形状不对,程序立刻报错,而不是跑到后面莫名其妙地崩溃。
self.weight.data += 1 这种操作。这会破坏计算图,导致梯度计算错误。如果你需要条件分支,用 if 语句控制计算路径,而不是修改参数。
知识体系总览
下面这张图,是我自己总结的 PyTorch 基础四件套的关系。你看一眼,心里就有谱了:
你看,这四个东西是环环相扣的。张量是数据载体,自动求导负责算梯度,nn.Module 帮你管参数,forward 定义前向逻辑。SwiGLU 的实现,就是在这四个基础上搭积木。
好了,基础就复习到这儿。下一章咱们直接上手写 SwiGLU 模块,到时候你会感谢今天打下的基础。