diff --git "a/notes/encyclopedia.md" "b/notes/encyclopedia.md" new file mode 100644--- /dev/null +++ "b/notes/encyclopedia.md" @@ -0,0 +1,1972 @@ +# 改进点百科:BabyLM Challenge 2026 Strict-Small + +> **目标读者**:有深度学习基础,但对大模型预训练不太熟悉的参赛者 +> **用途**:系统了解每个可调维度是什么、为什么有效、预期影响多大 +> **更新日期**: 2026-03-21 +> **组织方式**:按实验流程排列——先准备数据,再分词,选模型,定训练目标,选优化器,最后调参 + +--- + +## 目录 + +- [总览:改进维度一览表](#总览改进维度一览表) +- [A. 数据策略](#a-数据策略)(A1 数据源总览 / A2 质量过滤 / A3 采样策略 / A4 数据增强 / A5 构建方式总览) +- [B. 分词器](#b-分词器tokenizer)(B0 BPE基线 / B1 Morfessor / B2 词表大小) +- [C. 嵌入策略](#c-嵌入策略)(C0 标准嵌入 / C1 N-hot / C2 FastText) +- [D. 模型架构](#d-模型架构)(D0 GPT-2基线 / D1 GPT-BERT / D2 ModernBERT / D3 xLSTM / D4 ELECTRA / D5 MoE / D6 AttnRes) +- [E. 训练目标与掩码策略](#e-训练目标与掩码策略)(E0 标准掩码基线 / E1 AMLM / E2 掩码衰减 / E3 频率掩码 / E4 MNTP比例 / E5 反向MTP) +- [F. 优化器](#f-优化器)(F0 Adam基线 / F1 AdamW / F2 LAMB / F3 FORGETTER / F4 Muon) +- [G. 超参数](#g-超参数)(G1 学习率 / G2 Batch Size / G3 Weight Decay / G4 Dropout / G5 序列长度 / G6 序列构造) +- [H. 训练技巧](#h-训练技巧)(H1 Checkpoint选择 / H2 Checkpoint平均 / H3 知识蒸馏) +- [I. 需要注意的方法](#i-需要注意的方法效果有限或有风险) +- [附录:术语表](#附录术语表) + +--- + +## 总览:改进维度一览表 + +> **pp = percentage points**,例如 BLiMP 从 67% → 70% 就是 +3pp + +| 编号 | 改进点 | 类别 | 优先级 | 预期提升 | 实现难度 | +|:----:|--------|------|:------:|:--------:|:--------:| +| A1 | 可用数据源总览 | 数据 | ⭐⭐⭐ | 选对源 +4.8pp | 低 | +| A2 | 数据质量过滤 | 数据 | ⭐⭐ | 去噪提质 | 低 | +| A3 | 数据采样策略(4种) | 数据 | ⭐⭐⭐ | 精准分配10M预算 | 中 | +| A4 | 数据增强方法(4种) | 数据 | ⭐⭐⭐ | +3~8pp | 低~中 | +| A5 | 数据集构建方式总览(13种) | 数据 | ⭐⭐⭐ | 5维度×多选项组合 | 低~中 | +| B0 | BPE 8K 分词 | 分词器 | — | **默认基线** | 低 | +| B1 | Morfessor+BPE 分词 | 分词器 | ⭐⭐ | ET +40pp | 中 | +| B2 | 词表大小(4K/8K/16K) | 分词器 | ⭐⭐ | 嵌入质量 vs 序列长度 | 低 | +| C0 | 标准嵌入(随机初始化) | 嵌入 | — | **默认基线** | 低 | +| C1 | N-hot 形态嵌入 | 嵌入 | ⭐⭐⭐ | +4~60pp | 极低 | +| C2 | FastText 初始化 | 嵌入 | ⭐⭐ | +2~3pp | 极低 | +| D0 | GPT-2(纯 CLM) | 架构 | — | **默认基线**(最经典 decoder) | 低 | +| D1 | GPT-BERT(双目标混合) | 架构 | ⭐⭐⭐ | 赛道最优 | 低 | +| D2 | ModernBERT | 架构 | ⭐ | 纯双向 | 低 | +| D3 | xLSTM/mLSTM(+ShortConv) | 架构 | ⭐⭐ | +3.7pp 均分 | 中 | +| D4 | RTD/ELECTRA | 架构 | ⭐⭐ | 小数据高效 | 中 | +| D5 | MoE 稀疏路由(叠加到5种架构) | 架构 | ⭐⭐ | 综合冠军方案 | 高 | +| D6 | Attention Residuals(跨层注意力残差) | 架构 | ⭐ | 25%计算节省(深层模型) | 中 | +| E0 | 标准均匀掩码(固定 15%) | 掩码 | — | **默认基线** | 极低 | +| E1 | AMLM 自适应掩码 | 掩码 | ⭐⭐⭐ | +3~5pp BLiMP | 中 | +| E2 | 掩码率衰减 40%→15% | 掩码 | ⭐⭐ | +1~2pp | 极低 | +| E3 | 频率感知掩码 | 掩码 | ⭐ | +1~7.5pp | 低 | +| E4 | MNTP:CLM 比例 | 掩码 | ⭐⭐ | 稳定性 | 极低 | +| E5 | 反向课程学习 MTP | 训练目标 | ⭐⭐ | ET+5, WUG+6.5pp | 中 | +| F0 | Adam | 优化器 | — | **默认基线** | 低 | +| F1 | AdamW | 优化器 | ⭐ | 解耦正则,Transformer标配 | 低 | +| F2 | LAMB 优化器 | 优化器 | ⭐⭐ | 大batch更稳 | 低 | +| F3 | FORGETTER | 优化器 | ⭐⭐⭐ | +3pp BLiMP | 极低 | +| F4 | Muon 优化器 | 优化器 | ⭐⭐ | PPL -30% | 中 | +| G1 | 学习率调优 | 超参数 | ⭐⭐⭐ | 影响最大 | 极低 | +| G2 | 有效Batch Size | 超参数 | ⭐⭐ | +1~2pp GLUE | 极低 | +| G3 | Weight Decay | 超参数 | ⭐ | 正则化 | 极低 | +| G4 | Dropout | 超参数 | ⭐ | 正则化 | 极低 | +| G5 | 序列长度 | 超参数 | ⭐ | 权衡 | 极低 | +| G6 | 序列构造方式(Packing vs 逐句) | 超参数 | ⭐⭐ | 待测试 | 低 | +| H1 | Checkpoint 选择 | 技巧 | ⭐⭐⭐ | +1~2pp | 极低 | +| H2 | Checkpoint 平均 | 技巧 | ⭐⭐ | +1pp | 极低 | +| H3 | 知识蒸馏 | 技巧 | ⭐ | 待验证 | 低 | + +--- + +## A. 数据策略 + +> **核心事实**:三届 BabyLM 比赛的元分析表明,**数据构建是影响成绩最大的单一因素**(+4.8pp, p<0.05),超过模型架构、优化器、超参数的影响。在 10M 词预算下,"选什么数据"和"怎么用数据"比"用什么模型"更重要。 +> +> 本节按数据处理流程组织:先认识有哪些数据源 → 清洗过滤 → 采样策略 → 增强生成 → ��测数据利用 → 最终推荐方案。 +> +> 数据源的详细信息(来源、许可、引用等)见 `notes/ref_datasets.md`。 + +--- + +### A1. 可用数据源总览 ⭐⭐⭐ + +比赛规则已确认(2026-03-14):**所有数据都可以用,唯一约束是总训练数据 ≤ 10M 词**。我们有 14 个数据源(含 Open Subtitles 清洗版),共 119M 词原始数据,需从中选出最优的 10M 词。 + +**全部数据已下载**,统一存放在 `data/raw_files/`(外部数据以 `ext_` 前缀命名),详细信息见 `notes/ref_datasets.md`: + +| # | 数据源 | 文件名 | 词数 | +|:-:|--------|--------|-----:| +| 1 | Gutenberg | `gutenberg.train.txt` | 25.6M | +| 2 | CHILDES | `childes.train.txt` | 28.4M | +| 3 | Open Subtitles | `open_subtitles.train.txt` | 19.2M | +| 4 | Simple Wikipedia | `simple_wiki.train.txt` | 15.3M | +| 5 | BNC Spoken | `bnc_spoken.train.txt` | 7.6M | +| 6 | Open Subtitles Cleaned | `open_subtitles_cleaned_ckpt_70.txt` | 0.3M | +| 7 | Switchboard | `switchboard.train.txt` | 0.25M | +| 8 | FineWeb-Edu | `ext_fineweb_edu.txt` | 3.0M | +| 9 | Cosmopedia | `ext_cosmopedia.txt` | 3.0M | +| 10 | TinyDialogues | `ext_tiny_dialogues.txt` | 3.0M | +| 11 | OneStopEnglish | `ext_onestop_english.txt` | 0.38M | +| 12 | GenericsKB | `ext_generics_kb.txt` | 8.5M | +| 13 | ConceptNet | `ext_conceptnet.txt` | 2.7M | +| 14 | SNLI 前提句 | `ext_snli.txt` | 1.84M | +| | **训练数据合计** | **17 个文件** | **119.1M** | +| + | 评测数据(7 个评测集的正确句,详见 A5) | `data/7_eval_sentences/` | 1.36M | +| | **全部可用数据合计** | | **120.5M** | + +> 注:Open Subtitles Cleaned(#6)是 Open Subtitles 的 LLM 过滤子集,数据量极小(0.3M),在后续分类中归入 Open Subtitles 一并处理。评测数据由 Stage 7a 自动提取,Stage 8 采样时自动追加到训练集末尾,不参与采样策略的选择。 + +#### 数据源分类 + +按内容类型分为 4 类,每类覆盖不同的评测任务: + +**第一类:语法与句法(→ BLiMP, BLiMP Supplement)** + +| 数据源 | 词数 | 核心价值 | 证据 | +|--------|------|----------|------| +| **Gutenberg** | 25.6M | 句法最丰富,长句多(均 38.6 词/行),BLiMP 覆盖率最高 | ✦ Charpentier 2024 消融 | +| **BNC Spoken** | 7.6M | 英式口语,含疑问句/倒装(BLiMP Supplement 关键) | 理论推断 | +| **Open Subtitles** | 19.2M | 对话轮换(Turn Taking),口语多样性补充 | 理论推断 | + +**第二类:世界知识(→ EWoK, COMPS)** + +| 数据源 | 词数 | 核心价值 | 证据 | +|--------|------|----------|------| +| **Cosmopedia** | 3.0M | Mixtral 生成的教科书/百科,结构化知识 | ✦ Charpentier 2024 消融 | +| **GenericsKB** | 8.5M | 102 万条常识("Ice is cold"),直接覆盖 EWoK 考点 | 理论推断 | +| **ConceptNet** | 2.7M | 12 种语义关系转自然语言,覆盖 COMPS 属性继承 | 理论推断 | +| **Simple Wikipedia** | 15.3M | 百科知识,简化英语,世界知识丰富 | 理论推断 | + +**第三类:语言理解(→ GLUE)** + +| 数据源 | 词数 | 核心价值 | 证据 | +|--------|------|----------|------| +| **FineWeb-Edu** | 3.0M | CommonCrawl 筛选的教育文本,知识密度极高 | ✦ Charpentier 2024 消融 | +| **SNLI 前提句** | 1.84M | Flickr 图片描述句,MNLI/RTE 同源数据 | ✦ Phang 2018 STILTs(间接) | +| **OneStopEnglish** | 0.38M | 同一内容 3 种难度,句法多样性好 | 理论推断 | + +**第四类:儿童语言与发展(→ AoA)** + +| 数据源 | 词数 | 核心价值 | 证据 | +|--------|------|----------|------| +| **TinyDialogues** | 3.0M | GPT-4 生成的分龄对话(2/5/10/15 岁),比 CHILDES 更干净 | ✦ Feng 2024 | +| **CHILDES** | 28.4M | 真实儿童语言习得数据,噪声多但原汁原味 | 理论推断 | +| **Switchboard** | 0.25M | 电话对话,数据量极小,辅助用 | — | + +> **✦ 有实验证据的 4 个关键发现**(详见 `ref_datasets.md` 第四节): +> 1. Gutenberg → BLiMP:Charpentier 2024 消融实验,官方数据对 BLiMP 贡献最大 +> 2. FineWeb-Edu → GLUE:Charpentier 2024 消融实验,对 GLUE 提升最大 +> 3. Cosmopedia → EWoK:Charpentier 2024 消融实验,对 EWoK 提升最大 +> 4. TinyDialogues → AoA:Feng et al. 2024,合成 CDS 优于自然 CDS + +#### 三条关键选数据原则 + +1. **多样性 > 单一高质量**:不要把 10M 全用同一来源,混合使用效果最好 +2. **去除弱数据源比添加强数据源更重要**:低质量数据(未过滤的 MADLAD-400)会直接拉低成绩 +3. **每个数据源服务特定任务**:不是所有数据对所有任务有用,需要按任务需求配比 + +--- + +### A2. 数据质量过滤 ⭐⭐ + +> **核心结论**:**数据过滤 > 课程排序**。把精力花在过滤上比设计复杂的训练顺序更值得。 + +原始数据(尤其是 CHILDES、Open Subtitles)含大量噪声。我们的 Stage 1-4 流水线(`scripts/01_data/`)逐步清洗: + +| 阶��� | 过滤方法 | 说明 | 处理的主要问题 | +|:----:|----------|------|---------------| +| Stage 1 | **噪声过滤** | 删 <3 词短行、编码垃圾(U+FFFD)、ASCII<70% 行 | CHILDES `@`/`%` 注释、字幕时间戳/HTML | +| Stage 2 | **OOV 过滤** | 删除 OOV 率过高的行(对照 66K 评测词表) | 外语词、拼写错误密集行 | +| Stage 3 | **去重** | 删完全重复行 | CHILDES 和字幕重复率极高 | +| Stage 4 | **格式清洗** | 数据源特定处理 | CHILDES `*CHI:` 前缀、字幕全大写、Gutenberg 模板文本 | + +**额外可选过滤**(论文中提到的有效方法,⚠️ 均未实现,需要开发): + +| 方法 | 说明 | 效果 | 实现状态 | +|------|------|------|:--------:| +| **PPL 过滤** | 用预训练小模型(如 GPT-2 small)给每句算困惑度,删极高分的垃圾句 | 去掉 90% 低质量数据后性能无损 | ❌ 未实现 | +| **句法过滤** | 用 spaCy parser 分析句法树,删不完整/不可分析的句子 | 比 curriculum learning 更有效 | ❌ 未实现(Stage 5 有 spaCy 标注但只打标签不过滤) | +| **长度过滤** | Simple Wiki 保留 64-512 词的段落,过滤过短表头和过长表格 | 提升 Simple Wiki 数据质量 | ❌ 未实现(Stage 4 仅过滤 <3 词短行) | + +--- + +### A3. 数据采样策略 ⭐⭐⭐ + +清洗后有约 80-100M 词可用,需要从中选出 ≤ 10M 词的训练集。**怎么选**至关重要——随机抽样和精心设计的采样策略可以差出 5-10pp。 + +我们实现了 4 种采样策略(`scripts/01_data/stage8_sample_{A,B,C,D}.py`),代码全部写好,共享配置文件 `stage8_config.py`。 + +**当前运行状态**: + +| 策略 | 脚本 | 输出数据 | 状态 | +|:----:|------|---------|:----:| +| A | `stage8_sample_A.py` | `data/8_sample_A/train.txt` | ❌ 未运行 | +| **B** | `stage8_sample_B.py` | `data/8_sample_B/train.txt`(9.99M 词) | ✅ 已生成 | +| **C** | `stage8_sample_C.py` | `data/8_sample_C/train.txt`(9.99M 词) | ✅ 已生成 | +| D | `stage8_sample_D.py` | `data/8_sample_D/train.txt` | ❌ 未运行 | + +> **前置依赖**:4 种策略都依赖 Stage 5(规则标注)和 Stage 6(Qwen 质量标注)的输出。每行数据包含:来源、文本、词数、quality(1-5)、BLiMP/EWoK/Entity Tracking 等任务标注。所有来源都配有词数上限(`stage8_config.py` 中的 `cap`):Gutenberg 27%、Wiki 20%、CHILDES 20%、BNC 18%、Subtitles 12%、其他 3%。 + +--- + +#### 策略 A:纯质量排序(最简单) + +**核心思路**:谁质量高就选谁,不管覆盖什么任务。 + +**具体做法**: +1. 每行文本有一个 Qwen 标注的 quality 分(1-5 分,Stage 6 输出) +2. 在**每个数据源内部**按 quality 从高到低排序 +3. 逐源贪心取句,直到该源的词数上限(cap)或总预算 10M 用完 + +``` +例:Gutenberg cap=27% → 最多取 ~2.7M 词,取的是 Gutenberg 中 quality 最高的行 + Wiki cap=20% → 最多取 ~2.0M 词 + ... + 合计 ≤ 10M 词 +``` + +**优点**: +- 实现最简(~30 行核心代码),运行最快 +- 保证选出的每条数据都是该来源中质量最好的 + +**缺点**: +- 完全不考虑评测任务覆盖——可能选了大量质量高但与评测无关的文本 +- 依赖 Qwen quality 分数的准确性,如果标注有偏差会被放大 +- cap 仍然是手动设定的,没有自动优化 + +**适用场景**:快速基线实验,作为其他策略的对照组 + +--- + +#### 策略 B:任务配额 + 质量填充 ⭐⭐⭐(推荐) + +**核心思路**:先保证每个评测任务都有足够的相关数据("不偏科"),剩余预算再按质量填满。 + +**具体做法**:两轮(Pass)采样—— + +**Pass 1(任务配额)**:为每个评测任务预留专属数据 + +``` +Pass 1a — BLiMP 配额 + Stage 5 标注了每行匹配哪些 BLiMP 子任务(67 个) + 每个子任务最多取 800 句(按 quality 降序) + 上限 67 × 800 = 53,600 句 + +Pass 1b — EWoK 配额 + Stage 5 标注了每行匹配哪些 EWoK 域(11 个) + 每个域最多取 300 句 + 上限 11 × 300 = 3,300 句 + +Pass 1c — 专项配额 + entity_track 标注=3 的行,取 top 60,000 句 + wug 标注=3 的行,取 top 20,000 句 + comps 标注=3 的行,取 top 12,000 句 + aoa 标注=3 的行,取 top 15,000 句 +``` + +**Pass 2(质量填充)**:剩余预算按 quality 降序填满,遵守各来源 cap 上限。 + +**优点**: +- **任务覆盖有保障**:即使某任务相关数据的 quality 不高,也会被配额选入 +- 两轮设计兼顾了覆盖和质量——Pass 1 保底,Pass 2 提质 +- 配额参数可按实验结果调整(如某任务分数低就加大配额) + +**缺点**: +- 依赖 Stage 5 规则标注的准确性(rule-based 匹配可能有误判/漏判) +- 配额数值需要人工设定(800/300 等),没有自动优化 +- Pass 1 选中的行可能 quality 较低,拉低整体数据质量 + +**适用场景**:正式实验的默认策略���综合效果预期最好 + +--- + +#### 策略 C:加权随机采样 ⭐⭐ + +**核心思路**:不硬性分配配额,而是给每行算一个综合权重,按权重做随机采样。 + +**具体做法**: +1. 对每行计算综合采样权重: + +``` +weight = quality # 1-5 分,主导项 + + 0.8 × mean(entity_track, wug, comps, + aoa, reading_flow, glue) # 任务分均值(1-3) + + 0.5 × (1 if 有BLiMP标注 else 0) # BLiMP 加分 + + 0.4 × (1 if 有EWoK标注 else 0) # EWoK 加分 + + 0.2 × (1 if child_directed else 0) # 儿童语言加分 + +权重范围:最低 ~1.8,最高 ~8.5 +``` + +2. 在每个数据源内部,用 **Gumbel-max trick** 做加权随机排列(权重高的行排前面的概率大,但不是确定性的) +3. 按排列顺序逐行取句,直到该源 cap 上限或总预算用完 + +**与策略 B 的关键区别**: + +| | 策略 B(配额) | 策略 C(加权) | +|---|---|---| +| **选择方式** | 确定性:先选配额,再选质量 | 随机性:权重高的**大概率**被选,但不保证 | +| **任务覆盖** | **硬保证**(每个子任务有最低句数) | **软倾向**(加分提高被选概率,但可能不够) | +| **多样性** | 可能聚集在高 quality + 高任务匹配的行 | Gumbel 噪声引入随机性,多样性更好 | +| **可复现性** | 完全确定 | 依赖随机种子 | + +**优点**: +- 综合考虑质量和多个任务维度,不需要手动设配额 +- Gumbel-max 引入的随机性增加了数据多样性 +- 权重系数(alpha/beta/gamma/delta)可调 + +**缺点**: +- 没有硬性的任务覆盖保证——极端情况下某些冷门任务可能覆盖不足 +- 权重系数需要调优 +- 随机采样导致每次运行结果略不同(虽然设了种子) + +**适用场景**:作为策略 B 的对照实验,测试"软权重"vs"硬配额"的效果差异 + +--- + +#### 策略 D:嵌入相似度匹配 ⭐⭐(最复杂) + +**核心思路**:不依赖规则匹配,直接用神经网络计算训练数据与评测数据的语义相似度。 + +**具体做法**: +1. 用 Embedding 模型(默认 Qwen3-Embedding-4B)对**评测句**和**候选训练句**编码为向量 +2. 对每条训练句,计算与 6 个评测任务的 top-K 平均余弦相似度 +3. 按任务权重(BLiMP 25%、EWoK 15%、ET 15%、COMPS 15%、AoA 15%、Reading 15%)加权聚合为一个 relevance 分数 +4. 最终分 = α × relevance + (1-α) × quality,按此分数排序取前 10M 词 + +``` +支持的 Embedding 模型: + - Qwen3-Embedding-4B/8B(默认,last-token pooling) + - sentence-transformers 模型(如 all-MiniLM-L6-v2、bge-*) + +编码结果缓存在 data/embeddings_cache/,避免重复计算 +``` + +**与前三种策略的本质区别**: +- 策略 A/B/C 都依赖 **Stage 5 的规则标注**(关键词 + 正则匹配)来判断"这行数据和哪个评测任务相关" +- 策略 D 用**神经网络的语义理解**来判断——即使一行文本不包含任何关键词,只要语义上与评测任务相似,也会被选中 + +**优点**: +- 能发现规则匹配遗漏的语义相关数据 +- 不需要手动设计匹配规则 +- 任务权重和 α 参数可灵活调整 + +**缺点**: +- 需要 GPU 做 embedding 推理(几十万行 × Qwen-4B 需要较长时间) +- 容易**过拟合公开评测集**——选出来的数据高度匹配公开任务,但对 hidden tasks 可能没帮助 +- 嵌入模型本身的语义理解可能有偏差 + +**适用场景**:探索性实验,验证语义匹配 vs 规则匹配的价值 + +--- + +#### 采样策略总结对比 + +| 策略 | 核心思路 | 实现复杂度 | 任务覆盖 | 数据多样性 | 过拟合风险 | 状态 | +|:----:|---------|:----------:|:--------:|:----------:|:----------:|:----:| +| A | 纯质量 | ⭐ | 无保证 | 低 | 低 | ❌ 未运行 | +| **B** | **任务配额+质量** | **⭐⭐** | **硬保证** | **中** | **低** | **✅ 已生成** | +| C | 加权随机 | ⭐⭐ | 软倾向 | 高 | 低 | ✅ 已生成 | +| D | 嵌入相似度 | ⭐⭐⭐ | 语义匹配 | 中 | 高 | ❌ 未运行 | + +**推荐**:策略 B 作为默认方案,策略 C 作为对照实验。策略 A 和 D 按需运行。 + +--- + +#### 数据排序:MATTR ⭐⭐ + +采样之后,训练数据的**读取顺序**也有讲究。MATTR(Moving Average Type-Token Ratio)排序是目前唯一被证明有效的排序方法: + +1. 计算每条文本的 MATTR 值(滑动窗口内不重复词占比) +2. 按 MATTR 从低到高排列(简单 → 复杂) +3. 训练时按此顺序读取 + +- **为什么有效?** 低 MATTR = 词汇重复多(简单文本),高 MATTR = 词汇丰富(复杂文本)。先学简单词汇模式打基础,再学丰富词汇扩展能力。 +- **预期影响**:在 RoBERTa + D_2024 上 +11pp(但需在 GPT-BERT 上验证) +- **注意**:传统的"按语法难度排序"的 Curriculum Learning 几乎无效(<1pp, p=0.055,见 Section I)。MATTR 是罕见的例外,因为它衡量的是词汇多样性而非语法难度。 +- **实现状态**:❌ 未实现,需要开发 MATTR 计算 + 排序脚本 + +--- + +### A4. 数据增强方法 ⭐⭐⭐ + +当"选数据"的优化空间用尽后,还可以从现有数据**生成新数据**。增强后的数据计入 10M 词预算。 + +4 种方法按"需不需要外部模型"分为两类: + +#### 需要外部 LLM 的方法 + +##### (1) Paraphrase 增强(同义改写)⭐⭐⭐ — ROI 最高 + +**做法**:用 Qwen3.5-9B 对高质量训练句生成同义改写。 + +``` +原句: "The cat sat on the mat." +改写: "A feline was resting on the rug." +``` + +**原理**:同一个意思用不同的词汇和句式表达,增加训练数据的**表层多样性**——模型看到同一语义的多种表达后,更容易学到深层的语言规律而非记住特定的表面形式。 + +**优点**: +- 所有增强方法中 ROI 最高:GLUE **+8pp** +- 生成质量可控(可按句长、格式筛选) +- Qwen3.5-9B 在审批列表上,合规 + +**缺点**: +- 需要 GPU 推理时间 +- LLM 改写可能丢失原文的某些微妙语法结构 +- 改写质量取决于 prompt 设计 + +**建议用量**:20-25% 预算(~2M 词),混合原文使用,不完全替换原文。 + +##### (2) Variation Sets(变体表达)⭐⭐ + +**做法**:同样用 Qwen3.5-9B 生成,但目标不同——不是"换一种说法",而是"保持核心语义但改变句式结构"。灵感来自儿童语言习得研究:看护者会用不同方式重复同一意思("Put the ball in the box." → "Can you put it there?" → "The ball goes in the box.")。 + +**与 Paraphrase 的区别**: + +| | Paraphrase | Variation Sets | +|---|---|---| +| **目标** | 换词换句式,保持语义 | 改变信息结构,保持核心意思 | +| **例子** | "猫坐在垫子上" → "一只猫在垫子上休息" | "猫坐在垫子上" → "垫子上坐着一只猫" → "你看到垫子上的猫了吗?" | +| **改变** | 词汇、短语 | 语序、语态、句型(陈述→疑问→被动) | +| **效果** | GLUE +8pp | BLiMP & GLUE 均有提升 | + +**优点**:2024 BabyLM Outstanding Paper,句式变化更丰富 +**缺点**:prompt 设计更复杂,生成质量不如 Paraphrase 稳定 + +#### 不需要外部 LLM 的方法 + +##### (3) RecombiText(统计重组)⭐⭐ + +**做法**: + +``` +1. 在训练语料上训练 GloVe 词向量 +2. 选取 2.5M 高质量"参考句" +3. 对参考句中的词,用 GloVe 找语义近邻词替换,生成新句子 + 原句: "The dog chased the cat." + 替换: "The puppy pursued the kitten." (dog→puppy, chased→pursued, cat→kitten) +4. 生成 7.5M 新句子 +5. 最终: 25% 原文 + 75% 增强 = 10M 词 +``` + +**原理**:用词向量的近邻关系做"词级别替换",产生语义相近但用词不同的新句子。本质是一种**基于词向量的数据增强**。 + +**优点**: +- 完全不需要 LLM,CPU 即可运行 +- Entity Tracking **+6~7pp**,BLiMP Supplement **+3pp** +- 无合规风险 + +**缺点**: +- 替换是逐词的,不感知句子整体语义——可能产生不通顺的句子("The dog chased the freedom.") +- 需要先训练高质量 GloVe 向量 +- 增强数据应放在参考句旁边(保持局部连贯性),不能随机散布 + +##### (4) Contrastive Decoding 合成 ⭐⭐ + +**做法**:利用**模型自身**不同训练阶段的能力差异来生成数据。 + +``` +1. 训练一个模型到 step 500 → 保存为"弱模型" (early checkpoint) +2. 继续训练到 step 10000 → 得到"强模型" (latest checkpoint) +3. 生成时:P_生成 = P_强模型 - P_弱模型 + → 放大"强模型学到但弱模型不会的"语言模式 + → 抑制"两者都会的"简单模式(如 "the", "is") +4. 用 P_生成 采样文本,作为新的训练数据 +``` + +**原理**:两个 checkpoint 的概率分布之差,反映了模型在这段训练中**新学到的知识**。用这个差值采样出的文本,集中了模型"刚学到但还不稳固"的语言模式,继续训练相当于"强化复习"。 + +**优点**: +- 完全**自给自足**——不需要外部 LLM,不需要外部数据 +- Entity Tracking **+7.3%**,WUG **+8.2%**,EWoK **+1.2%** +- 无合规风险 + +**缺点**: +- 需要额外的生成步骤(两次前向传播) +- 早期 checkpoint 的选择影响效果(太早 = 弱模型太弱,差异太大;太晚 = 差异太小) +- 最优混合比例需实验确定(推荐 70% 真实 + 30% 合成) + +#### 数据增强方法对比总表 + +| 方法 | 需要 LLM? | 预期效果 | 实现难度 | 合规风险 | 推荐优先级 | +|------|:----------:|---------|:--------:|:--------:|:----------:| +| **Paraphrase** | ✅ Qwen3.5-9B | GLUE **+8pp** | 低 | 无(审批过) | ⭐⭐⭐ 最优先 | +| **Variation Sets** | ✅ Qwen3.5-9B | BLiMP+GLUE 提升 | 中 | 无 | ⭐⭐ | +| **RecombiText** | ❌ GloVe | ET +6pp, Supp +3pp | 低 | 无 | ⭐⭐ | +| **Contrastive Decoding** | ❌ 自身模型 | ET +7pp, WUG +8pp | 中 | 无 | ⭐⭐ | +| **信息密集合成** | ✅ Qwen3.5-9B | 节省 token 预算 | 中 | 无 | ⭐⭐ | + +##### (5) 信息密集数据合成(Multi-Task Dense Sentences)⭐⭐ + +**做法**:用 Qwen3.5-9B 生成**每句话同时覆盖多个评测维度**的高信息密度训练句。 + +普通训练句可能只对一个任务有帮助: +``` +"The cat sat on the mat." → 只练了简单 SVO 句型(BLiMP 1 个子任务) +``` + +信息密集句同时覆盖多个任务: +``` +"The heavy iron ball that she had placed inside the wooden box was moved by him to the larger container." + ├── 主谓一致 + 关系从句嵌套 → BLiMP(2-3 个子任务) + ├── 物质属性(iron=heavy)→ EWoK 物理域 + ├── 空间关系(inside, moved to)→ EWoK 空间域 + ├── 代词回指(she, him)→ Entity Tracking + └── 被动语态(was moved by)→ BLiMP Supplement +``` + +**具体生成方式**: + +1. 从评测任务中提取**语言现象清单**(67 个 BLiMP 子任务 + 11 个 EWoK 域 + Entity Tracking 模式 + WUG 形态后缀) +2. 随机组合 3-5 个现象作为 prompt 约束 +3. 用 Qwen3.5-9B 生成一句自然的英语句子,要求同时包含这些现象 +4. 质量过滤:检查生成的句子是否真的包含了指定的现象(用 Stage 5 规则验证) + +**Prompt 示例**: +``` +Generate a natural English sentence (15-30 words) that simultaneously demonstrates: +1. Subject-verb agreement with an intervening prepositional phrase +2. A physical property (weight or temperature) +3. A spatial containment relationship +4. At least one pronoun coreference + +Sentence: +``` + +**为什么有效?** + +10M 词预算下,每个 token 都很宝贵。普通文本中,一句话平均只对 1-2 个评测维度有贡献。信息密集句让每句话覆盖 3-5 个维度,**等效于用同样的 token 预算学到了 2-3 倍的语言知识**。 + +**优点**: +- 极大提高 token 效率——同样 10M 词,覆盖的评测维度更多 +- 可以精确控制每个评测任务的覆盖量 +- 生成句仍是自然英语,不会引入人工化噪声 + +**缺点**: +- 需要仔细设计 prompt,确保生成句真的自然(不是生硬拼凑) +- 质量验证需要额外的过滤步骤 +- 过度优化可能导致训练数据"太整齐"——模型学到的是人工合成的模式而非自然语言 + +**建议用量**:1-2M 词(10-20% 预算),混合自然数据使用。 + +**实现状态**:❌ 未实现,需写生成脚本。 + +**建议组合**:Paraphrase 作为首选增强方法(ROI 最高),信息密集合成和 Contrastive Decoding 作为第二阶段补充。 + +--- + +### A5. 数据集构建方式总览 ⭐⭐⭐ + +构建一个 10M 词的训练集,本质上是在 **5 个维度**上做选择。每个维度的不同组合就构成了一种"数据集构建方式",可以作为独立实验来测试。 + +#### 5 个维度 + +| 维度 | 问题 | 可选项 | +|------|------|--------| +| **① 数据源** | 用哪些数据? | 14 个训练源 + 7 个评测集(共 120.5M 词),各分配多少比例 | +| **② 采样策略** | 怎么从 120M 里选 10M? | A 纯质量 / B 任务配额 / C 加权随机 / D 嵌入相似度 | +| **③ 数据增强** | 要不要生成新数据? | 无增强 / Paraphrase / Variation Sets / RecombiText / Contrastive Decoding / 信息密集合成 | +| **④ 评测数据** | 评测句怎么用? | 全量追加 / 混入采样池 / 不使用 | +| **⑤ 数据排序** | 训练时按什么顺序读? | 随机打乱 / MATTR(词汇多样性由简到繁) | + +#### 评测数据详情(7 个评测集,共 1.36M 词) + +BabyLM 2026 允许将评测数据用于训练。由 `stage7a_extract_eval.py` 自动提取,存放于 `data/7_eval_sentences/`。**只取正确句子,不取错误句子**: + +| 评测集 | 提取内容 | 词数 | 注意事项 | +|--------|---------|------|---------| +| BLiMP | `sentence_good`(65 子任务) | 420K | 不要加 `sentence_bad`(会污染训练) | +| BLiMP Supplement | `sentence_good`(5 子任务) | 69K | 同上 | +| EWoK | `Context1 + Target1` 拼接 | 69K | 不要加 Context2/Target2(错误推断) | +| COMPS | `comps_base` 正确陈述 | 67K | 不取 `comps_wugs`(含虚构词) | +| Entity Tracking | 场景描述(去掉末尾问句) | 620K | 文体人工化,需控制比例 | +| AoA | CDI 目标词的上下文句 | 116K | 自然语境句 | +| Reading Time | 205 个测试句 | 1.9K | 量极小 | +| **合计** | | **1.36M** | | + +> **风险提示**:比赛有 hidden tasks(COMPS, WUG_PAST, AoA),过度拟合公开评测集对 hidden tasks 无益。 + +#### 具体构建方式清单 + +以下是我们可以测试的**具体数据集构建方式**,每种方式是上述 5 个维度的一种组合: + +| # | 名称 | 数据源 | 采样 | 增强 | 评测数据 | 排序 | 预期特点 | +|:-:|------|--------|:----:|------|:--------:|:----:|---------| +| 1 | **冠军复刻** | BabyLM 33% + FineWeb 33% + Cosmopedia 34% | 等比 | 无 | 不用 | 随机 | Charpentier 2024 方案复刻 | +| 2 | **纯质量** | 全部 14 源 + 评测 | A | 无 | 追加 | 随机 | 最简单的基线 | +| 3 | **任务配额** | 全部 14 源 + 评测 | B | 无 | 追加 | 随机 | 保证各评测任务覆盖 | +| 4 | **加权随机** | 全部 14 源 + 评测 | C | 无 | 追加 | 随机 | 与 #3 对照,测试硬配额 vs 软权重 | +| 5 | **语义匹配** | 全部 14 源 + 评测 | D | 无 | 追加 | 随机 | 测试嵌入相似度的价值 | +| 6 | **配额+改写** | 全部 14 源 + 评测 | B | Paraphrase ~2M | 追加 | 随机 | 在 #3 基础上加 Qwen3.5 改写 | +| 7 | **配额+变体** | 全部 14 源 + 评测 | B | Variation Sets ~1M | 追加 | 随机 | 在 #3 基础上加句式变体 | +| 8 | **配额+统计增强** | 全部 14 源 + 评测 | B | RecombiText ~5M | 追加 | 随机 | 大比例统计增强(50% 真实 + 50%) | +| 9 | **配额+自合成** | 全部 14 源 + 评测 | B | CD 合成 ~3M | 追加 | 随机 | 训练中期自身 checkpoint 生成 | +| 10 | **配额+MATTR** | 全部 14 源 + 评测 | B | 无 | 追加 | MATTR | 在 #3 基础上加 MATTR 排序 | +| 11 | **评测混入池** | 全部 14 源 + 评测 | B | 无 | 混入采样 | 随机 | 评测数据参与采样而非无条件追加 | +| 12 | **无评测数据** | 全部 14 源 | B | 无 | 不用 | 随机 | 对照组:不使用评测数据 | +| 13 | **全量增强** | 全部 14 源 + 评测 | B | Paraphrase + VS | 追加 | MATTR | 组合最强配置 | + +> **说明**: +> - 以上各方式中"增强"数据计入 10M 词预算(如 #6 为 ~8M 采样 + ~2M 改写 = 10M) +> - Paraphrase 和 Variation Sets 使用 **Qwen3.5-9B** 生成(已下载,见 `models/qwen3.5-9b/`) +> - RecombiText 使用 GloVe 词向量做统计替换,不需要 LLM +> - Contrastive Decoding 使用训练中的模型自身 checkpoint,不需要外部模型 +> - 每种方式生成一个 `data/8_sample_X/train.txt`,用相同的模型配置训练后对比评测成绩 + +#### 当前实现状态 + +| 组件 | 代码状态 | 数据状态 | +|------|:--------:|:--------:| +| 采样 A 纯质量 | ✅ `stage8_sample_A.py` | ❌ 未运行 | +| **采样 B 任务配额** | ✅ `stage8_sample_B.py` | **✅ 已生成 9.99M 词** | +| **采样 C 加权随机** | ✅ `stage8_sample_C.py` | **✅ 已生成 9.99M 词** | +| 采样 D 嵌入相似度 | ✅ `stage8_sample_D.py` | ❌ 未运行 | +| 增强 Paraphrase | ✅ `stage9_paraphrase.py` | ❌ 未运行 | +| 增强 Variation Sets | ❌ 未写 | ❌ | +| 增强 RecombiText | ❌ 未写 | ❌ | +| 增强 Contrastive Decoding | ❌ 未写 | ❌ | +| 排序 MATTR | ❌ 未写 | ❌ | +| **评测数据提取** | ✅ `stage7a_extract_eval.py` | **✅ 已提取 1.36M 词** | + +#### 2025 Strict-Small 基线与冠军(参考) + +数据来源:`notes/babylm_leaderboard_2025.csv` + +| 模型 | TextAvg | BLiMP | GLUE | EWoK | ET | +|------|:-------:|:-----:|:----:|:----:|:--:| +| **amlm_hd_fail**(2025 冠军) | **45.4** | 59.0 | 57.7 | 56.0 | 41.3 | +| **MoEP**(2025 亚军) | **44.5** | 60.8 | 64.2 | 50.2 | 35.6 | +| Baseline GPT-BERT (causal) | 41.2 | 71.7 | 65.1 | 49.5 | 34.6 | +| Baseline GPT-2 | 37.4 | 66.4 | 55.9 | 49.9 | 13.9 | + +**关键发现**(基于 2025 论文分析): +- 2025 冠军使用 AMLM + DeBERTa-v2,靠 WUG_ADJ=78.1 和 COMPS=82.2 拉高 TextAvg +- MoEP 使用 MoE 稀疏路由,综合表现均衡 +- 数据混合对不同任务各有所长,混合使用效果最好(Charpentier & Samuel, 2024 消融实验) + +--- + +## B. 分词器(Tokenizer) + +### B0. BPE(Byte-Pair Encoding)⭐⭐ + +**这是什么?** + +BPE 是目前最主流的子词分词算法。核心思想: + +1. 从单个字符开始(初始词表 = 所有字符) +2. 反复找出现频率最高的相邻 token 对,合并为新 token +3. 重复直到词表达到目标大小(我们用 8192) + +例如:"playing" 可能被分为 "play" + "ing","unplayable" 分为 "un" + "play" + "able"。 + +**词表大小的选择** ⭐⭐(作为实验变量): + +词表大小(vocab_size)是一个重要但容易被忽略的超参数。对于 10M 词的小语料: + +| vocab_size | 优点 | 缺点 | 适用场景 | +|:----------:|------|------|---------| +| **4096** | 每个 token 出现更多次,嵌入学得更充分;嵌入层参数少 | Fertility 高(每个词切更多 token),序列更长 | 嵌入学习困难时 | +| **8192** | 均衡选择,BLiMP 和 GLUE 最佳 | — | **当前默认**,推荐 | +| **16384** | Fertility 低,序列短,训练快 | 低频 token 嵌入学不好 | 探索性实验 | +| **32768** | 接近标准 LLM 设置 | 10M 词根本不够学这么多 token 的嵌入 | 不推荐 | + +**核心权衡**:vocab_size 越大,每个 token 的平均出现次数越少。在只有 10M 词的约束下: +- vocab=4096 → 每个 token 平均出现 ~2400 次(够学) +- vocab=8192 → 每个 token 平均出现 ~1200 次(勉强够) +- vocab=16384 → 每个 token 平均出现 ~600 次(可能不够) + +**实验建议**:用 4096、8192、16384 三个值做对比实验。分词器训练脚本(`scripts/02_model/train_tokenizer.py`)已支持 `--vocab_size` 参数。 + +**注意**:改变 vocab_size 需要重新训练分词器,然后用新分词器重新分词所有训练数据。这意味着同一个 vocab_size 的所有实验应共享同一个分词器。 + +**预期影响**:BPE 8k 是 BLiMP 和 GLUE 的最佳选择。Fertility(每个词平均被切成几个 token)约 1.4。 + +--- + +### B1. Morfessor + BPE(形态感知分词)⭐⭐ + +> **一句话**:先用 Morfessor 把词拆成有语言学意义的"形态素",再在此基础上训练 BPE,使 BPE 永远不会跨越形态边界合并。 + +**这是什么?** + +先用 Morfessor 做形态学切分,再在切分结果上做 BPE。两步串联: + +``` +原文: "unhappiness" + ↓ Step 1: Morfessor 切分 +"un happi ness" ← 在形态素边界插入空格 + ↓ Step 2: BPE 编码(空格两边不合并) +["un", "happi", "ness"] ← 三个形态素完整保留 +``` + +关键约束:Morfessor 在形态素边界插入空格后,BPE 训练/编码时**不会跨越空格合并**。即使 `"happi"+"ness"` 在语料中出现了一万次,BPE 也不会把它们合成 `"happiness"`,因为空格已经把它们隔开了。 + +--- + +#### Morfessor 是什么?它的"模型"是怎么来的? + +Morfessor(Creutz & Lagus, 2005/2007, Helsinki 大学)是一个**无监督形态分析工具**。"无监督"意味着它**不需要人工标注**——不需要告诉它 "unhappiness = un + happy + ness",它能从大量词汇的统计规律中自动发现这些结构。 + +**Morfessor 是一个概率模型**,具体来说是基于**最小描述长度**(Minimum Description Length, MDL)原理的生成模型。 + +#### MDL 原理:一句话解释 + +> 最好的切分方案 = 用最少的信息量来描述整个词表。 + +这个"信息量"包括两部分: + +``` +总代价 = 词典代价(codebook)+ 语料代价(data) + "我需要记住多少个形态素?" "用这些形态素拼出所有词有多方便?" +``` + +这是一个**权衡**: + +| 极端方案 | 词典代价 | 语料代价 | 总代价 | +|---------|---------|---------|-------| +| **不切分**:每个词是一个独立单元 | 很高(词典里要记住每个词:"unhappiness", "happiness", "unhappy"...) | 很低(每个词一查就到) | 高 | +| **切到字母**:每个字符是一个单元 | 很低(词典只有 26 个字母) | 极高(拼一个词要好多步) | 高 | +| **恰到好处**:切成形态素 | 中等("un", "happi", "ness", "play", "ing"...) | 中等(每个词 2~3 步拼出) | **最低** ✓ | + +Morfessor 的训练就是**找到让总代价最小的切分方案**。 + +#### 通俗类比:搭乐高 + +想象你有一堆乐高作品(= 词汇表中的所有词),需要拆成零件放进收纳盒(= 词典),以便将来能重新拼出任何一个作品。 + +- **方案 A(不拆)**:每个作品原封不动放进盒子。收纳盒要非常大(词典代价高),但取出来直接用(语料代价低)。 +- **方案 B(拆到最小)**:全部拆成 1×1 的小积木。收纳盒很小(词典代价低),但重新拼一个作品要好久(语料代价高)。 +- **方案 C(拆成模块)**:拆成有意义的模块——轮子、车身、窗户。收纳盒大小适中,重新拼也方便。**这就是 Morfessor 在做的事**。 + +#### 训练过程:具体怎么找到最优切分? + +**输入**:训练语料中所有词的词频表。例如: + +``` +happiness 1200 次 +unhappiness 300 次 +happily 800 次 +unhappy 500 次 +playing 2000 次 +played 1500 次 +player 600 次 +darkness 400 次 +... +``` + +**算法**:Morfessor Baseline 使用**贪心搜索 + Viterbi 切分**: + +1. **初始化**:每个词不切分,词典 = 所有唯一词 +2. **迭代优化**:对每个词尝试所有可能的切分点,选使总代价下降最多的切分 + - 尝试 `happiness` → `happi + ness`?算算新增形态素 "happi" 和 "ness" 的词典代价 vs 能覆盖多少其他词(unhappiness, darkness...都能复用 "ness") + - 如果总代价下降 → 接受这个切分 + - 如果总代价上升 → 不切 +3. **收敛**:反复迭代直到没有切分能进一步降低总代价 + +**关键洞察**:为什么 "ness" 会被发现? + +``` +dark+ness (400次) +happi+ness (1200次) +sad+ness (300次) +kind+ness (500次) +``` + +如果把 "ness" 作为独立形态素放入词典,只需记一次 "ness"(词典代价 +1),但能被 4 个词复用(语料代价大幅降低)。这个"复用"带来的收益远超"多记一个形态素"的成本,所以 Morfessor 会发现 "-ness" 是一个形态素。 + +同理,"un-" 出现在 unhappiness, unhappy, unable, undo... 复用率很高,也会被提取出来。 + +而 "happ" 不会被提取为形态素——因为它几乎只出现在 "happi-" 系列词中,单独提取它的复用收益不够大。 + +#### corpusweight 参数的作用 + +```python +model = morfessor.BaselineModel(corpusweight=0.01) +``` + +`corpusweight` 控制上面公式中**语料代价的权重**: + +``` +总代价 = 词典代价 + corpusweight × 语料代价 +``` + +- **corpusweight 大**(如默认 1.0)→ 语料代价权重高 → 模型倾向于少切(保持词完整,减少拼接步数)→ 切分**保守** +- **corpusweight 小**(我们用 0.01)→ 语料代价权重低 → 模型倾向于多切(缩小词典,多复用形态素)→ 切分**激进** + +为什么我们要用 0.01 这么小的值?因为英语形态变化相对丰富(前缀 un-/re-/dis-,后缀 -ing/-ness/-ly/-ed/-er),我们希望 Morfessor 尽可能多地发现这些形态素。默认的 1.0 太保守,很多词不会被切分。 + +#### 推理时的切分:Viterbi 算法 + +训练完成后,对新词做切分用的是 **Viterbi 算法**(和 HMM 中的相同原理): + +给定一个词(如 "internationally"),在所有可能的切分方案中,选概率最高的那个: + +``` +"internationally" 的可能切分方案: + inter + national + ly → P = 0.35 + inter + nation + al + ly → P = 0.42 ← 最优 + in + ter + nation + al + ly → P = 0.08 + international + ly → P = 0.31 + ... + +Viterbi 选择: "inter + nation + al + ly" +``` + +概率来自训练阶段学到的形态素词典——每个形态素有一个先验概率(根据它在训练语料中被使用的频次)。 + +#### 过滤规则:防止假阳性 + +Morfessor 有时会过度切分,产生无意义的形态素。我们加了两个过滤规则: + +```python +MIN_MORPH_LEN = 2 # 形态素最短 2 个字符 +MIN_WORD_LEN = 3 # 短于 3 字符的词不切 +``` + +例子: +- `"sit"` → Morfessor 可能切成 `"s" + "it"` → 因为 `"s"` 长度 < 2,**拒绝切分**,保持 `"sit"` +- `"band"` → 可能切成 `"b" + "and"` → 同理拒绝 +- `"an"` → 长度 < 3,直接跳过不切 + +#### 完整流程图 + +``` +训练语料 (10M词) + │ + ▼ + ┌─────────────────┐ + │ 统计词频 │ "happiness": 1200, "unhappy": 500, ... + └────────┬────────┘ + ▼ + ┌─────────────────┐ + │ Morfessor 训练 │ MDL 优化:找让总代价最小的切分方案 + │ (corpusweight │ 发现: un-, -ness, -ing, -ly, play, happi, ... + │ =0.01) │ + └────────┬────────┘ + │ 输出: morfessor.bin (形态素词典 + 概率) + ▼ + ┌─────────────────┐ + │ 预切分训练文本 │ "unhappiness" → "un happi ness" + │ (Viterbi 切分) │ "playing" → "play ing" + └────────┬────────┘ + │ 输出: 预切分文本(形态素边界 = 空格) + ▼ + ┌─────────────────┐ + │ 训练 BPE │ 在预切分文本上训练(空格阻止跨边界合并) + │ (vocab=8192) │ + └────────┬────────┘ + │ 输出: tokenizer.json + ▼ + 训练模型时:每行文本先 Morfessor 预切分 → 再 BPE 编码 +``` + +--- + +**为什么有效?** + +不同分词器对不同评测任务影响巨大: + +| 分词器 | 擅长的任务 | 弱势任务 | +|--------|-----------|---------| +| BPE 8k | BLiMP, WUG_ADJ | Entity Tracking | +| Morfessor+BPE | Entity Tracking (+40pp!), WUG_PAST, EWoK | BLiMP Supplement | + +**Entity Tracking 为什么提升 40pp?** Entity Tracking 任务需要追踪代词(he/him/his, she/her, they/them)指代的实体。标准 BPE 可能把 "him" 和 "himself" 编码成完全不同的 token 序列,模型不知道它们指同一个人。Morfessor 会把 "himself" 切成 "him + self",保留了 "him" 这个核心,模型天然知道它们相关。 + +**WUG 形态任务为什么提升?** WUG 测试虚构词的形态变化(如 "wug" → "wugs")。Morfessor+BPE 训练的模型一直看到 "-s", "-ed", "-ing", "-ness" 作为独立 token,所以能把这些后缀规则泛化到从未见过的词上。 + +**预期影响**:Entity Tracking +40pp,WUG_PAST 提升,但 BLiMP Supplement 可能下降 + +**关键参数**:corpusweight=0.01(切分较激进),MIN_MORPH_LEN=2(过滤假阳性形态素) + +--- + +## C. 嵌入策略 + +嵌入(Embedding)是模型的第一层,负责把离散的 token ID 转换成连续的向量表示。模型后续所有的计算都基于这个向量,所以嵌入的质量直接影响模型能学到什么。嵌入策略在分词之后、模型架构之前确定——因为你需要先知道词表(分词器决定),才能构建嵌入层;而模型架构又建立在嵌入层之上。 + +### C0. 标准嵌入(Standard Embedding)— 默认基线 + +最基本的方式:维护一个 `[词表大小 × 向量维度]` 的查找表(`nn.Embedding`)。给定 token ID,直接查表返回对应向量。 + +- 词表大小 = 8192,向量维度 = 384 → 查找表有 8192 × 384 ≈ 3.1M 个参数 +- 初始化时每个向量是随机的,token 之间没有任何先验关系 +- 所有关系("play" 和 "playing" 相关)完全靠训练数据学习 + +这是所有实验的默认设置(experiments.csv 中 `embedding=standard`)。 + +--- + +### C1. N-hot 形态嵌入 ⭐⭐⭐ + +**这是什么?** + +在标准嵌入的基础上,额外注入一层信息:**BPE 词表中 token 之间的子串包含关系**。 + +例如,词表中有 "play", "plays", "playing", "player" 这些 token。"playing" 包含子串 "play", "ing", "pl" 等。N-hot 嵌入会构建一个二值特征矩阵,标记每对 token 之间是否存在子串关系,然后通过线性投影加到标准嵌入上。 + +``` +embedding(x) = standard_lookup(x) + linear_projection(substring_features[x]) +``` + +**为什么有效?** + +这个方法把**形态学知识**(词的结构信息)直接注入了嵌入层。例如模型不用从训练数据中慢慢学到 "playing" 和 "play" 有关——嵌入层直接告诉它了。 + +在 BabyLM 2026 中,WUG 形态任务是正式评测项。WUG 测试模型对虚构词的形态变化(如 "wug" → "wugs")的预测能力,N-hot 嵌入让模型天然具备这种形态推理能力。 + +**预期影响**: +- WUG Adj Nominalization:+60pp(!这是所有改进中影响最大的) +- GLUE:+4pp +- 实现代价极低(预计算特征矩阵 + 一个线性层) + +**类比**:就像告诉一个学英语的人"动词加 -ing 变进行时"这条规则,而不是让他自己从几千个例子中摸索出来。 + +--- + +### C2. FastText 初始化 ⭐⭐ + +**这是什么?** + +在训练模型前,先用 FastText 在训练语料上学习词向量,然后用这些词向量初始化模型的嵌入层(而不是随机初始化)。训练开始后,嵌入层继续被正常的反向传播更新——FastText 只影响起点。 + +FastText 参数:skip-gram 模式,window=5,训练 5 个 epoch,维度与模型 hidden_size 一致(384)。 + +**FastText / Word2Vec 的原理** + +核心思想:如果两个词经常出现在相似的上下文中,它们的向量就应该相近。 + +**Word2Vec 完整流程(Skip-gram + Negative Sampling)**: + +第一步:初始化两个嵌入矩阵(不是一个): +- `W_in [词表×维度]` — 中心词用这个 +- `W_out [词表×维度]` — 上下文词用这个 + +第二步:遍历语料,用滑动窗口生成训练样本。以 `"the cat sat on the mat"`、窗口=2 为例: + +``` +中心词 "sat" → 上下文 ["the", "cat", "on", "the"] +拆成正样本: (sat, the)=1, (sat, cat)=1, (sat, on)=1 +再随机抽负样本: (sat, banana)=0, (sat, algorithm)=0 +``` + +第三步:前向传播(以正样本 `(sat, cat)=1` 为例): + +``` +1. 查 W_in,取 "sat" 的向量: v_sat = [0.3, -0.1, 0.5, 0.2] +2. 查 W_out,取 "cat" 的向量: u_cat = [0.1, 0.4, -0.2, 0.3] +3. 算点积: score = v_sat · u_cat = 0.03 - 0.04 - 0.10 + 0.06 = -0.05 +4. 过 sigmoid: p = sigmoid(-0.05) = 0.4875 +5. 算 loss: loss = -log(p) = -log(0.4875) = 0.718 +``` + +第四步:反向传播,更新 `W_in["sat"]` 和 `W_out["cat"]`。 + +**为什么 loss 下降 = 向量变近?** 反过来推: + +``` +loss = -log(p) 要小 +→ p = sigmoid(score) 要大 +→ score = dot_product(v, u) 要大 +→ 两个向量方向要一致 +``` + +所以梯度下降最小化 loss 的过程,就是在**把正样本词对的向量往同一个方向拉**。负样本 label=0 时 `loss = -log(1-p)`,梯度则**把向量推向不同方向**。两股力的平衡结果:经常共现的词靠近,不共现的词远离。 + +训练完后,只取 `W_in` 作为最终词向量,`W_out` 扔掉。经常共享上下文的词("cat" 和 "dog" 都跟 "sat"、"pet" 搭配)向量自然相近。 + +**Word2Vec 处理词频的两个技巧**: + +1. **高频词下采样**:`"the"` 这种词出现太多但信息量低,按频率随机丢弃一部分(频率越高丢弃概率越大),防止模型把训练时间浪费在无信息的高频词对上。 + +2. **负采样的频率偏置**:负样本不是均匀随机抽的,而是按 `词频^0.75` 的概率抽。高频词更容易当负样本,但 0.75 次方让低频词也有机会被抽到。 + +**FastText 相比 Word2Vec 多了什么**: + +Word2Vec 把每个词当独立单元。FastText 额外考虑子词信息——一个词的向量 = 自己的向量 + 所有字符 n-gram 向量之和。好处是即使 "playing" 出现很少次,也能通过与 "play" 共享的子串获得不错的向量。 + +**可能的优化方向:GloVe 加权方案** + +Word2Vec/FastText 的 loss 对每个样本权重相同,频率信息只通过样本数量间接体现。另一种方案是 GloVe(Global Vectors),直接按共现频率加权 loss: + +``` +loss = f(共现次数) × (dot(向量_i, 向量_j) - log(共现次数))² + +f(x) = (x/上限)^0.75 如果 x < 上限��防止超高频词对主导) +f(x) = 1 如果 x ≥ 上限 +``` + +共现多的词对权重大,模型更认真学。可以考虑用 GloVe 替代 FastText 做初始化,或者对 FastText 的 loss 引入类似的频率加权,作为实验变量测试。 + +**为什么有效?** + +随机初始化意味着模型从零开始学每个词的含义。FastText 初始化给了模型一个"起跑优势": +- 语义相近的词("good" 和 "great")在向量空间中已经距离较近 +- 模型可以把节省下来的训练时间用于学习更高层的语言结构 + +在数据只有 10M 词的限制下,任何"免费的先验知识"都很宝贵。 + +**预期影响**:BLiMP +2~3pp,零额外 GPU 成本(CPU 几分钟完成) + +--- + +## D. 模型架构 + +> **"架构"在本文中的含义** +> +> "架构"不仅仅指用了 Transformer 还是 LSTM(骨架),还包括注意力模式和训练目标。三者搭配在一起才是一个完整的方案: +> +> | 层次 | 决定什么 | 例子 | +> |------|---------|------| +> | **骨架** | 计算方式 | Transformer / LSTM / Mamba | +> | **注意力模式** | 信息流向(能看到哪些词) | 因果(只看左边)/ 双向(左右都看) | +> | **训练目标** | 模型被要求学什么 | CLM / MLM / MNTP | +> +> 注意力模式和训练目标是**互相约束**的:因果注意力看不到右边的词,所以不能做 MLM(猜中间被遮的词需要看右边);双向注意力能看到答案,所以不能做 CLM(直接抄答案就不用学了)。因此选了骨架之后,注意力模式和训练目标的搭配方式基本就确定了。 + +### D0. GPT-2(纯解码器)— 默认基线 + +**这是什么?** + +最经典的 "从左到右" 语言模型(Causal LM)。给一段文字的开头,模型预测后续内容。 + +结构很简单: +1. 输入 token → 查找嵌入向量 +2. 经过 12 层 Transformer 块(每层有自注意力 + FFN) +3. 输出下一个 token 的概率分布 + +自注意力是**因果的**(causal):位置 i 只能看到位置 0~i 的信息,不能"偷看"后面的词。 + +**为什么作为基线?** + +GPT-2 是最"纯净"的 Transformer 架构——标准因果注意力 + 标准 CLM 训练目标,没有任何花哨的改进。所有其他架构(GPT-BERT 的双目标、ModernBERT 的双向、xLSTM 的矩阵记忆等)都是在此基础上的某种改进。用 GPT-2 作为基线,能最清晰地看出每种改进的边际贡献。 + +**预期影响**:2025 基线 BLiMP=66.4%,TextAvg=37.4% + +--- + +### D1. GPT-BERT(双目标混合架构)⭐⭐⭐ + +**这是什么?** + +先介绍两种经典的语言模型训练方式: +- **从左到右生成**(Causal LM,简称 CLM):给定前面的词,预测下一个词。GPT 系列就是这样。注意力是**因果的**——每个位置只能看到它前面的词。 +- **填空**(Masked LM,简称 MLM):把句子中一些词遮住,让模型猜被遮住的词。BERT 就是这样。注意力是**双向的**——每个位置可以看到整个句子。 + +GPT-BERT 的设计巧妙之处在于:**用 GPT 的因果注意力(只看前面),但同时训练两种任务**: + +- **CLM(Causal Language Modeling)**:标准的从左到右预测下一个词,和 GPT 完全一样。 +- **MNTP(Masked Next Token Prediction)**:随机遮住一些词,模型**跳过被遮住的词**,只根据前面未被遮住的词来预测下一个词。 + +注意 MNTP ≠ MLM。区别在于: +- MLM(BERT)用双向注意力,直接预测被遮住的那个词本身 +- MNTP(GPT-BERT)仍然用因果注意力(只看前面),预测的是下一个词,只是输入中有些词被遮住了 + +MNTP 的效果类似于 MLM——被遮住的上下文迫使模型依靠更广泛的语言理解来预测,而不是简单地"抄"旁边的词。但因为用了因果注意力,同一个模型也能直接做生成(CLM)。 + +**为什么有效?** + +BabyLM 的评测包含多种类型: +- BLiMP(语法判断)→ 需要双向理解能力 +- Entity Tracking(实体追踪)→ 需要因果推理能力 +- GLUE(语言理解)→ 需要微调能力 + +单一目标的模型只擅长一类任务,GPT-BERT 两种都练,所以在综合分数上连续多年领先。 + +**预期影响**:2025 Strict-Small 基线 BLiMP=71.7%,TextAvg=41.2%(比 D0 GPT-2 基线 BLiMP 高 ~5pp) + +**类比**:就像一个学生同时练阅读理解和写作,而不是只练一种。 + +--- + +### D2. ModernBERT(现代双向编码器) + +**这是什么?** + +BERT 的现代改进版。和原始 BERT 的关键区别: +- **位置编码**:用 RoPE(旋转位置编码)代替学习式绝对位置嵌入。RoPE 将位置信息编码为向量的旋转角度,让模型能更好地理解词与词之间的相对距离。 +- **FFN**:用 GeGLU 代替 GELU。GeGLU 在前馈网络中加了一个门控机制,让网络学会"选择性地让信息通过"。 +- **归一化**:Pre-norm(先归一化再做注意力),比 BERT 原版的 Post-norm 训练更稳定。 + +**为什么要测试它?** + +ModernBERT 是纯双向的,在理解类任务(BLiMP、GLUE)上可能更强,但不能做因果生成任务(Entity Tracking 可能弱)。 + +**预期影响**:BLiMP 可能与 GPT-BERT 持平,但 Entity Tracking 可能偏低 + +--- + +### D3. xLSTM / mLSTM(矩阵记忆 LSTM)⭐⭐ + +**这是什么?** + +这是一个 **非 Transformer** 的架构。传统 LSTM 用一维向量作为记忆,mLSTM 改用**矩阵**作为记忆,大幅增加了信息容量。 + +核心创新是**指数门控**: +- 传统 LSTM 的门控值在 0~1 之间(sigmoid),记忆更新很"温和" +- mLSTM 用指数函数,输入门可以远大于 1,实现"强写入" + +另一个特性是 mLSTM 可以完全并行化(不像传统 LSTM 必须逐步计算),训练速度接近 Transformer。 + +**为什么有效?** + +在 BabyLM 2025 中,mLSTM 在 Entity Tracking 上从 18% 飙升到 40%(+22pp),GLUE 也显著更强。原因是 mLSTM 的序列建模能力天然适合追踪实体状态变化。 + +**代价**:BLiMP 略低于 GPT-BERT(约 -2pp),因为 BLiMP 主要测静态语法知识,Transformer 的全局注意力更擅长。 + +**预期影响**:Entity Tracking +20pp,GLUE +14pp,BLiMP -2pp + +**可选附加改进:ShortConv** + +在 Q/K 投影之前加一个 1D depthwise convolution(深度可分离卷积,kernel_size=3-5),让每个 token 在计算注意力之前先融合相邻 token 的信息。论文中被评价为"性价比最高的附加改进",几乎不增加参数量但能稳定训练。 + +--- + +### D4. RTD / ELECTRA(替换 Token 检测)⭐⭐ + +**这是什么?** + +一种"生成器-判别器"训练架构(注意:不是 GAN): + +1. **生成器**(小模型,~3.5M 参数):做标准 MLM,预测被遮住的词 +2. 用生成器的预测替换原文中被遮住的位置 +3. **判别器**(大模型,~20M 参数):对每个位置判断"这个词是原文还是被替换过的?" + +判别器在**所有位置**上训练(不只是被遮住的位置),所以每条训练样本的利用率是 BERT 的 ~6 倍(BERT 只在 15% 的掩码位置学习)。 + +**为什么对 BabyLM 有效?** + +Strict-Small 赛道只有 10M 词,数据极其稀缺。RTD/ELECTRA 的"全位置训练"让每条数据贡献更多梯度,相当于放大了有效数据量。 + +**预期影响**:小数据场景下可能优于标准 MLM,需实验验证 + +--- + +### D5. MoE 稀疏路由(Mixture of Experts)⭐⭐ + +**这是什么?** + +标准 Transformer 的每一层有一个 FFN(前馈网络)。MoE 将这个 FFN 替换为**多个小型专家网络**,加一个路由网络(Router)决定每个 token 走哪些专家,每个 token 只激活其中少数几个。 + +例如:64 个专家,每个 token 只激活 6 个。这样总参数量很大,但每个 token 的计算量不变。 + +**MoE 不是独立架构,而是一个可叠加的模块**。它可以加在任何基于 Transformer 的架构上: + +| 基础架构 | + MoE 后 | 说明 | +|---------|---------|------| +| GPT-BERT | GPT-BERT-MoE | 每层 FFN → MoE,其他不变 | +| GPT-2 | GPT-2-MoE | 同上 | +| ModernBERT | ModernBERT-MoE | GeGLU FFN → MoE | +| xLSTM | xLSTM-MoE | SwiGLU FFN → MoE | +| RTD/ELECTRA | RTD-MoE | 判别器的 FFN → MoE | + +**实验计划**:需要在 5 种基础架构上都测试 +MoE 的效果。代码已实现(`models/moe.py`),通过 `use_moe: true` 启用。 + +**为什么有效?** + +2025 年 MoEP 方案用这个方法拿了综合冠军(Macro 平均 42.3%)。稀疏路由让模型学会对不同类型的语言输入使用不同的"专家",类似于人脑对不同语言功能有不同区域处理。 + +**关键实现细节**: +- 2025 论文发现在小模型上**不要用辅助 loss 做负载均衡**(大模型常用的做法),会导致训练不稳定。改用无辅助 loss 的均衡算法(约 10 行代码) +- **线性专家优于 SwiGLU 专家**:参数更少但效果更好 +- 需要注意**参数量预算**:MoE 总参数量会增大,但 BabyLM 限制 ~30M 参数,需要调整每个专家的大小使总量合规 + +**风险**:实现复杂;最优 checkpoint 在 epoch 3(不是 10),过拟合严重。 + +**预期影响**:高风险高收益,综合分可能最强。当前项目优先级提升为 ⭐⭐,5 种架构都要测。 + +**实现状态**:✅ 已实现(`models/moe.py`)。通过 `config.yaml` 中 `model.use_moe: true` 启用,可叠加到全部 5 种架构。默认配置 32 experts, top-4, expert_size=48,GPT-BERT 总参数 24.7M(≤30M)。 + +--- + +### D6. Attention Residuals(注意力残差)⭐ — 2026,Moonshot AI / Kimi Team + +> **一句话**:用跨层注意力替换固定的残差累加,让每层自己决定"从之前哪些层获取多少信息"。 + +**这是什么?** + +标准 Transformer 的残差连接是最"无脑"的设计之一——每层的输出直接加到残差流上,权重恒为 1: + +``` +标准残差: h_l = h_{l-1} + Layer_l(h_{l-1}) # 每层等权累加,不可选择 +``` + +Moonshot AI 指出这带来三个问题: +1. **无选择性**:所有层接收到的是同一个均匀累加的残差流,无法选择性地获取某一层的信息 +2. **信息不可恢复**:一旦信息被混入残差流,就无法被后续层选择性地提取出来 +3. **输出膨胀**:随着层数加深,残差流幅度不断增长,单层输出必须越来越大才能"被听到" + +**Attention Residuals(AttnRes)** 的做法:用 softmax 注意力替换固定累加—— + +``` +AttnRes: h_l = Σ_i softmax(q_l · k_i) × v_i # 对之前所有层做注意力 + 其中 q_l = 每层学到的伪查询向量(不依赖输入) + k_i, v_i = 第 i 层输出经过 RMSNorm +``` + +每层有一个可学习的"伪查询"向量(pseudo-query),用它对之前所有层的输出做注意力。这样每层可以**自适应地选择**要从哪些层获取信息,而不是被迫接受等权累加。 + +**通俗类比**: + +- **标准残差** = 把所有课程的笔记摞在一起,每节课只能看最上面的那一摞(包含了之前所有笔记的混合) +- **AttnRes** = 每节课可以翻回去,选择性地重新看某几节课的笔记,重点看最相关的 + +**Block AttnRes(实用优化)** + +对每一层都做跨层注意力的开销是 O(Ld)(L=层数,d=维度),太大了。Block AttnRes 把 L 层分成 N 个 block(如 48 层分 8 block,每 block 6 层),block 内部正常残差累加,**block 之间**做注意力。复杂度降到 O(Nd)。 + +``` +例:48 层模型,分 8 个 block + Block 1 (层1-6): 内部正常残差累加 → 输出 b1 + Block 2 (层7-12): 内部正常残差累加 → 输出 b2 + ... + Block 8 (层43-48): 对 [b1, b2, ..., b7] 做注意力选择 + → 不是等权看所有 block,而是重点看最相关的 +``` + +**效果**: + +- 同等性能下节省约 **25% 计算量**(等效于用更少的层达到相同 loss) +- MMLU 73.5 → 74.6,GPQA-Diamond 36.9 → 44.4,Math 53.5 → 57.1 +- 已集成到 Kimi Linear 模型(48B MoE) + +**对我们项目的适用性分析**: + +AttnRes 主要解决的是**深层模型**中残差流膨胀导致的层效率下降问题。我们的模型只有 **12 层**,层数稀释效应远不如 48 层模型严重。而且 Block AttnRes 在只有 12 层的情况下分 block 意义不大(比如分 4 block 每 block 3 层,注意力只有 4 个选项)。 + +- **值得尝试**:实现成本低,可以作为架构改进实验 +- **优先级低**:预期收益在 12 层小模型上有限,排在 MoE(C6)和其他改进之后 + +**实现状态**:✅ 已实现(`models/attn_res.py`)。通过 `config.yaml` 中 `model.use_attn_res: true` 启用,支持 GPT-BERT/GPT-2/ModernBERT/xLSTM(RTD 不支持)。默认 4 blocks,仅增加 3K 参数。可与 MoE 组合使用。 + +**引用**:Kimi Team (2026). Attention Residuals. arXiv:2603.15031. + +--- + +## E. 训练目标与掩码策略 + +### E0. 标准均匀掩码 — 默认基线 + +每个 token 以**相同概率**(默认 15%)被随机遮住,不考虑 token 的频率、难度或模型的当前能力。这是 BERT 原论文的标准做法。 + +配置:`masking.type: standard`,`masking.mask_ratio: 0.15`(固定,不衰减) + +这是所有掩码策略实验的对照组。E1-E3 都是在此基础上的改进。 + +--- + +### E1. AMLM(Adaptive Masked Language Modeling)⭐⭐⭐ + +**这是什么?** + +标准 MLM 训练中,每个词被遮住的概率是均匀的(例如都是 15%)。AMLM 的核心思想是:**模型已经学会的词少遮,还没学会的词多遮**。 + +具体流程: +1. 训练若干步后,统计每个 token 被预测正确的比例(准确率) +2. 准确率高的 token → 降低掩码概率(已经学会了,不用反复练) +3. 准确率低的 token → 提高掩码概率(还不会,多练) +4. 为了避免极端情况,用一个 λ 参数(默认 0.2)控制"自适应"的程度,80% 还是均匀的 + +公式: +``` +score[t] = (正确次数 + 0.5) / (总次数 + 1.0) # Laplace 平滑 +raw_prob[t] = 1 - score[t] # 不准的 → 掩码多 +mask_prob = 0.2 × normalized(raw_prob) + 0.8 × uniform +``` + +**为什么有效?** + +均匀掩码会浪费大量训练时间在模型已经学会的常见词(如 "the", "is")上。AMLM 把有限的训练信号集中在真正需要学习的词上,效率大幅提高。 + +这就像一个学生做题:把已经会做的题跳过,专攻不会的题,学习效率自然更高。 + +**预期影响**:2025 年 NLP 赛道冠军方法。BLiMP +3~5pp,全面提升。 + +--- + +### E2. 掩码率衰减(Decaying Mask Ratio)⭐⭐ + +**这是什么?** + +训练开始时用较高的掩码率(40%),随训练进行线性衰减到较低掩码率(15%)。 + +**为什么有效?** + +- **训练早期**(模型还很弱):高掩码率 = 每条数据有更多位置需要预测 = 更多梯度信号 = 学得更��� +- **训练后期**(模型已经不错了):低掩码率 = 模型能看到更多上下文 = 预测更精细 + +类比:学英语时,刚开始完形填空空得多(强迫你猜),后来空得少(让你做精细理解)。 + +**预期影响**:+1~2pp + +**与 AMLM 的配合**:AMLM 和掩码率衰减是**组合使用**的。AMLM 决定"哪些词掩码更多",衰减决定"总体掩码多少"。2025 年冠军方案就是同时使用两者:AMLM + 40%→15% 衰减。 + +**实现**:在训练代码中每个 epoch 线性插值掩码率即可,一行代码搞定。 + +--- + +### E3. 频率感知掩码(Frequency-Informed Masking)⭐ + +**这是什么?** + +根据 token 在训练集中的**出现频率**分配掩码概率:低频词(通常语义丰富)获得更高的掩码概率,高频词(如 "the", "a")获得更低的掩码概率。 + +**为什么有效?** + +低频词往往是内容词(名词、动词、形容词),携带句子的核心语义。高频词是功能词(冠词、介词),模型很快就能学会。把掩码集中在低频词上,让模型花更多时间学习语义。 + +**与 AMLM 的区别**:频率感知掩码是**静态的**(基于语料统计,训练前确定),AMLM 是**动态的**(基于模型当前能力,训练中不断调整)。两者思路互补,可以叠加。 + +**预期影响**:BLiMP Supplement +1pp,Adj Nominalization +7.5pp + +--- + +### E4. MNTP:CLM 训练比例 ⭐⭐ + +**这是什么?** + +GPT-BERT 交替做两种任务(MNTP 填空 和 CLM 生成),比例是一个重要参数: +- **15:1**(默认)= 每 16 个 batch 中 15 个做填空、1 个做生成 → "填空为主" +- **1:1** = 一半一半 → "均衡" +- **1:15** = 生成为主 + +**为什么要调?** + +不同比例对不同评测任务影响很大: +- 15:1(填空为主)→ WUG 形态任务更好(+12pp),但 Entity Tracking 训练不稳定 +- 1:1(均衡)→ 最稳定,Entity Tracking 不会崩溃 +- 1:7 或 1:15 → 某些情况 Entity Tracking 完全崩溃(降到随机水平) + +**推荐**:1:1 是最安全的选择;如果 WUG 形态任务权重高,可以尝试 15:1。 + +--- + +### E5. 反向课程学习 MTP(Reverse Curriculum Multi-Token Prediction)⭐⭐ + +**这是什么?** + +标准语言模型每次只预测下一个 token(k=1)。Multi-Token Prediction(MTP)让模型同时预测下面的 k 个 token。"反向课程学习"是指训练策略从难到易: + +- **前 5 个 epoch**:k=2(同时预测下 2 个 token)→ 更难的任务 +- **后 5 个 epoch**:k=1(回到标准的单 token 预测)→ 更简单的任务 + +**为什么有效?** + +同时预测多个 token 迫使模型在隐藏层中编码更长范围的信息("接下来要发生什么"),类似于让学生先做难题打基础,再做简单题巩固。 + +这对追踪长距离依赖关系特别有帮助。例如 Entity Tracking 需要记住"球在哪个盒子里"这种跨多句的信息,MTP 训练让模型习惯于"向前看"。 + +**预期影响**:Entity Tracking +5pp,WUG Adj Nominalization +6.5pp。额外计算代价约 +10%。 + +**适用范围**:主要用于 decoder 架构(GPT-2)或 GPT-BERT 的 CLM 部分。纯 encoder(BERT)不适用。 + +--- + +## F. 优化器 + +> **阅读指南**:本节按时间顺序排列(从 2014 年的 Adam 到 2024 年的 Muon),帮助理解优化器的演化脉络。每个后来者都在前者的基础上解决了某个具体问题。 + +### F0. Adam(自适应矩估计)— 2014,Kingma & Ba + +> **一句话**:为每个参数自动调节学习率——梯度大的参数走小步,梯度小的参数走大步。 + +**这是什么?** + +最经典的自适应学习率优化器,几乎是深度学习优化器家族的"起点"。名字来自 **Ada**ptive **M**oment estimation(自适应矩估计)。 + +在 Adam 之前,最常用的是 SGD(随机梯度下降)。SGD 的问题是:所有参数共用一个学习率。但神经网络中不同参数的梯度差异巨大——有的参数梯度经常是 0.001,有的经常是 10。用同一个学习率,要么前者更新太慢,要么后者更新太快。手动给不同层设不同学习率?参数上百万,不可能。 + +Adam 的解决方案:让每个参数**自己**记住自己最近的梯度情况,自动调整步长。 + +**核心机制:两个"记忆"** + +Adam 为**每个参数**维护两个统计量("矩"): +- **一阶矩 m(动量/Momentum)**:梯度的指数移动平均 → 记住"最近一直在往哪个方向走" +- **二阶矩 v(方差/Variance)**:梯度**平方**的指数移动平均 → 记住"最近梯度有多大" + +参数更新公式: + +``` +m = β₁ × m + (1-β₁) × gradient # 更新动量(方向) +v = β₂ × v + (1-β₂) × gradient² # 更新方差(幅度) +param -= lr × m / (√v + ε) # 自适应步长 +``` + +**通俗类比:山坡上滚球** + +想象你蒙着眼睛在一片山地里找最低点,每一步你只能感受到脚下���坡度(= 梯度)。 + +- **没有 m(纯 SGD)**:每一步只看脚下的坡度就走。问题是如果地面坑坑洼洼,你会左右摇摆,走不了直线。 +- **有 m(动量)**:你记住最近几步的方向,综合起来决定下一步。像一个有惯性的球——即使某一步坡度突然变了,球不会立即改方向,而是平滑地转弯。 +- **有 v(自适应)**:你还记住最近几步坡度有多陡。如果某个方向一直很陡(梯度大),你就走**小步**,避免冲过山谷;如果某个方向很平缓(梯度小),你就走**大步**,加速通过平原。 + +**β₁ 和 β₂ 怎么理解?** + +- β₁=0.9 → 动量大约记住最近 **10 步**的梯度趋势(半衰期 ≈ 1/(1-0.9) = 10) +- β₂=0.98 → 方差大约记住最近 **50 步**的梯度幅度(半衰期 ≈ 1/(1-0.98) = 50) + +为什么 β₂ 比 β₁ 大?因为方差需要更长的历史才能稳定估计(短期的梯度大小波动不应该立刻改变步长)。 + +**数值例子:走两步看看** + +假设某个参数的学习率 lr=0.001,β₁=0.9,β₂=0.99,初始 m=0, v=0。 + +``` +第1步:gradient = 2.0 + m = 0.9×0 + 0.1×2.0 = 0.2 (开始记住"梯度往正方向") + v = 0.99×0 + 0.01×4.0 = 0.04 (开始记住"梯度大约是2") + 更新量 = 0.001 × 0.2 / (√0.04 + 1e-8) = 0.001 × 0.2 / 0.2 = 0.001 + +第2步:gradient = -0.5(梯度突然反向且变小) + m = 0.9×0.2 + 0.1×(-0.5) = 0.13 (动量还记着上一步,没有完全反转) + v = 0.99×0.04 + 0.01×0.25 = 0.042 (方差变化很小,历史记忆深) + 更新量 = 0.001 × 0.13 / (√0.042 + 1e-8) = 0.001 × 0.13 / 0.205 = 0.000634 +``` + +注意第 2 步:虽然当前梯度是 -0.5,但因为动量记住了上一步的 +2.0,实际更新方向仍然是正的(m=0.13)。这就是动量的"惯性"效果——不会被单步噪声带偏。 + +**偏差校正(Bias Correction)** + +上面例子中有个问题:第 1 步的 m=0.2,但真实梯度是 2.0,m 被严重低估了(因为初始值是 0)。Adam 用偏差校正修复: + +``` +m_hat = m / (1 - β₁^t) # 第1步: 0.2 / (1-0.9) = 2.0 ← 修正回真实值 +v_hat = v / (1 - β₂^t) # 第1步: 0.04 / (1-0.99) = 4.0 +``` + +训练越久,β^t 越接近 0,校正量越小——因为累积了足够多的历史,初始零值的影响被稀释了。 + +**Adam 的缺陷:正则化被削弱** + +Adam 中加 weight decay 等价于在梯度上加 L2 项 `gradient += wd × param`,然后这个加了正则项的梯度被 `1/√v` 缩放。 + +问题来了:梯度大的参数 v 也大,`1/√v` 就小,正则化力度被**缩小**了。但恰恰是那些梯度大(最活跃)的参数最需要正则化!这就像给最调皮的学生最少的管教——适得其反。这正是 AdamW 要解决的问题。 + +**在本项目中的角色**:作为对照基线。一般不推荐在 Transformer 训练中直接使用,建议至少用 AdamW。 + +--- + +### F1. AdamW(解耦权重衰减)⭐ — 2017/2019,Loshchilov & Hutter + +> **一句话**:Adam 的"修复版"——把 weight decay 从自适应缩放中拿出来,让正则化对所有参数公平生效。 + +**这是什么?** + +AdamW 和 Adam 的区别**只有一行代码**,但这一行的影响是巨大的。 + +先理解 weight decay(权重衰减):它是一种正则化手段,每次更新参数后,把参数值稍微缩小一点(乘以一个 < 1 的系数,如 0.999)。目的是防止参数值越来越大导致过拟合。 + +问题在于 Adam 中 weight decay 的**位置**: + +``` +Adam: gradient += wd × param # 先把正则项混入梯度 + param -= lr × adam_update(gradient) # 再做自适应更新(正则被 1/√v 缩放) + +AdamW: param -= lr × adam_update(gradient) # 先做正常的自适应更新(不含正则) + param -= lr × wd × param # 再单独做权重衰减(不受 1/√v 影响) +``` + +**通俗类比:税收的公平性** + +想象 weight decay 是"税",每个参数是"市民",v(梯度幅度)是"收入"。 + +- **Adam 的做法**(耦合):先把税加进收入一起算,再按收入比例扣。结果是**高收入者(v 大)交的税反而按比例更少**——因为税款被 `1/√v` 缩放了。最需要被约束(正则化)的大参数,反而逃税了。 +- **AdamW 的做法**(解耦):收入归收入,税归税,分开算。每个参数按固定税率缴税,不受收入高低影响。**公平!** + +**数值例子:同一个参数,两种方式对比** + +假设 param=10.0, gradient=0.5, wd=0.01, lr=0.001, √v=5.0 + +``` +Adam (耦合): + gradient' = 0.5 + 0.01 × 10.0 = 0.6 # 正则项混入梯度 + update = lr × gradient' / √v = 0.001 × 0.6 / 5.0 = 0.00012 + 其中正则部分 = 0.001 × (0.01×10) / 5.0 = 0.00002 ← 被 1/√v 缩小了! + +AdamW (解耦): + update = lr × gradient / √v = 0.001 × 0.5 / 5.0 = 0.0001 # 梯度部分不含正则 + decay = lr × wd × param = 0.001 × 0.01 × 10.0 = 0.0001 # 正则独立计算 + 总更新 = 0.0001 + 0.0001 = 0.0002 + 其中正则部分 = 0.0001 ← 不受 √v 影响,力度是 Adam 的 5 倍! +``` + +对于这个 √v=5 的活跃参数,Adam 的正则化力度只有 AdamW 的 **1/5**。参数越活跃(v 越大),差距越大。 + +**为什么重要?** + +Loshchilov & Hutter 在论文中证明:经典 Adam + L2 正则化在 Transformer 上效果远不如 AdamW + 解耦 weight decay。修复后,AdamW 成为了**几乎所有现代 Transformer 训练的标配**——BERT、GPT-2、RoBERTa、LLaMA 等模型都默认使用 AdamW。 + +**在本项目中的角色**:GPT-2、ModernBERT、xLSTM 的默认优化器。当不确定用什么优化器时,AdamW 是最安全的选择。 + +--- + +### F2. LAMB(逐层自适应矩)⭐⭐ — 2019,You et al. + +> **一句话**:在 AdamW 基础上加了"逐层缩放"——让每一层的更新幅度与该层权重的大小成正比,大 batch 训练更稳定。 + +**这是什么?** + +LAMB(**L**ayer-wise **A**daptive **M**oments for **B**atch training)在 AdamW 的更新公式后面加了一个**信赖域比率**(Trust Ratio): + +``` +AdamW: param -= lr × adam_update +LAMB: param -= lr × (‖param_layer‖ / ‖adam_update_layer‖) × adam_update + └─────────── trust ratio ──────────┘ +``` + +这里的关键是 **layer**——不是对单个参数做,而是对**整层**的参数统一做。把一层所有参数的权重向量看成一个整体,算出"这层的参数有多大"和"优化器建议的更新有多大",两者一除得到缩放系数。 + +**通俗类比:给不同体型的人配药** + +AdamW 像是一个药剂师,根据每个器官(参数)的病情(梯度)开药量。但它不考虑病人的体型——给 80kg 的成人和 30kg 的儿童开同样的剂量。 + +LAMB 多了一步:看看**整个人**(整层参数)的体型(参数范数),按体重调整总药量。体型大的人(参数范数大的层)可以承受更大的更新,体型小的人需要减量。 + +**为什么需要"逐层"?** + +Transformer 不同层的参数尺度差异巨大: + +``` +嵌入层 (Layer 0): ‖param‖ = 150.0, ‖update‖ = 0.3 → 层很"重",更新很小 +中间层 (Layer 6): ‖param‖ = 12.0, ‖update‖ = 0.5 → 比较均衡 +输出层 (Layer 11): ‖param‖ = 5.0, ‖update‖ = 2.0 → 层很"轻",更新很大 +``` + +AdamW 对这三层用同一个学习率,结果: +- 嵌入层:更新/参数 = 0.3/150 = 0.2% → 更新太慢 +- 输出层:更新/参数 = 2.0/5 = 40% → 更新太猛 + +LAMB 用 trust ratio 归一化后: +- 嵌入层:trust = 150/0.3 = 500 → 放大更新 +- 输出层:trust = 5/2.0 = 2.5 → 缩小更新 + +每层的实际更新幅度变得与该层参数大小成正比,**所有层以相同的"相对比例"更新**。 + +**为什么在大 batch 下更重要?** + +大 batch(如有效 batch 512)的梯度估计更准确,但方差更小。问题是不同层方差缩小的程度不一样——有的层几乎不变,有的层方差骤降。这放大了层间的尺度差异。LAMB 的逐层归一化刚好抵消了这个效应。 + +Google 用 LAMB 在 76 分钟内训完了 BERT(batch size 高达 64K),而 AdamW 在这么大的 batch 下训练直接崩溃。 + +**预期影响**:GPT-BERT 官方使用 LAMB + lr=1.41e-2,在大 batch 场景下比 AdamW 更稳定。 + +**在本项目中的角色**:GPT-BERT 的默认优化器。 + +--- + +### F3. FORGETTER(周期性重置优化器状态)⭐⭐⭐ — 2025,Yamamoto & Miura + +> **一句话**:每个 epoch 结束时"清空优化器的记忆"(但保留模型学到的知识),让优化器重新开始探索,打破收敛天花板。 + +**这是什么?** + +FORGETTER 不是一个新优化器,而是一种**训练策略**——可以搭配任何 Adam 系列优化器使用。核心操作极其简单:每个 epoch 结束后,**重置优化器的一阶矩 m 和二阶矩 v**,但保留模型权重不变。 + +```python +# 每个 epoch 结束时: +for param in optimizer.state: + optimizer.state[param] = {} # 清空 m 和 v,从零开始 +# 模型权重 model.parameters() 不变 +``` + +**区分"模型的知识"和"优化器的记忆"** + +这是理解 FORGETTER 的关键——模型和优化器储存的是**完全不同的东西**: + +| | 模型权重 (parameters) | 优化器状态 (m, v) | +|---|---|---| +| **储存内容** | "语言是怎么工作的"(学到的知识) | "最近的梯度方向和大小"(寻路记忆) | +| **类比** | 你脑中的知识 | 你对"复习策略"的习惯 | +| **FORGETTER 是否重置** | ❌ 保留 | ✅ 清零 | + +FORGETTER 清除的是优化器的"寻路记忆",不是模型的"知识"。模型仍然记得 "cats" 是 "cat" 的复数,它只是忘了"刚才在往哪个方向优化"。 + +**通俗类比:GPS 导航的惯性** + +想象你在用 GPS 导航找最佳路线。GPS 有��习惯:它会记住你最近走过的方向,并倾向于建议你继续走。 + +- 训练初期:GPS 记忆是有用的,帮你沿着一条好路走下去 +- 训练后期:GPS 记忆变成了"惯性"——即使旁边出现了一条更好的捷径,GPS 也会说"继续直走",因为它习惯了当前方向 +- **FORGETTER = 定期重启 GPS**。每重启一次,GPS 从头评估所有方向,可能发现之前被忽略的更优路线 + +论文作者自己的比喻是**睡眠与遗忘**:人类每天睡觉时大脑会整理和遗忘部分记忆,醒来后反而能更好地学习新东西。 + +**效果:锯齿形下降** + +``` +Loss ─┐ + │ 正常训练(无 FORGETTER) + │ ╲___________________________ ← 收敛了,不再下降 + │ + │ 使用 FORGETTER + │ ╲___╱╲_____╱╲________╱╲_______ ← 每次重置后短暂上升,然后降到更低 + │ ↑ ↑ ↑ + │ 重置1 重置2 重置3 + └──────────────────────────────── Step +``` + +每次重置后 loss 会**短暂上升**(优化器丢失了记忆,需要几百步重新适应),然后继续下降到比之前更低的水平——**打破了收敛天花板**。 + +**为什么正常训练会"收敛"停住?** + +Adam 的 m 和 v 经过几万步的累积,变得非常"确定"——β₂=0.98 意味着 v 记住了最近 50 步的平方梯度。这个估计越来越稳定,优化器的行为越来越保守(步长越来越固定),难以跳出当前的 loss 盆地探索新的低点。 + +重置 m 和 v 后,优化器回到了"什么都不记得"的状态,步长会因为偏差校正而暂时变大,给了优化器一次"重新探索"的机会。 + +**关键配置**:FORGETTER 需要配合较大的 weight_decay=1.0(正常是 0.1),因为重置状态后 Adam 的步长会暂时变大(偏差校正的影响),需要更强的正则化防止模型参数偏移过大。 + +**预期影响**:BLiMP +3pp。一个 22 层模型 + FORGETTER 能匹配标准 48 层模型的性能。 + +**实现**:训练循环中加一行 `reset_optimizer_state(optimizer)`,极其简单。 + +--- + +### F4. Muon(动量正交化)⭐⭐ — 2024,Jordan et al. + +> **一句话**:完全不同于 Adam 的思路——不用二阶矩调步长,而是对更新方向做"正交化",确保参数在所有方向均匀更新。 + +**这是什么?** + +Adam 系列(Adam → AdamW → LAMB)的核心思想都是**自适应学习率**:用二阶矩 v 来缩放每个参数的步长。Muon 走了一条完全不同的路——它不关心步长大小,而是关心**更新方向的质量**。 + +Muon(**M**omentum + Orthogonalizatio**n**)的工作流程: +1. 用 **Nesterov 动量**计算更新方向(和 SGD+Momentum 类似) +2. 对更新矩阵做 **Newton-Schulz 正交化**(5 次迭代,近似 SVD) +3. 正交化的效果:将更新矩阵的所有奇异值都变为 1 + +**什么是正交化?用 2D 的例子理解** + +一个矩阵可以看成一个"变换"——它把输入向量拉伸、压缩、旋转。用 SVD 分解: + +``` +矩阵 M = U × Σ × V^T + + U, V^T = 旋转操作 + Σ = 对角矩阵,对角线上是"奇异值" [σ₁, σ₂, ...] + σ 大 = 在这个方向上拉伸 + σ 小 = 在这个方向上压缩 +``` + +正交化就是把所有奇异值都变成 1: + +``` +正交化后: M' = U × I × V^T = U × V^T (只剩旋转,没有拉伸/压缩) +``` + +**通俗类比:撒种子** + +想象你要在一块方形田地里撒种子(= 更新参数)。 + +- **不做正交化(Adam 风格)**:你可能把大部分种子撒在某几条行上(梯度大的方向),其他行几乎没有种子。结果田地只有几条行长了庄稼,其余是荒地。 +- **做正交化(Muon 风格)**:先把种子均匀分配到每一行,再撒下去。结果整块田地都长满了庄稼。 + +在参数矩阵的语境中: +- **不做正交化**:参数矩阵可能逐渐"坍缩"——只有少数几个方向有大值,其他方向接近零。这意味着矩阵只用了一小部分容量(低秩),参数效率低下。 +- **做正交化**:确保更新在所有方向上均匀分布,参数矩阵保持"满秩",最大化利用每个参数。 + +**Newton-Schulz 迭代是什么?** + +理论上正交化需要做完整的 SVD 分解(计算量大)。Newton-Schulz 是一种**近似**方法,只需 5 次简单的矩阵乘法就能得到很好的近似。每次迭代: + +``` +X_{k+1} = X_k × (aI + bX_k^T X_k + cX_k^T X_k X_k^T X_k) +``` + +看起来复杂,但就是几次矩阵乘法——GPU 上非常快。5 次迭代后,X 就是一个接近正交的矩阵。 + +**与 Adam 系列的关键区别**: + +| | Adam/AdamW/LAMB | Muon | +|---|---|---| +| **核心思想** | 自适应学习率(大梯度走小步) | 正交化更新方向(各方向均匀) | +| **维护的状态** | m(动量)+ v(方差)= 2份 | 只有 m(动量)= 1份 | +| **内存占用** | 参数量 × 2 | 参数量 × 1(**省 1/3**) | +| **超参数** | β₁, β₂, ε, lr, wd | lr, momentum(**更少**) | +| **适合场景** | 通用,大量调参经验 | 小模型,内存紧张 | + +**为什么有效?** + +实验显示 PPL(困惑度)从 11.21 降到 7.95(降幅 30%),训练更稳定。在小模型上表现出色。但在 BabyLM 上的直接验证不多,属于值得尝试的新方向。 + +**预期影响**:有潜力提升 2~3pp,但需要实验验证。 + +**风险**:超参数调优经验较少(不像 Adam 系列有大量文献参考),可能需要额外的调参成本。此外 Muon 对 1D 参数(bias、LayerNorm)不做正交化(退化为普通 AdamW),只对 2D 及以上的矩阵参数生效。 + +--- + +### F 总结:优化器演化脉络 + +``` +Adam (2014) 最经典,自适应学习率 + │ + ├── 问题:weight decay 被自适应缩放削弱 + │ + ▼ +AdamW (2017) 解耦 weight decay,Transformer 标配 + │ + ├── 问题:大 batch 下不同层梯度尺度差异大 + │ + ▼ +LAMB (2019) 逐层信赖域归一化,大 batch 更稳 + │ + ├── 正交思路:FORGETTER (2025) 定期重置 m/v,打破收敛天花板 + │ + ▼ +Muon (2024) 完全不同的路线:动量 + 正交化,不需要二阶矩 +``` + +**推荐选择**: +- GPT-BERT → **LAMB**(官方默认)+ **FORGETTER**(+3pp) +- GPT-2 / ModernBERT / xLSTM → **AdamW**(最稳妥) +- 探索性实验 → **Muon**(可能有惊喜) +- 对照基线 → **Adam**(验证 AdamW 解耦的价值) + +--- + +## G. 超参数 + +### G1. 学习率(Learning Rate)⭐⭐⭐ + +**这是什么?** + +控制每次参数更新的步长大小。学习率太大 → 训练不稳定甚至发散;太小 → 收敛太慢,10 个 epoch 内学不够。 + +**为什么是最重要的超参数?** + +在所有可调参数中,学习率对最终性能的影响通常最大。选错学习率可能导致 BLiMP 差异达 3~5pp。 + +**搜索范围**: + +| 优化器 | 推荐范围 | 默认值 | +|--------|---------|-------| +| LAMB | 0.003 ~ 0.025 | 0.0141 | +| AdamW | 3e-4 ~ 1e-3 | 5e-4 | + +**学习率调度**:使用余弦退火(Cosine Annealing)+ 线性预热(Linear Warmup)。 + +- **预热**(Warmup):训练开始的前 3~6% 步,学习率从 0 线性增长到目标值。为什么?Adam/LAMB 的二阶矩估计在最初几步非常不准,预热给优化器时间来收集可靠的梯度统计。 +- **余弦退火**:预热结束后,学习率按余弦曲线逐渐降到接近 0。为什么?训练后期减小步长,让模型在最优解附近精细调整。 + +--- + +### G2. 有效 Batch Size(Effective Batch Size)⭐⭐ + +**这是什么?** + +`有效 Batch Size = 物理 Batch Size × 梯度累积步数` + +例如:物理 batch 64,梯度累积 8 步 → 有效 batch 512。 + +梯度累积的原理:GPU 每次处理 64 个样本计算梯度,但不立即更新参数。累积 8 次梯度后,求平均再更新一次。效果等同于一次处理 512 个样本,但 GPU 内存只需要够放 64 个。 + +**为什么要调?** + +| 有效 Batch Size | 优点 | 缺点 | +|:---------------:|------|------| +| 小(64-128) | 更新频率高,探索更多 | 梯度噪声大,不稳定 | +| 中(256-512) | GLUE 最优区间 | 需要梯度累积 | +| 大(1024+) | 梯度估计准确 | 泛化可能变差 | + +**推荐**:物理 batch 64 × 累积 4~8 = 有效 batch 256~512 + +--- + +### G3. Weight Decay ⭐ + +**这是什么?** + +每次更新参数时,额外将参数值乘以一个略小于 1 的系数(如 0.999),等效于 L2 正则化。防止参数值过大,缓解过拟合。 + +**推荐值**: +- 正常训练:0.1 +- 配合 FORGETTER:1.0(因为 FORGETTER 每个 epoch 重置优化器状态,需要更强的正则化约束参数) + +--- + +### G4. Dropout ⭐ + +**这是什么?** + +训练时随机将一部分神经元的输出设为 0。比例由 dropout rate 控制(如 0.1 = 10% 的神经元被关闭)。 + +**为什么用?** + +防止模型过度依赖特定神经元组合(过拟合)。但在数据极少的场景(10M 词),模型可能**欠拟合**而非过拟合,此时 dropout=0(不丢弃)可能更好。 + +**搜索范围**:0.0 ~ 0.2 + +--- + +### G5. 序列长度(Sequence Length)⭐ + +**这是什么?** + +每条训练样本的最大 token 数。 + +- seq_len=128 → 每条样本约 90 词 +- seq_len=256 → 每条样本约 180 词 + +BLiMP 评测用的都是短句子(1-2 句),128 已经足够覆盖。但过短的序列会影响模型学习长距离依赖关系(Entity Tracking、阅读理解需要)。 + +**推荐**:128(保守)或 256(如果 Entity Tracking 重要) + +--- + +### G6. 序列构造方式(Sequence Packing vs. Per-Sentence)⭐⭐ + +**这是什么?** + +训练数据从原始文本变成模型输入时,有两种构造方式: + +**方式 A:拼接切块(Packing)**——当前实现 + +把所���句子首尾拼接成一条超长 token 序列,然后按固定长度(如 128)切成等长的块。 + +``` +原始: "猫坐在垫子上。" "鸟在天上飞。" "她去了商店。" ... +拼接: 猫坐在垫子上。鸟在天上飞。她去了商店。... +切块: [猫坐在垫子上。鸟在天上飞。她] [去了商店。...] +``` + +- 优点:零浪费,每个 token 都参与训练;GPT/LLaMA 等主流模型的标准做法 +- 缺点:句子边界被打破,一个样本可能包含不相关句子的拼接,或一个句子被截断到两个样本里 + +**方式 B:逐句独立(Per-Sentence)**——待实现/待测试 + +每个句子(或每行文本)作为一个独立样本,短于 seq_len 的部分用 padding 填充。 + +``` +原始: "猫坐在垫子上。" "鸟在天上飞。" "她去了商店。" +样本1: [猫坐在垫子上。...] +样本2: [鸟在天上飞。...] +样本3: [她去了商店。...] +``` + +- 优点:每个样本是完整的语义单元,模型不会学到跨句子的虚假模式 +- 缺点:padding 浪费计算;短句子有大量无效 token;每个 epoch 的参数更新次数取决于句子数量而非总 token 数 + +**为什么需要测试?** + +BabyLM 的评测任务(BLiMP、EWoK)都是以**完整句子**为单位。如果模型训练时看到的都是完整句子(方式 B),可能更贴合评测场景。但方式 A 是业界标准做法,效率更高。哪种更好需要实验验证。 + +**可能的折中方案**:拼接时在句子之间加 EOS(句尾标记)token,让模型感知句子边界,同时保留 packing 的高效率。当前代码(`data.py` 第 91 行)用了 `add_special_tokens=False`,没有加 EOS。 + +--- + +## H. 训练技巧 + +### H1. Checkpoint 选择 ⭐⭐⭐ + +**这是什么?** + +每个 epoch 结束后保存一个 checkpoint(模型快照),训练结束后不一定用最后一个 checkpoint,而是选评测分数最高的那个。 + +**为什么不用最后一个?** + +2025 年 MoEP 方案发现:最优 checkpoint 出现在 epoch 3(只训练了 30M 词),而不是 epoch 10。后续训练反而导致过拟合,评测分数下降。 + +这在小数据场景很常见:模型很快学会了主要模式,继续训练会开始"记住"训练数据的噪声。 + +**实操**: +1. 每个 epoch 保存 checkpoint +2. 训练结束后,对每个 checkpoint 跑 fast_eval(~5 分钟) +3. 选 BLiMP 分数最高的 checkpoint 提交 + +--- + +### H2. Checkpoint 平均 ⭐⭐ + +**这是什么?** + +将最后 K 个 epoch 的 checkpoint 做权重平均: + +```python +avg_params = (params_epoch8 + params_epoch9 + params_epoch10) / 3 +``` + +**为什么有效?** + +训练后期模型参数在最优解附近"震荡"。取平均相当于对这些震荡做平滑,得到一个更稳定的解。这是一种免费的正则化手段。 + +**推荐**:K=3(平均最后 3 个 epoch) + +--- + +### H3. 知识蒸馏(Knowledge Distillation)⭐ + +**这是什么?** + +先训练一个"教师模型"(可以是更大的、训练更久的模型),然后训练"学生模型"时,不仅让学生学习正确答案,还让学生模仿教师的输出概率分布。 + +``` +L_total = α × KL(teacher_output || student_output) × T² + (1-α) × L_task +``` + +- T(温度)控制概率分布的"软化"程度,T=4.0 让教师输出更平滑,传递更多"次优选项"的信息 +- α 控制蒸馏损失与任务损失的权重 + +**在 BabyLM 中的应用**: + +2026 赛规禁止将外部模型的输出分布暴露给提交模型。所以只能用**自蒸馏**:用自己训练的模型当教师(例如用 Phase 2 的最佳模型当教师,训练 Phase 3 的模型)。 + +**优先级**:低,因为在 BabyLM 上的验证不够充分 + +--- + +## I. 需要注意的方法(效果有限或有风险) + +以下方法在 BabyLM 比赛的实验中**效果有限或存在风险**,投入前需谨慎评估: + +### 效果有限(投入产出比低,但不一定有害) + +| 方法 | 效果 | 说明 | +|------|------|------| +| **传统 Curriculum Learning(按语法难度排序)** | 提升 <1pp (p=0.055) | 人工设计"简单→难"的顺序,大多数情况与随机顺序无显著差异。但不是有害的——2025 第三名 ACLM 就用了课程学习。**有效的变体**:MATTR 排序(见 A3 采样策略末尾)、反向课程 MTP(见 E5) | +| **暴力增加计算量** | 相关性弱 | 2025 年 FLOPs 与性能几乎无相关,说明"算得更多"不如"算得更聪明" | +| **Wiktionary 定义** | 无提升 | 字典定义对语言模型训练没有明显帮助 | +| **总是跑满 10 个 epoch** | 可能过拟合 | 最优 checkpoint 经常在 epoch 3~7,后续训练可能让评测分数下降(见 H1) | + +### 有明确负面效果(应避免) + +| 方法 | 效果 | 说明 | +|------|------|------| +| **纯合成数据替代真实数据** | **-5~6pp** | 合成数据(如 TinyStories)不能完全替代真实语言数据。但作为**���合补充**(如 30% 合成 + 70% 真实)是有益的 | +| **累进式数据覆盖** | 显著下降 | 让早期数据在后期不再出现(模拟"遗忘")是有害的,应避免 | +| **通用网络数据(MADLAD-400)** | 有害 | 未经筛选的低质量网络文本引入噪声,拉低成绩 | + +--- + +## 附录:术语表 + +| 术语 | 解释 | +|------|------| +| **BLiMP** | Benchmark of Linguistic Minimal Pairs。给模型两个句子(一对语法正确和错误),看模型能否给正确句更高概率。测试 67 种语法现象。 | +| **EWoK** | Evaluation of World Knowledge。测试模型对物质属性、空间关系等常识知识的掌握。11 个知识域。 | +| **GLUE** | General Language Understanding Evaluation。7 个子任务(推理、蕴含、相似度等),需要微调。 | +| **Entity Tracking** | 给出一段文字描述物体位置变化(如"Alice 把球放进盒子,Bob 把盒子放到桌上"),问模型"球在哪?"。测因果推理能力。 | +| **WUG** | 用虚构词(如 "wug")测试形态学知识。例如:"一个 wug"→"两个 wugs",模型是否能正确预测复数形式。 | +| **COMPS** | 测试模型对属性比较(如"鲸鱼比老鼠大")的理解。 | +| **AoA** | Age of Acquisition。测试模型的词汇习得顺序是否与人类儿童相似。 | +| **MNTP** | Masked Next Token Prediction。GPT-BERT 特有:在因果注意力下做掩码预测。 | +| **CLM** | Causal Language Modeling。标准的从左到右预测下一个词。 | +| **MLM** | Masked Language Modeling。BERT 的填空训练方式。 | +| **PPL** | Perplexity(困惑度)。模型对文本的"意外程度",越低越好。 | +| **pp** | Percentage Points。百分点差值,如从 67% 到 70% 是 +3pp。 | +| **BPE** | Byte-Pair Encoding。一种子词分词算法,通过合并高频字符对建立词表。 | +| **RoPE** | Rotary Position Embedding。将位置信息编码为向量的旋转角度,支持相对位置感知。 | +| **GeGLU** | GELU 激活函数的门控变体。FFN 输出 = x × GELU(gate),让网络学会选择性传递信息。 | +| **Pre-norm** | 在注意力/FFN 之前做 LayerNorm(而非之后)。训练更稳定。 | +| **Cosine Annealing** | 余弦退火。学习率按余弦曲线从峰值衰减到接近 0。 | +| **Gradient Accumulation** | 梯度累积。多个小 batch 的梯度累加后再更新参数,等效于大 batch 训练。 | +| **Weight Decay** | 权重衰减。每步将参数值乘以 (1 - wd × lr),防止参数过大。等效于 L2 正则化。 | +| **Checkpoint** | 训练过程中保存的模型快照,包含模型参数和优化器状态。 | +| **Zero-shot** | 不经过微调,直接用预训练模型做评测。 | +| **Fine-tuning** | 微调。在预训练模型基础上,用任务特定数据继续训练。 | +| **Fertility** | 分词器的平均切分粒度。每个词被切成几个 token。值越小意味着分词越"粗"。 | + +--- + +## 变更记录 + +- **2026-03-22 v6**:嵌入策略(C)移到模型架构(D)前面(分词→嵌入→模型);新增 A4(5) 信息密集数据合成方法;更新编号 C↔D +- **2026-03-21 v5**:新增 D6 MoE 和 D7 AttnRes 的实现状态;修正过时描述;Llama→Qwen3.5 全局修正;A5 数据集构建方式总览(13种方案 × 5维度) +- **2026-03-21 v4**:重构 A 数据策略(合并为 A1数据源总览/A2过滤/A3采样策略/A4增强方法/A5构建方式总览),统一数据源不分内外部,增加采样策略对比、增强方法对比表 +- **2026-03-21 v3**:按实验流程重新排序章节(数据→分词→模型→嵌入→训练目标→优化器→超参→技巧),旧编号映射:F→A, E→B, A→C, C→D, B→E, D→F, G/H/I 不变 +- **2026-03-21 v2.1**:优化器部分新增 Adam (F1),按时间顺序重排并详细解释;Morfessor (B2) 补充 MDL 原理、训练算法、Viterbi 推理完整解释 +- **2026-03-20 v2**:补充缺失内容——反向课程MTP(E5)、ShortConv(C4)、Variation Sets(A4)、Cosmopedia/TinyDialogues/OneStopEnglish(A5)、Contrastive Decoding(A10)、MATTR排序(A11);修正课程学习的过度否定;更新数据混合方案 +- **2026-03-20 v1**:初版,整合 ref_strategy_2026.md、03_training.md、06_data_augmentation.md 内容