एक LLM बनाएँएडवांस्डपाठ 467 मिनट का पाठ

स्टेप 10 · इसे तेज़ बनाना

वही मॉडल, वही GPUs, तीन गुना थ्रूपुट। ज़्यादातर ट्रेनिंग रन इसका फ़ायदा उठाए बिना ही छूट जाते हैं।

पाठ वीडियो में

60 सेकंड में

स्टेप 10 · इसे तेज़ बनाना

वही मॉडल, वही GPUs, तीन गुना थ्रूपुट। ज़्यादातर ट्रेनिंग रन इसका फ़ायदा उठाए बिना ही छूट जाते हैं।

1/5
आसान शब्दों में
एक ही किचन में दो रसोइये: एक फ्रिज तक आने-जाने में ही आधा दिन बर्बाद कर देता है। यहाँ स्पीड का मतलब बस यही है कि बार-बार फ्रिज तक न जाना पड़े।
मॉडर्न GPUs डेटा मूव करने की तुलना में हिसाब-किताब (arithmetic) बहुत तेज़ी से कर सकते हैं। नीचे दिया गया लगभग हर ऑप्टिमाइज़ेशन असल में कम डेटा मूव करने के बारे में ही है।

रैंक की गई लिस्ट

तकनीकआमतौर पर मिलने वाला फ़ायदामेहनत (Effort)
bfloat16 मिक्स्ड प्रिसिजन2–3xएक लाइन
torch.compile1.3–2xएक लाइन
FlashAttentionलंबे कॉन्टेक्स्ट पर 1.5–3xscaled_dot_product_attention के ज़रिए मुफ़्त
फ्यूज्ड ऑप्टिमाइज़र5–10%एक आर्ग्यूमेंट
ग्रेडिएंट चेकपॉइंटिंगबड़े बैच संभव बनाता हैएक लाइन, ~30% कंप्यूट का ख़र्च
सीक्वेंस पैकिंगअलग-अलग लंबाई वाले डेटा पर 10–40%मध्यम
बेहतर डेटा लोडिंगकभी-कभी 2xपहले से टोकनाइज़ (Pre-tokenise) और मेमोरी-मैप करें
नए हार्डवेयर पर FP81.3–1.8xकाफ़ी सावधानी की ज़रूरत

FlashAttention इतना अहम क्यों है

एक सीधा-सादा (naive) इम्प्लीमेंटेशन मेमोरी में पूरा सीक्वेंस-दर-सीक्वेंस अटेंशन मैट्रिक्स बनाता है। 32 हेड्स के साथ 8k कॉन्टेक्स्ट पर इसका मतलब है अरबों नंबर्स को मेमोरी में लिखना और तुरंत वापस पढ़ना।
FlashAttention उस मैट्रिक्स को कभी पूरी तरह मेमोरी में नहीं बनाता। यह अटेंशन को ऐसे टाइल्स में प्रोसेस करता है जो तेज़ ऑन-चिप मेमोरी में ही रहते हैं, और सॉफ्टमैक्स की गणना धीरे-धीरे (incrementally) करता है। गणितीय नतीजा वही रहता है, मेमोरी का आना-जाना बहुत कम हो जाता है, और मेमोरी का इस्तेमाल लंबाई के साथ लीनियरली (linearly) बढ़ता है, न कि चौगुना (quadratically)।
NAIVEBuild full T x T score matrixwriteSlow HBM memoryread backmemory grows with T squaredFLASHATTENTIONTile the computationstays on chipFast SRAM · never spillsmemory grows with Tidentical result, far less traffic
यह एक सटीक एल्गोरिदम है, कोई अंदाज़ा (approximation) नहीं। इसका आउटपुट सीधे-सादे वर्शन से बिट-टू-बिट मेल खाता है। यह सिर्फ़ मेमोरी एक्सेस करने का एक बेहतर तरीक़ा है — यही वजह है कि कुछ ही महीनों में इसे हर जगह अपना लिया गया।

सीक्वेंस पैकिंग

अगर आपके उदाहरणों की लंबाई अलग-अलग है और आप सबसे लंबे उदाहरण के हिसाब से पैडिंग (padding) करते हैं, तो हो सकता है कि आप अपना 40% कंप्यूट सिर्फ़ पैडिंग पर बर्बाद कर रहे हों। पैकिंग छोटे उदाहरणों को जोड़कर पूरी लंबाई वाले सीक्वेंस बनाती है और साथ में एक अटेंशन मास्क लगाती है ताकि वे एक-दूसरे की सीमा पार न कर सकें।
Pack examples into full sequencespython
def pack(sequences, block_size, eos_id):
    buf, out = [], []
    for seq in sequences:
        buf.extend(seq + [eos_id])
        while len(buf) >= block_size:
            out.append(buf[:block_size])
            buf = buf[block_size:]
    return out
# NOTE: pass document boundaries to the attention mask, or examples
# will attend across each other and quietly learn nonsense.
ख़तरा
यह बात एक बहुत ही असली और आम बग की ओर इशारा करती है। डॉक्यूमेंट मास्क के बिना पैकिंग करने से मॉडल एक उदाहरण से दूसरे असंबंधित उदाहरण पर अटेंशन देने लगता है। ट्रेनिंग तब भी "चलती" रहती है — लॉस घटता है — लेकिन मॉडल अंदर ही अंदर ख़राब हो जाता है, जिसे कोई यूनिट टेस्ट पकड़ नहीं पाता।

