【发布时间】:2017-01-05 08:37:51
【问题描述】:
我使用 joblib 是为了在为离散数据构建概率密度的简单任务上获得一些效率。简而言之,我对我的性能改进在 2 个并行进程中饱和这一事实感到困惑,而拥有更多进程却没有任何收获。我也对优化该程序的其他可能方法感到好奇。我将首先详细介绍问题的具体细节。
我考虑一个形状为(n_samples, n_features) 的二进制数组X 和一个分类标签的向量y。出于实验的目的,这样做可以:
import numpy as np
X = np.random.randint(0,2,size=[n_samples,n_features])
y = np.random.randint(0,10,size=[n_samples,])
函数joint_probability_binary 将特征数组X(单个特征)的列和标签向量y 作为输入,并输出它们的联合分布。没有什么花哨。
def joint_probability_binary(x, y):
labels = list(set(y))
joint = np.zeros([len(labels), 2])
for i in xrange(y.shape[0]):
joint[y[i], x[i]] += 1
return joint / float(y.shape[0])
现在,我想将joint_probability_binary 应用于X 的每个功能(每一列)。我的理解是,这个任务(给定足够大的n_samples 值)对于多处理并行性来说是粗粒度的。我编写了一个顺序和并行函数来执行此任务。
from joblib import Parallel, delayed
def joints_sequential(X, y):
return [joint_probability_binary(X[:,i],y) for i in range(X.shape[1])]
def joints_parallel(X, y, n_jobs):
return Parallel(n_jobs=n_jobs, verbose=0)(
delayed(joint_probability_binary)(X = X[:,i],y = y)
for i in range(X.shape[1]))
我改编了 Guido van Rossum 自己写的计时函数,呈现在here,如下:
import time
def timing(f, n, **kwargs):
r = range(n)
t1 = time.clock()
for i in r:
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
f(**kwargs);
t2 = time.clock()
return round(t2 - t1, 3)
最后,为了研究性能的变化及其对作业数量的依赖性,我运行了
tseq = timing(joints_sequential,10, X=X,y=y)
print('Sequential list comprehension - Finished in %s sec' %tseq)
for nj in range(1,9):
tpar = timing(joints_parallel,10, X=X, y=y, n_jobs=nj)
print('Parallel execution - %s jobs - Finished in %s sec' %(nj,tpar))
对于n_samples = 20000 和n_features = 20,我明白了
Sequential list comprehension - Finished in 60.778 sec
Parallel execution - 1 jobs - Finished in 61.975 sec
Parallel execution - 2 jobs - Finished in 6.446 sec
Parallel execution - 3 jobs - Finished in 7.516 sec
Parallel execution - 4 jobs - Finished in 8.275 sec
Parallel execution - 5 jobs - Finished in 8.953 sec
Parallel execution - 6 jobs - Finished in 9.962 sec
Parallel execution - 7 jobs - Finished in 10.382 sec
Parallel execution - 8 jobs - Finished in 11.321 sec
1.
此结果证实,并行化此任务可以获得相当多的收益(在 OS X 上运行,配备 2 GHz Intel Core i7 和 4 核)。
然而,我发现最引人注目的是n_jobs = 2 的性能已经饱和。考虑到每个任务的大小,我很难认为这可能是由 Joblib 开销单独引起的,但是我的直觉又是有限的。我用更大的数组n_samples = 200000 和n_features = 40 重复了这个实验,这导致了相同的行为:
顺序列表理解 - 在 1230.172 秒内完成
Parallel execution - 1 jobs - Finished in 1198.981 sec
Parallel execution - 2 jobs - Finished in 94.624 sec
Parallel execution - 3 jobs - Finished in 95.1 sec
...
是否有人对为什么会出现这种情况有直觉(鉴于我的整体方法足够合理)?
2.
最后,在整体优化方面,还有哪些其他方法可以提高此类程序的性能?我怀疑编写计算联合概率的函数的 Cython 实现会收获很多,但我没有这方面的经验。
【问题讨论】:
标签: python optimization parallel-processing multiprocessing joblib