Python'da matrix multiplication: to'g'ri usulni tanlash bo'yicha to'liq qo'llanma
Python'da matrix multiplication tushuntirib berilgan: nested loop'lar, NumPy dot() va @ taqqoslanadi. Kod misollari, taqqoslash jadvali va keng tarqalgan xatolarni tuzatish usullari.
Muallif: Kimberly Fessel
Ko'paytirish yetarlicha oddiy tuyuladi va yillar oldin karra jadvalini o'zlashtirganingizdan beri bu haqda unchalik bosh qotirmagan bo'lsangiz kerak. Lekin tayyormisiz yoki yo'qmi, matritsalar oddiygina ikki yoki uch barobar oshirish haqida bilgan hamma narsangizni ostin-ustun qilib yuborish arafasida.
Ushbu maqolada biz matrix multiplication'ni matematik nuqtai nazardan ta'riflaymiz, so'ngra Python'da matritsalarni ko'paytirishning bir nechta usulini birma-bir ko'rib chiqamiz. To'g'risini tanlashingizga yordam berish uchun variantlarni taqqoslaymiz va oxirida ehtiyot bo'lish kerak bo'lgan bir nechta “tuzoq”ni ham ko'rsatib o'tamiz. Murakkab foizlarni, bakteriyalar o'sishini yoki quyonlar populyatsiyasini kuzatyapsizmi — ko'paytirish haqidagi bu maqola sizga kerak bo'lgan hamma narsani beradi.
Matrix multiplication nima?
Matrix multiplication — ikki matritsani birlashtirib, yangi matritsa hosil qiladigan asosiy matematik amal. Uni bajarish uchun birinchi matritsaning birinchi satrini olasiz va uning har bir elementini ikkinchi matritsaning birinchi ustunidagi mos elementga ko'paytirasiz, so'ngra barcha ko'paytmalarni qo'shasiz. Keyingi elementni hisoblash uchun xuddi shu amalni birinchi matritsaning birinchi satri va ikkinchi matritsaning ikkinchi ustuni uchun bajarasiz va hokazo.
Masalan, quyida ikkita 2x2 matritsa uchun matrix multiplication qanday ishlashini ko'rishingiz mumkin:
[1234]⋅[−1 0 2−2]=[1⋅(−1)+2⋅21⋅0+2⋅(−2)3⋅(−1)+4⋅23⋅0+4⋅(−2)]=[3−45−8]\begin{align*} \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} \cdot \begin{bmatrix} -1 & \;\;0 \\ \;\;2 & -2 \end{bmatrix} &= \begin{bmatrix} 1\cdot(-1) + 2 \cdot 2 & 1\cdot0 + 2 \cdot (-2) \\ 3\cdot (-1) + 4 \cdot 2 & 3 \cdot 0 + 4\cdot (-2) \end{bmatrix}\\ &= \begin{bmatrix} 3 & -4 \\ 5 & -8 \end{bmatrix} \end{align*}[1324]⋅[−120−2]=[1⋅(−1)+2⋅23⋅(−1)+4⋅21⋅0+2⋅(−2)3⋅0+4⋅(−2)]=[35−4−8]
Natija yana bir 2x2 matritsa bo'ladi. Kvadrat bo'lmagan matritsalar ustida ham amal bajarish mumkin, lekin amal to'g'ri bo'lishi uchun matritsalarning ichki o'lchamlari mos kelishi shart. Ya'ni birinchi matritsadagi ustunlar soni ikkinchi matritsadagi satrlar soniga teng bo'lishi kerak. Masalan, 2x3 matritsa bilan 3x4 matritsaning ko'paytmasini topishingiz mumkin; natija tashqi o'lchamlarni, ya'ni 2x4 ni oladi.
1-usul: ikki matritsani nested loop'lar yordamida ko'paytirish
Python'da matrix multiplication'ni amalga oshirish uchun bir nechta turli yondashuvdan foydalanishingiz mumkin. Avvalo, hech qanday tashqi kutubxona talab qilmaydigan sof Python yondashuvi sifatida nested loop'lardan foydalanishingiz mumkin.
Buning uchun ikkala matritsangizni nested list'lar ko'rinishida yaratasiz va natija matritsasini boshidan nollar bilan to'ldirib qo'yasiz. So'ng ichma-ich joylashgan uchta loop yozasiz:
-
Tashqi loop: birinchi matritsaning satrlari
-
O'rta loop: ikkinchi matritsaning ustunlari
-
Ichki loop: mos satr/ustun elementlarini ko'paytirish va ko'paytmalarni qo'shish
Ushbu kod boshlang'ich misolimizdagi matritsalarni uchta nested for loop yordamida ko'paytiradi:
A = [
[1, 2],
[3, 4]
]
B = [
[-1, 0],
[2, -2]
]
result = [
[0, 0],
[0, 0]
]
for i in range(len(A)): # rows of A
for j in range(len(B[0])): # columns of B
for k in range(len(B)): # columns of A/rows of B
result[i][j] += A[i][k]*B[k][j]
print(result)
# Expected result:
# [[3, -4], [5, -8]]
for loop'lar matrix multiplication'ning ichki ishlash mexanizmini o'rganish uchun ajoyib usul bo'lsa-da, ular nisbatan uzun kod hosil qiladi va katta matritsalarni ko'paytirishning eng samarali usuli hisoblanmaydi.
2-usul: nested list comprehension
Kamroq kod talab qiladigan muqobil sof Python yondashuvi sifatida nested list comprehension'larni sinab ko'rishingiz mumkin. Bu usul nested for loop'lar bilan bir xil natijani hisoblaydi, lekin ko'proq Pythonic hisoblanadi va natija matritsasini oldindan tayyorlab qo'yishni talab qilmaydi.
2x2 matritsa misoli nested list comprehension'lar bilan quyidagicha ko'rinadi. E'tibor bering: mos satr va ustun elementlarini juftlash uchun zip()'ga, ikkinchi matritsaning har bir ustun vektoriga murojaat qilish uchun zip(*B)'ga va juft-juft ko'paytmalarni qo'shish uchun sum()'ga tayanamiz:
A = [
[1, 2],
[3, 4]
]
B = [
[-1, 0],
[2, -2]
]
result = [
[sum(a * b for a, b in zip(row, col)) for col in zip(*B)]
for row in A
]
print(result)
# Expected result:
# [[3, -4], [5, -8]]
Bu usulda kod qisqaroq bo'lsa-da, yangi boshlovchilar uchun uni o'qish biroz qiyinroq. Shuningdek, u og'ir matritsa hisob-kitoblari uchun hali ham unchalik samarali emas.
3-usul: NumPy dot()
Yuqori unumdorlikdagi matrix multiplication uchun, ehtimol, NumPy kutubxonasidan foydalanishni xohlaysiz. NumPy — tez va samarali sonli hisob-kitoblar hamda ilmiy hisoblashlar uchun mo'ljallangan tashqi kutubxona. Uni alohida o'rnatishingiz va har safar foydalanganingizda import qilishingiz kerak bo'ladi.
Endi ikkala matritsangizni NumPy massivlari sifatida yaratasiz. Ularni NumPy'ning np.dot() funksiyasi yordamida ko'paytirishingiz mumkin. Bu funksiya kerakli satr-ustun ko'paytmalari va yig'indilarini avtomatik tarzda bajaradi:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
B = np.array([
[-1, 0],
[2, -2]
])
result = np.dot(A, B)
print(result)
# Expected result:
# [[ 3 -4]
# [ 5 -8]]
Bu usul massivlarni ko'paytirishning juda ixcham va o'qilishi oson yo'lini taqdim etadi. Shuningdek, u katta matritsalarni samarali ko'paytirish uchun ancha mosroq. Bu funksiyani qo'llash uchun sizga baribir o'lchamlari mos keladigan matritsalar kerak bo'ladi. Agar matritsalaringizning ichki o'lchamlari mos kelmasa, ValueError olasiz.
Dot product va scalar multiplication
np.dot() input'lariga qarab turlicha ishlaydi. Agar np.dot()'ga ikkita vektor (1-D massiv) bersangiz, natijada shu vektorlarning dot product'ini olasiz. Dot product matrix multiplication natijasining bitta elementini hisoblash bilan aynan bir xil ishlaydi. Birinchi input vektorini satr vektori, ikkinchi input vektorini esa ustun vektori deb tasavvur qiling. Natijangiz barcha mos elementlar ko'paytmalarini qo'shish orqali olinadigan bitta scalar qiymat bo'ladi.
[2−2]⋅[31]=2⋅3+(−2)⋅1=4\begin{bmatrix} 2 & -2 \end{bmatrix} \cdot \begin{bmatrix} 3 \\ 1 \end{bmatrix} = 2\cdot 3 + (-2) \cdot 1 = 4[2−2]⋅[31]=2⋅3+(−2)⋅1=4
np.dot() funksiyasidan foydalanilganda kod quyidagicha ko'rinadi:
import numpy as np
a = np.array([2, -2]) # vector: only one set of brackets
b = np.array([3, 1])
result = a.dot(b)
print(result)
# Expected result:
# 4
Agar np.dot() funksiyasiga matritsa va scalar qiymat bersangiz, uning yordamida matritsaning scalar multiplication'ini ham bajarishingiz mumkin. Aytaylik, A matritsangizni 2 koeffitsiyent bilan scale qilmoqchisiz, ya'ni A'ning har bir elementini 2 ga ko'paytirmoqchisiz. Bu np.dot() bilan qanday ishlashini keyingi misolda ko'rsatamiz:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
result = np.dot(A, 2) # second input is the scalar number 2
print(result)
# Expected result:
# [[2 4]
# [6 8]]
4-usul: matritsalar ko'paytmasi uchun NumPy matmul() / @ operatori
np.dot() ikki o'lchovli massivlar uchun juda yaxshi ishlasa-da, aynan matritsalarni ko'paytirishda dasturchilar odatda NumPy'ning boshqa funksiyasi — np.matmul()'ni afzal ko'radi.
Mana np.matmul() amalda:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
B = np.array([
[-1, 0],
[2, -2]
])
result = np.matmul(A, B)
print(result)
# Expected result:
# [[ 3 -4]
# [ 5 -8]]
Standart matrix multiplication uchun np.matmul() np.dot() bilan bir xil natija beradi, lekin ba'zi boshqa input o'lchamlari bilan ishlaganda bu ikki funksiya farq qiladi. np.dot()'dan farqli ravishda np.matmul() scalar input'larni qabul qilmaydi, ba'zan tensor'lar deb ataladigan yuqori o'lchovli massivlar uchun esa uning xatti-harakati broadcasting qoidalariga bo'ysunadi. Shu sababli, aynan matrix multiplication kerak bo'lganda uning maqsadi aniqroq ko'rinadi.
Python @ operatorini 3.5-versiyada aynan matrix multiplication uchun joriy qilgan. U np.matmul() bilan bir xil amalni tozaroq sintaksis bilan bajaradi. Yuqoridagi koddagi ko'paytirish qatorini quyidagicha qayta yozishingiz mumkin:
result = A @ B
Bu, ayniqsa, ko'paytmalarni zanjir qilib ulashda foydali. Agar natijani yana bir C matritsaga ko'paytirmoqchi bo'lsangiz, shunchaki A @ B @ C deb yozishingiz kifoya.
Matrix multiplication usullarini taqqoslash jadvali
Ikki matritsani ko'paytirishning shuncha ko'p usuli borligi sababli qaysi birini tanlashni bilmay qolishingiz mumkin. Quyida har bir usul uning odatiy qo'llanish holati, kuchli va zaif tomonlari bilan birga keltirilgan qulay taqqoslash jadvalini topasiz.
| Usul | Kutubxona | Eng mos qo'llanish holati | Kuchli tomonlari | Zaif tomonlari |
|---|---|---|---|---|
| Nested loop'lar | Yo'q | Matrix multiplication qanday ishlashini o'rganish; juda kichik matritsalar | Sof Python; shaffof mantiq; qadam-baqadam kuzatish oson | Kod uzun; katta matritsalar uchun sekin |
| Nested list comprehension'lar | Yo'q | Kichik matritsalar | Aniq yozilgan loop'lardan qisqaroq; natijaga boshlang'ich qiymat berish shart emas | O'qish qiyinroq; katta matritsalar uchun samarasiz |
| np.dot() | NumPy | Umumiy sonli ishlar; dot product'lar bilan bir qatorda matrix multiplication | Ixcham; tez; bir nechta input turini qo'llab-quvvatlaydi | Xatti-harakati o'lchovlar soniga qarab o'zgaradi; maqsadi kamroq aniq |
| np.matmul() | NumPy | Matrix multiplication, ayniqsa yuqori o'lchovli massivlar bilan | Aniq matrix multiplication; broadcasting'ni qo'llab-quvvatlaydi | NumPy talab qiladi; scalar input'larni qabul qilmaydi |
| @ operatori | Yo'q, lekin input'lar NumPy massivlari yoki shunga o'xshash obyektlar bo'lishi kerak | O'qilishi oson, ixcham matrix multiplication | Toza sintaksis; zanjir qilish oson; maqsadi aniq | @'ni implement qiladigan obyektlarni talab qiladi; yangi boshlovchilarga kamroq tanish bo'lishi mumkin |
Python dasturlaridagi keng tarqalgan xatolar va ularni tuzatish
Python matrix multiplication'ni, ayniqsa @ operatori kabi sintaksis tufayli, oddiy ishdek ko'rsatadi. Biroq alohida e'tibor berish kerak bo'lgan bir nechta jihat bor. Quyida amaliyotda duch keladigan eng keng tarqalgan muammolarning qisqacha xulosasi keltirilgan.
O'lcham / shape mos kelmasligi
Eng keng tarqalgan xato matritsalaringizning ichki o'lchamlari mos kelmaganda yuzaga keladi. Amal to'g'ri bo'lishi uchun birinchi matritsadagi ustunlar soni ikkinchisidagi satrlar soniga teng bo'lishi kerak. Ikki umumiy matritsa uchun shape'lar quyidagi pattern'ga mos kelishi kerak:
(m x n) @ (n x p) → (m x p)
Masalan, matritsalaringiz (3 x 2) @ (2 x 4) → (3 x 4) ko'rinishida bo'lishi mumkin. Biroq (2 x 3) @ (2 x 4) noto'g'ri, chunki ichki o'lchamlar bir xil emas.
Scalar'larni ko'paytirishdan farqli ravishda matrix multiplication kommutativ emas. Umuman olganda A @ B ≠ B @ A. Bundan tashqari, A @ B to'g'ri bo'lgani holda B @ A umuman bajarib bo'lmaydigan amal bo'lishi ham mumkin.
NumPy'da shape'lar mos kelmasa, ValueError xatosini olishingiz mumkin. Aniq xabar NumPy versiyangizga hamda foydalanayotgan funksiya yoki operatoringizga bog'liq, lekin unda “shapes not aligned” deyilgan bo'lishi mumkin. Sof Python implementatsiyalari o'lchamlar mos kelmasligi haqida foydali xato bermasligi mumkin. Siz IndexError olishingiz yoki hatto hech qanday exception'siz noto'g'ri natijani hisoblab qo'yishingiz mumkin.
Bu muammoning oldini olish yoki uni tuzatish uchun matritsalaringiz o'lchamlarini qayta tekshiring. NumPy massivlari uchun .shape attribute'idan foydalaning, nested list'lar bilan ishlayotgan bo'lsangiz, satrlar sonini bilish uchun len(A)'ni, ustunlarni sanash uchun esa len(A[0])'ni sinab ko'ring.
dtype muammolari
NumPy massivlari odatda barcha elementlari uchun yagona ma'lumot tipidan (dtype) foydalanadi. NumPy'dagi matrix multiplication odatda sonli qiymatlarni talab qiladi va sonli bo'lmagan ma'lumotlar uchun xato chiqarishi mumkin.
Aralash tipli massivga duch kelganda NumPy foydali dtype konvertatsiyasini bajarishga harakat qiladi. Shunga qaramay, sonli qiymatlar string sifatida saqlangan bo'lsa, xatolarga duch kelishingiz mumkin.
Bu muammoni hal qilish uchun avval massivingizning ma'lumot tipini ko'rish maqsadida .dtype attribute'ini tekshiring. So'ngra .astype() method'i yordamida ma'lumot tipini moslashtirishingiz mumkin (masalan, A.astype(float)). Shuningdek, integer'lar va float'lar qiymatlarni ifodalash va sonli aniqlikni boshqarish jihatidan bir-biridan farq qilishini yodda tuting.
* va @'ni chalkashtirish
Scalar qiymatlar ko'paytmasini olish uchun ishlatiladigan Python'ning ko'paytirish operatori * bilan allaqachon tanish bo'lsangiz kerak (masalan, 2 * 3 → 6). Ma'lum bo'lishicha, * yordamida NumPy massivlari uchun element-wise multiplication'ni ham bajarish mumkin. Bu operator massivdagi har bir qiymatni ikkinchi massivdagi mos element bilan juftlaydi va ularni ko'paytiradi. U satrlar yoki ustunlar bo'yicha yig'indi hisoblamaydi:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
result = A * A
print(result)
# Expected result:
# [[ 1 4]
# [ 9 16]]
Biroq @ operatori aynan matritsalarni ko'paytiradi. Bu operatorlarni almashtirib qo'ysangiz, ehtimol butunlay boshqa natija olasiz:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
result = A @ A # swap in @ operator
print(result)
# Expected result:
# [[ 7 10]
# [15 22]]
Ikkala amal ham to'g'ri bo'lgan hollarda * va @'ni almashtirib qo'yish Python dasturlarida ko'pincha hech qanday ogohlantirishsiz o'tib ketadigan xatolarga olib keladi, shuning uchun matrix multiplication uchun albatta @'dan foydalaning, *'ni esa element-wise yoki scalar multiplication uchun qoldiring.
Matrix multiplication uchun broadcasting
Oxirgi jihat sifatida, NumPy broadcasting deb ataladigan usul yordamida o'lchamlari har xil, lekin bir-biriga mos keladigan massivlar ustida amal bajara oladi. Bu ma'lumotlarni nusxalash yoki reshape qilishga hojat qoldirmasdan kichikroq va kattaroq massivlar o'rtasida hisob-kitob qilish imkonini beradi.
Masalan, A matritsaga b satr vektorini qo'shayotganingizda NumPy siz b'ni A matritsaning har bir satriga qo'shmoqchi ekaningizni nazarda tutadi:
import numpy as np
A = np.array([
[1, 2],
[3, 4]
])
b = np.array([10, 20])
result = A + b # add row vector b to each row of A
print(result)
# Expected result:
# [[11 22]
# [13 24]]
Broadcasting odatda 3 yoki undan ortiq o'lchovli massivlar (tensor'lar) ko'paytmasini olishda ahamiyat kasb etadi. np.matmul() ham, @ operatori ham oxirgi ikki axis'ni matritsa sifatida ko'radi, oldingi axis'larni esa matritsalar batch'lari kabi qabul qiladi. Bu bir xil matritsa amalini ko'plab matritsalar ustida bajarishda, masalan, ba'zi machine learning algoritmlarida juda foydali bo'ladi.
Xulosa
Matrix multiplication — ikki matritsani bitta natija matritsasiga birlashtiradigan keng tarqalgan matematik amal. Python buni amalga oshirish uchun ko'plab turli usullarni taklif qiladi: nested loop'lar va list comprehension'lar kabi sof Python usullari, NumPy funksiyalari (np.dot() va np.matmul()) hamda @ operatori. Eng keng tarqalgan tuzoqlarga tushmaslik uchun matritsalaringizning o'lchamlari va ma'lumot tiplarini qayta tekshiring hamda ko'paytirish operatori *'ni matrix multiplication operatori @ bilan adashtirmayotganingizga ishonch hosil qiling.
Endi AI Tutor bilan muloqot qilib, ko'paytirishni puxta o'zlashtirganingizga ishonch hosil qiling. Ushbu prompt * va @ haqidagi qolgan har qanday chalkashlikni bartaraf etib, ishni boshlab olishingizga yordam beradi: