Subword
[1] Unigram
[2] SentencePiece
Tokenizer
语言模型的第一步, 实际上是将句子 token 化, 然后向量化, 然后才有后面的一步步处理. 之前看论文的时候, 单纯的以为就是把每个单词作为一个 token 就好了. 比如 "My name is MTandHJ" 转换为: [My, name, is, MTandHJ]. 显然它有如下的问题:
- 对于中文可能还好一点, 对于英文它的词汇表将异常的庞大;
- 泛化性很差. 实际上, 你很难想象一个词汇表中恰好有 'MTandHJ' 这个词, 实际中, 'MTandHJ' 应当进一步分解为 [M, T, and, H, J].
另一种策略是将 [a-z, A-Z] 作为词汇表, 很显然绝大部分都能表示, 但是:
- 这样 token 化后的句子会非常的冗长;
- embeddings 几乎表达不了什么关系, 很难指望模型有很强的能力.
所以我们希望: 在限定的词汇表大小下, 尽可能'短'地 token 化句子.
我们知道, 在 encoding 方面, 大抵根据 '词频' 来分配编码长度, 一般来说, 出现频率越高, 编码长度越少. 放在这里, 可以这么理解, 对于那些高频次, 如 'we, you, love, peace' 等需要尽可能少拆分, 将原词作为 token; 而对于低频次, 比如 'compositionality' 往往就要被拆分多次, 如变成 [composition, al, ity].
Byte-Pair-Encoding (BPE)
- BPE 初始化词汇表为 [a-z, A-Z] 等;
- 然后依照词频, 将最有可能相邻的两个词拼在一起作为新词, 如 'a', 'n' -> 'an';
- 重复直到达到词汇表的上限.
Unigram
-
Unigram 主要是为了解决如下的痛点:
-
对于 'Hello world', 我们可能有多种编码方式. 作者采用采样的方式来解决.
-
假设我们有语料库 , 这里 为一个句子. 我们需要涉及一个表将句子 token 化: . 由于上述问题的存在, 我们的映射不是唯一的:
不一定非零即一.
-
故训练中, 作者希望:
而非是固定的.
-
现在的任务是, 如何估计 . 作者假设
其中 返回所有可能的编码方式. 故我们所要作的是估计
-
作者用 EM 算法估计, 要求 最大化:
-
有一个问题是, 词汇表我们是不知道的, 所以作者采用一种启发式的方法, 动态更新词汇表 (具体方法见 [1]).
SentencePiece
- SentencePiece 是一个更加综合性的工具, 它的流程如下:
- 和其它方法相比:
- 一个比较特别地方是, sentencepiece 用 "_" 代表空格.
使用
pip install sentencepiece
- Training: 首先我们需要给定语料库训练:
import sentencepiece as spm
spm.SentencePieceTrainer.train(input="botchan.txt", model_prefix="botchan_tokenizer", vocab_size=4000)
# 会生成:
# - botchan_tokenizer.model
# - botchan_tokenizer.vocab
- 使用:
spp = spm.SentencePieceProcessor(model_file='botchan_tokenizer.model')
outs = spp.encode("Try it first now !")
# outs: [415, 586, 15, 174, 191, 17, 82]
spp.encode_as_pieces("Try it first now !")
# outs: ['▁T', 'ry', '▁it', '▁first', '▁now', '▁', '!']
# 输出 5 中最好的编码
spp.nbest_encode("Try it first now !", nbest_size=5, out_type=str)
# [['▁T', 'ry', '▁it', '▁first', '▁now', '▁', '!'],
# ['▁T', 'r', 'y', '▁it', '▁first', '▁now', '▁', '!'],
# ['▁', 'T', 'ry', '▁it', '▁first', '▁now', '▁', '!'],
# ['▁T', 'ry', '▁it', '▁first', '▁no', 'w', '▁', '!'],
# ['▁T', 'ry', '▁', 'it', '▁first', '▁now', '▁', '!']]
spp.decode([415, 586, 15, 174, 191, 17, 82])
# 'Try it first now !'
spp.get_piece_size()
# 4000
还有如: piece_to_id
, id_to_piece
, encode_as_immutable_proto
等方法.
见 [here].
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2019-07-23 ZFNet: Visualizing and Understanding Convolutional Networks