क्वेरी, की, वैल्यू
एक ही इनपुट से तीन रैखिक मैप्स
एम्बेडिंग (गतिविधि 4) के बाद, प्रत्येक स्थिति एक 768-आयामी वेक्टर x_t ले जाती है। अटेंशन x के तीन अलग-अलग प्रोजेक्शन्स उत्पन्न करके शुरू होता है:
Q (क्वेरी): यह स्थिति क्या जानना चाहती है?
K (key): यह स्थिति अन्य स्थितियों को क्या प्रदान करती है?
V (value): यदि इस पर ध्यान दिया जाए तो यह स्थिति कौन सी सामग्री प्रदान करती है?
प्रत्येक प्रोजेक्शन एक सीखी हुई वजन मैट्रिक्स से आती है:
Q = x · W_Q # W_Q का आकार: (d_model, d_k)
K = x · W_K # W_K का आकार: (d_model, d_k)
V = x · W_V # W_V का आकार: (d_model, d_k)
तीन मैट्रिक्स, सभी बैकप्रोपगेशन के माध्यम से प्रशिक्षित। एक मॉडल सीखता है: इस स्थिति पर, कौन सा क्वेरी उपयोगी अतीत संदर्भ को सबसे अच्छी तरह पुनः प्राप्त करता है? कौन सी कुंजी इस स्थिति की सामग्री को अच्छी तरह प्रचारित करती है? यदि चयनित हो तो कौन सा वैल्यू प्रदान करता है?
लाइब्रेरी की उपमा
कल्पना कीजिए एक लाइब्रेरी कार्ड कैटलॉग की। आप एक विषय लेकर अंदर जाते हैं (आपका query)。 हर कार्ड में कीवर्ड्स सूचीबद्ध हैं (एक key)。 जब आपका विषय किसी कार्ड के कीवर्ड्स से मेल खाता है, तो आप किताब की सामग्री को पकड़ लेते हैं (एक value)。 Attention हर टोकन के लिए समानांतर में यह करता है: हर स्थिति हर अन्य स्थिति को क्वेरी करती है, संरेखण को रैंक करती है, और वैल्यू वेक्टर्स के भारित संयोजन को पुनर्प्राप्त करती है।
ANDREA-120M आयाम
| मात्रा | मान | टिप्पणियाँ |
|---|---|---|
| d_model | 768 | हर स्थिति पर वेक्टर का आकार |
| n_head | 12 | समानांतर ध्यान हेड्स |
| d_k | 64 | प्रति-हेड आयाम (= d_model / n_head) |
| T | 1024 | संदर्भ की लंबाई |
d_k = d_model / n_head = 768 / 12 = 64. प्रत्येक हेड पूर्ण 768-आयामी स्थान का 64-आयामी स्लाइस देखता है। Activity 6 (grow_a_language_model_multi_head) प्रति-हेड विभाजन को विस्तार से कवर करता है।
d_k की गणना करें
sqrt(d_k) से क्यों भाग करते हैं
एक स्कोर मैट्रिक्स
एक बार Q और K मौजूद हो जाएँ (प्रत्येक आकार (T, d_k)), एटेंशन एक स्कोर मैट्रिक्स की गणना करता है:
scores = Q · K^T # shape: (T, T)
scores[i, j] = यह दर्शाता है कि स्थिति i का क्वेरी स्थिति j की कुंजी के साथ कितना मजबूती से संरेखित होता है। हर (i, j) जोड़ी को एक स्कोर मिलता है: 1024 × 1024 = 1,048,576 स्कोर प्रति अटेंशन हेड प्रति फॉरवर्ड पास।
क्यों भाग करें
दो यादृच्छिक d-आयामी इकाई वेक्टरों के डॉट प्रोडक्ट का परिमाण sqrt(d) के क्रम का होता है। स्केलिंग के बिना, स्कोर d_k के साथ बढ़ते हैं:
- d_k = 64: सामान्य डॉट प्रोडक्ट्स का क्रम 8 के आसपास।
- d_k = 256: सामान्य डॉट प्रोडक्ट्स का क्रम 16 के आसपास।
- d_k = 4096: सामान्य डॉट प्रोडक्ट्स का क्रम 64 के आसपास।
बड़े स्कोर पीकी सॉफ्टमैक्स उत्पन्न करते हैं (एक स्थिति हावी हो जाती है, अन्य जगहों पर ग्रेडिएंट्स गायब हो जाते हैं)। ट्रेनिंग रुक जाती है। स्केलिंग एक परिमाण ठीक करती है:
scaled_scores = (Q · K^T) / sqrt(d_k)
ANDREA-120M के लिए, sqrt(d_k) = sqrt(64) = 8. हर स्कोर को 8 से विभाजित किया जाता है। परिमाण d_k की परवाह किए बिना लगभग यूनिट-स्केल पर बने रहते हैं। Softmax सुव्यवस्थित रहता है। ग्रेडिएंट्स प्रवाहित होते हैं।
Vaswani का मूल औचित्य
Attention Is All You Need (2017) से: 'd_k के बड़े मानों के लिए, डॉट प्रोडक्ट्स का परिमाण बड़ा हो जाता है, जो softmax फंक्शन को उन क्षेत्रों में धकेल देता है जहाँ इसके ग्रेडिएंट्स अत्यंत छोटे होते हैं।' sqrt(d_k) विभाजक उस वृद्धि का प्रतिकार करता है।
एक कोड दृश्य
microgpt_cuda.cu के अंदर, यह स्केलिंग एक शाब्दिक विभाजन के रूप में दिखाई देती है:
scores[i][j] = dot(Q[i], K[j]) * (1.0f / sqrtf(d_k));
प्रत्येक स्कोर के लिए एक फ्लोट गुणा। सस्ता। महत्वपूर्ण।
d_model = 4096 पर स्केलिंग
स्थिति i क्यों स्थिति j > i को नहीं देख सकती
एक बाधा जो पीढ़ी से उत्पन्न हुई
एंड्रिया एक समय में एक टोकन उत्पन्न करता है। इन्फरेंस के दौरान, स्थिति 0 पहला टोकन उत्पन्न करती है, फिर स्थिति 1 स्थिति 0 के आउटपुट को देखती है & दूसरा टोकन उत्पन्न करती है, & इसी तरह। मॉडल के पास जनरेशन के दौरान कभी भविष्य के टोकनों तक पहुँच नहीं होती।
ट्रेनिंग को इसकी नकल करनी चाहिए। यदि ट्रेनिंग के दौरान स्थिति 5 स्थिति 6 को देख सकती है, तो मॉडल एक शॉर्टकट सीख लेगा: 'टोकन 6 को टोकन 6 पढ़कर भविष्यवाणी करो'। इन्फरेंस पर, वह शॉर्टकट गायब हो जाता है (टोकन 6 अभी अस्तित्व में नहीं है)। मॉडल का ट्रेनिंग-बनाम-इन्फरेंस व्यवहार विनाशकारी रूप से विचलित हो जाएगा।
एक मास्क
एक कारण मास्क किसी भी स्थिति i से किसी भी स्थिति j > i पर ध्यान को ब्लॉक करता है। कार्यान्वयन: scaled_scores[i][j] = -infinity सेट करें जहाँ भी j > i। सॉफ्टमैक्स के बाद, वे एंट्रीज़ exp(-inf) = 0 हो जाती हैं। मास्क भविष्य की स्थितियों पर ध्यान को साफ़-सुथरे तरीके से शून्य कर देता है।
for i in range(T):
for j in range(T):
if j > i:
scaled_scores[i][j] = -1e9 # प्रभावी रूप से -inf
सॉफ्टमैक्स (पंक्ति-वार) के बाद, प्रत्येक पंक्ति का योग 1 होता है, लेकिन केवल एंट्री [0, i] ही प्रायिकता द्रव्यमान ले जाती हैं। स्थिति i केवल अतीत की स्थितियों से जानकारी को मिलाती है।
एक मास्क को विज़ुअलाइज़ करना
मास्क लागू करने के बाद स्कोर मैट्रिक्स का आकार (T, T) निम्न-त्रिकोणीय संरचना जैसा दिखता है:
मास्क के बाद scaled_scores, पंक्ति-वार सॉफ्टमैक्स:
पंक्ति 0: [1.0, 0, 0, 0, ...] # केवल खुद को देखता है
पंक्ति 1: [0.4, 0.6, 0, 0, ...] # स्थिति 0, 1 को देखता है
पंक्ति 2: [0.2, 0.3, 0.5, 0, ...] # देखता है 0, 1, 2
पंक्ति 3: [0.1, 0.2, 0.3, 0.4, ...] # देखता है 0, 1, 2, 3
...
प्रत्येक पंक्ति के लिए सख्त निचला-त्रिभुजाकार प्रायिकता वितरण। भविष्य अदृश्य रहता है।
Decoder-Only Transformer को इसकी क्यों आवश्यकता है
Decoder-only मॉडल जैसे ANDREA, GPT, & LLaMA सभी एक ही उद्देश्य साझा करते हैं: पिछले टोकन से अगला टोकन भविष्यवाणी करना। एक causal mask उस उद्देश्य को समानांतर में प्रशिक्षण योग्य बनाता है: हर स्थिति एक साथ अपनी अगले-टोकन भविष्यवाणी की गणना करती है, & कोई स्थिति आगे झांककर धोखा नहीं देती।
Mask & Flavor
Scores से Output तक
सॉफ्टमैक्स: स्कोर को प्रायिकताओं में
मास्क्ड, स्केल्ड स्कोर अभी भी वास्तविक संख्याओं पर फैले रहते हैं। सॉफ्टमैक्स प्रत्येक पंक्ति को एक प्रायिकता वितरण में बदल देता है:
A[i][j] = exp(scaled_scores[i][j]) / sum_k exp(scaled_scores[i][k])
तीन गुण परिणामस्वरूप प्राप्त होते हैं:
- सभी (i, j) के लिए A[i][j] >= 0।
- हर पंक्ति i के लिए sum_j A[i][j] = 1।
- बड़े कच्चे स्कोर बड़े प्रायिकताएँ उत्पन्न करते हैं (एकवचन)।
पंक्ति i का प्रायिकता सदिश मॉडल को बताता है: अपनी आउटपुट की गणना करते समय स्थिति i को प्रत्येक पूर्व स्थिति पर कितना ध्यान देना चाहिए?
भारित V योग
स्थिति i के लिए अंतिम ध्यान आउटपुट:
output[i] = sum_j A[i][j] · V[j]
प्रत्येक मान वेक्टर V[j] को ध्यान संभावना A[i][j] द्वारा भारित किया जाता है, फिर योग किया जाता है। स्थिति i का आउटपुट हर पूर्ववर्ती स्थिति से मान वेक्टर्स को प्रासंगिकता के अनुसार भारित करके जोड़ता है।
मैट्रिक्स रूप में, सभी स्थितियाँ एक साथ:
Attention(Q, K, V) = softmax(mask(Q · K^T / sqrt(d_k))) · V
एक लाइन। पूरा ध्यान तंत्र। Vaswani et al. ने 2017 में यह लाइन लिखी; ट्रांसफॉर्मर तब से मौलिक रूप से नहीं बदले हैं।
प्रति-हेड आउटपुट आकार
एक अटेंशन हेड का आउटपुट: आकार (T, d_k)। ANDREA-120M के लिए: (1024, 64)। सभी 12 हेड समानांतर में गणना करते हैं; उनके आउटपुट को (1024, 768) में संयोजित किया जाता है & अंतिम लीनियर प्रोजेक्शन (W_O) में भेजा जाता है, फिर ट्रांसफॉर्मर ब्लॉक के MLP में।
गतिविधि 6 (grow_a_language_model_multi_head) मल्टी-हेड स्प्लिट को कवर करती है। गतिविधि 7 (grow_a_language_model_transformer_block) अटेंशन के आसपास सब कुछ कवर करती है: रेसिडुअल कनेक्शन्स, लेयर नॉर्म, MLP।