14 poin oleh GN⁺ 2025-05-16 | 4 komentar | Bagikan ke WhatsApp
  • Penulis menjelaskan berbagai masalah pada NumPy beserta beberapa contohnya
  • Operasi array sederhana memang mudah dengan NumPy, tetapi ketika jumlah dimensinya bertambah, kompleksitas dan kebingungan meningkat drastis
  • Desain NumPy seperti broadcasting dan advanced indexing kurang memadai dari sisi kejelasan dan abstraksi
  • Menulis kode yang bergantung pada tebakan dan trial-and-error menjadi keharusan, alih-alih menentukan axis secara eksplisit
  • Penulis mengajukan ide untuk bahasa array yang lebih baik dan berencana memperkenalkan alternatif konkretnya di tulisan berikutnya

Pendahuluan: hubungan cinta-benci dengan NumPy

  • Penulis menyatakan telah lama menggunakan NumPy, tetapi juga sangat kecewa dengan berbagai keterbatasannya
  • NumPy adalah library penting dan berpengaruh untuk operasi array di Python
  • Masalah serupa dengan NumPy juga ada di library machine learning modern seperti PyTorch

Hal yang mudah dan sulit di NumPy

  • Operasi sederhana seperti menyelesaikan persamaan linear dasar dapat dilakukan dengan sintaks yang jelas dan elegan
  • Namun, ketika dimensi array bertambah atau operasinya menjadi lebih kompleks, pemrosesan batch tanpa for loop pun diperlukan
  • Di lingkungan yang tidak memungkinkan penggunaan loop langsung, seperti komputasi GPU, dibutuhkan sintaks vektorisasi yang aneh atau cara pemanggilan fungsi yang khusus
  • Namun, cara penggunaan yang tepat dari fungsi-fungsi ini sering kali ambigu dan sulit dipahami dengan jelas hanya dari dokumentasinya
  • Dalam praktiknya, untuk fungsi linalg.solve milik NumPy, sulit bagi siapa pun untuk benar-benar yakin bagaimana cara menggunakannya dengan benar pada array berdimensi tinggi

Masalah pada NumPy

  • NumPy tidak memiliki teori yang konsisten untuk menerapkan operasi pada sebagian atau axis tertentu dari array multidimensi
  • Ketika dimensi array 2 atau kurang, semuanya jelas, tetapi pada 3 dimensi atau lebih, tidak jelas axis mana yang menjadi target operasi pada tiap array
  • Pengguna dipaksa memakai cara-cara rumit seperti None, broadcasting, atau np.tensordot untuk menyesuaikan dimensi secara eksplisit
  • Pendekatan seperti ini memicu kesalahan, menurunkan keterbacaan kode, dan meningkatkan kemungkinan bug

Perulangan dan kejelasan

  • Jika perulangan benar-benar diizinkan, penulisan kode yang jauh lebih ringkas dan jelas justru menjadi mungkin
  • Kode berbasis perulangan mungkin terlihat kurang anggun, tetapi memiliki keunggulan besar dari sisi kejelasan
  • Sebaliknya, ketika dimensi array berubah, kita harus memikirkan transpose atau urutan axis satu per satu, sehingga kompleksitas bertambah

np.einsum: fungsi yang bagus sebagai pengecualian

  • np.einsum sangat kuat karena menyediakan bahasa khusus domain yang fleksibel untuk memberi nama pada axis
  • Einsum membuat maksud operasi menjadi jelas dan juga sangat mudah digeneralisasi, sehingga operasi axis yang kompleks dapat diimplementasikan secara eksplisit
  • Namun, dukungan operasi dengan gaya seperti einsum hanya tersedia untuk sebagian operasi, dan misalnya tidak bisa dipakai untuk linalg.solve

Masalah pada broadcasting

  • Broadcasting, trik inti NumPy, adalah fitur yang secara otomatis menyesuaikan dimensi ketika tidak cocok
  • Pada kasus sederhana memang nyaman, tetapi dalam praktiknya justru menyulitkan pemahaman dimensi secara jelas dan sering menimbulkan kesalahan
  • Karena broadcasting bersifat implisit, saat membaca kode kita harus terus memeriksa bagaimana operasi itu sebenarnya bekerja

