0Pricing
Pandas & NumPy Academy · درس

ضرب المصفوفات باستخدام np.matmul و@

اضرب المصفوفات بشكل صحيح باستخدام np.matmul والعامل @، وميّز بين * للضرب عنصرًا بعنصر وضرب المصفوفات الفعلي

ضرب المصفوفات باستخدام np.matmul و@ درس مجاني في Pandas & NumPy Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Pandas & NumPy Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

أهمية ضرب المصفوفات

يُعد ضرب المصفوفات محور علم البيانات وتعلم الآلة. فهو يدعم الشبكات العصبية، وتقليل الأبعاد، والتحويلات الخطية. في Python، يوفر لك NumPy طريقتين واضحتين لضرب المصفوفات: الدالة np.matmul() والعامل @ الذي قُدّم في Python 3.5. كلتاهما سريعتان، ومتجهتان، وأكثر كفاءة بكثير من حلقات Python المتداخلة.

قواعد ضرب المصفوفات

بالنسبة إلى المصفوفتين A وB، يكون حاصل الضرب A @ B صالحًا فقط عندما يكون عدد الأعمدة في A مساويًا لعدد الصفوف في B. إذا كان شكل A هو (m, n) وشكل B هو (n, k)(m, k). تُسمى هذه القاعدة قيد البعد الداخلي، وتنطبق بغض النظر عن دالة NumPy التي تستخدمها.

import numpy as np
A = np.array([[1, 2], [3, 4]])   # shape (2, 2)
B = np.array([[5, 6], [7, 8]])   # shape (2, 2)
C = A @ B
print(C)  # [[19 22] [43 50]]

np.matmul مقابل العامل @

ينتج عن np.matmul(A, B) وA @ B نتائج متطابقة للمصفوفات ثنائية الأبعاد. العامل @ مجرد صياغة مختصرة؛ فهو يستدعي __matmul__ داخليًا، الذي ينفذه NumPy باستخدام روتين BLAS نفسه على مستوى C. استخدم @ في الشيفرة اليومية لتحسين سهولة القراءة، واستخدم np.matmul عندما تريد تمرير الدالة بوصفها دالة قابلة للاستدعاء أو معالجة الحالات الخاصة صراحةً.

import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.matmul(A, B))   # same as A @ B
print(A @ B)             # same result

التمييز بين @ و* وnp.dot

يضم NumPy ثلاث دوال للضرب يسهل الخلط بينها. يمثّل A * B ضربًا عنصريًا، أي حاصل ضرب هادامار. ويتصرف np.dot(A, B) مثل np.matmul مع المصفوفات ثنائية الأبعاد، لكنه يتبع دلالات مختلفة مع الأبعاد الأعلى. أما A @ B فهو ضرب مصفوفات بالمعنى الدقيق، ويتبع التعريف الرياضي بدقة؛ لذا يُفضّل استخدامه بدلًا من np.dot في أعمال المصفوفات.

import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print('element-wise:', A * B)    # [[5 12] [21 32]]
print('matmul:', A @ B)          # [[19 22] [43 50]]

ضرب المصفوفات ليس تبديليًا

بخلاف ضرب الأعداد، فإن ضرب المصفوفات ليس تبديليًا: إذ يختلف A @ B عمومًا عن B @ A. وقد لا تكون الأشكال متوافقة في كلا الاتجاهين أصلًا. ويهم ذلك عند كتابة التحويلات؛ إذ يغيّر ترتيب العمليات النتيجة. احرص دائمًا على معرفة المصفوفة الموجودة إلى اليسار وتلك الموجودة إلى اليمين.

import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[0, 1], [1, 0]])
print('A @ B:', A @ B)   # [[2 1] [4 3]]
print('B @ A:', B @ A)   # [[3 4] [1 2]]  -- different!

ضرب المصفوفات على دفعات

عندما تحتوي مصفوفاتك على أكثر من بُعدين، يتعامل np.matmul و@ مع الأبعاد البادئة الإضافية بوصفها أبعاد دفعات، ويضربان المصفوفات بالتوازي. على سبيل المثال، يؤدي ضرب مصفوفة شكلها (32, 3, 4) في أخرى شكلها (32, 4, 5) إلى ناتج شكله (32, 3, 5)؛ أي إجراء 32 عملية ضرب مستقلة من الشكل 3×4 @ 4×5 في آن واحد. هكذا تعالج أطر التعلم العميق الدفعات الصغيرة بكفاءة.

import numpy as np
batch_A = np.random.rand(32, 3, 4)
batch_B = np.random.rand(32, 4, 5)
result = batch_A @ batch_B
print(result.shape)  # (32, 3, 5)

ضرب المصفوفة في المتجه

من الحالات الخاصة الشائعة ضرب مصفوفة في متجه. إذا كان شكل A هو (m, n) وكان v مصفوفة أحادية البعد طولها n، فإن A @ v ينتج مصفوفة أحادية البعد طولها m. يتولى NumPy محاذاة الأشكال تلقائيًا؛ ولا تحتاج إلى إعادة تشكيل v ليصبح متجه عمود. يظهر هذا النمط في كل مكان: عند تطبيق الأوزان في الانحدار الخطي، وإسقاط السمات، وحساب حاصل الضرب النقطي لكل صف في الوقت نفسه.

import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6]])  # (2, 3)
v = np.array([1, 0, -1])              # (3,)
print(A @ v)   # [1-3, 4-6] = [-2, -2]

