27届大模型岗面试准备(五):预训练全流程拆解——从数据清洗到 Tokenizer 再到 PT 的每

27届大模型岗面试准备(五):预训练全流程拆解------从数据清洗到 Tokenizer 再到 PT 的每一个环节

面试官问"讲讲预训练流程"时,他想听的不是"收集数据然后训练"这种一句话答案,而是你能不能把数据管线、Tokenizer、训练配置三大块的关键决策点讲清楚。27届校招里,即使岗位 JD 写的是"微调/应用方向",预训练流程依然是高频考点------因为它是判断候选人对大模型理解深度的分水岭。这篇按照真实预训练项目的执行顺序,把每个环节的原理、工程实现和面试追问一次讲透。

一、全流程鸟瞰:预训练到底分几步

先给出全景,后面逐节展开:

  1. 数据获取:网页(Common Crawl)、书籍、代码(GitHub/The Stack)、论文(arXiv)、百科、对话等多源采集;
  2. 数据清洗:语种识别 → 质量过滤 → 去重 → 有害内容过滤 → 隐私信息(PII)脱敏;
  3. 数据配比与课程:不同来源按比例混合,可能分阶段调整(курriculum);
  4. Tokenizer 训练:BPE/BBPE/Unigram,决定词表大小与压缩率;
  5. 预训练(PT):Next Token Prediction 目标,配合学习率调度、混合精度、分布式并行;
  6. 评估与退火:训练中持续评估 loss/下游任务,末期用高质量数据做 learning rate annealing。

面试官常从任意一环切入往下挖,所以每一环都要有"能讲三分钟"的储备。

二、数据清洗:预训练成败的第一决定因素

业界共识是"数据质量 > 模型结构微创新"。LLaMA、Qwen、DeepSeek 的技术报告里,数据处理章节的篇幅都远超模型结构。清洗管线通常包含以下步骤:

语种识别:用 fastText 的语言分类器打分,保留目标语种置信度高的文档。中文模型通常保留中英双语,因为英文语料在代码、学术领域的质量密度更高。

质量过滤分两派:

  • 规则派(启发式):Gopher 规则是代表------文档词数在 50~100k 之间、平均词长 3~10、符号占比不超标、含有足够多的常见停用词等。优点是便宜、可解释;缺点是误伤高质量的特殊文本(如诗歌、表格)。
  • 模型派:训练一个小分类器(fastText 或 BERT 级别),用高质量语料(维基、书籍)当正例,随机网页当负例,给每个文档打质量分。LLaMA 系列用的就是类似思路(以维基引用页面为正例)。

去重是重中之重。重复数据会导致模型记忆而非泛化,还会放大隐私泄露风险。三个层级:

  • URL 级:直接对 URL 去重,最便宜;
  • 文档级:MinHash + LSH(局部敏感哈希)做近似去重,容忍轻微改写;
  • 段落/行级:精确哈希去掉模板化内容(导航栏、版权声明)。

面试高频追问:MinHash 为什么能近似 Jaccard 相似度? 答案要点:MinHash 的核心性质是"两个集合的最小哈希值相等的概率等于它们的 Jaccard 相似度"。对每个文档的 n-gram 集合取 k 个独立哈希函数的最小值组成签名,两个签名逐位比较的相等比例就是 Jaccard 的无偏估计。再配合 LSH 分band,把候选对检索从 O(n²) 降下来。

三、数据配比:不是越多越好,而是配得越准越好

不同来源的数据对模型能力的贡献不同,配比是预训练的核心超参之一:

数据源 典型占比 主要贡献 风险
网页(CC 清洗后) 50%~70% 语言多样性、世界知识 质量参差、噪声多
代码 10%~20% 逻辑推理、结构化输出 占比过高伤自然语言流畅度
书籍 5%~10% 长文本连贯性、叙事能力 版权风险
学术论文 3%~8% 专业知识、严谨表达 领域分布偏 STEM
百科 2%~5% 事实密度高 体量有限
数学 3%~10% 数学推理 高质量数学语料稀缺

两个面试要点:一是代码数据被证明能提升非代码任务的推理能力 (多个消融实验支持),这是"为什么纯 NLP 模型也要混代码"的标准答案;二是多轮退火(annealing)阶段用高质量小数据------训练末期把学习率降下来的同时切换到教科书级语料,能显著提升下游 benchmark,MiniCPM、Llama 3 都公开过这个技巧。

四、Tokenizer:被低估的关键组件