Ketidakjelasan indexing

  • Advanced indexing di NumPy membuat prediksi shape array menjadi sangat sulit dan tidak jelas
  • Shape array hasil dapat berubah tergantung kombinasi indexing yang digunakan, sehingga sulit diprediksi tanpa pengalaman langsung menanganinya
  • Dokumentasi yang menjelaskan aturan indexing juga panjang dan kompleks, sehingga memakan banyak waktu untuk dipelajari
  • Bahkan jika ingin memakai indexing sederhana saja, pada operasi tertentu kita tetap terpaksa menggunakan advanced indexing

Keterbatasan desain fungsi NumPy

  • Banyak fungsi NumPy dioptimalkan hanya untuk shape array tertentu
  • Untuk array berdimensi tinggi, kita harus memakai argumen axes, nama fungsi terpisah, atau konvensi khusus tambahan, dan semuanya tidak konsisten antar fungsi
  • Struktur ini bertentangan dengan prinsip pemrograman yang menjadikan abstraksi dan penggunaan ulang sebagai dasar
  • Bahkan setelah memakai fungsi untuk menyelesaikan masalah tertentu, jika ingin menerapkannya lagi ke berbagai array dan axis, kita sering harus menulis ulang kodenya dengan cara yang sama sekali berbeda

Contoh nyata: implementasi self-attention

  • Saat menulis implementasi self-attention dengan NumPy, penggunaan perulangan membuatnya jelas, tetapi pemaksaan vektorisasi justru membuat kode menjadi rumit
  • Ketika dibutuhkan operasi berdimensi tinggi seperti multi-head attention, kita harus menggabungkan einsum dan transformasi axis, sehingga kodenya menjadi sulit dipahami

Kesimpulan dan alternatif

  • Penulis menyatakan bahwa NumPy adalah "satu-satunya pilihan yang menjadi sepenting ini di pasar, meski memiliki lebih banyak kelemahan dibanding bahasa array lain"
  • Untuk mengatasi berbagai masalah NumPy seperti broadcasting, ketidakjelasan indexing, dan inkonsistensi fungsi, penulis mengisyaratkan bahwa ia telah membuat prototipe bahasa array yang ditingkatkan
  • Usulan perbaikan yang konkret, yaitu API bahasa array baru, rencananya akan diperkenalkan dalam tulisan terpisah di masa mendatang

4 komentar

 
youn17 2025-05-16

Ini terasa seperti cerita tentang mengapa Julia lahir. Meski perlu mempelajari berbagai library, ini tampaknya benar-benar pilihan yang menarik karena mampu menyelesaikan banyak masalah di NumPy.

 
ahwjdekf 2025-05-16

Kalau tidak bisa memanfaatkan vectorization di NumPy dengan baik, performanya jadi berantakan. Menulis dengan mempertimbangkan hal-hal seperti itu bikin stres dan sulit.

 
domino 2025-05-16

