乐于分享
好东西不私藏

MicroGPT 源码剖析(二):预训练数据处理与分词器

MicroGPT 源码剖析(二):预训练数据处理与分词器

源码之前,了无秘密。

《STL源码剖析》 By  侯捷

MicroGPT 是 AI 大神 Andrej Karpathy 纯手搓的极简 GPT(Generative Pre-trained Transformer)实现——用 200 行纯 Python(无任何外部依赖)完成了完整的 Transformer 训练与推理流程。

本源码剖析系列总共包含以下六个部分
本篇为第二部分,将逐行分析 MicroGPT 的预训练数据处理(对应源码第 14-21 行)与分词器模块(对应源码第 23-27 行)
1、MicroGPT 预训练数据处理
这是训练一个 GPT 大模型的第一步。
源码:第14行-21行
# Let there be a Dataset `docs`: list[str] of documents (e.g. a list of names)if not os.path.exists('input.txt'):    import urllib.request    names_url = 'https://raw.githubusercontent.com/karpathy/makemore/988aa59/names.txt'    urllib.request.urlretrieve(names_url, 'input.txt')docs = [line.strip() for line in open('input.txt') if line.strip()]random.shuffle(docs)print(f"num docs: {len(docs)}")
预训练数据集 input.txt 是一系列的英文名字(人名)字符串集合 list。然后通过对 list 进行初始化(如首尾空白字符(空格、换行符等)和空行),最终得到一个列表 docs (第19行),包含文件中所有非空行的文本。
第20行:random.shuffle(docs) 对 docs 列表进行原地随机打乱,使训练数据的顺序随机化。如果不打乱的话,假设 step1: aaron; step2: abbey; step3: abby … 那么模型很可能连续学到的都是字母 a 开头名字,这样模型就产生了偏向。
顺序随机化是训练语言模型的常见做法,避免模型学习到数据的固定顺序,提升泛化能力。最终得到的 docs 是一个随机的包含3w多个英文名字的 list 数组。
2、MicroGPT 字符级分词器原理

源码:第23行-27行

# Let there be a Tokenizer to translate strings to sequences of integers ("tokens") and backuchars = sorted(set(''.join(docs))) # unique characters in the dataset become token ids 0..n-1BOS = len(uchars) # token id for a special Beginning of Sequence (BOS) tokenvocab_size = len(uchars) + 1 # total number of unique tokens, +1 is for BOSprint(f"vocab size: {vocab_size}")

2.1 什么是分词(Tokenization)

首先来思考一个问题:什么是分词?

由于模型不能直接理解文字,只能处理数字。分词就是把文本转换成数字序列的过程。

MicroGPT 使用最简单的分词方式——字符级分词:每个字符对应一个整数编号(这点和 ASCII 码的思路是一致的,不同的是字符级分词的编码值按排序顺序从 0 开始紧密编排,词表尽可能小 – 极简目标)。

如 a 对应 0,E 对应 4(不区分大小写),26 个字母对应 0-25。

虽然 MicroGPT 的字符级分词器是最简单的分词方案。但是了解它后,理解更复杂的分词器如 BPE (Byte Pair Encoding)分词(GPT-2/3/4 使用的生产级分词方案)也会更容易:

字符级分词是使用一个字符对应一个 token,如果使用一个字符串文本(即一个单词)对应一个 token 呢?那会导致词表巨大(英文单词总计有100w之多),且无法处理没见过的新词。

BPE 则是一种折中方案:介于字符级和单词级之间的子词分词核心思路是从字符开始,反复合并出现最频繁(子字符串复用越多,所需要的 token 总数就越少)的相邻字符对,逐步构建更大的 token。
下面是不同大模型使用 BPE 分词方案最终的 token 词汇表大小:
模型
BPE 词汇表大小
说明
GPT-2
50,257
基础子词单元
GPT-3
50,000
与 GPT-2 类似
BERT
30,522
WordPiece 算法
LLaMA 2
32,000
优化多语言支持
GPT-4
~100,000+
未公开,估计值
也就是说:英语常用核心词约几万,总词汇超百万,但大模型用 3–10 万子词单元(即 tokens) 即可高效编码几乎所有英文文本。
关于 BPE 在此不再继续展开,有时间单独开篇讨论。我们继续往下走。
2.2 如何构建字符表
分词方式确定之后,接下来就是构造出一个字符表:
对应源码的第 24 行:
uchars = sorted(set(''.join(docs)))
完整的词表如下:
注意到还有一个 token ID = 26,表示 BOS (Beginning of Sequence,起始序列标记),这是一个特殊 token,表示序列的开始和结束
对应源码的第 25 行:
BOS = len(uchars) 
词表总大小 = 唯一字符数 + 1(BOS token),对应源码的第 26 行:
vocab_size = len(uchars) + 1 
BOS 充当开始符与结束符在训练与推理时的作用:
2.3 文本名字如何变成 Token 序列
下面以一个完整的示例(英文名字:emma,来展示文本如何变成 Token 序列)
文本 emma 对应的 token 序列为:[26, 4, 12, 12, 0, 26]
用图形直观展示最终的 token 序列:

对应源码(第 157 行):

tokens = [BOS] + [uchars.index(ch) for ch in doc] + [BOS]

那么 token 在训练的时候是如何使用的呢?

模型的任务是 “Next token prediction”,即给定当前 token,预测下一个 token。

所以一个 token 序列会产生多个”输入→目标”训练对:
关于 token 的更多逻辑不篇不作展开,详情在第五部分训练循环与优化器继续讨论。
2.4 分词器(Tokenizer)数据流总览

分词器(Tokenizer本身非常简单——就是一个字符到整数的查找表。但正是这个简单的桥梁,让模型能够用纯数学运算来”理解”和”生成”文本。

本篇完。

下篇第三部分继续分析 MicroGPT的自动微分引擎逻辑,如果觉的有趣,欢迎讨论关注:)

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » MicroGPT 源码剖析(二):预训练数据处理与分词器

评论 抢沙发

1 + 7 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