14 पॉइंट द्वारा GN⁺ 2025-05-16 | 4 टिप्पणियां | WhatsApp पर शेयर करें
  • लेखक NumPy के प्रति अपनी असंतुष्टि पर कई उदाहरणों के साथ समस्याएँ समझाते हैं
  • साधारण array operations NumPy में आसान हैं, लेकिन dimensions बढ़ते ही जटिलता और भ्रम तेज़ी से बढ़ जाते हैं
  • Broadcasting और advanced indexing जैसी NumPy की डिज़ाइन में स्पष्टता और abstraction की कमी है
  • explicit रूप से axis निर्दिष्ट करने के बजाय, अनुमान और trial-and-error पर निर्भर कोड लिखना लगभग अनिवार्य हो जाता है
  • बेहतर array language के लिए कुछ विचार प्रस्तुत किए गए हैं, और ठोस विकल्प अगली पोस्ट में पेश किए जाएंगे

परिचय: NumPy के प्रति प्रेम और असंतोष

  • लेखक बताते हैं कि उन्होंने लंबे समय तक NumPy का उपयोग किया है, लेकिन उसकी सीमाओं से काफ़ी निराश भी हुए हैं
  • NumPy Python में array computation के लिए एक अनिवार्य और प्रभावशाली library है
  • PyTorch जैसी आधुनिक machine learning libraries में भी NumPy जैसी समस्याएँ मौजूद हैं

NumPy की आसान और कठिन बातें

  • बुनियादी operations, जैसे सरल linear equations हल करना, स्पष्ट और सुरुचिपूर्ण syntax के साथ किए जा सकते हैं
  • लेकिन जैसे ही array dimensions बढ़ते हैं या operations जटिल होते हैं, for loops के बिना batch processing की ज़रूरत पड़ती है
  • ऐसे environment में जहाँ loops का उपयोग नहीं किया जा सकता (जैसे GPU computation), अजीब vectorized syntax या विशेष function call patterns की आवश्यकता होती है
  • लेकिन इन functions का सटीक उपयोग अस्पष्ट होता है, और केवल documentation से भी उसे साफ़-साफ़ समझना कठिन है
  • वास्तव में, numpy का linalg.solve function high-dimensional arrays के मामले में सही तरह से कैसे इस्तेमाल किया जाए, इस पर किसी को भी पूरी तरह भरोसा होना मुश्किल है

NumPy की समस्याएँ

  • NumPy में multi-dimensional arrays के किसी हिस्से या किसी खास axis पर operations लागू करने के लिए एक सुसंगत सिद्धांत की कमी है
  • जब array dimensions 2 या उससे कम हों तो बात स्पष्ट रहती है, लेकिन 3 या उससे अधिक dimensions पर हर array में operation किन axes पर होना चाहिए, यह अस्पष्ट हो जाता है
  • dimensions को explicit रूप से मिलाने के लिए None का उपयोग, broadcasting, np.tensordot जैसी जटिल विधियाँ मजबूरन अपनानी पड़ती हैं
  • ये तरीके गलतियों की संभावना बढ़ाते हैं, code readability घटाते हैं, और bugs की आशंका बढ़ा देते हैं

लूप और स्पष्टता

  • वास्तव में यदि loops की अनुमति हो, तो और भी संक्षिप्त और स्पष्ट code लिखा जा सकता है
  • loop-based code कम परिष्कृत दिख सकता है, लेकिन स्पष्टता के लिहाज़ से इसका बड़ा लाभ है
  • इसके विपरीत, array dimensions बदलते ही transpose या axis order के बारे में बार-बार सोचना पड़ता है, जिससे जटिलता बढ़ती है

np.einsum: एक अपवादस्वरूप अच्छा function

  • np.einsum शक्तिशाली है क्योंकि यह axes के नाम निर्दिष्ट करने वाली एक लचीली domain-specific language देता है
  • einsum में operation का इरादा स्पष्ट रहता है और उसका generalization भी शानदार है, इसलिए जटिल axis operations को explicit रूप से लागू किया जा सकता है
  • लेकिन einsum जैसी शैली का support कुछ ही operations तक सीमित है; उदाहरण के लिए, इसे linalg.solve पर लागू नहीं किया जा सकता

Broadcasting की समस्याएँ

  • NumPy की मुख्य तरकीब broadcasting है, जो dimensions न मिलने पर उन्हें अपने-आप मिलाने की सुविधा देती है
  • सरल मामलों में यह सुविधाजनक है, लेकिन व्यवहार में यह dimensions को स्पष्ट रूप से समझना कठिन बना देती है और गलती के कई मामले पैदा करती है
  • broadcasting implicit होने के कारण code पढ़ते समय हर बार यह जाँचना पड़ता है कि operation वास्तव में कैसे काम करेगा

