Lewati ke konten
8/30Bab 8 dari 30

Prediksi Next-Token: Embeddings, dan Arti Perplexity

Latih model karakter pada 32.033 nama, lihat gradient descent menemukan ulang tabel hitungan, lalu mengapa perplexity jarang cocok.

Di halaman ini

Berikut sepuluh nama yang dihasilkan oleh program yang belum pernah melihat satu kata pun:

TEXT
cexze   momakurailezitynn   konimittain   llayn   ka
da      moliellavo          emia          sade    ftlsp

Tak satu pun adalah nama. Hampir semuanya sedang mencoba. Mereka bisa dilafalkan, berakhir di tempat nama biasanya berakhir, dan salah satunya — emia — hanya terpaut satu huruf dari nama sungguhan. Program yang menghasilkannya menyimpan 729 angka, tidak punya konsep tentang kata, suku kata, atau manusia, dan di-fit lewat satu pass menghitung pasangan huruf yang bersebelahan.

Pada akhir bab ini, sebuah neural network akan memangkas skor program itu sepertiga pada pengukuran yang sama. Bagian yang layak kamu tunggu adalah apa yang pertama kali dilakukan network tersebut: ia mereproduksi tabel hitungan hingga tiga angka desimal pada setiap baris yang datanya cukup banyak, tanpa diminta, karena kedua objek itu adalah jawaban untuk pertanyaan yang sama. Semua setelah itu adalah hal yang tak pernah bisa dilakukan oleh penghitungan.

Objective adalah identitas, bukan pilihan desain

Tautan ke bagian: Objective adalah identitas, bukan pilihan desain

Bab 7 meninggalkan kamu dengan deretan bilangan bulat dan tanpa alasan mengapa satu bilangan harus mengikuti bilangan lain. Inilah alasannya, dan ia adalah satu baris dari Bab 2.

Language model adalah fungsi yang mengambil token sejauh ini dan mengembalikan distribusi atas token mana yang muncul berikutnya: satu angka per entri vocabulary, non-negatif, dan jumlahnya satu. Tidak ada yang lain. Untuk beranjak dari situ ke probabilitas sebuah dokumen utuh, terapkan aturan rantai probabilitas:

P(x1,x2,,xT)=t=1TP(xtx1,,xt1)P(x_1, x_2, \ldots, x_T) = \prod_{t=1}^{T} P(x_t \mid x_1, \ldots, x_{t-1})

Itu adalah identitas, benar untuk urutan apa pun dari apa pun, tanpa asumsi tambahan. Jadi model yang melakukan pekerjaan kecil — next token dengan token-token sebelumnya — sudah melakukan pekerjaan besar untuk memberi probabilitas pada setiap dokumen yang mungkin, secara persis dan gratis. Pembingkaian populer bahwa ini trik murah (“ia hanya memprediksi kata berikutnya”) membalik logikanya: memprediksi next token adalah memodelkan distribusi gabungan. Tidak pernah ada hal kedua yang harus dilakukan.

Loss-nya mengikuti sama mekanisnya. Di setiap posisi, model menghasilkan distribusi qq dan kebenarannya adalah satu token yang diketahui, jadi cross-entropy dari Bab 4 berlaku tanpa perubahan:

L=1Tt=1Tlogqθ(xtx<t)L = -\frac{1}{T}\sum_{t=1}^{T} \log q_\theta(x_t \mid x_{<t})

Itu adalah average negative log-likelihood — resep Bab 2 dengan distribusi kategorikal di slot tempat Gaussian sebelumnya berada. Dan karena distribusi sejatinya one-hot, entropinya nol, jadi menurut identitas Bab 4, cross-entropy sama dengan KL divergence: menurunkan angka ini dan menarik keyakinan model ke arah data adalah tindakan yang sama.

Satu konsekuensi layak mendapat kalimatnya sendiri, karena ini fakta ekonomis di bawah seluruh bidang ini. Labelnya adalah data, digeser satu posisi. Tidak ada yang menganotasi apa pun. Satu triliun token teks adalah satu triliun contoh yang sudah berlabel, itulah mengapa korpus training model modern adalah “internet” dan bukan “dataset yang dibuat seseorang”.

Sebelum network apa pun, baseline-nya: 32.033 nama, satu per baris, dan tugas menghasilkan lebih banyak nama, satu huruf demi satu huruf.1

Vocabulary-nya adalah 26 huruf ditambah simbol batas . yang menandai awal sekaligus akhir sebuah nama, jadi model harus belajar di mana nama dimulai dan di mana ia berhenti. Itu berarti 27 simbol, dan model terkecil yang mungkin adalah tabel seberapa sering setiap simbol mengikuti setiap simbol lain.

bigram.pyPYTHON
N = torch.zeros((27, 27), dtype=torch.int32)
for w in words:
    cs = ["."] + list(w) + ["."]
    for a, b in zip(cs, cs[1:]):
        N[stoi[a], stoi[b]] += 1

P = N.float()
P = P / P.sum(1, keepdim=True)            # one distribution per row   

