MiniMind(1)Tokenizer与训练数据
ChatGPT、DeepSeek,Qwen的成功激励人们探索AGI。 然而,动辄数百亿参数的庞大规模,使得它们对个人设备而言不仅难以训练,甚至连部署都显得遥不可及。 打开大模型的“黑盒子”,探索其内部运作机制,是每个初学者的必经之路,遗憾的是,99%的探索只能止步于使用LoRA等技术对现有大模型进行少量微调。
MiniMind从零开始训练,而不仅仅是推理,成本最低3元。MiniMind系列已完成多个型号模型的预训练,最小仅需25.8M(0.02B),即可具备流畅对话能力,截至2025.6.12的模型列表如下:
原始链接:https://github.com/jingyaogong/minimind
在学习minimind前,先简要了解LLM的流程(预训练-学知识,SFT-注入领域专业知识以及学对话方式),下面的一些内容来自minimind主页。
文章目录
- Tokenizer与训练数据
- Tokenizer
- Pre-train数据
- SFT数据
- RLHF数据
- Reason数据集
- 数据总结
- 补充内容:RLHF与GRPO
- 补充内容:RLVR
Tokenizer与训练数据
Tokenizer
分词器将单词从自然语言通过“词典”映射到0, 1, 36
这样的数字,可以理解为数字就代表了单词在“词典”中的页码。可以选择自己构造词表训练一个“词典”,代码可见./scripts/train_tokenizer.py
(仅供学习参考,若非必要无需再自行训练,MiniMind已自带tokenizer)。或者选择比较出名的开源大模型分词器,正如同直接用新华/牛津词典的优点是token编码压缩率很好,缺点是页数太多,动辄数十万个词汇短语;自己训练的分词器,优点是词表长度和内容随意控制,缺点是压缩率很低(例如"hello"也许会被拆分为 “h e l l o” 五个独立的token),且生僻词难以覆盖。“词典”的选择固然很重要,LLM的输出本质上是SoftMax到词典N个词的多分类问题,然后通过“词典”解码到自然语言。因为MiniMind体积需要严格控制,为了避免模型头重脚轻(词嵌入embedding层参数在LLM占比太高),所以词表长度短短益善。
tokenizer先分词,再把词映射到id上,以transformers的bert为例:
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')text = '今天是个好天气,我们可以出去走走。'
token_ids = tokenizer.encode(text, max_length = 30, add_special_tokens = True, padding = 'max_length', truncation = True)
# [101, 791, 1921, 3221, 702, 1962, 1921, 3698, 8024, 2769, 812, 1377, 809, 1139, 1343, 6624, 6624, 511, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]tokened_text = tokenizer.convert_ids_to_tokens(token_ids)
# ['[CLS]', '今', '天', '是', '个', '好', '天', '气', ',', '我', '们', '可', '以', '出', '去', '走', '走', '。', '[SEP]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']
Minimind选择了自己训练的minimind_tokenizer作为分词器,以保持整体参数轻量,避免编码层和计算层占比失衡,头重脚轻,由于自定义词表长度为6400,使得LLM总参数量最低只有25.8M。
Pre-train数据
预训练的目的是学知识,minimind的预训练数据为pretrain_hq.jsonl(1.56 GB),hq代表high quality,预训练数据提取了匠数大模型数据集的中文数据,并清洗出字符数小于512的语料拼接成预训练数据,格式如下:
可以看到,预训练数据格式是"text"作为key,句子作为value这样的形式:
SFT数据
SFT的目的是让模型拟合到对话方式上,比如在预训练数据上更偏向于大规模的补全,但在对话中,模型需要习惯一问一答这种形式。
对于SFT:匠数大模型数据集 是一个完整、格式统一、安全的大模型训练和研究资源。从网络上的公开数据源收集并整理了大量开源数据集,对其进行了格式统一,数据清洗, 包含10M条数据的中文数据集和包含2M条数据的英文数据集。
以上是官方介绍,下载文件后的数据总量大约在4B tokens,是适合作为中文大语言模型的SFT数据的。 但是官方提供的数据格式很乱,全部用来SFT代价太大。 minimind开发者把官方数据集进行了二次清洗,把含有符号污染和噪声的条目去除;另外依然只保留了总长度<512 的内容,此阶段希望通过大量对话补充预训练阶段欠缺的对话习惯。SFT文件为sft_512.jsonl(大约7.5GB)。
另一方面,Magpie-SFT数据集 收集了大约1M条来自Qwen 2.5的高质量对话,minimind开发者将这部分数据进一步清洗,把总长度<2048的部分导出为sft_2048.jsonl(大约9GB)。长度<1024的部分导出为sft_1024.jsonl(大约5.5GB),这种用大模型对话数据直接进行SFT就属于“黑盒蒸馏”的范畴。
进一步清洗这两个SFT数据集(只保留中文字符占比高的内容),筛选长度<512的对话,得到sft_mini_512.jsonl(大约1.2GB)。所有SFT文件 sft_X.jsonl 数据格式均为:
比如:
补充内容:SFT可以实现注入领域知识,也可以规范对话习惯,对于对话风格还需要RL进一步对齐。当前的医疗,金融LLM通常是LoRA-based SFT注入领域知识,再进一步用DPO或者GRPO对齐对话风格。
ReLIFT(Learning What Reinforcement Learning Can’t: Interleaved Online Fine-Tuning for Hardest Questions)中指出,RL主要是优化现有知识,难以学习到新的能力。SFT虽然能引入新知识,但高度依赖高质量数据。因此,ReLIFT(Reinforcement Learning Interleaved with Online Fine-Tuning,强化学习与在线微调交错)在RL中,动态识别"困难问题",再针对性收集高质量解法进行SFT,RL与SFT交替进行。
题外话:当前RL面临可扩展性问题,RLHF依赖人类偏好数据,并且容易受到奖励欺骗,为了避免奖励欺骗,RLVR虽然可以验证奖励,但本质上数据规模还是受限。因此有研究希望在海量语料库上做RL预训练,PRT(Reinforcement Pre-Training)把下一个词预测视为推理过程,模型在预测前生成思维链,预测词作为可验证奖励,这使得RL可以扩展到海量语料。
RLHF数据
来自 Magpie-DPO数据集 大约200k条偏好数据(均是英文),生成自Llama3-70B,可以用于训练奖励模型,优化模型回复质量,使其更符合人类偏好。Minimind把数据总长度<3000的内容重组为dpo.jsonl(大约0.9 GB),包含chosen
和rejected
两个字段,chosen
为偏好回复,rejected
为拒绝回复。
dpo.jsonl数据格式为:
比如:
Reason数据集
自从DeepSeek-R1爆火后,又流行起了新的低成本方案,使用R1所使用的数据进行SFT,minimind中对应的文件为r1_mix_1024.jsonl
,数据格式和sft_X.jsonl
一致。
数据总结
minimind中的所有数据如下:
./dataset/
├── dpo.jsonl (909MB)
├── lora_identity.jsonl (22.8KB)
├── lora_medical.jsonl (34MB)
├── pretrain_hq.jsonl (1.6GB, ✨)
├── r1_mix_1024.jsonl (340MB)
├── sft_1024.jsonl (5.6GB)
├── sft_2048.jsonl (9GB)
├── sft_512.jsonl (7.5GB)
├── sft_mini_512.jsonl (1.2GB, ✨)
└── tokenizer_train.jsonl (1GB)
被用在各个阶段:
补充内容:RLHF与GRPO
RLHF是语言模型对齐的早期方法,基于SFT后的模型,先进行奖励建模(RM),即训练一个神经网络,学习人类对回答的偏好排序,再进行策略优化(PPO),即基于奖励模型,通过强化学习更新模型策略。
GRPO在RLHF-PPO上进行简化,取消了显式的奖励模型,直接用规则函数(比如数学求解器)或人工规则评估输出质量,然后组内相对评估,对同一个问题生成多个response,归一化奖励计算相对优势。GRPO省去了RM训练环节,降低了计算复杂度。GRPO也有局限,比如规则奖励函数难以覆盖主观任务(如创意写作)。
GRPO中的规则函数实现例子,以数学问题求解为例,可以分别设计正确性奖励,格式规范性奖励,数值答案奖励,渐进式格式奖励:
# 对比模型提取的答案与标准答案是否一致
def correctness_reward_func(completions, answers):rewards = []for comp, ans in zip(completions, answers):extracted_ans = extract_xml_answer(comp) # 从XML响应中提取答案rewards.append(2.0 if extracted_ans == ans else 0.0)return rewards# 检查响应是否完全符合 XML 标签结构
def strict_format_reward_func(completions):pattern = r"<reasoning>.*?</reasoning>\s*<answer>.*?</answer>"return [0.5 if re.match(pattern, comp) else 0.0 for comp in completions]# 鼓励模型输出数字而非文本
def int_reward_func(completions):extracted_answers = [extract_xml_answer(comp) for comp in completions]return [0.5 if ans.isdigit() else 0.0 for ans in extracted_answers]# 对部分符合格式的行为给予部分奖励
def xmlcount_reward_func(completions):rewards = []for comp in completions:tag_count = comp.count("<reasoning>") + comp.count("</reasoning>") + comp.count("<answer>") + comp.count("</answer>")rewards.append(tag_count * 0.125) # 每个标签奖励0.125分return rewards
假设用户提问:
问题:小明有 12 个苹果,吃了 3 个,还剩几个?
标准答案:9
模型生成 3 个候选响应:
响应1: <reasoning>12-3=9</reasoning><answer>9</answer> → 完全正确
响应2: <reasoning>12-3=9</reasoning> <answer>nine</answer> → 答案非数字
响应3: 答案:12-3=9 → 格式错误
则有:
计算组内相对优势,响应1最高: A 1 = 3.5 − m e a n ( s c o r e s ) s t d ( s c o r e s ) = 1.82 A_{1}=\frac{3.5-mean(scores)}{std(scores)}=1.82 A1=std(scores)3.5−mean(scores)=1.82,因此,更新策略为:模型参数向生成响应1的方向调整(梯度上升)。
补充内容:RLVR
RLVR(Reinforcement Learning with Verifiable Rewards)通过直接利用可自动验证的规则或目标函数生成奖励信号,避免了RLHF(Reinforcement Learning from Human Feedback)对人工标注的依赖和奖励欺骗风险。其核心思路是:将任务目标转化为可计算的规则函数,用客观标准替代主观偏好。
RLHF的局限性:
- 奖励欺骗:模型可能通过“讨好”奖励模型(RM)而非真正解决问题来获取高分(例如生成冗长但空洞的回答);
- 标注稀缺:需大量人工标注的偏好数据(如对比多个回答的优劣),成本高且主观性强。
RLVR的突破:
- 奖励可验证性:奖励基于预定义的规则或参考答案自动计算(如代码通过单元测试、数学答案匹配解析树),无需人工标注;
- 抗欺骗设计:规则函数直接关联任务本质(如正确性、格式),模型无法通过“作弊”绕过验证。
对于目前最先进的大模型,通常是采用RLVR生成奖励+GRPO优化策略两个联合框架。