Tokenizer 决定了模型"看世界的粒度"。主流选择是 BBPE(Byte-level BPE),GPT 系列、LLaMA、Qwen 都在用。

BPE 训练过程 :从单字符(或字节)词表出发,统计语料中相邻 token 对的共现频率,迭代合并最高频的对,直到词表达到目标大小。BBPE 的改进是以字节为初始单元,任何 UTF-8 字符都能表示,彻底消灭 OOV,代价是中文等非拉丁文字可能被切成多个字节。

词表大小的权衡 :词表越大,压缩率越高(同样文本的 token 数越少,等效上下文更长、推理更省),但 embedding 矩阵参数越多、低频 token 训练不充分。LLaMA-2 是 32k(对中文很不友好,一个汉字常被拆成 2~3 个 token),Qwen 用 152k 的大词表显著提升了中文压缩率,DeepSeek-V3 用 128k。中文压缩率是国产模型面试的高频考点:同一段中文,LLaMA-2 tokenizer 的 token 数可能是 Qwen 的 1.7 倍以上,直接影响推理成本和有效上下文。

五、可运行代码:迷你数据清洗 + BPE 训练管线

下面这段代码用纯 Python 标准库实现一个可运行的迷你管线:启发式质量过滤 → MinHash 近似去重 → 简化版 BPE 训练。直接 python pipeline.py 即可运行。

python 复制代码
import re
import hashlib
from collections import Counter

# ---------- 1. 启发式质量过滤(Gopher 风格规则的简化版) ----------
def quality_filter(doc: str) -> bool:
    words = doc.split()
    if not (10 <= len(words) <= 100000):          # 词数范围
        return False
    avg_len = sum(len(w) for w in words) / len(words)
    if not (1.0 <= avg_len <= 12.0):               # 平均词长
        return False
    symbol_ratio = len(re.findall(r'[#@$%^&*]', doc)) / max(len(doc), 1)
    if symbol_ratio > 0.1:                         # 符号占比
        return False
    return True

# ---------- 2. MinHash 近似去重 ----------
def minhash_signature(doc: str, num_hashes: int = 64, ngram: int = 3):
    tokens = doc.split()
    shingles = {' '.join(tokens[i:i+ngram]) for i in range(len(tokens)-ngram+1)}
    if not shingles:
        return None
    sig = []
    for seed in range(num_hashes):
        min_h = min(
            int(hashlib.md5(f"{seed}:{s}".encode()).hexdigest(), 16)
            for s in shingles
        )
        sig.append(min_h)
    return tuple(sig)

def jaccard_est(sig1, sig2):
    return sum(a == b for a, b in zip(sig1, sig2)) / len(sig1)

def dedup(docs, threshold: float = 0.8):
    kept, sigs = [], []
    for d in docs:
        sig = minhash_signature(d)
        if sig is None:
            continue
        if all(jaccard_est(sig, s) < threshold for s in sigs):
            kept.append(d)
            sigs.append(sig)
    return kept

# ---------- 3. 简化版 BPE 训练 ----------
def train_bpe(corpus: str, num_merges: int = 50):
    # 初始:每个词拆成字符序列,词尾加 </w>
    words = Counter(corpus.split())
    vocab = {tuple(w) + ('</w>',): c for w, c in words.items()}
    merges = []
    for _ in range(num_merges):
        pairs = Counter()
        for word, cnt in vocab.items():
            for i in range(len(word) - 1):
                pairs[(word[i], word[i+1])] += cnt
        if not pairs:
            break
        best = max(pairs, key=pairs.get)
        merges.append(best)
        new_vocab = {}
        for word, cnt in vocab.items():
            w, i = [], 0
            while i < len(word):
                if i < len(word)-1 and (word[i], word[i+1]) == best:
                    w.append(word[i] + word[i+1]); i += 2
                else:
                    w.append(word[i]); i += 1
            new_vocab[tuple(w)] = cnt
        vocab = new_vocab
    return merges

if __name__ == "__main__":
    raw_docs = [
        "the quick brown fox jumps over the lazy dog again and again in the field",
        "the quick brown fox jumps over the lazy dog again and again in the park",  # 近重复
        "#### @@@@ $$$$ spam spam spam",                                            # 低质量
        "language models are trained with next token prediction on large corpora",
    ]
    cleaned = [d for d in raw_docs if quality_filter(d)]
    print(f"质量过滤: {len(raw_docs)} -> {len(cleaned)}")
    deduped = dedup(cleaned, threshold=0.6)
    print(f"MinHash 去重: {len(cleaned)} -> {len(deduped)}")
    merges = train_bpe(' '.join(deduped), num_merges=20)
    print(f"BPE 前 5 个合并规则: {merges[:5]}")

