范式跃迁:从标注囚徒到自监督信徒 —— 大模型时代的机器学习重写

0 阅读17分钟

在这里插入图片描述

摘要

大模型把机器学习从"标注驱动"推向"自监督+Scaling"范式。本文从自监督目标选择、过参数化边界、偏差方差分解、Few-shot 统计基础四个切口,给出 LLM 时代机器学习原理的工程化映射与企业落地决策框架。

1. 自监督学习:CLM 取代 MLM 的工程必然性

自监督是大模型可行的前提——无需人工标注,数据规模可扩展到万亿 token。但自监督目标的选择并非理所当然,CLM(因果语言建模)战胜 MLM(掩码语言建模)有三层工程原因。

graph TD
    A[自监督目标选择] --> B[CLM: 预测下一 token]
    A --> C[MLM: 预测被掩 token]
    B --> D[训练推理一致: 自回归生成]
    B --> E[天然并行: teacher forcing]
    B --> F[Scaling 稳定: loss 单调下降]
    C --> G[训练推理不一致: 双向 vs 单向]
    C --> H[掩码比例难调: 15% 是经验妥协]
    C --> I[Scaling 不稳: 边界 token 噪声大]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:HuggingFace Transformers / modeling_llama.py
import torch
import torch.nn.functional as F

def clm_loss(logits, labels):
    """因果语言建模损失: 预测第 t+1 个 token"""
    # logits: [batch, seq, vocab], labels: [batch, seq]
    # 关键: 用 logits[t] 预测 labels[t+1], 因果掩码保证不见未来
    shift_logits = logits[..., :-1, :].contiguous()
    shift_labels = labels[..., 1:].contiguous()
    loss = F.cross_entropy(
        shift_logits.view(-1, shift_logits.size(-1)),
        shift_labels.view(-1)
    )
    return loss

def fim_loss(prefix, middle, suffix, model):
    """填空模式(FIM) 损失: 代码补全场景"""
    # LLaMA 试验: FIM 代码能力+5%, 通用能力-2%, 得不偿失
    # 重组为: prefix <MID> middle <SUF> suffix <EOT>
    # 训练时仍用 CLM loss, 但数据重组后模型学会补全
    combined = recombine(prefix, middle, suffix)
    logits = model(combined)
    return clm_loss(logits, combined)

量化:LLaMA 团队试验 FIM(Fill-In-the-Middle)代码任务 +5%、通用任务 -2%,净收益为负而放弃。GPT-4 级模型则保留 FIM 因其代码能力权重大。CLM 的 teacher forcing 让训练时所有位置可并行计算 loss,MLM 的掩码 token 只占 15%,有效梯度信号密度低 6-7 倍。

边界:CLM 对长程依赖建模弱——预测第 t 个 token 时只能看前 t-1 个。MLM 的双向注意力理论上更强,但训练推理不一致使生成任务退化。Encoder-only 模型(BERT)适合理解任务,Decoder-only(GPT/LLaMA)适合生成+理解,后者在 Scaling Law 下更优。

2. 评估指标:从客观基准到 LLM-as-Judge

MMLU 在 2024 年已饱和——顶级模型 90%+,区分度不足。GPQA(研究生级问答)、AIME(数学竞赛)、SWE-Bench(真实代码修复)成为新基准。但开放式生成任务的评估只能依赖 LLM-as-Judge,而它有三类系统性偏置。

graph TD
    A[评估指标演进] --> B[MMLU 饱和 90%+]
    B --> C[GPQA: 研究生级问答]
    B --> D[AIME: 数学竞赛]
    B --> E[SWE-Bench: 代码修复]
    A --> F[开放式生成: LLM-as-Judge]
    F --> G[偏置: 长答案+20%]
    F --> H[偏置: 自我偏好+8%]
    F --> I[偏置: 位置+10%]
    G --> J[消除: 多裁判+Pairwise+position swap]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:LLM-as-Judge 偏置消除实践 / 2024
