如何在Python中有效地编码bigram计数和替换的字节对?
|
在 Byte Pair Encoding算法中,有一个替换步骤,它将由空格分隔的字符串更改为bigrams. 即,给出一个str元组列表: [('t','h','i','s','ue000'),('c','o','r','p','u',('i','n',('t','x','t','f','l','e',('s','c',('b','a',('a','d',('f',('.','ue000')]
和一个字符串元组:(‘我’,’s’) 如何处理列表,使其迭代所有元组键,并用(‘is’)替换(‘i’,’s’),即输出Counter看起来像这样: [('t','is',('is','ue000')]
我试过这个: >>> cin
[('t','ue000')]
>>> [tuple(' '.join(i).replace(' '.join(qtuple),''.join(qtuple)).split()) for i in cin]
[('t','ue000')]
但是有没有比循环遍历每个单词更有效的方法,然后将它们更改为字符串以进行替换并再次拆分它们然后将它们转换回元组? 正则表达式替换会更快吗?有没有办法处理元组列表而不处理字符串? 我试过这个,似乎用str.replace替换字符串不是问题.它真的在计算双子座并提取它们: import io
from collections import Counter
import time
infile = 'big.txt' # comes from norvig.com/big.txt
n = 2
with io.open(infile,encoding='utf8') as fin:
text = fin.read().lower().replace(u' ',u"uE000")
for j in range(1,6400):
unused_char = unichr(ord(u'uE001') + j)
start = time.time()
char_bigrams = zip(*[text[i:] for i in range(n)])
bigram_time = time.time() - start
start = time.time()
most_freq_bigram = Counter(filter(lambda x: u"uE000" not in x and 'n' not in x,char_bigrams)).most_common(1)[0][0]
max_time = time.time() - start
start = time.time()
text = text.replace(''.join(most_freq_bigram),unused_char)
replace_time = time.time() - start
print j,''.join(most_freq_bigram),most_freq_bigram,bigram_time,max_time,replace_time
print text
这是在norvig.com/big.txt测试的 [OUT]: 1 th (u't',u'h') 0.896255016327 3.28389787674 0.0253069400787 2 e (u'ue002',u'e') 1.47053217888 3.16544914246 0.0280749797821 3 in (u'i',u'n') 1.13404297829 3.10529899597 0.0245559215546 4 an (u'a',u'n') 1.20013689995 3.63801002502 0.0242891311646 5 er (u'e',u'r') 1.41387891769 3.13376092911 0.0237591266632 6 on (u'o',u'n') 1.22826981544 3.06997895241 0.0227301120758 7 re (u'r',u'e') 1.21916294098 2.97599196434 0.0238041877747 8 at (u'a',u't') 1.14608097076 2.97988891602 0.0226521492004 9 en (u'e',u'n') 1.20747494698 2.88649988174 0.019054889679 10 ed (u'e',u'd') 1.16296696663 2.8995718956 0.0198271274567 11 is (u'i',u's') 1.17692494392 3.02292394638 0.0228500366211 12 d (u'ue005',u'd') 1.13779211044 2.85169506073 0.0229239463806 我已经尝试过scikit-learn CountVectorizer,我似乎没有使用zip那么快,参见Fast/Optimize N-gram implementations in python 另外,如果没有它们在Counter步骤中进行过滤操作,则需要更长的时间.计数器操作每次迭代需要3秒=( 如何优化此操作? Counter(filter(lambda x: u"uE000" not in x and 'n' not in x,char_bigrams)).most_common(1)[0][0] 解决方法如果你将字符串元组保持为长度2,你可以像这样使用reduce:def cons_2(word_list,t):
j = ''.join(t)
f = lambda acc,e: acc[:-1] + (j,) if (acc[-1] == t[0] and e == t[1]) else acc + (e,)
return [reduce(f,i[1:],(i[0],)) for i in word_list]
print cons_2(cin,'s'))
不涉及替换,f应用于每个元素i,cin的值不会改变,而是生成并返回新的数组. 细节: > reduce对每个数组元素i应用f并将值返回到累加器acc. > f:要应用的功能. > f:是一个lambda函数,累加器acc和当前元素e作为输入: >如果累加器的最后一个元素等于字符串元组的第一个元素,并且当前元素e等于字符串元组的第二个元素,则返回元组:acc [-1](j,)else继续正常连接:acc(e,). 对于字符串元组> 2这个想法是一样的,但我们必须管理元组的长度l. def cons_n(word_list,t):
l = len(t)
j = ''.join(t)
f = lambda acc,e: acc[:-l] + (j,e,) if acc[-l:] == t or acc[:l] == t else acc + (e,i[l:],(i[:l])) for i in word_list]
print cons_n(cin,'s'))
这应该适用于n长度的字符串元组. 细节: >与上面相同的过程,但使用l:reduce将f应用于其余元素i [l:],并且累加器的初始值是具有前l个元素的元组:(i [:l]). 这是一种功能方法,没有数据被修改但是生成,因此同时拥有多个进程应该是安全的(理论上,我不是Python解释器的专家). 如果上面的代码对于没有进入函数式编程的人来说太奇怪了,这是另一种方法: def cons_n_iter(tuple_list,tuple_seq):
jnt = ''.join(tuple_seq)
lnt = len(tuple_seq)
res = []
for word in tuple_list:
acc = (word[:lnt])
for letter in word[lnt:]:
if acc[-lnt:] == tuple_seq or acc[:lnt] == tuple_seq:
acc = acc[:-lnt] + (jnt,letter,)
else:
acc += (letter,)
res += (acc,)
return res
print cons_n_iter(cin,'s'))
逻辑与功能方法相同,累加器使用相同.在这种情况下,res累加器是显式的,因为在上面的例子中,reduce正在处理它. (编辑:鄂州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