Dua baris aritmetika dan modelnya sudah di-fit — dan ini bukan heuristik: membagi hitungan dengan total baris adalah estimasi maximum-likelihood untuk distribusi kategorikal, yaitu resep Bab 2 dengan kalkulusnya sudah dikerjakan.

TEXT
names: 32033        train/val/test: 25626 / 3203 / 3204
training bigrams: 182583

the six most likely letters after 'a':
    a -> '.'  0.1944   a -> 'n'  0.1600   a -> 'r'  0.0967
    a -> 'l'  0.0749   a -> 'h'  0.0690   a -> 'y'  0.0606

Ambil sample darinya — pilih satu huruf dari baris huruf saat ini, pindah ke baris itu, ulangi sampai simbol batas muncul — dan kamu mendapat nama-nama di awal bab ini. Mereka gagal dengan cara yang spesifik dan informatif: masuk akal secara lokal, omong kosong secara global. Setiap pasangan huruf bersebelahan dalam momakurailezitynn adalah pasangan yang muncul dalam nama sungguhan; hanya saja ada tujuh belas pasangan itu berderet. Model punya memori satu huruf, jadi ia tidak bisa tahu bahwa ia sudah berjalan terlalu lama.

Loss pada nama-nama held-out adalah 2,4546 nats. Angka itu tidak berarti apa-apa sendirian, itulah mengapa perplexity ada:

PPL=exp ⁣(1Ttlogq(xtx<t))=eL\mathrm{PPL} = \exp\!\left(-\frac{1}{T}\sum_t \log q(x_t \mid x_{<t})\right) = e^{L}

Ditulis lengkap, tanpa library yang mengerjakan semuanya:

perplexity.pyPYTHON
@torch.no_grad()
def perplexity(logits, Y):
    logp = F.log_softmax(logits, dim=1)          # log q for every symbol
    chosen = logp[torch.arange(len(Y)), Y]       # log q of the one that came next   
    return torch.exp(-chosen.mean())             

Eksponensiasi membatalkan logaritma dan mengembalikan angka itu ke satuan menghitung sesuatu. Cara paling bersih untuk melihat apa yang dihitungnya adalah mengukur model yang tidak tahu apa-apa sama sekali — model yang memberi probabilitas 1/271/27 pada setiap simbol tanpa memedulikan konteks:

TEXT
uniform over 27 symbols            loss 3.2958 nats   ppl  27.000
bigram counts, add-one smoothed    loss 2.4546 nats   ppl  11.642

Tepat 27,000, karena elog27=27e^{\log 27} = 27. Perplexity adalah jumlah efektif opsi yang sama-sama mungkin yang sedang dipilih model. Perplexity 27 berarti “tidak tahu, bisa apa saja”. Nilai 11,642 pada count model berarti satu huruf konteks membuatnya setidaknya sama tidak yakinnya dengan seseorang yang memilih buta dari sekitar dua belas opsi, bukan dua puluh tujuh — itulah mengapa perplexity dikutip dan loss mentah tidak.

Ada dua hal yang salah dengannya, dan yang kedua sering salah dalam paper terbitan.

Probabilitas nol fatal. Dari 729 sel dalam tabel, 113 tidak pernah muncul saat training — 15,5% tabel kosong. Itu tidak masalah sampai held-out set mendarat di salah satunya, dan tujuh bigram dalam validation memang begitu, di antaranya dq, zj, dan qo dua kali. Probabilitas nol berarti log -\infty, yang berarti loss tak hingga dan perplexity tak hingga: satu nama dari tiga ribu menghancurkan metrik. Tambalan biasanya adalah menambahkan 1 ke setiap hitungan sebelum normalisasi, yang biayanya hampir tidak ada di sini (2,4546 alih-alih 2,4524). Tapi tambalan itu adalah pengakuan. Count model tidak bisa generalise sama sekali. Ia tidak punya cara untuk menduga bahwa qo masuk akal karena qu umum dan o berperilaku seperti u di tempat lain, sebab ia tidak punya konsep bahwa dua simbol bisa mirip satu sama lain. Setiap sel dipelajari sendirian, dan memperbaiki itulah tujuan sisa bab ini.

Perplexity adalah harga per token, dan token adalah parameter bebas. Ini kesalahan yang terus muncul saat model dibandingkan, dan mudah terlihat begitu kamu melihatnya. Ambil korpus prosa Inggris yang sama dari Bab 7, interpolated bigram model yang sama, dan ubah hanya cara teksnya dipotong:

unitvocabularytoken di testcross-entropyperplexitybits per character
karakter7614.4692,521712,453,6378
BPE, 512 merge3296.8713,854747,212,6407
BPE, 2.048 merge1.8204.2335,7468313,202,4254
kata2.9916.2843,562735,262,2322

Perplexity bervariasi 25 kali lipat di baris-baris itu. Tidak ada yang berubah pada model; hanya ukuran benda yang diprediksi. Memprediksi satu kata utuh lebih sulit daripada memprediksi satu huruf, jadi biayanya lebih tinggi per prediksi — dan jumlah prediksi yang harus dibuat lebih sedikit.

