Query, Key, Value
Tiga Peta Linear dari Input yang Sama
Setelah embedding (aktivitas 4), setiap posisi membawa vektor 768-dimensi x_t. Attention dimulai dengan menghasilkan tiga proyeksi berbeda dari x:
Q (query): apa yang ingin diketahui posisi ini?
K (key): apa yang ditawarkan posisi ini kepada posisi lainnya?
V (value): konten apa yang disampaikan posisi ini jika diperhatikan?
Setiap proyeksi berasal dari matriks bobot yang dipelajari:
Q = x · W_Q # Bentuk W_Q: (d_model, d_k)
K = x · W_K # Bentuk W_K: (d_model, d_k)
V = x · W_V # Bentuk W_V: (d_model, d_k)
Tiga matriks, semuanya dilatih melalui backpropagation. Model belajar: pada posisi ini, query apa yang terbaik untuk mengambil konteks masa lalu yang berguna? Key apa yang mengiklankan konten posisi ini dengan baik? Value apa yang dikirimkan jika dipilih?
Analogi Perpustakaan
Bayangkan katalog kartu perpustakaan. Anda masuk dengan topik di pikiran (您的 query). Setiap kartu mencantumkan kata kunci (key). Ketika topik Anda cocok dengan kata kunci kartu, Anda mengambil isi buku (value). Attention melakukan ini untuk setiap token secara paralel: setiap posisi mengquery setiap posisi lainnya, meranking keselarasan, & mengambil kombinasi tertimbang dari vektor value.
Dimensi ANDREA-120M
| Kuantitas | Nilai | Catatan |
|---|---|---|
| d_model | 768 | Ukuran vektor di setiap posisi |
| n_head | 12 | Kepala perhatian paralel |
| d_k | 64 | Dimensi per-kepala (= d_model / n_head) |
| T | 1024 | Panjang konteks |
d_k = d_model / n_head = 768 / 12 = 64. Setiap kepala melihat irisan 64-dimensi dari ruang 768-dimensi penuh. Aktivitas 6 (grow_a_language_model_multi_head) membahas pemisahan per-kepala secara detail.
Hitung d_k
Mengapa Membagi dengan sqrt(d_k)
Matriks Skor
Setelah Q & K ada (bentuk masing-masing (T, d_k)), attention menghitung matriks skor:
scores = Q · K^T # bentuk: (T, T)
scores[i, j] = seberapa kuat query pada posisi i selaras dengan key pada posisi j. Setiap pasangan (i, j) mendapatkan satu skor: 1024 × 1024 = 1.048.576 skor per attention head per forward pass.
Mengapa Membagi
Dot product dari dua vektor unit acak berdimensi d memiliki magnitudo pada orde sqrt(d). Tanpa penskalaan, skor akan bertambah seiring dengan d_k:
- d_k = 64: dot product tipikal berurutan 8.
- d_k = 256: dot product tipikal berurutan 16.
- d_k = 4096: dot product tipikal berurutan 64.
Skor besar menghasilkan softmax yang tajam (satu posisi mendominasi, gradien hilang di tempat lain). Pelatihan terhenti. Skala memperbaiki magnitudo:
scaled_scores = (Q · K^T) / sqrt(d_k)
Untuk ANDREA-120M, sqrt(d_k) = sqrt(64) = 8. Setiap skor dibagi dengan 8. Magnitudo tetap dalam skala unit secara kasar terlepas dari d_k. Softmax tetap berperilaku baik. Gradien mengalir.
Justifikasi Asli Vaswani
Dari Attention Is All You Need (2017): 'Untuk nilai d_k yang besar, produk titik menjadi besar dalam magnitudo, mendorong fungsi softmax ke wilayah di mana gradiennya sangat kecil.' Pembagi sqrt(d_k) melawan pertumbuhan tersebut.
Tampilan Kode
Di dalam microgpt_cuda.cu, penskalaan ini muncul sebagai pembagian literal:
scores[i][j] = dot(Q[i], K[j]) * (1.0f / sqrtf(d_k));
Satu perkalian float per skor. Murah. Kritis.
Skala pada d_model = 4096
Mengapa Posisi i Tidak Bisa Melihat Posisi j > i
Sebuah Kendala yang Lahir dari Generasi
ANDREA menghasilkan satu token pada satu waktu. Saat inferensi, posisi 0 menghasilkan token pertama, kemudian posisi 1 melihat output posisi 0 & menghasilkan token kedua, & seterusnya. Model tidak pernah memiliki akses ke token masa depan selama generasi.
Pelatihan harus mencerminkan ini. Jika selama pelatihan posisi 5 bisa menghadiri posisi 6, model akan belajar jalan pintas: 'prediksi token 6 dengan membaca token 6'. Saat inferensi, jalan pintas itu hilang (token 6 belum ada). Perilaku pelatihan-versus-inferensi model akan menyimpang secara katastrofik.
Sebuah Mask
Mask kausal memblokir perhatian dari posisi i mana pun ke posisi j > i mana pun. Implementasi: atur scaled_scores[i][j] = -infinity di mana pun j > i. Setelah softmax, entri tersebut menjadi exp(-inf) = 0. Mask menghapus perhatian ke posisi masa depan dengan bersih.
untuk i dalam range(T):
untuk j dalam range(T):
jika j > i:
scaled_scores[i][j] = -1e9 # secara efektif -inf
Setelah softmax (per baris), setiap baris menjumlahkan menjadi 1, tetapi hanya entri [0, i] yang membawa massa probabilitas. Posisi i hanya mencampur informasi dari posisi-posisi sebelumnya.
Memvisualisasikan Mask
Matriks skor bentuk (T, T) dengan mask yang diterapkan terlihat seperti struktur segitiga bawah:
scaled_scores setelah mask, softmax per baris:
baris 0: [1.0, 0, 0, 0, ...] # hanya melihat dirinya sendiri
baris 1: [0.4, 0.6, 0, 0, ...] # melihat posisi 0, 1
baris 2: [0.2, 0.3, 0.5, 0, ...] # melihat 0, 1, 2
baris 3: [0.1, 0.2, 0.3, 0.4, ...] # melihat 0, 1, 2, 3
...
Distribusi probabilitas strict lower-triangular per baris. Masa depan tetap tidak terlihat.
Mengapa Transformer Decoder-Only Membutuhkan Ini
Model decoder-only seperti ANDREA, GPT, & LLaMA semuanya memiliki satu tujuan: memprediksi token berikutnya dari masa lalu. Masker kausal membuat tujuan tersebut dapat dilatih secara paralel: setiap posisi menghitung prediksinya sendiri untuk token berikutnya sekaligus, & tidak ada posisi yang curang dengan mengintip ke depan.
Mask & Rasa
Dari Skor ke Output
Softmax: Dari Skor ke Probabilitas
Skor yang dimasker dan diskalakan masih berkisar pada bilangan real. Softmax mengonversi setiap baris menjadi distribusi probabilitas:
A[i][j] = exp(scaled_scores[i][j]) / sum_k exp(scaled_scores[i][k])
Tiga properti dihasilkan:
Jawaban
Jawaban yang benar: C. Semua pernyataan di atas Penjelasan: - Softmax menghasilkan probabilitas positif: Benar, fungsi eksponensial memastikan semua nilai > 0. - Softmax menjumlahkan menjadi 1: Benar, pembagian dengan jumlah memastikan ∑A[i][j] = 1. - Softmax mempertahankan urutan: Benar, nilai lebih besar tetap dominan setelah softmax.- A[i][j] >= 0 untuk semua (i, j).
- sum_j A[i][j] = 1 untuk setiap baris i.
- Skor mentah yang lebih besar menghasilkan probabilitas yang lebih besar (monoton).
Vektor probabilitas baris i memberi tahu model: seberapa besar posisi i harus memperhatikan setiap posisi sebelumnya saat menghitung outputnya?
Jumlah V Tertimbang
Keluaran perhatian akhir untuk posisi i:
output[i] = sum_j A[i][j] · V[j]
Setiap vektor nilai V[j] diberi bobot oleh probabilitas perhatian A[i][j], kemudian dijumlahkan. Keluaran posisi i menggabungkan vektor nilai dari setiap posisi sebelumnya, diberi bobot berdasarkan relevansi.
Dalam bentuk matriks, semua posisi sekaligus:
Attention(Q, K, V) = softmax(mask(Q · K^T / sqrt(d_k))) · V
Satu baris. Seluruh mekanisme attention. Vaswani et al. menulis baris itu pada 2017; transformer belum berubah secara fundamental sejak saat itu.
Bentuk Output Per-Head
Output dari satu attention head: bentuk (T, d_k). Untuk ANDREA-120M: (1024, 64). Semua 12 head dihitung secara paralel; output mereka digabungkan menjadi (1024, 768) & dimasukkan ke proyeksi linear akhir (W_O), kemudian ke MLP blok transformer.
Aktivitas 6 (grow_a_language_model_multi_head) membahas pemisahan multi-head. Aktivitas 7 (grow_a_language_model_transformer_block) membahas segala hal yang mengelilingi attention: koneksi residual, layer norm, MLP.