Mengapa mengekspresikan perhitungan sebagai perkalian matriks membuatnya lebih cepat?

18

Di Google tutorial MNist menggunakan TensorFlow , perhitungan diperlihatkan di mana satu langkah setara dengan mengalikan matriks dengan vektor. Google pertama kali menunjukkan gambar di mana setiap perkalian numerik dan penambahan yang akan digunakan untuk melakukan perhitungan dituliskan secara lengkap. Selanjutnya, mereka menunjukkan gambar yang sebaliknya dinyatakan sebagai perkalian matriks, mengklaim bahwa versi perhitungan ini, atau setidaknya mungkin, lebih cepat:

Jika kita menuliskannya sebagai persamaan, kita mendapatkan:

persamaan skalar

Kita dapat "membuat vektor" prosedur ini, mengubahnya menjadi perkalian matriks dan penambahan vektor. Ini bermanfaat untuk efisiensi komputasi. (Ini juga cara yang berguna untuk berpikir.)

persamaan vektor

Saya tahu bahwa persamaan seperti ini biasanya ditulis dalam format perkalian matriks oleh praktisi pembelajaran mesin, dan tentu saja dapat melihat keuntungan dari melakukannya dari sudut pandang kesederhanaan kode atau pemahaman matematika. Apa yang saya tidak mengerti adalah klaim Google bahwa mengkonversi dari bentuk tulisan tangan ke bentuk matriks "sangat membantu untuk efisiensi komputasi"

Kapan, mengapa, dan bagaimana mungkin untuk mendapatkan peningkatan kinerja dalam perangkat lunak dengan menyatakan perhitungan sebagai perkalian matriks? Jika saya menghitung perkalian matriks pada gambar kedua (berbasis matriks) sendiri, sebagai manusia, saya akan melakukannya dengan secara berurutan melakukan masing-masing perhitungan berbeda yang ditunjukkan pada gambar (skalar) pertama. Bagi saya, mereka hanyalah dua notasi untuk urutan perhitungan yang sama. Mengapa berbeda untuk komputer saya? Mengapa komputer dapat melakukan perhitungan matriks lebih cepat dari yang skalar?

Mark Amery
sumber

Jawaban:

19

Ini mungkin terdengar jelas, tetapi komputer tidak menjalankan formula , mereka mengeksekusi kode , dan berapa lama eksekusi itu tergantung langsung pada kode yang mereka jalankan dan hanya secara tidak langsung pada konsep apa pun yang diterapkan oleh kode tersebut. Dua potongan kode yang identik secara logis dapat memiliki karakteristik kinerja yang sangat berbeda. Beberapa alasan yang cenderung muncul dalam perkalian matriks secara khusus:

  • Menggunakan banyak utas. Hampir tidak ada CPU modern yang tidak memiliki banyak core, banyak yang memiliki hingga 8 core, dan mesin khusus untuk komputasi kinerja tinggi dapat dengan mudah memiliki 64 di beberapa soket. Menulis kode dengan cara yang jelas, dalam bahasa pemrograman normal, hanya menggunakan salah satunya . Dengan kata lain, mungkin menggunakan kurang dari 2% dari sumber daya komputasi yang tersedia dari mesin itu berjalan.
  • Menggunakan instruksi SIMD (membingungkan, ini juga disebut "vektorisasi" tetapi dalam arti yang berbeda dari kutipan teks dalam pertanyaan). Intinya, alih-alih 4 atau 8 atau lebih instruksi skalar skalar, berikan CPU satu instruksi yang melakukan aritmatika pada 4 atau 8 atau lebih register secara paralel. Ini benar-benar dapat membuat beberapa perhitungan (ketika mereka sangat independen dan cocok untuk set instruksi) 4 atau 8 kali lebih cepat.
  • Memanfaatkan cache dengan lebih cerdas . Akses memori lebih cepat jika mereka sementara dan spasial koheren , yaitu, akses berturut-turut ke alamat terdekat dan ketika mengakses alamat dua kali Anda mengaksesnya dua kali berturut-turut dengan cepat daripada dengan jeda panjang.
  • Menggunakan akselerator seperti GPU. Perangkat ini adalah binatang yang sangat berbeda dari CPU dan pemrograman mereka secara efisien adalah bentuk seni tersendiri. Misalnya, mereka memiliki ratusan inti, yang dikelompokkan ke dalam kelompok beberapa lusin inti, dan kelompok-kelompok ini berbagi sumber daya - mereka berbagi beberapa KiB memori yang jauh lebih cepat daripada memori normal, dan ketika setiap inti dari kelompok mengeksekusi ifpernyataan semua yang lain dalam grup itu harus menunggu.
  • Bagikan pekerjaan melalui beberapa mesin (sangat penting dalam superkomputer!) Yang memperkenalkan serangkaian besar sakit kepala baru, tetapi tentu saja dapat memberikan akses ke sumber daya komputasi yang jauh lebih besar.
  • Algoritma yang lebih cerdas. Untuk perkalian matriks, algoritma O (n ^ 3) yang sederhana, dioptimalkan dengan tepat dengan trik-trik di atas, seringkali lebih cepat daripada yang sub-kubik untuk ukuran matriks yang masuk akal, tetapi kadang-kadang mereka menang. Untuk kasus khusus seperti matriks jarang, Anda dapat menulis algoritma khusus.