Sekarang baca kolom terakhir, yang membagi total biaya dengan jumlah karakter dan mengubahnya menjadi bit. Kolom itu mengurutkan ulang tabel. Menurut perplexity, peringkatnya adalah karakter, kata, BPE-512, BPE-2048; menurut bits per character, peringkatnya adalah kata, BPE-2048, BPE-512, karakter. Model karakter berpindah dari posisi pertama ke terakhir. Model 2.048-merge, yang menurut perplexity tampak 6,6 kali lebih buruk daripada model 512-merge, sebenarnya lebih baik dari keduanya pada 2,4254 bit dibanding 2,6407.

Jadi perplexity hanya bisa dibandingkan antara dua model yang memakai tokenizer yang sama, dan model dengan tokenizer berbeda hanya bisa dibandingkan dalam bits per character — besaran yang diukur Shannon pada 1951 dengan meminta subjek manusia menebak huruf berikutnya dari teks Inggris, dan dibatasi kira-kira satu bit per karakter.2 Bigram terbaik kita berada di 2,23 bit, ringkasan yang adil tentang seberapa jauh bab ini masih harus berjalan.

Sekarang bangun model yang sama sebagai network. Ia akan membutuhkan aritmetika berkali-kali lipat lebih banyak untuk sampai ke tempat yang sama, dan sampai ke tempat yang sama itulah intinya.

Ganti tabel dengan satu matriks bobot WW berbentuk 27×2727 \times 27. Ubah huruf saat ini menjadi vektor one-hot, kalikan, dan sebut hasilnya logits — skor yang belum dinormalisasi dari Bab 4. Lalu softmax, lalu cross-entropy, lalu gradient descent.

neural_bigram.pyPYTHON
W = torch.randn((27, 27), requires_grad=True)

for step in range(3000):
    logits = W[xs]                            
    loss = F.cross_entropy(logits, ys)
    W.grad = None
    loss.backward()
    W.data -= 50.0 * W.grad

Baris yang disorot berisi definisi yang layak dimiliki. Mengalikan vektor one-hot dengan matriks memilih satu baris darinya, jadi perkalian itu adalah lookup — dan setiap implementasi melewati aritmetikanya dan melakukan lookup langsung, itulah W[xs].

Itu adalah embedding table. Matriks dengan satu baris per entri vocabulary, diindeks oleh token id. Tidak ada geometri, tidak ada semantik, tidak ada algoritma terpisah: tabel lookup yang isinya kebetulan dipelajari oleh gradient descent bersama semua hal lain. Setiap klaim mistis tentang “embedding space” berakhir di sini.

Latih dan lihat ke mana ia bergerak:

TEXT
  step     1   train 3.7550   val 3.3882   max gap to the count table 0.757269
  step   100   train 2.4732   val 2.4726   max gap to the count table 0.388354
  step  1000   train 2.4557   val 2.4549   max gap to the count table 0.041862
  step  3000   train 2.4547   val 2.4544   max gap to the count table 0.004048

Kolom terakhir adalah selisih absolut terbesar antara sel mana pun dari softmax(W) dan sel yang cocok pada count table, dan nilainya menuju nol. Setelah 3.000 langkah, ketidaksepakatan terbesar di mana pun dalam 729 sel adalah 0,004048 dan mean-nya 0,000224. Sel terburuk adalah qi, terlihat dua belas kali di seluruh training set; di antara 22 baris dengan lebih dari seribu kemunculan, ketidaksepakatan terburuk adalah 0,000562.

TEXT
                 count table   network
    a -> '.'        0.1945     0.1945
    a -> 'n'        0.1601     0.1601
    a -> 'r'        0.0967     0.0967

Gradient descent, mulai dari angka acak dan tidak diberi tahu apa pun selain “buat log-probability huruf berikutnya besar”, menemukan ulang tabel hitungan. Dan memang harus begitu: hitungan adalah estimasi maximum-likelihood, cross-entropy adalah negative log-likelihood, jadi kedua prosedur mengoptimalkan objective yang sama dan objective itu punya satu optimum. Network tidak mempelajari sesuatu yang mirip menghitung. Ia konvergen ke penghitungan, perlahan.

Ini memunculkan pertanyaan wajar: kenapa ada orang yang repot melakukannya. Karena count table tidak punya tempat lagi untuk melangkah dari sini, sementara network punya.

Context adalah bottleneck, bukan capacity

Tautan ke bagian: Context adalah bottleneck, bukan capacity

Perluas model agar melihat lebih dari satu karakter sebelumnya. Ini arsitektur Bengio tahun 2003, leluhur langsung setiap model dalam sisa kursus ini:4 ambil tiga karakter terakhir, petakan masing-masing melalui embedding table menjadi baris 10 dimensi, konkatenasi baris-baris itu menjadi 30 angka, dorong melalui hidden layer dari Bab 5, dan akhiri dengan output layer yang menghasilkan satu logit per entri vocabulary.

mlp.pyPYTHON
C  = torch.randn((27, 10))          # the embedding table
W1 = torch.randn((3 * 10, 200))     # the hidden layer from Chapter 5
W2 = torch.randn((200, 27))         # one output per vocabulary entry

emb = C[X].view(-1, 30)             # three lookups, concatenated   
h = torch.tanh(emb @ W1 + b1)
logits = h @ W2 + b2                
loss = F.cross_entropy(logits, Y)