ऑप्टिमाइज़ करने से पहले नापें

Tokens per second, honestlypython
import time
torch.cuda.synchronize(); t0 = time.time()
for _ in range(20):
    x, y = get_batch("train")
    with torch.autocast("cuda", dtype=torch.bfloat16):
        _, loss = model(x, y)
    loss.backward(); opt.step(); opt.zero_grad(set_to_none=True)
torch.cuda.synchronize()

tok_per_s = 20 * x.numel() / (time.time() - t0)
flops = 6 * n_params * tok_per_s
print(f"{tok_per_s:,.0f} tok/s   MFU {flops / peak_flops:.1%}")
यह करें
हमेशा MFU कैलकुलेट करें। "12% यूटिलाइज़ेशन" जैसा नंबर इस धुंधले अहसास को कि चीज़ें धीमी चल रही हैं, एक साफ़ और हल की जा सकने वाली इंजीनियरिंग समस्या में बदल देता है — और आम तौर पर यह डेटा लोडर की गड़बड़ी होती है।

और देखें और पढ़ें

लैब

उसी हार्डवेयर पर 2x की स्पीड, स्टेप-बाय-स्टेप।

~20 min

समस्या

अपनी ट्रेनिंग का बेसलाइन थ्रूपुट नापें। फिर एक-एक करके लागू करें: bf16 ऑटोकास्ट, torch.compile, SDPA के ज़रिए FlashAttention, एक फ्यूज्ड ऑप्टिमाइज़र, और सीक्वेंस पैकिंग। हर बदलाव के बाद नापें और देखें कि किस बदलाव से कितना फ़ायदा हुआ।

आपका काम तब पूरा होगा जब

कठिन सवाल

उत्तर देखने से पहले ख़ुद जवाब देने की कोशिश करें। अगर आप इनके जवाब दे सकते हैं, तो आप पाठ समझ गए हैं।

Q1अलग-अलग लंबाई वाले डेटा पर पैकिंग से आपको 35% फ़ायदा मिला। बताएं कि इससे कौन सा बग आता है और आप इसे कैसे पकड़ते हैं।दिखाएं
क्रॉस-कंटैमिनेशन: डॉक्यूमेंट मास्क के बिना, टोकन्स सीमा पार करके किसी दूसरे असंबंधित उदाहरण में ताक-झांक करने लगते हैं, जिससे मॉडल ऐसे सीक्वेंसेज़ से सीख लेता है जो कभी थे ही नहीं। यह चुपचाप होता है — लॉस घटता है, कुछ भी क्रैश नहीं होता। इसे पकड़ने के लिए एक ऐसा बैच बनाएं जहाँ डॉक्यूमेंट A एक ख़ास टोकन पर ख़त्म होता है और डॉक्यूमेंट B उसी के आगे की बात से शुरू होता है, फिर यह जाँचें कि क्या B की शुरुआत में मॉडल का प्रेडिक्शन A से प्रभावित हो रहा है या नहीं। या फिर बस यह पक्का कर लें (assert) कि अटेंशन मास्क ब्लॉक-डायगोनल है, जो कि आसान है और हर बार इसे पकड़ लेता है।

जारी रखने के लिए कृपया साइन इन करें।

अक्सर पूछे जाने वाले सवाल

bf16 या fp16?

Ampere या उसके बाद के किसी भी हार्डवेयर पर bfloat16 इस्तेमाल करें। इसमें fp32 जितनी ही एक्सपोनेंट रेंज होती है, इसलिए यह ओवरफ्लो नहीं होता और इसमें लॉस स्केलिंग की ज़रूरत नहीं पड़ती। fp16 में मैन्टिसा प्रिसिजन ज़्यादा होता है लेकिन रेंज कम होती है, जिससे बड़े मॉडल की ट्रेनिंग नाज़ुक (fragile) हो जाती है।

क्या FP8 काम का है?

जो हार्डवेयर इसे सपोर्ट करता है, उस पर बड़े रन के लिए यह वाकई काम का है — लेकिन इसमें हर-टेन्सर स्केलिंग और नाज़ुक लेयर्स को बहुत सावधानी से संभालने की ज़रूरत होती है। यहाँ से शुरुआत न करें। पहले bf16 और compile को ठीक से काम करने दें।

मेरा GPU यूटिलाइज़ेशन ज़्यादा है लेकिन थ्रूपुट कम क्यों है?

यूटिलाइज़ेशन पर्सेंटेज सिर्फ़ यह बताता है कि GPU व्यस्त है, यह नहीं कि वह काम का हिसाब-किताब (arithmetic) कर रहा है। हो सकता है कि वह सिर्फ़ मेमोरी इधर-उधर करने में व्यस्त हो। MFU ही सही और सच्चा मीट्रिक है।

क्या torch.compile से कभी नुकसान हो सकता है?

यह पहली बार चलने पर कंपाइल होने में समय लेता है और हर नए इनपुट शेप पर दोबारा कंपाइल होता है। बहुत ज़्यादा बदलने वाले (dynamic) शेप्स के साथ आप कंप्यूट करने से ज़्यादा समय कंपाइल करने में बिता सकते हैं। फ़िक्स्ड शेप्स के साथ compile करना ही सबसे तेज़ तरीक़ा है।

पाठ का टेस्ट

5 सवाल। पास होने और इस पाठ को पूरा करने के लिए 3 सही होने चाहिए (60%)।

टेस्ट देने और अपनी प्रोग्रेस सेव करने के लिए साइन इन करें