14 puan yazan GN⁺ 2025-05-16 | 4 yorum | WhatsApp'ta paylaş
  • Yazar, NumPy hakkındaki memnuniyetsizliğini konu alarak çeşitli örneklerle sorunları açıklıyor
  • Basit dizi işlemleri NumPy ile kolay olsa da, boyutlar arttıkça karmaşıklık ve kafa karışıklığı hızla büyüyor
  • Broadcasting ve gelişmiş indeksleme gibi NumPy tasarımı, açıklık ve soyutlama açısından yetersiz kalıyor
  • Eksenleri açıkça belirtmek yerine tahmin ve deneme-yanılmaya dayalı kod yazmak neredeyse zorunlu hale geliyor
  • Yazar, geliştirilmiş bir dizi dili için fikirler sunuyor ve somut alternatifleri bir sonraki yazıda tanıtmayı planlıyor

Giriş: NumPy'ye duyulan sevgi-nefret

  • Yazar, NumPy'yi uzun zamandır kullandığını ancak sınırları nedeniyle büyük hayal kırıklığı yaşadığını belirtiyor
  • NumPy, Python'da dizi işlemleri için vazgeçilmez ve etkili bir kütüphane
  • PyTorch gibi modern makine öğrenimi kütüphanelerinde de NumPy'ye benzer sorunlar bulunuyor

NumPy'nin kolay ve zor yanları

  • Temel doğrusal denklem çözümü gibi basit işlemler, açık ve zarif bir sözdizimiyle yapılabiliyor
  • Ancak dizi boyutu büyüdüğünde veya işlemler karmaşıklaştığında, for döngüsü olmadan toplu işleme ihtiyaç duyuluyor
  • Döngü kullanılamayan ortamlarda (GPU işlemleri gibi), alışılmadık vektörleştirme sözdizimi veya özel fonksiyon çağrı biçimleri gerekiyor
  • Ne var ki bu fonksiyonların doğru kullanım biçimi belirsiz ve yalnızca dokümantasyondan net biçimde anlaşılması zor
  • Gerçekte NumPy'nin linalg.solve fonksiyonunun yüksek boyutlu dizilerde nasıl doğru kullanılacağını kimsenin kesin biçimde bildiğini söylemek zor

NumPy'nin sorunları

  • NumPy, çok boyutlu dizilerin bir kısmına veya belirli eksenlerine işlem uygulamak için tutarlı bir kuram sunmuyor
  • Dizi boyutu 2 veya altındayken durum açıkken, 3 boyut ve sonrasında her dizide hangi eksenlerin işleneceği belirsizleşiyor
  • Boyutları açıkça eşlemek için None kullanımı, broadcasting, np.tensordot gibi karmaşık yöntemleri zorunlu kılıyor
  • Bu yaklaşım hata yapmayı kolaylaştırıyor, kod okunabilirliğini düşürüyor ve bug olasılığını artırıyor

Döngüler ve açıklık

  • Aslında döngülere izin verilse, çok daha kısa ve açık kod yazmak mümkün olabiliyor
  • Döngülü kod daha az şık görünebilir, ancak açıklık açısından büyük avantaj sağlıyor
  • Buna karşılık, dizi boyutu değiştiğinde transpose veya eksen sırasını tek tek düşünmek gerekiyor ve karmaşıklık artıyor

np.einsum: İstisnai derecede iyi bir fonksiyon

  • np.einsum, eksen adlarını belirtmeye izin veren esnek bir alan-özel dil sunduğu için güçlü
  • einsum, işlemin niyetini açık hale getiriyor ve iyi genellenerek karmaşık eksen işlemlerini açıkça ifade etmeyi sağlıyor
  • Ancak einsum benzeri yaklaşımın desteklediği işlemler yalnızca bazı işlemlerle sınırlı; örneğin linalg.solve için kullanılamıyor

Broadcasting'in sorunları

  • NumPy'nin temel hilelerinden biri olan broadcasting, boyutlar uyuşmadığında bunları otomatik olarak eşleştiren bir özellik
  • Basit durumlarda kullanışlı olsa da, gerçekte boyutları net biçimde anlamayı zorlaştırıyor ve hata örnekleri çok fazla
  • Broadcasting örtük olduğu için kodu okurken işlemin her seferinde nasıl çalıştığını yeniden kontrol etmek gerekiyor

