如何在 CSP 考场上训练 AI 并辅助编程?
_colin1112_ · · 休闲·娱乐
上一期我们提到用人脑模拟 AI。然而,这未免过于简单。并且目前开源模型库中,也无法体验到最新最强的模型(如 gpt-6-astra 等),可能在 CSP 中无法解决较难题目。
那么,怎么才能在 CSP 考场中简单地体验到方便,智商高的模型呢?
我们充分发挥群众的力量:
那么,理论存在,实践开始。
:::warning[叠甲]
- 本文章非常显然的,大量依赖于 AI,但是文章大致是没有问题的。
- 文章纯属整活,请不要在正式考试时尝试使用该方法。
- 本文章是笔者在学习 LMCC 时灵光乍现,特此记录。
:::
:::error[文章声明] 目前能力较强的模型都并没有公开完整训练数据与方法。 本文章依据:
- 网络公开数据以及训练方式进行创作。
- xiaomi mimo v2.6 公开的后训练方法。
:::
上一期,我们背诵了模型训练完成后的
然而这显然是一种投机取巧的行为。真正严谨的做法,应当从随机数开始,在考场上现场把这
一. 寻找完整训练数据集并背诵
训练 AI 的第一步,自然是背诵完整训练集。
遗憾的是,GPT、Claude、Gemini 等主流模型通常不会公开逐条训练语料。以 Qwen3 为例,官方技术报告公开了:
- 总计约 36T token
- 覆盖 119 种语言和方言
- 第一阶段使用超过 30T 通用 token
- 第二阶段使用约 5T 高质量 STEM、代码、推理和合成数据
- 第三阶段使用数百 B 长文本 token
但是,官方并没有公开每一条文档、完整混合比例、数据排列顺序和所有随机种子。
因此,我们并不能直接套用 Qwen3 的方式进行训练。相反,我们将沿用类似的网络结构,使用公开语料从随机权重训练一个新的模型:
其中
后文中的所有训练计算都使用变量
目前比较完整的公开训练语料包括:
| 数据集 | 规模与内容 | 用途 |
|---|---|---|
| OLMo 2 Mix 1124 | 3.90T token,22.4TB 未压缩数据,明确列出各来源 | 通用预训练与完整配方参考 |
| Dolma | 约 3T token,网页、论文、代码、书籍等 | 通用英文预训练 |
| FineWeb-Edu | 1.3T 教育质量较高的英文 token | 知识与推理 |
| WanJuan 1.0 | 超过 1TB 中文网页、书籍、百科和教材 | 中文能力 |
| The Stack v2 | 全量 67.5TB,训练集约 900B token | 代码预训练 |
| OpenWebMath | 630 万篇文档,14.7B token | 数学能力 |
| CodeContests | 竞赛题、正确和错误代码、测试数据 | 竞赛编程后训练 |
如果需要一个数据、训练代码、模型权重和训练过程都相对开放的参考项目,最合适的是 OLMo。
至此,数据问题已经完美解决。我们只需背诵这小小的一部分数据即可。
二. 清洗训练数据
下载到网页并不能直接开始训练。网页中可能包含:
- 导航栏
- 广告
- 重复模板
- 乱码
- 机器生成的垃圾内容
- 隐私信息
- 完全相同或高度相似的文档
- 测试集中的题目和答案
因此,我们需要完成以下流水线:
- 提取网页正文,删除导航栏和广告
- 判断语言和内容质量
- 删除乱码、恶意内容和过短文档
- 隐去邮箱、IP 地址等隐私信息
- 使用哈希进行精确去重
- 使用 MinHash 或 SimHash 进行近似去重
- 检查训练语料是否包含未来要评测的题目
- 保留文档来源、许可证、URL 和数据版本
去重尤其重要。
如果一篇题解在不同网站转载了 100 次,模型就会认为它比其他内容重要 100 倍。更严重的是,如果评测题目的标准答案出现在训练集中,模型可能只是在背答案。
于是,我们专门安排一批大脑负责判断:
这个信息是不是已经出现在训练数据里?
三. 训练分词器
清洗完成后,从不同类型的语料中按比例抽样,训练 BBPE 分词器。
分词器决定:
- 每个 token 对应什么字节序列
- 词表中每个 token 的编号
- 特殊 token 的编号
- 对话模板使用哪些标记
分词器一旦确定,就必须冻结。
如果训练到一半修改分词器,同一个 token id 可能突然表示另一个字符串,之前学到的词嵌入就全部失去意义。
分词完成后,每篇文档会变成 token id 序列:
[151644, 872, 198, 14880, 11622, ...]
然后:
- 在文档之间加入结束标记
- 将多篇短文档打包成固定长度序列
- 保存训练、验证和测试划分
- 固定数据顺序和随机种子
四. 随机初始化模型
上一期中,我们直接背诵了
这一次不能作弊,所有参数都必须从随机数开始:
训练时需要保存的并不只有模型权重。
以 AdamW 为例,还可能需要:
- 模型参数
- 参数梯度
- 一阶动量
- 二阶动量
- 混合精度训练使用的 FP32 主权重
- 前向传播的中间激活值
因此,参数量为
五. 完成一次预训练
假设一段训练文本被分成:
模型的输入为:
标准答案则整体向左移动一位:
模型的训练目标,是预测每一个位置的下一个 token:
假设某个位置的正确 token 是 2,模型只给了它 1% 的概率,那么这个位置的损失为:
softmax 与交叉熵结合后,logit 的梯度为:
正确 token 得到负梯度,更新后 logit 会升高;错误 token 得到正梯度,更新后 logit 会降低。
梯度再沿着输出层、MLP、注意力层和词嵌入层一路反向传播,得到全部参数的梯度。
最后用优化器更新参数。以 AdamW 为例:
至此,才完成了一个 optimizer step。
注意,模型生成时确实是一个 token 一个 token 地向外输出;但训练使用 teacher forcing,一次前向传播可以同时计算整个序列上所有位置的损失。
六. 完成一个 Batch
假设一个全局 batch 包含
每算完一个小批量,就把梯度加到同一份累加器中:
这里最重要的是,在整个 batch 计算完成以前,模型参数必须始终保持为
全部
然后执行梯度裁剪和一次优化器更新,得到
因此,我们需要同时记录:
- 当前的
N 个参数 -
- 优化器的一阶和二阶状态
- 当前数据位置和随机数状态
- 尚未完成的前向传播和反向传播中间结果
如果中途算错一个小数,可以回到上一个检查点重新计算。如果没有保存检查点,那么从这一步开始,后面的全部结果都会与正确模型不同。
七. 重复 D 个 token
设整个预训练过程一共使用
每一步都要重复前向传播、反向传播、梯度累积和参数更新。对于参数量为
其中:
-
-
- 常数 6 粗略包含一次前向传播和两倍左右的反向传播计算
这个公式只是量级估算,没有精确计入词嵌入、注意力的序列长度开销、数据处理和优化器更新。
八. 预训练结束,开始后训练
经过漫长的串行训练,我们终于得到了一个 Base 模型。
此时,它可能知道 C++,可能知道线段树,甚至可能知道某道题的标准答案。
但是,当你对它说:
请解决这道题,只输出代码。
它可能回答:
当然可以!这道题主要考查……
然后写一篇八百字题解,最后忘记输出代码。
这是因为预训练只教会了模型预测下一个 token,并没有教它怎样按照用户要求完成任务。
因此,我们还需要后训练。
MiMo-V2.6 公开了一套相当完整的后训练方案,包括:
- 面向 agent 的中期训练
- 混合任务强化学习
- 异步 GRPO
- 动态采样与 partial rollout
- Groupwise Reward Synthesis
- Groupwise Advantage Redistribution
- reward hacking 防御
- Multi-Prefix Multi-Teacher On-Policy Distillation
因此,我们决定参考 MiMo-V2.6,训练:
CSP-aB-RL
九. 面向 Agent 的中期训练
MiMo-V2.6 在正式 RL 之前,没有直接把 Base 模型扔进环境,而是先进行 mid-training。
中期训练的数据不再只是普通网页,而是混合了:
- 代码仓库
- 软件工程轨迹
- 通用工具调用
- 视觉任务
- 研究任务
- 图像、视频和音频
- 长上下文文本
- 多轮 agent 交互记录
中期训练分成两个上下文阶段:
- 先使用 256K 上下文完成大部分训练
- 再将上下文长度扩展到 1M token
这样做的目的,是让模型先熟悉看长文档、浏览代码仓库、调用工具和修改文件,再进入昂贵的强化学习阶段。
否则,模型连工具调用格式都不会写,就已经开始寻找奖励函数的漏洞了。
十. 后训练
预训练和中训练结束后,模型已经会续写文本、阅读长代码并调用工具,但还不一定会按照指令完成任务。参考 MiMo-V2.6,后训练可以概括为:冷启动、环境交互、GRPO、奖励修正和多教师蒸馏。
1. 冷启动
首先准备少量高质量示范
这一步不负责把模型训练得很强,只负责让它进入环境以后能够正常行动,而不是连工具格式都写错。
2. 在环境中生成轨迹
对于任务
行动可以是输出代码、运行程序、修改文件或调用工具。环境最后通过测试、规则或 grader 给出奖励:
MiMo-V2.6 公布的环境覆盖代码、通用工具、视觉和网络安全等任务。其一次 RL 更新抽取 1568 个 prompt,每个 prompt 生成 16 条轨迹,共约 25088 条;每步产生约 2.7B 至 3.7B token,上下文最长 1M token。
对于 CSP-aB-RL,我们只保留算法题环境。验证器直接编译并运行代码:
3. 使用 GRPO 更新模型
对同一个任务生成