5-A2S2-attention.ipynb 逐步说明

这份 notebook 在 2/1-class-prog.md 里已经讲过整体结构。这里补的是更具体的“每一步到底在实现什么”。

它和后面第 3 课的最小 LoRA 实验有非常清楚的对应关系:

2.1 部分:从零实现 Attention

Cell 8:init_qkv_proj(...)self_attention(...)

init_qkv_proj(n_embd:int)

return (nn.Linear(n_embd, n_embd), nn.Linear(n_embd, n_embd), nn.Linear(n_embd, n_embd))

这说明:

这和你们第 3 课里看到的:

target_modules=['q_proj', 'k_proj', 'v_proj', ...]

可以直接对上。

self_attention(...)

这一格是整份作业的总装函数。你会反复回来补它。

它的结构本质上是:

  1. 如果多头,就先 split heads
  2. QKT
  3. 做 scaling
  4. 如果是语言模型,就加 causal mask
  5. 做 softmax
  6. 和 V 相乘得到输出
  7. 如果多头,就 merge heads

这就是 Transformer block 里 self-attention 的主链。

Cell 10:Step 1 的核心函数

pairwise_similarities(Q, K)

这一格让你实现:

A = QKT

它真正想让你注意的是:

attn_scaled(A, n_embd, n_heads)

这里要求你实现标准 scaling:

$$\frac{A}{\sqrt{d_k}}$$

其中:

为什么要除以它?

这和前面 A2S1 故意把 scaling 去掉,刚好形成对照。

attn_softmax(A)

这一步很简单,但有一个最容易犯错的点:

因为你是在“对每个 query,看所有 key 的分布”。

compute_outputs(A, V)

这里要求实现:

O = AV

也就是用注意力权重去加权求和 value。

Cell 14:Step 2,Causal Mask

make_causal_mask(n_tok)

目标是构造一个下三角 mask,让每个位置只能看自己和过去。

为什么一定要这样?

apply_causal_mask(mask, A)

通常的做法是:

这样 softmax 后,未来位置的概率就变成 0。

这一步和后面第 3 课的所有 causal LM 微调完全同源。

Cell 18:Step 3,多头注意力

split_heads_qkv(Q, K, V, n_heads)

这里的真正难点不是数学,而是 shape。

你要学会把:

变成类似:

也就是说:

多头为什么不是“多做几次 attention”?

因为还涉及:

这些张量组织方式的变化,决定了每个 head 真正处理的是 embedding 的哪一部分。

2.2 部分:在 mini Transformer 中做实验

从这里开始,notebook 从“手写 attention 小练习”进入“放进最小 Transformer 里跑训练”。

Cell 27:下载数据

这部分和课程目录里的:

直接对应。你们在课程页里已经准备好了本地副本,就是为了让这一步不依赖在线下载。

Cells 28-35:数据与词表处理

这部分很值得和第 3 课 course_lora_qwen_src/data/*.json 对照:

形式不同,但本质都是“把原始文本组织成 next-token prediction 数据”。

Cells 37-40:模型与训练器

你可以把它理解成第 2 课版本的“最小训练闭环”。

Cells 42-47:评估和生成

这和后面你们第 3 课里训练完后立刻做推理,是同样的节奏:

训练不是终点,验证行为才是。

这份 notebook 最值得学生掌握的 4 个点

  1. QK^T、scaling、softmax、加权和是 attention 的四步主链
  2. causal mask 是语言模型和普通 attention 的关键分界线
  3. multi-head attention 的难点主要在 shape
  4. 最终这些实现要能回到一个真实可训练的 Transformer 里