Indexing की अस्पष्टता

  • NumPy की advanced indexing में array shape का अनुमान लगाना बेहद कठिन और अस्पष्ट है
  • indexing के अलग-अलग संयोजनों के अनुसार result array का shape बदल जाता है, इसलिए वास्तविक अनुभव के बिना इसका अनुमान लगाना मुश्किल है
  • indexing rules समझाने वाला documentation भी लंबा और जटिल है, इसलिए इसे सीखने में काफ़ी समय लगता है
  • भले ही केवल simple indexing ही इस्तेमाल करनी हो, कुछ operations में advanced indexing से बचना संभव नहीं होता

NumPy function design की सीमाएँ

  • कई NumPy functions केवल कुछ खास array shapes के लिए optimized हैं
  • high-dimensional arrays के लिए अतिरिक्त axes arguments, अलग function names, या conventions का उपयोग करना पड़ता है, और यह function दर function सुसंगत नहीं है
  • यह abstraction और reuse जैसे मूल programming principles के विरुद्ध जाने वाली संरचना है
  • किसी विशेष समस्या को हल करने वाला function इस्तेमाल करने के बाद भी, अलग-अलग arrays और axes पर उसे दोबारा लागू करने के लिए अक्सर code को लगभग नए सिरे से लिखना पड़ता है

वास्तविक उदाहरण: self-attention implementation

  • NumPy में self-attention implementation लिखते समय, loops का उपयोग करने पर code स्पष्ट रहता है, लेकिन vectorization को मजबूरी बनाने पर code जटिल हो जाता है
  • multi-head attention जैसे high-dimensional operations की ज़रूरत होने पर einsum और axis transformation को मिलाकर उपयोग करना पड़ता है, जिससे code कठिन हो जाता है

निष्कर्ष और विकल्प

  • लेखक कहते हैं कि NumPy "दूसरी array languages की तुलना में कई मायनों में खराब है, लेकिन फिर भी बाज़ार में महत्वपूर्ण बन चुकी लगभग एकमात्र पसंद" है
  • NumPy की कई समस्याओं—जैसे broadcasting, indexing की अस्पष्टता, और functions की असंगति—को दूर करने के लिए उन्होंने एक बेहतर array language का prototype बनाया है
  • ठोस सुधार प्रस्ताव (नई array language API) को वे आगे एक अलग लेख में प्रस्तुत करने की योजना रखते हैं

4 टिप्पणियां

 
youn17 2025-05-16

यह सुनने में Julia के पैदा होने की वजह वाली कहानी जैसा लगता है। लाइब्रेरीज़ को सीखना तो पड़ता है, लेकिन NumPy की कई समस्याओं को हल कर देने के मामले में यह सचमुच एक बहुत आकर्षक विकल्प लगता है।

 
ahwjdekf 2025-05-16

अगर vectorization का सही इस्तेमाल न कर पाएं तो NumPy की performance बुरी तरह गिर जाती है। उन बातों को ध्यान में रखकर लिखना तनावपूर्ण भी है और मुश्किल भी।

 
domino 2025-05-16

