MiniMind 学习笔记 04:模型结构总览,Config、CausalLM 与 Backbone

前面已经看过 tokenizer、dataset 和训练循环。现在进入模型结构。

MiniMind 的模型代码集中在:

model/model_minimind.py

第一次看这个文件时,很容易被各种类名绕住。其实可以先抓三层:

MiniMindConfig
-> MiniMindForCausalLM
-> MiniMindModel

这三层分别对应:

模型配置
-> 语言模型外壳
-> Transformer 主干

1. MiniMindConfig:模型的图纸

MiniMindConfig 保存模型结构参数。

一些关键默认值包括:

参数默认值含义
vocab_size6400词表大小。
hidden_size768hidden state 维度。
num_hidden_layers8Transformer block 层数。
num_attention_heads8Q head 数量。
num_key_value_heads4K/V head 数量。
intermediate_size约 2432FFN 中间层维度。
dropout0.0dropout 概率。
rms_norm_eps1e-6RMSNorm 的稳定项。
rope_theta1e6RoPE 频率参数。
use_moeFalse是否启用 MoE。

这些参数会决定模型里各个矩阵的形状。

例如:

hidden_size = 768
num_attention_heads = 8
head_dim = 768 / 8 = 96

所以你调试 Attention 时看到的 head 维度 96,就是从这里来的。

2. vocab_size 会影响哪些参数

vocab_size 不只是 tokenizer 的概念,它会直接影响模型参数量。

MiniMind 里有两个和词表相关的层:

self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)

如果 vocab_size=6400hidden_size=768

embedding 参数量 = 6400 * 768
lm_head 参数量   = 768 * 6400

如果没有权重共享,这两块都很大。

很多模型会把 embedding 和 lm_head 绑定:

self.lm_head.weight = self.model.embed_tokens.weight

这样可以减少参数量,也让输入 token embedding 和输出 token 分类共享同一套语义空间。

3. MiniMindForCausalLM:语言模型外壳

MiniMindForCausalLM 是面向 causal language modeling 的最外层模型。

它主要负责:

创建 MiniMindModel 主干;
创建 lm_head;
处理 labels 和 loss;
组织返回值;
实现 generate。

可以把它理解成:

Transformer backbone + 词表预测头 + 训练/推理接口

它的 forward 大致是:

input_ids
-> MiniMindModel
-> hidden_states
-> lm_head
-> logits
-> 如果传入 labels,则计算 loss

logits 的形状是:

[batch_size, seq_len, vocab_size]

每个位置都预测下一个 token 的词表分数。

4. 为什么叫 CausalLM

CausalLM 里的 causal 指的是因果语言模型。

它的限制是:

当前位置只能看当前位置及之前的 token,不能看未来 token。

这和生成任务完全一致:

生成第 t 个 token 时,只能依赖前面已经生成的 token。

所以 CausalLM 通常使用:

decoder-only Transformer
masked self-attention
next-token prediction

MiniMind 就是这种结构。

5. MiniMindModel:Transformer 主干

MiniMindModel 是真正堆叠 Transformer block 的部分。

它主要包含:

self.embed_tokens
self.dropout
self.layers
self.norm

可以画成:

input_ids
   |
   v
Embedding
   |
   v
Dropout
   |
   v
MiniMindBlock x N
   |
   v
Final RMSNorm
   |
   v
hidden_states

这里的 MiniMindBlock x N 中,N 就是:

config.num_hidden_layers

默认是 8 层。

6. embedding 是 lookup,不是普通矩阵乘法

embed_tokens 的定义是:

nn.Embedding(vocab_size, hidden_size)

它可以理解成一个表:

[vocab_size, hidden_size]

每个 token id 对应表里的一行。

当输入是:

[12, 35, 98]

embedding 做的是查表:

取第 12 行
取第 35 行
取第 98 行

输出形状变成:

[seq_len, hidden_size]

所以它更像 lookup table,而不是对 one-hot 做显式矩阵乘法。数学上两者等价,工程上查表更高效。

7. Dropout 为什么是 0

MiniMind 默认:

dropout = 0.0

这意味着训练和推理时都不会随机丢弃 hidden state。

在现代 LLM 中,dropout 不一定总是开启。尤其是:

数据量较大;
模型规模较大;
训练策略较稳定;
更追求训练/推理一致性;

时,dropout 可能设得很低,甚至为 0。

但 dropout 本身仍然是经典正则化手段。它的作用是:

训练时随机屏蔽部分激活,降低过拟合。

小模型、小数据或特定微调任务中,dropout 仍然可能有价值。

8. KV Cache 和 start_pos

MiniMindModel.forward 里还会处理:

past_key_values
start_pos
use_cache

这些主要服务于推理。

训练时通常一次性处理完整序列:

seq_len = N

推理 decode 时,每次只新增一个 token。如果每次都从头计算所有历史 token,成本会很高。

KV Cache 的思路是:

历史 token 的 K/V 已经算过了,缓存起来;
下一步只计算新 token 的 Q/K/V;
再和历史 K/V 一起做 attention。

start_pos 则告诉 RoPE 等位置相关逻辑:当前新 token 在完整序列中的位置从哪里开始。

所以它和 KV Cache 是配套的。

9. 三层关系总结

可以把三者关系记成:

MiniMindConfig
  决定模型有多大、多少层、多少 head。

MiniMindForCausalLM
  包装主干模型,负责 logits、loss 和 generate。

MiniMindModel
  真正的 Transformer backbone,负责 embedding、blocks、norm。

如果从调用链看:

MiniMindForCausalLM.forward
    -> MiniMindModel.forward
        -> embed_tokens
        -> MiniMindBlock x N
        -> norm
    -> lm_head
    -> loss / logits

10. 小结

学习模型结构时,不要一开始就陷入每一行代码。

先建立这张地图:

配置层:MiniMindConfig
模型外壳:MiniMindForCausalLM
主干网络:MiniMindModel
基本单元:MiniMindBlock

这篇文章讲前三层。下一篇继续进入 MiniMindBlock,也就是每一层 Transformer decoder block 内部到底做了什么。

发表评论