minimind/learning/day01/02_deep_dive.md

108 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Day 1 Deep Dive: MiniMind 模型结构拆解
## 深挖问题 1:MiniMind 的最小 forward 路径是什么?
打开 `model/model_minimind.py`:
1. `MiniMindForCausalLM.forward`
2. `MiniMindModel.forward`
3. `MiniMindBlock.forward`
4. `Attention.forward`
5. `FeedForward.forward`
你要能追踪:
```text
input_ids
-> embed_tokens
-> dropout
-> N x MiniMindBlock
-> RMSNorm
-> lm_head
-> logits
-> cross_entropy loss
```
重点观察:
- `input_ids.shape == [batch, seq_len]`
- `hidden_states.shape == [batch, seq_len, hidden_size]`
- `logits.shape == [batch, seq_len, vocab_size]`
## 深挖问题 2:为什么 forward 里是这样算 loss?
代码位置:`MiniMindForCausalLM.forward`
核心逻辑:
```python
x = logits[..., :-1, :]
y = labels[..., 1:]
loss = F.cross_entropy(x.view(-1, x.size(-1)), y.view(-1), ignore_index=-100)
```
理解方式:
- 位置 0 的 logits 预测 token 1。
- 位置 1 的 logits 预测 token 2。
- 最后一个位置没有下一个 token,所以丢掉。
- label 的第一个 token 没有被任何上文预测,所以丢掉。
`ignore_index=-100` 是训练中最重要的 mask 约定之一。pretrain 用它忽略 padding;SFT 用它忽略 user/system/prompt 部分。
## 深挖问题 3:Attention 里 GQA 是怎么实现的?
看 `Attention.__init__`:
- `num_attention_heads`
- `num_key_value_heads`
- `n_rep = q_heads // kv_heads`
看 `repeat_kv`:
- K/V heads 先少算。
- attention 前把 K/V 复制到和 Q heads 对齐。
工业理解:
- 训练时省一部分参数和计算。
- 推理时 KV cache 更小。
- 对小模型通常是合理取舍。
## 深挖问题 4:MoE 为什么有 aux loss?
看 `MOEFeedForward.forward`:
- gate 给每个 token 分配 expert。
- `topk_idx` 决定 token 去哪个 expert。
- `aux_loss` 鼓励 expert 负载更均衡。
工业风险:
- 没有负载均衡,部分 expert 可能被过度使用。
- 原生 PyTorch MoE 训练不一定快,因为 expert 分派有额外调度开销。
## 今日小实验
写一个临时 Python 片段,不修改项目文件:
```bash
python - <<'PY'
import torch
from model.model_minimind import MiniMindConfig, MiniMindForCausalLM
config = MiniMindConfig(hidden_size=128, num_hidden_layers=2)
model = MiniMindForCausalLM(config)
input_ids = torch.randint(0, config.vocab_size, (2, 16))
labels = input_ids.clone()
out = model(input_ids, labels=labels)
print(out.logits.shape)
print(out.loss.item())
PY
```
你要解释:
- 为什么随机模型也有 loss?
- loss 大概应接近 `log(vocab_size)`,为什么?