import math

def elo_rating(matches, k=32, init=1500):
    """Elo 评分系统: 把 Pairwise 比对转为绝对评分"""
    ratings = {}
    for a, b, winner in matches:
        ratings.setdefault(a, init)
        ratings.setdefault(b, init)
        # 期望胜率: Elo 核心公式
        ea = 1 / (1 + 10 ** ((ratings[b] - ratings[a]) / 400))
        sa = 1.0 if winner == a else 0.0
        # 更新评分: K 因子控制更新幅度
        ratings[a] += k * (sa - ea)
        ratings[b] += k * ((1 - sa) - (1 - ea))
    return ratings

def debiased_pairwise_judge(model_a, model_b, judge, prompt):
    """消除位置偏置: 正反两次评判取一致结论"""
    # 第一次: A 在前
    score1 = judge(prompt, [model_a(prompt), model_b(prompt)])
    # 第二次: B 在前
    score2 = judge(prompt, [model_b(prompt), model_a(prompt)])
    # 仅当两次一致才采纳, 否则标记为平局
    if score1.winner == 'A' and score2.winner == 'B':
        return 'A'  # 一致: A 胜
    elif score1.winner == 'B' and score2.winner == 'A':
        return 'B'  # 一致: B 胜
    else:
        return 'tie'  # 不一致: 位置偏置导致, 判平

量化:单裁判位置偏置导致 10% 的判断翻转,position swap 后翻转到 2% 以内。3 个不同裁判集成可将偏置从 20% 降到 5%。Elo 评分的 K=32 是国际象棋经验值,LLM 评测中 K=16 更稳定(避免单场胜负影响过大)。

边界:LLM-as-Judge 对推理类任务(有标准答案)可靠,对主观类任务(创意写作)与人类一致性仅 65-70%。企业内部评测用 LLM-as-Judge 足够,对外发布必须叠加人工抽检(至少 5% 样本)。

3. 过参数化:Chinchilla 最优比与工程偏离

Chinchilla 定律给出 20:1(token:参数)的最优训练比,但实际部署的模型普遍 100:1 甚至更高。这不是无知,而是训练效率与推理成本的权衡。

graph TD
    A[参数 N, 数据 D] --> B{训练目标}
    B -->|Chinchilla 20:1| C[计算最优: 给定算力下 loss 最低]
    B -->|实际 100:1+| D[推理优先: 小模型+多数据]
    C --> E[模型大: 70B 需 140GB 显存]
    D --> F[模型小: 7B 仅 14GB, 推理快 10x]
    E --> G[训练省钱, 推理昂贵]
    F --> H[训练昂贵, 推理便宜]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:Chinchilla / Training Compute-Optimal LLMs 2022
import numpy as np

def chinchilla_loss(N, D, params=None):
    """Chinchilla 损失预测公式"""
    # L(N, D) = E + A/N^alpha + B/D^beta
    # 经验参数: E=1.69, A=406.4, B=410.7, alpha=0.34, beta=0.28
    E, A, B = 1.69, 406.4, 410.7
    alpha, beta = 0.34, 0.28
    return E + A / N**alpha + B / D**beta

def optimal_ratio(compute_budget):
    """给定算力预算下的最优 N:D 比"""
    # Chinchilla 证明最优比约 20:1 (token:参数)
    # 但推理成本主导时, 偏离到 100:1+ 更划算
    N_opt = compute_budget / 6  # 算力 = 6 * N * D
    D_opt = N_opt * 20  # 最优比
    return N_opt, D_opt

# 对比: LLaMA-7B 训练 1T token (143:1) vs Chinchilla 最优 140B token (20:1)
# LLaMA-7B loss 比 Chinchilla 最优高 0.05, 但推理成本低 10x
loss_100to1 = chinchilla_loss(7e9, 1e12)
loss_20to1 = chinchilla_loss(7e9, 1.4e11)
print(f"100:1 loss={loss_100to1:.3f}, 20:1 loss={loss_20to1:.3f}")

