大模型训练中的数据配比调优指南

如何科学地确定预训练和中训练阶段的数据配比,包含实验方法论、经验公式和案例分析。

教程指南 数据配比 预训练 中训练 调优 Data Mix

为什么数据配比如此重要?

在相同的总训练 token 数下,不同的数据配比可以导致模型性能的巨大差异。一个直观的例子:

假设你有 1T tokens 的预算,以下两种配比训出的模型表现可能天差地别:

配比方案英文 Web代码数学MMLUHumanEvalGSM8K
方案 A80%10%10%63.228.542.1
方案 B55%30%15%61.845.258.7

方案 B 牺牲了 1.4% 的 MMLU,但 HumanEval 提升 16.7%,GSM8K 提升 16.6%——这就是配比的力量。

数据配比的核心原则

原则 1: 目标驱动

先明确你的模型要优化什么能力,再倒推数据需求。

目标:通用聊天助手
→ 重点:英文通用、中文通用、代码基础、指令跟随
→ 配比倾向:英文 45%, 中文 25%, 代码 15%, 其他 15%

目标:代码助手
→ 重点:代码质量、多语言编程、代码理解
→ 配比倾向:代码 50%, 英文技术 20%, 中文技术 15%, 其他 15%

目标:科研助手
→ 重点:论文理解、数学推理、多语言学术
→ 配比倾向:学术文本 30%, 英文 25%, 代码 15%, 数学 15%, 中文 15%

原则 2: 数据源之间存在正向迁移

不同数据源的训练效果不是独立的,存在复杂的交互关系:

正向迁移(verified):

  • 代码训练 → 提升逻辑推理和数学能力
  • 英文高质量文本 → 一定程度提升其他语言能力
  • 数学训练 → 提升结构化推理能力
  • 百科知识 → 提升事实问答能力

负向迁移(需注意):

  • 过多低质量 Web 文本 → 降低生成质量
  • 过多代码 → 回复倾向于代码风格
  • 过多特定语言 → 其他语言能力退化

原则 3: 边际收益递减

每种数据源都存在边际收益递减点。例如,代码数据从 0% 增加到 15% 时提升显著,但从 30% 增加到 45% 时提升明显减小。

实验方法论

方法 1: Proxy Model 实验

核心思想:用小模型快速验证配比,再应用到大模型。

设计配比方案 × 5-10 种
    ↓
训练 1B 模型 × 10B tokens (每个方案约 2-4 GPU·hours)
    ↓
在关键 benchmark 上评估
    ↓
选出 top-3 方案
    ↓
训练 7B 模型 × 30B tokens 验证 (每个方案约 200 GPU·hours)
    ↓
确定最终配比
    ↓
应用到目标模型

这种方法的假设是:小模型上的配比趋势与大模型基本一致。这在大多数情况下成立,但有时小模型上的最优配比与大模型会有偏差。

方法 2: DoReMi 风格的自动搜索

DoReMi 的核心思路:训练一个”参考模型”来指导数据权重的分配。

# DoReMi 简化流程
def doremi_search(
    data_sources: dict[str, Dataset],
    proxy_model_config: dict,
    reference_model_config: dict,
    target_loss_by_domain: dict[str, float] = None,
):
    """
    1. 训练参考模型(均匀配比或某个初始配比)
    2. 计算每个数据源的"超额 loss"
    3. 根据超额 loss 调整权重
    4. 重复迭代
    """
    # 初始化均匀权重
    weights = {name: 1.0/len(data_sources) for name in data_sources}
    
    for iteration in range(max_iterations):
        # 用当前权重训练 proxy model
        proxy_model = train_proxy(data_sources, weights)
        
        # 计算每个源的 loss
        domain_losses = {}
        for name, dataset in data_sources.items():
            loss = evaluate_loss(proxy_model, dataset)
            domain_losses[name] = loss
        
        # 计算参考 loss(用参考模型)
        ref_losses = {}
        for name, dataset in data_sources.items():
            ref_losses[name] = evaluate_loss(reference_model, dataset)
        
        # 更新权重:loss 差距越大的源,权重越高
        for name in data_sources:
            excess_loss = domain_losses[name] - ref_losses[name]
            weights[name] *= math.exp(step_size * excess_loss)
        
        # 归一化
        total = sum(weights.values())
        weights = {k: v/total for k, v in weights.items()}
    
    return weights

