Является ли numpy.argmax медленнее, чем MATLAB = max ()?

Я пишу байесовский классификатор для нормального распространения. У меня есть код в python и MATLAB, которые почти идентичны. Однако код MATLAB работает примерно на 50 раз быстрее, чем мой скрипт Python. Я новичок в Python, поэтому, возможно, я сделал что-то ужасное. Я предполагаю, что это где-то, где я петлю над набором данных.

Возможно, numpy.argmax () намного медленнее, чем [~, idx] = max ()? Проникновение через кадр данных происходит медленно? Плохое использование словарей (раньше я пробовал объект, и он был даже медленным)?

Любые советы приветствуются.

Код Python

import numpy as np import pandas as pd #import the data as a data frame train_df = pd.read_table('hw1_traindata.txt',header = None)#training train_df.columns = [1, 2] #rename column titles 

Данные здесь представляют собой 2 столбца (300 строк / образцов для обучения и 300000 для теста). Это параметры функции; mi и Si – образцы и ковариации.

 case3_p = {'w': [], 'w0': [], 'W': []} case3_p['w']={1:S1.I*m1,2:S2.I*m2,3:S3.I*m3} case3_p['w0']={1: -1.0/2.0*(m1.T*S1.I*m1)- 1.0/2.0*np.log(np.linalg.det(S1)), 2: -1.0/2.0*(m2.T*S2.I*m2)-1.0/2.0*np.log(np.linalg.det(S2)), 3: -1.0/2.0*(m3.T*S3.I*m3)-1.0/2.0*np.log(np.linalg.det(S3))} case3_p['W']={1: -1.0/2.0*S1.I, 2: -1.0/2.0*S2.I, 3: -1.0/2.0*S3.I} #W1=-1.0/2.0*S1.I #w1_3=S1.I*m1 #w01_3=-1.0/2.0*(m1.T*S1.I*m1)-1.0/2.0*np.log(np.linalg.det(S1)) def g3(x,W,w,w0): return xT*W*x+wT*x+w0 

Это классификатор / цикл

 train_df['case3'] = 0 for i in range(train_df.shape[0]): x = np.mat(train_df.loc[i,[1, 2]]).T#observation #case 3 vals = [g3(x,case3_p['W'][1],case3_p['w'][1],case3_p['w0'][1]), g3(x,case3_p['W'][2],case3_p['w'][2],case3_p['w0'][2]), g3(x,case3_p['W'][3],case3_p['w'][3],case3_p['w0'][3])] train_df.loc[i,'case3'] = np.argmax(vals) + 1 #add one to make it the class value 

Соответствующий код MATLAB

 train = load('hw1_traindata.txt'); 