İndekslemenin belirsizliği

  • NumPy'nin gelişmiş indekslemesi, dizi shape'ini öngörmeyi son derece zor ve belirsiz hale getiriyor
  • Farklı indeksleme kombinasyonlarına göre sonuç dizisinin shape'i değiştiğinden, pratik deneyim olmadan bunu tahmin etmek güç
  • İndeksleme kurallarını anlatan dokümanlar da uzun ve karmaşık olduğu için öğrenme maliyeti yüksek
  • Sadece basit indeksleme kullanmak isteseniz bile, bazı işlemlerde kaçınılmaz olarak gelişmiş indeksleme kullanmanız gerekiyor

NumPy fonksiyon tasarımının sınırları

  • Birçok NumPy fonksiyonu yalnızca belirli dizi shape'leri için optimize edilmiş durumda
  • Yüksek boyutlu diziler için ek axes argümanı, ayrı fonksiyon adları veya farklı teamüller kullanmak gerekiyor ve bu yapı fonksiyondan fonksiyona tutarlı değil
  • Bu, soyutlama ve yeniden kullanımın temel olduğu programlama ilkelerine ters düşüyor
  • Belirli bir sorunu çözen bir fonksiyon kullansanız bile, bunu farklı dizilere ve eksenlere yeniden uygulamak istediğinizde kodu baştan farklı şekilde yazmanız gerekebiliyor

Gerçek örnek: self-attention uygulaması

  • NumPy ile self-attention yazarken, döngü kullanımı daha açık olsa da vektörleştirme zorunlu tutulduğunda kod karmaşıklaşıyor
  • Multi-head attention gibi yüksek boyutlu işlemler gerektiğinde, einsum ile eksen dönüşümlerini birlikte kullanmak gerekiyor ve kod anlaşılmaz hale gelebiliyor

