1. 预训练数据集到底在解决什么问题很多人做大模型训练眼睛只盯着模型结构和训练参数觉得把模型搭起来、把 loss 调下去就完事了。但真正跑过一轮完整预训练的人都清楚数据集的质量和构建方式直接决定了模型最终能力的上限。你喂给模型什么语料它就学成什么样子这件事在预训练阶段体现得淋漓尽致。预训练数据集构建说白了就是把互联网上、书籍里、代码仓库中散落的各种文本经过采集、清洗、去重、过滤、格式化之后变成模型能直接“吃”的标准数据。这个过程听起来不复杂但实际操作中涉及的环节非常多每一步都有坑。比如原始网页里大量导航栏、广告、乱码你不清理干净模型就会学到一堆无意义的重复模式再比如不同来源的数据格式五花八门有的用 JSON有的用纯文本有的用 Markdown你得统一成训练框架能识别的格式。这篇文章面向的是已经了解 Transformer 基本结构、跑过至少一次微调、现在想往预训练方向深入的朋友。我会围绕LLamaFactory这个训练框架把预训练数据集从零构建的完整流程拆开讲清楚。包括数据从哪里来、怎么清洗、怎么去重、怎么格式化、怎么配比、怎么接入训练管道以及我在实际操作中踩过的那些坑。读完你至少能做到手里有一批原始语料能独立把它变成一份合格的预训练数据集并且知道每一步为什么要这么做。2. 预训练数据集的核心设计思路2.1 为什么预训练数据不能直接拿来就用原始语料和可训练数据之间差距比很多人想象的大得多。我拿最常见的网页抓取数据举例一个原始 HTML 页面里真正有价值的正文可能只占 20% 到 30%剩下的全是导航、侧边栏、评论区、页脚、广告脚本。如果你不做正文提取直接把这些内容喂给模型模型会花大量容量去记忆那些在每个页面上都重复出现的模板文本这不仅是浪费算力还会让模型在生成时频繁输出无意义的导航式内容。除了正文提取还有几个必须处理的问题。重复数据是预训练里最隐蔽的杀手互联网上大量内容互相转载同一篇文章可能在几百个站点出现如果不去重模型会在这些重复内容上反复训练导致过拟合和记忆化。低质量文本同样致命比如乱码、机器翻译的劣质结果、关键词堆砌的垃圾页面这些内容会拉低模型的语言理解能力。敏感和有害内容也需要过滤这不是可选项而是必须做的合规步骤。所以预训练数据集构建的核心思路可以概括为一句话在保证数据多样性的前提下尽可能提升单位 token 的信息密度。多样性保证模型见过足够多的语言现象信息密度保证每一份算力都花在刀刃上。2.2 数据集配比背后的取舍逻辑预训练数据不是越多越好而是配比越合理越好。业界常见的做法是把数据分成几个大类通用网页文本、高质量书籍和论文、代码、多语言文本、领域专有数据。每一类的占比会显著影响模型的能力偏向。我自己的经验是通用网页文本通常占大头大概 60% 到 70%因为它覆盖面最广能提供最丰富的语言模式。书籍和论文类高质量文本占 15% 到 20%这部分数据语法规范、逻辑严密对提升模型的推理和长文本理解能力帮助很大。代码数据占 10% 到 15%即使你最终不是要做代码模型适量代码数据也能提升模型的结构化思维能力。剩下的留给多语言和领域数据。这个配比不是固定的要根据你的目标来调。如果你要做的是中文能力强的模型那中文网页和中文书籍的占比就要往上提。如果你要做的是法律或医疗领域的模型那领域专有数据的占比可能要拉到 30% 以上。关键是要有意识地去设计配比而不是把手里所有数据一股脑倒进去。2.3 为什么选择 LLamaFactory 作为训练框架LLamaFactory 这两年在开源社区里热度很高我自己用下来的感受是它把预训练、微调、推理这条链路整合得比较顺尤其是对数据格式的抽象做得不错。它支持多种数据格式包括 Alpaca 格式、ShareGPT 格式也支持纯文本的预训练格式。对于预训练任务你只需要把数据整理成它要求的 JSON 或 JSONL 结构配置好 YAML 文件就能直接启动训练。选择 LLamaFactory 的另一个原因是它对显存和分布式训练的支持比较友好。预训练对显存的需求远大于微调LLamaFactory 内置了梯度累积、混合精度、DeepSpeed 集成等特性能让你在有限的硬件上跑起更大的模型。当然如果你用的是其他框架比如 Megatron 或 DeepSpeed 原生流程数据构建的思路是一样的只是格式对接方式不同。3. 数据采集与清洗的实操要点3.1 数据来源的选择与采集策略预训练数据的来源大致分几类。公开网页数据是最主要的来源常见的有 Common Crawl 这类大规模网页快照也有针对特定语言或领域的爬取数据。书籍和论文可以通过公开的电子书库、学术论文开放获取平台获取。代码数据主要来自开源代码托管平台。百科和问答社区也是高质量文本的重要来源。采集的时候有几个点要注意。第一是遵守来源的使用条款不同数据源对使用范围有不同规定商用和非商用差别很大这个必须提前确认。第二是控制采集频率不要对目标站点造成压力合理设置并发和间隔。第三是保留元数据比如来源 URL、采集时间、语言标识这些信息在后续清洗和去重时非常有用。我一般会把采集下来的原始数据按来源分目录存放每个目录下再按采集批次分子目录文件名里带上时间戳。这样做的好处是后续如果发现某个来源的数据质量有问题可以快速定位并剔除而不用把整个数据集重新跑一遍。3.2 正文提取从 HTML 到纯文本拿到原始 HTML 之后第一步是正文提取。常用的工具有trafilatura、readability-lxml、newspaper3k等。我实测下来trafilatura的综合表现比较稳对中英文页面的正文识别准确率都不错而且它能同时输出文本和元数据。正文提取的核心逻辑是识别页面中密度最高、链接最少、标点最丰富的文本块。导航栏和侧边栏通常链接密度极高评论区往往短句多、重复模式明显这些都会被算法排除。但没有任何工具能做到 100% 准确所以提取完之后还需要人工抽检。我的做法是每批数据随机抽 200 条人工看一遍如果正文提取的准确率低于 90%就要调整工具参数或者换工具。提取出来的文本还要做基础清洗。去除多余空白把连续多个换行合并成一个把制表符和连续空格处理掉。去除控制字符那些不可见的 Unicode 控制字符会干扰 tokenizer。统一标点比如把全角半角混用的情况规范化。这些操作看起来琐碎但不做的话后面 tokenize 阶段会出各种奇怪问题。3.3 质量过滤把垃圾挡在门外质量过滤是预训练数据构建里最需要经验的一环。我通常从几个维度来筛。长度过滤是最简单的太短的文本比如少于 50 个字符通常信息量不足太长的文本比如超过 10 万字符可能是异常拼接都要处理。重复率过滤统计文本内部的 n-gram 重复比例如果一段文本里同一个短语反复出现大概率是垃圾内容。语言过滤用语言检测工具确认文本语言把不符合目标语言的剔除。困惑度过滤用一个小的参考语言模型计算文本的困惑度困惑度太高的文本通常语法混乱或质量低下。还有一个容易被忽略的维度是符号与数字比例。如果一段文本里符号和数字占比超过 50%那它很可能是表格、代码片段或者乱码不适合作为通用预训练数据。我一般会把符号数字占比超过 40% 的文本标记出来单独处理或者直接剔除。下面这张表是我常用的质量过滤阈值供参考过滤维度阈值建议处理方式最小长度50 字符剔除最大长度100000 字符截断或剔除符号数字占比40%标记复查重复 n-gram 比例30%剔除语言置信度0.9剔除困惑度参考模型前 10%剔除注意这些阈值不是绝对的要根据你的数据来源和目标模型调整。比如代码数据的符号占比天然就高不能用通用阈值来卡。4. 去重、格式化与 LLamaFactory 对接4.1 去重从精确匹配到语义近似去重是预训练数据构建里投入产出比最高的环节之一。重复数据不仅浪费算力还会让模型对某些内容过度记忆影响泛化能力。去重一般分三个层次。精确去重最简单对整段文本做哈希哈希值相同的只保留一份。这个能干掉完全一样的重复。近似去重用 MinHash 或 SimHash 这类局部敏感哈希算法能找出内容高度相似但不完全一样的文本。语义去重用嵌入向量计算相似度能发现换了说法但意思相同的文本但计算成本最高一般只在高价值数据上做。我通常的做法是先做精确去重再做 MinHash 近似去重语义去重只在书籍和论文这类高质量数据上做。MinHash 的关键参数是 n-gram 大小和哈希函数数量n-gram 一般取 5 到 10哈希函数数量取 128 到 256。阈值方面Jaccard 相似度超过 0.8 的我一般会判为重复。去重的时候要注意不要跨类别去重。比如代码数据和网页文本即使有相似片段也不应该互相去重因为它们属于不同的数据类别保留下来对模型有不同价值。去重应该在每个类别内部进行。4.2 格式化成 LLamaFactory 能识别的结构LLamaFactory 对预训练数据的格式要求比较灵活最常用的是 JSONL 格式每行一个 JSON 对象。对于纯文本预训练结构可以很简单{text: 这里是你的预训练文本内容...}如果要做多语言或者带元数据的训练可以扩展成{text: 文本内容, language: zh, source: web, category: general}LLamaFactory 在读取数据时会根据配置文件里的dataset字段去匹配对应的数据文件。你需要在data/dataset_info.json里注册你的数据集指定文件名和格式。比如{ my_pretrain_data: { file_name: my_pretrain_data.jsonl, formatting: text, columns: { prompt: text } } }这里formatting设为text表示这是纯文本预训练数据columns里的映射告诉框架从哪个字段读取文本。配置好之后在训练 YAML 里把dataset指向my_pretrain_data就能用了。4.3 数据配比与采样策略格式化完成之后下一步是配比。如果你有多个类别的数据需要按设计好的比例混合。LLamaFactory 支持通过interleave方式混合多个数据集也支持你提前把数据按比例采样好再合并成一个文件。我一般倾向于提前采样合并因为这样更可控。具体做法是先统计每个类别的总 token 数然后按目标配比计算每个类别应该采样多少 token再随机采样对应数量的样本最后合并打乱。打乱这一步很重要如果不打乱训练时模型会先看完所有网页数据再看所有书籍数据这种分布偏移对训练不利。采样的时候要注意按 token 数采样而不是按样本数采样。不同类别的单条样本长度差异很大网页文本可能平均 500 token书籍章节可能平均 5000 token如果按样本数 1:1 混合实际 token 比例会严重偏离预期。5. 常见问题与排查技巧实录5.1 数据格式报错怎么快速定位LLamaFactory 在读取数据时如果格式不对报错信息有时候不够直观。我遇到最多的几类问题JSONL 文件里有空行导致解析失败、JSON 对象缺少必要字段、文本里包含未转义的特殊字符。排查的时候先用python -c逐行读取验证 JSON 合法性import json with open(data.jsonl, r, encodingutf-8) as f: for i, line in enumerate(f): line line.strip() if not line: print(f第 {i} 行为空) continue try: json.loads(line) except json.JSONDecodeError as e: print(f第 {i} 行解析失败: {e})这样能快速定位到具体哪一行有问题。空行问题最容易被忽略很多文本处理工具在输出时会留下尾部空行。5.2 训练 loss 异常波动的数据侧原因预训练过程中 loss 突然飙升或者不收敛很多人第一反应是调学习率但有时候问题出在数据上。我遇到过几种情况数据里混入了大量重复样本导致局部过拟合、某个类别的数据质量突然变差、数据没有打乱导致分布偏移。排查方法是把训练数据按来源和类别分组分别计算模型在这些组上的 loss。如果某个组的 loss 明显高于其他组说明这个组的数据可能有问题。另外可以检查训练过程中 loss 的滑动平均如果 loss 在某个时间点突然变化对照一下那个时间点对应的是哪批数据。5.3 常见问题速查表问题现象可能原因排查方法解决方式数据加载报错JSONL 格式错误逐行验证 JSON修复或剔除问题行loss 不下降数据重复率高统计去重前后数量加强去重loss 波动大数据未打乱检查数据顺序重新打乱生成内容重复训练数据模板化严重抽样检查数据增加数据多样性显存溢出单条样本过长统计长度分布截断或过滤长样本训练速度慢数据文件过大检查文件大小分片存储提示预训练数据的问题往往在训练开始后才会暴露所以正式训练前先用小规模数据跑几百步确认 loss 正常下降再上全量数据能省下大量时间。5.4 我踩过的几个坑第一个坑是低估了去重的重要性。早期我做数据集的时候觉得去重差不多就行结果模型训练到后期 loss 降得很低但生成质量很差明显是过拟合了。后来加强去重同样数据量下模型泛化能力提升明显。第二个坑是忽略了数据顺序。有一次我偷懒没有打乱数据直接把网页数据、书籍数据、代码数据按顺序拼接结果模型在训练前期表现很好到中期突然变差。原因是分布偏移导致模型需要不断重新适应新分布。打乱之后这个问题就消失了。第三个坑是tokenizer 和数据不匹配。我用的是一个中文优化的 tokenizer但数据里混了大量英文和代码导致英文和代码的 token 效率很低同样文本量下 token 数暴涨训练成本增加。后来我调整了数据配比并且确认 tokenizer 的词汇表覆盖足够。6. 从数据到训练的完整串联6.1 构建流程的标准化把前面所有环节串起来一个完整的预训练数据集构建流程是这样的采集原始数据、正文提取、基础清洗、质量过滤、去重、格式化、配比采样、打乱、注册到 LLamaFactory、启动训练。每一步都应该有日志记录记录输入多少条、输出多少条、剔除了多少、剔除原因是什么。这些日志在后续排查问题时非常关键。我习惯把整个流程写成一个可配置的 pipeline每个环节的参数放在配置文件里。这样换一批数据的时候只需要改配置不用改代码。pipeline 的每个阶段输出到独立目录方便回溯和复用。6.2 小规模验证的重要性正式跑全量预训练之前一定要用小规模数据做验证。我的做法是从每个类别里采样一小部分组成一个大概 100 万 token 的小数据集跑几百步训练观察 loss 曲线和生成效果。如果小规模验证有问题全量训练大概率也有问题而且全量训练的试错成本高得多。小规模验证主要看几点loss 是否平稳下降、生成内容是否通顺、有没有明显的重复或模板化输出、不同类别的数据是否都被有效学习。如果发现某个类别拖后腿可以回到数据侧调整。6.3 持续迭代的数据策略预训练数据集不是构建一次就完事的。模型训练过程中你会发现新的问题比如某类知识覆盖不足、某种语言能力偏弱这些都需要通过调整数据来改进。我一般会维护一个数据版本记录每次调整都记录改了什么、为什么改、效果如何。这样积累下来你对什么样的数据能带来什么样的效果会越来越有感觉。另外数据构建和模型训练是相互影响的。不同模型结构对数据的敏感度不同同样的数据在不同模型上效果可能不一样。所以数据策略要结合具体模型来调不能照搬别人的配比。7. 一些实操中的个人体会预训练数据集构建这件事技术门槛其实不算特别高但极其考验耐心和细节把控。我见过太多人把大部分精力花在调模型结构上数据随便弄弄就开跑结果训练出来的模型效果不理想回头找原因发现是数据的问题。我的建议是在数据上多花时间绝对值得。一份干净、多样、配比合理的数据集能让一个普通模型发挥出超出预期的效果而一份脏乱差的数据集再好的模型结构也救不回来。具体操作上去重和质量过滤这两步千万不要省它们对最终效果的影响远超你的想象。还有一点不要追求一步到位。数据构建是一个迭代过程先跑通流程再逐步优化每个环节。一开始可以把阈值设得宽松一些保证数据量然后根据训练效果逐步收紧。这样比一开始就追求完美、结果卡在某个环节动弹不得要高效得多。最后分享一个小技巧在格式化数据的时候可以顺便统计一下 token 分布、长度分布、语言分布把这些统计信息存下来。这些数据不仅能帮你判断数据集质量还能在训练出现问题时提供排查线索。我现在的习惯是每批数据都生成一份统计报告时间长了之后看一眼统计数字就能大致判断这批数据能不能用。