Perhatikan apa yang baru dan apa yang tidak. Hidden layer adalah milik Bab 5, tanpa perubahan; loss adalah milik Bab 4, tanpa perubahan. Yang baru adalah embedding table di depan dan output layer selebar vocabulary Bab 7 — dan bagian kedua itu adalah bagian mahal dari setiap language model yang pernah dibangun, karena vocabulary sungguhan punya 100.000 entri dan perkalian matriks ini berjalan di setiap posisi.

Kode yang sama, dilatih secara identik, hanya dengan ukuran context window yang diubah:

contextparametervalidation lossvalidation perplexity
counting, 1 karakter7292,454611,642
neural, 1 karakter7.8972,457711,678
neural, 3 karakter11.8972,11458,285
neural, 8 karakter21.8972,05067,773

Baris kedua adalah yang menarik. Network dengan hidden layer 200-unit dan parameter sebelas kali lebih banyak daripada count table berkinerja persis sebaik count table dan tidak lebih baik. Capacity tidak pernah menjadi batasannya. Satu karakter context mengizinkan loss tertentu dan apa pun yang kamu tempelkan tidak bisa turun di bawahnya, karena informasinya tidak ada.

Beri tiga karakter dan perplexity turun dari 11,68 ke 8,29 — pemangkasan 29%, dibeli dengan 4.000 parameter ekstra. Ia mengalahkan counting di sini tepat karena alasan yang didiagnosis sebelumnya: count model atas context tiga karakter membutuhkan 273=19,68327^3 = 19{,}683 baris, sebagian besar kosong atau berisi satu observasi, dan ia mempelajari semuanya sendirian. Network berbagi. Jika a, e, dan i berakhir dengan baris embedding yang mirip, apa yang dipelajarinya setelah bra berpindah ke bre tanpa pernah melihat bre. Transfer itulah seluruh nilai embedding table, dan itulah celah antara baris dua dan tiga.

Sample-nya membaik sesuai itu:

TEXT
deliah   nellara   joce     kael      quintis
salayson  reety    khyrmin  mahnen    madiaryxia

Masih bukan daftar nama sungguhan. Tapi deliah, nellara, dan kael tidak akan terasa janggal di daftar seperti itu, dan monster yang terus berlari sudah hilang: yang terpanjang dari dua puluh sample count model adalah sembilan belas huruf, yang terpanjang dari dua puluh sample model ini adalah tiga belas.

Apa yang sebenarnya ada di dalam embedding table

Tautan ke bagian: Apa yang sebenarnya ada di dalam embedding table

Tabelnya adalah 27×1027 \times 10: satu baris berisi sepuluh angka per karakter, semuanya diinisialisasi acak dan hanya digerakkan oleh gradient dari next-character loss. Tidak ada yang memasukkan apa pun ke sana. Jadi apa yang akhirnya ada di dalamnya?

Alat untuk bertanya adalah cosine similarity, yaitu dot product dari Bab 1 setelah panjangnya dibagi habis:

cos(a,b)=abab\cos(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\lVert \mathbf{a} \rVert \, \lVert \mathbf{b} \rVert}

Ia mengukur sudut antara dua vektor dan mengabaikan panjangnya, yang memang kamu inginkan saat panjang baris mencerminkan seberapa sering token-nya muncul, bukan apa maknanya. Normalisasikan setiap vektor ke panjang 1 terlebih dulu — seperti yang dilakukan sistem nyata, sekali, saat indexing — dan cosine similarity hanyalah dot product.

Berikut nearest neighbours dari beberapa karakter dalam tabel yang sudah dilatih:

TEXT
  'c' -> 'k':+0.598      'j' -> 'z':+0.650      'i' -> 'y':+0.541
  'u' -> 'e':+0.482      'a' -> 'h':+0.367      '.' -> 'q':+0.077

Sebagian dari itu sesuai janji folklor. c dan k bisa saling menggantikan dalam nama, begitu juga i dan y; j dan z sama-sama konsonan langka yang kebanyakan muncul di awal dan berperilaku mirip. Simbol batas . tidak dekat dengan apa pun — 0,077 dari huruf terdekatnya — karena ia satu-satunya simbol yang menandai posisi, bukan bunyi.

Dan sebagian tidak. Nearest neighbour dari a adalah h, bukan vokal lain. Dirata-ratakan atas semua pasangan:

TEXT
mean cosine, vowel to vowel         : +0.1889
mean cosine, consonant to consonant : +0.0765
mean cosine, vowel to consonant     : -0.0042

Vokal lebih mirip satu sama lain daripada dengan konsonan, dan efeknya nyata tetapi kecil. Diuji terhadap 2.000 kelompok acak berisi lima huruf, 58 kelompok itu terpisah setidaknya sebersih ini — celah yang signifikan sekitar p=0.03p = 0.03. Jadi nyata, tetapi sama sekali bukan pulau geometris yang tegas seperti yang disiratkan cerita populer tentang embeddings.

