• 0

الفرق بين numpy.dot وضرب المصفوفة باستخدام المعامل @

لقد لاحظت مؤخرًا أن معامل ضرب المصفوفة الجديد (@) يتصرف أحيانًا بشكل مختلف عن معامل numpy.dot . على سبيل المثال، للمصفوفات ثلاثية الأبعاد:

import numpy as np

a = np.random.rand(2,3,3)
b = np.random.rand(2,3,3)
c = a @ b  # Python 3.5+
d = np.dot(a, b)

يُرجع المعامل @ شكل المصفوفة كالتالي:

>>> c.shape
(2, 3, 3)

بينما الدالة np.dot() تعيد شكل مصفوفة كالتالي:

>>> d.shape
(2, 3, 2, 3)

ما الفرق بين numpy.dot وضرب المصفوفة باستخدام المعامل @؟ وكيف يمكنني إعادة إنتاج نفس النتيجة باستخدام numpy.dot؟

1 شخص أعجب بهذا

انشر على الشّبكات الاجتماعية


رابط هذه المساهمة
شارك على الشبكات الإجتماعية
  • 1

في بايثون، يتم التعامل مع ال arrays كمتجهات Vectors. أيضاً تسمى ال 2D-Arrays بال matrices. وهناك توابع جاهزة لتنفيذ الضرب بينهما في بايثون. وهما @ وتمثل التابع __matmul__ و numpy.dot وهما متشابهين جداً مع بعض الاختلافات. حيث تستخدم الدالة numpy.dot  لإجراء ضرب المصفوفات matrices في بايثون (الضرب العادي) حيث يتحقق فيما إذا كان شرط ضرب المصفوفات محقق أولاً، أي أن عدد أعمدة المصفوفة الأولى يجب أن يكون مساوياً لعدد صفوف المصفوفة الثانية. ويعمل مع مصفوفات متعددة الأبعاد أيضاً. يمكننا أيضاَ تحديد مصفوفة بديلة كمعامل لتخزين النتيجة. المعامل @  يستدعي دالة __matmul__ لمصفوفة تُستخدم لإجراء نفس عملية الضرب. فمثلاً:

# لاحظ كيف أعطيانا نفس الناتج
import numpy as np
a1 = np.array([[3,5],[0,3]])
a2 = np.array(([4,1],[6,7]))
print(a1@a2)
"""
[[42 38]
 [18 21]]
"""
print(np.dot(a1,a2))
"""
[[42 38]
 [18 21]]
"""

لكن عندما نتعامل مع المصفوفات متعددة الأبعاد ndArrays تكون النتيجة مختلفة قليلاً. يمكنك أن ترى الفرق في المثال:

import numpy as np
a1 = np.random.rand(2,2,2)
a2 = np.random.rand(2,2,2)
r1 = np.dot(a1,a2)
r1
"""
[[[[0.12022633 0.02387914]
   [0.01322726 0.14207161]]

  [[0.61570778 0.04646624]
   [0.66232634 0.33838594]]]


 [[[0.63522905 0.0644122 ]
   [0.55415297 0.43366682]]

  [[0.79912989 0.13910756]
   [0.2417268  0.84365592]]]]
"""
print(r1.shape) # (2, 2, 2, 2)
r2 = (a1@a2)
"""
[[[0.12022633 0.02387914]
  [0.61570778 0.04646624]]

 [[0.55415297 0.43366682]
  [0.2417268  0.84365592]]]
"""
print(r2,r2.shape) # (2, 2, 2)

حيث أن الدالة matmul تقوم بعملية "Brodcasting" على المصفوفة، أي يتم التعامل معها على أنها كومة من المصفوفات الموجودة في الفهرين الأخيرين ويتم بثها وفقاً لذلك. لكنها تحافظ على أبعاد الخرج أي :

(n,k),(k,m)->(n,m)

مثلاً:

a = np.ones([9, 5, 7, 4])
c = np.ones([9, 5, 4, 3])
np.dot(a, c).shape
#(9, 5, 7, 9, 5, 3)
np.matmul(a, c).shape
#(9, 5, 7, 3)
# n is 7, k is 4, m is 3

ومن ناحية أخرى، تقوم الدالة numpy.dot بالضرب كمجموع حاصل الضرب على المحور الأخير من المصفوفة الأولى والثاني إلى الأخير من الثانية.
هناك اختلاف آخر بينهما، وهو أن الدالة matmul لا يمكنها إجراء عملية ضرب للمصفوفة بقيم عددية:

import numpy as np
a = np.array([1, 2])
np.dot(a,5)
# array([ 5, 10])
a@5 # ValueError: matmul: Input operand 1 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)

 

1 شخص أعجب بهذا

انشر على الشّبكات الاجتماعية


رابط هذه المساهمة
شارك على الشبكات الإجتماعية
  • 0

المعامل @ يقوم باستدعاء الدالة ()matmul و ليس ()dot.

>>> a = np.random.rand(2,3,3)
>>> b = np.random.rand(2,3,3)
>>> np.matmul(a, b).shape
(2, 3, 3)
>>> np.dot(a, b).shape
(2, 3, 3)

matmul تختلف عن dot في أمرين:

الضرب في scalar غير مسموح به.
يتم عمل broadcasting للمصفوفات معًا كما لو كانت المصفوفات عناصر.

بالنسبة للجزء الثاني من السؤال, لا يمكنك الحصول على هذه النتيجة باستعمال ()dot. يجب استعمال اما ()matmul او @.

تمّ تعديل بواسطة Walid K
1 شخص أعجب بهذا

انشر على الشّبكات الاجتماعية


رابط هذه المساهمة
شارك على الشبكات الإجتماعية

يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن