返回关卡地图
预判 0 / 3 闯关题:0 / 3 XP 0 / 300

Level 00 | 零基础导学关卡

PyTorch Warmup

每个 Mission 先猜一猜(押个假设解锁讲解)→ 看图解原理 → 做巩固题 → 回 notebook 写代码。带着问题学,比直接读记得牢。

00_PyTorch_Warmup.ipynb TensorEmbeddingAutograd
先猜一猜
图解原理
巩固 + 作业
先学会看懂 shape、查表、梯度流,再回 notebook 写 PyTorch 代码
Mission 1 TODO 1.1 / 1.2

Tensor 变形:先学会读 shape,再写 reshape

🎯 先猜一猜(下注解锁)

把 [B,C,H,W] 变成 [B,H×W,C] 时,最危险的不是 API 名字,而是元素顺序。reshape 只会重新分组,不会自动把 C 挪到最后。

先判断:为什么通常要先 permute,再 reshape?

先押一个假设,下面的讲解就会解锁。猜错完全没关系——带着疑问读,记得最牢。

先补的知识

  • Tensor 的 shape 不是一串数字,而是每个轴的语义。
  • 图像特征常见布局是 [B, C, H, W]:B 是样本数,C 是通道,H/W 是空间网格。
  • Transformer 更喜欢序列布局:[B, S, C],其中 S 可以是 H * W。

图解原理

把一张 H×W 的小棋盘摊平成一条路线:每个格子变成一个 token,格子里带着 C 个通道特征。

1. 原始小图:[B=1, C=2, H=2, W=3]

同一张图有两个通道,像两张叠在一起的 2×3 透明胶片。

Channel 0 abc def
Channel 1 ABC DEF
换轴:把 C 放到每个格子里
2. 通道放后:[B=1, H=2, W=3, C=2]

现在每个空间位置都带着一个 2 维小向量。

[a,A][b,B][c,C] [d,D][e,E][f,F]
摊平 H×W:按行走成序列
3. Token 序列:[B=1, S=6, C=2]

Transformer 看到的是 6 个 token,每个 token 有 2 个特征。

t0[a,A] t1[b,B] t2[c,C] t3[d,D] t4[e,E] t5[f,F]

你不需要先背 permute(0, 2, 3, 1)。先问自己:我要把 C 从第 2 个轴挪到最后,再把 H 和 W 合成序列长度。

4. 从玩具 shape 迁移到 Notebook TODO
玩具例子Notebook 变量检查点
[b,t,f]x: [B,C,H,W]先说清每个轴代表什么
permute[B,H,W,C]C 必须来到最后
reshape[B,H*W,C]只合并 H 和 W
测试失败时,不要盲换 API
5. 三类常见错法
现象优先检查
shape 变成 [B,C,H×W]C 没有移动到最后,permute 顺序不对。
shape 正确但 allclose 失败元素顺序被打乱;重新画出第一个 token 应来自哪个像素。
reshape 报不连续理解 reshape/contiguous 的关系,或用 einops 明确表达轴语义。
行业视角:shape 语义是读模型代码的第一道门

ViT patch、图像 token、Attention head 拆分和 KV Cache 都在做同一类事情:重新组织轴,但不能破坏“哪个数属于哪个 token”的语义。

语法热身:换轴和合并维度怎么写

下面用一个“时间序列”小例子练语法,不使用 notebook 里的图像 shape。

# x: [batch, time, feature]
x = torch.randn(2, 4, 3)

# 把 feature 放到 time 前面: [batch, feature, time]
y = x.permute(0, 2, 1)

# 把 batch 和 time 合并: [batch * time, feature]
z = x.reshape(2 * 4, 3)

# einops 写法:名字就是维度含义
z2 = rearrange(x, "b t f -> (b t) f")

读法:permute 是“重新排列轴”,reshape 是“重新分组元素”,einops 是“把变形规则写成句子”。

举一反三:每次变形前写一行 shape 合同

# 输入合同: x [B,C,H,W]
# 目标合同: tokens [B,H*W,C]
# 数量守恒: C*H*W == (H*W)*C

tokens = x.permute(0, 2, 3, 1).reshape(B, H * W, C)
assert tokens.shape == (B, H * W, C)

shape 合同只能保证外形;最后仍要用一个小张量或 allclose 检查元素顺序。

巩固一下

如果 x 的 shape 是 [2, 3, 4, 5],转成图像 token 序列后 shape 应该是多少?

回到 notebook 的作业

这里不直接写答案。你已经拿到足够输入,最后用 notebook 的 TODO 做举一反三。

Mission 2 TODO 2.1 / 2.2

Embedding 查表:token id 是地址,不是连续数值

🎯 先猜一猜(下注解锁)

token id 看起来是整数,但 9 并不比 2 更‘大’或更‘重要’。它只是词表中的行号。

先判断:如果把 token id 直接当连续数值送进 Linear,最根本的问题是什么?

先押一个假设,下面的讲解就会解锁。猜错完全没关系——带着疑问读,记得最牢。

先补的知识

  • 文本会先被 tokenizer 变成 token id,例如 [4, 1, 3]。
  • 神经网络不能直接理解 id 的大小关系;id 只是查表地址。
  • Embedding 权重表的 shape 是 [vocab_size, hidden_dim],第 i 行就是 token i 的向量。

图解原理

像在词典里按页码找词条:id 是页码,Embedding 表是词典,查出来的一整行向量才是模型真正使用的表示。

input_ids203
ID 0[0.1, 0.4] ID 1[0.8, 0.2] ID 2[0.5, 0.9] ID 3[0.3, 0.7]
输出向量序列[0.5, 0.9][0.1, 0.4][0.3, 0.7]

nn.Embedding(input_ids)weight[input_ids] 做的是同一个核心动作:按 id 取行。

4. 把查表动作映射回 Notebook
对象含义shape
input_ids每个位置的词表行号[B,S]
emb_layer.weight整本可学习词典[V,H]
output每个 id 查到的一行向量[B,S,H]
错误通常发生在索引,不在矩阵乘法
5. 测试排查表
报错/现象优先检查
indices must be Longinput_ids.dtype 应为整型索引。
index out of range所有 id 必须满足 0 ≤ id < vocab_size
shape 少了 hidden 维输出应在 input_ids 后追加 embedding_dim。
行业视角:Embedding 往往是大模型最大的单块参数之一

词表 V 很大时,[V,H] 权重会占据显著显存;词表扩容、权重共享和并行切分都从这张查找表出发。

语法热身:Embedding 和高级索引

这里用“颜色编号查颜色向量”的例子,不使用 notebook 的 vocab/hidden 设置。

# 4 种颜色,每种颜色用 3 个数字表示
palette = torch.tensor([
    [1.0, 0.0, 0.0],  # red
    [0.0, 1.0, 0.0],  # green
    [0.0, 0.0, 1.0],  # blue
    [1.0, 1.0, 0.0],  # yellow
])

color_ids = torch.tensor([[2, 0], [3, 1]])

# 高级索引:每个 id 替换成对应行
colors = palette[color_ids]  # shape: [2, 2, 3]

# nn.Embedding 的调用方式也一样:传入 id,返回向量
emb = nn.Embedding(num_embeddings=4, embedding_dim=3)
vectors = emb(color_ids)

读法:weight[input_ids] 会把 input_ids 的每个整数当成“行号”,输出 shape 会在 input_ids 后面追加 embedding_dim。

举一反三:用高级索引验证 nn.Embedding

official = emb_layer(input_ids)
manual = emb_layer.weight[input_ids]

assert official.shape == (*input_ids.shape, emb_layer.embedding_dim)
assert torch.allclose(official, manual)

这组对照把“Embedding 是查表”从比喻变成了可运行证据。