Itulah deskripsi jujur sebuah embedding table dan layak kamu pegang untuk sisa kursus ini. Ia bukan peta makna. Ia adalah perubahan koordinat, dipelajari alih-alih dirancang, yang satu-satunya tugasnya adalah memudahkan pekerjaan layer berikutnya — kalimat yang sama dipakai Bab 5 untuk hidden layer yang melipat bidang untuk menyelesaikan XOR. Struktur apa pun yang kamu temukan di dalamnya ada karena ia menurunkan loss, dan struktur yang tidak menurunkan loss memang tidak ada.

word2vec, GloVe, dan aritmetika yang dikutip semua orang

Tautan ke bagian: word2vec, GloVe, dan aritmetika yang dikutip semua orang

Jika bagian yang berguna adalah tabelnya, kamu bisa mengejarnya secara langsung. Itulah word2vec: pertahankan embedding lookup, buang language model.5

Objective skip-gram with negative sampling adalah satu baris. Untuk pasangan (pusat, konteks) sungguhan yang diambil dari korpus, dorong dot product mereka naik; untuk kk pasangan palsu yang diambil dari distribusi noise, dorong turun:6

logσ(vcvo)+i=1klogσ(vcvni)\log \sigma(\mathbf{v}_c \cdot \mathbf{v}_o) + \sum_{i=1}^{k} \log \sigma(-\mathbf{v}_c \cdot \mathbf{v}_{n_i})

Itu adalah klasifikasi biner — “apakah dua kata ini benar-benar muncul bersama?” — dan murah tepat karena ia tidak pernah menyentuh vocabulary penuh, yang membuat training pada miliaran kata praktis pada 2013. GloVe tiba di vektor serupa dari arah lain, dengan memfaktorkan matriks hitungan co-occurrence global alih-alih streaming melalui contoh.7 Keduanya di-fit persis pada statistik yang dipakai untuk membangun count table. Mereka adalah penghitungan, dikompresi.

Dilatih pada text8 — 17.005.207 kata dari Wikipedia bahasa Inggris, 71.290 di antaranya muncul setidaknya lima kali, 100 dimensi, tiga pass — vektornya keluar dengan properti yang membuatnya terkenal:

TEXT
king     -> charles 0.700, son 0.693, queen 0.686, henry 0.669, throne 0.667
physics  -> chemistry 0.672, electromagnetism 0.661, quantum 0.654, theoretical 0.624
guitar   -> bass 0.733, vocals 0.732, acoustic 0.728, guitars 0.703, drums 0.685
three    -> seven 0.892, two 0.877, one 0.875, five 0.871, four 0.870

Tidak ada yang memberi kategori untuk instrumen atau numeral. Sekarang bagian yang terkenal: ambil king, kurangi man, tambahkan woman, lalu cari vektor terdekat dengan hasilnya.

TEXT
king - man + woman
   nothing excluded : king 0.693, elizabeth 0.657, wife 0.629, woman 0.607
   a, b, c excluded : elizabeth 0.657, wife 0.629, mary 0.607   (queen is 4th, 0.604)

Vektor terdekat dengan king - man + woman adalah king. Itu bukan keanehan dari satu contoh. Evaluation set Mikolov mengajukan pertanyaan berbentuk a : b :: c : ? — 8.869 pertanyaan semantik (paris : france :: rome : italy) dan 10.675 pertanyaan sintaktik (walking : walked :: swimming : swam) — dan dari 4.103 pertanyaan semantik yang bisa dijawab vocabulary ini, pemenangnya adalah salah satu dari tiga kata input 99,8% dari waktu. Demonstrasi yang diterbitkan tidak menyebutkannya, karena aturan scoring standar menghapus a, b, dan c sebelum melihat. Itu aturan yang sah, dan ia bekerja lebih keras daripada aritmetikanya:

cara jawaban dipilihsemantiksintaktik
offset, dengan input dikecualikan (standar)17,0%11,9%
offset, tanpa ada yang dikecualikan0,1%0,4%
nearest neighbour dari c saja, input dikecualikan13,1%9,3%
nearest neighbour dari b saja, input dikecualikan2,3%0,4%

Baris ketiga adalah yang perlu direnungkan. Buang a dan b, jangan lakukan aritmetika sama sekali, kembalikan apa pun yang paling dekat dengan c — dan kamu mempertahankan 77% skor semantik. Sebagian besar yang tampak seperti penalaran analogis adalah kedekatan plus aturan yang melarang jawaban yang jelas, persis seperti yang diukur Linzen pada vektor yang dilatih dengan benar dan seperti yang direplikasi baseline di atas.8 Vektor khusus ini kecil — 17 juta kata dibanding miliaran di balik model yang diterbitkan — jadi baca persentasenya sebagai bentuk, bukan state of the art. Bentuk itulah yang bertahan di setiap skala: aritmetikanya nyata, dan jauh lebih lemah daripada satu demonstrasi yang dikutip semua orang.

Static dan contextual: satu vektor per kata, atau satu per kemunculan

Tautan ke bagian: Static dan contextual: satu vektor per kata, atau satu per kemunculan

Semua sejauh ini punya batas keras yang tertanam dalam struktur datanya. Tabel punya satu baris per token. Kata bank mendapat satu vektor, vektor yang sama dalam kalimat tentang sungai dan kalimat tentang hipotek — harus begitu, karena lookup berdasarkan id tidak bisa bergantung pada hal lain.