Sepertinya banyak library Python yang sudah agak lama punya masalah yang mirip.

 
GN⁺ 2025-05-16
Komentar Hacker News
  • Pada contoh pertama, jika hanya melihat tipe b lalu membaca dokumentasinya, ini sulit dipahami, tetapi karena ada penjelasan tentang shape yang dikembalikan, perlu dicek apakah vektor b sebenarnya berbentuk matriks, terutama saat K=1
  • Jika dimensi array lebih dari 2, disarankan memakai Xarray yang menambahkan nama dimensi ke array NumPy; sebagian besar masalah seperti ini terselesaikan karena broadcasting/perataan berjalan otomatis tanpa perlu mencocokkan dimensi atau melakukan transpose, Xarray memang lebih lemah daripada NumPy dari sisi aljabar linear tetapi mudah kembali ke NumPy dan cukup membuat fungsi pembantu, dengan Xarray produktivitas sangat meningkat saat menangani data 3 dimensi atau lebih
    • Xarray terasa seperti gabungan kelebihan Pandas dan NumPy, indexing seperti da.sel(x=some_x).isel(t=-1).mean(["y", "z"]) mudah dilakukan, broadcasting juga jelas karena nama dimensi dihormati, dan kuat untuk memproses data geospasial dengan beberapa CRS, pemakaiannya bersama Arviz juga sangat baik sehingga penanganan dimensi tambahan dalam analisis Bayesian menjadi mudah, beberapa array juga bisa diikat dalam satu dataset dan berbagi koordinat yang sama sehingga bisa dengan mudah diterapkan ke semua array yang memiliki sumbu waktu seperti ds.isel(t=-1)
    • Berkat Xarray, penggunaan NumPy yang masih mendasar jadi jauh berkurang dan produktivitas meningkat pesat
    • Saya penasaran apakah framework seperti Tensorflow, Keras, dan Pytorch punya hal serupa, saya ingat pernah susah payah men-debug hal yang disebut tadi
    • Terima kasih atas perkenalannya, saya pasti akan mencobanya, saya kira hanya saya yang merasa sintaks seperti array[:, :, None] tidak nyaman, senang ada yang sependapat
    • Di bidang biosignal, NeuroPype di atas NumPy mendukung named axes untuk tensor n-dimensi dan juga bisa menyimpan data per-elemen untuk tiap sumbu (nama kanal, posisi, dan lain-lain)
    • Ini mengingatkan pada masa NumPy diturunkan dari pustaka Numeric dan Numarray, saya membayangkan kubu Numarray terus mempertahankan argumennya selama 20 tahun lalu mendapat pendanaan, berganti nama menjadi Xarray, dan akhirnya mengalahkan NumPy (tentu saja sebagian besar ini fiksi)
  • Salah satu alasan saya mulai memakai Julia adalah karena sintaks NumPy terlalu sulit, saat berpindah dari MATLAB ke NumPy saya justru merasa kemampuan pemrograman memburuk dan menghabiskan waktu mempelajari trik performa alih-alih matematika, di Julia baik vektorisasi maupun loop sama-sama bekerja dengan baik sehingga saya hanya perlu fokus pada keterbacaan kode, saya bisa merasakan pengalaman dan emosi itu persis dari tulisan tersebut, saya juga tidak menganggap pendekatan “kotak hitam” seperti memaksa memakai np.linalg.solve karena dianggap paling cepat itu benar, ada banyak alasan mengapa menulis kernel khusus masalah secara langsung bisa lebih baik
    • Penyebabnya adalah Julia memang bahasa yang dirancang untuk komputasi ilmiah, sedangkan NumPy adalah pustaka yang dipaksakan berdiri di atas bahasa yang bukan untuk komputasi ilmiah, semoga suatu hari Julia menang dan orang-orang yang memakai Python karena efek jaringan bisa terbebas
    • MATLAB juga sama lambatnya dengan Python jika menjalankan loop tanpa vektorisasi, masalah terbesar adalah lambatnya Python, Julia jelas punya kelebihan tetapi dalam praktiknya tetap hanya bisa dipakai untuk kegunaan yang cukup terbatas, Python sudah punya hal seperti hack JIT tetapi masih belum lengkap, kebutuhan akan alternatif Python sangat mendesak
    • Apakah MATLAB benar-benar berbeda? Loop tetap lambat, dan yang paling cepat tetap “kotak hitam” yang sepenuhnya dioptimalkan seperti operator '\\'
    • Fortran versi modern juga, seperti Julia, membuat vektorisasi dan loop sama-sama cepat sehingga bisa fokus sepenuhnya pada keterbacaan
  • Jika keluhan terhadap numpy dibanding Matlab dan Julia diringkas, intinya adalah bahwa argumen terkait sumbu, penamaan, dan cara penyediaan vektorisasi berbeda-beda di setiap fungsi, dan jika ingin menerapkan fungsi pada sumbu tertentu maka kodenya harus ditulis ulang sepenuhnya, padahal dasar pemrograman adalah abstraksi dan NumPy justru mempersulit itu, di Matlab kode vektorisasi biasanya tetap berjalan hampir tanpa perubahan atau modifikasinya jelas, tetapi di NumPy kita selalu harus membongkar dokumentasi dan pencocokan tipe seperti transpose/reshape juga tidak konsisten sehingga terasa samar
    • Dukungan Matlab untuk array 3 dimensi ke atas terlalu lemah sehingga justru masalah yang disebut di tulisan itu tidak terlalu sering muncul
    • Untuk masalah kedua, jax vmap layak dicoba
    • Keinginan menulis fungsi untuk array 2x2 lalu menerapkannya pada sebagian dari array 3x2x2 bisa dilakukan dengan slice dan squeeze, masalah ini sendiri terasa terlalu samar sampai sulit dipahami
    • Itu bisa ditangani dengan reshape
  • Hal paling membingungkan di numpy adalah tidak jelas operasi mana yang berjalan secara tervectorisasi, dan tidak bisa dinyatakan eksplisit dengan sintaks dot seperti di Julia, ada juga banyak jebakan terkait tipe nilai balik, misalnya untuk objek poly1d P, jika dikalikan dari kanan dengan z0 hasilnya poly1d, tetapi jika dikalikan dari kiri dalam bentuk z0*P yang dikembalikan hanya array sehingga konversi tipe terjadi diam-diam, koefisien terdepan dari quadratic juga bisa diakses dengan dua cara P.coef[0] dan P[2] sehingga mudah membingungkan, secara resmi poly1d adalah API “lama” dan kode baru disarankan memakai kelas Polynomial, tetapi nyatanya bahkan tidak ada peringatan deprecated, seperti konversi tipe dan ketidakcocokan datatype ini, ada ranjau di seluruh pustaka sehingga debugging jadi mimpi buruk
  • Saya setuju dengan hal-hal yang ditunjukkan penulis, saat berpindah dari Matlab ke Numpy ada banyak ketidaknyamanan, dan slicing data juga terasa lebih tidak nyaman di Numpy dibanding Matlab/Julia, tetapi jika mempertimbangkan biaya lisensi toolbox Matlab, kekurangan Numpy masih tertutupi, masalah yang disajikan di tulisan itu terutama muncul pada tensor dengan lebih dari 2 dimensi dan Numpy memang pada dasarnya berbasis matriks (2D) sehingga keterbatasan itu wajar, pustaka khusus seperti Torch lebih baik tetapi itu pun tidak mudah, pada akhirnya rasanya jawaban yang tepat adalah “NumPy sedikit lebih buruk daripada bahasa array lain mana pun, tetapi juga hampir tidak ada hal lain yang benar-benar bisa dipakai”
    • NumPy sejak awal memang menargetkan array N-dimensi sebagai kelanjutan dari numarray, jadi bukan hanya berhenti di 2D
  • Masalah terbesar ekosistem data science Python adalah semuanya tidak standar, sekitar belasan pustaka masing-masing bekerja seberbeda empat bahasa berbeda, dan yang paling seragam hanya to_numpy(), akibatnya waktu yang dihabiskan untuk konversi format data lebih banyak daripada waktu untuk menyelesaikan masalah, Julia juga bukan tanpa kekurangan tetapi integrasi antarpustaka untuk hal-hal seperti satuan dan ketidakpastian berjalan baik, sedangkan di Python selalu perlu banyak boilerplate code
    • Proyek array-api sedang berupaya menstandarkan API manipulasi array di seluruh ekosistem Python
    • R malah lebih rumit lagi karena memiliki 4 sistem kelas
  • Saya penasaran mengapa orang memakai numpy alih-alih sage
  • Sebagian masalah ini bisa diatasi dengan numpysane dan gnuplotlib, setelah kombinasi ini ada saya aktif memakai numpy untuk semua pekerjaan, tanpa itu rasanya benar-benar tidak bisa dipakai
    • numpysane pada akhirnya tetap loop Python, jadi bukan vektorisasi yang sesungguhnya
    • Terima kasih atas perkenalannya, saya juga sering menggerutu soal masalah seperti ini jadi tidak terpikir bahwa ada pustaka lapisan atas yang sederhana
  • Untuk vectorized multi-head attention, saya memasukkan semua perkalian matriks ke dalam einsum dan memakai algoritma perkalian rantai matriks dengan optimize="optimal" untuk meningkatkan performa, memang menjadi sekitar 2x lebih cepat dibanding implementasi vektorisasi umum, tetapi mengejutkannya implementasi naif berbasis loop justru lebih cepat, bagi yang penasaran alasannya silakan lihat kodenya, saya menduga masih ada ruang untuk perbaikan pada cache coherency di dalam einsum