Saya berjuang untuk mengerti bagaimana tepatnya einsum
bekerja. Saya telah melihat dokumentasi dan beberapa contoh, tetapi sepertinya tidak melekat.
Inilah contoh yang kami pelajari di kelas:
C = np.einsum("ij,jk->ki", A, B)
untuk dua array A
danB
Saya pikir ini akan memakan waktu A^T * B
, tapi saya tidak yakin (itu mengambil alih salah satu dari mereka, kan?). Adakah yang bisa menuntun saya melalui apa yang terjadi di sini (dan secara umum saat menggunakan einsum
)?
python
arrays
numpy
multidimensional-array
numpy-einsum
Selat Lance
sumber
sumber
(A * B)^T
, atau setaraB^T * A^T
.einsum
sini . (Saya senang mentransplantasikan bit yang paling relevan ke jawaban di Stack Overflow jika berguna).numpy
dokumentasi tidak memadai ketika menjelaskan rincian.*
bukan perkalian matriks tetapi perkalian elemen. Awas!Jawaban:
(Catatan: jawaban ini didasarkan pada posting blog pendek tentang yang
einsum
saya tulis beberapa waktu lalu.)Apa yang
einsum
harus dilakukanBayangkan kita memiliki dua array multi-dimensi,
A
danB
. Sekarang mari kita anggap kita ingin ...A
denganB
cara tertentu untuk menciptakan berbagai produk baru; dan mungkinAda peluang bagus yang
einsum
akan membantu kami melakukan ini lebih cepat dan lebih efisien-memori yang disukai oleh kombinasi fungsi NumPymultiply
,sum
dantranspose
akan memungkinkan.Bagaimana cara
einsum
kerjanya?Berikut adalah contoh sederhana (tetapi tidak sepenuhnya sepele). Ambil dua array berikut:
Kami akan mengalikan
A
danB
elemen-bijaksana dan kemudian menjumlahkan sepanjang baris array baru. Dalam NumPy "normal" kami akan menulis:Jadi di sini, operasi pengindeksan pada
A
baris atas sumbu pertama dari dua array sehingga multiplikasi dapat disiarkan. Baris-baris array produk kemudian dijumlahkan untuk mengembalikan jawabannya.Sekarang jika kita ingin menggunakan
einsum
, kita bisa menulis:The signature String
'i,ij->i'
adalah kunci di sini dan membutuhkan sedikit menjelaskan. Anda bisa memikirkannya dalam dua bagian. Di sisi kiri (kiri->
) kami telah memberi label pada dua array input. Di sebelah kanan->
, kami memberi label array yang ingin kami selesaikan.Inilah yang terjadi selanjutnya:
A
memiliki satu sumbu; kami telah melabeli itui
. DanB
memiliki dua sumbu; kami telah memberi label sumbu 0 sebagaii
dan sumbu 1 sebagaij
.Dengan mengulangi label
i
di kedua larik input, kami memberi tahueinsum
bahwa kedua sumbu ini harus dikalikan bersama. Dengan kata lain, kami mengalikan arrayA
dengan setiap kolom arrayB
, sepertiA[:, np.newaxis] * B
halnya.Pemberitahuan yang
j
tidak muncul sebagai label pada output yang diinginkan; kami baru saja menggunakani
(kami ingin berakhir dengan array 1D). Dengan menghilangkan label, kami menyuruheinsum
untuk menjumlahkan sepanjang sumbu ini. Dengan kata lain, kami menjumlahkan deretan produk, seperti.sum(axis=1)
halnya.Pada dasarnya itu semua yang perlu Anda ketahui untuk digunakan
einsum
. Ini membantu untuk bermain sedikit; jika kita membiarkan kedua label di output'i,ij->ij'
,, kita mendapatkan kembali array produk 2D (sama sepertiA[:, np.newaxis] * B
). Jika kami mengatakan tidak ada label keluaran'i,ij->
,, kami mendapatkan kembali satu nomor (sama dengan melakukan(A[:, np.newaxis] * B).sum()
).Hal yang hebat tentang
einsum
itu, adalah tidak membangun array produk sementara terlebih dahulu; itu hanya menjumlahkan produk saat berjalan. Ini dapat menyebabkan penghematan besar dalam penggunaan memori.Contoh yang sedikit lebih besar
Untuk menjelaskan produk titik, berikut adalah dua array baru:
Kami akan menghitung penggunaan produk titik
np.einsum('ij,jk->ik', A, B)
. Berikut adalah gambar yang menunjukkan pelabelanA
danB
dan larik keluaran yang kita dapatkan dari fungsi:Anda dapat melihat label
j
diulangi - ini berarti kami mengalikan barisA
dengan kolomB
. Selanjutnya, labelj
tidak termasuk dalam output - kami menjumlahkan produk ini. Labeli
dank
disimpan untuk output, jadi kami kembali array 2D.Mungkin lebih jelas untuk membandingkan hasil ini dengan array di mana label
j
ini tidak dijumlahkan. Di bawah, di sebelah kiri Anda dapat melihat larik 3D yang dihasilkan dari penulisannp.einsum('ij,jk->ijk', A, B)
(yaitu, kami telah menyimpan labelj
):Sumbu penjumlahan
j
memberikan produk titik yang diharapkan, yang ditunjukkan di sebelah kanan.Beberapa latihan
Untuk mendapatkan lebih banyak rasa
einsum
, dapat berguna untuk mengimplementasikan operasi array NumPy yang sudah dikenal menggunakan notasi subskrip. Apa pun yang melibatkan kombinasi mengalikan dan menjumlahkan sumbu dapat ditulis menggunakaneinsum
.Misalkan A dan B menjadi dua array 1D dengan panjang yang sama. Sebagai contoh,
A = np.arange(10)
danB = np.arange(5, 15)
.Jumlahnya
A
dapat ditulis:Perkalian elemen-bijaksana
A * B
,, dapat ditulis:Produk dalam atau produk titik,
np.inner(A, B)
ataunp.dot(A, B)
, dapat ditulis:Produk luar
np.outer(A, B)
,, dapat ditulis:Untuk array 2D,
C
danD
, asalkan sumbu adalah panjang yang kompatibel (baik panjang yang sama atau salah satunya memiliki panjang 1), berikut adalah beberapa contoh:Jejak
C
(jumlah diagonal utama)np.trace(C)
,, dapat ditulis:Perkalian elemen-bijaksana
C
dan transpos dariD
,C * D.T
, dapat ditulis:Mengalikan setiap elemen
C
dengan arrayD
(untuk membuat array 4D)C[:, :, None, None] * D
,, dapat ditulis:sumber
ij,jk
dapat bekerja dengan sendirinya (tanpa panah) untuk membentuk perkalian matriks. Tapi sepertinya untuk kejelasan yang terbaik adalah menempatkan panah dan kemudian dimensi output. Ada dalam posting blog.A
panjangnya 3, sama dengan panjang kolom diB
(sedangkan barisB
memiliki panjang 4 dan tidak dapat dikalikan dengan elemenA
).->
semantik mempengaruhi: "Dalam mode implisit, subskrip yang dipilih penting karena sumbu output disusun ulang sesuai abjad. Ini berarti bahwanp.einsum('ij', a)
tidak mempengaruhi array 2D, sambilnp.einsum('ji', a)
mengambil transposinya."Memahami ide
numpy.einsum()
sangat mudah jika Anda memahaminya secara intuitif. Sebagai contoh, mari kita mulai dengan deskripsi sederhana yang melibatkan perkalian matriks .Untuk menggunakannya
numpy.einsum()
, yang harus Anda lakukan adalah meneruskan string subkrip yang disebut sebagai argumen, diikuti oleh array input Anda .Katakanlah Anda memiliki dua array 2D,
A
danB
, dan Anda ingin melakukan perkalian matriks. Jadi, Anda lakukan:Di sini string subskrip
ij
berhubungan dengan arrayA
sedangkan string subskripjk
berhubungan dengan arrayB
. Juga, hal yang paling penting untuk dicatat di sini adalah bahwa jumlah karakter dalam setiap string subskrip harus sesuai dengan dimensi array. (yaitu dua karakter untuk array 2D, tiga karakter untuk array 3D, dan sebagainya.) Dan jika Anda mengulangi karakter di antara string subskrip (j
dalam kasus kami), maka itu berarti Anda inginein
jumlah terjadi di sepanjang dimensi tersebut. Dengan demikian, jumlah tersebut akan dikurangi. (Yaitu dimensi itu akan hilang )The String subscript setelah ini
->
, akan array yang dihasilkan kami. Jika Anda membiarkannya kosong, maka semuanya akan dijumlahkan dan nilai skalar dikembalikan sebagai hasilnya. Lain array yang dihasilkan akan memiliki dimensi sesuai dengan string subskrip . Dalam contoh kita, itu akan menjadiik
. Ini intuitif karena kita tahu bahwa untuk perkalian matriks jumlah kolom dalam arrayA
harus cocok dengan jumlah baris dalam arrayB
yang merupakan apa yang terjadi di sini (yaitu kita menyandikan pengetahuan ini dengan mengulangi charj
dalam string subskrip )Berikut adalah beberapa contoh yang menggambarkan penggunaan / kekuatan
np.einsum()
dalam mengimplementasikan beberapa operasi tensor atau nd-array yang umum , secara ringkas.Input
1) Perkalian matriks (mirip dengan
np.matmul(arr1, arr2)
)2) Ekstrak elemen di sepanjang main-diagonal (mirip dengan
np.diag(arr)
)3) Produk Hadamard (yaitu produk elemen-bijaksana dari dua array) (mirip dengan
arr1 * arr2
)4) Elemen-bijaksana kuadrat (mirip dengan
np.square(arr)
atauarr ** 2
)5) Jejak (yaitu jumlah elemen main-diagonal) (mirip dengan
np.trace(arr)
)6) Matriks transpose (mirip dengan
np.transpose(arr)
)7) Produk Luar (dari vektor) (mirip dengan
np.outer(vec1, vec2)
)8) Produk Dalam (dari vektor) (mirip dengan
np.inner(vec1, vec2)
)9) Jumlah sepanjang sumbu 0 (mirip dengan
np.sum(arr, axis=0)
)10) Jumlahkan sepanjang sumbu 1 (mirip dengan
np.sum(arr, axis=1)
)11) Penggandaan Matriks Batch
12) Jumlah sepanjang sumbu 2 (mirip dengan
np.sum(arr, axis=2)
)13) Jumlahkan semua elemen dalam array (mirip dengan
np.sum(arr)
)14) Jumlah lebih dari beberapa sumbu (yaitu marginalisasi)
(mirip dengan
np.sum(arr, axis=(axis0, axis1, axis2, axis3, axis4, axis6, axis7))
)15) Produk Dot Ganda (mirip dengan np.sum (produk hadamard) lih. 3 )
16) penggandaan array 2D dan 3D
Penggandaan seperti itu bisa sangat berguna ketika menyelesaikan sistem persamaan linear ( Ax = b ) di mana Anda ingin memverifikasi hasilnya.
Sebaliknya, jika seseorang harus menggunakan
np.matmul()
verifikasi ini, kita harus melakukan beberapareshape
operasi untuk mencapai hasil yang sama seperti:Bonus : Baca lebih banyak matematika di sini: Einstein-Summation dan pasti di sini: Tensor-Notation
sumber
Mari kita membuat 2 array, dengan dimensi yang berbeda, tetapi kompatibel untuk menyoroti interaksi mereka
Perhitungan Anda, mengambil 'titik' (jumlah produk) dari (2,3) dengan (3,4) untuk menghasilkan array (4,2).
i
adalah redup pertamaA
, yang terakhirC
;k
yang terakhirB
, tanggal 1C
.j
'dikonsumsi' oleh penjumlahan.Ini sama dengan
np.dot(A,B).T
- ini adalah hasil akhir yang ditransformasikan.Untuk melihat lebih banyak tentang apa yang terjadi
j
, ubahC
langganan keijk
:Ini juga dapat diproduksi dengan:
Yaitu, tambahkan
k
dimensi ke akhirA
, dan ai
ke depanB
, menghasilkan array (2,3,4).0 + 4 + 16 = 20
,,9 + 28 + 55 = 92
dll; Jumlahkanj
dan transpos untuk mendapatkan hasil sebelumnya:sumber
Saya menemukan NumPy: Trik perdagangan (Bagian II) instruktif
Perhatikan ada tiga sumbu, i, j, k, dan j yang diulang (di sisi kiri).
i,j
mewakili baris dan kolom untuka
.j,k
untukb
.Untuk menghitung produk dan menyelaraskan
j
sumbu, kita perlu menambahkan sumbua
. (b
akan disiarkan di sepanjang (?) Sumbu pertama)j
tidak ada di sisi kanan sehingga kami menjumlahkanj
yang merupakan sumbu kedua dari array 3x3x3Akhirnya, indeks (alfabet) terbalik di sisi kanan sehingga kami transpos.
sumber
Saat membaca persamaan einsum, saya merasa paling membantu jika hanya mampu merebusnya hingga ke versi imperatifnya.
Mari kita mulai dengan pernyataan berikut (mengesankan):
Bekerja melalui tanda baca pertama-tama kita melihat bahwa kita memiliki dua gumpalan yang dipisahkan koma 4 huruf -
bhwi
danbhwj
, sebelum panah, dan gumpalan 3 huruf tunggalbij
setelahnya. Oleh karena itu, persamaan menghasilkan hasil tensor peringkat-3 dari dua input tensor peringkat-4.Sekarang, biarkan setiap huruf di setiap gumpalan menjadi nama variabel rentang. Posisi di mana huruf muncul di gumpalan adalah indeks sumbu yang berkisar di tensor itu. Penjumlahan imperatif yang menghasilkan setiap elemen C, oleh karena itu, harus dimulai dengan tiga bersarang untuk loop, satu untuk setiap indeks C.
Jadi, pada dasarnya, Anda memiliki
for
lingkaran untuk setiap indeks output C. Kami akan membiarkan rentang tidak ditentukan untuk saat ini.Selanjutnya kita melihat sisi kiri - apakah ada variabel rentang di sana yang tidak muncul di sisi kanan ? Dalam kasus kami - ya,
h
danw
. Tambahkanfor
loop bersarang bagian dalam untuk setiap variabel seperti:Di dalam loop terdalam kita sekarang memiliki semua indeks yang ditentukan, sehingga kita dapat menulis penjumlahan yang sebenarnya dan terjemahan selesai:
Jika Anda telah dapat mengikuti kode sejauh ini, selamat! Ini yang Anda butuhkan untuk dapat membaca persamaan einsum. Perhatikan khususnya bagaimana rumus einsum asli memetakan ke pernyataan penjumlahan akhir dalam cuplikan di atas. For-loop dan batas jangkauan hanya bulu dan pernyataan akhir adalah yang Anda benar-benar perlu memahami apa yang terjadi.
Demi kelengkapan, mari kita lihat cara menentukan rentang untuk setiap variabel rentang. Nah, rentang masing-masing variabel hanyalah panjang dimensi yang diindeks. Jelas, jika suatu variabel mengindeks lebih dari satu dimensi dalam satu atau lebih tensor, maka panjang masing-masing dimensi tersebut harus sama. Berikut kode di atas dengan rentang lengkap:
sumber
Saya pikir contoh paling sederhana adalah dalam dokumen tensorflow
Ada empat langkah untuk mengubah persamaan Anda menjadi notasi einsum. Mari kita ambil persamaan ini sebagai contoh
C[i,k] = sum_j A[i,j] * B[j,k]
ik = sum_j ij * jk
sum_j
istilah sebagaimana adanya. Kita mendapatkanik = ij * jk
*
dengan,
. Kita mendapatkanik = ij, jk
->
tanda. Kita mendapatkanij, jk -> ik
Einsum interpreter hanya menjalankan 4 langkah ini secara terbalik. Semua indeks yang hilang dalam hasil dijumlahkan.
Berikut adalah beberapa contoh dari dokumen
sumber