स्थानीय ग्रेडिएंट्स गुणा होते हैं
फॉरवर्ड पास
ANDREA-120M का फॉरवर्ड पास इनपुट को एक श्रृंखला ऑपरेशन्स के माध्यम से ले जाता है:
x = embed(token_ids) # टोकन एम्बेडिंग्स
for layer in 12_layers:
x = x + attn(LN(x)) # ध्यान उप-स्तर
x = x + mlp(LN(x)) # MLP उप-स्तर
logits = LN(x) @ embed.T # बंधी हुई आउटपुट प्रोजेक्शन
loss = cross_entropy(logits, targets)
प्रत्येक ऑपरेशन इनपुट टेंसर्स पढ़ता है और आउटपुट टेंसर्स उत्पन्न करता है। फॉरवर्ड पास एकल स्केलर में समाप्त होता है: इस बैच के लिए क्रॉस-एंट्रॉपी लॉस।
बैकवर्ड पास
ट्रेनिंग वजन को लॉस को कम करने की दिशा में अपडेट करता है। अपडेट दिशाएँ प्राप्त करने के लिए, इंजन को चाहिए:
मॉडल में हर सीखने योग्य W के लिए dL/dW
चेन रूल यह देता है। एक चेन loss = f(g(h(x))) के लिए:
dL/dx = (dL/df) * (df/dg) * (dg/dh) * (dh/dx)
प्रत्येक फैक्टर एक स्थानीय ग्रेडिएंट है: एक ऑपरेशन का आउटपुट उसके इनपुट में थोड़े बदलाव के साथ कैसे बदलता है। स्थानीय ग्रेडिएंट्स को ग्राफ के माध्यम से पीछे की ओर गुणा करके लॉस सिग्नल को हर वेट तक प्रचारित किया जाता है।
रिवर्स-मोड डिफरेंशिएशन
बैकप्रॉप ग्रेडिएंट्स को उलटे क्रम में गणना करता है: dL/dlogits = 1 से शुरू करके, फिर क्रॉस-एंट्रॉपी के माध्यम से पीछे चलते हुए, फिर आउटपुट प्रोजेक्शन, फिर लेयर नॉर्म, फिर बारह ट्रांसफॉर्मर ब्लॉक्स, फिर एम्बेडिंग्स। प्रत्येक चरण में, आने वाले ग्रेडिएंट को स्थानीय जेकोबियन से गुणा करें।
रिवर्स-मोड तब कुशल होता है जब आउटपुट एकल स्केलर (लॉस) हो & कई इनपुट्स हों (वेट्स)। एक बैकवर्ड पास मॉडल के हर वेट के लिए ग्रेडिएंट्स उत्पन्न करता है। फॉरवर्ड-मोड को प्रत्येक वेट के लिए एक पास की आवश्यकता होगी; ~120M वेट्स वाले ANDREA-120M के लिए, फॉरवर्ड-मोड असंभव है।
रिवर्स-मोड क्यों
हर फॉरवर्ड ऑप को एक बैकवर्ड जुड़वां मिलता है
जोड़ी बनाने की अनुशासन
microgpt_cuda.cu हर ऑपरेशन के लिए दो CUDA कर्नेल शिप करता है: एक जो फॉरवर्ड आउटपुट की गणना करता है, एक जो आउटपुट ग्रेडिएंट्स दिए इनपुट ग्रेडिएंट्स की गणना करता है। जोड़ी एक-से-एक है:
| आगे का kernel | पीछे का kernel | ऑपरेशन |
|---|---|---|
k_embed_fwd | k_embed_bwd | टोकन एम्बेडिंग लुकअप |
k_layernorm_fwd | k_layernorm_bwd | लेयर नॉर्मलाइजेशन |
k_attn_qkv_fwd | k_attn_qkv_bwd | Q, K, V प्रोजेक्शन |
k_attn_fwd | k_attn_bwd | स्केल्ड डॉट-प्रोडक्ट अटेंशन |
k_attn_out_fwd | k_attn_out_bwd | आउटपुट प्रोजेक्शन W_O |
k_mlp_fwd | k_mlp_bwd | MLP (GELU के साथ) |
k_residual_add | k_residual_add_bwd | अवशेष कनेक्शन |
k_loss_fwd | k_loss_bwd | क्रॉस-एंट्रॉपी लॉस |
आठ ऑपरेशन जोड़ियाँ पूर्ण ट्रांसफॉर्मर को कवर करती हैं। प्लस कुछ उपयोगिता कर्नेल: k_grad_norm_partial, k_grad_norm_final, k_grad_scale ग्रेडिएंट क्लिपिंग के लिए (गतिविधि 75 देखें)।
एक बैकवर्ड कर्नेल का कार्य
बाद की लेयर्स से आने वाले ग्रेडिएंट (grad_output) को दिए गए, एक बैकवर्ड कर्नेल गणना करता है:
1. grad_input: ऑपरेशन के इनपुट टेंसर के सापेक्ष ग्रेडिएंट। यह आगे पीछे की ओर पास किया जाता है।
2. grad_weight: ऑपरेशन में सीखने योग्य पैरामीटर्स के सापेक्ष ग्रेडिएंट। यह ऑप्टिमाइज़र स्टेट में जाता है।
दोनों को एक ही कर्नेल लॉन्च में कम्प्यूट किया जाता है। CUDA थ्रेड्स ग्रेडिएंट टेंसर के टाइल्स पर समानांतर में सहयोग करते हैं।
सहेजे गए टेंसर
पीछे की ओर कम्प्यूटेशन को अक्सर फॉरवर्ड पास से मानों की आवश्यकता होती है। उदाहरण के लिए, k_layernorm_bwd को फॉरवर्ड के दौरान कम्प्यूट किए गए मीन और वैरिएंस की जरूरत होती है; k_mlp_bwd को GELU प्री-एक्टिवेशन की जरूरत होती है। ट्रेनिंग इंजन इन्हें फॉरवर्ड के दौरान समर्पित बफर्स में स्टोर करता है, फिर पीछे की ओर के दौरान इन्हें पढ़ता है।
मेमोरी लागत: प्रत्येक सहेजे गए टेंसर के लिए फॉरवर्ड आउटपुट के समान आकार। ANDREA-120M के लिए batch=8, seq=1024, d_model=768 के साथ, एक सहेजा गया टेंसर 8 × 1024 × 768 × 4 bytes = 25 MB है। 12 लेयर्स और प्रति लेयर कई सहेजे गए टेंसर्स के पार, एक्टिवेशन्स ट्रेनिंग के दौरान VRAM को हावी करते हैं (~5-10 GB एक 24 GB कार्ड पर)।
एक बैकवर्ड स्टेप का ट्रेसिंग
मेमोरी में ग्रेडिएंट्स कहाँ रहते हैं
प्रत्येक वजन टेंसर के लिए एक ग्रेडिएंट टेंसर
ANDREA-120M में हर सीखने योग्य वजन टेंसर का समान आकार का एक मिलान करने वाला ग्रेडिएंट टेंसर होता है। प्रत्येक ब्लॉक के लिए:
W_Q [768, 768] ↔ grad_W_Q [768, 768]
W_K [768, 768] ↔ grad_W_K [768, 768]
W_V [768, 768] ↔ grad_W_V [768, 768]
W_O [768, 768] ↔ grad_W_O [768, 768]
W_1 [768, 3072] ↔ grad_W_1 [768, 3072]
W_2 [3072, 768] ↔ grad_W_2 [3072, 768]
LN1.gamma [768] ↔ grad_LN1.gamma [768]
LN1.beta [768] ↔ grad_LN1.beta [768]
LN2.gamma [768] ↔ grad_LN2.gamma [768]
LN2.beta [768] ↔ grad_LN2.beta [768]
प्लस टोकन एम्बेडिंग्स, पोजीशन एम्बेडिंग्स, और एक अंतिम लेयर नॉर्म। ग्रेडिएंट बफर का कुल मेमोरी वजन मेमोरी से मेल खाता है: ~120M फ्लोट्स, FP32 पर ~480 MB, FP16 पर ~240 MB।
माइक्रोबैचों में संचय
ANDREA का batch_size = 8 FP16 पर VRAM में फिट हो जाता है। बड़े प्रभावी बैचों के लिए ग्रेडिएंट संचय की आवश्यकता होती है: छोटे बैचों पर कई फॉरवर्ड+बैकवर्ड पास चलाएं, ग्रेडिएंट्स को उसी बफर में जोड़ें, फिर एक ऑप्टिमाइजर स्टेप लें।
for microbatch in range(n_microbatches):
forward(microbatch)
backward() # ग्रेड बफर्स में जोड़ता है, ओवरराइट नहीं करता
scale_grads(1.0 / n_microbatches) # माइक्रोबैचों के पार औसत
optimizer_step()
zero_grads() # अगले ट्रेनिंग स्टेप के लिए रीसेट करें
Backward kernels += semantics का उपयोग करते हैं, = का नहीं। प्रत्येक कॉल मौजूदा बफर में ग्रेडिएंट योगदान जोड़ता है; बफर रनिंग सम को होल्ड करता है जब तक zero_grads() इसे क्लियर न कर दे।
ऑप्टिमाइज़र स्टेट
AdamW (गतिविधि 73) प्रत्येक वजन के लिए दो और बफ़र्स रखता है: पहला क्षण m और दूसरा क्षण v। कुल ट्रेनिंग-टाइम मेमोरी:
वजन: 1× वजन संख्या
ग्रेडिएंट्स: 1× वजन संख्या
Adam m: 1× वजन संख्या
Adam v: 1× वजन गणना
सेव की गई क्रियाएँ: ~2-4× परतों और बैच के आधार पर
──────────────────────────────────────────
कुल: ~6-8× वजन गणना
ANDREA-120M FP16 पर: ~240 MB × 4 बफर (वजन, ग्रेडिएंट, m, v) + ~5-10 GB एक्टिवेशन = ~10-12 GB कुल। RTX 4090 के 24 GB की सीमा से आराम से नीचे। ANDREA-12M 1.4 GB में प्रशिक्षित; 10× पैरामीटर स्केलिंग ~10× मेमोरी लाती है।
ग्रेडिएंट बफर का आकार निर्धारण
मेमोरी & प्रेसिजन पर पूर्ण नियंत्रण
सामान्य फ्रेमवर्क्स की कीमत क्या है
PyTorch & JAX ऑटोग्रैड को सुविधाजनक बनाते हैं: Python कोड लिखें, ग्रेडिएंट्स स्वचालित रूप से प्राप्त करें। कीमत: आपके कोड & CUDA के बीच एक सामान्य डिस्पैच लेयर। हर ऑपरेशन Python इंटरप्रेटर ओवरहेड, फ्रेमवर्क बुककीपिंग, & डायनामिक कर्नेल चयन से गुजरता है। एक GPU पर छोटे भाषा मॉडल को ट्रेन करने के लिए, वह ओवरहेड मायने रखता है।
कंक्रीट लागतें जो ANDREA टालता है:
1. पायथन इंटरप्रेटर लेटेंसी। हर PyTorch ऑपरेशन पायथन/C++ की सीमा पार करता है। प्रति ट्रेनिंग स्टेप ~100 कर्नेल लॉन्च पर ~9 स्टेप्स/मिनट के हिसाब से, यह प्रति मिनट ~900 सीमा पारियाँ हैं। C-लेवल डिस्पैच इसे समाप्त करता है।
2. फ्रेमवर्क एलोकेटर की अप्रत्याशितता। PyTorch का कैशिंग एलोकेटर औसतन अच्छा थ्रूपुट देता है लेकिन अप्रत्याशित पीक मेमोरी। ANDREA का ट्रेनिंग इंजन स्टार्टअप पर हर बफर को पहले से एलोकेट करता है; ट्रेनिंग के दौरान कोई री-अलोकेशन नहीं, कोई फ्रैगमेंटेशन नहीं, स्टेप 100K पर कोई आश्चर्यजनक OOM नहीं।
3. जेनेरिक कर्नेल चयन। PyTorch रनटाइम पर हीयूरिस्टिक्स के माध्यम से कर्नेल चुनता है। ANDREA कंपाइल टाइम पर कर्नेल चुनता है, जो RTX 4090 टेंसर कोर टाइल साइज के लिए ट्यून किया गया है।
4. मिश्रित-सटीकता प्लंबिंग। ANDREA-120M का FP16 cuBLAS पथ और ANDREA के FP8 E4M3 टेंसर कोर प्रयोगों को इस बात पर सटीक नियंत्रण की आवश्यकता है कि कौन से टेंसर किस सटीकता पर रहते हैं। सामान्य फ्रेमवर्क इस नियंत्रण को लेयर्ड APIs के माध्यम से उजागर करते हैं; कस्टम CUDA इसे सीधे लिखता है।
समझौता
कस्टम CUDA की लागत: अधिक कोड लिखना, अधिक बग ढूंढना, कोई समुदाय पारिस्थितिकी तंत्र नहीं। ANDREA का microgpt_cuda.cu लगभग 6000 पंक्तियों का हस्तलिखित CUDA है जिसे डिबग करने में महीनों लगे। प्रत्येक नया ऑपरेशन के लिए फॉरवर्ड कर्नेल, बैकवर्ड कर्नेल, और टेस्ट लिखने पड़ते हैं।
ANDREA को क्या लाभ मिलता है:
- पूर्ण पुनरुत्पाद्यता। प्रशिक्षण पाइपलाइन एक C बाइनरी प्लस एक Python प्रॉक्सी है। PyTorch रिलीज़ों में कोई संस्करण विचलन नहीं, फ्रेमवर्क व्हील्स के साथ कोई CUDA संस्करण असंगति नहीं।
- बिट-सटीक रिज्यूम। SIGTERM चेकपॉइंट राइट ट्रिगर करता है जो हर टेंसर को ठीक वैसा ही कैप्चर करता है जैसा GPU देखता है। रिज्यूम उसी लॉस ट्रैजेक्टरी को उठाता है जिस पर रन था।
- पूर्वानुमानित मेमोरी। ANDREA-120M को 200K स्टेप्स के लिए प्रशिक्षित किया गया बिना किसी OOMs के। मेमोरी को इंजन स्टार्टअप पर ही हिसाब किया गया।
- प्रत्यक्ष हार्डवेयर एक्सेस। टेंसर कोर टाइल साइज़, FP8 E4M3 सेटिंग्स, असिंक्रोनस मेमोरी कॉपीज़: सभी CUDA में सीधे संबोधनीय, सामान्य फ्रेमवर्क्स में अपारदर्शी।
पुनरुत्पाद्यता को मिशन के रूप में
ANDREA व्हाइटपेपर अनुभाग 9 में पूर्ण पुनरुत्पाद्यता स्टैक सूचीबद्ध है:
**प्रशिक्षण इंजन:** microgpt/microgpt_cuda.cu
**प्रशिक्षण प्रॉक्सी:** microgpt/training_proxy.py
**प्रयोग कॉन्फ़िग्स:** experiments/ANDREA-*-TRAIN.json
**डेटा पाइपलाइन:** scripts/pull-hermes3.py, scripts/prep-megachat.py
**डैशबोर्ड:** scripts/live-loss-dashboard.html
**बैंडिट स्पेसिफिकेशन:** docs/FIREHOSE-BANDIT.md
**मॉडल दस्तावेज़ीकरण:** docs/ANDREA.md
हार्डवेयर आवश्यकता: एक NVIDIA GPU जिसमें ≥8 GB VRAM हो (RTX 3060 या बेहतर)। कोई भी इन आर्टिफैक्ट्स से ANDREA-12M को पुनरावृत्ति (reproduce) कर सकता है। कस्टम CUDA पथ इसी का हिस्सा है: कोई फ्रेमवर्क वर्शन फ्रीज नहीं, पांच साल बाद कोई डिपेंडेंसी सरप्राइज नहीं।
सिग्नल्स और चेकपॉइंट्स
CUDA ट्रेनिंग लूप दो POSIX सिग्नल्स का जवाब देता है:
- SIGTERM: तुरंत एक चेकपॉइंट लिखें, फिर बाहर निकलें। ट्रेनिंग को साफ़ तरीके से रोकते समय उपयोग किया जाता है।
- SIGUSR1: तत्काल चेकपॉइंट लिखें, प्रशिक्षण जारी रखें। v3 में पॉलिश पिवट के दौरान उपयोग किया गया ताकि रन को बाधित किए बिना स्टेट कैप्चर हो सके।
चेकपॉइंट फॉर्मेट: [int32 step][int32 n_params][n_params × float32 weights][n_params × float32 m][n_params × float32 v]. स्टेप काउंटर, वेट काउंट, फिर वेट्स उसके बाद Adam मोमेंट्स। बिट-एग्जैक्टली रिज्यूम करता है। प्रॉक्सी पॉलिश पर .samples.json & .state.json को अलग-अलग आर्काइव करता है; .loss.json कभी आर्काइव नहीं होता (यह पूर्ण प्रशिक्षण इतिहास जमा करता है)।