预训练数据工程
2020 年,当美国人工智能研究公司 OpenAI 发布 GPT-3 时,外界对它 1750 亿参数的规模惊叹不已。但 GPT 论文中另外一个数字却被多数公众忽略了 —— GPT-3 的训练数据约为 5000 亿(500B)tokens。三年后,Meta 的 LLaMA 将这一数字推到 1.4T,到了 2024 年末的 DeepSeek-V3 更是使用了 14.8T tokens,相当于 GPT-3 训练数据量的近 30 倍。这些天文数字的背后,是一条精心设计的数据管道:从多种来源采集原始文本,经过严格的质量过滤,再以特定比例混合在一起。
数据是模型能力的上限。无论模型架构多么精妙、训练算法多么先进,都只是在尽可能逼近这个上限。如果数据质量不佳或覆盖率不足,无论算法多么优秀,模型能力也必然受限。本文讨论预训练过程如何取得高质量数据,系统梳理数据工程的流程,从数据来源到质量过滤,从混合策略到污染检测,最后探讨合成数据的可能性。
数据来源
在构建预训练数据集之前,首先要解决训练文本从哪里来的问题。互联网上有无穷无尽的文字,却并非所有文字都适合用来训练模型。不同来源的文本在质量、长度、领域覆盖上差异悬殊,本节介绍几个被现代 LLM 广泛使用的数据来源。
CommonCrawl:2007 年,一个名为 CommonCrawl 的非营利项目开始定期爬取互联网上的公开网页,将原始 HTML 数据免费开放给所有人。到 2023 年,单次爬取就包含约 250 亿网页,原始 HTML 大小超过 250 TB。经过清洗和去重后,可用于训练的文本量仍可达数万亿 token。GPT-3、LLaMA、DeepSeek 等模型都将 CommonCrawl 作为主要数据来源。
CommonCrawl 数据集里新闻、博客、论坛、百科、电商页面等几乎无所不包,涵盖 100 多种语言。爬取持续进行,数据因此具有时效性,最新的信息和事件都能在其中找到。不过,正因为爬虫无差别地爬取信息,CommonCrawl 的数据质量参差不齐。新闻文章和维基百科条目旁边,混着大量 SEO 垃圾信息、广告文案和机器生成内容。HTML 标签、导航栏和页脚等与正文内容无关的信息也占据了相当大的比例。同一篇文章被多个网站转载所造成的重复更是 Web 数据的顽疾。所以要把 CommonCrawl 的原始 HTML 变成可用的训练数据,需要经历一条完整的处理流水线:
图:CommonCrawl 数据处理流程
Books3:Web 爬取数据虽然量大,但单个文档通常很短,无法帮助模型学习长距离的语义依赖。一篇博客可能只有几百字,一条论坛帖子甚至只有一两句话。模型要理解"前文铺垫伏笔,后文呼应收束"这样的长文本结构,最好的办法是以完整的书籍作为训练素材。
Books3 数据集正是为此而生的。它包含约 20 万本书籍,来源于 Bibliotik 私有种子网站。相比碎片化的网页,书籍是完整的长篇叙事或系统论述,信息密度更高,有助于模型学习篇章级别的连贯性。同样一万字,一本教科书承载的知识量远超数十篇博客文章。书籍经过编辑校对,语言更加规范,内容覆盖小说、非虚构、教科书和专业书籍等多个领域。
书籍数据最大的问题是版权,Books3 因版权问题被要求下架,引发了关于训练数据知识产权归属的广泛讨论。版权问题迫使研究者寻找替代来源,目前最知名的是 Project Gutenberg,它收录了约 7 万本已进入公有领域的书籍。虽然数量远少于 Books3,但胜在无需担心法律上的争议。
GitHub:网页教会模型如何说话,书籍教会模型如何写文章,代码数据则教会模型如何编程。代码数据对于模型的代码生成能力和逻辑推理能力至关重要,GPT-4、DeepSeek-Coder 等模型都使用了大量代码数据进行训练。
代码数据的主要来源是 GitHub 上的公开仓库,此外还包括 Codeforces、LeetCode 等代码竞赛平台的题目和解答,以及官方文档和 Stack Overflow 上的问答。与自然语言文本不同,代码是形式化语言,有明确的语法结构,每一行都必须符合语法规则。代码中的条件分支、循环、递归等控制流天然就是思维链的训练素材,而注释则是自然语言与形式语言的桥梁,帮助模型学会在两种表达方式之间切换。
arXiv:1991 年,美国物理学家保罗·金斯帕格(Paul Ginsparg)创建了 arXiv 网站,初衷是让物理学家能在正式发表前快速分享预印本论文。三十多年后的今天,arXiv 已经成为物理、数学、计算机科学等领域最重要的学术预印本平台,也是语言模型学习专业知识和学术写作风格的重要数据来源。
学术文献的价值在于它的专业性。深度学习论文中的技术细节、量子物理论文中的推导过程、数学证明中的严密逻辑,这些内容在普通 Web 文本中几乎找不到。论文还有标准的结构规范,从摘要、引言到方法、结论,这种结构本身就是一种有价值的训练信号。LaTeX 公式更是 arXiv 的特色,数学公式的表达方式是语言模型需要学习的特殊语言,LaTeX 源码恰好提供了海量的训练素材。此外,论文之间的引用关系还可以用于构建知识图谱。
不过,学术文献的处理也有特殊的挑战。LaTeX 源码需要被正确解析为可训练的文本,数学公式如何分词是一个独立的技术问题,而且论文中的图表信息目前还难以直接利用。
Wikipedia:与 CommonCrawl 的大浪淘沙不同,Wikipedia 提供的是经过人工审核的高质量百科全书数据。几乎所有 LLM 的训练数据集都包含 Wikipedia,并且给予它超出自然比例的采样权重。
Wikipedia 的核心优势是事实准确性。与网上随处可见的谣言和错误信息不同,Wikipedia 的条目经过社区审核,信息相对可靠,为模型提供了可靠的事实知识。它的结构也十分清晰,明确的标题层级和分类体系让模型容易学习知识的组织方式,条目内部的链接则帮助模型理解实体之间的关系。在多语言方面,每种语言的 Wikipedia 都是独立的知识库,同一条目的不同语言版本还可以用于对齐学习,让模型学会"苹果"和"Apple"指的是同一个事物。
数据质量
CommonCrawl 数 PB 的原始 HTML 中,真正适合训练的文本比例可能不到 10%。如果直接把这些充满噪声的数据喂给模型,就像让学生读一本满是错别字和排版错误的书,不仅学不到正确的知识,还可能养成坏习惯。数据质量过滤是数据管道中首要的环节。
去噪
互联网论坛上大部分帖子是有意义的讨论,但混杂着乱码、机器翻译的生硬句子、"点击下载免费领取"的垃圾广告,以及整页重复同一句话的灌水内容。
人类凭语感就能识别这些垃圾文本,但面对数以十亿计的文档,不可能依靠人工逐一检查,必须用自动化的方法来完成。启发式规则是常用的去噪手段。一段正常的英文文本,标点符号不会太少,停用词(the、is、are 等)也会占一定比例,词汇也不会高度重复。如果一段文本违反了这些基本规律,它大概率是低质量的。下面的代码展示了如何用一组启发式规则来检测低质量文本:
# 低质量文本检测示例
import re
def detect_low_quality(text):
"""检测低质量文本"""
issues = []
# 1. 长度检查
if len(text) < 50:
issues.append("文本过短")
elif len(text) > 100000:
issues.append("文本过长")
# 2. 字符比例检查
# 乱码检测:非 ASCII 字符比例异常
non_ascii_ratio = len(re.findall(r'[^\x00-\x7F]', text)) / len(text)
if non_ascii_ratio > 0.5 and not any(c in text for c in '中文日本語한국어'):
issues.append(f"乱码嫌疑(非ASCII比例: {non_ascii_ratio:.1%})")
# 3. 标点符号检查
# 缺少标点的文本可能是低质量内容
punct_ratio = len(re.findall(r'[.!?。!?]', text)) / max(len(text.split()), 1)
if punct_ratio < 0.01:
issues.append("标点符号过少")
# 4. 重复词检查
words = text.lower().split()
if len(words) > 10:
unique_ratio = len(set(words)) / len(words)
if unique_ratio < 0.3:
issues.append(f"词汇重复度高(唯一词比例: {unique_ratio:.1%})")
# 5. 停用词检查
# 高质量文本通常包含一定比例的停用词
stopwords = {'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
'的', '是', '在', '有', '和', '了', '不'}
stopword_count = sum(1 for w in words if w in stopwords)
stopword_ratio = stopword_count / max(len(words), 1)
if stopword_ratio < 0.05 and len(words) > 50:
issues.append(f"停用词比例异常低({stopword_ratio:.1%})")
# 6. 特殊模式检测
# SEO 垃圾文本常见模式
seo_patterns = [
r'click here',
r'buy now',
r'free download',
r'点击下载',
r'免费领取',
]
for pattern in seo_patterns:
if re.search(pattern, text, re.IGNORECASE):
issues.append(f"SEO 垃圾文本模式: {pattern}")
break
return issues
# 测试示例
test_cases = [
("This is a normal English sentence with proper punctuation and structure.", "正常英文"),
("点击下载免费领取优惠券限时抢购", "SEO 垃圾"),
("asdfghjkl qwertyuiop zxcvbnm", "乱码"),
("the the the the the the the the the the", "重复词"),
]
print("低质量文本检测示例:\n")
for text, label in test_cases:
issues = detect_low_quality(text)
status = "问题: " + ", ".join(issues) if issues else "通过"
print(f"【{label}】")
print(f" 文本: {text[:50]}...")
print(f" {status}\n")
启发式规则简单高效,但只能捕获已知的低质量模式,对更隐蔽的噪声,如这两年开始出现大量语法正确但内容空洞的 AI 生成文本就无能为力。这决定了现代数据管道只能把启发式规则当作前端粗过滤器,管道后端还要加入基于模型的过滤,训练一个轻量级分类器来区分高质量和低质量文本。
去重
互联网上一篇新闻稿可能被几十个网站转载,一段代码可能被无数项目 fork,一条知识可能在问答平台上被反复回答。重复数据不仅浪费训练资源,更危险的是可能导致模型记忆而非学习,对见过的内容倒背如流,对没见过的内容一筹莫展。
最基础的去重方式是精确去重,譬如计算每篇文档的哈希值(如 MD5、SHA256),删除哈希值完全相同的文档。但精确去重无法识别近似重复。两篇文档可能只有几个词的差异(比如转载时修改了标题或添加了来源说明),精确哈希就会将它们视为完全不同的文档。
1997 年,以色列计算机科学家安德烈·布罗德(Andrei Broder)在研究搜索引擎的文档相似度检测时,提出了 MinHash 方法,不直接比较两个文档的内容,而是将它们映射为一组简短的签名,通过比较签名的相似度来估计原文档的相似度。具体来说,MinHash 将每个文档表示为一个词的集合,文档之间的相似度用 Jaccard 相似度来衡量:
分子 是两个文档共有的词的数量,分母 是两个文档中所有不重复词的总数,整体比值衡量的是两个文档有多大的重叠。如果两个文档完全相同,Jaccard 相似度为 1,如果完全没有共同词则为 0。直接计算 Jaccard 相似度需要将两个集合完整比较,成本很高。MinHash 的巧妙之处在于通过一组随机排列将集合映射为签名,使得两个签名的相似度约等于 Jaccard 相似度。使用多个独立的随机排列,就得到 MinHash 签名向量;比较两个签名向量中对应位置相等的比例,就是对 Jaccard 相似度的无偏估计。
# MinHash 去重演示
import hashlib
from collections import defaultdict
class SimpleMinHash:
"""简化的 MinHash 实现"""
def __init__(self, num_hashes=128):
self.num_hashes = num_hashes
# 使用不同的哈希种子模拟不同的排列
self.seeds = [i * 1000003 for i in range(num_hashes)]
def _hash(self, token, seed):
"""计算单个 token 的哈希值"""
h = hashlib.md5(f"{seed}{token}".encode()).hexdigest()
return int(h, 16)
def get_signature(self, tokens):
"""计算文本的 MinHash 签名"""
signature = []
for seed in self.seeds:
min_hash = float('inf')
for token in tokens:
h = self._hash(token, seed)
min_hash = min(min_hash, h)
signature.append(min_hash)
return signature
def jaccard_similarity(self, sig1, sig2):
"""通过签名估计 Jaccard 相似度"""
matches = sum(1 for a, b in zip(sig1, sig2) if a == b)
return matches / len(sig1)
# 演示去重
minhash = SimpleMinHash(num_hashes=64)
# 模拟文档
docs = {
'doc1': "The quick brown fox jumps over the lazy dog",
'doc2': "The quick brown fox jumps over the lazy dog", # 完全重复
'doc3': "A quick brown fox jumped over a lazy dog", # 近似重复
'doc4': "Machine learning is a subset of artificial intelligence", # 完全不同
}
# 计算签名
signatures = {}
for doc_id, text in docs.items():
tokens = text.lower().split()
signatures[doc_id] = minhash.get_signature(tokens)
# 计算相似度矩阵
print("文档相似度矩阵(MinHash 估计):\n")
doc_ids = list(docs.keys())
print(" ", " ".join(f"{d:6}" for d in doc_ids))
for i, id1 in enumerate(doc_ids):
row = [id1.ljust(10)]
for j, id2 in enumerate(doc_ids):
sim = minhash.jaccard_similarity(signatures[id1], signatures[id2])
row.append(f"{sim:.2f} ")
print(" ".join(row))
即使使用了 MinHash 签名,两两比较的复杂度仍然是 ,当文档数量达到数十亿时计算成本不可接受。局部敏感哈希(Locality Sensitive Hashing,LSH)把签名分成若干桶,只有落入同一桶的文档才需要详细比较,将复杂度降为 。实际应用中,去重通常在三个粒度上采用不同的策略来执行:
| 去重级别 | 方法 | 阈值 | 效果 |
|---|---|---|---|
| 文档级 | MinHash + LSH | Jaccard > 0.8 | 删除约 30-50% 重复文档 |
| 句子级 | 精确哈希 | 完全匹配 | 删除约 10-20% 重复句子 |
| Token 级 | N-Gram 去重 | n=13 | 防止模型记忆长序列 |
文档级去重移除整体高度相似的文档,句子级去重处理文档内部或跨文档的重复句子,Token 级的 N-Gram 去重更为精细,确保训练数据中不存在连续 13 个 token 完全相同的片段,从而防止模型通过记忆长序列来作弊。
毒性过滤
除了词法、语法上的质量问题,数据中还存在语义上的安全性问题。包括仇恨言论、暴力描述、色情内容和歧视性语言,模型训练时会将这些内容当作知识吸收,生成时再原样输出。毒性过滤的目标就是在训练前尽可能移除这些内容。
过滤方法按复杂度递进。最简单的是关键词过滤,维护一个敏感词列表,包含敏感词的文档被直接过滤。但这种方法容易误伤,譬如医学文献中频繁出现"性"相关词汇,法律文本中涉及"暴力"的描述,这些都不是毒性内容。更精细的方法是分类器过滤,训练一个毒性检测模型,对文档进行评分,只过滤评分超过阈值的文档。常用的模型有 Google 的 Perspective API、在仇恨言论数据上微调的 HateBERT,以及 Meta 的基于 LLM 的安全守护模型 Llama Guard。最精细的方法是直接用大模型(如 GPT-4)判断内容是否有害,然后用它的标注数据训练一个小模型来进行大规模过滤,兼顾精度和效率。
毒性过滤的关键是阈值的选取。阈值太低会过度过滤,损失大量有用数据,阈值太高则会让有害内容漏网。实践中通常需要在不同维度(暴力、仇恨、色情、自残等)上分别设置阈值,并对医学、法律等专业领域的内容做特殊处理。
语言检测
在将文本交给语言模型训练之前,还需要识别每篇文本的语言,并据此决定是否保留以及如何采样。Facebook 的 FastText 语言识别模型是最常用的工具,支持 176 种语言,速度极快。Python 的 langdetect 库和 Google 的 CLD3 模型也是常见选择。对于单语言模型,只保留目标语言的文本就行。对于多语言模型,需要按语言分组,再根据混合策略决定每种语言的保留比例。
语言检测看似简单,不过是一次 API 或模型调用——实际却有不少需要注意的地方。短文本的检测准确率往往较低,一句话可能同时符合多种语言的语法;同一篇文档也可能混合多种语言(如中英文混写的技术博客)。对于低资源语言,检测模型的训练数据本身就不足,准确率自然受限。
数据混合策略
经过来源采集和质量过滤,我们手上有了来自 Web、书籍、代码、论文等不同领域的高质量文本。使用这些文本就像烹饪一样,好的食材还需要正确的配比,不能简单地混在一起。举个例子,如果 99% 都是 Web 文本,模型可能开口就是地道的"互联网腔";如果代码数据很少,模型的编程能力就会偏弱。数据混合策略要解决的是不同来源之间的采样比例分配问题,使模型在各方面都得到充分训练。
固定配比
观察近年来主流 LLM 的数据配比,可以发现一些趋同的经验规律。以 LLaMA 为例,其训练数据中 CommonCrawl 及其清洗版本 C4 共占了约 82%,GitHub 代码约 5%,Wikipedia 和书籍各约 4.5%,arXiv 和 StackExchange 补充了约 4.5% 的专业知识:
| 数据来源 | 比例 | Token 数量 |
|---|---|---|
| CommonCrawl | 67% | ~940B |
| C4 | 15% | ~210B |
| GitHub | 5% | ~70B |
| Wikipedia | 4.5% | ~63B |
| Books3 | 4.5% | ~63B |
| arXiv | 2.5% | ~35B |
| StackExchange | 2% | ~28B |
这个配比背后的逻辑值得深思:CommonCrawl 占据绝对主导地位,因为它提供了最广泛的通用知识和语言能力。尽管 CommonCrawl 在原始数据中的占比远高于 82%,但经过质量过滤后被大量淘汰,使用比例大幅降低,最终需要从其他高质量来源补充回来。代码数据虽然只占 5%,但对模型逻辑推理能力的提升效果显著。DeepSeek 在其技术报告中提到,代码数据的比例从 7% 提升到 14% 时,模型在 HumanEval 上的通过率提升了近 10 个百分点。Wikipedia 和书籍虽然比例不高,却为模型提供了高质量的知识基准,确保模型在事实准确性方面不偏离轨道。arXiv 和 StackExchange 则补充了专业领域的知识深度。
按领域动态调权
人工设定权重虽然有效,但终究是经验性的,且固定的来源配比隐含着同一来源内的所有文档价值相同的假设。事实并非如此,同样是 Web 文本,一篇维基百科级别的文章和一段论坛灌水文的质量完全不可同日而语。既然 Wikipedia 的信息密度远高于普通 Web 页面,那么即使它只占 4.5% 的原始比例,也可以在采样时给予更高的权重,让模型在训练过程中更频繁地遇到 Wikipedia 的文本。
领域权重(Domain Weighting)就是要打破人工设定的固定权重,对不同领域的数据分配不同的采样权重。2023 年,谷歌研究院提出了 DoReMi(Domain Reweighting with Minimax Optimization)算法,让模型自己决定需要什么数据。DoReMi 先训练一个小型代理模型,然后让代理模型在训练过程中动态调整不同领域数据的采样权重。如果一个领域的损失下降得慢,说明模型在这个领域还有学习空间,就增加该领域的采样权重,反之则减少。这种方法使得训练过程中数据的配比不再是固定的,而是随模型的学习状态动态调整,无需人工调参。而且权重本身具有可解释性,它直观地反映了各领域的学习难度。
数据污染
2023 年,一项令人尴尬的发现震动了 NLP 社区:多个开源大模型在 MMLU、GSM8K 等基准测试上取得异常高分,原因竟是测试集数据混入了训练集。模型不是学会了解题,而是提前见过了答案。这种现象被称为数据污染(Data Contamination),是预训练数据工程中最容易被忽视的问题。
这次数据污染并不是有人故意往训练数据里塞测试题。它的产生方式要隐蔽得多,是维基百科上有人添加了 MMLU 试题的完整题干和答案,Stack Overflow 上有人讨论 LeetCode 题目的解法,而 LeetCode 恰好是代码评测的数据来源。此外,一篇 arXiv 论文在附录中列出了 GSM8K 的数学题和标准解答。这些内容一旦被爬取进入训练数据,模型就会在评测中作弊,得到的分数虚高,无法真实反映模型的能力。
污染可以按严重程度分为三个层级。最严重的是输入 + 输出完全匹配,测试题的题干和标准答案都原封不动地出现在训练数据中,模型只需回忆即可答对。其次是输入匹配,只有题干出现,模型见到了问题但没见过标准答案,仍然比未见过该题的模型有优势。最轻的是近似匹配,训练数据中出现了与测试题相似但不完全相同的内容,可能给模型提供了一定的解题线索。
如何高效地从数万亿 token 的训练数据中找出与测试集重叠的片段才是真正的挑战。最直接的方法是 N-Gram 匹配,将测试集中的每个样本切分为连续的 n 个词,然后在训练数据中搜索是否出现了相同的序列。GPT-2 的技术报告使用了 8-Gram 和 10-Gram 两种粒度进行检测。N-Gram 匹配简单可靠,但对于改写了措辞的近似污染无能为力。只能使用基于语义的检测方法才能有效识别。用词嵌入模型将文本映射到向量空间,通过计算向量相似度来识别出语义上高度相似但措辞不同的文本,但这样计算成本就变得很高了。
数据污染处理方式取决于发现的时机。如果还在数据准备阶段,最简单的方法是直接删除被污染的文档或段落。如果模型已经训练完成,就只能通过在评测时排除被污染的样本、报告未污染样本的分数来补救。这不是在修复模型本身的问题,但至少能让评测结果更具参考价值。
数据污染的检测看似是一个技术问题,本质上却关乎研究的诚信。提前偷看了考试卷的学生,分数再高也不能代表真实水平。随着社区对数据污染的关注度提升,主流评测基准都开始要求报告去污后的结果,新的训练数据集也普遍将 Decontamination 作为标准流程。
合成数据
前面讨论的所有数据来源,无论是 Web 爬取还是书籍扫描,本质上都是消费人类已经产生的文本。随着模型规模的增长,高质量的人类生成数据正在被快速消耗。有研究估计,按照目前的使用速度,互联网上的高质量文本可能在 2026 年前后就被训练殆尽。面对数据枯竭的威胁,研究者开始探索一个新的想法:能不能用模型生成的文本来训练模型?
2023 年,微软研究院在 Phi 系列模型的工作中提出与其让模型阅读互联网上的海量低质量文本,不如用 GPT-3.5 或 GPT-4 这样的强模型,按照特定的教学大纲,精心生成高质量的训练数据。这种教科书级的合成数据质量远高于普通 Web 文本。Phi-1 仅用 1.3B 参数和远少于 LLaMA 的训练数据,就在编程基准上取得了令人瞩目的成绩,有力地证明了数据质量对模型性能的决定性影响。
这种方法的逻辑在于质量胜于数量。传统预训练数据中,大量低质量文本占据了训练资源却不贡献有效学习信号,甚至可能产生负面效果。合成数据则可以按需生成,针对模型的薄弱环节定向补充训练素材。具体来说,合成数据的生成有三种主要方式。第一种是强模型生成,用 GPT-4 等大模型根据精心设计的提示词生成特定领域、特定难度、特定风格的文本。Phi 系列就是使用 GPT-3.5 生成的编程教材和练习题来训练的。第二种是自我蒸馏(Self-Distillation),用模型自身的输出来训练自己,通常是先让模型生成多个候选回答,再用某种质量信号(如奖励模型评分)筛选出高质量的部分加入训练数据。第三种是数据增强,对已有数据进行改写、扩展、翻译等变换来生成新的训练样本,成本最低但多样性也相对有限。
Phi-2 以 2.7B 的参数量,在多项基准上超越了参数量为其 2-3 倍的 LLaMA-2 7B 和 Mistral 7B,这证明了高质量合成数据确实能打破模型越大越好的简单线性假设。合成数据前景诱人,局限性也同样真实。
最根本的担忧是模型坍塌(Model Collapse)。2023 年,牛津大学的伊利亚·舒马伊洛夫(Ilia Shumailov)等人从理论上证明了如果一个模型完全用另一个模型的输出来训练,若干代之后,生成的分布会逐渐退化。模型会忘记原始数据中的长尾分布,越来越倾向于生成高频的常见内容,最终导致多样性丧失。这就像反复复印一份文件,每一代都会丢失一些细节,最终变成模糊的影子。
合成数据还存在偏见放大(Bias Amplification)问题,模型生成的内容会继承并放大训练数据中的偏见,如果这些内容又被用来训练下一代模型,偏见就会像滚雪球一样越滚越大。在事实准确性方面,模型可能生成看似合理但实际错误的信息,这些错误如果混入训练数据,会对下一代模型的可靠性造成严重损害。此外,当前模型生成的合成数据仍局限于训练分布内的内容,无法像人类那样创造真正新颖的知识。
因此,当前的主流观点是合成数据可以作为人类数据的补充,而不能完全替代。最务实的策略是人机混合,让模型生成初稿,再由人工审核修正。或者用合成数据做数据增强,在人类数据的基础上扩展多样性,不能从零开始就只用合成数据训练。
本章小结
预训练数据工程是一条从"原始素材"到"训练燃料"的精密流水线:从 CommonCrawl、书籍、代码、学术论文和维基百科等多种来源采集文本,经过质量过滤和去污清洗,再按策略混合配比。经过本节的介绍,我们知道了数据从哪里来、如何清洗和混合。但面对数万亿 token 的数据,到底需要多少计算资源才能训练出一个强大的模型?模型规模、数据规模与计算量之间是否存在可预测的规律?下一章将探讨缩放定律和这些问题的数学答案。
练习题
实现一个简单的数据质量过滤管道,依次对输入文本执行长度过滤、语言检测、重复度检测和 SEO 垃圾检测。要求每个过滤步骤都能输出具体的过滤原因。
参考答案
import re def quality_pipeline(text): """多步骤数据质量过滤管道""" reasons = [] # 步骤 1:长度过滤 if len(text) < 30: reasons.append("过短") elif len(text) > 100000: reasons.append("过长") # 步骤 2:语言检测 chinese_ratio = len(re.findall(r'[一-鿿]', text)) / max(len(text), 1) if chinese_ratio > 0.3: lang = "中文" else: lang = "英文/其他" # 步骤 3:重复度检测 words = text.lower().split() if len(words) > 10: unique_ratio = len(set(words)) / len(words) if unique_ratio < 0.3: reasons.append(f"词汇重复度过高({unique_ratio:.1%})") # 步骤 4:SEO 垃圾检测 seo_patterns = [r'click here', r'免费领取', r'buy now'] for p in seo_patterns: if re.search(p, text, re.IGNORECASE): reasons.append(f"SEO垃圾模式: {p}") break passed = len(reasons) == 0 return passed, reasons, lang # 测试 samples = [ "这是一段正常的中文学术文本,讨论了深度学习在自然语言处理中的应用。", "click here to buy now free download限时优惠", "the the the the the the", ] for text in samples: passed, reasons, lang = quality_pipeline(text) status = "通过" if passed else f"过滤({', '.join(reasons)})" print(f"语言: {lang}, 状态: {status}")点击 Run 按钮执行代码MinHash 签名的期望相似度等于 Jaccard 相似度,请解释这一结论的直觉含义和数学证明思路。
参考答案
直觉含义:MinHash 对集合做一次随机排列,取排列后最小元素作为签名值。两个集合 、 在这次排列下碰巧取到同一个最小元素,等价于排列中最先出现的元素恰好落在 中——而这个概率恰好等于 ,即 Jaccard 相似度。因此,多次独立排列后签名相等的比例就是对 Jaccard 相似度的无偏估计。
数学证明思路:固定一个随机排列 ,令 为集合 在该排列下的 MinHash 值。要证:
- 将全集元素按 排列,从前往后扫描,最先遇到的元素一定属于 。
- 若该元素同时属于 ,则 (两者最小值相同);否则属于 或 ,最小值不同。
- 因此 当且仅当 中排在最前面的 元素落在 中。
- 排列是随机的,最前元素落在 的概率为 。
用 个独立排列得到 位签名向量,签名相等的比例是 的无偏估计量,方差随 增大而降低。
