اذهب إلى المحتوى
  • 0

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

Amer Abdallah

السؤال

لقد لاحظت مؤخرًا أن معامل ضرب المصفوفة الجديد (@) يتصرف أحيانًا بشكل مختلف عن معامل 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؟

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

Recommended Posts

  • 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)

 

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

  • 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
رابط هذا التعليق
شارك على الشبكات الإجتماعية

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...