Skip to content

约 12 分钟阅读

06. 预训练数据与分词:语料从哪来,怎样变成 token ​

第一部分算过一笔账:Llama-3.1-8B 用了约 15T token 预训练,按 6 × 参数量 × token 数 约 7.2×10²³ FLOPs(见第 04 篇)。算力账里 token 数只是一个乘数,可这 15T token 从哪里来、为什么是这些文字而不是别的,决定了模型最后会什么、不会什么。这一篇从大语言模型的第一道工序讲起:语料怎样收集、过滤、去重、去隐私、配比,文字又怎样被分词器切成 token。

读完应能判断:一份公开数据集的“15T token”背后删掉了多少东西;分词器为什么会让同一段中文的训练和推理成本差出一倍多;评测分数为什么可能被训练数据“污染”;以及资料助手团队手里那几百万字的内部制度,适不适合拿来做预训练。

15T token 是多大一堆文字 ​

先把数字换成能想象的量。Llama 3 的分词器在英文上平均每个 token 约 3.94 个字符:

text
15T token × 3.94 字符/token ≈ 5.9×10¹³ 个字符
按一本书 50 万个英文字符估算 ≈ 1.2 亿本书(量级估算)

世界上没有这么多整理好的书,所以大部分只能来自网页。最常用的公开起点是 Common Crawl:一个从 2007 年起持续抓取网页的非营利项目,官网在 2026 年标注已存档超过 3000 亿个网页、每月新增 30 亿到 50 亿个。网页之外,常见来源还有公开代码仓库、书籍与论文、数学题解和多语言文本。

网页量大但很脏。原始抓取的 HTML 里有导航栏、广告、Cookie 提示、自动生成的垃圾页、同一篇新闻在上百个网站的转载。直接拿来训练,算力会大量花在模型学“版权所有 © 2023”这种句子上。所以预训练数据工程的主体不是“收集”,而是“删”。

从网页到训练文本:一层层删 ​

下面这张图是一条典型的网页处理流水线。条形长度只示意趋势,不是某份报告的真实比例;底部给了一组公开的真实数字。

网页语料依次经过 URL 过滤、正文抽取、语言识别、规则过滤、去重、隐私安全处理和模型打分,剩余量逐级减少

以 Hugging Face 2024 年发布的 FineWeb 为例,各步具体做的事是:

  1. URL 过滤:用黑名单去掉已知的恶意站点和成人站点。
  2. 抽取正文:用 trafilatura 这类工具从 HTML 里只保留正文,丢掉菜单、页脚和脚本。
  3. 语言识别:用 fastText 语言分类器给每篇文档打语种分,FineWeb 只保留英语分数不低于 0.65 的文档。多语言数据集则按语种分桶,分别处理。
  4. 规则过滤:一组可解释的启发式规则,例如重复行比例过高、文档太短、符号和数字比例异常、行尾没有标点的“列表页”。FineWeb 组合了 DeepMind Gopher 和 Google C4 两套规则,再加了自己的几条。Llama 3 还用“脏词”计数补漏成人内容,用 token 分布的 KL 散度剔除用词分布明显异常的文档。
  5. 去重:后面单独讲。
  6. 隐私与安全:FineWeb 把邮箱和公网 IP 替换成占位符;电话号码没有处理,因为正则匹配的误报太多,会把大量正常数字改坏。Llama 3 直接过滤“可能含有大量个人信息”的网站。

最终 FineWeb 用 96 个 Common Crawl 快照得到约 15T token(按 GPT-2 分词器计数,后续版本扩到 18.5T 以上)。

去重:同一篇文章不该读一百遍 ​

网上同一段文字被转载、镜像、改个日期再发布是常态。重复数据有三个害处:浪费算力;模型反复看到同一段文字更容易逐字背下来,研究者曾从 GPT-2 里直接“问”出数百条训练原文,其中包括姓名、电话和邮箱;评测题如果在网上被大量转载,还会造成后面讲的污染。

完全相同的文档用哈希值一比就能删掉,难的是近似重复:两份公司年假制度页面,正文一样,只是页脚和发布时间不同。这时用 MinHash:把每篇文档切成连续 5 个词的小片段(称为 shingle),两篇文档的相似度用 Jaccard 系数衡量:

text
Jaccard(A, B) = 两篇共有的片段数 ÷ 两篇片段的并集数

例:A、B 各有 100 个片段,其中 90 个相同
Jaccard = 90 ÷ (100 + 100 − 90) = 90 ÷ 110 ≈ 0.82

对上亿篇文档两两比较是不可能的。MinHash 的技巧是:对片段集合做一次随机哈希,取最小值,两篇文档最小值相等的概率恰好等于它们的 Jaccard 系数。用很多个哈希函数(FineWeb 用 14 组、每组 8 个)得到一串“签名”,签名在某一组里全部相同的文档被放进同一个桶,只在桶内比较。这样不用两两比,也能以很高概率把相似度高的文档找出来。

