1、分词器训练

硬件要求:

分词器训练对CPU要求不高,CPU主要用来读取数据集,而训练过程主要受到机器物理内存的限制。

由于BPE算法的限制,中文与英文数据集要求不同,比如中文30GB数据集需要至少2200GB的物理内存,而英文30GB只需要至少40GB物理内存;中文1GB数据需要至少140GB物理内存,而英文1GB只需要4GB物理内存。 如果物理内存不够可以通过磁盘空间来创建虚拟内存进行训练,但是训练速度会受到磁盘I/O速率限制,通常会非常慢,所以最好还是选择足够内存的机器来训练。

环境要求:

必须使用我们自己编译的tokenizers0.19.1版本,否则无法实现多层分词器的训练。

2、模型预训练

前提条件:

计算模型参数规模和计算量大小,从而可以估算所需要的显存大小和训练时长。

公式:设hidden_size=h,layer_num=l,vocab_size=v

对于Decoder_only模型:

self_attention层参数为:4h2+4h;

前馈神经网络mlp层参数:假如由h维度上升到h'维度:h*h'+h';假如由h'维度下降到h维度:h'*h+h;

一层layernorm层参数:2h;

一层RMS norm层参数:h;

嵌入层参数:v*h;如果位置嵌入可学习,则还要加上位置嵌入的参数,但是通常很少所以可以忽略掉;

计算量:FP32精度下通常为6*total_params*data_size;FP16精度下则除以2

一次前向传播和反向传播训练时长:计算量/GPU峰值速率*GPU利用率*GPU数量

显存计算:

主要集中在优化器的显存占用,如果是fp32精度,通常优化器所需要的显存为4*2*total_params,模型权重和梯度所需要的显存共为2*4*total_params,所以至少需要64*total_params的显存

模型主要参数:

num_hidden_layers:模型block数量

layer_num:前馈神经网络的mlp层层数

layer_dimdiff:前馈神经网络中每一层mlp层升维的维数

训练参数:

block_size:训练样本的最大token数/如果数据集中数据量太少,而block_size太大,就无法进行分块,训练会出bug

优化器参数:

per_device_train_batch_size:每个设备的批次大小

gradient_accumulation_steps:梯度累积步数(多少步计算一次梯度)

learning_rate:学习率

weight_decay:权重衰减率

adam_beta1/beta2:adam优化器的β1/β2参数

adam_eps:adam优化器的ε值

max_grad_norm:梯度裁剪的最大范数

训练控制参数:

max_steps:最大训练步数

warmup_steps:学习率预热步数

num_train_epochs:训练轮数

max_epochs:最大训练轮数

保存与日志参数:

output_dir: 检查点输出目录

save_steps: 每多少步保存一次检查点

save_strategy: 保存策略,"steps" 或 "epoch"

save_total_limit: 最多保留多少个检查点

logging_steps: 每多少步记录一次日志

log_dir: TensorBoard 日志目录

report_to: 日志报告工具(默认tensorboard)

运行时参数:

seed: 随机种子(默认 42)

fp16: 是否使用混合精度训练

resume_from_checkpoint: 从检查点恢复训练的路径

数据加载器参数:

dataloader_drop_last: 是否丢弃最后一个不完整的批次

max_seq_length: 最大序列长度

dataloader_num_workers: 数据加载的子进程数

早停与监控参数:

early_patience: 早停的耐心值,即验证损失不降低的最大轮数

early_min_delta: 早停的最小改进量

eval_every_steps: 每多少步评估一次

grad_norm_threshold: 梯度范数阈值,用于检测梯度爆炸

其他参数:

by_eos: 是否按 EOS 截断

time_budget_hours: 训练时间预算

ckpt_dir: 检查点保存目录

save_images: 是否保存输入图像到 TensorBoard

log_interval: 记录图像的间隔

valid_loader: 验证数据加载器

3、训练注意事项:

损失曲线震荡或上升,梯度范数过大(>10):学习率过大;训练前期损失几乎不下降,训练后期损失曲线停滞但验证集未过拟合:学习率过小;

显存受限时,可以降低批次大小,提高梯度累积步数;显存充足时,可以提高批次大小,降低梯度累积步数,这样可以保持有效批次不变;

小批次时,正则化效果好,泛化能力强,但是训练可能不稳定;中批次是中和训练速度和稳定性的选择;大批次训练稳定收敛块,但可能过拟合;