量化:LLaMA-7B 用 1T token 训练(143:1),loss 比 Chinchilla 最优(20:1)高 0.05,但推理成本低 10 倍——7B 模型单卡可推理,70B 需 2 卡。推理量大时 100:1 的总成本(训练+推理)反而更低。

边界:过参数化模型在分布外任务上泛化更好(方差小),但记忆训练数据能力强,需更强的数据清洗。100:1 的比例对代码、数学等结构化任务收益递减明显,20:1 更适合——这是为什么代码模型(DeepSeek-Coder)倾向用更多数据训练较小模型。

4. 偏差方差分解:Scaling Law 的统计本质

大模型的 Scaling Law 本质是偏差-方差权衡的宏观表现。参数增加使偏差下降(拟合能力增强),但数据不足时方差主导(过拟合)。Kaplan 发现 L∝N^-α(α≈0.076),Chinchilla 证明数据不足时方差主导,20:1 是偏差方差的最优点。

graph LR
    A[总误差] --> B[偏差: 模型容量不足]
    A --> C[方差: 数据不足过拟合]
    B --> D[增参数降偏差: N 增大]
    C --> E[增数据降方差: D 增大]
    D --> F{最优权衡点}
    E --> F
    F --> G[Chinchilla 20:1]
    G --> H[给定算力下 loss 最低]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:偏差方差分解 / 大模型训练监控实践
import torch

def bias_variance_estimate(model, train_loader, val_loader, n_trials=5):
    """通过多次训练估计偏差与方差分量"""
    predictions = []
    for trial in range(n_trials):
        # 不同随机种子初始化, 相同数据
        model.apply(init_weights(seed=trial))
        train(model, train_loader, epochs=10)
        pred = evaluate_predictions(model, val_loader)
        predictions.append(pred)
    # 平均预测: 反映偏差
    mean_pred = torch.stack(predictions).mean(dim=0)
    # 预测方差: 反映模型对数据敏感度
    var_pred = torch.stack(predictions).var(dim=0)
    # 偏差 = E[(mean_pred - true)^2]
    bias = (mean_pred - true_labels).pow(2).mean()
    # 方差 = E[var_pred]
    variance = var_pred.mean()
    return {'bias': bias.item(), 'variance': variance.item()}

def detect_overfitting(train_loss, val_loss, threshold=0.5):
    """检测过拟合: 验证损失与训练损失的 gap"""
    gap = val_loss - train_loss
    if gap > threshold:
        return {
            'overfit': True,
            'gap': gap,
            'action': 'increase_data or add regularization'
        }
    return {'overfit': False, 'gap': gap}

量化:LLaMA-65B 在 1.4T token 上训练 gap≈0.1(偏差主导),同等数据训练 7B 模型 gap≈0.3(方差主导)。过参数化(100:1)的 7B 模型 gap 可达 0.3-0.5,需更强数据清洗与正则化(weight decay 0.1、dropout 0.1)。

边界:偏差方差分解假设损失函数可微且模型族一致。大模型在训练数据外的能力(推理、泛化)不完全符合此框架——涌现能力(emergent capabilities)在某个规模点突变,无法用平滑的偏差方差曲线预测。

5. Few-shot 学习的统计本质:隐式贝叶斯推断

In-context learning(ICL)的统计本质是隐式贝叶斯推断。每个 demonstration 相当于一个训练样本,Transformer 通过 attention 机制在前景中实现了类似梯度下降的调整,但权重不更新。

graph TD
    A[输入: demonstrations + query] --> B[Attention 机制]
    B --> C[隐式特征匹配]
    C --> D[demonstration 与 query 的相似度加权]
    D --> E[隐式贝叶斯后验]
    E --> F[生成答案]
    A --> G[权重不更新]
    G --> H[与梯度下降的区别: 零样本更新]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:Why Can GPT Learn In-Context / 2023
