web-dev-qa-db-fra.com

Différence entre numpy dot () et Python 3.5 + multiplication matricielle @

Je me suis récemment déplacé vers Python 3.5 et j'ai remarqué que le nouvel opérateur de multiplication de matrice (@) se comporte parfois différemment de l'opérateur point numpy . Par exemple, pour les tableaux 3D:

import numpy as np

a = np.random.Rand(8,13,13)
b = np.random.Rand(8,13,13)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

L'opérateur @ renvoie un tableau de forme:

c.shape
(8, 13, 13)

pendant que la fonction np.dot() retourne:

d.shape
(8, 13, 8, 13)

Comment puis-je reproduire le même résultat avec numpy dot? Y a-t-il d'autres différences significatives?

88
blaz

L'opérateur _@_ appelle la méthode ___matmul___ du tableau, et non dot. Cette méthode est également présente dans l'API en tant que fonction np.matmul .

_>>> a = np.random.Rand(8,13,13)
>>> b = np.random.Rand(8,13,13)
>>> np.matmul(a, b).shape
(8, 13, 13)
_

De la documentation:

matmul diffère de dot de deux manières importantes.

  • La multiplication par scalaires n'est pas autorisée.
  • Les piles de matrices sont diffusées ensemble comme si les matrices étaient des éléments.

Le dernier point indique clairement que les méthodes dot et matmul se comportent différemment lorsqu'elles transmettent des tableaux 3D (ou de dimension supérieure). Citant de la documentation un peu plus:

Pour matmul:

Si l'un des arguments est N-D, N> 2, il est traité comme une pile de matrices résidant dans les deux derniers index et diffusé en conséquence.

Pour np.dot :

Pour les matrices bidimensionnelles, cela équivaut à la multiplication matricielle, et pour les matrices bidimensionnelles au produit interne des vecteurs (sans conjugaison complexe). Pour N dimensions, il s’agit d’une somme du produit sur le dernier axe de a et l’avant-dernier de b

105
Alex Riley

La réponse de @ajcr explique la différence entre dot et matmul (invoqué par le symbole @). En regardant un exemple simple, on voit clairement comment les deux se comportent différemment lorsqu'ils travaillent sur des piles de matrices ou de tenseurs.

Pour clarifier les différences, prenez un tableau 4x4 et renvoyez le produit dot et le produit matmul avec une pile de matrices ou tenseur 2x4x3.

import numpy as np
fourbyfour = np.array([
                       [1,2,3,4],
                       [3,2,1,4],
                       [5,4,6,7],
                       [11,12,13,14]
                      ])


twobyfourbythree = np.array([
                             [[2,3],[11,9],[32,21],[28,17]],
                             [[2,3],[1,9],[3,21],[28,7]],
                             [[2,3],[1,9],[3,21],[28,7]],
                            ])

print('4x4*4x2x3 dot:\n {}\n'.format(np.dot(fourbyfour,twobyfourbythree)))
print('4x4*4x2x3 matmul:\n {}\n'.format(np.matmul(fourbyfour,twobyfourbythree)))

Les produits de chaque opération apparaissent ci-dessous. Notez comment est le produit scalaire,

... un produit somme sur le dernier axe de a et l'avant-dernier de b

et comment le produit matriciel est formé en diffusant la matrice ensemble.

4x4*4x2x3 dot:
 [[[232 152]
  [125 112]
  [125 112]]

 [[172 116]
  [123  76]
  [123  76]]

 [[442 296]
  [228 226]
  [228 226]]

 [[962 652]
  [465 512]
  [465 512]]]

4x4*4x2x3 matmul:
 [[[232 152]
  [172 116]
  [442 296]
  [962 652]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]

 [[125 112]
  [123  76]
  [228 226]
  [465 512]]]
8
Nathan

En mathématiques, je pense que le point ​​numpy a plus de sens

point ​​(a, b) _ {i, j, k, a, b, c} =\sum_m a_ {i, j, k, m} b_ {a, b, m, c}

puisqu'il donne le produit scalaire lorsque a et b sont des vecteurs, ou la multiplication matricielle lorsque a et b sont des matrices


Quant à l'opération matmul dans numpy, elle consiste en parties de dot ​​résultat, et peut être définie comme

matmul (a, b) _ {i, j, k, c} =\sum_m a_ {i, j, k, m} b_ {i, j, m, c}


Ainsi, vous pouvez voir que matmul (a, b) renvoie un tableau de petite forme, qui utilise moins de mémoire et qui a plus de sens dans les applications. En particulier, en combinant avec diffusion , vous pouvez obtenir

matmul (a, b) _ {i, j, k, l} =\sum_m a_ {i, j, k, m} b_ {j, m, l}

par exemple.


Parmi les deux définitions ci-dessus, vous pouvez voir la configuration requise pour utiliser ces deux opérations. Supposons que a.shape = (s1, s2, s3, s4) et b.shape = (t1, t2, t3, t4)

  • Pour utiliser point (a, b) vous avez besoin de

     1. **t3=s4**;
    
  • Pour utiliser matmul (a, b) vous avez besoin de

    1. t3 = s4
    2. t2 = s2, ou l'un de t2 et s2 est 1
    3. t1 = s1, ou l'un de t1 et s1 est 1

Utilisez le code suivant pour vous convaincre.

Échantillon de code

import numpy as np
for it in xrange(10000):
    a = np.random.Rand(5,6,2,4)
    b = np.random.Rand(6,4,3)
    c = np.matmul(a,b)
    d = np.dot(a,b)
    #print 'c shape: ', c.shape,'d shape:', d.shape

    for i in range(5):
        for j in range(6):
            for k in range(2):
                for l in range(3):
                    if not c[i,j,k,l] == d[i,j,k,j,l]:
                        print it,i,j,k,l,c[i,j,k,l]==d[i,j,k,j,l] #you will not see them
3
Yong Yang