Skip to main content
 首页 » 编程设计

python之Word Toptimizer

2025年05月04日21insus

for s_index, s in enumerate(sentences): 
        s_tokens = s.split() 
        if (local_q_set.intersection(set(s_tokens)) == local_q_set): 
            q_results.append(s_index) 

上面的代码片段是我用来在包含查询中所有标记的海量文本数据中查找相关句子的核心算法。例如,对于查询“happy apple”,它会找到恰好包含一个或多个所有给定标记(即“happy”和“apple”)的所有句子。我的方法很简单:找到共同的相交集,看看它们是否匹配。但是,我没有获得足够的性能。如果有人看到针对此类问题的优化,我将非常感谢该想法的任何方向或链接 - 提前感谢您的时间

请您参考如下方法:

您可以采取一些措施来提高顺序搜索的性能,但真正的提升来自索引 token 。

设置差异

使用 not local_q_set.difference(s_tokens) 而不是将交集与原始集进行比较可能会更快一些。

正则表达式过滤器

如果你的句子很长,使用正则表达式可以通过在对照标记集检查它们之前将潜在标记从句子中分离出来来提高速度:

import re 
tokens     = re.compile("|".join(local_q_set)) 
tokenCount = len(local_q_set) 
for s_index, s in enumerate(sentences): 
    s_tokens = tokens.findall(s) 
    if len(s_tokens) < tokenCount or local_q_set.difference(s.split()): 
       continue 
    q_results.append(s_index)  

使用 in 运算符过滤

您还可以使用简单的 in 运算符来检查标记的存在,而不是使用正则表达式(当查询中的标记很少时,这应该会更快):

result = [] 
tokenSet = set(queryTokens) 
for index, sentence in enumerate(sentences): 
     if any( token not in sentence for token in queryTokens) \ 
     or tokenSet.difference(sentence.split()): 
         continue 
     result.append(index) 

缓存句子词集

为了改进对同一个句子列表进行多个查询时的顺序搜索,您可以构建与句子对应的词集的缓存。这将消除在遍历句子以找到匹配项时解析句子的工作。

cachedWords = [] 
 
queryTokens = ["happy","apple"] 
 
queryTokenSet = set(queryTokens) 
if not cachedWords: 
    cachedWords = [ set(sentence.split()) for sentence in sentences ] 
result = [ index for index,words in enumerate(cachedWords) if not queryTokenSet.difference(words) ] 

token 索引

如果您要对同一个句子列表执行许多查询,那么在标记和句子索引之间创建映射会更有效。您可以使用字典来做到这一点,然后通过与查询标记的句子索引相交来直接获得查询结果:

tokenIndexes = dict() 
for index,sentence in enumerate(sentences): 
    for token in sentence.lower().split(): 
        tokenIndexes.setdefault(token,[]).append(index) 
 
def tokenSet(token): return set(tokenIndexes.get(token,[])) 
 
queryTokens = ["happy","apple"] 
 
from functools import reduce 
result = reduce(set.intersection , (tokenSet(token) for token in queryTokens) ) 

这将使您能够使用集合运算符经济地实现复杂查询。例如:

import re 
 
querySring = " happy & ( apple | orange | banana ) " 
result = eval(re.sub("(\w+)",r"tokenSet('\1')", querySring))  
 
# re.sub(...) transforms the query string into " tokenSet('happy') & ( tokenSet('apple') | tokenSet('orange') | tokenSet('banana') ) " 

性能测试:

我做了一些性能测试(在 80,000 个句子中找到两个标记):

original algorithm: 105 ms           1x 
set.difference:      88 ms         1.2x 
regular expression:  60 ms         1.8x 
"in" operator:       43 ms         2.4x 
caching word sets:   23 ms         4.6x (excluding 187ms to build cache) 
token indexing:       0.0075 ms  14000x (excluding 238ms to build tokenIndexes) 

因此,如果您要对同一个句子执行多个查询,使用标记索引,一旦构建了 tokenIndexes 字典,您将获得 14,000 倍的响应速度。