Я использую joblib для повышения эффективности при простой задаче построения плотностей вероятности для дискретных данных. Короче говоря, меня озадачивает тот факт, что мое улучшение производительности насыщается двумя параллельными процессами, и ничего не получается благодаря тому, что у них больше. Мне также интересно узнать о других возможных подходах к оптимизации этой программы. Сначала я рассмотрю особенности проблемы в деталях.Параллелизация с joblib - Насыщенность производительности и общие соображения
Я рассматриваю двоичный массив X
формы (n_samples, n_features)
и вектор 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]))
Я приспособил функцию синхронизации, написанную самим Гвидо ван Россум, как представлено 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 ГГц 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-реализации функции, которая вычисляет общую вероятность, будет многое, но у меня нет опыта с ней.