
做过几次大模型训练项目之后我悟出一个特别朴素又容易被人忽略的真相模型结构可以照搬算力可以堆但真正拉开落地效果差距的往往不是注意力头数也不是学习率调度而是喂给模型的那几万亿token。大模型训练全流程里预训练数据集构建是最琐碎、最不性感、却又最致命的一个环节。同一个模型同一份算力换了批数据下游效果能差出好几个档次。这篇是实战篇第十六篇我单独把预训练数据集的构建拿出来讲透从数据构成、来源、清洗、去重、配比到训练前的验证和污染排查完整过一遍我在实际项目里的做法和踩过的坑。1. 预训练数据集整体设计与构成拆解1.1 数据决定模型的下限很多人把大模型训练理解成“堆算力跑起来就行”但我在项目里反复看到的情况是训练跑到一半loss降不下去或者downstream效果不稳定最后追根溯源都能回到数据上。模型本质上是在学习训练数据中的统计分布你给它的数据是什么质量它就长成什么样子。说得直白一点模型是一个“照单全收的实习生”数据就是教材教材里全是错题实习生考试必然出错。预训练数据集不光影响模型的知识广度还决定了模型的“语言习惯”。比如数据里代码比例高了模型在代码生成上的表现会好但纯自然语言任务可能变差中文语料占比低了模型中文表达可能生硬。所以构建数据集的第一步不是写脚本而是想清楚你的模型目标是什么、你手里有什么数据、每个来源的数据准备用多少。这些决策会影响后面所有环节。1.2 一份可用预训练数据的基本构成一份用于通用大模型训练的预训练数据集常规会包含这几大类网页数据来自爬虫或公开快照占比通常是最大的往往占到60%到70%。书籍和长文本提供连贯的长程语义和高质量叙事占比10%到15%。学术论文、百科、问答社区用来补充知识密度高的内容占比5%到10%。代码数据GitHub等来源占比15%到20%如果模型需要强化代码能力比例还要更高。多语言数据根据目标用户群体决定比如中文模型要加入足够的中文语料。token总量上一个7B模型通常需要1T以上token做预训练10B到30B模型按1.5到2T准备也不夸张。理论公式各家不一但行业里有个常见经验数据量至少要是模型参数的100倍以上冗余度高一点没坏处。数据不足时模型容易表现为“知道但表达不清”或者“知识面窄”。我建议先按这个总量倒推每个来源需要多少原始文本再去规划采集和清洗流程而不是先抓一堆数据再说。2. 数据来源与采集落地2.1 开源数据集可以直接用吗现阶段做预训练大家几乎都会从公开语料起步。C4、The Pile、RedPajama、RefinedWeb、OSCAR这几类都是很常被拿来做基座的数据集。C4基于Common Crawl快照做了清洗量大概在800GB文本左右The Pile做的是多来源混合包含书籍、论文、代码、对话等RefinedWeb在抽取质量上做得更讲究文本更干净OSCAR则主打多语言网页文本。但这里有个容易踩的坑这些开源数据集大多数以英文为主中文占比很低直接用中文场景的模型效果会打折扣。另外它们各自有各自的清洗逻辑有的去重做得松有的过滤规则偏激进你拿来做基座前最好自己抽样看一遍。我的做法是把开源数据集当作“起始库存”再补充自有的中文、行业和代码数据混出符合业务目标的比例。还有一个需要注意的事项使用外部数据集时要关注版权和使用条款。优先选择明确允许研究或商业使用的来源把来源、版本、快照时间记录清楚方便追溯。这不是小事后面前置风险排查时来源清晰的语料能省掉很多麻烦。2.2 自建爬虫管线的关键取舍如果需要长期积累语料或者要补充特定领域的内容自建爬虫是绕不开的。工业级做法基本围绕Common Crawl快照展开因为它提供了一个月级更新的全网爬取结果。处理流程大体是下载WARC或者WET文件WET已经是抽取过的纯文本相对省事然后按域名、语言、内容类型做筛选最后丢弃导航、广告、模板性内容。我建议自建爬虫时不要追着实时页面抓取抓回来的页面噪音非常大处理成本高。更可控的方案是先从一个小的种子站点集开始抓取后跑一轮严格清洗再评估内容质量逐步扩大站点池。抓取过程中要控制并发设置合理的robots规则避免给对方服务器造成压力。这类工程细节虽不复杂但直接影响采集稳定性和合规风险。3. 清洗、过滤与去重的完整流水线3.1 从HTML到干净文本解析与抽取数据落盘后第一件事是把HTML标签、导航栏、评论区、广告这些“噪音”去掉。网页正文抽取这块trafilatura和readability-lxml是实践里用得最多的两个库。trafilatura对新闻、博客这类页面的正文识别效果很好输出结构化字段可以拿标题和正文一起用readability-lxml更轻量适合批量处理。如果拿到的已经是WET纯文本这一步会轻很多但仍然要处理剩下的URL、日期、乱码等无意义文本。编码检测也要在这一步做chardet或uchardet把非UTF-8内容统一转换识别不了的一律丢弃。语言识别推荐fastText的lid.176模型速度快准确率高先过滤掉非目标语言后续再细看。这个阶段的目标是让数据“看起来像正常文章”不要追求绝对干净过度清洗反而会丢失有效信息。3.2 质量过滤规则之外还需要模型评价启发式规则是质量过滤的兜底做法直接也有效。常见规则包括文本最短长度不低于多少字符、感叹号和问号占比不能过高、重复字符比例不能过高、字母数字占比是否正常、是否包含垃圾词表中的词。这些规则组合在一起能把网页里的碎片文本、验证码语料、无意义乱码大量干掉。但规则过滤只能解决“一眼脏”的问题“看起来还行但内容空洞”的文本就要用模型来筛了。常用方案有两个方向训练一个fastText二分类器正样本是人工挑出来的高质量段落负样本是从低质量页面里抽出来的段落然后对全量数据打分。用KenLM或者GPT-2的perplexity困惑度来判断句子流畅度。困惑度过高大概率是文字不通或者语义混乱困惑度极低的要警惕是否过于重复或模板化。这两个方向可以结合使用先分类器粗筛再用低困惑度/高困惑度两个边界做细筛。不过困惑度过滤很容易误伤专业文章比如数学论文、法律条文因为用词特殊困惑度天然偏高所以阈值要分域设置不要一套阈值打天下。3.3 三种去重方法各有各的用场去重在预训练数据集里是重中之重数据重复率过高轻则训练时loss收敛变慢重则模型出现“复读机”现象。实际工程中我基本会做三层去重。文档级去重最简单直接对所有文档取MD5完全一样的删掉适合去掉靠复制粘贴产生的重复页。但稍微改几个字就躲过了MD5所以还要做模糊去重。SimHash能处理一部分局部改动但分布不均匀时效果不稳定。更可靠的做法是MinHash配合LSH把文档切分成w-shingles比如w5的token集合用MinHash算相似度相似度大于阈值的文档合并或删除。datasketch这个库有现成的MinHashLSH实现调参时只需要关注shingle大小、哈希数量、阈值三个参数我习惯用shingle5、num_perm128、threshold0.8起步。除此之外还有一类重复藏在页面内部同一个网站的大量页面共用头尾模板每个文档里都有一段相同的导航文本。这种子串级重复靠文档相似度很难消掉需要用后缀数组或者BWA这类工具做更细粒度的重复子串检测把长公共子序列切割出来。做这一步比较费资源但做与不做对模型重复率的影响非常明显。我建议先做MD5去重再做MinHash去重最后抽样检查公共子串如果问题严重再上子串去重。3.4 隐私与有害内容过滤不能省隐私过滤的核心目标是把手机号、邮箱、身份证号、银行卡号这类个人身份信息找出来并处理掉。正则表达式是最快的手段先按模式匹配再用上下文规则降低误召回。替换时不要简单删掉或者置空最好替换成占位符比如[EMAIL]、[PHONE]这样既能保护隐私又不破坏句子的结构。有害内容过滤可以用基于词表的黑名单也可以训练分类器或者用开源模型做零样本分类。这块的难点在于平衡过滤太严会把正常内容误杀比如医疗科普文章可能包含敏感词过滤太松又没能达到目的。我的经验是做分级处理高危内容直接删除中危内容降低在数据集里的权重低危内容保留但不参与特定领域的评测。训练数据的合规和真实性会直接反映到模型输出上这个环节宁可多花时间也不要抱着“清洗之后再清洗”的侥幸心理。4. 数据配比、采样与token化4.1 配比是被严重低估的超参数前面说的清洗都是“剔除坏数据”但一份好数据集的长相不只是干净还取决于不同来源数据的比例。配比实际上是预训练阶段最被低估的超参数。同一个网页、书籍、代码占比会让模型表现出完全不同的能力倾向。比如代码占比从10%提到30%模型在代码生成上的通过率能涨不少但常识问答可能稍微下降书籍占比太低模型的叙事能力和长文本理解会变得薄弱。配比设计要从模型定位倒推。要是做一个代码助手代码数据占比可以到35%到40%要是做一个通用中文助手中文网页和中文书籍的占比就需要重点保证同时保留一定比例的英文语料维持跨语言能力。一般不建议把单一来源压到70%以上否则模型面会比较窄。这是一个需要反复试的方向我在项目里通常先用小规模数据跑配比实验确认各来源loss都正常再放大。4.2 采样权重与混比调优不同来源的数据量天然不一样。网页语料动辄几千亿token而书籍可能只有几十亿token。直接按原始比例混合书籍的贡献少到可以忽略。这时需要做采样权重调整。常用的做法是目标配比除以上限token数得到一个采样比例再根据实际数据量做归一化。如果某个域数据不够就做多次重复采样如果某个域数据太多就降低采样率。实际操作中我习惯记录每个域的“有效epoch数”。对少量高价值数据epoch数在2到3以内可以接受超过这个数可能会过拟合需要警惕。训练过程中会持续监测各域的loss如果某个域loss异常高先检查这个域数据是否清洗不干净再看是不是采样比例过低。混比调优更像是“小火慢炖”用1B到10B token规模的试训练观察各域loss曲线再微调配比而不是一次梭哈。4.3 token化与打包格式数据清洗配比完成之后下一步是把文本切成token并打包成训练样本。这一步通常在全量训练任务开始前做因为训练框架直接读原始文本效率太低。预训练任务一般是自回归需要把文本拼接到固定长度。我的持久做法是把每个doc单独存成一个jsonl行字段至少包含text和来源信息解析方便也能做后续溯源。打包时多个短文本要拼接进同一条样本同时要避免跨文档的上下文污染。常规做法是在每段文本结尾加一个特殊分隔符[EOD]让模型学会“文档结束”的边界。如果训练序列长度是4096那就把多个文档一直拼接直到塞满。这段逻辑看起来简单但实现时容易出错要处理好最后一段不足seq_len时的padding。还有一个细节训练时建议做全局级别的数据shuffle防止同一个来源的文档总是连续出现导致模型学到无意义的共现模式。5. 数据集质量的验证与污染排查5.1 训练前的小规模烟雾测试数据工程做完不等于可以直接进入大规模训练。在我自己的流程里正式训练前一定先做一轮小规模烟雾测试。取1B到2B token的样本数据用一个小规模模型比如100M到300M参数跑上几千步观察loss曲线是否平滑下降、是否有异常spike、各域名loss是否在合理区间。这一步能提前暴露很多问题最常见的是脏数据导致loss spike比如某条文本里混入了二进制乱码、一段倒排索引表格、或者抓取时未解析干净的HTML标签。通过打印异常高loss样本的id能定位到具体是哪一批数据出了问题再回到流水线里修。烟雾测试的另一个作用是快速验证tokenizer的词表覆盖情况如果OOV率过高肯定要补充词表或换tokenizer。5.2 评测集污染检测不能缺席预训练数据集如果和下游评测集有重叠评测分数会被污染看起来很高但实际是“背题”背出来的。常见的检测方法是n-gram重叠把评测集切成长度为8到13的连续n-gram在训练数据里扫一遍凡是重叠超过一定比例的文档直接删除。这一步看起来小实际影响很大尤其是从开源数据集继承来的语料评测集文本可能在互联网上出现过不加处理就会带污染。处理方式我建议做得保守一些评测集相关的域名直接排除比如部分公开数据集来自某些固定站点剩下用n-gram扫描删掉具体重叠文档。这里要注意避免过度删除比如把普通技术文章误判为评测集内容而删掉损失有效数据。可以先在少量样本上测试阈值确认误删率再上全量。5.3 常见问题与排查实录训练过程中遇到的问题很多根因都能追溯到数据上这里整理一份我遇到的典型情况和排查方法现象可能原因排查方法loss呈周期性spike特定脏数据批次混入记录异常loss样本id回溯数据文件模型生成重复内容数据重复率过高检查MD5去重和MinHash去重是否完整执行中英文混杂严重语言过滤未生效抽样检测fastText识别结果下游评测突然掉点评测集污染或训练数据泄漏运行n-gram重叠检测训练收敛过慢数据配比失衡低质内容过多分域loss对比调整采样权重输出里出现奇怪符号编码转换遗漏或HTML残留抽样查看原始清洗前文本排查时最怕的是没有日志。所以我从一开始就在数据流水线每个阶段记录drop比例比如原始10T文本HTML解析后剩8T质量过滤后剩4T去重后剩3T。每一层的比例都记录下来模型训练出问题时往后退一层就能定位到是哪个环节丢了什么或者漏了什么。6. 一点实际的工程建议数据管线写起来门槛不高真正难的是治理和管理。我在实际构建数据的过程中最大的体会是永远不要相信自己的第一版清洗流程也不要急着跑全量。宁可先取1%的数据做一次全流程预演看每一层的drop比例是否符合预期再回过来调整规则也不要一头扎进去跑三天三夜最后发现数据路径不对。我吃过最痛的亏是去重没做干净训练到中后期模型开始出现明显的复读现象不得不从头增量补充数据再做第二阶段续训时间和算力都白烧了。如果能重来一次我会在项目启动的第一天就建立数据溯源表每一条数据来源、清洗版本、处理时间都落库。模型的loss曲线、下游评测结果和数据版本绑定在一起方便复盘。预训练数据集构建这件事本质上不是一次性工程而是随着模型迭代持续演进的资产。数据管线的可观测性和可回溯性比一次性写出多少清洗代码都重要。