Hotdry.

Article

从零构建LLM数据工程链路:BPE分词器增量训练、语料去重与内存映射加载器

深入解析从原始语料到可训练数据集的端到端工程实现,涵盖BPE分词器构建、语料去重策略与内存映射数据加载器的性能优化要点。

2026-06-02mlops

在大型语言模型(LLM)的训练流程中,数据工程层往往决定了模型质量的上限。与模型架构设计相比,从原始语料到可训练数据集的转换链路涉及更多工程细节与性能权衡。本文聚焦于这一数据工程层,深入探讨 BPE 分词器的增量构建机制、语料去重的技术策略,以及内存映射数据加载器的实现原理。

BPE 分词器的增量构建机制

Byte Pair Encoding(BPE)算法是现代 LLM 分词的事实标准,从 GPT-2 到 Llama 3 均采用此方案。其核心思想是通过迭代合并高频字符对,将文本压缩为子词(subword)序列,在词汇量与序列长度之间取得平衡。

基础原理与初始化

BPE 分词器以 256 个基础字节值作为初始词汇表(对应 ASCII 字符集),每个字节映射到一个唯一 ID。对于输入文本,首先转换为字节序列,然后进入迭代合并阶段。在每一轮迭代中,算法扫描整个语料,统计所有相邻字符对的出现频率,将最高频的字符对合并为一个新 token,并赋予其一个新的 ID(从 256 开始递增)。

这一过程持续进行,直到达到预设的词汇表大小。以 GPT-2 为例,其词汇表大小为 50,257,意味着在 256 个基础 token 之上,通过迭代合并生成了约 50,000 个新 token。GPT-4 的词汇表扩展至 100,256,而 GPT-4o 更是达到了 199,997,反映了更大规模语料对词汇丰富度的需求。

特殊 Token 的工程处理

在实际训练中,特殊 token(如<|endoftext|><|padding|>)的处理需要特别注意。这些 token 不参与 BPE 合并过程,而是在词汇表构建完成后直接插入。在编码阶段,需要通过正则表达式匹配这些特殊 token,确保它们被正确映射到对应的 ID,而不是被拆分为子词单元。

此外,空格字符的处理方式也值得关注。GPT-2 的实现将空格替换为特殊符号Ġ,使得词边界信息被编码进 token 本身。这种设计允许模型在没有显式分隔符的情况下区分词边界,但需要在解码阶段进行相应的反向转换。

语料去重的技术策略

语料去重是数据清洗的关键环节,直接影响模型的泛化能力。未经去重的训练数据会导致模型过度学习重复模式,产生低质量输出。

精确去重与近似去重

精确去重通过计算文档的哈希值(如 SHA-256 或 MD5)进行完全匹配,适用于去除完全相同的重复文档。然而,对于经过轻微改写的近似重复内容,需要采用更复杂的策略。

MinHash 与 SimHash 是处理近似去重的常用算法。MinHash 通过局部敏感哈希(LSH)将文档映射为签名,相似文档的签名碰撞概率较高。通过设置适当的 Jaccard 相似度阈值(通常 0.8-0.9),可以有效识别并过滤近似重复内容。

去重的粒度选择

去重可以在不同粒度进行:文档级、段落级或句子级。文档级去重开销最小,但可能保留大量内部重复的段落;段落级去重更精细,但计算成本更高。实践中,通常采用多级策略:先进行文档级去重过滤明显重复,再在段落级进行精细去重。

去重后的语料分布也需要监控。理想情况下,去重应均匀影响各个主题领域,避免某些领域的样本被过度删除导致分布偏移。

内存映射数据加载器的设计

经过分词和去重后的语料通常以 token ID 序列的形式存储。对于数十亿 token 规模的训练数据,传统的全量加载方式在内存受限的环境中不可行。

内存映射(Memory-Mapped)机制

内存映射文件允许程序将磁盘文件映射到虚拟地址空间,实现按需加载。在 PyTorch 中,numpy.memmaptorch.Tensor的内存映射功能可以高效处理大规模 tokenized 数据。

具体实现中,首先将 tokenized 数据写入二进制文件(如.bin格式),每个 token 以固定长度的整数存储(通常为 uint16 或 uint32)。在训练时,数据加载器通过内存映射访问这些文件,只将当前需要的片段加载到物理内存,显著降低内存占用。

数据分片与并行加载

为进一步提升 IO 效率,通常将大规模语料切分为多个分片(shard),每个分片独立存储。数据加载器可以并行读取多个分片,通过预取(prefetch)机制隐藏 IO 延迟。

在分布式训练场景下,数据并行策略需要与分片策略协调。常见的做法是将不同分片分配给不同进程,确保每个训练步骤处理的数据不重复。同时,需要实现动态数据加载,当当前分片读取完毕后自动切换到下一个分片,实现无缝训练。

工程实践建议

基于上述技术要点,以下是可落地的工程实践建议:

分词器训练参数:对于通用领域语料,词汇表大小建议设置在 32,000-65,000 之间。过小的词汇表会增加序列长度,过大的词汇表则导致嵌入层参数膨胀。训练语料应覆盖目标领域的典型文本,建议至少包含 10GB 原始文本以确保词汇表充分学习。

去重阈值配置:文档级去重采用精确匹配即可;段落级去重建议设置 Jaccard 相似度阈值 0.85,兼顾去重效果与计算开销。去重后应保留 10-20% 的验证样本,用于监控去重对数据分布的影响。

内存映射优化:单个分片大小建议控制在 1-4GB,匹配操作系统页缓存的粒度。使用np.memmap时设置mode='r'确保只读访问,避免意外修改。对于多 epoch 训练,实现分片级别的 shuffle,避免每个 epoch 读取相同顺序的数据。

监控指标:数据管道应暴露关键指标,包括 tokenization 速率(tokens/second)、去重比例、内存映射命中率等。这些指标有助于及时发现数据瓶颈,优化管道性能。

数据工程层是 LLM 训练的基础设施,其质量直接决定了模型能否从海量语料中有效学习。通过精心设计的 BPE 分词器、严格的去重策略和高效的内存映射加载器,可以构建出稳定、可扩展的数据管道,为模型训练提供高质量的数据输入。


参考来源

  • FareedKhan-dev/train-llm-from-scratch: A straightforward method for training your LLM from downloading data to generating text
  • Sebastian Raschka, "Implementing A Byte Pair Encoding (BPE) Tokenizer From Scratch", 2025

mlops

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com