去重也有粒度选择。Llama 3 做了三层:同一 URL 只留最新版本;全局 MinHash 删近似重复文档;再在每 3000 万篇文档里,删掉出现超过 6 次的行,专门清理模板化的页眉页脚。FineWeb 则选择每个快照内部去重、不跨快照全局去重,它的消融实验发现:跨全部快照去重后留下的旧快照文本里,广告、关键词堆砌和格式混乱的页面反而更多,训练效果几乎没有提升。去重做到多狠,没有通用答案,要靠小模型实验比较。

模型打分:让一个分类器挑“好文本” ​

规则只能删掉明显的垃圾,剩下的文本质量仍参差不齐。近年的做法是训练一个小分类器给每篇文档打质量分。FineWeb-Edu 的流程很有代表性:

  1. 让 Llama-3-70B-Instruct 给约 50 万篇 FineWeb 文档按 0—5 分打“教育价值”分;
  2. 用这些分数在一个小型文本编码模型上训练一个回归头,得到能快速给海量文档打分的分类器;
  3. 给全部 15T token 打分,只保留 3 分及以上的文档,剩下 1.3T token,删掉了 92%。

大模型打分贵,只用它标一小部分;小分类器便宜,用它扫全量。Llama 3 用了同样的思路:一个 fastText 分类器判断“这段文字会不会被维基百科引用”,再用 Llama 2 的判断结果训练 DistilRoberta 分类器。

模型打分的边界要看清楚。分类器学的是标注者的口味:FineWeb-Edu 的分类器卡片自己写明,它偏好中小学教育类内容,高分段可能偏爱“看起来像学术文章”的文本。如果资料助手团队拿这个分类器去筛人事制度和报销流程,这些文本大多会拿到低分被删掉,因为它们不是“教育内容”。筛选标准要和目标能力一致,否则删得越狠,偏得越远。

配比:每类数据放多少 ​

过滤完的数据还要决定比例。代码太少,模型写不好程序;网上娱乐内容太多,模型的知识分布就偏向娱乐。下图是 Llama 3 论文公布的主训练配比:

网页、代码、书籍论文、数学推理、多语言文本先分类,再用配比实验决定比例;Llama 3 主训练配比为通用知识 50%、数学与推理 25%、代码 17%、多语言 8%

Llama 3 的方法分两步。先给每篇文档分主题类别,把网上明显过多的类别(论文举的例子是艺术与娱乐)下采样;再在小模型上试不同配比,拟合配比与效果的规模定律,预测大模型用哪种配比最好。小模型实验便宜,结论再外推到大模型,这是预训练里反复出现的套路。

配比还会随训练阶段变化。训练最后一小段通常换成更高质量的数据,叫退火数据,下一篇讲它在学习率调度里的位置。Llama 3 明确写了退火数据里不放常用基准的训练集,原因见后面的“评测污染”。

分词:把文字切成 token ​

模型不直接读字符,而是读 token:分词器(tokenizer)把文字切成片段,每个片段对应词表里的一个编号。词表大小、切法,直接决定同一段话占多少 token。

BPE:反复合并最常见的相邻对 ​

主流大模型的分词器大多用 BPE(字节对编码)训练。它的过程可以手算:

左:在小语料上 BPE 依次合并 es、est、lo、low;右:“年假怎么申请?”在 GPT-2、Llama 2、Llama 3 三种分词器下分别切成 16、14、5 个 token

假设语料里只有四个词:low 出现 5 次、lower 2 次、newest 6 次、widest 3 次。起点是把每个词拆成单个字母,统计所有相邻字母对出现的次数。e s 在 newest 和 widest 里共出现 6 + 3 = 9 次,s t 也是 9 次,并列最多,取 e s 合并成新 token es。合并后重新统计,es t 出现 9 次,合并成 est。接着 l o 出现 5 + 2 = 7 次,合并成 lo;lo w 7 次,合并成 low。

每次合并都是一条规则,按顺序存进词表,直到词表达到预定大小。切新文本时按同样的顺序套用规则:newest 切成 n e w est;训练时没见过的 lowest 会切成 low est。常见片段变成一个整块,生僻词拆成已知的小块,词表大小有限,却能覆盖任何文本。

词表大小的取舍 ​

词表越大,常见词组越可能成为一个 token,同一段文字的 token 数越少,训练和推理都更省;同样长度的上下文窗口也能装下更多内容。Llama 3 把词表从 Llama 2 的 32K 扩到 128K(OpenAI tiktoken 的 100K 加上 28K 个主要面向非英语的 token),英文平均每 token 从 3.17 个字符提高到 3.94 个。

代价在参数和数据上。词表里每个 token 都在词嵌入层和输出层各占一行:

text
单层参数 = 词表大小 × 隐藏维度 = 128,256 × 4,096 ≈ 5.25 亿
Llama-3.1-8B 嵌入层和输出层不共享,两层合计 ≈ 10.5 亿,约占 80.3 亿参数的 13%

对小模型来说这部分占比更高。词表太大时,很多罕见 token 在训练中出现次数很少,它们的向量学得不好。词表大小是压缩率、参数量和训练充分度之间的折中。

中文的 token 数 ​

对中文用户,分词器的影响更直接。下面是用四个公开分词器实测同一段 115 字的年假制度段落(单段样例实测,不代表平均值):

