2015-09-07 1 views
1

La question est plus axée sur la performance du calcul.Performance de 2 produit matriciel vectoriel

J'ai 2 vecteur-matrice. Cela signifie qu'ils ont une dimension de profondeur de 3 pour X, Y, Z. Chaque élément de la matrice doit faire un produit scalaire avec l'élément sur la même position de l'autre matrice.

Un simple et non du code efficace sera celui-ci:

import numpy as np 
a = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3)) 
b = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3)) 
c = np.zeros((1000,1000)) 
numRow,numCol,numDepth = np.shape(a) 

for idRow in range(numRow): 
    for idCol in range(numCol): 
     # Angle in radians 
     c[idRow,idCol] = math.acos(a[idRow,idCol,0]*b[idRow,idCol,0] + a[idRow,idCol,1]*b[idRow,idCol,1] + a[idRow,idCol,2]*b[idRow,idCol,2]) 

Cependant, les fonctions de numpy peuvent accélérer les calculs comme les suivants, le code rend beaucoup plus rapide:

# Angle in radians 
d = np.arccos(np.multiply(a[:,:,0],b[:,:,0]) + np.multiply(a[:,:,1],b[:,:,1]) + np.multiply(a[:,:,2],b[:,:,2])) 

Cependant , Je voudrais savoir s'il y a d'autres sintaxis qui améliorent celui-ci avec peut-être d'autres fonctions, indices, ...

Le premier code prend 4.658s tandis que le second prend 0.354s

Répondre

5

Vous pouvez le faire avec np.einsum, qui multiplie et résume ensuite sur les axes:

np.arccos(np.einsum('ijk,ijk->ij', a, b)) 

La façon de faire plus simple ce que vous avez publié dans la question est d'utiliser np.sum, où vous additionnez le long du dernier axe (-1):

np.arccos(np.sum(a*b, -1)) 

Ils donnent la même réponse, mais einsum est le plus rapide et sum est suivante:

In [36]: timeit np.arccos(np.einsum('ijk,ijk->ij', a, b)) 
10000 loops, best of 3: 20.4 µs per loop 

In [37]: timeit e = np.arccos(np.sum(a*b, -1)) 
10000 loops, best of 3: 29.8 µs per loop 

In [38]: %%timeit 
    ....: d = np.arccos(np.multiply(a[:,:,0],b[:,:,0]) + 
    ....:    np.multiply(a[:,:,1],b[:,:,1]) + 
    ....:    np.multiply(a[:,:,2],b[:,:,2])) 
    ....: 
10000 loops, best of 3: 34.6 µs per loop 
+0

@ askewchan bonne réponse. Cependant je ne vois pas tellement de différence entre 3 options. Il est vrai que einsum donne aussi la meilleure solution dans mon cas mais seulement une amélioration de 5%. Peut-être que c'est parce que précédemment je calcule les vecteurs de cette façon? '' 'a [:,:, 0] = np.sin (azimA) * np.cos (elevA) a [:,:, 1] = -np.cos (azimA) * np.cos (elevA) a [:,:, 2] = np.sin (elevA) '' 'à partir d'un tableau de taille = (1000,1000)? – iblasi

+0

Hmm, j'ai réduit la taille en fait pour mes timings ... probablement les différences ne sont pas trop grandes. Vous avez vraiment économisé la plupart de vos économies en passant de votre boucle à la solution de multiplication, mais la façon standard de le faire est ce que j'ai montré. – askewchan

+0

Vous pouvez gagner du temps dans ce que vous avez collé dans votre commentaire en calculant seulement 'cos' et' sin' une fois par tableau: 'cosazimA = np.cos (azimA)' etc, puisque vous calculez deux fois la même chose. Mais cela ne devrait pas avoir d'effet sur le calcul ultérieur. – askewchan

0

Le compilateur pythran peut encore optimiser votre expression originale:

  • suppression de tableaux temporaires
  • en suivant les instructions SIMD
  • en utilisant le multithreading

Comme présenté par cet exemple:

$ cat cross.py 
#pythran export cross(float[][][], float[][][]) 
import numpy as np 
def cross(a,b): 
    return np.arccos(np.multiply(a[:,:,0],b[:,:,0]) + np.multiply(a[:,:,1],b[:,:,1]) + np.multiply(a[:,:,2],b[:,:,2])) 
$ python -m timeit -s 'import numpy as np;a = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3));b = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3)); c = np.zeros((1000,1000)); from cross import cross' 'cross(a,b)' 
10 loops, best of 3: 35.4 msec per loop 
$ pythran cross.py -DUSE_BOOST_SIMD -fopenmp -march=native 
$ python -m timeit -s 'import numpy as np;a = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3));b = np.random.uniform(low=-1.0, high=1.0, size=(1000,1000,3)); c = np.zeros((1000,1000)); from cross import cross' 'cross(a,b)' 
100 loops, best of 3: 11.8 msec per loop