108 lines
2.5 KiB
Markdown
108 lines
2.5 KiB
Markdown
# 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)`,为什么?
|