Perbaikannya adalah berhenti membaca vektor dari tabel dan mulai menghitungnya dari kalimat. Itulah contextual embedding, diperkenalkan oleh ELMo pada 2018 dan dijadikan standar oleh BERT pada tahun yang sama.910 Diukur pada model sungguhan, angkanya lebih tajam daripada penjelasannya:

TEXT
sentence A: "He sat on the bank of the river and watched the water go by."
sentence B: "She deposited the cheque at the bank on the corner of the street."

static vector for 'bank' (a row of the input embedding table)
    cosine A vs B ........................ 1.000000

contextual vector for 'bank', layer by layer
    layer  |  A vs B  |  A vs another river sentence  |  B vs another money sentence
        0  |  0.9512  |            0.9512             |            0.9359
        4  |  0.5647  |            0.8987             |            0.7716
        9  |  0.4284  |            0.8699             |            0.7568
       12  |  0.5278  |            0.8702             |            0.7335

Baris pertama persis, bukan perkiraan: vektor static untuk bank adalah 768 angka yang sama di kedua kalimat, jadi cosine-nya 1 by construction. Sembilan layer kemudian, dua kemunculan itu berada di 0,43, sementara bank dalam dua kalimat sungai berbeda tetap di 0,87. Tidak ada sense yang dilabeli di mana pun dalam proses ini; sense terpisah karena memisahkannya membuat training objective — menebak token tersembunyi dari tetangganya — lebih mudah dipenuhi.

Dua detail layak diperhatikan. Layer 0 sudah 0,9512, bukan 1,0, karena position embeddings sudah ditambahkan dan kata itu berada di tempat berbeda dalam tiap kalimat. Dan similarity naik lagi pada layer 11 dan 12: layer akhir pretrained model terspesialisasi untuk training objective-nya, dan sering kali bukan tempat terbaik untuk mengambil representasi.

Tampilkan detail

Opsional: weight tying.

Dalam bert-base-uncased, embedding table adalah 30,522×76830{,}522 \times 768 — 23.440.896 angka, 21,4% dari 109.482.240 parameter model. Dalam language model kecil, fraksinya lebih besar lagi, itulah mengapa satu trik hampir universal: input table dan output layer yang menghasilkan logits adalah matriks yang sama, dipakai sekali lewat row lookup dan sekali dalam bentuk transposed.11 Output layer sudah memberi setiap entri vocabulary sebuah vektor — ia mengambil dot product terhadap masing-masing — dan tying mengatakan vektor yang dipakai untuk membaca token dan vektor yang dipakai untuk menulisnya seharusnya objek yang sama. Ia memangkas parameter dan meningkatkan perplexity sekaligus, hal yang cukup langka untuk diperhatikan.

Untuk mencari korpus berdasarkan makna, kamu membutuhkan satu vektor per kalimat. Dengan vektor-vektor itu, pencariannya trivial — inilah seluruh semantic retrieval, dan Bab 19 membahas segala hal di sekitarnya:

search.pyPYTHON
E = normalise(embed(sentences))       # (200, d), every row of length 1
q = normalise(embed([query]))         # (1, d)
scores = q @ E.T                      # one matrix multiply   
top5 = scores[0].argsort()[::-1][:5]

Jadi satu-satunya pertanyaan nyata adalah dari mana embed berasal. Langkah yang jelas adalah mengambil pretrained language model, menjalankan setiap kalimat melaluinya, lalu merata-ratakan vektor token. Inilah metode itu dibanding empat alternatif, dinilai dengan dua cara: rank correlation antara cosine dan penilaian similarity manusia atas 1.379 pasangan benchmark STS, dan top-1 retrieval pada index yang dibangun dari 200 pasangan paling kuat terparafrasekan — satu sisi tiap pasangan di-index, sisi lain dipakai sebagai query.

cara kalimat di-embedrank correlationtop-1 pada index 200 kalimat
binary word overlap (tanpa model sama sekali)0,550089,0%
mean dari vektor static yang dilatih di atas0,526385,5%
BERT, token [CLS]0,203067,0%
BERT, mean dari vektor token0,472984,0%
MiniLM, dilatih secara contrastive0,820392,0%

Baca tiga baris tengah dibanding dua baris pertama. Pretrained transformer 109 juta parameter, digunakan dengan cara yang jelas, lebih buruk dalam menilai similarity kalimat daripada menghitung berapa banyak kata yang dimiliki dua kalimat bersama — dan lebih buruk daripada merata-ratakan vektor text8 100 dimensi yang baru dilatih tadi. Token [CLS], yang masih direkomendasikan tutorial karena BERT dipretrain dengan objective tingkat kalimat yang melekat padanya, lebih buruk daripada setengah dari itu.

