La différence de performance entre numpy et matlab
Je suis le calcul de la backpropagation
algorithme pour un clairsemée autoencoder. J'ai implémenté en python à l'aide de numpy
et dans matlab
. Le code est presque le même, mais les performances sont très différentes. Le temps matlab nécessaire pour terminer la tâche est 0.252454 secondes, tandis que numpy 0.973672151566, qui est près de quatre fois plus. Je vais appeler ce code plusieurs fois plus tard dans un problème de minimisation donc cette différence conduit à quelques minutes de retard entre les implémentations. Est-ce un comportement normal? Comment pourrais-je améliorer les performances dans numpy?
Numpy mise en œuvre:
Rares.rho est un paramètre d'optimisation, éparses.les nœuds sont au nombre de nœuds dans la couche cachée (25), un peu.d'entrée (64) le nombre de nœuds dans la couche d'entrée, theta1 et theta2 sont le poids des matrices pour la première et de la deuxième couche, respectivement, avec les dimensions 25x64 et 64x25, m est égal à 10000, rhoest a une dimension de (25,), x a une dimension de 10000x64, a3 10000x64 et a2 10000x25.
UPDATE
: J'ai introduit des changements dans le code ci-dessous quelques idées de réponses. La performance est maintenant numpy: 0.65 vs matlab: 0.25.
partial_j1 = np.zeros(sparse.theta1.shape)
partial_j2 = np.zeros(sparse.theta2.shape)
partial_b1 = np.zeros(sparse.b1.shape)
partial_b2 = np.zeros(sparse.b2.shape)
t = time.time()
delta3t = (-(x-a3)*a3*(1-a3)).T
for i in range(m):
delta3 = delta3t[:,i:(i+1)]
sum1 = np.dot(sparse.theta2.T,delta3)
delta2 = ( sum1 + sum2 ) * a2[i:(i+1),:].T* (1 - a2[i:(i+1),:].T)
partial_j1 += np.dot(delta2, a1[i:(i+1),:])
partial_j2 += np.dot(delta3, a2[i:(i+1),:])
partial_b1 += delta2
partial_b2 += delta3
print "Backprop time:", time.time() -t
Matlab mise en œuvre:
tic
for i = 1:m
delta3 = -(data(i,:)-a3(i,:)).*a3(i,:).*(1 - a3(i,:));
delta3 = delta3.';
sum1 = W2.'*delta3;
sum2 = beta*(-sparsityParam./rhoest + (1 - sparsityParam) ./ (1.0 - rhoest) );
delta2 = ( sum1 + sum2 ) .* a2(i,:).' .* (1 - a2(i,:).');
W1grad = W1grad + delta2* a1(i,:);
W2grad = W2grad + delta3* a2(i,:);
b1grad = b1grad + delta2;
b2grad = b2grad + delta3;
end
toc
- il y a un module appelé mlabwrap. Vous pouvez utiliser matlab comme une bibliothèque python par l'importation. La syntaxe est très simple. Vous trouverez la source et le détail de la documentation ici.mlabwrap.sourceforge.net
- Jetez un oeil à
cython
. La différence de temps est prévu, depuis MATLAB dispose d'une équipe commune d'enquête, et Disponible ne l'est pas. Si tout le code a été une seule numpy appel, puis le temps serait similaire, mais ce que vous voyez peut être interpréter les frais généraux. L'écriture d'une extension avec cython est vraiment facile et vous pouvez obtenir de gros gains de l'ajout de certains types de variables dans les bons endroits. - Quelle est la forme de
data
? Comment, concrètement, nem
comparer avec l'autre dimension? - m = 10000, x est un 10000x64 de la matrice, theta1 est un 25x64 de la matrice et theta2 64x25.
- Si vous ne pouvez pas travailler avec
x
l'ensemble de la matrice, il est préférable de boucle sur la petite dimension de la grande. Mais que peut exiger une certaine ingéniosité.
Vous devez vous connecter pour publier un commentaire.
Il serait faux de dire que "Matlab est toujours plus rapide que NumPy" ou vice
versa. Souvent, leurs performances sont comparables. Lors de l'utilisation de NumPy, pour obtenir de bons
la performance que vous devez garder à l'esprit que NumPy la vitesse vient d'appeler
sous-jacent de fonctions écrites en C/C++/Fortran. Il fonctionne bien lorsque vous appliquez
ces fonctions pour l'ensemble des tableaux. En général, vous obtenez la dégradation des performances lorsque vous appelez ces NumPy fonction sur les petits groupes ou les scalaires dans une boucle Python.
Quel est le problème avec une boucle Python vous demandez-vous? Chaque itération de la boucle Python est
un appel à une
next
méthode. Chaque utilisation de[]
l'indexation est un appel à une__getitem__
méthode. Chaque+=
est un appel à__iadd__
. Tous les pointillés de l'attributde recherche (par exemple comme dans le
np.dot
) implique des appels de fonction. Ces appels de fonctionajouter jusqu'à une importante hinderance à la vitesse. Ces crochets donner Python
la puissance expressive -- indexation pour les chaînes signifie quelque chose de différent que l'indexation
pour les dicts par exemple. Même syntaxe, des significations différentes. La magie est accompli en donnant les objets différents
__getitem__
méthodes.Mais que la puissance expressive arrive à un coût de vitesse. Ainsi, lorsque vous n'avez pas besoin de toutes les
cette dynamique de l'expressivité, pour obtenir de meilleures performances, essayez de vous limiter à
NumPy appels de fonction sur l'ensemble des tableaux.
Retirez la boucle for; utiliser "vectorisé" équations lorsque cela est possible. Par exemple, au lieu de
vous pouvez calculer
delta3
pour chaquei
tout à la fois:Alors que dans le
for-loop
delta3
est un vecteur, à l'aide de la vectorisé équationdelta3
est une matrice.Certains des calculs dans le
for-loop
ne dépendent pas dei
et, par conséquent, doit être soulevée à l'extérieur de la boucle. Par exemple,sum2
ressemble à une constante:Ici est un exécutable exemple avec une variante de mise en œuvre (
alt
) de votre code (orig
).Mon timeit de référence montre une 6.8 x amélioration de la vitesse:
Astuce: Avis que j'ai laissé dans les commentaires de la forme de tous les tableaux intermédiaires. Connaissant la forme de tableaux m'ont aidé à comprendre ce que votre code a été fait. La forme de tableaux peut aider à vous guider vers la droite NumPy les fonctions à utiliser. Ou au moins, de payer l'attention sur les formes peuvent vous aider à savoir si une opération est sensible. Par exemple, lorsque vous calculez
et
A.shape = (n, m)
etB.shape = (m, p)
, puisnp.dot(A, B)
sera un tableau de la forme(n, p)
.Il peut aider à construire les tableaux de C_CONTIGUOUS ordre (au moins, si l'utilisation de
np.dot
). Il pourrait être l'un de 3x la vitesse par le faire:Ci-dessous,
x
est le même quexf
sauf quex
est C_CONTIGUOUS etxf
est F_CONTIGUOUS -- et la même relation poury
etyf
.%timeit
benchmarks montrent la différence de vitesse:Concernant l'analyse comparative en Python:
Il peut être trompeur à utiliser la différence de paires de
time.time()
appels à l'indice de référence de la vitesse de code en Python.Vous devez répéter la mesure à de nombreuses reprises. Il est préférable de désactiver le système automatique de garbage collector. Il est également important de mesurer de grandes périodes de temps (au moins 10 secondes de répétitions) pour éviter des erreurs dues à la mauvaise résolution de l'horloge, de la minuterie et afin de réduire l'importance de
time.time
appel de surcharge. Au lieu d'écrire tout ce que le code vous-même, Python vous offre la module timeit. Je suis essentiellement en temps les morceaux de code, sauf que je suis en l'appelant par l'intermédiaire d'un IPython terminal pour des raisons de commodité.Je ne suis pas sûr si cela est touchant tes repères, mais il faut être conscient qu'il pourrait faire une différence. Dans le question j'ai relié à, selon
time.time
deux morceaux de code diffèrent d'un facteur de 1,7 x alors que les tests utilisant letimeit
a montré les morceaux de code a couru pour l'essentiel, les mêmes quantités de temps.delta3
avant lafor-loop
et prendresum2
hors aide (j'ai mis à jour la question), mais il est encore plus de deux fois plus lent que matlab. Ce qui m'impressionne, c'est que le temps qu'il faut pour matlab pour calculerdelta3
à l'intérieur de la boucle for est presque le même qu'il prend pour numpy pour accéder à une ligne d'un précalculées delta3 comme une matrice comme je l'ai maintenant. Est-ce toujoursnumpy
si lent par rapport àmatlab
?sum1
a des dimensions25,10000
toutsum2
est(25,)
sum2 = np.dot(sum2.reshape(-1,1),np.ones((1,sum1.shape[1])))
. Maintenant ça fonctionne, est-il une meilleure manière de le faire? merci beaucoup pour votre réponse.sum2 = sum2[:, np.newaxis]
pour convertirsum2
à partir d'un tableau de la forme (25) pour un tableau de la forme (25,1). NumPy radiodiffusion prendre soin de "mise à niveau" à la forme (25, 10000) sans consommer inutilement de la mémoire de répéter les mêmes valeurs 10000 fois.sum2[:, np.newaxis]
est d'environ 4300x plus vite quenp.dot(sum2.reshape(-1,1),np.ones((1,sum1.shape[1])))
sur mon ordinateur. Bien sûr, nous ne sommes qu'à le faire une fois de sorte que le total un gain de vitesse est négligeable. Pourtant, c'est un bon truc à savoir.NumPy
exécute le code dans0.125s
(grande amélioration). Pour faire une comparaison juste, j'ai recodé leMatlab
script pour éviter lafor-loop
alors maintenant, il ressemble à la nouvelle NumPy code. Matlab est encore plus rapide d'exécution dans0.02s
. Et dans Matlab pour somme2 je suis à l'aide d'un produit scalaire car je ne connais pas de truc comme celle que vous m'avez dit de Numpy.NumPy
à0.10s
en changeant la lignesum1 = (sparse.theta2.T, delta3)
à ` sum1 = scipy.linalg.blas.dgemm(alpha=1.0, un=sparse.theta2.T, b=delta3, trans_b=False)` parce que les deux matrices sont fortran Contigus. Pourrais-je optimiser encore plus la refonte de il de sorte que toutes les opérations (ou au moins la plupart d'entre eux) ont été entre le fortran contiguë matrices ou entre C contiguë matrices et ensuite appliquer la plus pratique du point de produits de l'opérateur ou de la improvemnet serait négligeable?np.dot
(et probablement pour de nombreuses autres fonctions de NumPy ainsi). J'ai ajouté un peu de code ci-dessus montrant quenp.dot(x, y)
est environ 3x plus rapide en utilisant C_CONTIGUOUS tableaux denp.dot(xf, yf)
à l'aide de F_CONTIGUOUS tableaux. Donc, si vous pouvez construire vos tableaux comme C_CONTIGUOUS depuis le début, vous pourriez voir un gain de performance.sum2 = np.random.random((p,1))
produit le(25,1)
tableau tout de suite.(25, )
. Il a besoin d'un moyen de le refaçonner à(25, 1)
.np.random.random((p, ))
est juste un tableau que j'ai fait pour servir comme un substitut pour son vrai tableau.Je voudrais commencer par en place des opérations pour éviter d'attribuer de nouveaux tableaux à chaque fois:
Vous pouvez remplacer cette expression:
avec une plus simple et plus rapide (grâce à Bi Rico):
Aussi, cette ligne:
semble être la même à chaque tour de boucle, vous n'avez pas besoin de le recalculer.
Et, probablement mineur d'optimisation, vous pouvez remplacer toutes les occurrences de
x[i,:]
avecx[i]
.Enfin, si vous pouvez vous permettre d'allouer les
m
fois plus de mémoire, vous pouvez suivre unutbu suggestion et de la vectorisation de la boucle:avec:
Et vous pouvez toujours utiliser Numba et le gain en vitesse de manière significative sans la vectorisation (et sans utiliser plus de mémoire).
a1[i,:].reshape(1,a1.shape[1])
peut-on écrit commea[i:i+1]
Différence de performance entre les numpy et matlab ont toujours frustré moi. Ils sont souvent dans la fin se résument à la sous-jacentes lapack bibliothèques. Autant que je sache, matlab utilise l'intégralité de l'atlas lapack comme un défaut, tandis que numpy utilise un lapack la lumière. Matlab estime que les gens ne se soucient de l'espace et en vrac, tandis que numpy estime des gens. Question similaire avec une bonne réponse.
for i = 1:m
eta3(i,:)
sont lents code drapeaux.