Дискриминантные функции

 W1=-1/2*S1^-1;%there isn't one for the other cases w1_3=S1^-1*m1';%fix the transpose thing w10_3=-1/2*(m1*S1^-1*m1')-1/2*log(det(S1)); g1_3=@(x) x'*W1*x+w1_3'*x+w10_3'; W2=-1/2*S2^-1; w2_3=S2^-1*m2'; w20_3=-1/2*(m2*S2^-1*m2')-1/2*log(det(S2)); g2_3=@(x) x'*W2*x+w2_3'*x+w20_3'; W3=-1/2*S3^-1; w3_3=S3^-1*m3'; w30_3=-1/2*(m3*S3^-1*m3')-1/2*log(det(S3)); g3_3=@(x) x'*W3*x+w3_3'*x+w30_3'; 

Классификатор

 case3_class_tr = Inf(size(act_class_tr)); for i=1:length(train) x=train(i,:)';%current sample %case3 vals = [g1_3(x),g2_3(x),g3_3(x)];%compute discriminant function value [~, case3_class_tr(i)] = max(vals);%get location of max end 

В таких случаях лучше всего профилировать свой код. Сначала я создал некоторые макетные данные:

 import numpy as np import pandas as pd fname = 'hw1_traindata.txt' ar = np.random.rand(1000, 2) np.savetxt(fname, ar, delimiter='\t') m1, m2, m3 = [np.mat(ar).T for ar in np.random.rand(3, 2)] S1, S2, S3 = [np.mat(ar) for ar in np.random.rand(3, 2, 2)] 

Затем я завернул ваш код в функцию и профилировал lprun IP-типа lprun (line_profiler). Вот результаты:

 %lprun -f train train(fname, m1, S1, m2, S2, m3, S3) Timer unit: 5.59946e-07 s Total time: 4.77361 s File: <ipython-input-164-563f57dadab3> Function: train at line 1 Line # Hits Time Per Hit %Time Line Contents ===================================================== 1 def train(fname, m1, S1, m2, S2, m3, S3): 2 1 9868 9868.0 0.1 train_df = pd.read_table(fname ,header = None)#training 3 1 328 328.0 0.0 train_df.columns = [1, 2] #rename column titles 4 5 1 17 17.0 0.0 case3_p = {'w': [], 'w0': [], 'W': []} 6 1 877 877.0 0.0 case3_p['w']={1:S1.I*m1,2:S2.I*m2,3:S3.I*m3} 7 1 356 356.0 0.0 case3_p['w0']={1: -1.0/2.0*(m1.T*S1.I*m1)- 8 9 1 204 204.0 0.0 1.0/2.0*np.log(np.linalg.det(S1)), 10 1 498 498.0 0.0 2: -1.0/2.0*(m2.T*S2.I*m2)-1.0/2.0*np.log(np.linalg.det(S2)), 11 1 502 502.0 0.0 3: -1.0/2.0*(m3.T*S3.I*m3)-1.0/2.0*np.log(np.linalg.det(S3))} 12 1 235 235.0 0.0 case3_p['W']={1: -1.0/2.0*S1.I, 13 1 229 229.0 0.0 2: -1.0/2.0*S2.I, 14 1 230 230.0 0.0 3: -1.0/2.0*S3.I} 15 16 1 1818 1818.0 0.0 train_df['case3'] = 0 17 18 1001 17409 17.4 0.2 for i in range(train_df.shape[0]): 19 1000 4254511 4254.5 49.9 x = np.mat(train_df.loc[i,[1, 2]]).T#observation 20 21 #case 3 22 1000 298245 298.2 3.5 vals = [g3(x,case3_p['W'][1],case3_p['w'][1],case3_p['w0'][1]), 23 1000 269825 269.8 3.2 g3(x,case3_p['W'][2],case3_p['w'][2],case3_p['w0'][2]), 24 1000 274279 274.3 3.2 g3(x,case3_p['W'][3],case3_p['w'][3],case3_p['w0'][3])] 25 1000 3395654 3395.7 39.8 train_df.loc[i,'case3'] = np.argmax(vals) + 1 26 27 1 45 45.0 0.0 return train_df 

Есть две линии, которые вместе занимают 90% времени. Итак, давайте разделим эти строки немного и запустим профайлер:

 %lprun -f train train(fname, m1, S1, m2, S2, m3, S3) Timer unit: 5.59946e-07 s Total time: 6.15358 s File: <ipython-input-197-92d9866b57dc> Function: train at line 1 Line # Hits Time Per Hit %Time Line Contents ====================================================== ... 19 1000 5292988 5293.0 48.2 thing = train_df.loc[i,[1, 2]] # Observation 20 1000 265101 265.1 2.4 x = np.mat(thing).T ... 26 1000 143142 143.1 1.3 index = np.argmax(vals) + 1 # Add one to make it the class value 27 1000 4164122 4164.1 37.9 train_df.loc[i,'case3'] = index 

Большинство времени тратится на индексацию кадра данных Pandas! Взятие argmax составляет всего 1,5% от общего времени выполнения.

Ситуацию можно несколько улучшить, предварительно выделив train_df['case3'] и используя .iloc :

 %lprun -f train train(fname, m1, S1, m2, S2, m3, S3) Timer unit: 5.59946e-07 s Total time: 3.26716 s File: <ipython-input-192-f6173cdf9990> Function: train at line 1 Line # Hits Time Per Hit %Time Line Contents ======= ======= ====================================== 16 1 1548 1548.0 0.0 train_df['case3'] = np.zeros(len(train_df)) ... 19 1000 2608489 2608.5 44.7 thing = train_df.iloc[i,[0, 1]] # Observation 20 1000 228959 229.0 3.9 x = np.mat(thing).T ... 26 1000 123165 123.2 2.1 index = np.argmax(vals) + 1 # Add one to make it the class value 27 1000 1849283 1849.3 31.7 train_df.iloc[i,2] = index 

Тем не менее, повторение отдельных значений из блоков данных Pandas в плотных циклах – плохая идея. В этом случае используйте Pandas только для загрузки текстовых данных (это очень хорошо), но кроме этого используют «необработанные» массивы Numpy. Например, используйте train_data = pd.read_table(fname, header=None).values . И когда вы достигнете стадии анализа, возможно, вернитесь в Панды.

Некоторые другие промахи:

  • Используйте индексирование на основе нуля на основе Python и не пытайтесь использовать однонаправленную индексацию.
  • Подумайте об использовании обычных матриц Numpy вместо матриц. Когда вы используете матрицы, вы, как правило, смешиваете их с массивами и сталкиваетесь с трудностями для отладки проблем.
  • MATLAB имеет JIT-компилятор, поэтому ожидается разность скоростей между Python и MATLAB для тяжелого кода цикла.

Это очень сложно сказать, но прямо из пакета Matlab будет быстрее, чем Numpy. Прежде всего потому, что он поставляется с собственной библиотекой ядра математики

Является ли 50x разумным приближением, будет сложно сравнивать базовые модели Numpy vs Matlab's MKL.

Есть и другие дистрибутивы Python, которые поставляются со своими собственными MKL, такими как Enthought и Anaconda

На странице « Оптимизация MKL» в Анаконде вы увидите диаграмму, сравнивающую разницу между обычной Anaconda и MKL. Улучшение не линейно, но определенно есть.