方法 3: Data Mixing Laws (Yi-style)

基于幂律模型拟合数据配比与性能的关系:

# Data Mixing Laws 实验设计
import itertools
import numpy as np
from scipy.optimize import curve_fit

# 1. 定义搜索空间
data_sources = ["web_en", "web_zh", "code", "math", "academic"]
N_SOURCES = len(data_sources)

# 2. 生成配比网格(Dirichlet 采样)
def sample_mixing_ratios(n_samples: int = 50, n_sources: int = 5):
    """用 Dirichlet 分布均匀采样配比"""
    # alpha=1 给出均匀分布
    ratios = np.random.dirichlet(np.ones(n_sources), size=n_samples)
    return ratios

# 3. 训练 proxy models
ratios = sample_mixing_ratios(50)
results = []
for i, ratio in enumerate(ratios):
    config = dict(zip(data_sources, ratio))
    # 训练 1B 模型 × 10B tokens
    scores = train_and_evaluate(config)
    results.append({"ratio": config, "scores": scores})

# 4. 拟合 mixing law
def mixing_law(ratios, *params):
    """
    L(p) = Σ c_i * p_i^(-α_i) + Σ_{i≠j} d_ij * p_i^β_i * p_j^β_j
    简化为线性交互项:
    L(p) = Σ c_i * p_i^(-α_i) + Σ_{i<j} d_ij * (p_i * p_j)^γ_ij
    """
    # ... 拟合实现
    pass

# 5. 在拟合的 law 上搜索最优配比
from scipy.optimize import minimize

def objective(ratios):
    """最小化目标 benchmark 的预测 loss"""
    return mixing_law(ratios, *fitted_params)

# 约束:所有比例和为 1,每个比例 >= 0.01
constraints = [
    {"type": "eq", "fun": lambda x: sum(x) - 1},
]
bounds = [(0.01, 0.8)] * N_SOURCES

result = minimize(objective, x0=np.ones(N_SOURCES)/N_SOURCES,
                 method='SLSQP', bounds=bounds, constraints=constraints)
optimal_ratios = dict(zip(data_sources, result.x))

各大模型的配比参考

已公开的配比信息

模型Web代码学术/书籍对话其他来源
LLaMA-1 (65B)67%4.5%4.5%2.5%21.5%论文
LLaMA-2 (70B)~80%~5%~5%~1%~9%估算
LLaMA-3 (405B)~50%~25%~7%~3%~15%技术报告
Qwen-2 (72B)~50%~20%~10%~5%~15%估算
DeepSeek-V3~40%~25%~15%~5%~15%技术报告

趋势观察:

  • 代码比例逐年增加(4.5% → 25%)
  • 高质量筛选后的 Web 数据占比下降
  • 学术/书籍数据比例保持稳定
  • 对话/指令数据开始加入预训练

LLaMA-3 的配比策略分析

LLaMA-3 的配比值得深入分析,因为它的代码比例高达 ~25%,远超之前版本:

代码数据的比例变化与影响:

LLaMA-1: 4.5% 代码 → HumanEval 23.7%
LLaMA-2: ~5% 代码  → HumanEval 29.9%
LLaMA-3: ~25% 代码 → HumanEval 84.1%

代码比例翻了 5 倍,HumanEval 提升了 54.2 个百分点。当然这不全是配比的功劳(模型更大、训练更多 tokens、数据质量更好),但配比是核心因素之一。

中训练的配比特殊考量

中训练的配比与预训练有本质不同:需要同时优化新能力的获取和旧能力的保持。