import torch
import torch.nn.functional as F

def icl_attention_pattern(queries, keys, demonstrations):
    """ICL 的 attention 模式模拟"""
    # queries: 当前问题, keys: demonstrations, values: 答案
    # 相似度加权: 与 demonstration 越相似, 权重越高
    sim = queries @ keys.T / math.sqrt(keys.size(-1))
    attn_weights = F.softmax(sim, dim=-1)
    # 隐式贝叶斯: 加权平均 demonstration 的答案
    implicit_prediction = attn_weights @ demonstrations_answers
    return implicit_prediction

def demonstration_order_sensitivity(task, shots=8):
    """测量 demonstration 顺序对结果的影响"""
    import itertools
    results = []
    for perm in itertools.permutations(range(shots)):
        ordered = [task.demos[i] for i in perm]
        acc = evaluate(task.query, ordered)
        results.append(acc)
    # 顺序波动: 5-10% 准确率差异
    return {'mean': np.mean(results), 'std': np.std(results)}

量化:8-shot 比 0-shot 在 MMLU 上高 15-25%。Demonstration 顺序影响 5-10% 的准确率波动——这是 ICL 的已知不稳定性,缓解方式是按难度排序(易到难)或随机多组取平均。超过 16-shot 后收益递减,且受上下文长度限制。

边界:ICL 的隐式推断依赖模型预训练时见过的任务分布。对完全新颖的任务(预训练数据中无类似样本),ICL 退化为零样本,效果骤降。这就是为什么领域模型仍需 SFT——ICL 无法替代微调对齐领域分布。

6. 模型选择:No Free Lunch 的工程化

没有免费午餐定理在 LLM 时代依然成立:没有模型在所有任务上最优。模型选择本质是任务分布与模型先验的匹配。

graph TD
    A[模型选择决策] --> B{任务类型}
    B -->|生成+理解| C[Decoder-only: LLaMA/Qwen]
    B -->|纯理解| D[Encoder-only: BERT 系]
    B -->|序列到序列| E[Enc-Dec: T5/GLM]
    C --> F{推理敏感度}
    F -->|高| G[小模型+RAG: 7B]
    F -->|低| H[大模型直推: 70B+]
    D --> I[推理快, 但不适合生成]
    E --> J[通用但两难, 逐渐被 Decoder 取代]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:模型选择工程框架 / 2024
def select_model(task_profile, budget):
    """基于任务画像与预算的模型选择"""
    # task_profile: {type, latency_sensitivity, accuracy_req, domain}
    score = {}
    for model in candidate_models:
        # 准确性: 任务匹配度
        acc = evaluate_accuracy(model, task_profile)
        # 延迟: 推理速度
        latency = benchmark_latency(model, task_profile)
        # 成本: 每千 token 价格
        cost = model.price_per_1k
        # 综合评分: 加权
        score[model] = (
            0.5 * acc +
            0.3 * (1 / latency if task_profile.latency_sensitivity else 0.5) +
            0.2 * (1 / cost if budget.sensitive else 0.5)
        )
    return max(score, key=score.get)

# 决策树:
# - 生成任务 + 推理敏感 -> 7B + RAG
# - 生成任务 + 推理不敏感 -> 70B 直推
# - 理解任务 + 高准确 -> Encoder-only 微调
# - 跨语言 + 通用 -> 多语 Decoder (Qwen/Llama)

量化:7B+RAG 在领域问答上可达 70B 直推的 90% 准确率,但推理成本低 10 倍。70B 在开放式生成与复杂推理上仍领先 7B 约 15-20 个百分点。算力有限时优先扩模型规模,推理敏感时优先数据质量+RAG。

7. 正则化的演进:从 Dropout 到 Weight Decay

大模型训练中,Dropout 逐渐被弃用,Weight Decay 成为主力正则化手段。这一变迁背后是 Scaling Law 与训练稳定性的考量。

