当与多处理一起使用时,PyTesseract 调用工作非常慢
PyTesseract call working very slow when used along with multiprocessing
我有一个函数,它接收图像列表并在对图像应用 OCR 后在列表中生成输出。我有另一个函数通过使用多处理来控制此函数的输入。因此,当我有一个列表(即没有多处理)时,列表中的每个图像花费了大约 1 秒,但是当我将必须并行处理的列表增加到 4 个时,每个图像花费了惊人的 13 秒。
为了了解问题的真正所在,我尝试创建一个问题的最小工作示例。这里我有两个函数 eat25
和 eat100
,它们打开图像 name
并将其提供给使用 API pytesseract
的 OCR。 eat25
做了 25 次,eat100
做了 100 次。
我的目标是 运行 eat100
没有多处理, eat25
有多处理(有 4 个进程)。从理论上讲,如果我有 4 个独立的处理器(我有 2 个内核,每个内核有 2 个线程,因此 CPU(s) = 4(如果我'我在这里错了))。
但是当我看到代码在打印 "Processing 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
可以找到 here。如果人们重现代码并检查问题是否仍然存在,我会很高兴。
我是pathos
作者。如果您的代码连续使用 1s
到 运行,那么很可能在天真的并行过程中需要更长的时间才能达到 运行。使用天真的进程并行会产生开销:
- 必须在每个处理器上启动一个新的 python 实例
- 您的函数和依赖项需要序列化并发送到每个处理器
- 您的数据需要序列化并发送给处理器
- 反序列化相同
- 您可以 运行 从长期池或大量数据序列化中解决内存问题。
我建议检查一些简单的事情来检查您的问题可能出在哪里:
- 尝试
pathos.pools.ThreadPool
使用线程并行而不是进程并行。这可以减少序列化和启动池的一些开销。
- 尝试
pathos.pools._ProcessPool
更改 pathos
管理池的方式。如果没有下划线,pathos
将池保持为单例,并且需要 'terminate' 来显式终止池。带有下划线的是,当您删除池对象时,池将消失。请注意,您的 caller
函数不会 close
或 join
(或 terminate
)池。
- 您可能想通过尝试
dill.dumps
您尝试并行处理的元素之一来检查您正在序列化多少。像大 numpy
数组这样的东西可能需要一段时间才能序列化。如果传递的内容很大,您可以考虑使用共享内存数组(即 multiprocess.Array
或 numpy
数组的等效版本——另请参阅:numpy.ctypeslib
)最小化每个进程之间传递的内容。
后者需要做更多的工作,但如果您有很多要序列化的内容,则可以节省大量资金。没有共享内存池,所以如果你需要走那条路,你必须对单个 multiprocess.Process
对象进行 for 循环。
我有一个函数,它接收图像列表并在对图像应用 OCR 后在列表中生成输出。我有另一个函数通过使用多处理来控制此函数的输入。因此,当我有一个列表(即没有多处理)时,列表中的每个图像花费了大约 1 秒,但是当我将必须并行处理的列表增加到 4 个时,每个图像花费了惊人的 13 秒。
为了了解问题的真正所在,我尝试创建一个问题的最小工作示例。这里我有两个函数 eat25
和 eat100
,它们打开图像 name
并将其提供给使用 API pytesseract
的 OCR。 eat25
做了 25 次,eat100
做了 100 次。
我的目标是 运行 eat100
没有多处理, eat25
有多处理(有 4 个进程)。从理论上讲,如果我有 4 个独立的处理器(我有 2 个内核,每个内核有 2 个线程,因此 CPU(s) = 4(如果我'我在这里错了))。
但是当我看到代码在打印 "Processing 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
可以找到 here。如果人们重现代码并检查问题是否仍然存在,我会很高兴。
我是pathos
作者。如果您的代码连续使用 1s
到 运行,那么很可能在天真的并行过程中需要更长的时间才能达到 运行。使用天真的进程并行会产生开销:
- 必须在每个处理器上启动一个新的 python 实例
- 您的函数和依赖项需要序列化并发送到每个处理器
- 您的数据需要序列化并发送给处理器
- 反序列化相同
- 您可以 运行 从长期池或大量数据序列化中解决内存问题。
我建议检查一些简单的事情来检查您的问题可能出在哪里:
- 尝试
pathos.pools.ThreadPool
使用线程并行而不是进程并行。这可以减少序列化和启动池的一些开销。 - 尝试
pathos.pools._ProcessPool
更改pathos
管理池的方式。如果没有下划线,pathos
将池保持为单例,并且需要 'terminate' 来显式终止池。带有下划线的是,当您删除池对象时,池将消失。请注意,您的caller
函数不会close
或join
(或terminate
)池。 - 您可能想通过尝试
dill.dumps
您尝试并行处理的元素之一来检查您正在序列化多少。像大numpy
数组这样的东西可能需要一段时间才能序列化。如果传递的内容很大,您可以考虑使用共享内存数组(即multiprocess.Array
或numpy
数组的等效版本——另请参阅:numpy.ctypeslib
)最小化每个进程之间传递的内容。
后者需要做更多的工作,但如果您有很多要序列化的内容,则可以节省大量资金。没有共享内存池,所以如果你需要走那条路,你必须对单个 multiprocess.Process
对象进行 for 循环。