分词器词表大小token 数每个汉字约几个 token
GPT-250,2572462.14
Llama 232,0001581.37
Llama 3128,256930.81
Qwen2.5151,665820.71

同一段话,Llama 2 的分词器比 Llama 3 多用 70% 的 token。按 token 计费的 API 多付 70% 的钱,训练时同样的算力能看的中文少了四成,128K 的上下文窗口能装的中文也相应变少。按这段样例的比例粗算,Llama 3 分词器下 128K token 大约能放 16 万个汉字;换到 GPT-2 的分词器,只剩 6 万字左右。

字节回退:遇到没见过的字怎么办 ​

图里 Llama 2 那一行有很多红格子。它的词表只有 32K,装不下常用汉字,“假”“怎”“申”不在词表里。这时分词器不会输出“未知字符”,而是把这个字的 UTF-8 编码拆成 3 个字节,每个字节一个 token,例如“假”变成 E5 81 87。这叫字节回退(byte fallback):词表里预留 256 个字节 token 兜底,任何文字都能被表示,代价是生僻字很费 token。

GPT-2 和 Llama 3 走得更彻底,它们的 BPE 直接从字节开始合并(字节级 BPE),词表的起点就是 256 个字节,所以永远不会出现无法编码的字符。GPT-2 词表里中文片段很少,几乎每个汉字都被拆成两三个字节 token;Llama 3 新增了大量非英语 token,“怎么”“申请”都成了一个整块。

分词器在预训练开始前就固定下来,之后很难改:换词表意味着词嵌入层和输出层要重新学。微调时给词表加“钉钉审批”这样的新词,新 token 的向量从随机值开始,需要足够的数据才学得会,大多数情况下不如直接让模型用已有的子词去拼。选底座时就应该实测它的分词器在自己语料上的 token 数。

评测污染:考题被提前看过 ​

公开评测集的题目和答案常常出现在网页上:论坛讨论、教程转载、GitHub 仓库。预训练数据从全网抓取,很可能把它们一起吞进去。模型在这些评测上的高分,有一部分可能只是背过原题。这叫评测污染(benchmark contamination)。

检测方法通常是查重叠:把评测题切成 n 个 token 一组的片段,看训练语料里有没有出现过。Llama 3 用 8-gram 重叠估计污染,结论很有参考价值:PiQA、HellaSwag 估计污染较高,模型在这些题上确实因此得分更高;Natural Questions 估计 52% 的题有污染,分数却几乎不受影响;MBPP、HumanEval、MMLU 等评测上 8-gram 标出的污染太多,根本无法可靠估计影响。重叠检测本身也会误报,常见的题目格式和公式片段会大量命中。

对资料助手团队,这件事的实际意义有两层:看公开模型的榜单分数时,要知道它可能被污染抬高;自己做训练时,训练数据要和自己的评测集做去重,评测集最好用训练数据截止之后收集的真实员工提问,并且从不放进任何训练流程。第 21 篇会专门讲怎样建这样的评测集。

授权、版权与 robots.txt ​

数据能不能用,是比技术更硬的约束。几个必须核对的点:

  • 抓取是否遵守站点意愿:Common Crawl 的爬虫 CCBot 先读 robots.txt,被禁止就不抓,网站可以写 User-agent: CCBot 加 Disallow: / 退出。自己写爬虫也应同样遵守,并尊重网站服务条款。
  • 数据集许可证:FineWeb 使用 ODC-By 许可,要求署名;代码数据要按仓库许可证筛选,有些许可证对衍生作品有要求。
  • 版权与个人信息:公开可访问不等于可以随意用于训练,各地法规和诉讼仍在变化。公开数据集通常提供删除渠道,FineWeb 有个人信息删除表单,也曾按下架通知删除特定域名。具体能否使用要由法务按所在地区判断。
  • 内部数据:公司制度、聊天记录、工单里有员工个人信息和商业机密。拿来训练之前要确认数据所有方授权、做脱敏,并想清楚模型可能把这些内容逐字说出来。

资料助手该不该自己做预训练数据 ​

回到贯穿案例。资料助手团队手里有几百万字的制度文件和问答记录,按上面的表格大约是几百万个 token。和 15T 相比差了六个数量级,从零预训练无从谈起;即便做继续预训练,这点数据也太少(下一篇会讲什么规模才值得)。更现实的用法是:制度原文进检索库,回答样例做成 SFT 数据,员工的真实提问留作评测集。这一篇的过滤、去重、去隐私、评测去污染的做法,在构造 SFT 和评测数据时同样适用。

预训练数据的主体来自网页,靠 URL 过滤、正文抽取、语言识别、规则过滤、去重、隐私处理和模型打分一层层筛出来,再按配比实验决定各类数据的比例。分词器用 BPE 从字符或字节出发合并常见片段,词表大小决定同一段文字的 token 数,对中文的成本和上下文影响尤其明显。训练数据还要和评测集去重,否则分数不可信。数据和 token 准备好以后,下一篇看模型怎样用“预测下一个 token”这一个目标,从这堆文字里学出能力:07. 预训练。

别急,先让缓存热一下。