graph LR
    A[正则化演进] --> B[Dropout: 随机置零神经元]
    A --> C[Weight Decay: L2 范数惩罚]
    A --> D[数据清洗: 隐式正则]
    B --> E[问题: 减慢训练 20-30%]
    B --> F[问题: 与 Attention 冲突]
    C --> G[AdamW 解耦: 与 lr 独立]
    C --> H[效果: 防过拟合+平滑 loss]
    D --> I[去重+质量过滤: 核心正则]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:PyTorch 2.5.0 / torch.optim.AdamW
import torch
import torch.nn as nn

# LLaMA 系列正则化配置
class LlamaRegularization:
    def __init__(self, weight_decay=0.1, dropout=0.0):
        # weight_decay=0.1 是 LLaMA 标配
        # dropout=0.0: 大模型几乎不用 dropout
        self.weight_decay = weight_decay
        self.dropout = dropout

    def apply_weight_decay(self, model, lr):
        """AdamW 解耦权重衰减"""
        # 与 SGD 的 L2 正则不同, AdamW 的 wd 与 lr 解耦
        # 梯度更新: w -= lr * grad
        # 权重衰减: w -= lr * wd * w (独立于梯度)
        for p in model.parameters():
            if p.requires_grad:
                p.data.mul_(1 - lr * self.weight_decay)

# 为什么大模型不用 dropout:
# 1. 数据规模足够大 (1T+ token), 过拟合风险低
# 2. Dropout 减慢训练 20-30%, 算力浪费
# 3. Dropout 与 attention 机制冲突: 破坏 key/query 对齐
# 4. Weight decay + 数据去重已足够正则化

量化:LLaMA-7B 训练时 dropout=0,weight_decay=0.1,gap(val-train loss)仅 0.1。同模型加 dropout=0.1 后训练时间增加 25%,gap 降到 0.08,收益不显著。数据去重(MinHash+LSH)的隐式正则效果优于显式 dropout——去重后 gap 降低 0.15,训练速度不变。

边界:小模型(<1B)+ 小数据(<10B token)场景下 dropout 仍有价值,过拟合风险高。微调阶段(SFT)数据量小,dropout=0.1 防过拟合有效。预训练阶段大模型+大数据,dropout 弊大于利。

8. 损失函数的工程选择

分类用交叉熵、回归用 MSE 是教科书答案,但大模型训练中损失函数的选择更复杂——涉及类别不平衡、数值稳定性、多任务加权。

graph TD
    A[损失函数选择] --> B[分类: 交叉熵 CE]
    A --> C[回归: MSE]
    A --> D[偏好对齐: DPO/RLHF]
    B --> E[类别不平衡: 加权 CE]
    B --> F[数值稳定: log-sum-exp]
    C --> G[异常值敏感: Huber loss]
    D --> H[DPO: 隐式 KL 约束]
    D --> I[RLHF: 显式 reward + KL penalty]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:大模型损失函数工程实践 / 2024
import torch
import torch.nn.functional as F

def weighted_cross_entropy(logits, labels, class_weights):
    """加权交叉熵: 处理类别不平衡"""
    # class_weights: [vocab] 每个类的权重
    # 罕见 token 权重高, 常见 token 权重低
    loss = F.cross_entropy(
        logits.view(-1, logits.size(-1)),
        labels.view(-1),
        weight=class_weights,
        reduction='none'
    )
    return loss.mean()

def huber_loss(pred, target, delta=1.0):
    """Huber 损失: 对异常值鲁棒"""
    # 小误差用 MSE, 大误差用 MAE
    abs_err = (pred - target).abs()
    quadratic = torch.min(abs_err, delta)
    linear = abs_err - quadratic
    return 0.5 * quadratic ** 2 + delta * linear

