Hello,大家好,时隔这么长时间,我又来写文章了,这一年发生了不少事情,换了工作还要照顾受伤的父亲,不过生活还是要继续向前,学习也要继续的进行下去。好了,废话少说,下面开始今天的正题吧。
这次,要向大家解释的是BPE的分词算法,为什么要解释这个东东呢,原因是最近在研究大模型的分词方法,发现对大模型来说,其分词即token的处理方法,并不是传统用的结巴分词方法。就Llama和Qwen以及Baichuan2等大模型采用的都是BPE(Byte Pair Encoding)优化算法,下面对它进行介绍:
BPE(Byte Pair Encoding)是一种无监督的优化算法,其所采用的思想为计算相邻字符的出现频率,取出现最高的频次的字符对对其进行合并以减少深度学习中所需要词典的单词数量。其具体代码如下:
def get_stats(vocab):
pairs = collections.defaultdict(int)
for word, freq in vocab.items():
symbols = word.split()
for i in range(len(symbols)-1):
pairs[symbols[i],symbols[i+1]] += freq # 这里是每两个两个的算频率
return pairs # 第一次计算时:pairs:defaultdict(<class 'int'>, {('l', 'o'): 7, ('o', 'w'): 7, ('w', '</w>'): 5, ('w', 'e'): 8, ('e', 'r'): 2, ('r', '</w>'): 2, ('n', 'e'): 6, ('e', 'w'): 6, ('e', 's'): 9, ('s', 't'): 9, ('t', '</w>'): 9, ('w', 'i'): 3, ('i', 'd'): 3, ('d', 'e'): 3})
def merge_vocab(pair, v_in):
v_out = {}
bigram = re.escape(' '.join(pair))
print(bigram)
p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
for word in v_in:
w_out = p.sub(''.join(pair), word) # 合并
v_out[w_out] = v_in[word] # 频率赋值
return v_out
vocab = {'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}
num_merges = 1000
for i in range(num_merges):
pairs = get_stats(vocab)
if not pairs:
break
best = max(pairs, key=pairs.get) # ('e', 's')
vocab = merge_vocab(best, vocab)
print(best)
print(f"vocab {i}:{vocab}")
其中,get_stats()函数的作用为频次统计,计算通过传入的字典,计算相邻字符的频次。merge_vocab()函数是一个用于合并的函数,通过正则表达式,合并共线频率最高的字符对,最后的循环就是依次调用这两个函数,这样运用无监督的方法可以有效降低词典规模。
上面的例子是对英文的处理,对中文的处理原理上是一样的,不过处理方式上会有所不同,这主要是由中文不同的编码方式决定的,在utf-8编码方式上,可以用字节的方法处理相邻字符,此外,也可以以字为单位进行处理。
在函数调用上,可用sentencepiece.spm.SentencePieceProcessor()方法进行实现,
参数说明:
参数 | 说明 |
---|---|
input | 训练语料文件,可以传递以逗号分隔的文件列表。文件格式为每行一个句子。 无需运行tokenizer、normalizer或preprocessor。 |
model_prefix | 输出模型名称前缀。 训练完成后将生成 <model_name>.model 和 <model_name>.vocab 文件。 |
vocab_size | 训练后的词表大小,例如:8000、16000 或 32000 |
character_coverage | 模型覆盖的字符数量,对于字符集丰富的语言(如日语或中文)推荐默认值为 0.9995,对于其他字符集较小的语言推荐默认值为 1.0。 |
model_type | 模型类型。 可选值:unigram(默认)、bpe、char 或 word 。 使用word类型时,必须对输入句子进行pretokenized。 |