赞
踩
这个是当前英文分词的标准方法了。
在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
{‘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}
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('==========')
在之前的算法中,我们已经得到了 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>"
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。