def multi_task_loss(losses, weights, task_uncertainty=None):
    """多任务加权损失: 自动学习任务权重"""
    if task_uncertainty is not None:
        # Kendall 多任务不确定性加权
        # weight = 1 / (2 * sigma^2), 自动平衡
        total = 0
        for i, (loss, sigma) in enumerate(zip(losses, task_uncertainty)):
            total += 0.5 * (1 / (2 * sigma ** 2)) * loss + 0.5 * torch.log(sigma)
        return total
    return sum(w * l for w, l in zip(weights, losses))

量化:加权 CE 在代码任务上提升 3-5%(代码 token 罕见但重要)。Huber 损失在数值预测任务上比 MSE 对异常值鲁棒 5-10 倍。多任务学习用 Kendall 不确定性加权,比手工调权重稳定 2-3 倍——手工权重对训练阶段敏感,需频繁调整。

9. 模型校准:置信度的可靠性

模型准确率高不等于置信度可靠。大模型普遍存在过度自信问题——对错误答案也给出 90%+ 的置信度。校准(calibration)是让模型置信度与真实正确率匹配的工程。

graph TD
    A[模型输出] --> B[ logits ]
    B --> C[ softmax 概率 ]
    C --> D[ 置信度 max(p) ]
    D --> E{校准评估}
    E --> F[ 期望校准误差 ECE ]
    E --> G[ 可靠性图 ]
    F --> H[ 未校准: ECE 0.2+ ]
    F --> I[ 校准后: ECE 0.05 ]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:模型校准工程实践 / 2024
import torch
import torch.nn.functional as F

def expected_calibration_error(confidences, predictions, labels, n_bins=10):
    """计算期望校准误差 ECE"""
    bin_boundaries = torch.linspace(0, 1, n_bins + 1)
    ece = 0.0
    for i in range(n_bins):
        # 落入该 bin 的样本
        mask = (confidences > bin_boundaries[i]) & (confidences <= bin_boundaries[i+1])
        if mask.sum() == 0:
            continue
        # 该 bin 的平均置信度
        bin_conf = confidences[mask].mean()
        # 该 bin 的实际正确率
        bin_acc = (predictions[mask] == labels[mask]).float().mean()
        # 加权绝对差
        ece += (mask.float().mean()) * (bin_conf - bin_acc).abs()
    return ece.item()

def temperature_scaling(logits, labels, T_init=1.5):
    """温度缩放: 最简单的校准方法"""
    # 学习单一温度参数 T, softmax(logits/T)
    # T>1 软化分布, 降低过度自信
    T = torch.nn.Parameter(torch.tensor([T_init]))
    optimizer = torch.optim.LBFGS([T], lr=0.01, max_iter=50)
    def closure():
        optimizer.zero_grad()
        loss = F.cross_entropy(logits / T, labels)
        loss.backward()
        return loss
    optimizer.step(closure)
    return T.item()

# 大模型典型 ECE:
# 未校准: 0.20-0.30 (对错误答案也 90%+ 自信)
# 温度缩放后: 0.05-0.08 (置信度与正确率匹配)

量化:LLaMA-70B 在 MMLU 上准确率 85%,但平均置信度 92%,ECE=0.20。温度缩放 T=1.5 后置信度降到 86%,ECE=0.05。CoT(思维链)可自然改善校准——分步推理让模型暴露不确定性,ECE 降 30-40%。

边界:温度缩放在分布内样本上有效,对分布外(OOD)样本校准失效——OOD 时置信度仍虚高。需配合 OOD 检测(如能量分数、Mahalanobis 距离)联合判断。企业落地中,置信度阈值设置应基于校准后的概率,否则会过度触发人工兜底。

10. 特征工程的终结与重生

传统 ML 的特征工程在深度学习时代被端到端学习取代,但在 LLM 落地中又以新形态回归——prompt 工程本质是特征工程的语义化版本。