配比框架

中训练配比 = 目标数据 × (1 - 回放比例) + 回放数据 × 回放比例

其中回放数据本身也有配比:
回放数据 = 通用Web × α + 代码 × β + 学术 × γ + ...

配比实验模板

# 以"注入中文能力"为例的配比实验设计
experiments:
  - name: "exp-01-baseline"
    description: "30% 回放"
    mix:
      zh_web: 0.45
      zh_books: 0.15
      zh_wiki: 0.10
      replay_en_web: 0.15
      replay_code: 0.10
      replay_academic: 0.05
    
  - name: "exp-02-more-replay"
    description: "50% 回放"
    mix:
      zh_web: 0.30
      zh_books: 0.10
      zh_wiki: 0.10
      replay_en_web: 0.25
      replay_code: 0.15
      replay_academic: 0.10
    
  - name: "exp-03-quality-focus"
    description: "重质量轻数量"
    mix:
      zh_web_highq: 0.25  # 只用高质量筛选后的 Web
      zh_books: 0.20
      zh_wiki: 0.15
      replay_en_web: 0.20
      replay_code: 0.10
      replay_academic: 0.10

动态配比 (Curriculum-style)

更高级的做法是在训练过程中动态调整配比:

def get_dynamic_ratio(step: int, total_steps: int) -> dict:
    """
    训练初期:更多通用数据,帮助平稳过渡
    训练中期:增加目标数据比例
    训练后期:高质量数据为主,进入精化阶段
    """
    progress = step / total_steps
    
    if progress < 0.1:  # 前 10%:warmup 阶段
        return {
            "target": 0.30,
            "replay": 0.50,
            "high_quality": 0.20,
        }
    elif progress < 0.8:  # 中间 70%:主训练阶段
        return {
            "target": 0.50,
            "replay": 0.30,
            "high_quality": 0.20,
        }
    else:  # 最后 20%:精化阶段
        return {
            "target": 0.30,
            "replay": 0.20,
            "high_quality": 0.50,
        }

调优实操步骤

Step 1: 确定数据源和预算

# 盘点可用数据
available_data = {
    "web_en":      {"tokens": "5T",   "quality": "medium"},
    "web_zh":      {"tokens": "800B", "quality": "medium"},
    "code":        {"tokens": "1T",   "quality": "high"},
    "math":        {"tokens": "50B",  "quality": "high"},
    "academic_en": {"tokens": "200B", "quality": "very_high"},
    "academic_zh": {"tokens": "30B",  "quality": "high"},
    "wiki_en":     {"tokens": "5B",   "quality": "very_high"},
    "wiki_zh":     {"tokens": "2B",   "quality": "very_high"},
    "books":       {"tokens": "100B", "quality": "high"},
}

training_budget = "2T tokens"
target_benchmarks = ["MMLU", "C-Eval", "HumanEval", "GSM8K", "MATH"]

Step 2: 设计配比候选方案

基于经验和文献,设计 5-10 个候选方案:

candidates = {
    "balanced": {
        "web_en": 0.40, "web_zh": 0.15, "code": 0.20,
        "math": 0.05, "academic": 0.10, "wiki": 0.02,
        "books": 0.08,
    },
    "code_heavy": {
        "web_en": 0.30, "web_zh": 0.10, "code": 0.35,
        "math": 0.08, "academic": 0.08, "wiki": 0.02,
        "books": 0.07,
    },
    "chinese_focus": {
        "web_en": 0.25, "web_zh": 0.30, "code": 0.15,
        "math": 0.05, "academic": 0.10, "wiki": 0.03,
        "books": 0.12,
    },
    "quality_first": {
        "web_en": 0.20, "web_zh": 0.10, "code": 0.20,
        "math": 0.10, "academic": 0.20, "wiki": 0.05,
        "books": 0.15,
    },
}

Step 3: Proxy 实验

