Вызов PyTesseract работает очень медленно при использовании вместе с многопроцессорностью - PullRequest
0 голосов
/ 25 ноября 2018

У меня есть функция, которая берет список изображений и производит вывод в виде списка после применения OCR к изображению.У меня есть другая функция, которая управляет вводом этой функции, используя многопроцессорность.Итак, когда у меня есть один список (то есть нет многопроцессорной обработки), каждое изображение списка заняло ~ 1 с, но когда я увеличил списки, которые должны были обрабатываться параллельно, до 4, каждое изображение заняло поразительные 13 с.

Чтобы понять, где на самом деле проблема, я попытался создать минимальный рабочий пример проблемы.Здесь у меня есть две функции eat25 и eat100, которые открывают изображение name и передают его в OCR, который использует API pytesseract.eat25 делает это 25 раз, а eat100 делает это 100 раз.

Моя цель здесь - запустить eat100 без многопроцессорной обработки и eat25 с многопроцессорной обработкой (с 4 процессами).Теоретически, это должно занять в 4 раза меньше времени, чем eat100, если у меня 4 отдельных процессора (у меня 2 ядра с 2 потоками на ядро, поэтому CPU (s) = 4 (поправьте меня, если я ошибаюсь здесь)).

Но вся теория была потрачена впустую, когда я увидел, что код даже не отвечал после печати «Обработка 0» 4 раза.Хотя однопроцессорная функция eat100 работала нормально.

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

  • pytesseract: См. this
  • Неверный код?Что-то я не правильно делаю.

`

from pathos.multiprocessing import ProcessingPool
from time import time 
from PIL import Image
import pytesseract as pt
def eat25(name):
    for i in range(25):
        print('Processing :'+str(i))
        pt.image_to_string(Image.open(name),lang='hin+eng',config='--psm 6')
def eat100(name):
    for i in range(100):
        print('Processing :'+str(i))
        pt.image_to_string(Image.open(name),lang='hin+eng',config='--psm 6')
st = time()
eat100('normalBox.tiff')
en = time()
print('Direct :'+str(en-st))
#Using pathos
def caller():
    pool = ProcessingPool()
    pool.map(eat25,['normalBox.tiff','normalBox.tiff','normalBox.tiff','normalBox.tiff'])
if (__name__=='__main__'):
    caller()
en2 = time()

print('Pathos :'+str(en2-en))

Так в чем же проблема?Любая помощь приветствуется!

РЕДАКТИРОВАТЬ: изображение normalBox.tiff можно найти здесь .Я был бы рад, если бы люди воспроизвели код и проверили, сохраняется ли проблема.

1 Ответ

0 голосов
/ 30 ноября 2018

Я pathos автор.Если вашему коду для последовательного запуска требуется 1s, вполне возможно, что для параллельного запуска наивного процесса потребуется больше времени.Затраты на работу с параллельным наивным процессом сопряжены с издержками:

  1. новый экземпляр python должен быть запущен на каждом процессоре
  2. ваша функция и зависимости должны быть сериализованы и отправлены на каждый процессор
  3. ваши данные должны быть сериализованы и отправлены процессорам
  4. то же самое для десериализации
  5. вы можете столкнуться с проблемами памяти из долговременных пулов или большого количества сериализации данных.

Я бы посоветовал проверить несколько простых вещей, чтобы проверить, где могут быть ваши проблемы:

  • попробуйте pathos.pools.ThreadPool использовать параллельный поток вместо параллельного процесса.Это может уменьшить некоторые накладные расходы на сериализацию и ускорение пула.
  • попробуйте pathos.pools._ProcessPool, чтобы изменить способ управления pathos пулом.Без подчеркивания pathos сохраняет пул как одноэлементное и требует «завершения», чтобы явно уничтожить пул.С подчеркиванием, пул умирает, когда вы удаляете объект пула.Обратите внимание, что ваша caller функция не close или join (или terminate) в пуле.
  • вы можете проверить, сколько вы сериализуете, попытавшись dill.dumps один изэлементы, которые вы пытаетесь обрабатывать параллельно.Такие вещи, как большие массивы numpy, могут занять некоторое время для сериализации.Если размер передаваемого объекта велик, вы можете рассмотреть возможность использования массива совместно используемой памяти (т. Е. multiprocess.Array или эквивалентной версии для массивов numpy - также см. numpy.ctypeslib), чтобы минимизировать то, что передаетсямежду каждым процессом.

Последний - немного больше работы, но может обеспечить огромную экономию, если у вас есть много для сериализации.Нет общего пула памяти, поэтому вам нужно выполнить цикл for для отдельных multiprocess.Process объектов, если вам нужно пойти по этому пути.

...