Ini bukan cacat pada BERT. Ini objective-nya. Language model dilatih agar hidden states-nya memprediksi token; tidak ada di sana yang meminta dua parafrasa berakhir dekat satu sama lain, dan tidak ada yang memberi reward geometri di mana cosine berarti “makna yang sama”. Baris terakhir adalah model berukuran seperlima (22.713.216 parameter) yang dilatih pada loss yang sepenuhnya berbeda: contrastive learning, dengan contoh berupa pasangan — pertanyaan dan jawabannya, kalimat dan parafrasanya — dan objective menarik pasangan benar agar mendekat sambil mendorong sampled negatives menjauh. Itulah kontribusi Sentence-BERT dan asal-usul seluruh industri embedding-model.12 Dense Passage Retrieval menerapkan resep yang sama langsung ke search, dengan satu encoder untuk query dan satu untuk passage.13

Jadi, aturan praktisnya:

Embedding model bukan language model yang layer terakhirnya dilepas. Ia adalah model berbeda dengan objective berbeda, biasanya jauh lebih kecil, yang cosine-nya berarti apa yang kamu inginkan karena ia dilatih pada pasangan yang menjadikan hal itu targetnya. Tabel di atas adalah biaya ketika yang satu digantikan dengan yang lain.

Dan keluarga model ini gagal pada urutan kata. “The dog bit the man” dan “the man bit the dog” punya bag of words yang identik, jadi word overlap dan rata-rata vektor static memberi cosine persis 1,000000, dan mean-pooled BERT, yang memang melihat posisi, masih mendarat hampir di sana — dan MiniLM yang dilatih secara contrastive masih menaruh keduanya di 0,979. Jika retrieval task kamu bergantung pada siapa melakukan apa kepada siapa, tidak ada cosine threshold yang akan menyelamatkanmu.

Bab 19 membangun sistem retrieval produksi di atas pijakan ini dan sampai pada cut-off cosine yang konkret. Pengukuran terakhir dalam bab ini adalah yang membuat angka seperti itu bisa dipertanggungjawabkan, bukan sihir.

Kutukan dimensionality, dalam satu tabel

Tautan ke bagian: Kutukan dimensionality, dalam satu tabel

Embeddings nyata punya ratusan atau ribuan komponen, dan jarak berperilaku aneh di sana. Ambil 1.000 titik acak dalam unit cube berdimensi dd dan lihat rasio antara jarak terbesar dan terkecil di antara pasangan mana pun:

dimensipasangan terdekatpasangan terjauhrasio
20,00071,36121921,66
100,23612,33979,91
1003,00475,17521,72
1.00011,780914,03061,19
10.00039,615242,01251,06

Dalam sepuluh ribu dimensi, pasangan titik terjauh hanya 6% lebih jauh daripada pasangan terdekat. Semuanya kira-kira berjarak sama dari yang lain, “nearest neighbour” berhenti membawa banyak informasi, dan itulah curse of dimensionality — sekaligus salah satu alasan vector database besar tidak melakukan exact nearest-neighbour search. Sisi lain dari koin yang sama adalah hal yang membuat cosine threshold bisa dipakai: diukur atas seribu pasangan vektor unit acak, mean cosine berada di 0.0052-0.0052 dalam 100 dimensi dan +0.0003+0.0003 dalam 768, dengan standard deviation 0,0968 dan 0,0357 — dan dalam 768 dimensi hanya 0,2% pasangan acak yang melebihi 0,1 dalam nilai absolut. Similarity terukur 0,4 karena itu bukan “40% mirip”; ia jauh di luar apa pun yang dihasilkan kebetulan, itulah mengapa threshold antara 0,3 dan 0,7 memisahkan signal dari noise, bukan duduk di tengah-tengahnya.

Model dalam bab ini membaca jumlah tetap karakter sebelumnya, melakukan lookup masing-masing, dan menempelkan hasilnya bersama-sama secara berurutan. Desain itu punya dua masalah, dan keduanya adalah masalah yang sama.

Lihat lagi tabel context: berpindah dari tiga karakter ke delapan hampir menggandakan parameter dan hanya membeli 0,06 nats. Biaya tumbuh linear dengan context — setiap posisi ekstra membutuhkan slab-nya sendiri pada matriks bobot pertama — sedangkan manfaatnya tidak. Dorong hingga seribu token dan layer pertama saja lebih berat daripada sisa model, sebagian besar dihabiskan pada posisi yang tidak relevan untuk prediksi tertentu.

Itulah masalah kedua: model tidak punya cara untuk memutuskan token sebelumnya mana yang penting. Posisi dua punya bobotnya sendiri dan posisi tujuh punya bobotnya sendiri, secara permanen, apa pun isinya. Saat model mengeja nell, karakter penentunya adalah yang tepat sebelumnya. Saat sebuah kalimat mengandung pronomina, kata yang menetapkan referennya mungkin berada empat puluh token di belakang — dan tidak ada slot tetap yang bisa ditugaskan untuk “empat puluh di belakang”, karena lain kali bisa jadi enam.

Yang kita inginkan adalah model yang menghitung, untuk setiap prediksi, seberapa besar setiap token sebelumnya harus dihitung — bobot atas context yang dihasilkan oleh konten, bukan ditetapkan oleh tata letak. Tulis itu dengan hati-hati dan ia bermula sebagai sesuatu yang sepenuhnya biasa: rata-rata atas token-token sebelumnya. Lalu biarkan bobot rata-rata itu dipelajari, dan biarkan mereka bergantung pada token mana yang sedang bertanya.

