Embedding token + posisi
Lapisan pertama GPT: setiap token id diubah menjadi vektor dengan mengambil barisnya dari tok_table, lalu ditambah vektor posisi dari pos_table:
Kembalikan matriks (T, d).
ids = [2, 0, 2] → [tok[2] + pos[0], tok[0] + pos[1], tok[2] + pos[2]]
PE(pos=10, i=0) = sin(10 / 10000^(0/32)) = -0.5440
Baris = posisi token (0 di atas), kolom = dimensi. Kolom kiri berosilasi cepat, kolom kanan sangat lambat, mirip jarum detik, menit, dan jam sebuah jam. Kombinasinya unik untuk tiap posisi.
Posisi yang berdekatan punya encoding yang mirip, dan kemiripan turun seiring jarak. Inilah yang membuat model bisa "merasakan" urutan.
Embedding = lookup
tok_table[ids] adalah perkalian onehot(ids) @ tok_table tanpa membuat matriks one-hot yang boros memori. Saat backward, hanya baris-baris yang dipakai yang menerima gradien. Token yang sama di dua posisi akan menjumlahkan gradien dari keduanya (lihat np.add.at di minitorch).
Kenapa butuh posisi
Attention memperlakukan input sebagai himpunan: kalau urutan token diacak, output setiap token tidak berubah (hanya ikut teracak). Tanpa informasi posisi, “anjing menggigit orang” dan “orang menggigit anjing” terlihat sama.
Ada dua cara umum:
- Embedding posisi yang dipelajari (GPT-2, soal ini): tabel
(block_size, d)yang dilatih seperti bobot lain. Sederhana, tapi tidak bisa dipakai melewatiblock_size. - Sinusoidal (Transformer asli, widget di atas): rumus tetap sin/cos dengan frekuensi berbeda di setiap dimensi. Model LLM modern umumnya memakai RoPE, yang memutar vektor Q dan K sesuai posisinya.
Penjelasan
Yang diminta. Setiap token id diubah menjadi vektor, lalu ditambah vektor posisinya: tok_table[ids[t]] + pos_table[t].
Langkah di kode:
tok_table[ids]: fancy indexing dengan array id mengambil satu baris per id. Bentuknya(T,)→(T, d). Id yang sama (case[1, 1, 1, 1]) mengambil baris yang sama berkali-kali.pos_table[np.arange(T)](sama denganpos_table[:T]) mengambil baris 0 sampai T−1, juga(T, d).- Jumlahkan.
Kenapa ditambah, bukan digabung (concat)? Menambah menjaga dimensi tetap d dan ternyata cukup: model belajar memakai sebagian arah ruang vektor untuk “apa tokennya” dan sebagian lain untuk “di mana posisinya”.
Cek case 2. ids = [2, 0, 2]: [5,5]+[0,0], [1,0]+[0.1,0.1], [5,5]+[0.2,0.2]. Token yang sama di posisi berbeda (id 2) menghasilkan vektor yang berbeda. Itulah inti positional embedding.
Yang dikembalikan: array (T, d).
Topik
Petunjuk 1
Indexing dengan array: tok_table[ids] mengambil satu baris untuk setiap id, hasilnya (T, d).
Petunjuk 2
Posisi ke-t mengambil pos_table[t]. Untuk semua posisi sekaligus: pos_table[np.arange(T)], atau cukup pos_table[:T].
Nilai ditulis sebagai JSON. Output yang diharapkan dihitung dari solusi referensi.