运行结果会展示:4 篇文档过滤掉 1 篇低质量、去掉 1 篇近重复,最终用 2 篇干净文档训练出 BPE 合并规则。虽然是玩具规模,但过滤→去重→分词训练的骨架和工业界完全一致,面试手撕环节写出 MinHash 或 BPE 任意一个都非常加分。

六、预训练目标与训练动力学

训练目标就是 Next Token Prediction:最大化 P(x_t | x_<t),损失是交叉熵。看似简单,但有几个面试深挖点:

Scaling Law:Chinchilla 定律指出,给定算力预算,模型参数量 N 和训练 token 数 D 应大致满足 D ≈ 20N 才算"算力最优"。但推理成本的存在让业界普遍"过训练"(over-train)------LLaMA 3 8B 训了 15T token,远超 Chinchilla 最优点,因为小模型多训省下的是部署后每一次推理的钱。这个"训练最优 vs 推理最优"的权衡是高频考点。

学习率调度:主流是 warmup + cosine decay。warmup(通常几百到几千步)防止训练初期梯度爆炸;cosine 衰减到峰值的 10% 左右。近年 WSD(Warmup-Stable-Decay)调度兴起:中段恒定学习率方便随时分叉做退火,MiniCPM 与 DeepSeek 系列使用。

混合精度与稳定性:BF16 已成为标配(比 FP16 动态范围大,不易溢出);梯度裁剪(clip norm 1.0)、z-loss、QK-norm 等手段防 loss spike。遇到 loss 突刺的标准处理:回滚到最近 checkpoint + 跳过问题数据批次。

七、面试答题框架与自检清单

被问"如果让你从零预训练一个 7B 模型,你会怎么做"时,推荐按这个框架作答:

  1. 明确目标:通用还是领域模型?决定数据配比与词表设计;
  2. 算力核算:用 6ND 估算 FLOPs(N 参数量、D token 数),反推 GPU 时;
  3. 数据管线:采集 → fastText 语种/质量过滤 → MinHash 去重 → PII 脱敏,产出比目标训练量多 20% 的 token 备用;
  4. Tokenizer:BBPE,中文场景 100k+ 词表,先在样本语料上测压缩率;
  5. 训练配置:BF16、AdamW(β2=0.95)、warmup+cosine、序列长度分阶段拉长(先 4k 后 32k,省算力);
  6. 监控:loss 曲线、梯度范数、下游任务小评测集,出现 spike 有回滚预案;
  7. 收尾:退火阶段上高质量数据,产出 base 模型再进入 SFT。

自检清单:能手推 BPE 合并过程吗?能解释 MinHash 原理吗?能说出 Chinchilla 结论和它为什么被"违背"吗?知道为什么混代码数据吗?这四个问题全能答上,预训练环节基本稳了。

下一篇讲 SFT:数据怎么构造、损失怎么算、什么样的 SFT 数据是好数据。

相关推荐
attitude.x1 小时前
2026年数据透视分析工具推荐:多维与融合对比
人工智能
就是一顿骚操作1 小时前
Agent Loop 入门到深入:从 ReAct 到 LangGraph、Agents SDK 与 MCP
人工智能·大模型·llm·论文解读·ai agent
quanjui1 小时前
【医学尝试】基于Segment Anything Model的医学图像分割研究:眼底OCT与X线胸片微调实战
人工智能·笔记·学习
就是一顿骚操作1 小时前
Dropout:神经网络正则化的经典解读
人工智能·深度学习·神经网络·论文解读
2401_843253702 小时前
数据隐私AI:从合规检查到隐私计算的Skill化
人工智能
清川渡水2 小时前
NL2SQL 的正确打开方式:从「AI 猜 SQL」到「置信度闭环」的工程化落地
数据库·人工智能·sql
Lyra_Infra2 小时前
OpenClaw 服务异常故障分析报告
linux·人工智能
众人皆醒我独醉2 小时前
为什么 AI 每次回答不一样?—— 温度参数是 AI 的"创意调节旋钮"
面试·ai编程
半个落月2 小时前
用 Node.js 搭建 EPUB 问答助手:从文本切片、向量检索到 RAG
人工智能·node.js