)
从零构建 GPT 字符级词汇表stoi/itos 双向映射与 encode/decode 实现LeetCode 课程实战【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode构建语言模型的第一步不是搭建网络而是为模型准备好它唯一能看懂的整数输入。本篇文章围绕 LeetCode 课程中 articles/build-vocabulary.md 所讲解的**字符级词汇表Character-Level Vocabulary**展开通过 Python 字典构建字符串到整数的双向映射stoi/itos并实现encode/decode这对互逆函数将原始训练文本无损地转换为 GPT 模型处理的整数序列。读完本文你将掌握字符级分词的核心原理、可复制的 Python 实现、复杂度分析与常见陷阱并理解它在本课程 GPT 项目数据管线中的位置以及它与 BPE、词级分词之间的取舍。前置知识在动手实现之前需要具备两项基础能力Python 字典Dictionaries词汇表本质上是两本字典——字符串到整数的stoistring-to-integer与整数到字符串的itosinteger-to-string。需要熟练创建键值对、用推导式批量构建映射并理解dict.items()在反转映射中的作用。字符级分词Character-Level Tokenization把每个字符当作一个独立 token是最简单的分词方式也是本课程 GPT 模型采用的方式。相关概念可参考 articles/nlp-intro.md 与本系列的 articles/tokenizer-bpe.md 对照学习。核心概念为什么语言模型需要词汇表在语言模型处理文本之前必须先建立一份词汇表字符或 token与整数之间的双向映射。模型内部只与整数打交道因此需要encode把文本转换为整数序列decode把整数序列还原为文本。构建过程分为四步提取唯一字符从训练文本中取出所有不重复的字符排序按字母序排序保证确定性deterministic的顺序构建stoistring-to-integer为每个字符分配一个从 0 开始、唯一的索引构建itosinteger-to-string即反向映射。这就是字符级分词词汇表大小等于训练数据中唯一字符的个数英文文本通常在50100之间。对比其他方案分词方案词汇表规模特点字符级本课程50100序列更长但训练中出现过的字符永远不会 OOVout-of-vocabularyBPEGPT-2 等生产模型50,000子词粒度压缩常见模式、拆分罕见词词级100,000序列短但遇到未登录词即失效encode与decode必须是互逆的decode(encode(text)) text。这一往返round-trip性质是硬性要求——如果无法完美还原原始文本模型就无法学到正确的输入输出映射。这一点与 articles/string-encode-and-decode.md 中编码/解码互为逆操作的设计思想一脉相承。解决方案直觉Intuition用set()提取唯一字符sorted()排序再用enumerate一次性构建两本字典。编码是字典查询的列表推导式解码是把查到的字符拼接成字符串。实现Implementationfrom typing import Dict, List, Tuple class Solution: def build_vocab(self, text: str) - Tuple[Dict[str, int], Dict[int, str]]: chars sorted(set(text)) stoi {ch: i for i, ch in enumerate(chars)} itos {i: ch for ch, i in stoi.items()} return stoi, itos def encode(self, text: str, stoi: Dict[str, int]) - List[int]: return [stoi[ch] for ch in text] def decode(self, ids: List[int], itos: Dict[int, str]) - str: return .join(itos[i] for i in ids)要点说明chars sorted(set(text))一行同时完成去重 排序set保证唯一性sorted保证索引分配的确定性enumerate(chars)从 0 开始顺序编号天然满足每个字符一个唯一索引itos {i: ch for ch, i in stoi.items()}通过反转stoi的键值对构建从构造上保证两本字典互为精确逆映射而不是另起炉灶独立编号独立编号极易引入错位。逐步走查Walkthrough以text hello为例步骤输入输出提取唯一字符hello{h, e, l, o}排序集合[e, h, l, o]构建 stoi排序后的字符{e: 0, h: 1, l: 2, o: 3}构建 itos反转 stoi{0: e, 1: h, 2: l, 3: o}编码 hello逐字符查表[1, 0, 2, 2, 3]解码[1, 0, 2, 2, 3]逐整数查表hello往返验证decode(encode(hello)) hello。时间与空间复杂度时间构建词汇表为 $O(N \log N)$对唯一字符排序编码/解码均为 $O(N)$其中 $N$ 是文本长度。空间$O(V)$$V$ 为唯一字符个数用于存储两本词汇字典。常见陷阱Common Pitfalls1. 不对唯一字符排序Python 的set不保证迭代顺序。如果不排序同一段文本在不同运行环境下可能产生不同的词汇表导致索引分配不可复现破坏实验的确定性。# 错误顺序不确定 chars list(set(text)) # 正确排序保证可复现 chars sorted(set(text))2. 独立构建 itos 导致映射错位itos必须是stoi的精确逆映射。如果独立构建例如对同一个chars列表重新 enumerate一旦stoi的键顺序与chars顺序不一致两本字典就会出现错位decode(encode(text))便不再等于text。# 错误独立构建可能不是精确逆映射 itos {i: ch for i, ch in enumerate(chars)} # 正确从 stoi 派生保证互逆关系 itos {i: ch for ch, i in stoi.items()}排序陷阱与互逆性陷阱的本质都指向同一原则词汇表的构建必须确定且自洽。这也与 BPE 学习合并表时词频相同则按字典序打破平局的确定性要求见 articles/tokenizer-bpe.md是一致的。在 GPT 项目中词汇表在整个数据管线中的位置在课程 GPT 项目中本节内容对应data/vocab.py。本课程 GPT 模型采用字符级分词因此这份词汇表负责把原始训练文本转换成模型实际处理的整数序列。要理解它的位置需要把它放入完整的数据管线中看待数据集加载对应data/dataset.py见 articles/gpt-dataset.md从原始文本生成输入 目标右移一位的训练对词汇表编码对应data/vocab.py即本文把文本字符映射为整数 ID模型前向对应model/gpt.py见 articles/code-gpt.md模型接收整数 ID输出词汇表上的 logits——注意输出维度正是vocab_size即本文词汇表的规模两者必须严格一致训练对应train.py见 articles/train-your-gpt.md交叉熵损失在每个位置把输出视为从 $V$ 个词汇中选下一个 token的分类问题$V$ 即词汇表大小未训练模型的初始损失应接近 $\ln(V)$生成对应generate.py见 articles/make-gpt-talk-back.md自回归循环每步采样一个 token ID再通过itos解码成字符输出——生成函数的签名int_to_char正是本文构建的itos映射。从以上调用链可以推断词汇表是数据管线的枢纽——它既决定了数据加载器输出什么整数也决定了模型输出层的维度还决定了生成阶段能否把采样结果还原成可读文本。任何一个环节的 ID 约定不一致整条管线都会断裂。生产级模型如 GPT-2并不使用字符级词汇表而是采用 BPE其编码器/解码器也是同样的双向映射思想只是 token 从单个字符变成了子词详见 articles/tokenizer-bpe.md。理解字符级版本是理解真实 tokenizer 的最佳起点。关键要点Key Takeaways字符级词汇表是最简单的分词方式词汇表大小等于训练数据中唯一字符的个数英文文本通常为 50100。stoi/itos组合保证无损往返转换文本与整数序列之间可以完美互转这是任何 tokenizer 的硬性要求decode(encode(text)) text必须成立。排序保证确定性的 ID 分配不排序时同一段文本在不同运行中可能产生不同词汇表破坏实验可复现性。词汇表贯穿 GPT 全流程从数据加载到模型输出维度、再到生成解码vocab_size与itos是整个数据管线的公共约定值得在动手搭建模型前优先夯实。【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考