Sonuç ve alternatif

  • Yazar, NumPy'nin "diğer dizi dillerine kıyasla daha çok kötü yanı olsa da, piyasada bu kadar önemli hale gelmiş tek seçenek" olduğunu söylüyor
  • NumPy'nin çeşitli sorunlarını (broadcasting, indeksleme belirsizliği, fonksiyonlardaki tutarsızlık vb.) aşmak için geliştirilmiş bir dizi dili prototipi geliştirdiğini duyuruyor
  • Somut iyileştirme önerilerini (yeni dizi dili API'si) daha sonra ayrı bir yazıda tanıtmayı planlıyor

4 yorum

 
youn17 2025-05-16

Bu biraz Julia'nın neden ortaya çıktığıyla ilgili bir hikâye gibi görünüyor. Kütüphaneleri öğrenmek gerekiyor ama NumPy'nin birçok sorununu çözmesi açısından gerçekten çok cazip bir seçenek gibi duruyor.

 
ahwjdekf 2025-05-16

numpy'de vectorization'ı iyi kullanamazsanız performans mahvoluyor. Bunu hesaba katarak yazmak da stresli ve zor.

 
domino 2025-05-16

Biraz eski Python kütüphanelerinin hepsinde benzer sorunlar var gibi görünüyor.

 
GN⁺ 2025-05-16
Hacker News görüşleri
  • İlk örnekte yalnızca b'nin tipine bakıp dokümantasyona başvurunca okumak zor, ancak dönen shape ile ilgili açıklama olduğundan b vektörünün gerçekten matris biçiminde olup olmadığını, özellikle de K=1 durumunda, kontrol etmek gerekiyor
  • Dizinin boyut sayısı 2'yi aşıyorsa, NumPy dizilerine boyut adları ekleyen Xarray kullanılması öneriliyor; boyut eşleştirme ya da transpose işlemleri olmadan broadcasting/hizalama otomatik olduğu için bu tür sorunların çoğu çözülüyor. Xarray lineer cebir açısından NumPy'den daha zayıf ama kolayca NumPy'ye geri dönülebiliyor ve yalnızca yardımcı fonksiyonlar yazmak yeterli oluyor. Xarray kullanınca 3 boyut ve üzerindeki verilerle çalışırken üretkenlik ciddi biçimde artıyor
    • Xarray, Pandas ile NumPy'nin güçlü yanlarını birleştirmiş gibi hissettiriyor. da.sel(x=some_x).isel(t=-1).mean(["y", "z"]) gibi indeksleme kolay, boyut adlarına saygı duyulduğu için broadcasting de net oluyor. Birden fazla CRS içeren coğrafi verileri işlemede güçlü, Arviz ile birlikte de çok iyi çalıştığından Bayesyen analizde ek boyutları yönetmek kolay. Birden fazla diziyi tek bir dataset içinde toplayıp ortak koordinatları paylaşmak da mümkün; böylece ds.isel(t=-1) gibi bir işlem zaman ekseni olan tüm dizilere kolayca uygulanabiliyor
    • Xarray sayesinde temel NumPy kullanımı çok azaldı ve üretkenlik büyük ölçüde arttı
    • Tensorflow, Keras, Pytorch gibi framework'lerde buna benzer bir şey olup olmadığı merak ediliyor; daha önce bahsedilen türden şeyleri zor debug etmek zorunda kalındığı hatırlanıyor
    • Tanıtım için teşekkürler, kesinlikle denenecek; array[:, :, None] gibi sözdiziminden rahatsız olanın yalnızca kendisi olmadığına sevinilmiş
    • Biyosinyal alanında NeuroPype, NumPy üzerinde n-boyutlu tensörler için adlandırılmış eksen desteği sunuyor ve her eksen için per-element veri, örneğin kanal adı ya da konum gibi bilgiler saklayabiliyor
    • NumPy'nin eski Numeric ve Numarray kütüphanelerinden türetildiği dönem hatırlatılıyor; Numarray tarafının 20 yıl boyunca tezini sürdürüp sonunda fon alarak adını Xarray'e çevirdiği ve artık NumPy'yi yendiği hayal ediliyor, tabii bunun çoğu kurgu
  • Julia kullanmaya başlamanın nedenlerinden biri NumPy sözdiziminin aşırı zor olmasıydı. MATLAB'dan NumPy'ye geçince programlama becerisi sanki daha da kötüleşmiş, matematikten çok performans hileleri öğrenmeye zaman harcanmış. Julia'da hem vektörleştirme hem döngüler iyi çalıştığı için yalnızca kodun okunabilirliğine odaklanılabiliyor. Yazıdaki deneyim ve duyguların aynısı hissedilmiş. np.linalg.solve gibi şeylerin en hızlı yöntem olduğu varsayılarak herkesten buna uymasını bekleyen “kara kutu” yaklaşımının doğru olmadığı düşünülüyor; probleme özel kernel'ları doğrudan yazmanın daha iyi olduğu pek çok durum da var
    • Bunun nedeni Julia'nın bilimsel hesaplama için tasarlanmış bir dil olması, NumPy'nin ise bilimsel hesaplama için tasarlanmamış bir dilin üstüne zorla yerleştirilmiş bir kütüphane olması. Bir gün Julia'nın kazanıp ağ etkisi yüzünden Python kullananları özgürleştirmesi umuluyor
    • MATLAB da vektörleştirme olmadan döngü çalıştırıldığında Python kadar yavaş. Asıl büyük sorun Python'un yavaşlığı ve Julia'nın belirgin avantajları olsa da pratikte kullanım alanı keskin biçimde sınırlı. Python'da JIT hack'leri ortaya çıktı ama hâlâ eksik; Python'a alternatif çok gerekli
    • MATLAB gerçekten farklı mı? Döngülerin yavaş olması değişmiyor ve en hızlı olan yine \ operatörü gibi tamamen optimize edilmiş kara kutular
    • Fortran'ın modern sürümlerinde de Julia'daki gibi hem vektörleştirme hem döngüler hızlı çalışıyor; böylece yalnızca okunabilirliğe odaklanmak mümkün
  • Matlab ve Julia ile karşılaştırıldığında NumPy'ye yönelik şikâyetler şöyle özetleniyor: her fonksiyonun eksenle ilgili argümanları, adlandırması ve vektörleştirme sunma biçimi farklı; bir fonksiyonu başka bir eksene uygulamak isteyince kodu neredeyse baştan yazmak gerekiyor. Programlamanın temeli soyutlama ama NumPy bunu zorlaştırıyor. Matlab'da vektörleştirilmiş kod çoğunlukla olduğu gibi çalışır ya da değişiklik açıksa, NumPy'de sürekli dokümantasyona bakmak ve transpose/reshape gibi tip uydurma işlemleri yapmak gerekiyor; bu da tutarsız ve muğlak hissettiriyor
    • Matlab'ın 3 boyut ve üzeri dizilere desteği çok zayıf olduğundan, yazıda değinilen sorunlar aslında orada pek ortaya çıkmıyor
    • İkinci sorun için jax içindeki vmap denenebilir
    • 2x2 bir dizi için yazılmış belirli bir fonksiyonu 3x2x2 bir dizinin bazı parçalarına uygulamak istemenin slice ve squeeze gibi işlemlerle çözülebileceği, hatta sorunun kendisinin anlaşılması zor derecede muğlak olduğu söyleniyor
    • reshape ile de çözülebilir
  • NumPy'deki en kafa karıştırıcı noktalardan biri, hangi işlemlerin vektörleştirilmiş çalıştığının açık olmaması ve Julia'daki gibi nokta sözdizimiyle bunun belirtilememesi. Dönen tipler konusunda da birçok tuzak var. Örneğin poly1d nesnesi P, sağdan z0 ile çarpılırsa yine poly1d dönerken soldan z0*P biçiminde çarpıldığında yalnızca dizi dönüyor ve tip dönüşümü sessizce gerçekleşiyor. Bir ikinci dereceden polinomun leading coefficient değeri için hem P.coef[0] hem de P[2] kullanılabilmesi de kolayca kafa karıştırıyor. Resmî olarak poly1d “eski” bir API ve yeni kod için Polynomial sınıfı öneriliyor ama ortada fiilen bir deprecated uyarısı da yok. Bu tür tip dönüşümleri ve veri tipi uyumsuzlukları gibi mayınlar kütüphanenin her yerine dağılmış durumda ve debug sürecini kâbusa çeviriyor
  • Yazarın işaret ettiği noktalara katılındığı belirtiliyor. Matlab'dan NumPy'ye geçerken çok rahatsızlık yaşanmış, veri dilimleme de NumPy'de Matlab/Julia'ya göre daha uğraştırıcı gelmiş. Ama Matlab'ın toolbox lisans maliyetleri düşünülünce NumPy'nin kusurları tolere edilebiliyor. Yazıda gösterilen sorunlar çoğunlukla 2 boyutun üzerindeki tensörlerde ortaya çıkıyor ve NumPy başlangıçta matrislere odaklı olduğundan bu sınır anlaşılır bulunuyor. Torch gibi özel kütüphaneler daha iyi olabilir ama onlar da kolay değil. Sonuçta “NumPy diğer dizi dillerinin çoğundan biraz daha kötü ama elde başka da pek seçenek yok” gibi bir his oluşuyor
    • NumPy'nin en baştan beri n-boyutlu dizileri hedeflediği, numarray'in devamı niteliğinde olduğu ve yalnızca 2D ile sınırlı kalmadığı belirtiliyor
  • Python veri bilimi ekosistemindeki en büyük sorun, her şeyin standart dışı olması. On kadar kütüphane sanki dört farklı dil gibi birbirinden farklı davranıyor ve ortak olan tek şey neredeyse to_numpy() düzeyi. Sonuçta asıl problemi çözmeye harcanandan daha fazla zaman veri formatı dönüştürmeye gidiyor. Julia'nın da yalnızca artıları yok ama birimler, belirsizlikler vb. alanlarda farklı kütüphaneler arası entegrasyon iyi çalışıyor; Python'da ise sürekli bol miktarda boilerplate kod gerekiyor
    • array-api projesi, Python ekosisteminin genelinde dizi işleme API'sini standartlaştırmaya çalışıyor
    • R ise tam tersine dört ayrı sınıf sistemi yüzünden daha da karmaşık
  • İnsanların neden sage yerine numpy kullandığı merak ediliyor
  • Bazı sorunların numpysane ve gnuplotlib ile çözülebildiği söyleniyor. Bu ikili ortaya çıktıktan sonra NumPy her işte aktif biçimde kullanılmaya başlanmış; bunlar olmasa katlanılamayacak düzeyde olacağı düşünülüyor
    • numpysane sonuçta Python döngüsü; gerçek vektörleştirme değil
    • Tanıtım için teşekkür ediliyor; bu tür sorunlardan sık sık yakınıldığı hâlde böyle basit bir üst katman kütüphanesi olduğu akla gelmemiş
  • Vektörleştirilmiş multi-head attention için tüm matris çarpımları einsum içine alınmış ve optimize="optimal" ile matrix chain çarpım algoritması kullanılarak performans artırılmaya çalışılmış. Gerçekten de genel vektörleştirilmiş uygulamaya göre yaklaşık 2 kat hızlanma elde edilmiş, ancak şaşırtıcı biçimde döngü kullanan saf uygulama daha da hızlı çıkmış. Nedenini merak edenlerin koda bakması öneriliyor; tahmin olarak einsum içindeki cache coherency tarafında hâlâ iyileştirme payı olduğu düşünülüyor