შეკითხვა, გასაღები, მნიშვნელობა
სამი ლინეარული რუკა ერთი და იმავე შეყვანიდან
ჩაშენების შემდეგ (აქტივობა 4), თითოეული პოზიცია არის 768-განზომილებიანი ვექტორი x_t. ყურადღება იწყება x-ის სამი განსხვავებული პროექციის შექმნით:
Q (შეკითხვა): რას სურს ამ პოზიციას იცოდეს?
K (გასაღები): რას სთავაზობს ეს პოზიცია სხვა პოზიციებს?
V (მნიშვნელობა): რა კონტენტს მიაწოდებს ეს პოზიცია, თუ მას ყურადღება მიექცევა?
თითოეული პროექცია მოდის შეხსენებული წონის მატრიციდან:
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)
სამი მატრიცა, რომლებიც ყველა სწავლობს უკუაგზა გავრცელებით. მოდელი სწავლობს: ამ პოზიციაზე, რომელი შეკითხვა საუკეთესოდ მიიღებს სასარგებლო წინა კონტექსტს? რომელი გასაღები კარგად განაცხადებს ამ პოზიციის შინაარსს? რომელი მნიშვნელობა მოგცემს თუ შეარჩევ?
ბიბლიოთეკის ანალოგია
წარმოიდგინეთ ბიბლიოთეკის კარტოთეკა. შედიხართ თემით გონებაში (თქვენი მოთხოვნა). ყველა ბარათი შეიცავს საკვანძო სიტყვებს (გასაღები). როდესაც თქვენი თემა ემთხვევა ბარათის საკვანძო სიტყვებს, იღებთ წიგნის შინაარსს (მნიშვნელობა). ყურადღება ასე მუშაობს ყველა ტოკენისთვის პარალელურად: ყველა პოზიცია მოითხოვს ყველა სხვა პოზიციას, აფასებს შესაბამისობას და იღებს მნიშვნელობის ვექტორების შეწონილი კომბინაციას.
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. ყოველი თავი ხედავს 64-განზომილება სრული 768-განზომილება სივრცის ნაწილს. აქტივობა 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 ქულა თითო attention head-ზე თითო forward pass-ზე.
რატომ გავყოთ
ორი შემთხვევითი d-ზომიანი ერთეული ვექტორის სკალярური ნამრავლის სიმაღლე sqrt(d)-ის ზომისაა. მასშტაბირების გარეშე, ქულები d_k-ით იზრდება:
- d_k = 64: ტიპური სკალярური ნამრავლები დაახლოებით 8-ის ზომისაა.
- d_k = 256: ტიპური სკალярური ნამრავლები დაახლოებით 16-ის ზომისაა.
- d_k = 4096: ტიპური სკალярური ნამრავლები დაახლოებით 64-ის ზომისაა.
დიდი ქულები იწვევს მწვავე softmax-ს (ერთი პოზიცია დომინირებს, გრადიენტები სხვაგან ქრება). სწავლება ჩერდება. მასშტაბირება ასწორებს მაგნიტუდას:
მასშტაბირებული_შედეგები = (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));
ერთი float ნამრავლგება თითო ქულაზე. იაფი. კრიტიკული.
მასშტაბირება d_model = 4096-ზე
რატომ ვერ ხედავს პოზიცია i პოზიციას j > i
შეზღუდვა, რომელიც გენერაციიდან მოდის
ANDREA ერთ ჯერზე ერთი ტოკენს გენერირებს. ინფერენსის დროს პოზიცია 0 პირველ ტოკენს ქმნის, შემდეგ პოზიცია 1 ხედავს პოზიცია 0-ის გამომავალს და ქმნის მეორე ტოკენს, და ასე შემდეგ. მოდელს არასდროს აქვს წვდომა მომავალ ტოკენებზე გენერაციის დროს.
ტრეინინგმა უნდა ასახოს ეს. თუ ტრეინინგის დროს პოზიცია 5-მა შეძლო ყურადღების მიქცევა პოზიცია 6-ზე, მოდელი ისწავლიდა შოუთკუტს: „ტოკენი 6-ის პროგნოზირება ტოკენი 6-ის წაკითხვით“. ინფერენსის დროს ეს შოუთკუტი ქრება (ტოკენი 6 ჯერ არ არსებობს). მოდელის ტრეინინგის-წინააღმდეგ ინფერენსის ქცევა კატასტროფულად განსხვავდებოდა.
ნიღაბი
კაუზალური ნიღაბი ბლოკავს ყურადღებას ნებისმიერი პოზიციიდან i ნებისმიერ პოზიციაზე j > i. იმპლემენტაცია: scaled_scores[i][j] = -უსასრულოება დაყენება იქ, სადაც j > i. სოფტმაქსის შემდეგ ეს ელემენტები ხდება exp(-inf) = 0. ნიღაბი სუფთად ნულამდე ართმევს ყურადღებას მომავალ პოზიციებზე.
for i in range(T):
for j in range(T):
if j > i:
scaled_scores[i][j] = -1e9 # ეფექტურად -inf
Softmax-ის შემდეგ (სტრიქონების მიხედვით), თითოეული სტრიქონი თანაბრად 1-ს უდრის, მაგრამ მხოლოდ ელემენტები [0, i] შეიცავს ალბათობის მასას. პოზიცია i აერთიანებს ინფორმაციას მხოლოდ წინა პოზიციებიდან.
ნიღბის ვიზუალიზაცია
შედეგების მატრიცა ფორმით (T, T) ნიღბის გამოყენებით ჩამოყალიბებული ზუსტ სამკუთხედის სტრუქტურის მსგავსია:
მასშტაბირებული შედეგები ნიღბის შემდეგ, ხაზზე softmax:
ხაზი 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-ს
დეკოდერ-მხოლოდ მოდელები, როგორიცაა ANDREA, GPT და LLaMA, ერთსა და იმავე მიზანს იზიარებენ: წინასწარი ტოკენებიდან შემდეგი ტოკენის პროგნოზირება. კაუზალური ნიღაბი ამ მიზანს პარალელურად შესაძლებელს ხდის: ყველა პოზიცია ერთდროულად ყოფს თავის შემდეგი-ტოკენის პროგნოზს და არცერთი პოზიცია არ მოტყუებს წინ ყურებით.
ნიღაბი და გემრიელობა
შედეგებიდან გამომავალი
სოფტმაქსი: ქულები შესაძლებლობების გარდაქმნა
ფარული, გასქელებული ქულები მაინც რეალურ რიცხვებზე ვრცელდება. სოფტმაქსი თითოეულ ხაზს გარდაქმნის შესაძლებლობის განაწილებად:
A[i][j] = exp(scaled_scores[i][j]) / sum_k exp(scaled_scores[i][k])
შედეგად მიიღება სამი თვისება:
ახლა შეადარე ორი ვერსია
შეადარე ქვემოთ მოყვანილი ორი სურათი. რა განსხვავებაა მათ შორის?- A[i][j] >= 0 ყველა (i, j)-ისთვის.
- sum_j A[i][j] = 1 ყოველი მწკრივის i-სთვის.
- უფრო დიდი შეუმუშავებელი ქულები იძლევა უფრო დიდ ალბათობებს (მონოტონული).
მწკრივი 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.