自1.5版以来,我在Matplotlib中遇到了多处理问题。字体随机跳转到原始位置。示例在这里:
重现此错误的简单示例如下:
import multiprocessing
import matplotlib.pyplot as plt
fig = plt.figure()
def plot(i):
fig = plt.gcf()
plt.plot([],[])
fig.savefig('%d.png' % i)
plot(0)
pool = multiprocessing.Pool(4)
pool.map(plot, range(10))
如果多处理和简单绘图的顺序相反
pool = multiprocessing.Pool(4)
plot(0)
pool.map(plot, range(10))
然后它可以工作,但这个解决方法对我来说是无用的。
谢谢。
答案 0 :(得分:2)
我最近在测试平行绘制大量图的方法时遇到了同样的问题。虽然我还没有找到使用多处理模块的解决方案,但我发现使用Parallel Python包(http://www.parallelpython.com/)时我没有看到相同的错误。在我的早期测试中,它似乎比多处理模块慢约50%,但仍然比串行绘图有显着的加速。对模块导入也有点挑剔,所以我最终希望找到一个使用多处理的解决方案,但是现在这是一个可以通过的解决方法(至少对我而言)。也就是说,我对并行处理很陌生,所以我在这里缺少两种方法的细微差别。
###############################################################################
import os
import sys
import time
#import numpy as np
import numpy # Importing with 'as' doesn't work with Parallel Python
#import matplotlib.pyplot as plt
import matplotlib.pyplot # Importing with 'as' doesn't work with Parallel Python
import pp
import multiprocessing as mp
###############################################################################
path1='./Test_PP'
path2='./Test_MP'
nplots=100
###############################################################################
def plotrandom(plotid,N,path):
numpy.random.seed() # Required for multiprocessing module but not Parallel Python...
x=numpy.random.randn(N)
y=x**2
matplotlib.pyplot.scatter(x,y)
matplotlib.pyplot.savefig(os.path.join(path,'test_%d.png'%(plotid)),dpi=150)
matplotlib.pyplot.close('all')
############################################################################## #
# Parallel Python implementation
tstart_1=time.time()
if not os.path.exists(path1):
os.makedirs(path1)
ppservers = ()
if len(sys.argv) > 1:
ncpus = int(sys.argv[1])
job_server = pp.Server(ncpus, ppservers=ppservers)
else:
job_server = pp.Server(ppservers=ppservers)
print "Starting Parallel Python v2 with", job_server.get_ncpus(), "workers"
jobs = [(input_i, job_server.submit(plotrandom,(input_i,10,path1),(),("numpy","matplotlib.pyplot"))) for input_i in range(nplots)]
for input_i, job in jobs:
job()
tend_1=time.time()
t1=tend_1-tstart_1
print 'Parallel Python = %0.5f sec'%(t1)
job_server.print_stats()
############################################################################## #
# Multiprocessing implementation
tstart_2=time.time()
if not os.path.exists(path2):
os.makedirs(path2)
if len(sys.argv) > 1:
ncpus = int(sys.argv[1])
else:
ncpus = mp.cpu_count()
print "Starting multiprocessing v2 with %d workers"%(ncpus)
pool = mp.Pool(processes=ncpus)
jobs = [pool.apply_async(plotrandom, args=(i,10,path2)) for i in range(nplots)]
results = [r.get() for r in jobs] # This line actually runs the jobs
pool.close()
pool.join()
tend_2=time.time()
t2=tend_2-tstart_2
print 'Multiprocessing = %0.5f sec'%(t2)
###############################################################################
答案 1 :(得分:1)
我找到了解决方案。麻烦的主要原因是在/matplotlib/backends/backend_agg.py中的字典_fontd中的字体缓存
因此,我通过在函数_get_agg_font中添加multiprocessing.current_process()。pid到名为 key 的哈希,为每个进程使用了不同的哈希。
如果有人知道更优雅的解决方案,不需要修改matplotlib文件,请告诉我。
答案 2 :(得分:1)
我遇到了同样的问题:缓存字体的同时使用会在文本图中(例如轴编号)产生故障。问题似乎是由lru缓存进行的字体缓存:
对我来说,升级到python 3.7可以解决它(显然支持分叉后的清理状态)。
在您的工作程序中运行以下命令也可能会有所帮助:
import matplotlib
matplotlib.font_manager._get_font.cache_clear()
答案 3 :(得分:0)
此处显示了我如何更改 backend_agg.py 中的功能 _get_agg_font :
from multiprocessing import current_process
def _get_agg_font(self, prop):
"""
Get the font for text instance t, cacheing for efficiency
"""
if __debug__: verbose.report('RendererAgg._get_agg_font',
'debug-annoying')
key = hash(prop)
key += current_process().pid
font = RendererAgg._fontd.get(key)
if font is None:
fname = findfont(prop)
#font = RendererAgg._fontd.get(fname)
if font is None:
font = FT2Font(
fname,
hinting_factor=rcParams['text.hinting_factor'])
RendererAgg._fontd[fname] = font
RendererAgg._fontd[key] = font
font.clear()
size = prop.get_size_in_points()
font.set_size(size, self.dpi)
return font
答案 4 :(得分:0)
解决方案是将您的matplotlib导入 放入传递给多处理的函数中。