简介大规模预训练语言模型的数据集构成是当前模型可复现性与透明度研究的重要议题。这份英文报告系统梳理了2018年至2022年初用于训练GPT-1、GPT-2、GPT-3、GPT-NeoX-20B、Megatron-11B、MT-NLG及Gopher等模型的数据集逐一拆解维基百科、Common Crawl、书籍语料、Reddit链接等来源对比各数据集大小、令牌数量与内容占比指出主流实验室在训练数据披露上的缺失并强调建立统一文档标准的重要性。报告引用大量参考文献并附有Top 50资源等附录适合自然语言处理研究者、机器学习工程师及数据科学家作为模型溯源、数据配比优化与数据审计的参考。压缩包为单个PDF文档2.36MB结构清晰便于离线阅读、标注与快速查阅。该资源已有112人学习对关注大模型底层数据构成、希望提升训练数据可复现性的读者具有较强实用价值。1. 预训练语言模型的数据集分析为什么大多数团队输在数据准备这一关模型选型和训练框架的讨论总被放在最前面可真正决定一个预训练语言模型能走多远的往往是被当作“黑匣子”处理的数据集。大规模预训练语言模型数据集的综合分析不是把语料下下来交给训练脚本而是对来源、去重、过滤、配比、泄露检测这些环节做一次系统性的盘账。这篇文章把分析拆到可执行先回顾几代公开数据集的组成逻辑再给出五个关键质量维度然后提供一套可以直接跑的 Python 分析脚本最后梳理最容易误判的几个坑。适合正准备自建语料、评估是否采用某个开源数据集、或是被数据配比问题困扰的算法工程师。2. 数据集形态的三个时代从人工清洗到网络快照再到多源混合2.1 WebText 与 BooksGPT-2/BERT 之前的语料逻辑现在回看 BERT 那批语料会觉得相当朴素BooksCorpus 加上英文维基百科总字数大约是八亿词和二十五亿词这个量级全部文本经过标准化清洗句子边界干净没有网页导航和广告噪音。这种“小但精”的语料是当时构建神经语言模型的基本逻辑——先保证文本质量再谈规模。今天很多团队在刚开始做数据的时候反而会犯相反的错误一上来就堆几十 TB 的网页快照结果过滤阶段就耗费掉大部分人力。GPT-2 的 WebText 给业界带来第一个真正意义上的“质量信号”概念。它把 Reddit 上被用户投票较高的外部链接作为文本池本质上是用社区投票当弱标注把“人类愿意读什么”变成数据筛选器。这个思路影响深远后续几乎所有数据集都会引入某种人工偏好或领域专家的先验信号而不是单纯看语法是否正确。对从业者的启发是数据准备的第一步不是写脚本而是明确“你认可什么样的文本质量”以及“哪些源头更接近你的目标分布”。当时还有个容易被忽视的细节GPT-2 做了一次分量不小的清洗把网页标签、重复模板、导航文本都去掉了。这说明即使是从高质量来源抓文本原始 HTML 的污染率也不会低。我一般建议团队在分析任何数据集前先随机抽两百条原始样本人工过目一遍对污染类型做分类统计再决定后续用词级别还是句子级别的过滤器。2.2 清洗之后的 Common CrawlC4 与 The PileCommon Crawl 每月从互联网抓取的海量网页快照是预训练语料库从“精选小众”转向“海量噪声”的分水岭。直接用原始抓取数据训练几乎不可行真正有价值的是清洗后的版本。Google 的 C4 是这类清洗链路的典型代表从 Common Crawl 出发先丢弃非英文页面再过滤掉包含“JavaScript”、“法规”等关键词的文本做一次近似去重再按语言模型困惑度等信号做质量打分。处理完之后C4 大约有 750GB 原始文本、十五万级别以上 token成为 T5 时代的标配语料。The Pile 则走了一条不同的路它刻意不追求“全网最大”而是把 ArXiv、GitHub、Stack Exchange、PubMed、书籍等二十多个高质量子集混合在一起总原始数据约 800GB。它验证了一件事领域多样性对模型能力的广度影响巨大缺代码语料的模型在程序合成任务上会明显偏弱缺数学语料的模型在推理类任务上也会露出疲态。但 The Pile 也有自己的问题某些子集包含相当多的噪声标签和作者偏好如果不做二次过滤下游任务会被这些隐藏偏差带偏。数据集来源构成规模量级代表性模型BooksCorpus电子书约 8 亿词BERT 系列C4Common Crawl 过滤后英文文本约 750GB / 156B tokensT5The Pile22 个混合子集约 800GB 原始GPT-J / GPT-NeoXRedPajama复刻 LLaMA 配方的多源混合1.2T tokensRedPajama-INCITESlimPajamaRedPajama 清洗 去重627B tokensOLMo 等RefinedWebCommon Crawl 多级过滤数 T tokens 量级Falcon这张表想说明的不仅是规模增长更重要的是配方差异同样一批底层来源过滤策略和配比不同产出的模型能力会差出一大截。2.3 公开复现时代RedPajama、SlimPajama 与 1T Token 共识进入 LLaMA 时代之后数据工程被推到了前所未有的公开透明度上。LLaMA 系列公开了语料配比Common Crawl 占大头C4、维基、书籍、ArXiv、Stack Exchange、GitHub 各自占有不同比例总计约 1.4T tokens。这个配比数据被不少人当成配方表RedPajama 数据集就是按它复刻出的 1.2T tokens 开源版本。跟着 RedPajama 走了一遍全流程的人会发现复刻配方容易复刻清洗质量很难——同样的源按官方描述做一遍token 量分布会有细微差异这些差异在训练收敛曲线上都会被放大。SlimPajama 给了第二个重要提示RedPajama 经过全局近似去重和进一步质量过滤之后体积从 1.2T tokens 降到 627B tokens减少了将近一半。后续用 SlimPajama 训练的一系列模型并没有因为数据量减半而明显变弱反而在部分任务上更稳。这说明大语料里被浪费的重复数据远比想象的要多去重和过滤不是在“削减数据”而是在“找回原本该有的数据效率”。我一般会建议团队把去重分析放在抽样阶段就做掉不要等到全量清洗完才开始查重否则计算和存储都白烧一轮。2.4 配比与监督信号多源混合不是堆材料数据配比是预训练里最像“玄学”的部分但背后仍有一些可复用的经验。Books、ArXiv、Stack Exchange 这类高质量语料在总 token 里占比很小却对模型的知识密度和推理能力贡献极大Common Crawl 占比很大但单篇文本的平均信息密度低过度提高它的采样权重会让模型变得“话多而空洞”。常见的做法是先按 token 占比设定一个初始配比然后用小规模对照实验调倍率而不是一开始就追求全网最大语料。在配比之外监督信号的混合也属于数据集分析的一部分。纯网页文本的监督信号太疏代码语料提供了更强的结构性信号维基百科和书籍提供了段落级的逻辑闭环指令类数据则在后期对齐阶段起作用。把这些信号按训练阶段分离来看比把它们全部混在一起更健康。尤其是大规模预训练语言模型的数据集分析必须区分“预训练阶段语料”和“对齐阶段语料”两者承担的任务不同清洗标准也不同。3. 五个必须落地的质量维度规模、去重、过滤、平衡与泄露检测3.1 规模统计Token 计数不等于字节数预训练数据集的规模统计看起来是最简单的一步实际翻车率很高。很多人在分析报告里写“数据集共 1.2TB”但 1.2TB 到底是磁盘占用、文本字符数还是 token 数这三个数字在中文、代码和多语言混合语料上的差异非常大。同一个数据集用 GPT-2 的 BPE tokenizer 和用 LLaMA 的 SentencePiece tokenizer 统计结论可能相差 20% 以上。因此分析的第一步必须锁定 tokenizer并让分析脚本与训练脚本用同一套分词配置。实际操作中我会对每个 jsonl 文档统计字符数、token 数、单词数和文档长度分布并单独观察“超长文档”和“极短文档”两端的占比。极短文档往往是解析残留或对话片段超长文档则可能是一本书或一个被拼接的网页两类都值得单独标记而不是只报一个平均长度。你后边做配比时这些统计数字直接决定每个来源的采样权重一旦早期统计失真后期整个训练计划都会被带偏。3.2 重复率量化文档内、文档间与跨数据集的三种重复重复文本对预训练的影响已经是被反复验证的教训高重复率会拉高模型对训练样本的记忆程度导致验证损失曲线出现“看着很低、实际泛化很差”的假象。但重复不能只看“文件是否完全一样”这一种情况。文档内重复指单篇文章中反复出现的段落或句子常见于 SEO 拼凑页和自动生成内容文档间重复指不同文档之间存在大段相同文本比如转载和镜像站点跨数据集重复则是把多个开源语料混合时同一批网页也在别的数据集里出现。最后一种最隐蔽因为单独看每个来源的去重率都很低混到一起后重复比例突然飙到两位数。要量化三类重复常见做法是先做精确 hash 去重再做 MinHash 近似去重。精确去重用 sha1 或 md5 对整篇文档算指纹速度快但只能抓住完全一致的副本近似去重则需要把文本切分成 shingle再用 MinHash 计算 Jaccard 相似度。很多团队只做了精确去重就开始训练结果 MinHash 扫描后才发现有大量“标题相同、正文加了一段推广文字”的近似重复文档这部分对训练信号的污染同样严重。3.3 质量过滤信号启发式、语言模型与分类器各自的位置质量过滤是数据集分析里最需要手工作业的环节。启发式规则是第一道防线主要处理标点符号比例、HTML 残留、代码片段误混、URL 残留、无意义重复句等显性污染。它不需要模型参与执行快适合在大规模语料上做初筛。真正让团队纠结的是后续的“语义级”过滤哪些文档虽然语法完整但内容空洞哪些文档领域价值不高语言模型困惑度是常用的语义打分手段做法是拿一个已经训练好的小型语言模型计算每篇文档的困惑度困惑度过高的文本通常被认为是“不像自然语言”或“领域偏差过大”。但这里有个容易被忽略的坑困惑度低不代表质量高一本精心书写的法律文书可能拿到很低的困惑度但它对通用预训练的价值未必高于一篇科普文章。困惑度更像是一个粗粒度异常检测器而不是质量排序器。更稳的做法是训练一个 fastText 或小型 Transformer 分类器用少量人工标注样本区分“高质量领域文本”和“低质 网页噪声”再把困惑度结果作为特征喂进去。3.4 语言与领域配比低资源语料的脆弱性语言配比直接关系到模型的多语言能力。英语语料在绝大多数开源数据集中占绝对主导中文、日文、西班牙语等语种加起来的 token 也不一定能超过英语的一半。分析语言分布时第一件事是统一语言识别工具fastText 的 lid.176 模型是行业里最常见的选型它对句子长度敏感短文本识别误差较大。中文语料还需要额外处理繁简、分词差异和编码残留否则 GB 级别的统计结果可能包含大量无效字节。低资源语料在过滤环节特别脆弱。质量分类器通常是在英语样本上训练的迁移到低资源语言时很容易把正常表达判成噪声。数据量一旦少于某个阈值过滤后的剩余文本可能连一个小型 tokenizer 都训练不出来。我的建议是给每个语言族设定一个保留底线分析阶段就把语言 ID、过滤前后 token 量、平均困惑度三个字段写进元数据后续任何清洗步骤都不能让某类语种低于这个底线除非你明确决定放弃该语言能力。3.5 数据泄露检测与评测集的重叠就是日后翻车的伏笔数据泄露对大规模预训练语言模型数据集的影响经常被低估。如果评测集里的原题、改写题或长段落已经在预训练语料中出现过模型在下游评测上的分数会虚高且这种虚高无法通过增加训练数据来消除。最常见的检测手段是 n-gram 重叠扫描把评测集文本切成 8-gram 到 13-gram 的片段再在预训练语料里做倒排索引查询。重叠比例超过一定阈值就需要逐条审查是合理的领域知识重复还是真实的任务样本泄漏。如果检测出高重叠不是简单删掉那几篇文档就完事。很多评测任务的数据来自网页、维基或 GitHub这些内容本身就在 Common Crawl 里完全去除几乎做不到。更合理的处理是把重叠文档按“完全命中、改写命中、语义近似”分成三档完全命中直接剔除改写命中做人工复核语义近似则通过降低该领域采样权重来缓解。这个分析结果同时要写进模型卡里否则别人在无泄漏数据上复测你的模型时分数对不上信任度就崩了。4. 亲手搭建分析工具链统计、去重、困惑度与泄漏检测四条脚本4.1 从 jsonl 读入并产出 Token 级统计大型预训练语料最常见的存储格式是 jsonl每行一个 JSON 对象text 字段存放正文。这一节先从读取和基础统计开始。以下脚本使用 zstandard 处理 .zst 压缩适合直接从开源数据集目录读取import json import zstandard as zstd from tiktoken import get_encoding def open_jsonl(path: str): if path.endswith(.zst): dctx zstd.ZstdDecompressor() raw open(path, rb) return dctx.stream_reader(raw) return open(path, rb) enc get_encoding(o200k_base) # 用训练一致的 tokenizer 效果更准 total_docs 0 total_chars 0 total_tokens 0 short_docs 0 for line in open_jsonl(pretrain.jsonl.zst): obj json.loads(line) text obj.get(text, ) if len(text) 80: short_docs 1 tokens len(enc.encode(text)) total_docs 1 total_chars len(text) total_tokens tokens print(fdocs{total_docs}) print(fchars{total_chars}) print(ftokens{total_tokens}) print(fshort_docs_ratio{short_docs / total_docs:.4f})这段脚本的关键在 tokenizer 选择。tiktoken 的 o200k_base 只是通用基线如果你的训练脚本用 LLaMA 的 SentencePiece这里就要替换成与训练完全一致的分词器否则 token 统计会失真。short_docs_ratio 这个指标专门用来捕捉“无效短文本”的占比阈值 80 字符是我常用的初始值你可以根据语料类型调整。4.2 用 MinHash 估算近似重复率精确去重只能抓完全一致的文档真正的重复大户往往是近似重复。下面这段脚本用 datasketch 做 MinHash 和 LSH 查询统计文档间的近似重复比例from datasketch import MinHash, MinHashLSH NUM_PERM 128 THRESHOLD 0.7 SHINGLE_K 13 def make_minhash(text: str): words text.split() if len(words) SHINGLE_K: return None mh MinHash(num_permNUM_PERM) for i in range(len(words) - SHINGLE_K 1): gram .join(words[i:i SHINGLE_K]) mh.update(gram.encode(utf-8)) return mh def scan_duplicates(docs): lsh MinHashLSH(thresholdTHRESHOLD, num_permNUM_PERM) dup_ids set() for idx, doc in enumerate(docs): mh make_minhash(doc) if mh is None: continue matched lsh.query(mh) if matched: dup_ids.add(idx) lsh.insert(str(idx), mh) return dup_ids参数说明num_perm 是 MinHash 的哈希函数个数128 是性价比比较高的选择256 会更精确但内存翻倍。THRESHOLD 设为 0.7表示 Jaccard 相似度在 0.7 以上的文档会被判定为近似重复如果希望只保留非常严厉的去重可以把阈值提到 0.8 以上。SHINGLE_K 取 13这是文本去重场景里一个常见经验值太大会漏掉改写较多的文本太小会把同主题的正常文章误判为重复。4.3 用一个小模型给每篇文档打困惑度分困惑度是质量过滤里的信号之一。用一个已经训练好的小型因果语言模型对每篇文档计算平均 token 的交叉熵再取指数得到困惑度。这里用 Hugging Face 的 transformers 实现import math import torch from transformers import AutoTokenizer, AutoModelForCausalLM model_name gpt2 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name).to(cuda) model.eval() def score_ppl(text: str): enc tokenizer(text, return_tensorspt, truncationTrue, max_length512) if enc[input_ids].size(1) 20: return None with torch.no_grad(): logits model(**enc).logits[:, :-1].contiguous() labels enc[input_ids][:, 1:].contiguous() loss torch.nn.functional.cross_entropy( logits.view(-1, logits.size(-1)), labels.view(-1) ) return math.exp(loss.item())推理逻辑是模型对每个 token 预测一个概率交叉熵越小说明这段文本越接近模型学到的语言分布困惑度就越低。实际使用时要记得两点一是模型选型必须与目标语料领域接近拿着英文 GPT-2 给中文医疗文本打分没有参考价值二是困惑度只建议作为排序特征不要单独指定一个硬阈值来粗暴删除否则会把低资源语言和特殊领域文本误杀。4.4 用 N-gram 碰撞做评测集泄露检查泄露检测需要把评测集和预训练语料放到同一个 n-gram 空间里做碰撞。下面用离线扫描的方式把评测集的 n-gram 集合构建成内存字典再遍历语料文档统计命中次数from collections import defaultdict def build_ngram_index(docs, n8): index defaultdict(list) for doc_id, doc in enumerate(docs): words doc.split() if len(words) n: continue for i in range(len(words) - n 1): gram .join(words[i:i n]) index[gram].append(doc_id) return index def check_leakage(corpus_docs, eval_docs, n8): eval_index build_ngram_index(eval_docs, n) hit_docs set() for doc in corpus_docs: words doc.split() if len(words) n: continue for i in range(len(words) - n 1): gram .join(words[i:i n]) if gram in eval_index: hit_docs.add(i) break return len(hit_docs) / len(corpus_docs)参数上n 取 8 会让扫描更敏感适合做“找疑似泄漏”的第一轮筛选确认阶段建议提高到 13减少普通知识性文本带来的误报。评测集的 n-gram 索引如果内存吃紧可以先用 MinHash 对评测集做粗筛只对有相似嫌疑的文档再做精确 n-gram 碰撞。5. 数据集分析避坑指南五个最容易导致误判的坑5.1 去重阈值过强把代码和文档一起删掉现象做完全局 MinHash 去重后训练 loss 不降反升模型在代码生成任务上的表现尤其差。检查数据量发现删掉了接近一半文本远超出“重复数据”应有的比例。原因Thresold 设到了 0.9SHINGLE_K 又取到了 5导致大量同主题、同模板但内容不同的合法文档被识别成近似重复。代码文件里重复的 import 和函数定义特别多很容易触发高相似度判定。解决先把阈值降到 0.7 重新扫描对比两次删除量的差异代码语料要单独处理计算相似度时可以忽略只含 import、include、package 等关键词的行每次去重都保留一份“被删文档清单”供人工抽查而不是只看一个删减比例数字。5.2 困惑度模型与目标领域不匹配过滤结果完全失真现象用英文通用模型给中文法律语料打分几乎所有领域文本都被标成高困惑度过滤后语料只剩通用新闻和百科领域能力明显下降。原因困惑度反映的是“这段文本像不像模型训练分布”而不是“这段文本有没有价值”。目标领域与模型训练分布差异越大评分失真越严重。解决要么在目标领域采样几千条文本微调一个小型质量分类器要么把困惑度只作为“离群点检测”信号结合长度、标点、领域关键词等规则综合判断。不要拿单个通用模型的困惑度分数当质量金标准。5.3 用全局 token 数推导配比忽略数据内部的噪音权重现象配比表里 Common Crawl 占了 60% token但训练出来的模型总是显得啰嗦、知识密度低同一个配比换成书籍和 ArXiv 后模型在问答任务上反而变好。原因全局 token 数没有考虑“有效信息密度”。低质量网页文本虽然 token 多但大量内容是无意义重复和广告话术实际贡献的训练信号远低于同 token 数的书籍文本。解决在配比计算前先按质量分层统计每个来源的 token 量把“高分高质量文本”和“低分噪声文本”分开记录。采样时对低质量层降权甚至直接丢弃高层里占比最大的那部分。数据配比应该是“有效 token”的配比不是“原始字符”的配比。5.4 只做文档级去重漏了句子级与模板级重复现象文档级去重率不到 5%但模型生成结果里总是出现相同的官方腔开头和固定列表结构像是背下了某些网页模板。原因重复发生在段落或句子层级。很多 SEO 页面由同一个模板生成每页中间内容不同但开头介绍和结尾引导语完全一样。文档级去重无法捕捉这种局部重复。解决对每篇文档做段落切分再对所有段落做 MinHash 索引也可以把高频模板句提取出来建立黑名单在过滤阶段直接去除。检查模板级重复时可以把相似度计算单位从整篇文档缩小到连续三到五个句子。5.5 分析脚本与训练脚本的分词器不一致现象分析报告里写语料共 30B tokens训练时却显示 45B配比参数完全偏离预期中文语料的偏差尤其明显。原因分析阶段用通用 tokenizer 估算训练框架里实际用的是另一个词表两者对中文分词、英文子词切分的结果差异很大。tokenizer 不一致让所有基于 token 的统计、配比、学习率设置都站不住脚。解决把 tokenizer 的配置文件作为数据版本的一部分分析、清洗、训练三个环节必须引用同一个 tokenizer 路径。每次统计时在脚本开头打印 tokenizer 名称和词表大小方便回溯。6. 用一个小实验验证你的数据配方最小可行基线6.1 三天内建立质量闭环完整的预训练实验动辄几周不适合用来快速验证数据是否干净。我一般会从待评估的数据集里随机抽取 20 万到 50 万篇文档作为子集再用一个参数量在 0.5B 以下的小模型跑一到两万步训练。这个量级在两三天内能看出曲线差异足够判断某个清洗方案值不值得推广到全量。验证时不只看训练 loss还要固定一个包含摘要、常识问答和代码补全的小评测池每五千步测一次。6.2 自监督与下游任务的双重验证自监督验证观察的是 loss 收敛速度和稳定性数据净化做得越干净验证集 loss 和训练集 loss 之间的差距越小过拟合到来的步数越晚。但自监督信号只能说明“模型更容易学习这堆数据”不能说明“学到的东西有用”。所以关键在于第二步——下游任务评测池必须和最终业务场景对齐。如果模型要对中文医学问答做预训练评测池里医学问答和英文法律文本的比例就要拉开。把离线指标和采集少量的人工评估样本结合比单独依赖自动指标更能暴露数据配比的问题。我现在的固定习惯是每次改动数据都记一份版本说明写明 tokenizer 路径、去重阈值、过滤规则和采样比例。这行记录在分析时觉得很麻烦但三个月后回看项目它是唯一能让你快速定位“模型为什么变成这样”的后悔药。数据工程里最贵的成本从来不是计算而是忘掉自己当初怎么处理数据的。希望这篇文章能帮你在第一次做数据准备时就避开这些弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?