2

Я пытаюсь в течение некоторого времени улучшить время выполнения этой части кода. Поскольку вычисления действительно трудоемкие, я считаю, что лучшим решением будет распараллеливание кода. Вывод также может быть сохранен в памяти и впоследствии записан в файл.Распараллеливать цикл вложенного цикла в python для нахождения максимального значения

Я новичок как в Python, так и в параллелизме, поэтому мне трудно применять понятые объяснения here и here. Я также нашел this вопрос, но мне не удавалось выяснить, как реализовать то же самое для моей ситуации. Я работаю на платформе Windows, используя Python 3.4.

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), а значения - словари следующего формата: {слово: значение}, где слова являются подмножеством значений в unique_words
  • для каждого слова, I loo k для наиболее похожего слова, основанного на его значении в global_map. Я бы не предпочел хранить каждое сходство в памяти, так как карты уже слишком много.
  • calculate_similarity возвращает значение от 0 до 1
  • результат должен содержать наиболее похожий слово для каждого из слов в unique_words (самый подобное слово должно быть иным, чем само слово, поэтому я добавил условие if not i == j, но это также может быть сделано, если я проверю, если max_similarity отличается от 1)
  • если max_similarity для слова 0, это нормально, если самые подобное слово является пустой строкой
+0

Я не думаю, что вы можете запустить этот точный контур параллельно без сохранения некоторых значений сходства в памяти. Можете ли вы описать, каков ожидаемый результат кода? Если вы ищете наиболее похожее слово для * каждого * слова в наборе, я не думаю, что это так. – Matt

+0

Да, это должно найти самое похожее слово для каждого слова. Должна ли она зацикливать все слова во внутреннем цикле, или ошибка находится где-то в другом месте? Также, если у вас есть решение, в котором хранятся результаты в памяти, отправьте его, и я найду способ сделать некоторое пространство. – van

+2

Я думаю, проблема в обоих циклах. Внешний цикл пропускает последнее слово, поэтому вы не найдете наиболее подходящего для него слова. Внутренний цикл также должен был бы видеть все слова. Например, если я не пропустил что-то, с вашим текущим кодом и списком слов [a, b, c], где b больше похоже на a, чем на c, ваш результат будет: a больше всего похож на b (цикл видит b), b больше всего похож на c (петля b не видит a, поэтому выбирает c вместо), и не будет выхода для c. – Matt

ответ

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) 

Вот документы для библиотек, которые я использовал:

+0

Спасибо за предлагаемое решение. Я протестировал его для небольшого ввода, а время выполнения было хуже. Я читал, что это может быть нормально, если вход не такой большой, не так ли? – van

+0

@nicck, да, это может быть медленнее для небольших входов, поскольку есть некоторые накладные расходы, чтобы настроить пул потоков и также накладные расходы для перемещения данных между потоками. Его трудно сказать, будет ли скорость улучшаться для больших входов. – Matt

+0

@nicck, я забыл про gil, что может быть частью проблемы здесь. Вы можете попробовать ProcessPoolExecutor вместо ThreadPoolExecutor. Это может работать или не работать. – Matt