NLTK 分词优化
NLTK Tokeninizing Optimization
我有一个 NLTK
解析函数,用于解析 TREC 数据集的 ~2GB 文本文件。此数据集的目标是标记整个集合,执行一些计算(例如计算 TF-IDF 权重等),然后 运行 对我们的集合进行一些查询以使用余弦相似度和 return最好的结果。
就目前而言,我的程序可以运行,但需要一个多小时(通常在 44-61 分钟之间)才能完成 运行。时间细分如下:
TOTAL TIME TO COMPLETE: 4487.930628299713
TIME TO GRAB SORTED COSINE SIMS: 35.24157094955444
TIME TO CREATE TFIDF BY DOC: 57.06743311882019
TIME TO CREATE IDF LOOKUP: 0.5097501277923584
TIME TO CREATE INVERTED INDEX: 2.5217013359069824
TIME TO TOKENIZE: 4392.5711488723755
很明显,令牌化占了大约 98% 的时间。我正在寻找加快速度的方法。
标记化代码如下:
def remove_nums(arr):
pattern = '[0-9]'
arr = [re.sub(pattern, '', i) for i in arr]
return arr
def get_words(para):
stop_words = list(stopwords.words('english'))
words = RegexpTokenizer(r'\w+')
lower = [word.lower() for word in words.tokenize(para)]
nopunctuation = [nopunc.translate(str.maketrans('', '', string.punctuation)) for nopunc in lower]
no_integers = remove_nums(nopunctuation)
dirty_tokens = [data for data in no_integers if data not in stop_words]
tokens = [data for data in dirty_tokens if data.strip()]
def driver(file):
myfile = get_input(file)
p = r'<P ID=\d+>.*?</P>'
paras = RegexpTokenizer(p)
document_frequency = collections.Counter()
collection_frequency = collections.Counter()
all_lists = []
currWordCount = 0
currList = []
currDocList = []
all_doc_lists = []
num_paragraphs = len(paras.tokenize(myfile))
print()
print(" NOW BEGINNING TOKENIZATION ")
print()
for para in paras.tokenize(myfile):
group_para_id = re.match("<P ID=(\d+)>", para)
para_id = group_para_id.group(1)
tokens = get_words(para)
tokens = list(set(tokens))
collection_frequency.update(tokens)
document_frequency.update(set(tokens))
para = para.translate(str.maketrans('', '', string.punctuation))
currPara = para.lower().split()
for token in tokens:
currWordCount = currPara.count(token)
currList = [token, tuple([para_id, currWordCount])]
all_lists.append(currList)
currDocList = [para_id, tuple([token, currWordCount])]
all_doc_lists.append(currDocList)
d = {}
termfreq_by_doc = {}
for key, new_value in all_lists:
values = d.setdefault(key, [])
values.append(new_value)
for key, new_value in all_doc_lists:
values = termfreq_by_doc.setdefault(key, [])
values.append(new_value)
我对优化还很陌生,正在寻找一些反馈。我确实看到 this post 谴责我的很多列表理解为 "evil",但我想不出解决我正在做的事情的方法。
代码没有很好的注释,所以如果由于某种原因它无法理解,那没关系。我在这个论坛上看到其他问题:在没有很多反馈的情况下加速 NLTK
标记化,所以我希望有一个关于标记化优化编程实践的积极话题。
来自:
https://codereview.stackexchange.com/users/25834/reinderien
开启:https://codereview.stackexchange.com/questions/230393/tokenizing-sgml-text-for-nltk-analysis
正则表达式编译
如果性能是一个问题,这:
arr = [re.sub(pattern, '', i) for i in arr]
是个问题。您要在每次函数调用和每次循环迭代时重新编译您的正则表达式!相反,将正则表达式移动到函数外部的 re.compile()
d 符号。
同样适用于re.match("<P ID=(\d+)>", para)
。换句话说,你应该发布类似
group_para_re = re.compile(r"<P ID=(\d+)>")
循环外,然后
group_para_id = group_para_re.match(para)
循环内部。
过早的生成器实现
同一行还有另一个问题 - 您将 return 值强制为列表。查看您的 no_integers
用法,您只需再次迭代它,因此将整个结果保存在内存中没有任何价值。相反,将其保留为生成器 - 用圆括号替换括号。
同样适用于nopunctuation
。
设置会员资格
stop_words
不应该是 list
- 它应该是 set
。了解其性能 here。查找是平均 O(1),而不是列表的 O(n)。
变量名
nopunctuation
应该是 no_punctuation
.
我有一个 NLTK
解析函数,用于解析 TREC 数据集的 ~2GB 文本文件。此数据集的目标是标记整个集合,执行一些计算(例如计算 TF-IDF 权重等),然后 运行 对我们的集合进行一些查询以使用余弦相似度和 return最好的结果。
就目前而言,我的程序可以运行,但需要一个多小时(通常在 44-61 分钟之间)才能完成 运行。时间细分如下:
TOTAL TIME TO COMPLETE: 4487.930628299713
TIME TO GRAB SORTED COSINE SIMS: 35.24157094955444
TIME TO CREATE TFIDF BY DOC: 57.06743311882019
TIME TO CREATE IDF LOOKUP: 0.5097501277923584
TIME TO CREATE INVERTED INDEX: 2.5217013359069824
TIME TO TOKENIZE: 4392.5711488723755
很明显,令牌化占了大约 98% 的时间。我正在寻找加快速度的方法。
标记化代码如下:
def remove_nums(arr):
pattern = '[0-9]'
arr = [re.sub(pattern, '', i) for i in arr]
return arr
def get_words(para):
stop_words = list(stopwords.words('english'))
words = RegexpTokenizer(r'\w+')
lower = [word.lower() for word in words.tokenize(para)]
nopunctuation = [nopunc.translate(str.maketrans('', '', string.punctuation)) for nopunc in lower]
no_integers = remove_nums(nopunctuation)
dirty_tokens = [data for data in no_integers if data not in stop_words]
tokens = [data for data in dirty_tokens if data.strip()]
def driver(file):
myfile = get_input(file)
p = r'<P ID=\d+>.*?</P>'
paras = RegexpTokenizer(p)
document_frequency = collections.Counter()
collection_frequency = collections.Counter()
all_lists = []
currWordCount = 0
currList = []
currDocList = []
all_doc_lists = []
num_paragraphs = len(paras.tokenize(myfile))
print()
print(" NOW BEGINNING TOKENIZATION ")
print()
for para in paras.tokenize(myfile):
group_para_id = re.match("<P ID=(\d+)>", para)
para_id = group_para_id.group(1)
tokens = get_words(para)
tokens = list(set(tokens))
collection_frequency.update(tokens)
document_frequency.update(set(tokens))
para = para.translate(str.maketrans('', '', string.punctuation))
currPara = para.lower().split()
for token in tokens:
currWordCount = currPara.count(token)
currList = [token, tuple([para_id, currWordCount])]
all_lists.append(currList)
currDocList = [para_id, tuple([token, currWordCount])]
all_doc_lists.append(currDocList)
d = {}
termfreq_by_doc = {}
for key, new_value in all_lists:
values = d.setdefault(key, [])
values.append(new_value)
for key, new_value in all_doc_lists:
values = termfreq_by_doc.setdefault(key, [])
values.append(new_value)
我对优化还很陌生,正在寻找一些反馈。我确实看到 this post 谴责我的很多列表理解为 "evil",但我想不出解决我正在做的事情的方法。
代码没有很好的注释,所以如果由于某种原因它无法理解,那没关系。我在这个论坛上看到其他问题:在没有很多反馈的情况下加速 NLTK
标记化,所以我希望有一个关于标记化优化编程实践的积极话题。
来自: https://codereview.stackexchange.com/users/25834/reinderien
开启:https://codereview.stackexchange.com/questions/230393/tokenizing-sgml-text-for-nltk-analysis
正则表达式编译
如果性能是一个问题,这:
arr = [re.sub(pattern, '', i) for i in arr]
是个问题。您要在每次函数调用和每次循环迭代时重新编译您的正则表达式!相反,将正则表达式移动到函数外部的 re.compile()
d 符号。
同样适用于re.match("<P ID=(\d+)>", para)
。换句话说,你应该发布类似
group_para_re = re.compile(r"<P ID=(\d+)>")
循环外,然后
group_para_id = group_para_re.match(para)
循环内部。
过早的生成器实现
同一行还有另一个问题 - 您将 return 值强制为列表。查看您的 no_integers
用法,您只需再次迭代它,因此将整个结果保存在内存中没有任何价值。相反,将其保留为生成器 - 用圆括号替换括号。
同样适用于nopunctuation
。
设置会员资格
stop_words
不应该是 list
- 它应该是 set
。了解其性能 here。查找是平均 O(1),而不是列表的 O(n)。
变量名
nopunctuation
应该是 no_punctuation
.