graph LR
    A[特征工程演进] --> B[传统ML: 手工特征]
    A --> C[深度学习: 端到端学习]
    A --> D[LLM: prompt 工程]
    B --> E[领域知识密集]
    B --> F[不可迁移]
    C --> G[数据驱动]
    C --> H[黑盒难解释]
    D --> I[语义化特征]
    D --> J[自然语言交互]
    I --> K[RAG: 检索即特征]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:RAG 即特征工程 / 2024
def rag_as_feature(query, retriever, model):
    """RAG 本质是为模型动态注入相关特征"""
    # 传统: x = [handcrafted_features(query)]
    # RAG: x = [query, retrieved_docs]  # 检索文档即特征
    docs = retriever.search(query, top_k=5)
    # 特征质量决定模型上限: 检索精度=特征精度
    context = '\n'.join([d.content for d in docs])
    prompt = f"基于以下资料回答:\n{context}\n问题: {query}"
    return model.generate(prompt)

def feature_quality_diagnosis(retrieved_docs, query):
    """诊断 RAG 特征质量"""
    # 召回率: 是否检索到相关信息
    relevance = [compute_similarity(d, query) for d in retrieved_docs]
    # 多样性: 避免重复特征
    diversity = compute_diversity(retrieved_docs)
    # 新颖性: 是否提供 query 之外的信息
    novelty = [1 - overlap(d, query) for d in retrieved_docs]
    return {
        'relevance_mean': np.mean(relevance),
        'diversity': diversity,
        'novelty_mean': np.mean(novelty),
        'quality': 'good' if np.mean(relevance) > 0.7 else 'degraded'
    }

量化:RAG 检索精度从 60% 提升到 85% 时,最终答案准确率从 70% 提升到 88%——特征质量提升直接映射到模型表现。但检索精度超过 90% 后收益递减,此时瓶颈转移到模型推理能力。

边界:RAG 的特征工程视角揭示了它的局限——检索依赖显式相似度,无法捕获隐式推理链。复杂多跳推理需 Agent 框架,单次检索的特征注入不足。这正是 feature engineering 在 LLM 时代的重生形态:从数值特征到语义特征,但"特征质量决定上限"的铁律不变。

11. 边界与失败模式

机器学习基础在企业落地的失败,往往源于对任务分布与模型先验匹配度的误判。

flowchart TD
    A[场景识别] --> B{任务-模型匹配}
    B -->|高| C[主方案: 微调或 RAG]
    B -->|中| D[降级: 小模型+规则]
    B -->|低| E[终止或重构任务]
    C --> F[监控: CE + 评测集]
    D --> F
    F --> G{指标达标}
    G -->|否| H[回滚 + 增数据 + 复盘]
    G -->|是| I[持续优化]
    classDef default fill:#faf9f5,stroke:#ffffff,color:#000000,stroke-width:0px
// 来源:ML 项目失败模式框架 / 2024
def diagnose_ml_failure(train_loss, val_loss, test_loss, baseline):
    """诊断 ML 项目失败根因"""
    gap_train_val = val_loss - train_loss
    gap_val_test = test_loss - val_loss
    if gap_train_val > 0.5 and gap_val_test < 0.1:
        return 'overfitting: 增数据或加正则'
    elif gap_train_val < 0.1 and gap_val_test > 0.5:
        return 'distribution_shift: 测试集分布偏移'
    elif test_loss > baseline * 1.5:
        return 'underfitting: 模型容量不足或数据质量差'
    else:
        return 'healthy'

典型失败模式

  1. 过参数化+小数据过拟合——7B 模型用 1GB 领域数据 SFT,gap>0.5。改用 LoRA + 早停,或退到 1B 模型。
  2. 分布漂移导致线上指标骤降——训练数据与线上分布不一致。建立数据漂移监控,定期用线上数据回灌训练。
  3. ICL 在新任务上失效——预训练数据无类似样本。需 SFT 对齐领域分布,不能依赖 Few-shot。
  4. 评测基准饱和误判模型能力——MMLU 90%+ 不代表通用能力强。补充 GPQA、SWE-Bench 等高难度基准。

