Bab-bab sebelumnya memakai satu bentuk kontraksi yang tetap: @ menjumlahkan perkalian sepanjang sumbu terakhir operand kiri dan sumbu pertama operand kanan. np.tensordot menggeneralisasi aturan itu — kita yang memilih sumbu mana yang dikalikan lalu dijumlahkan (kontraksi), termasuk dua sumbu sekaligus atau tidak ada sama sekali. Bergerak ke arah sebaliknya, np.kron (produk Kronecker) tidak mengurangi dimensi: setiap elemen a[i, j] menjadi satu blok utuh berisi a[i, j] * b.
Dua fungsi ini merangkum dua pola besar array multidimensi: kontraksi (meringkas sumbu) dan replikasi blok (mengulang pola).
Tiga Tingkat Kontraksi: axes 0, 1, dan 2 #
Argumen axes mengatur pemilihan sumbu: bilangan N memasangkan N sumbu terakhir a dengan N sumbu pertama b; tuple (axis_a, axis_b) memasangkan sumbu secara eksplisit. Tiga tingkat yang paling sering dipakai:
axes | Kontraksi | Setara dengan |
|---|---|---|
0 | tanpa kontraksi — semua pasangan elemen dipertahankan | np.outer(a, b) |
1 | satu sumbu: terakhir a, pertama b | A @ B |
2 | dua sumbu sekaligus | np.sum(A * B) untuk 2-D |
Kita uji ketiganya:
import numpy as np
a = np.array([1, 2])
b = np.array([3, 4])
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
print(np.tensordot(a, b, axes=0))
print(np.array_equal(np.tensordot(a, b, axes=0), np.outer(a, b)))
print(np.tensordot(A, B, axes=1))
print(np.array_equal(np.tensordot(A, B, axes=1), A @ B))
print(np.tensordot(A, B, axes=2))
print(np.array_equal(np.tensordot(A, B, axes=2), np.sum(A * B)))Output yang Dihasilkan:
[[3 4]
[6 8]]
True
[[19 22]
[43 50]]
True
70
TrueBedah baris demi baris:
- Baris 9-10 (
axes=0): tanpa kontraksi; setiap pasangana[i]·b[j]ditaruh pada grid (2, 2) →[[3, 4], [6, 8]], persisnp.outerdari Inner dan Outer Product. - Baris 11-12 (
axes=1): sumbu terakhirA(panjang 2) dipasangkan dengan sumbu pertamaB→[[19, 22], [43, 50]], identik denganA @ B— inti Dot Product dan perkalian matriks. - Baris 13-14 (
axes=2): kedua sumbu dikontraksi →1·5 + 2·6 + 3·7 + 4·8 = 70, sama dengannp.sum(A * B); bentuk eksplisitnyaaxes=((0, 1), (0, 1)). Nilainya berguna sebagai skor kemiripan dua matriks.
Batch Perkalian Matriks sebagai tensordot #
Chapter Operasi Matriks Batch menunjukkan batch @ M: operand 2-D di-broadcast ke seluruh tumpukan. Tensordot menyatakan operasi yang sama tanpa aturan broadcast — cukup sebut sumbu yang dikontraksi. Kasusnya: dua gudang dengan matriks pengiriman 2 kanal x 3 produk, dikalikan matriks ongkos kemas 3 produk x 2 jenis pengiriman:
import numpy as np
batch = np.array([[[1, 2, 3],
[4, 5, 6]],
[[7, 8, 9],
[10, 11, 12]]])
M = np.array([[1, 0],
[0, 1],
[1, 1]])
hasil = np.tensordot(batch, M, axes=(2, 0))
print(hasil.shape)
print(np.array_equal(hasil, batch @ M))
print(hasil)Output yang Dihasilkan:
(2, 2, 2)
True
[[[ 4 5]
[10 11]]
[[16 17]
[22 23]]]Bedah baris demi baris:
- Baris 10 (
axes=(2, 0)): sumbu 2batch(produk, panjang 3) dipasangkan dengan sumbu 0M(juga produk); sumbu tersisa (gudang, kanal, jenis pengiriman) menata hasil (2, 2, 2). - Baris 11-12: hasilnya identik dengan
batch @ M;axes=1memberi hasil yang sama karena sumbu terakhirbatchadalah pasangan itu. - Baris 13: anggota pertama
[[4, 5], [10, 11]]adalahbatch[0] @ M; baris[1, 2, 3]dikalikan kolom-kolomM.
np.kron: Satu Elemen, Satu Blok #
np.kron(a, b) menata hasil sebagai blok: blok pada posisi (i, j) berisi seluruh matriks b yang dikalikan a[i, j]; untuk a berordo (p, q) dan b berordo (r, s), hasil berordo (p·r, q·s). Kita bedah satu contoh:
import numpy as np
a = np.array([[1, 2],
[3, 4]])
b = np.array([[10, 20],
[30, 40]])
k = np.kron(a, b)
print(k.shape)
print(k)Output yang Dihasilkan:
(4, 4)
[[ 10 20 20 40]
[ 30 40 60 80]
[ 30 60 40 80]
[ 90 120 120 160]]Bedah baris demi baris:
- Blok kiri-atas (
a[0,0] = 1):1 * b=[[10, 20], [30, 40]]— dua baris dan dua kolom pertama. - Blok kanan-atas (
a[0,1] = 2):2 * b=[[20, 40], [60, 80]]— baris 1-2, kolom 3-4. - Blok bawah (
a[1,0] = 3,a[1,1] = 4):3 * bdan4 * bmenempati dua blok bawah; kron menggandakan nilai (angka 40 muncul di dua blok), bukan memindahkannya.
Pola Blok Berulang: Blok Diagonal dan Desain Eksperimen #
Dua kasus nyata berikut hampir selalu dirumuskan lewat kron. Kasus 1 — blok diagonal per kategori: dua kategori produk, tiap kategori punya pola kovarians [[4, 2], [2, 4]] antara dua produknya, dan kategori berbeda dianggap bebas (kovarians nol); matriks gabungan 4x4-nya kron(np.eye(2), pola). Kasus 2 — desain eksperimen: tiga lokasi uji, dua perlakuan, pola perlakuan sama diulang di tiap lokasi → kron(np.ones((3, 1)), treatment):
import numpy as np
pola = np.array([[4.0, 2.0],
[2.0, 4.0]])
blok = np.kron(np.eye(2), pola)
print(blok)
desain = np.kron(np.ones((3, 1), dtype=int), np.array([[1, 0], [0, 1]]))
print(desain)Output yang Dihasilkan:
[[4. 2. 0. 0.]
[2. 4. 0. 0.]
[0. 0. 4. 2.]
[0. 0. 2. 4.]]
[[1 0]
[0 1]
[1 0]
[0 1]
[1 0]
[0 1]]Bedah baris demi baris:
- Baris 5 (
np.kron(np.eye(2), pola)): elemen diagonaleye(2)bernilai 1 → blokpolautuh; elemen lain 0 → blok nol. Nol antar-kategori muncul otomatis. - Baris 8 (
np.ones((3, 1), dtype=int)): operand kiri (3, 1) mengulangtreatmenttiga kali ke bawah → desain 6x2, tiga lokasi x dua perlakuan.
Untuk k kategori, pakai kron(np.eye(k), pola); bila antar-kategori punya hubungan, operand kiri diganti matriks korelasi antar-kategori.
tensordot atau einsum #
einsum menyatakan kontraksi yang sama — tensordot menyebut nomor sumbu, einsum menyebut huruf per sumbu. Yang menarik semantiknya: tensordot menjumlahkan habis semua sumbu yang dipasangkan; einsum mempertahankan sumbu bersama sebagai pasangan sejajar — persis semantik @ berbatch:
import numpy as np
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
print(np.array_equal(np.tensordot(A, B, axes=1), np.einsum("ij,jk->ik", A, B)))
print(np.array_equal(np.tensordot(A, B, axes=2), np.einsum("ij,ij->", A, B)))
Ab = np.array([[[1, 2, 3], [4, 5, 6]],
[[7, 8, 9], [10, 11, 12]]])
Bb = np.array([[[1, 0], [0, 1], [1, 1]],
[[2, 0], [0, 2], [1, 1]]])
print(np.array_equal(Ab @ Bb, np.einsum("kij,kjp->kip", Ab, Bb)))
print(np.tensordot(Ab, Bb, axes=(2, 1)).shape)Output yang Dihasilkan:
True
True
True
(2, 2, 2, 2)Bedah baris demi baris:
- Baris 7-8: kontraksi satu sumbu dan kontraksi ganda — tensordot dan einsum menghasilkan nilai yang identik.
- Baris 14 (
einsum("kij,kjp->kip", ...)): hurufkada di kedua operand dan tetap di output → matriks ke-k dikalikan matriks ke-k — identik denganAb @ Bb. - Baris 15 (
tensordot(..., axes=(2, 1))): sumbu produk dan batch ikut terkontraksi → hasil (2, 2, 2, 2): semua kombinasi anggota batch dipasangkan, bukan anggota-ke-anggota.
Dengan tensordot untuk kontraksi dan kron untuk replikasi blok, matriks besar dapat dirakit dari pola kecil yang mudah diverifikasi.