block_size调优:较小时显存占用小,可快速迭代;较大时模型可学习到长距离依赖,但是显存占用大。前期可用小block_size,后期增大;

如果要确定最佳超参数,可以在2000步进行不同超参数的测试,选择损失最低的超参数组合,再进行长时间的训练;

断点续训时,当前模型配置文件要与断点的配置文件参数一致,不能修改配置文件的参数,否则可能会出现张量形状不一致的错误;

我们的大模型不需要验证集,因为验证集的作用就是监控模型预训练过程会不会过拟合或者没有泛化能力,不过作用也只能监控,无法通过调参彻底解决这个问题,这是以往大模型无法解决的问题,但是我们的损失设计就会迫使模型更“聪明”,而不是“死记硬背”,所以不如将验证集也放入预训练集中,让模型学习到更多的知识;而微调的时候需要验证集,就是防止模型在新的领域中过拟合或者失去泛化能力;

训练的总token数=训练步数*有效批次数*序列长度

4、使用DeepSeek-R1的训练流程训练我们的模型

注意:训练脚本只适用于Mandelbrot-V1模型的多层分词器架构

阶段一:训练R1-zero:

R1-zero是使用基于规则奖励的纯强化学习的方式,训练基座模型得到的。#lc_weight=0表示不加语言一致性奖励

python R1/run_rl_grpo.py

--reward_mode rule --policy_model_path <基座模型> --tokenizer_root <多层tokenizer根目录(含layer0/layer1...)> --train_file R1/sample_data_en/train_rl.jsonl --output_dir outputs/r1_zero --lc_weight 0 --log_samples

阶段二:R1-zero with Cold Start(直接生成一批冷启动推理 SFT 数据,然后再 SFT 一次)

然后用 R1-Zero 作为初始化,做一次 SFT(冷启动):

python R1/run_sft.py

--model_path outputs/r1_zero --tokenizer_root <多层tokenizer根目录(含layer0/layer1...)> --train_file R1/cold_start_data_en/train_sft.jsonl --eval_file R1/cold_start_data_en/eval_sft.jsonl --output_dir outputs/cold_start_sft --mask_prompt

再用 cold_start_sft 作为初始化,做一次 RL(GRPO):

注意:论文中的冷启动的微调数据是使用R1-zero生成的,这里省略了这一步。

python R1/run_rl_grpo.py

--reward_mode rule --policy_model_path outputs/cold_start_sft --tokenizer_root <多层tokenizer根目录(含layer0/layer1...)> --train_file R1/sample_data_en/train_rl.jsonl --output_dir outputs/r1_zero_with_cold_start --lc_weight 0.5 --log_samples

阶段三:最终 R1 的“混合 SFT”(推理数据 + 非推理数据)

论文中最终 R1 会混合“推理/非推理”监督数据。最简单的做法是把两类样本合并成同一个 JSONL:

- 推理样本:`response` 里包含 `Reasoning:` 和 `Final answer:`(或自定义的 think/final 标签)

- 非推理样本:`response` 只包含简洁答案(不包含推理段)

注意:论文中原流程是使用上一阶段训练的模型生成推理数据并进行拒绝采样,并且非推理数据也是用基座模型生成的,但由于我实验的模型参数规模很小,无法正常输出有效数据,所以就舍去了这一步,而是直接收集了数据。

合并后用 `run_sft.py` 再训一次基座模型:

python R1/run_sft.py

--model_path <基座模型> --tokenizer_root <多层tokenizer根目录(含layer0/layer1...)> --train_file <混合SFT训练集.jsonl> --eval_file <混合SFT验证集.jsonl> --output_dir outputs/final_sft --mask_prompt

最后再用强化学习训练一次得到最终的R1模型:

python R1/run_rl_grpo.py

--reward_mode rule --policy_model_path <final_sft> --tokenizer_root <多层tokenizer根目录(含layer0/layer1...)> --train_file R1/sample_data_en/train_rl.jsonl --output_dir outputs/r1_final --lc_weight 0.5 --log_samples

Authentication code:

Toolbar

Comment signature

Add new attachment

In order to upload a new attachment to this page, please use the following box to find the file, then click on “Upload”.
Ho hum, it seems that the EditPageHelp page is missing. Someone must’ve done something to the installation...

You can copy the text from the EditPageHelp page on jspwiki.org.