TT Lab
开始
学习 学习路径 课程

LLM 工程

从零做出 BPE 分词器

在 TT Lab 中继续学习

目标

不依赖任何库,仅使用 Python 实现 BPE 分词器,完成从训练到编码、解码的完整循环。

为什么重要

分词器是 LLM 流水线中最先执行、却最少被理解的组件,但许多关键行为都由它决定。提示词成本按 token 数而非字符数计算,上下文长度限制也以 token 为单位。模型在处理单词拼写问题时格外薄弱,也是因为 token 边界与字符边界不同。

亲手实现后,你会发现这些特性并非抽象理论,而是实现方式的自然结果。尤其在最后一步测量压缩比时,可以用数字确认为什么韩语通常比英语消耗更多 token。

本实验不会下载模型,也不需要互联网或 GPU。语料库是实验数据库 docs 表中的 30 篇韩语文档。

步骤

工作目录为 /root/llm。必须严格遵守规则才能通过评分。

  1. 将 docs 表的 body 按 id 升序逐行保存到 /root/llm/corpus.txt,共 30 行。
  2. 将语料库中字符的频率保存到 /root/llm/char_freq.tsv。格式为 문자<탭>빈도,并且不统计空格字符。按频率降序排列;频率相同时按字符升序排列。
  3. 将基础词表逐行保存到 /root/llm/vocab_base.txt。把语料库中的字符(排除空格)与词尾标记字符 _组成集合,再按码点升序排列。
  4. 将 120 条 BPE 合并规则保存到 /root/llm/merges.txt。每行用空格分隔写成 앞토큰 뒤토큰。训练规则如下。
    • 按空格拆分每一行得到单词,每个单词以 문자들 + ['_'] 列表开始。
    • 每一步统计所有单词中的相邻 token 对频率,并合并频率最高的 token 对。
    • 频率相同时,选择 (앞토큰, 뒤토큰) 元组中字典序较小的 token 对。
    • 在每个单词中从左到右应用合并,且不得重叠。
  5. 将最终词表保存到 /root/llm/vocab.tsv,格式为 토큰<탭>id。先按第 3 步的顺序从编号 0 开始加入基础字符,再按合并发生的顺序追加生成的 token。
  6. 把每篇文档转换为 token id 序列,保存到 /root/llm/encoded.tsv。每行格式为 docs.id<탭>id id id ...,共 30 行。编码时必须按记录的顺序应用已训练的合并规则。
  7. 仅使用 vocab.tsv 和 encoded.tsv 还原原文,并保存到 /root/llm/decoded.txt。连接 token,把 _ 替换为空格,再删除行尾空格,结果必须与原文一致。
  8. 在 /root/llm/stats.tsv 中以一行保存 문자수<탭>토큰수<탭>압축비。字符数是语料库各行长度之和(包括空格),token 数是全部 token 的总数;压缩比是字符数除以 token 数并四舍五入到小数点后三位。

参考

下载语料库

将 docs 表的 body 按 id 升序逐行保存到 /root/llm/corpus.txt,共 30 行。

将 docs 表的 body 按 id 顺序逐行写入文件。使用 psql 只输出查询结果的选项会更方便。

统计字符频率

将语料库中字符的频率保存到 /root/llm/char_freq.tsv。格式为 문자<탭>빈도,并且不统计空格字符。按频率降序排列;频率相同时按字符升序排列。

不要统计空格。请注意有两个排序条件。

创建基础字符词表

将基础词表逐行保存到 /root/llm/vocab_base.txt。把语料库中的字符(排除空格)与词尾标记字符 _组成集合,再按码点升序排列。

除了语料库中出现的字符,表示词尾的标记字符也必须加入词表。

训练 120 条合并规则

将 120 条 BPE 合并规则保存到 /root/llm/merges.txt。每行用空格分隔写成 앞토큰 뒤토큰。训练规则如下。

每一步都必须重新统计相邻 token 对的频率。请严格遵守并列时的处理规则。

创建最终词表

将最终词表保存到 /root/llm/vocab.tsv,格式为 토큰<탭>id。先按第 3 步的顺序从编号 0 开始加入基础字符,再按合并发生的顺序追加生成的 token。

先加入基础字符,再按合并发生的顺序追加。id 从 0 开始连续编号。

将文档转换为 token id 序列

把每篇文档转换为 token id 序列,保存到 /root/llm/encoded.tsv。每行格式为 docs.id<탭>id id id ...,共 30 行。编码时必须按记录的顺序应用已训练的合并规则。

必须按训练时的相同顺序应用合并规则,才能得到一致结果。

仅用 id 序列还原原文

仅使用 vocab.tsv 和 encoded.tsv 还原原文,并保存到 /root/llm/decoded.txt。连接 token,把 _ 替换为空格,再删除行尾空格,结果必须与原文一致。

连接 token 后,把词尾标记还原为空格,并删除行尾多余空格。

计算压缩比

在 /root/llm/stats.tsv 中以一行保存 문자수<탭>토큰수<탭>압축비。字符数是语料库各行长度之和(包括空格),token 数是全部 token 的总数;压缩比是字符数除以 token 数并四舍五入到小数点后三位。

用字符数除以 token 数。字符数中也包含空格。