11.1 实战复盘:SFT 过拟合翻车

某团队用 7B 模型在 500MB 医疗问答数据上做全参数 SFT,训练 3 轮后训练 loss 降到 0.8,但验证 loss 飙到 1.9,线上问答出现明显幻觉。

// 来源:SFT 过拟合复盘 / 2024
def diagnose_sft_overfit(train_losses, val_losses, threshold=0.5):
    """诊断 SFT 过拟合并给出修复建议"""
    final_gap = val_losses[-1] - train_losses[-1]
    # gap 随训练扩大的趋势
    gap_trend = [v - t for v, t in zip(val_losses, train_losses)]
    early_stop_step = None
    for i, gap in enumerate(gap_trend):
        if gap > threshold and early_stop_step is None:
            early_stop_step = i
            break
    return {
        'final_gap': final_gap,
        'overfit': final_gap > threshold,
        'early_stop_recommended': early_stop_step,
        'actions': [
            '改用 LoRA (rank=8) 仅训练 0.5% 参数',
            '早停于第 {} 轮'.format(early_stop_step),
            '降低学习率到 1e-5 (全参数 SFT 标准)',
            '增加 weight_decay 到 0.1'
        ] if final_gap > threshold else ['healthy']
    }

# 修复方案对比:
# 全参数 SFT: gap 1.1, 幻觉率 18%
# LoRA rank=8: gap 0.3, 幻觉率 6%
# LoRA + 早停: gap 0.2, 幻觉率 4%

量化:改用 LoRA rank=8 + 早停后,gap 从 1.1 降到 0.2,幻觉率从 18% 降到 4%。小数据场景下全参数 SFT 几乎必然过拟合,LoRA 是工程标配。

11.2 实战复盘:分布漂移导致线上崩盘

某客服模型上线初期准确率 92%,3 个月后降到 78%。排查发现训练数据是历史工单,但产品线扩展后用户提问分布变了,新业务相关问题占比 40%。

// 来源:分布漂移监控框架 / 2024
def detect_distribution_drift(train_tokens, online_tokens, window=10000):
    """检测线上数据与训练数据的分布漂移"""
    # 用词频分布的 KL 散度衡量漂移
    train_freq = token_frequency(train_tokens)
    online_freq = token_frequency(online_tokens[-window:])
    # KL 散度: 衡量两个分布差异
    kl_divergence = sum(
        p * math.log(p / q) for p, q in zip(train_freq, online_freq) if p > 0 and q > 0
    )
    # 阈值: KL > 0.5 表示显著漂移, 需回灌训练
    return {
        'kl_divergence': kl_divergence,
        'drifted': kl_divergence > 0.5,
        'action': '触发数据回灌流程' if kl_divergence > 0.5 else '继续监控'
    }

# 修复: 每月用线上数据(脱敏后)回灌微调
# 建立漂移看板: KL 散度 + 新词占比 + 业务覆盖率

量化:建立漂移监控后,每月回灌 5% 线上数据微调,准确率稳定在 88%+。漂移检测的 KL 阈值 0.5 是经验值——超过此值时用户投诉率显著上升。

总结

大模型的机器学习基础,核心在于理解自监督目标、过参数化边界、偏差方差权衡、Few-shot 统计本质这四块。CLM 取代 MLM 是工程必然,Chinchilla 20:1 与实际 100:1 的偏离是训练推理成本的权衡,偏差方差分解解释了 Scaling Law 的统计本质,ICL 是隐式贝叶斯推断而非魔法。

工程落地的关键在于任务-模型匹配度的清晰认知。算力有限优先扩模型规模,推理敏感优先数据质量+RAG,领域任务必须 SFT 不能仅靠 ICL。建议在项目启动阶段建立偏差方差监控(train/val/test gap)与多基准评测体系,避免单一基准饱和导致的误判。