लगता है कि थोड़ी पुरानी Python libraries में लगभग सबमें एक जैसी समस्या होती है।

 
GN⁺ 2025-05-16
Hacker News राय
  • पहले उदाहरण में सिर्फ़ b का type देखकर docs पढ़ना कठिन है, लेकिन returned shape का विवरण दिया गया है, इसलिए यह जाँचना ज़रूरी है कि b vector वास्तव में matrix रूप में है या नहीं, ख़ासकर K=1 के मामले में
  • अगर array के dimensions 2 से ज़्यादा हों, तो Numpy array में dimension names जोड़ने वाला Xarray इस्तेमाल करने की सिफारिश है; इसमें broadcasting/alignment अपने-आप हो जाता है, इसलिए dimension matching या transpose के बिना ऐसी ज़्यादातर समस्याएँ हल हो जाती हैं. Xarray linear algebra के मामले में NumPy से कमज़ोर है, लेकिन आसानी से फिर NumPy पर लौटा जा सकता है और बस कुछ helper functions बना लेने होते हैं. Xarray इस्तेमाल करने पर 3D या उससे अधिक डेटा सँभालते समय productivity काफ़ी बढ़ जाती है
    • Xarray, Pandas और NumPy के फ़ायदों का मिला-जुला रूप लगता है. da.sel(x=some_x).isel(t=-1).mean(["y", "z"]) जैसे indexing आसान हैं, और dimension names का सम्मान होने से broadcasting भी स्पष्ट रहता है. कई CRS वाले geospatial data को संभालने में इसकी ताकत है. Arviz के साथ भी इसका उपयोग शानदार है, इसलिए Bayesian analysis में अतिरिक्त dimensions संभालना आसान हो जाता है. कई arrays को एक dataset में बाँधकर common coordinates साझा किए जा सकते हैं, इसलिए ds.isel(t=-1) जैसा ऑपरेशन time axis वाले सभी arrays पर आसानी से चल जाता है
    • Xarray की वजह से शुरुआती स्तर का NumPy इस्तेमाल बहुत कम करना पड़ता है और productivity कहीं ज़्यादा बढ़ती है
    • जिज्ञासा है कि Tensorflow, Keras, Pytorch जैसे frameworks में भी ऐसा कुछ है या नहीं; पहले ऐसी बातों को debug करने में काफ़ी मुश्किल हुई थी
    • परिचय के लिए धन्यवाद, इसे ज़रूर आज़माने का इरादा है. array[:, :, None] जैसी syntax असुविधाजनक लगती थी; लगा था शायद सिर्फ़ मुझे ऐसा लगता है, इसलिए समान राय देखकर अच्छा लगा
    • biosignal क्षेत्र में NeuroPype, NumPy के ऊपर n-dimensional tensor के लिए named axes का support देता है और हर axis के लिए per-element data (जैसे channel names, position आदि) भी store कर सकता है
    • इससे वह समय याद आता है जब NumPy, पुराने Numeric और Numarray libraries से विकसित हो रहा था. कल्पना की जा सकती है कि Numarray पक्ष ने 20 साल तक अपनी बात जारी रखी, funding मिली, नाम बदलकर Xarray रखा, और अब NumPy को हरा दिया—बेशक यह ज़्यादातर काल्पनिक है
  • Julia इस्तेमाल करना शुरू करने का एक कारण यह था कि NumPy syntax बहुत कठिन लगी. MATLAB से NumPy पर आने के बाद लगा कि programming और कमज़ोर हो गई, और गणित से ज़्यादा समय performance tricks सीखने में गया. Julia में vectorization और loops दोनों अच्छे से चलते हैं, इसलिए सिर्फ़ code readability की चिंता करनी पड़ती है. लेख में वही अनुभव और भावनाएँ साफ़ महसूस हुईं. np.linalg.solve जैसी चीज़ को सबसे तेज़ मानकर हर हाल में उसी के अनुरूप लिखने वाली ‘black box’ approach सही नहीं लगती; कई कारणों से problem-specific kernels ख़ुद लिखना बेहतर हो सकता है
    • वजह यह है कि Julia, scientific computing के लिए डिज़ाइन की गई भाषा है, जबकि NumPy एक ऐसी भाषा के ऊपर ज़बरदस्ती चढ़ाई गई library है जो scientific computing के लिए बनी ही नहीं थी. उम्मीद है कि किसी दिन Julia जीते और network effects की वजह से Python इस्तेमाल करने वाले लोग मुक्त हों
    • MATLAB भी vectorization के बिना loops चलाने पर Python जितना ही धीमा है. असली बड़ी समस्या Python की धीमी गति है. Julia के फ़ायदे साफ़ हैं, लेकिन व्यवहार में उसका उपयोग-क्षेत्र बहुत सीमित है. Python में JIT hack जैसी चीज़ें आई हैं, लेकिन वे अब भी अधूरी हैं. Python के विकल्प की सख़्त ज़रूरत है
    • क्या MATLAB सच में अलग है? loops का धीमा होना तो वहीं है, और सबसे तेज़ चीज़ अब भी '\' operator जैसा पूरी तरह optimized black box ही है
    • Fortran के आधुनिक versions भी Julia की तरह vectorization और loops दोनों को तेज़ी से चलाते हैं, इसलिए ध्यान सिर्फ़ readability पर रखा जा सकता है
  • Matlab और Julia की तुलना में numpy को लेकर शिकायत यह है कि हर function में axis-संबंधी arguments, naming, और vectorization देने का तरीका अलग-अलग है, और किसी विशेष axis पर function लागू करना हो तो code लगभग पूरी तरह फिर से लिखना पड़ता है. programming का मूल सिद्धांत abstraction है, लेकिन NumPy इसे कठिन बनाता है. Matlab में vectorized code अक्सर लगभग वैसे ही चलता है या बदलाव स्पष्ट होते हैं, जबकि NumPy में हर बार docs खंगालनी पड़ती है और transpose/reshape जैसी type matching भी सुसंगत नहीं होती, इसलिए बात धुँधली लगती है
    • Matlab का 3D या उससे अधिक arrays के लिए support बहुत कमज़ोर है, इसलिए लेख में बताई गई समस्याएँ वहाँ उतनी पैदा ही नहीं होतीं
    • दूसरी समस्या के लिए jax का vmap आज़माना उपयोगी हो सकता है
    • 2x2 array के लिए कोई विशेष function लिखकर उसे 3x2x2 array के किसी हिस्से पर लागू करना slice और squeeze आदि से किया जा सकता है; यह समस्या इतनी अस्पष्ट है कि समझना ही कठिन लगता है
    • reshape से भी इसे संभाला जा सकता है
  • numpy में सबसे उलझाने वाली बात यह है कि कौन-से operations vectorized होकर चलेंगे, यह स्पष्ट नहीं होता, और Julia की तरह dot syntax से इसे साफ़-साफ़ बताया भी नहीं जा सकता. return types को लेकर भी बहुत से traps हैं. उदाहरण के लिए, poly1d object P को दाईं तरफ़ से z0 के साथ गुणा करें तो poly1d मिलता है, लेकिन बाईं तरफ़ से z0*P लिखें तो सिर्फ़ array लौटता है और type conversion चुपचाप हो जाता है. quadratic का leading coefficient भी P.coef[0] और P[2] दोनों तरीकों से लिया जा सकता है, जो भ्रम पैदा करता है. आधिकारिक रूप से poly1d एक ‘पुराना’ API है और नए code के लिए Polynomial class की सिफारिश की जाती है, लेकिन व्यवहार में deprecated warning भी नहीं मिलती. इस तरह के type conversions और datatype inconsistencies पूरी library में बिखरे हुए landmines जैसे हैं, जो debugging को दुःस्वप्न बना देते हैं
  • लेखक की बातों से सहमति है. Matlab से Numpy पर जाते समय काफ़ी असुविधा हुई, और data slicing भी Numpy में Matlab/Julia की तुलना में ज़्यादा असहज लगी. लेकिन Matlab के toolbox license cost को देखें तो Numpy की कमियाँ कुछ हद तक ढक जाती हैं. लेख में बताए गए मुद्दे ज़्यादातर 2D से अधिक tensors में आते हैं, और Numpy मूलतः matrix (2D) पर आधारित रहा है, इसलिए यह सीमा स्वाभाविक लगती है. Torch जैसी dedicated libraries बेहतर हो सकती हैं, लेकिन वे भी आसान नहीं हैं. आख़िरकार ऐसा लगता है कि “NumPy दूसरी array languages की तुलना में थोड़ा ज़्यादा खराब है, लेकिन इस्तेमाल करने के लिए और भी ज़्यादा कुछ उपलब्ध नहीं है”
    • NumPy शुरू से ही N-dimensional arrays को लक्ष्य बनाकर numarray की परंपरा में था, इसलिए यह सिर्फ़ 2D तक सीमित नहीं था
  • Python data science ecosystem की सबसे बड़ी समस्या यह है कि सब कुछ non-standard है. लगभग 10 libraries ऐसे बर्ताव करती हैं जैसे 4 अलग भाषाएँ हों, और बस to_numpy() जैसी कुछ चीज़ें ही थोड़ी एकरूप हैं. नतीजा यह कि समस्या हल करने से ज़्यादा समय data format conversion में चला जाता है. Julia भी सिर्फ़ फ़ायदों से भरी नहीं है, लेकिन units और uncertainty जैसी कई libraries के बीच उसका integration अच्छा है, जबकि Python में हमेशा बहुत boilerplate code लिखना पड़ता है
    • array-api project पूरे Python ecosystem में array manipulation API को standardize करने की कोशिश कर रहा है
    • R तो अपने 4 class systems की वजह से और भी जटिल है
  • समझ नहीं आता कि लोग sage की जगह numpy क्यों इस्तेमाल करते हैं
  • कुछ समस्याएँ numpysane और gnuplotlib इस्तेमाल करने से हल हो जाती हैं. इस संयोजन के बाद numpy को हर तरह के काम में सक्रिय रूप से इस्तेमाल करना शुरू किया; इनके बिना तो इसे इस्तेमाल करना लगभग असंभव लगता
    • numpysane आख़िरकार Python loop ही है, असली vectorization नहीं
    • परिचय के लिए धन्यवाद; इन समस्याओं पर अक्सर शिकायत की थी, लेकिन यह ख़याल ही नहीं आया कि कोई सरल higher-level library भी हो सकती है
  • vectorized multi-head attention के लिए सभी matrix multiplications को einsum में डालकर optimize="optimal" के साथ matrix chain multiplication algorithm इस्तेमाल कर performance बढ़ाने की कोशिश की. सचमुच सामान्य vectorized implementation की तुलना में लगभग 2x तेज़ी मिली, लेकिन हैरानी की बात यह रही कि loop-आधारित सीधी-सादी implementation उससे भी तेज़ निकली. वजह जानने वालों के लिए code देखना उपयोगी होगा. अनुमान है कि einsum के अंदर cache coherency में अभी और सुधार की गुंजाइश है