الأداء: @ مقابل حلقات Python

يستدعي ضرب المصفوفات في NumPy روتينات BLAS/LAPACK محسّنة، ويعمل في شيفرة C المترجمة. أما الحلقة المتداخلة المكتوبة بلغة Python الخالصة لتنفيذ العملية نفسها فتكون عادةً أبطأ بمقدار 100–1000 مرة مع المصفوفات الكبيرة. عندما تحتاج إلى عمليات ضرب مصفوفات متكررة في خط أنابيب للبيانات، مثل تحويلات السمات أو إسقاطات PCA أو تحديثات الأوزان، استخدم دائمًا @ أو np.matmul، ولا تنفذ الحلقة الداخلية بنفسك.

import numpy as np, time
A = np.random.rand(500, 500)
B = np.random.rand(500, 500)
t0 = time.time()
C = A @ B
print(f'NumPy: {(time.time()-t0)*1000:.1f} ms')  # single-digit ms

تسلسل عمليات ضرب مصفوفات متعددة

يمكنك تسلسل عمليات @ متعددة في تعبير واحد: حيث يُقيَّم A @ B @ C من اليسار إلى اليمين. لا يحسّن NumPy ترتيب الضرب تلقائيًا، بخلاف mtimes في MATLAB؛ لذلك قد يؤثر الترتيب في السرعة إذا كانت أحجام المصفوفات مختلفة جدًا. عند التعامل مع ثلاث مصفوفات أو أكثر، فكّر في استخدام np.linalg.multi_dot([A, B, C])، الذي يختار تلقائيًا الاقتران الأكثر كفاءة.

import numpy as np
A = np.random.rand(100, 20)
B = np.random.rand(20, 50)
C = np.random.rand(50, 10)
result = np.linalg.multi_dot([A, B, C])
print(result.shape)  # (100, 10)

أخطاء الأشكال الشائعة وطرق إصلاحها

أكثر الأخطاء شيوعًا في ضرب المصفوفات هو عدم تطابق الأبعاد: ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0. ولإصلاح ذلك، تحقّق من الأبعاد باستخدام A.shape وتأكد من توافق الأبعاد الداخلية. إذا كان لديك متجه صفّي ذو شكل (n,) وتحتاج إلى التعامل معه كمتجه عمودي ذي شكل (n, 1)، فاستخدم v.reshape(-1, 1). وإذا كنت تحتاج إلى النقل، فاستخدم A.T.

import numpy as np
A = np.ones((3, 4))
B = np.ones((3, 4))  # wrong -- inner dims 4 != 3
try:
    A @ B
except ValueError as e:
    print('Error:', e)
print('Fix:', (A @ B.T).shape)  # (3, 3)

مثال عملي: تحويل السمات

من حالات الاستخدام المعتادة في علم البيانات: أن تكون لديك مجموعة بيانات X ذات شكل (n_samples, n_features) ومصفوفة أوزان W ذات شكل (n_features, n_outputs). يطبّق حاصل ضرب المصفوفتين X @ W تحويلًا خطيًا على كل عينة في الوقت نفسه، وينتج شكلًا (n_samples, n_outputs). وهذا هو الحساب الأساسي للطبقة الخطية في الشبكة العصبية وللتنبؤ في الانحدار الخطي.

import numpy as np
n_samples, n_features, n_outputs = 200, 10, 3
X = np.random.rand(n_samples, n_features)
W = np.random.rand(n_features, n_outputs)
b = np.zeros(n_outputs)
predictions = X @ W + b   # shape (200, 3)
print(predictions.shape)

تحقّق سريع

اختبر مدى فهمك لضرب المصفوفات في NumPy.

مراجعة الدرس

في هذا الدرس تعلّمت أن: np.matmul و @ ينفّذان ضرب المصفوفات الحقيقي (وليس ضرب العناصر واحدًا بواحد)، وأن الأبعاد الداخلية للمصفوفتين يجب أن تتطابق، وأن @ يدعم الضرب على دفعات عبر الأبعاد السابقة. بعد ذلك سنستكشف المحددات والمعكوسات والمنقولات — وهي العمليات التي تخبرك بما إذا كان من الممكن «التراجع» عن تأثير المصفوفة.

الأسئلة الشائعة

هل درس «ضرب المصفوفات باستخدام np.matmul و@» مجاني؟

نعم — نص درس «ضرب المصفوفات باستخدام np.matmul و@» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Pandas & NumPy Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Pandas & NumPy Academy 4 دروس في المجموع.

ماذا ستتعلم في «ضرب المصفوفات باستخدام np.matmul و@»؟

اضرب المصفوفات بشكل صحيح باستخدام np.matmul والعامل @، وميّز بين * للضرب عنصرًا بعنصر وضرب المصفوفات الفعلي تتمرن على Pandas & NumPy Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Pandas & NumPy Academy؟

لا تُشترط خبرة سابقة. Pandas & NumPy Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «ضرب المصفوفات باستخدام np.matmul و@»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Pandas & NumPy Academy هذا؟

نعم. كل درس في Pandas & NumPy Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ضرب المصفوفات باستخدام np.matmul و@
  2. المحددات والمعكوسات والمنقولات
  3. حل الأنظمة الخطية
  4. نظرة عامة على القيم الذاتية وSVD
← العودة إلى Pandas & NumPy Academy