NLP分词notes

BPE

贪心提取所有出现频率高的成为词。

BPE的训练流程

1.初始化:将所有单个字符作为初始词汇表的元素。
2.迭代合并:

  • 统计语料中所有相邻符号对(包括字符和合并后的符号)的出现频率。
  • 找到出现频率最高的符号对,将其合并为新的符号,并添加到词汇表中。
  • 在语料中用新符号替换所有该符号对的出现。

3.重复迭代,直到达到预设的词汇表大小或到达合并次数。

输入:语料D,预设的合并次数N
初始化:词汇表V为语料中的所有字符
for i from 1 to N do:
    统计语料中所有相邻符号对的频率,得到列表P
    选择频率最高的符号对(s1, s2) ∈ P
    合并符号对s1、s2为新符号s_new
    将s_new添加到词汇表V
    用s_new替换语料D中所有的s1 s2
end for
输出:词汇表V

WordPiece

WordPiece目标是在固定大小的词表预算下,选择一组子词单元,使整个训练语料在当前词表下的分词似然最大化。它的核心思想是:初始词汇表包含所有单个字符,通过迭代合并带来最大似然增益的高频字符对,逐步扩展为更长的子词单元。

在 WordPiece 的建模假设中,一个单词的概率是其分解后子词序列概率的乘积:
P(word)=∏i=1nP(tokeni)P(\text{word}) = \prod_{i=1}^{n} P(\text{token}_i)P(word)=i=1nP(tokeni)

因此,WordPiece 的训练目标是最大化整个训练语料的对数似然:
max⁡∑wordslog⁡P(word)\max \sum_{\text{words}} \log P(\text{word})maxwordslogP(word)

WordPiece 的训练流程

1.初始化词汇表:从所有单个字符开始,非开头的token会加上前缀/后缀(例如 ##ing##ed)。
2.计算相邻字符对频率:统计整个训练语料中所有相邻字符对的出现频率。
3. 迭代合并:在每次迭代中,计算增益Gain=loglikelihood(new)−loglikelihood(old)Gain=loglikelihood(new)-loglikelihood(old)Gain=loglikelihood(new)loglikelihood(old),选择能够使语言模型对数似然增益最大的字符对进行合并,并更新词表。重复此过程直到词汇表达到预设大小。
4. 得到最终词汇表

输入:语料D,预设的词汇表大小N
初始化:词汇表V为所有字符
while |V| < N do:
    初始化增益字典G = {}
    for 语料D中的每个可能的子词s(由现有词汇表中的符号组合而成):
        计算将s添加到词汇表V后,语料D的似然增益ΔL
        将(s, ΔL)加入G
    从G中选择ΔL最大的子词s_max
    将s_max添加到词汇表V
    更新模型参数(如概率)
end while
输出:词汇表V

eg.给定初始词表 ["a", "b", "c", "w"] 和词汇 v = ["abc", "abw"],各字符的概率分布如下:

字符概率分布:p(a)=p(b)=13p(a) = p(b) = \frac{1}{3}p(a)=p(b)=31,p(c)=p(w)=16p(c) = p(w) = \frac{1}{6}p(c)=p(w)=61

假如合并ab,合并后词表更新为 ["ab", "c", "w"],重新归一化概率 p(ab)=12,p(c)=p(w)=14p(ab) = \frac{1}{2},p(c) = p(w) = \frac{1}{4}p(ab)=21,p(c)=p(w)=41

词汇序列概率计算:p(v)=p(ab)×p(c)+p(ab)×p(w)=14p(v) =p(ab) \times p(c) + p(ab) \times p(w) = \frac{1}{4}p(v)=p(ab)×p(c)+p(ab)×p(w)=41

原始未合并概率p(old v)=p(a)×p(b)×p(c)+p(a)×p(b)×p(w)=127p(old\ v) =p(a) \times p(b) \times p(c) + p(a) \times p(b) \times p(w) = \frac{1}{27}p(old v)=p(a)×p(b)×p(c)+p(a)×p(b)×p(w)=271
Gain=p(v)−p(old v)=14−127=23108 Gain = p(v) - p(old\ v) = \frac{1}{4} - \frac{1}{27} = \frac{23}{108}Gain=p(v)p(old v)=41271=10823

GPT写的一个demo

from tokenizers import Tokenizer, models, trainers, pre_tokenizers
# #BPE
# from tokenizers import models, trainers
# tokenizer = Tokenizer(models.BPE())
# trainer = trainers.BpeTrainer(vocab_size=30, special_tokens=["[UNK]"])

# 1️⃣ 定义模型类型:WordPiece
tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))

# 2️⃣ 定义预分词规则
tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()

# 3️⃣ 定义训练器
trainer = trainers.WordPieceTrainer(
    vocab_size=100,
    special_tokens=["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]
)

# 4️⃣ 训练文本
corpus = [
    "the quick brown fox jumps over the lazy dog",
    "the quick brown dog jumps over the lazy fox",
    "i love natural language processing",
    "wordpiece helps with unknown words"
]

# 5️⃣ 训练
tokenizer.train_from_iterator(corpus, trainer)

# 6️⃣ 测试编码
output = tokenizer.encode("the quick brown fox")
print("Tokens:", output.tokens)
print("IDs:", output.ids)

# 7️⃣ 保存分词器
tokenizer.save("wordpiece-tokenizer.json")

Q:为什么 vocab_size 越大,分词后句子里的 token 数会更少?
A:因为 vocab_size 越大,分词器词表能收录更多子词单元,包括更长的子词或整个单词。这让分词器在处理文本时可以用更大的块来覆盖文本,减少切分次数,所以最后得到的 token 数会更少。

Q:vocab_size 和合并次数是一个概念吗?
A:不是。vocab_size 是词表大小的上限,但并不保证最后一定会有 vocab_size 那么多的子词单元。分词器会尽量把词表填满到这个上限,但如果语料规模很小、可合并的模式有限,最终学到的词表可能会小于设定的 vocab_size。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值