大模型的tokenization
文章摘要:本文探讨了子词级编码在语言模型中的应用与挑战。BPE算法通过合并高频子词优化token表示,但不同语言(如韩语、日语)和代码缩进会消耗更多token。GPT-4改进了空格合并机制,提高了代码处理效率。文章对比了Tiktokenizer和WordSentence分词策略,指出词表大小、特殊token处理等差异。最佳实践包括:字符级任务需拆分单词、数字需标准化处理、选择token高效的格式(
https://tiktokenizer.vercel.app/?model=gpt2
# 子词级(subword)编码
- 一个英文单词可能由大于等于两个token组成
- 大小写可能敏感
- 可能空格和一个单词前缀共同构成一个token
- 韩语,日语所占用的tokens数量可能比英文多,因为表达相同意思,韩语的单词更多,因此token会更多。后果是在context window有限的情况下,可能上下文信息会不够用
- 对python代码,如果有缩进,这会消耗很多的context window长度,gpt2对于每个空格都用一个token表示
- gpt4对于缩进的所有空格只用一个token表示,因此对代码缩进支持更好;同时gpt4的总token数增多,因此对相同的文本可以用较少的token表示,好处是相同上下文窗口可以表示更多的句子,缺点是embedding的维度会增加,softmax的维度也会增加
- 每个character使用unicode表示,用utf-8 encode(4个字节)。不使用unicode作为token,因为这样词表太大了,因此使用utf-8
# BPE
- 对诸如冷门语言和代码,你可能需要自己训练一个分词器,因为如果使用韩语,可能本身会有很多的token数量,因此merge token的数目应该增加,训练BPE的超参数是merge的次数,如果是2次,那么最多可以表示的是256和257
- 对bpe训练好之后,可以对原始文本进行encode和decode
- 注意点,不是所有text经过encode再经过decode可以恢复,比如128就无法decode,因为它不符合utf-8的规则,不能作为开头

- 使用regex正则项进行chunk的分块处理,这样可以避免不同的单词之间进行合并,经过chunk处理之后,每个word首先单独处理,然后把所有经过处理后的words concat起来,注意:这里每个split的word前面有空格
- gpt2对于代码的缩进前的很多空格,没有将他们合并,依然是一个一个220的tokens,openai训练tokenizer代码未公开,可能更复杂,比如大小写敏感,空格处理等
- gpt4解决了空格merge的问题
# special tokens

- gpt2的vocab的长度是50257=256个raw token+50000次merge操作+1个特殊token(<|endoftext|>)
- 可以自己自定义特殊token的id
- 启示意义:prompt中空格matters,因为“空格x”和x表示的token可能不一样
# Tiktokenizer和wordsentence
- 这两个是完整的分词处理步骤
- 它们都可以调用bpe的核心算法
- chunk和其他操作不属于bpe算法
- 区别在于:前者针对utf-8的字节进行bpe,而后者针对unicode进行bpe;前者词表很少,只有256个base(不包括merge),后者有14万个base,如果有个很古老的符号没有出现在unicode中,那么就会变成UNK
- 假设我们使用wordsentence自己用训练数据训练一个tokenizer,如果其中只包含了英文,如果我们推断的时候想要encode一个韩文,如果在option中未开启bytefallback,该韩文会变成UNK,因为韩文没有出现在训练数据;还有一种情况是,在option中有coverage比例,如果一个单词在数据中出现概率<99.5%,就忽略掉,因此如果推断的时候出现了该单词,也会是UNK
- wordsentence中为什么空格会显示成_? 原因是可视化
- 为什么句子之前会有一个前导空格?wordsentence的预处理,这样可以让hello world, hello中前后两个_hello的token是一样的,这样解决了tiktokenizer的问题
## 词表对比
- Tiktokenizer直接对utf-8编码,0-255个是前256个byte,之后是merge,最后是eot
- wordsentence一开始是unk,eos,pad等特殊token,如果开启bytefallback,是256个byte(和每个单词分别存放),之后是合并的单词,最后是单个的code point
# 代码解释
- 为什么vocabsize不能无限增加?
- 因为嵌入层和最终的输出层logit会受影响
- 这样每一个token出现次数会降低,因此每个token会训练不足
- 相同句子使用的token数量更少了,太多信息被融入单个token
# 最佳实践
- llm对于字符级别任务效果很差,eg:找到.defaultstyle中出现了多少个不同的letter:原因是可能用一个token表示了一个很长的单词;解决办法是 告诉llm,先将这个word根据character用空格分开,这样encode就是不同的tokens
- 为什么LLM处理韩语不好?因为相同的句子,英语所占用的tokens少,韩语更多,原因是英语语料更多,因此bpe会将更多英语合并成一个token,因此tokens语义信息更加分散
- 为什么LLM处理简单数学不好?因为一个四位数可能encode为一个token,也可能是4个tokens,完全随机任意。改进方法是对所有数字进行split
- 为什么gpt2在代码效果不好?因为gpt2处理代码中的缩进时候,把所有空格都重复考虑,显著增加了上下文窗口;这在gpt4中改进
- 为什么LLM遇到<|endoftext|>时终止输出?因为它是结束标记,token的是最后一个id
- 为什么llm break当我询问solidgoldmagikarp? 因为这是一个reddit用户,碰巧在训练分词器的时候,该用户发了很多帖子导致分词器给它分配了一个特殊的tokenid,但是训练的时候使用的是过滤后的数据集,没有出现这个token id,因此在推断的时候,llm会产概率坍塌,生成的内容完全是随机的,因为该tokenid的嵌入完全是随机初始化的
- 为什么使用yaml而不是json?因为表达相同的意思,yaml占用的token数量少于json,对于收费计划是按照token来收费的
更多推荐


所有评论(0)