Itulah attention, dan itu Bab 9.


Juga layak dibaca berdampingan: bab 3 dari Speech and Language Processing karya Jurafsky dan Martin, yang membahas n-gram model, smoothing, dan perplexity jauh lebih hati-hati daripada ruang yang tersedia di sini, termasuk mengapa interpolation dan back-off mengalahkan menambahkan satu; catatan Stanford CS229 §17.1–17.2 untuk language modelling dari sisi probabilistik; dan paper Linzen di atas, yang singkat dan layak dibaca penuh.

  1. Contoh generasi nama, dataset, dan progresi dari count table ke network bergaya Bengio mengikuti seri building makemore dari Andrej Karpathy, yang dua bagian pertamanya adalah pendamping terbaik untuk bab ini.

  2. Shannon, C. E. Prediction and Entropy of Printed English. Bell System Technical Journal 30(1), hlm. 50–64 (1951). Subjek manusia menebak huruf berikutnya dari bahasa Inggris tertulis, dan pengukuran bits-per-character yang asli.

  3. Shannon, C. E. A Mathematical Theory of Communication. Bell System Technical Journal 27 (1948). Teorema source coding, dan identifikasi prediksi dengan kompresi.

  4. Bengio, Y., Ducharme, R., Vincent, P. dan Jauvin, C. A Neural Probabilistic Language Model. Journal of Machine Learning Research 3, hlm. 1137–1155 (2003). Arsitektur yang dipakai di atas: satu embedding per kata, dikonkatenasi dalam window tetap, melalui hidden layer, ke softmax atas vocabulary.

  5. Mikolov, T., Chen, K., Corrado, G. dan Dean, J. Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781 (2013). CBOW dan skip-gram, serta analogy set yang dipakai di atas.

  6. Mikolov, T., Sutskever, I., Chen, K., Corrado, G. dan Dean, J. Distributed Representations of Words and Phrases and their Compositionality. arXiv:1310.4546 (2013). Negative sampling, subsampling kata-kata sering, dan distribusi noise yang dipangkatkan 3/4 seperti dipakai di atas.

  7. Pennington, J., Socher, R. dan Manning, C. GloVe: Global Vectors for Word Representation. EMNLP 2014. Word vectors dari faktorisasi matriks co-occurrence global, bukan dari window lokal yang di-stream.

  8. Linzen, T. Issues in evaluating semantic spaces using word analogies. RepEval 2016, arXiv:1606.07736. Sumber baseline tanpa offset yang direplikasi di atas.

  9. Peters, M. et al. Deep contextualized word representations. arXiv:1802.05365 (2018). ELMo: satu vektor per kemunculan, dihitung oleh bidirectional language model.

  10. Devlin, J., Chang, M.-W., Lee, K. dan Toutanova, K. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv:1810.04805 (2018). Model yang diukur dalam eksperimen bank.

  11. Press, O. dan Wolf, L. Using the Output Embedding to Improve Language Models. arXiv:1608.05859 (2016), dan Inan, H., Khosravi, K. dan Socher, R. Tying Word Vectors and Word Classifiers. arXiv:1611.01462 (2016). Dua argumen independen untuk trik yang sama.

  12. Reimers, N. dan Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. arXiv:1908.10084 (2019). Pengukuran pembukanya — mean-pooled BERT kalah dari rata-rata vektor static pada sentence similarity — adalah yang direproduksi tabel di atas.

  13. Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906 (2020). Contrastive training untuk retriever dua-encoder; leluhur langsung retrieval stack Bab 19.


Dibuat oleh

David Vicente Campos

Pendiri NeuraLIA Labs & salah satu pendiri MyRealFood

Saya seorang insinyur komputer lulusan Universitas León. Saya ikut mendirikan MyRealFood, tempat saya sebagai CTO membangun aplikasi yang telah digunakan jutaan orang untuk makan lebih sehat, dan saya mendirikan NeuraLIA Labs, tempat saya membangun produk AI. Di sini saya menulis tentang hal-hal yang harus saya pahami sepanjang perjalanan, sebagaimana dulu saya berharap ada yang menjelaskannya kepada saya.

Selengkapnya tentang penulis

Diterbitkan oleh NeuraLIA Labs.

Dapatkan postingan baru di inbox kamu

Berita AI, panduan, dan update produk — email singkat saat kami menerbitkan sesuatu yang layak kamu baca.

Indeks kursus

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev11 menit baca

Model AI Jev dibuat untuk keputusan, bukan prosa

Jev dari TypeSafe AI menarik perhatian karena memperlakukan kecerdasan software sebagai persoalan probabilitas: pilih cabang yang tepat, sertakan keyakinan, dan hindari membayar LLM untuk menulis teks saat kode membutuhkan keputusan.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering11 menit baca

Rekayasa konteks untuk agen AI jangka panjang

Agen yang berjalan lama tidak gagal hanya karena window-nya kecil. Mereka gagal ketika file, output tool, dan riwayat lama menggeser tugas yang seharusnya diselesaikan agen.

Siap membiarkan LIA yang memilih?

Berkarya dengan semua model AI dalam satu tempat — mulai gratis hari ini.