基于BPE的汉语tokenization
2018211316班 小透明
算法思想:
BPE主要目的是为了数据压缩,算法描述为字符串里频率最常见的一对字符被一个没有在这个字符中出现的字符代替的层层迭代过程
数据结构
变量 | 含义 |
---|---|
book | 列表形式储存训练集文本 |
vocab | 列表形式储存词表 |
pair | 字典形式存储两词组成新词的频数 |
函数
函数 | 解释 |
---|---|
initvocab | 初始化词表、book、记录每个字出现的起始位置 |
init_pairs | 初始化pair字典 |
max_pairs | 寻找pairs中频数最大的子词 |
update_pairs | 根据频率最大子词更新pair字典 |
merge | 利用训练集得出的词表对测试集进行 划词 |
伪代码
#初始化词表vocab、字典pairs、文本列表book
vocab=initvocab()
while len(vocab)<=10000:
#寻找pairs中频数最大的子词
most_word=max_pair()
#更新pair字典
update_pairs()
#利用训练集得出的词表对测试集进行 划词
merge(test,vocab)
优化
问题:在更新book时,起初使用del将两个子词合并新词,但由于列表后单词前移造成巨大时间消耗
解决:采用标志位flag,表示删除,遇到flag标志后移或前移寻找临接词
问题:更新pair时,要寻找新合成词的邻居,搜索范围过大
解决:在初始化时,记录每个字出现位置范围用begin、end描述
import collections
flag='$$$FLAG'
def initvocab(filepath, book, vocabfile,begin,end):
i=0
f = open(filepath, encoding='utf-8')
fv = open(vocabfile, "w",encoding='utf-8')
vocab = []
vocab.append('\n')
for lines in f.readlines():
for word in lines.split(' '):
word=word.strip(' \r\t\n')
book.append(word)
end[word]=i
if word not in vocab:
begin[word]=i
fv.write(word+'\n')
vocab.append(word)
i=i+1
fv.close()
return vocab
def init_pairs(book,pair):
preword = ""
for word in book:
pair[preword + word] = pair[preword + word] + 1
preword = word
print(pair)
return pair
def max_pairs(pair,vocab,last):
last=max(pair,key=lambda x: pair[x])
print(last,pair[last])
vocab.append(last)
return last
def islast(book,i,last):
flag="$$$FLAG"
if book[i]==flag:
return 0
if book[i]==last[0:len(book[i])]:
j=i+1
while book[j]==flag and j+1<len(book):
j=j+1
if book[i]+book[j]==last:
return j
return 0
def update_book(book,last,pair):
i=begin[last[0]]
while i<end[last[0]]:
if (i< len(book)-1 and islast(book,i,last)!=0 ):
ni=islast(book,i,last)
pair[book[i] + book[ni]] = 0
j=i-1
k=ni+1
while(book[j]==flag and j>=0):
j=j-1
while(book[k]==flag and k<len(book)):
k=k+1
if j>=0:
pair[book[j] + book[i]] = pair[book[j] + book[i]] -1
if k<len(book):
pair[book[ni] + book[k]] = pair[book[ni] + book[k]] - 1
book[i]=last
book[ni]=flag
if j>=0 :
pair[book[j]+book[i]]=pair[book[j]+book[i]]+1
if k<len(book):
pair[book[i] + book[k]] = pair[book[i] + book[k]] + 1
i=i+1
return pair
if __name__ == '__main__':
filepath = 'train_BPE.txt'
logpath=r'C:\Users\Administrator\Desktop\bpe\log_train1.txt'
vocabfile=r'C:\Users\Administrator\Desktop\bpe\vocab_train1.txt'
fl=open(logpath,"w",encoding='utf-8')
fv = open(vocabfile, "w",encoding='utf-8')
book=[] #book is list by line of train set
vocab=[]
pair = collections.defaultdict(int)
begin=collections.defaultdict(int)
end = collections.defaultdict(int)
i=0
last=''
added=''
vocab=initvocab(filepath,book,vocabfile,begin,end)
print(len(vocab))
pair=init_pairs(book,pair)
while len(vocab)<=10000:
print(i,':')
added=max_pairs(pair,vocab,added)
s=str(i)+": "+added+' '+str(pair[added])+'\r\n'
fl.write(s)
fv.write(added+'\n')
pair= update_book(book,added,pair)
i=i+1
#print(vocab[:])
fl.close()
fv.close()