深度学习系列28:BPE分词模型
Posted IE06
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习系列28:BPE分词模型相关的知识,希望对你有一定的参考价值。
这个是当前英文分词的标准方法了。
1. 简介
在NLP模型中,输入通常是一个句子,例如I went to New York last week.。传统做法:空格分隔,例如[‘i’, ‘went’, ‘to’, ‘New’, ‘York’, ‘last’, ‘week’]。
BPE算法通过训练,能够把[‘loved’, ‘loving’, ‘loves’]拆分成[“lov”, “ed”, “ing”, “es”]几个部分,这样可以把词的本身的意思和时态分开,有效的减少了此表的数量。算法流程如下:
1)设定最大subwords个数
2)将所有单词拆分为单个字符,并且在最后添加一个停止符,同时标记处该单词出现的次数。例如,"low"这个单词出现了5次,那么它将会被处理为‘l o w ’: 5
3)统计每一个连续字节对的出现频率,选择最高频者合成新的subword
4)重复第3步直到达到第1步设定的subwords词表大小或下一个最高频的字节对出现频率为1
2. 例子
‘l o w ’: 5, ‘l o w e r ’: 2, ‘n e w e s t ’: 6, ‘w i d e s t ’: 3
出现最频繁的字节对是e和s,共出现了 6+3 = 9次,因此将它们合并
‘l o w ’: 5, ‘l o w e r ’: 2, ‘n e w es t ’: 6, ‘w i d es t ’: 3
出现最频繁的字节对是es和t,共出现了6+3=9次,所以将它们合并
‘l o w ’: 5, ‘l o w e r ’: 2, ‘n e w est ’: 6, ‘w i d est ’: 3
出现最频繁的字节对是est和,共出现了6+3=9次,因此将它们合并
‘l o w ’: 5, ‘l o w e r ’: 2, ‘n e w est’: 6, ‘w i d est’: 3
出现最频繁的字节对是l和o,共出现了5+2 = 7 次,因此将它们合并
‘lo w ’: 5, ‘lo w e r ’: 2, ‘n e w est’: 6, ‘w i d est’: 3
3. 简单的实现
import re, collections
# 生成初始词汇字典
def get_vocab(filename):
vocab = collections.defaultdict(int)
with open(filename, 'r', encoding='utf-8') as fhand:
for line in fhand:
words = line.strip().split()
for word in words:
vocab[' '.join(list(word)) + ' </w>'] += 1
return vocab
# 获取所有字节对的频率(找出最大的进行合并)
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
def merge_vocab(pair, v_in):
v_out =
bigram = re.escape(' '.join(pair))
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
def get_tokens(vocab):
tokens = collections.defaultdict(int)
for word, freq in vocab.items():
word_tokens = word.split()
for token in word_tokens:
tokens[token] += freq
return tokens
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
# Get free book from Gutenberg
# wget http://www.gutenberg.org/cache/epub/16457/pg16457.txt
# vocab = get_vocab('pg16457.txt')
print('==========')
print('Tokens Before BPE')
tokens = get_tokens(vocab)
print('Tokens: '.format(tokens))
print('Number of tokens: '.format(len(tokens)))
print('==========')
num_merges = 5
for i in range(num_merges):
pairs = get_stats(vocab)
if not pairs:
break
best = max(pairs, key=pairs.get)
vocab = merge_vocab(best, vocab)
print('Iter: '.format(i))
print('Best pair: '.format(best))
tokens = get_tokens(vocab)
print('Tokens: '.format(tokens))
print('Number of tokens: '.format(len(tokens)))
print('==========')
4. 编码和解码
在之前的算法中,我们已经得到了 subword 的词表,对该词表按照字符个数由多到少排序。编码时,对于每个单词,遍历排好序的子词词表寻找是否有 token 是当前单词的子字符串,如果有,则该 token 是表示单词的 tokens 之一
我们从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。最终,我们将迭代所有的tokens,并将所有子字符串替换为tokens。 如果仍然有子字符串没被替换但所有token都已迭代完毕,则将剩余的子词替换为特殊token,如
例如
# 给定单词序列
["the</w>", "highest</w>", "mountain</w>"]
# 排好序的subword表
# 长度 6 5 4 4 4 4 2
["errrr</w>", "tain</w>", "moun", "est</w>", "high", "the</w>", "a</w>"]
# 迭代结果
"the</w>" -> ["the</w>"]
"highest</w>" -> ["high", "est</w>"]
"mountain</w>" -> ["moun", "tain</w>"]
解码
将所有的tokens拼在一起即可,例如
# 编码序列
["the</w>", "high", "est</w>", "moun", "tain</w>"]
# 解码序列
"the</w> highest</w> mountain</w>"
以上是关于深度学习系列28:BPE分词模型的主要内容,如果未能解决你的问题,请参考以下文章