巩固一下

给定 weight 有 10 行、hidden_dim=4,input_ids 的 shape 是 [2, 3],Embedding 输出 shape 是什么?

回到 notebook 的作业

这里不直接写答案。你已经拿到足够输入,最后用 notebook 的 TODO 做举一反三。

Mission 3 TODO 3.1 / 3.2 / 3.3

Linear + ReLU 反传:梯度像闯关一样倒着走

🎯 先猜一猜(下注解锁)

Linear+ReLU 的 backward 必须逆着 forward 走。forward 最后经过 ReLU,所以传回来的梯度第一站不是 Linear,而是 ReLU mask。

先判断:grad_output 回来后,应该先计算什么?

先押一个假设,下面的讲解就会解锁。猜错完全没关系——带着疑问读,记得最牢。

先补的知识

  • forward 不是只算输出,还要保存 backward 会用到的中间信息。
  • ReLU 的导数很简单:z > 0 时梯度通过,z <= 0 时梯度变 0。
  • Linear 的三个梯度要和三个输入形状对齐:x、weight、bias。

图解原理

把前向看成三扇门:Linear 门、ReLU 门、Loss 门。反向时梯度从 Loss 倒着回来,ReLU 门先决定哪些位置能通行。

z
[-1, 0, 2]
生成 mask z > 0
[0, 0, 1]
乘上传回梯度 grad_z
[0, 0, 5]
grad_z grad_x = grad_z @ weight grad_weight = grad_z.T @ x

如果前向里某个 z 是负数,ReLU 把它压成 0;反向时这条路也会被 mask 关掉。

4. 反向 shape 账本
梯度公式必须匹配
grad_xgrad_z @ weightx.shape
grad_weightgrad_z.T @ xweight.shape
grad_biasgrad_z.sum(dim=0)bias.shape
数值对,不代表计算图一定对
5. 测试失败时按顺序定位
现象优先检查
负 z 位置仍有梯度是否先乘了 ReLU mask。
weight 梯度转置矩阵乘法顺序是否让结果 shape 等于 weight。
bias 梯度多一维是否沿 batch/token 维求和,只保留输出特征维。
行业视角:手写 backward 是理解 fused kernel 的入口

FlashAttention、融合激活和自定义 Triton 算子都需要明确“前向保存什么、反向重算什么、每个梯度是什么 shape”。这道小题就是最小版本。

语法热身:自定义 autograd 函数长什么样

这里用“平方函数”的 backward 练结构,不直接写 notebook 的 Linear+ReLU。

class SquareFunction(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x):
        ctx.save_for_backward(x)
        return x * x

    @staticmethod
    def backward(ctx, grad_output):
        (x,) = ctx.saved_tensors
        grad_x = grad_output * 2 * x
        return grad_x

# 调用自定义 Function 要用 .apply
y = SquareFunction.apply(x)

读法:ctx.save_for_backward 像是在前向路上放书签;backward 里先取回书签,再按链式法则乘上传回来的 grad_output

举一反三:用 autograd 当裁判

# custom 与 reference 使用同一份输入和参数
loss_custom.backward()
loss_reference.backward()

assert torch.allclose(custom_grad_x, reference_grad_x, atol=1e-5)
assert torch.allclose(custom_grad_w, reference_grad_w, atol=1e-5)

不要只比 forward 输出;自定义 Function 的核心验收对象是每一个输入梯度。

巩固一下

已知 grad_output = [5, 5, 5],z = [-1, 0, 2],经过 ReLU 反向后 grad_z 是什么?

回到 notebook 的作业

这里不直接写答案。你已经拿到足够输入,最后用 notebook 的 TODO 做举一反三。

完成 3 道闯关题后,本关即算完成;作业 checklist 用来辅助你回 notebook 练习。
进度只保存在当前浏览器 localStorage,分享 HTML 不会带走你的记录。
返回地图 下一关:L01