Attention Bahdanau berhasil memperbaiki bottleneck, tetapi skor additive-nya menuntut jaringan penilai terpisah dan tetap berpasangan dengan decoder rekuren. Vaswani dkk. (2017) menyusun ulang seluruh mekanisme menjadi operasi matriks murni: setiap token memproyeksikan diri ke dalam tiga peran — query, key, dan value — lalu kecocokan dihitung sebagai perkalian titik. Perubahan bahasa ini membuat attention bisa berdiri sendiri tanpa rekurensi dan menjadi bata penyusun Transformer. Chapter ini membedah ketiga peran itu dan tiga matriks proyeksinya pada contoh dua kata yang bisa kita hitung dengan tangan.
Analogi Pencarian di Balik Attention #
Cara tercepat memahami ketiganya adalah mesin pencari. Query adalah kata kunci yang kita ketik; key adalah judul halaman yang terindeks oleh mesin; value adalah isi halaman yang akhirnya kita baca. Kecocokan query dengan key menentukan halaman mana yang penting, sedangkan value-lah yang benar-benar diambil isinya. Soft attention membawa analogi ini lebih jauh: daripada memilih satu halaman, model mengambil seluruh halaman sekaligus dalam kadar yang sesuai kecocokannya — rata-rata berbobot, persis pola dari chapter Attention Mechanism .
Bedanya dengan mesin pencari: pada attention antar token, setiap token memainkan ketiga peran sekaligus. Token yang sama bertanya lewat query-nya, memajang label lewat key-nya, dan menyerahkan isi lewat value-nya.
Tiga Proyeksi dari Input yang Sama #
Ketiga peran itu dibentuk dari satu masukan yang sama — matriks token X berbentuk (jumlah token, d_model) — melalui tiga matriks proyeksi linear yang dilatih:
| Peran | Rumus | Analogi mesin pencari |
|---|---|---|
| Query | Q = X @ W_Q | kata kunci yang kita ketik |
| Key | K = X @ W_K | judul halaman yang terindeks |
| Value | V = X @ W_V | isi halaman yang diambil |
Tidak ada informasi baru yang masuk: Q, K, dan V semuanya hanyalah kombinasi linear dari baris-baris X. Perbedaan perannya murni lahir dari bobot yang dipelajari selama training — W_Q menyoroti aspek token yang relevan untuk bertanya, W_K aspek yang layak diindeks, W_V aspek yang layak diserahkan. Dimensi keluaran d_k juga boleh lebih kecil daripada d_model; contoh kita memakai d_model = 4 dan d_k = 3.
Dua Token: ikan dan makan #
Contoh kita memakai dua token dengan empat fitur biner: [makanan, hewan, kata-kerja, butuh-objek]. Token ikan bernilai pada fitur makanan dan hewan; token makan bernilai pada fitur kata kerja dan butuh-objek. Karena matriks proyeksi kita tulis manual, makna tiap barisnya terbaca langsung — kata kerja yang butuh objek diajari bertanya tentang makanan, sementara kata benda memajang dirinya sebagai makanan:
"""Query, Key, dan Value: tiga peran dari input yang sama.
Dua token ("ikan" dan "makan") direpresentasikan X berukuran (2, 4).
Tiga matriks proyeksi berbeda -- W_Q, W_K, W_V -- mengubah X menjadi
query (pertanyaan), key (indeks), dan value (isi). Kecocokan awal
diukur lewat Q @ K^T; skor 2.0 pada pasangan "makan"->"ikan" menunjukkan
query kata kerja menemukan objeknya.
"""
import numpy as np
np.set_printoptions(precision=2, suppress=True)
# Kolom X: [makanan, hewan, kata-kerja, butuh-objek]
X = np.array(
[
[1.0, 1.0, 0.0, 0.0], # token "ikan"
[0.0, 0.0, 1.0, 1.0], # token "makan"
]
)
token = ["ikan", "makan"]
# Tiga proyeksi linear dari input yang sama (d_model=4 -> d_k=3)
W_Q = np.array(
[
[0.0, 0.0, 0.5], # fitur "makanan" -> sedikit mencari kata kerja
[0.0, 0.0, 0.0], # fitur "hewan" -> tidak bertanya
[0.0, 1.0, 0.0], # fitur "kata kerja" -> mencari hewan
[1.0, 0.0, 0.0], # fitur "butuh objek" -> mencari makanan
]
)
W_K = np.array(
[
[1.0, 0.0, 0.0], # fitur "makanan" -> diindeks sebagai makanan
[0.0, 1.0, 0.0], # fitur "hewan" -> diindeks sebagai hewan
[0.0, 0.0, 1.0], # fitur "kata kerja" -> diindeks sebagai kata kerja
[0.0, 0.5, 0.0], # fitur "butuh objek" -> setengah diindeks sebagai hewan
]
)
W_V = np.array(
[
[1.0, 0.0, 0.0], # nilai yang diserahkan: identitas makanan
[0.0, 1.0, 0.0], # identitas hewan
[0.0, 0.0, 1.0], # identitas kata kerja
[0.0, 0.0, 0.5], # setengah identitas kata kerja
]
)
Q = X @ W_Q
K = X @ W_K
V = X @ W_V
print("Input X (2 token, d_model=4):")
for i, nama in enumerate(token):
print(f" token '{nama}': {X[i]}")
print()
print("Proyeksi dari input yang sama:")
for nama, M in (("W_Q", W_Q), ("W_K", W_K), ("W_V", W_V)):
print(f" {nama} (bentuk {M.shape}):")
print(M)
print()
for nama, M, peran in (("Q", Q, "query "), ("K", K, "key "), ("V", V, "value ")):
print(f"{nama} = X @ W_{nama[0]} ({peran.strip()}):")
for i, t in enumerate(token):
print(f" {peran} '{t}': {M[i]}")
print()
S = Q @ K.T
print("Skor kecocokan Q @ K^T (belum diskalakan, belum softmax):")
print(" ", " ".join(f"{t:>6}" for t in token))
for i, t in enumerate(token):
print(f" {t:>6}: {S[i]}")
print()
print(f"Skor tertinggi: query 'makan' terhadap key 'ikan' = {S.max():.1f}")
print("Query kata kerja menemukan objeknya -- langkah softmax dan")
print("rata-rata berbobot V dibahas pada chapter berikutnya.")Output yang Dihasilkan:
Input X (2 token, d_model=4):
token 'ikan': [1. 1. 0. 0.]
token 'makan': [0. 0. 1. 1.]
Proyeksi dari input yang sama:
W_Q (bentuk (4, 3)):
[[0. 0. 0.5]
[0. 0. 0. ]
[0. 1. 0. ]
[1. 0. 0. ]]
W_K (bentuk (4, 3)):
[[1. 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]
[0. 0.5 0. ]]
W_V (bentuk (4, 3)):
[[1. 0. 0. ]
[0. 1. 0. ]
[0. 0. 1. ]
[0. 0. 0.5]]
Q = X @ W_Q (query):
query 'ikan': [0. 0. 0.5]
query 'makan': [1. 1. 0.]
K = X @ W_K (key):
key 'ikan': [1. 1. 0.]
key 'makan': [0. 0.5 1. ]
V = X @ W_V (value):
value 'ikan': [1. 1. 0.]
value 'makan': [0. 0. 1.5]
Skor kecocokan Q @ K^T (belum diskalakan, belum softmax):
ikan makan
ikan: [0. 0.5]
makan: [2. 0.5]
Skor tertinggi: query 'makan' terhadap key 'ikan' = 2.0
Query kata kerja menemukan objeknya -- langkah softmax dan
rata-rata berbobot V dibahas pada chapter berikutnya.Mari kita telaah kode di atas baris per baris:
- Baris 14-21: matriks
Xberbentuk (2, 4) — dua token, empat fitur biner. Tokenikanaktif pada kolom makanan dan hewan; tokenmakanaktif pada kolom kata-kerja dan butuh-objek. - Baris 23-47: tiga matriks proyeksi berbentuk (4, 3). Setiap baris memetakan satu fitur masukan ke tiga dimensi keluaran — baris keempat
W_Q, misalnya, menjadikan fitur butuh-objek sebagai pertanyaan tentang makanan. - Baris 49-51: ketiga proyeksi diterapkan pada
Xyang sama. Inilah satu-satunya tempat input masuk ke perhitungan. - Baris 53-61: pencetakan input dan ketiga matriks bobot — bentuk (4, 3) menegaskan penyusutan dari d_model ke d_k.
- Baris 63-66: hasil proyeksi per token. Query
makanbernilai[1; 1; 0]— bertanya tentang makanan dan hewan; keyikanjuga bernilai[1; 1; 0]— memajang diri persis di dua aspek itu. - Baris 69-73: skor kecocokan
Q @ K^T— perkalian titik setiap pasangan query-key, dihasilkan sebagai satu operasi matriks tanpa perulangan pasangan. - Baris 75-77: skor tertinggi dilaporkan; dua langkah yang belum dilakukan (softmax dan rata-rata berbobot
V) menjadi pokok chapter berikutnya.
Hasilnya memperlihatkan mekanisme pencarian yang dimainkan dua kata. Query makan [1; 1; 0] dan key ikan [1; 1; 0] sejajar sempurna, menghasilkan skor 2,0 — satu-satunya pasangan besar pada matriks skor. Sebaliknya, query ikan hanya memberi skor 0,5 ke kedua key: kata benda ini tidak benar-benar bertanya. Nilai ikan sendiri [1; 1; 0] adalah isi yang akan diserap oleh makan begitu bobot softmax dan rata-rata berbobot diterapkan.
Visualisasi langkah demi langkah query, key, dan value tersedia pada The Illustrated Transformer karya Jay Alammar — rujukan yang pas untuk melengkapi angka-angka di atas.
Skor 2,0 dan 0,5 pada output masih angka mentah: belum berupa bobot yang bisa dirata-rata, dan skalanya belum dijaga agar stabil pada dimensi besar. Kedua kelengkapan itu dirangkai dalam satu rumus — scaled dot-product attention — pokok bahasan chapter berikutnya.