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?
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 dedot
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
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]]]
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
Utilisez le code suivant pour vous convaincre.
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