# 用 1B 模型跑每个配比方案
for config in balanced code_heavy chinese_focus quality_first; do
    python train.py \
        --model-size 1b \
        --tokens 10b \
        --data-mix configs/${config}.yaml \
        --output checkpoints/${config}/ \
        --eval-benchmarks mmlu,ceval,humaneval,gsm8k
done

Step 4: 分析结果并微调

# 分析 proxy 实验结果
results = load_experiment_results()

# 绘制雷达图对比各方案
plot_radar_chart(results, metrics=target_benchmarks)

# 基于结果微调配比
# 例如:balanced 方案整体最好,但数学偏弱
# → 创建 "balanced_v2",增加 math 比例
refined = {
    "balanced_v2": {
        "web_en": 0.38, "web_zh": 0.14, "code": 0.18,
        "math": 0.10, "academic": 0.10, "wiki": 0.02,
        "books": 0.08,
    },
}

Step 5: 大模型验证

# 用目标模型规模验证 top-2 方案
python train.py \
    --model-size 7b \
    --tokens 50b \
    --data-mix configs/balanced_v2.yaml \
    --output checkpoints/7b_balanced_v2/

常见误区

误区 1: “数据越多越好”

事实:数据量增加到一定程度后,质量比数量重要得多。FineWeb-Edu 用 1.3T tokens(经过严格筛选)训出的模型优于用 15T tokens(宽松筛选)训出的模型。

误区 2: “配比确定后就不用改了”

事实:最优配比随训练阶段变化。训练初期适合更多通用数据,后期适合更多高质量/专业数据。

误区 3: “小模型实验可以完全迁移到大模型”

事实:大部分趋势可以迁移,但最优点可能有偏移。通常大模型对数据质量更敏感,对配比变化更鲁棒。

误区 4: “代码数据只影响编程能力”

事实:代码训练被广泛观察到能提升逻辑推理和数学能力。LLaMA-3 的代码比例增加到 25% 后,不仅 HumanEval 大幅提升,GSM8K 也有明显提升。

实战案例:构建中英双语通用模型的配比

目标

  • 7B 参数量
  • 2T tokens 训练预算
  • 中英双语同等重要
  • 保持代码和数学能力

最终配比(经过 3 轮 proxy 实验确定)

final_mix:
  # 英文 (50%)
  en_web_highq: 0.30    # 高质量筛选的英文 Web
  en_academic: 0.10     # 学术论文 + 教科书
  en_wiki: 0.02         # Wikipedia
  en_books: 0.08        # 开源书籍
  
  # 中文 (25%)
  zh_web_highq: 0.15    # 高质量中文 Web
  zh_academic: 0.05     # 中文学术 + 教材
  zh_wiki: 0.01         # 中文维基
  zh_books: 0.04        # 中文书籍
  
  # 代码 (15%)
  code_python: 0.06     # Python (最高权重)
  code_js: 0.03         # JavaScript
  code_other: 0.06      # Java/C++/Go/Rust 等
  
  # 数学 (7%)
  math_textbook: 0.03   # 数学教材
  math_problems: 0.02   # 数学题库
  math_code: 0.02       # 数学相关代码
  
  # 其他 (3%)
  multilingual: 0.02    # 其他语言
  misc: 0.01            # 杂项高质量数据

# 训练课程
curriculum:
  phase1:  # 0-20%
    upweight: [en_web_highq, zh_web_highq]
    factor: 1.2
  phase2:  # 20-80%
    # 使用标准配比
  phase3:  # 80-100%
    upweight: [en_academic, zh_academic, math_textbook]
    factor: 1.5
    downweight: [en_web_highq, zh_web_highq]
    factor: 0.7

实验结果

Benchmark均匀配比最终配比提升
MMLU58.262.1+3.9
C-Eval50.157.8+7.7
HumanEval35.442.1+6.7
GSM8K38.648.3+9.7
MATH12.118.5+6.4

通过系统的配比优化,在相同训练预算下获得了平均 6.9 个百分点的提升。