【问题标题】:Parallelize a nested for loop in python for finding the max value在python中并行化嵌套for循环以查找最大值
【发布时间】:2015-03-23 17:54:28
【问题描述】:

我正在努力改善这段代码的执行时间。由于计算非常耗时,我认为最好的解决方案是并行化代码。 输出也可以存储在内存中,然后写入文件。

我对 Python 和并行性都是新手,所以我发现很难应用 herehere 解释的概念。我还发现了this 问题,但我无法弄清楚如何针对我的情况实施同样的问题。 我正在使用 Python 3.4 在 Windows 平台上工作。

for i in range(0, len(unique_words)):
    max_similarity = 0        
    max_similarity_word = ""
    for j in range(0, len(unique_words)):
        if not i == j:
            similarity = calculate_similarity(global_map[unique_words[i]], global_map[unique_words[j]])
            if similarity > max_similarity:
                 max_similarity = similarity
                 max_similarity_word = unique_words[j]
    file_co_occurring.write(
        unique_words[i] + "\t" + max_similarity_word + "\t" + str(max_similarity) + "\n")

如果您需要对代码进行解释:

  • unique_words 是单词列表(字符串)
  • global_map 是一个字典,其键是单词(global_map.keys() 包含与 unique_words 相同的元素),值是以下格式的字典:{word: value},其中单词是 @ 中值的子集987654330@
  • 对于每个单词,我会根据其在global_map 中的值查找最相似的单词。我不想将每个相似性都存储在内存中,因为地图已经占用了太多空间。
  • calculate_similarity 返回一个从 0 到 1 的值
  • 结果应该包含unique_words中每个单词最相似的单词(最相似的单词应该与单词本身不同,这就是我添加条件if not i == j的原因,但是如果我检查max_similarity 是否不同于 1)
  • 如果一个词的max_similarity为0,那么如果最相似的词是空字符串就可以了

【问题讨论】:

  • 我认为如果不将一些相似性值存储在内存中,您将无法并行运行该精确循环。你能描述一下代码的预期输出是什么吗?如果您正在寻找与集合中的 每个 单词最相似的单词,我认为不会这样做。
  • 是的,它应该为每个单词找到最相似的单词。它也应该循环内循环中的所有单词,还是错误在其他地方?另外,如果您有将结果存储在内存中的解决方案,请发布它,我会找到一种方法来腾出一些空间。
  • 我认为问题出在两个循环中。外部循环跳过最后一个单词,因此您不会找到最相似的单词。内部循环还需要查看所有单词。例如,除非我遗漏了某些内容,否则对于您当前的代码和单词列表 [a,b,c],其中 b 与 a 比与 c 更相似,您的输出将是:a 与 b 最相似(a 循环看到b),b 与 c 最相似(b 循环没有看到 a,所以它选择了 c)并且 c 没有输出。
  • 你是对的。我刚刚编辑了这个问题,更好地解释了预期的输出,现在我将再次编辑它,如你所指的那样解决这个问题。谢谢。
  • 不确定这是否适用于 Python 3,但这可能值得研究:stackoverflow.com/questions/2846653/…

标签: python parallel-processing multiprocessing python-multithreading python-multiprocessing


【解决方案1】:

这是一个适合您的解决方案。我最终更改了很多代码,所以请询问您是否有任何问题。

这远不是实现这一目标的唯一方法,尤其是这不是一种内存效率高的解决方案。

您需要将 max_workers 设置为适合您的值。通常你的机器中逻辑处理器的数量是一个很好的起点。

from concurrent.futures import ThreadPoolExecutor, Future
from itertools import permutations
from collections import namedtuple, defaultdict

Result = namedtuple('Result', ('value', 'word'))

def new_calculate_similarity(word1, word2):
    return Result(
        calculate_similarity(global_map[word1], global_map[word2]),
        word2)

with ThreadPoolExecutor(max_workers=4) as executer:
    futures = defaultdict(list)
    for word1, word2 in permutations(unique_words, r=2):
            futures[word1].append(
                executer.submit(new_calculate_similarity, word1, word2))

    for word in futures:
        # this will block until all calculations have completed for 'word'
        results = map(Future.result, futures[word])
        max_result = max(results, key=lambda r: r.value) 
        print(word, max_result.word, max_result.value, 
            sep='\t', 
            file=file_co_occurring)

以下是我使用的库的文档:

【讨论】:

  • 感谢您提供的解决方案。我测试了它的小输入,执行时间更糟。我读过,如果输入不是那么大,这可能是正常的,对吧?
  • @nicck,是的,小输入可能会更慢,因为设置线程池有一些开销,并且在线程之间移动数据也有开销。很难说对于更大的输入速度是否会提高。
  • @nicck,我忘记了 gil,这可能是这里问题的一部分。您可以尝试使用 ProcessPoolExecutor 而不是 ThreadPoolExecutor。不过,它可能有效,也可能无效。
  • 代码中有一个错误,但我真的不知道在哪里。它对完全不同的单词输出相似度为 1,错误肯定不在calculate_similarity 函数中,所以可能在给它的参数中。我尝试了一种不同的方法来解决这个问题,(我认为)使用更简单的循环来并行化,但我又被卡住了,所以here 是新问题。
  • @nicck,global_map 是在此开始之前设置的,还是在运行时更新?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多