MiniMind学习流程
type
Post
status
Published
date
Dec 11, 2025
slug
minimind
summary
minimind walkthrough
tags
开发
category
技术分享
titleIcon
password
icon
insider
训练显存要求并不高,时间要求会很高
Tokenizer
是什么
- 类似图片使用像素表征自然图像(RGB→色块)
- 分词器用数值表征自然语言(ID→语言块)
怎么做
- BPE
- 计数所有相邻两个字符的组合,选择最多的一对合并为新的单元字符,持续迭代至词典大小(最后为每个字符与合并创建的各个新字符组成)
- 在词典大小允许情况下,常见词会变成单元字符,不常见的会存在其一部分,如unfamiliar会存在un(如果其足够频繁)这样的一部分
Model
总览
- 对比原本的Transformer,结构上主要有以下变化:
- Norm: LayerNorm → RMSNorm
- Attention: MHA → GQA
- Positional Encoding: Sinusoidal Positional Encoding → ROPE


RMSNorm
- 令μ = 0,带入LayerNorm
- 意味着不做平移,只做缩放,减少计算要求,同时仍能维持训练稳定性
- LayerNorm



- RMSNorm


GQA
- 将Queries分成x组,每组内合用一对kv

- 一些相比原本的Attention(Transformer中)额外加的东西
- 分组复用的kv主要通过更小的线性层输出体现
- kv cache用于decode推演时缓存(prefill为user prompt → 首个token,decode为自回归,之前的 → 下一个),在prefill阶段,一次性并行处理完user prompt,seq_len即为其tokenizer后长度,decode时,恒为1,在计算完kv与应用rope后,拼接进kv cache,seq长度变为上次长度+1

ROPE
- 记录相对位置,而非绝对位置 → 外推性好(训练在短文本如512,但可以在768,1024等更长文本仍有不错表现)
- 位置编码用于表达位置信息,对m位置向量(embedding)与n位置向量建模相对位置m-n可通过空间旋转建模,有如下过程:

SwiGLU FFN
- 多出个门控,也就是在扩中间层时候,分两个Linear出来,一个是原本的,一个是做完后走SiLU激活,然后点对点乘,实现门控筛选

Transformer Block
MiniMind
- 若果没有kv cache,就是prefill,此时开始位置为0,输入为全局,一次算出user prompt里所有的kv入cache,当此次过后,有kv cache,长度为user prompt tokenize后,其长度对应下标为起始位置,输入为单个token(prefill得到的首个token,之后是每次的上次生成的单个token),算出自己的kv入cache,自己的q与kv cache算结果(即只需要输入这一token,便通过cache得到所有截止当前的所有kv,从user prompt开头到当前token)
Embedding
- 作用:ID → Vector
- 即将tokenizer输出结果转为对应hidden_size大小的vector,数值初始随机,在学习过程中调整
- 共享输入输出的转换权重,节省空间
Pretrain
是什么
- 任务为通过之前的tokens预测下一token(causal language modeling)
- 学习通用语言理解
Learning Objective | Example |
Grammar | "The cat sits" not "The cat sit" |
Facts | "Paris is the capital of France" |
Patterns | Code syntax, mathematical notations |
Multilingual | Chinese characters, English words |
Context | Long-range dependencies |
怎么做
数据
- 数据量相对大,可以通过head命令看一眼数据pattern

- 数据内容均为纯文本,jsonl里存放的每个json对象只有text属性,内容为一句句话,每句开头结尾为特殊token

- 特殊token的配置

- 相关代码
- 读入文本,对过长的截断,对过短的padding,进行tokenizer编码,定义loss_mask对padding出来的内容不做loss计算,由于任务为下一token预测,开始的token不为标签,结尾的token不为输入,开始的token也不算loss
训练
- loss_mask盖住padding部分
- 选择CE Loss
结果
- 单卡4090 - 使用MiniMind2 104M参数配置
- ~4h完成单epoch



- 与仓库效果图拟合

SFT/IFT
是什么
- Supervised Fine-Tuning (SFT) / Instruction Fine-Tuning
- 让预训练的模型适配指令遵循与对话,而不是仅续写
怎么做
数据
- 一个json对象里有conversations属性,其中含一个个句子,每个由content与对应role组成,后续只有assistant对应role求loss

- 将assistant的部分定为起始id与结束id
- 找上方对assistant部分定义的起始与结束id,对中间内容标记mask为1,约束仅对此部分计算loss
训练
- 数据格式与计算出的loss mask pretrain与sft唯一的区别
- 对比train_pretrain.py与train_full_sft.py,在训练逻辑上没有任何区别

结果
- 并非完全连续训,但确实训练时间很长,应该不少于两天


- 难评,看上去都是震荡的


RL
- P → Policy,待训练LLM充当RL领域对应角色,O → Optimization
- Preference|Reasoning Fine-Tuning
- DPO | RLHF(Human Feedback) & RLVR(Verifiable Rewards)(PPO → GRPO → SPO)
DPO
- 对比学习,要求数据集里准备Chosen与Rejected对
- 通过与冻住的基线模型结果作差算Loss
- 有限程度地最大化chosen与rejected概率差

- 建模偏好为概率
- log → 数值连乘改求和
- - → 最大改最小
- bata约束相比ref模型偏离程度
- 代码中调整顺序
GRPO
- 增大相比组内其他答案的优势,减小相对原本的偏移


SPO
PPO
Reason
Lora
Distillation
θ角