Banyak orang pintar telah menulis kode yang sangat efisien untuk operasi aljabar linier umum , menggunakan trik di atas dan banyak lagi dan biasanya bahkan dengan trik bodoh platform-spesifik. Oleh karena itu, mengubah rumus Anda menjadi perkalian matriks dan kemudian menerapkan perhitungan dengan memanggil pustaka aljabar linier yang matang akan mendapat manfaat dari upaya pengoptimalan tersebut. Sebaliknya, jika Anda cukup menuliskan formula dengan cara yang jelas dalam bahasa tingkat tinggi, kode mesin yang pada akhirnya dihasilkan tidak akan menggunakan semua trik itu dan tidak akan secepat itu. Ini juga benar jika Anda mengambil formulasi matriks dan mengimplementasikannya dengan memanggil rutin perkalian matriks naif yang Anda tulis sendiri (sekali lagi, dengan cara yang jelas).

Membuat kode dengan cepat membutuhkan kerja , dan seringkali cukup banyak pekerjaan jika Anda ingin kinerja yang terakhir. Karena begitu banyak perhitungan penting dapat dinyatakan sebagai kombinasi dari beberapa operasi aljabar linier, adalah ekonomis untuk membuat kode yang sangat dioptimalkan untuk operasi ini. Kasus penggunaan khusus Anda yang sekali pakai? Tidak ada yang peduli tentang itu kecuali Anda, jadi mengoptimalkan hal itu tidak ekonomis.

Komunitas
sumber
4

(Jarang) Penggandaan matriks-vektor sangat paralel. Yang sangat berguna jika data Anda besar dan Anda memiliki server farm yang Anda inginkan.

Ini berarti bahwa Anda dapat membagi matriks dan vektor menjadi potongan-potongan dan membiarkan mesin yang terpisah melakukan beberapa pekerjaan. Kemudian bagikan beberapa hasil mereka satu sama lain dan kemudian dapatkan hasil akhirnya.

Dalam contoh Anda, operasinya adalah sebagai berikut

  1. setup grid prosesor masing-masing memegang Wx, y sesuai dengan koordinat mereka di grid

  2. siaran vektor sumber di sepanjang setiap kolom (biaya O(log height))

  3. mintalah masing-masing prosesor untuk perkalian secara lokal (biaya O(width of submatrix * heightof submatrix))

  4. tutup hasil di sepanjang setiap baris menggunakan jumlah (biaya O(log width))

Operasi terakhir ini valid karena jumlahnya adalah asosiatif.

Ini juga memungkinkan bangunan dalam redundansi dan memungkinkan Anda menghindari keharusan memasukkan semua informasi ke dalam satu mesin.

Untuk matriks 4x4 kecil seperti yang Anda lihat dalam grafik, itu karena CPU memiliki instruksi khusus dan register untuk menangani operasi tersebut.

ratchet freak
sumber