🥎分词

type
status
date
slug
summary
category
tags
icon
password
AI summary
Blocked by
Blocking
Category

一、为什么要分词

考虑一个简单的句子Hello world,不分词直接拆成字符:H、e、l、l、o、空格、w、o、r、l、d共11个字符:
  • 语义完全丢失
  • 一个复杂的句子或片段拆分出来的字母序列非常长
考虑直接空格分词:
  • 中文没有空格,多语言不支持
  • TokenTokenization语义相近,但词表里是两个毫不相关的词,再来一个Tokenizer就不认识了
  • 词表巨大(OOV)
概括一下:
  • 语言的开放性:语种、语法、专业词汇、人名、地名、网络梗、……
  • 模型的泛化能力:理解词与词的关系,提高学习效率
  • 计算效率:Transformer的自注意力机制计算复杂度为O(N^2)
在语义、泛化能力、效率之间达到平衡:$$\text{有效性} \propto \frac{\text{语义保持}}{( \text{词表大小} + \text{序列长度} )}$$

二、BPE(Byte Pair Encoding)

把最常见的字节对合并为一个新符号以减少长度

步骤:

  1. 把训练语料的每个词看作由字符组成的序列,通常在每个词尾加一个特殊结束符(比如 </w>)以保存词边界信息。
  1. 统计整个语料中所有相邻符号对(pair,例如 ('l','o')('o','w'))的出现频率。
  1. 找到出现频率最高的符号对,把它们合并成一个新符号(比如把 'l'+'o' 合并成 'lo')。
  1. 在语料中用新符号替换所有该对出现位置。
  1. 重复步骤 2–4,直到达到预定的合并次数或词表大小。
训练结果是一系列“合并操作(merge ops)”或最终词表。用于推理时,对新词按同样策略进行合并(贪心地应用已学合并),得到子词序列。

举个🌰:

  1. 给定一组语料:
  1. </w>作为结尾保留边界,初始化
  1. 统计初始相邻对频率
  • low</w>:(l,o) (o,w) (w, </w>)
  • lower</w>:(l,o) (o,w) (w,e) (e,r) (r, </w>)
  • lowest</w>:(l,o) (o,w) (w,e) (e,s) (s, t) (t, </w>)
频率表:
pair
计数
(l,o)
3
(o,w)
3
(w,e)
2
(w,</w>)
1
(e,r)
1
(r,</w>)
1
(e,s)
1
(s,t)
1
(t,</w>)
1
频率最高的是(l,o) (o,w),先合并(l,o),也可以定义不同的优先级选取不同的结果
  1. 合并(l,o) -> lo
  1. 更新频率表
pair
计数
(lo,w)
3
(w, e)
2
(w,</w>)
1
(e,r)
1
(e,s)
1
(s,t)
1
(t,</w>)
1
(r,</w>)
1
频率最高的是(lo,w)
  1. 再合并(lo, w) -> low
pair
计数
(low, e)
2
(low, </w>)
1
(e,r)
1
(e,s)
1
(s,t)
1
(t,</w>)
1
(r,</w>)
1
除主项之外,其余项的频率都是1,没有再继续合并的必要,算法终止
  1. 最终合并结果:

编码一个输入:lower

初始:l o w e r </w>
贪心匹配:
  • l + o -> lo => lo w e r </w>
  • lo + w -> low => low e r </w>
  • low + e -> lowe => lowe r </w>
最终tokens:

三、WordPiece

评价函数

基于BPE,在如何选择新的字词上做概率计算,每次选择加入词表的子词,都是为了最大化训练语料在当前词表下的似然(或最大减少负对数似然),说人话就是:优先合并那些组合在一起后,看起来更像一个整体的、有意义的 Token的字符对,而不是仅仅是高频的随机组合,进而减小token数量
数学表达:$$\text{Score}(\text{A}, \text{B}) = \log\left( \frac{P(\text{A}, \text{B})}{P(\text{A}) \cdot P(\text{B})} \right)$$
其中,P(*)是 Token 在训练语料中出现的概率
  • P(A)·P(B) : 表示如果AB是两个独立事件(即它们随机相邻),它们同时出现的概率。
  • P(A,B): 表示AB 实际相邻出现的概率(作为整体)
那么如果Score越大,说明 $$P(\text{A}, \text{B}){}$$比 $$P(\text{A}) \cdot P(\text{B})$$大得多,就说明AB相邻不是一个偶然事件,以整体的形式出现更有意义

再举例🌰

  1. 还是这组语料
初始词表为:{ l, o, w, e, r, s, t, </w> }
  1. 统计相邻对
  • (l,o): 3
  • (o,w): 3
  • (w,e): 2
  • 其它都是 1
如果把 lo(l+o)作为新 token,会对训练语料的似然增益有多大贡献?同理评估ow(o+w)、we,这里选择(l,o)lo,因为英语里lo的组合更具语义,胜过ow组合
  1. 再评估
  • (lo, w): 3
  • (w, e): 2
  • 其余 1
(lo,w)low在三个单词中都能减少token数量,显著提升似然
  1. 再次评估
  • (low, e): 2
  • others 1
得到词表:{ l, o, w, e, r, s, t, </w>, lo, low, lowe }

编码一个输入:lowest

按最新词表用 longest-match-first:
  1. 从左到右,先试最长匹配:lowe 存在 → 输出 lowe
  1. 余下 s t </w>st</w> 不在表;尝试 s → 存在 → 输出 s
  1. t </w> → 输出 t</w>t + </w> 取决实现
最终得到tokens:
问题:
  • 多语言怎么办?
  • 没空格怎么办?

四、SentencePiece

基本思想:大词表优化

  • 给定一组候选 token,生成初始的候选词表
  • 不断移除不重要的 token
  • 最后剩下最能表达语料的 token
特殊处理:把空格映射为_,这样能对所有语言生效

最后举例🌰

预处理后:
  1. 初始大词表
  1. 对语料分词,以“_我爱学习”为例
同理,列出每段语料可能的分词情况
  1. 计算每个token对语料的贡献得分
  • 定性分析
    • token
      使用频率(想象即可)
      ▁我
      很高
      很高
      学习
      北京
      一般
      偶尔(因为多数用了“▁我”)
      ▁我爱学习
      几乎不用
      ▁我爱北京
      几乎不用
      ▁我爱你
      几乎不用
  • 实际的定量计算是一套关于后验概率和概率期望值的复杂计算
  1. 去除贡献得分低的token
  1. 重复步骤2-4,重新分词,计算新token的贡献得分
    1. token
      被使用次数
      ▁我
      很高
      很高
      学习
      北京
  1. 得到最终词表

总结

  1. 分词的原因:语义、泛化能力、效率之间达到平衡
  1. 三种分词算法:BPE、WordPiece、SentencePiece
特性 / 算法
BPE(Byte Pair Encoding)
WordPiece
SentencePiece(Unigram/BPE)
核心思想
频繁 token 对合并
最大似然概率合并(基于语言模型)
用概率模型选最优 token 序列
输入处理
只能处理空格分好词的文本
同 BPE(依赖空格)
不依赖空格,直接基于字符序列
空格处理方式
需要预先空格分词
需要预先空格分词
将空格视为「▁」符号,直接处理原始文本
词表构建方式
贪心:频率最高的 pair
贪心 + 概率:最大化 likelihood
基于概率模型:删除低贡献 token
优点
简单、高效
拆分结果更自然
最灵活,最通用,不依赖空格
缺点
不考虑上下文概率
仍需空格分词
训练复杂度高一点
典型应用
GPT、LLaMA 早期模型
BERT
GPT-4、T5、现代模型
Prev
vLLM 初体验
Next
k8s informer 架构
Loading...
Article List
如果去做,还有一丝希望;但是不去做,就毫无希望
技术分享
个人总结
转发