การทำนาย Next-Token: Embedding และความหมายของ Perplexity
ฝึกโมเดลอักขระกับชื่อ 32,033 ชื่อ ดู gradient descent ค้นพบตารางนับใหม่ แล้วเหตุใด perplexity จึงมักเทียบกันไม่ได้
ในหน้านี้
นี่คือชื่อสิบชื่อที่สร้างโดยโปรแกรมซึ่งไม่เคยเห็นคำมาก่อน:
cexze momakurailezitynn konimittain llayn ka
da moliellavo emia sade ftlspไม่มีชื่อใดเป็นชื่อจริงเลย แต่เกือบทั้งหมดกำลัง พยายาม อยู่ มันออกเสียงได้ จบตรงที่ชื่อมักจบ และหนึ่งในนั้น — emia — ต่างจากชื่อจริงเพียงตัวอักษรเดียว โปรแกรมที่สร้างชื่อเหล่านี้เก็บตัวเลขไว้ 729 ตัว ไม่มีแนวคิดเรื่องคำ พยางค์ หรือบุคคล และถูก fit ด้วยการนับคู่ตัวอักษรที่อยู่ติดกันเพียงรอบเดียว
เมื่อจบบทนี้ neural network จะลดคะแนนของโปรแกรมนั้นลงได้หนึ่งในสามบนการวัดเดียวกัน ส่วนที่คุ้มค่าจะติดตามคือสิ่งที่ network ทำก่อน: มัน สร้างตารางนับซ้ำได้ถูกต้องถึงทศนิยมสามตำแหน่งในทุกแถวที่มีข้อมูลมากพอ โดยไม่มีใครสั่ง เพราะวัตถุทั้งสองคือคำตอบของคำถามเดียวกัน ทุกอย่างหลังจากนั้นคือสิ่งที่การนับไม่มีวันทำได้
Objective คือเอกลักษณ์ ไม่ใช่การตัดสินใจออกแบบ
ลิงก์ไปยังส่วน: Objective คือเอกลักษณ์ ไม่ใช่การตัดสินใจออกแบบบทที่ 7 ทิ้งคุณไว้กับลำดับของจำนวนเต็มและไม่มีเหตุผลว่าทำไมตัวหนึ่งจึงตามอีกตัวหนึ่งมา นี่คือเหตุผล และมันคือหนึ่งบรรทัดจาก บทที่ 2
language model คือฟังก์ชันที่รับ tokens ที่มีจนถึงตอนนี้ แล้วคืน distribution ว่า token ใดจะมาต่อไป: หนึ่งตัวเลขต่อหนึ่งรายการใน vocabulary ไม่ติดลบ และรวมกันได้หนึ่ง ไม่มีอย่างอื่นอีก จากตรงนั้น หากต้องการ probability ของ ทั้งเอกสาร ให้ใช้ chain rule ของ probability:
นั่นคือ identity เป็นจริงกับลำดับใด ๆ ของอะไรก็ได้ โดยไม่มีสมมติฐานพ่วงมา ดังนั้น model ที่ทำงานเล็ก ๆ — ทำนาย token ถัดไปจาก token ก่อนหน้า — ก็ได้ทำงานใหญ่ในการกำหนด probability ให้เอกสารทุกฉบับที่เป็นไปได้แล้ว อย่างถูกต้องและฟรี การพูดกันทั่วไปว่านี่เป็นกลลวงราคาถูก (“มันแค่ทำนายคำถัดไป”) กลับตรรกะด้านผิด: การทำนาย token ถัดไป คือ การ model joint distribution ไม่เคยมีงานชิ้นที่สองให้ทำเลย
loss ตามมาแบบกลไกไม่ต่างกัน ในแต่ละตำแหน่ง model สร้าง distribution และความจริงคือ token ที่รู้แน่นอนหนึ่งตัว ดังนั้น cross-entropy จาก บทที่ 4 จึงใช้ได้เหมือนเดิม:
นั่นคือ average negative log-likelihood — สูตรของบทที่ 2 โดยใส่ categorical distribution ลงในช่องที่เดิมเป็น Gaussian และเพราะ true distribution เป็น one-hot entropy ของมันจึงเป็นศูนย์ ดังนั้นตาม identity ของบทที่ 4 cross-entropy จึง เท่ากับ KL divergence: การลดตัวเลขนี้ลงกับการดึงความเชื่อของ model ให้เข้าใกล้ข้อมูลคือการกระทำเดียวกัน
ผลตามมาข้อหนึ่งสมควรมีประโยคของตัวเอง เพราะมันคือข้อเท็จจริงทางเศรษฐศาสตร์ใต้ทั้งวงการนี้ labels คือข้อมูลที่เลื่อนไปหนึ่งตำแหน่ง ไม่มีใครต้อง annotate อะไร text หนึ่งล้านล้าน tokens คือ examples ที่ติด label ไว้แล้วหนึ่งล้านล้านตัว นี่คือเหตุผลที่ training corpus ของ model สมัยใหม่คือ “อินเทอร์เน็ต” ไม่ใช่ “dataset ที่ใครบางคนสร้างขึ้น”
Baseline ที่ซื่อสัตย์: การนับ
ลิงก์ไปยังส่วน: Baseline ที่ซื่อสัตย์: การนับก่อนมี network ใด ๆ เริ่มจาก baseline: ชื่อ 32,033 ชื่อ หนึ่งชื่อต่อหนึ่งบรรทัด และงานคือสร้างชื่อเพิ่มทีละตัวอักษร1
Vocabulary คือ 26 ตัวอักษรบวก boundary symbol . ที่ทำเครื่องหมายทั้งจุดเริ่มและจุดจบของชื่อ ดังนั้น model ต้องเรียนรู้ว่าชื่อเริ่มตรงไหนและหยุดตรงไหน นั่นคือ 27 สัญลักษณ์ และ model ที่เล็กที่สุดเท่าที่เป็นไปได้คือตารางที่บอกว่าสัญลักษณ์แต่ละตัวตามหลังสัญลักษณ์อีกตัวบ่อยแค่ไหน
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 คำนวณเลขสองบรรทัดแล้ว model ก็ fit เสร็จ — และนี่ไม่ใช่ heuristic: การหาร counts ด้วยผลรวมรายแถวคือ maximum-likelihood estimate สำหรับ categorical distribution ซึ่งเป็นสูตรของบทที่ 2 หลังทำ calculus ให้เรียบร้อยแล้ว
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.0606Sample จากมัน — เลือกตัวอักษรจากแถวของตัวอักษรปัจจุบัน ย้ายไปแถวนั้น ทำซ้ำจนกว่า boundary symbol จะปรากฏ — แล้วคุณจะได้ชื่อที่อยู่ด้านบนของบทนี้ มันล้มเหลวในแบบที่เฉพาะเจาะจงและให้ข้อมูล: plausible ในระดับ local แต่ไร้สาระในระดับ global คู่ตัวอักษรที่ติดกันทุกคู่ใน momakurailezitynn เป็นคู่ที่เกิดขึ้นในชื่อจริง เพียงแต่มันมีถึงสิบเจ็ดคู่เรียงติดกัน model มี memory แค่หนึ่งตัวอักษร จึงไม่รู้ว่ามันลากยาวเกินไปแล้ว
Perplexity และวิธีอ่านมัน
ลิงก์ไปยังส่วน: Perplexity และวิธีอ่านมันloss บนชื่อที่กันไว้ทดสอบคือ 2.4546 nats ตัวเลขนี้ไม่มีความหมายมากนักด้วยตัวมันเอง นี่คือเหตุผลที่ perplexity มีอยู่:
เขียนออกมาโดยไม่มี library ทำงานให้:
@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()) การยกกำลังกลับ undo logarithm และคืนตัวเลขไปสู่หน่วยของ การนับสิ่งของ วิธีที่สะอาดในการดูว่ามันนับอะไรคือวัด model ที่ไม่รู้อะไรเลย — model ที่กำหนด probability ให้ทุกสัญลักษณ์โดยไม่สน context:
uniform over 27 symbols loss 3.2958 nats ppl 27.000
bigram counts, add-one smoothed loss 2.4546 nats ppl 11.642ได้ 27.000 พอดี เพราะ Perplexity คือจำนวน effective ของตัวเลือกที่มีโอกาสเท่ากันซึ่ง model กำลังเลือกอยู่ Perplexity 27 หมายถึง “ไม่รู้เลย อะไรก็เป็นได้” ค่า 11.642 ของ count model หมายความว่า context หนึ่งตัวอักษรทำให้มันยังไม่แน่ใจเทียบเท่ากับคนที่สุ่มเลือกจากประมาณสิบสองตัวเลือกแทนที่จะเป็นยี่สิบเจ็ด — นี่คือเหตุผลที่คนอ้าง perplexity ไม่ใช่ raw loss
มีสองอย่างที่มักผิดพลาด และอย่างที่สองผิดพลาดแม้ใน papers ที่ตีพิมพ์แล้ว
Zero probabilities เป็นอันตรายถึงตาย จาก 729 cells ในตาราง มี 113 cells ที่ไม่เคยเกิดในการ train — 15.5 % ของตารางว่าง นั่นไม่เป็นไรจนกว่า held-out set จะตกลงไปใน cell หนึ่ง และมี bigrams เจ็ดตัวใน validation ที่เป็นเช่นนั้น รวมถึง d→q, z→j และ q→o สองครั้ง Probability เป็นศูนย์หมายถึง log ซึ่งหมายถึง loss อนันต์และ perplexity อนันต์: ชื่อหนึ่งชื่อในสามพันชื่อทำลาย metric ได้ วิธีแก้ปกติคือบวก 1 ให้ทุก count ก่อน normalise ซึ่งแทบไม่เสียอะไรที่นี่ (2.4546 แทน 2.4524) แต่ patch นี้คือคำสารภาพ count model ไม่สามารถ generalise ได้เลย มันไม่มีทางสงสัยว่า q→o plausible เพราะ q→u พบได้บ่อยและ o มีพฤติกรรมเหมือน u ในที่อื่น เพราะมันไม่มีแนวคิดว่าสองสัญลักษณ์อาจคล้ายกันได้ ทุก cell ถูกเรียนแยกกัน และการแก้สิ่งนี้คือสิ่งที่ส่วนที่เหลือของบทนี้ทำ
Perplexity คือราคาต่อ token และ token เป็น free parameter นี่คือข้อผิดพลาดที่โผล่มาตลอดเมื่อเปรียบเทียบ models และเห็นได้ง่ายเมื่อคุณมอง ลองเอา corpus เดียวกันของ prose ภาษาอังกฤษจากบทที่ 7, interpolated bigram model เดียวกัน และเปลี่ยนเฉพาะวิธีตัด text:
| unit | vocabulary | tokens in test | cross-entropy | perplexity | bits per character |
|---|---|---|---|---|---|
| characters | 76 | 14,469 | 2.5217 | 12.45 | 3.6378 |
| BPE, 512 merges | 329 | 6,871 | 3.8547 | 47.21 | 2.6407 |
| BPE, 2,048 merges | 1,820 | 4,233 | 5.7468 | 313.20 | 2.4254 |
| words | 2,991 | 6,284 | 3.5627 | 35.26 | 2.2322 |
Perplexity แปรผันต่างกันถึง 25 เท่าระหว่างแถวเหล่านี้ ไม่มีอะไรเกี่ยวกับ model ที่เปลี่ยนไป มีเพียงขนาดของสิ่งที่กำลังถูกทำนายเท่านั้นที่เปลี่ยน การทำนายคำทั้งคำยากกว่าการทำนายตัวอักษร จึงมีต้นทุนสูงกว่าต่อการทำนาย — และมีจำนวนการทำนายน้อยกว่า
ตอนนี้อ่านคอลัมน์สุดท้าย ซึ่งหารต้นทุนรวมด้วยจำนวน characters แทน แล้วแปลงเป็น bits มันจัดลำดับตารางใหม่ ถ้าดูด้วย perplexity อันดับคือ characters, words, BPE-512, BPE-2048; ถ้าดูด้วย bits per character อันดับคือ words, BPE-2048, BPE-512, characters ตัว character model จากอันดับหนึ่งกลายเป็นอันดับสุดท้าย model 2,048-merge ซึ่งดูจาก perplexity เหมือนแย่กว่า 512-merge 6.6 เท่า แท้จริงแล้วดีกว่าในสองตัวนี้ที่ 2.4254 bits เทียบกับ 2.6407
ดังนั้น perplexity จะเปรียบเทียบกันได้เฉพาะระหว่างสอง models ที่ใช้ tokenizer เดียวกัน และ models ที่มี tokenizer ต่างกันจะเปรียบเทียบกันได้เฉพาะใน bits per character — ปริมาณที่ Shannon วัดในปี 1951 โดยให้มนุษย์เดาตัวอักษรถัดไปของ text ภาษาอังกฤษ และตีกรอบไว้ประมาณหนึ่ง bit ต่อ character2 bigram ที่ดีที่สุดของเราอยู่ที่ 2.23 bits ซึ่งสรุปได้ดีว่าบทนี้ยังต้องไปอีกไกลแค่ไหน
สิ่งเดียวกัน แต่เรียนรู้ขึ้นมา
ลิงก์ไปยังส่วน: สิ่งเดียวกัน แต่เรียนรู้ขึ้นมาตอนนี้สร้าง model เดียวกันในรูป network มันจะใช้ arithmetic มากกว่าเดิมหลาย orders of magnitude เพื่อไปถึงที่เดียวกัน และการไปถึงที่เดียวกันนี่เองคือประเด็น
แทนที่ตารางด้วย weight matrix หนึ่งตัว ที่มี shape เปลี่ยนตัวอักษรปัจจุบันเป็น one-hot vector คูณ แล้วเรียกผลลัพธ์ว่า logits — คะแนนที่ยังไม่ normalise จากบทที่ 4 จากนั้น softmax จากนั้น cross-entropy จากนั้น gradient descent
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บรรทัดที่ไฮไลต์มีนิยามที่ควรเก็บไว้ การคูณ one-hot vector ด้วย matrix คือการเลือกหนึ่งแถวของ matrix นั้น ดังนั้นการคูณนี้คือ lookup — และ implementation ทุกตัวข้าม arithmetic แล้วทำ lookup โดยตรง ซึ่งก็คือสิ่งที่ W[xs] เป็น
นั่นคือ embedding table matrix ที่มีหนึ่งแถวต่อหนึ่งรายการใน vocabulary และถูก index ด้วย token id ไม่มี geometry ไม่มี semantics ไม่มี algorithm แยกต่างหาก: เป็น lookup table ที่เนื้อหาของมันบังเอิญถูกเรียนรู้ด้วย gradient descent พร้อมกับทุกอย่างอื่น คำกล่าวลึกลับทั้งหลายเกี่ยวกับ “embedding space” มาจบที่ตรงนี้
Train มันแล้วดูว่ามันไปไหน:
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คอลัมน์สุดท้ายคือ absolute difference ที่ใหญ่ที่สุดระหว่าง cell ใด ๆ ของ softmax(W) กับ cell ที่ตรงกันใน count table และมันลดลงสู่ศูนย์ หลัง 3,000 steps ความเห็นไม่ตรงกันที่ใหญ่ที่สุดใน 729 cells คือ 0.004048 และค่าเฉลี่ยคือ 0.000224 cell ที่แย่ที่สุดคือ q→i ซึ่งพบเพียงสิบสองครั้งใน training set ทั้งหมด; ใน 22 แถวที่มี occurrence มากกว่าหนึ่งพันครั้ง ความเห็นไม่ตรงกันที่แย่ที่สุดคือ 0.000562
count table network
a -> '.' 0.1945 0.1945
a -> 'n' 0.1601 0.1601
a -> 'r' 0.0967 0.0967Gradient descent เริ่มจากตัวเลขสุ่ม และได้รับคำสั่งเพียงว่า “ทำให้ log-probability ของตัวอักษรถัดไปสูง” แล้วก็ ค้นพบตารางนับอีกครั้ง และมันจำเป็นต้องทำเช่นนั้น: counts คือ maximum-likelihood estimate, cross-entropy คือ negative log-likelihood ดังนั้นทั้งสองกระบวนการ optimise objective เดียวกัน และ objective นั้นมี optimum หนึ่งเดียว network ไม่ได้เรียนรู้สิ่งที่ คล้าย การนับ มัน converge ไปสู่การนับอย่างช้า ๆ
ซึ่งนำไปสู่คำถามที่ยุติธรรมว่า แล้วทำไมใครต้องลำบากทำด้วย เพราะ count table ไปต่อจากตรงนี้ไม่ได้ แต่ network ไปได้
Context คือคอขวด ไม่ใช่ capacity
ลิงก์ไปยังส่วน: Context คือคอขวด ไม่ใช่ capacityขยาย model ให้มองมากกว่าหนึ่ง character ก่อนหน้า นี่คือ architecture ปี 2003 ของ Bengio บรรพบุรุษโดยตรงของทุก model ในส่วนที่เหลือของคอร์สนี้:4 นำ characters สามตัวล่าสุด map แต่ละตัวผ่าน embedding table เป็นแถว 10 มิติ concatenate แถวเหล่านั้นเป็นตัวเลข 30 ตัว ผลักผ่าน hidden layer ของ บทที่ 5 แล้วจบด้วย output layer ที่สร้างหนึ่ง logit ต่อหนึ่งรายการใน vocabulary
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)สังเกตว่าสิ่งใดใหม่และสิ่งใดไม่ใหม่ hidden layer คือของบทที่ 5 ไม่เปลี่ยน; loss คือของบทที่ 4 ไม่เปลี่ยน สิ่งใหม่คือ embedding table ด้านหน้าและ output layer ที่กว้างเท่ากับ vocabulary ของบทที่ 7 — และอย่างหลังคือส่วนแพงของ language model ทุกตัวที่เคยสร้างมา เพราะ vocabulary จริงมี 100,000 รายการ และ matrix multiply นี้รันในทุกตำแหน่ง
code เดียวกัน train แบบเดียวกัน โดยเปลี่ยนเฉพาะขนาดของ context window:
| context | parameters | validation loss | validation perplexity |
|---|---|---|---|
| counting, 1 character | 729 | 2.4546 | 11.642 |
| neural, 1 character | 7,897 | 2.4577 | 11.678 |
| neural, 3 characters | 11,897 | 2.1145 | 8.285 |
| neural, 8 characters | 21,897 | 2.0506 | 7.773 |
แถวที่สองคือแถวที่น่าสนใจ network ที่มี hidden layer 200 units และมี parameters มากกว่า count table สิบเอ็ดเท่าทำงานได้ ดีเท่ากับ count table และไม่ดีกว่านั้นเลย capacity ไม่เคยเป็นข้อจำกัด context หนึ่ง character อนุญาตให้ loss ต่ำได้ระดับหนึ่ง และไม่ว่าคุณจะต่ออะไรเพิ่มก็ลงต่ำกว่านั้นไม่ได้ เพราะข้อมูลไม่ได้อยู่ตรงนั้น
ให้มันสาม characters แล้ว perplexity ลดจาก 11.68 เป็น 8.29 — ลดลง 29 % ซื้อด้วย parameters เพิ่ม 4,000 ตัว มันชนะการนับที่นี่ด้วยเหตุผลเดียวกับที่วินิจฉัยไว้ก่อนหน้า: count model เหนือ contexts สาม characters ต้องมี แถว ส่วนใหญ่เป็นแถวว่างหรือมี observation เดียว และมันเรียนแต่ละแถวแยกกัน network แชร์ได้ หาก a, e และ i ลงเอยด้วย embedding rows ที่คล้ายกัน สิ่งที่มันเรียนหลัง bra จะ transfer ไปยัง bre ได้โดยไม่เคยเห็น bre เลย การ transfer นั้นคือคุณค่าทั้งหมดของ embedding table และคือช่องว่างระหว่างแถวสองกับแถวสาม
Samples ก็ดีขึ้นตามนั้น:
deliah nellara joce kael quintis
salayson reety khyrmin mahnen madiaryxiaยังไม่ใช่รายชื่อจริง แต่ deliah, nellara และ kael จะไม่ดูแปลกถ้าอยู่ในรายชื่อหนึ่ง และสัตว์ประหลาดที่ลากยาวหายไปแล้ว: ตัวที่ยาวที่สุดใน 20 samples จาก count model มีสิบเก้าตัวอักษร ส่วนตัวที่ยาวที่สุดใน 20 samples จาก model นี้มีสิบสามตัว
จริง ๆ แล้วมีอะไรอยู่ใน embedding table
ลิงก์ไปยังส่วน: จริง ๆ แล้วมีอะไรอยู่ใน embedding tableตารางคือ : หนึ่งแถวที่มีตัวเลขสิบตัวต่อหนึ่ง character ทั้งหมด initialise แบบสุ่ม และเคลื่อนที่ด้วย gradient ของ next-character loss เท่านั้น ไม่มีใครใส่อะไรลงไป แล้วอะไรลงเอยอยู่ในนั้น?
เครื่องมือที่ใช้ถามคือ cosine similarity ซึ่งคือ dot product จาก บทที่ 1 โดยหารความยาวออกไป:
มันวัดมุมระหว่างสอง vectors และไม่สนความยาว ซึ่งเป็นสิ่งที่คุณต้องการเมื่อความยาวของแถวสะท้อนว่า token ของมันปรากฏบ่อยแค่ไหน ไม่ใช่มันหมายถึงอะไร normalize ทุก vector ให้ยาว 1 ก่อน — เช่นเดียวกับระบบจริงที่ทำครั้งเดียวตอน indexing — แล้ว cosine similarity ก็เป็นเพียง dot product
นี่คือ nearest neighbours ของ characters บางตัวในตารางที่ train แล้ว:
'c' -> 'k':+0.598 'j' -> 'z':+0.650 'i' -> 'y':+0.541
'u' -> 'e':+0.482 'a' -> 'h':+0.367 '.' -> 'q':+0.077บางส่วนตรงกับสิ่งที่ folklore สัญญาไว้ c และ k ใช้แทนกันได้ในชื่อ และ i กับ y ก็เช่นกัน; j และ z ต่างเป็น consonants ที่พบยาก มักอยู่ต้นคำ และมีพฤติกรรมคล้ายกัน boundary symbol . ไม่ใกล้อะไรเลย — 0.077 ถึงตัวอักษรที่ใกล้ที่สุด — เพราะมันเป็นสัญลักษณ์เดียวที่ทำเครื่องหมายตำแหน่ง ไม่ใช่เสียง
และบางส่วนก็ไม่ใช่ nearest neighbour ของ a คือ h ไม่ใช่ vowel อีกตัว เมื่อเฉลี่ยจากทุกคู่:
mean cosine, vowel to vowel : +0.1889
mean cosine, consonant to consonant : +0.0765
mean cosine, vowel to consonant : -0.0042vowels คล้ายกันเองมากกว่าคล้าย consonants และ effect นี้จริงแต่เล็ก เมื่อตรวจเทียบกับกลุ่มตัวอักษรห้าตัวที่สุ่มเลือก 2,000 กลุ่ม มี 58 กลุ่มที่แยกกันได้อย่างสะอาดพอ ๆ กันหรือดีกว่า — ช่องว่างที่ significant ประมาณ ดังนั้นมันจริง แต่ไม่เหมือนเกาะเรขาคณิตคมชัดแบบที่คำอธิบายยอดนิยมเกี่ยวกับ embeddings ทำให้คิด
นี่คือคำอธิบายที่ซื่อสัตย์ของ embedding table และคุ้มค่าที่จะจำไว้ตลอดคอร์สที่เหลือ มันไม่ใช่แผนที่ของความหมาย แต่มันคือ การเปลี่ยนพิกัดที่เรียนรู้ขึ้นมาแทนที่จะออกแบบขึ้นมา และงานเดียวของมันคือทำให้งานของ layer ถัดไปง่ายขึ้น — ประโยคเดียวกับที่บทที่ 5 ใช้กับ hidden layer ที่พับระนาบเพื่อแก้ XOR โครงสร้างใด ๆ ที่คุณพบในนั้นมีอยู่เพราะมันลด loss และโครงสร้างที่ไม่ลด loss ก็ไม่มีอยู่ตรงนั้น
word2vec, GloVe และ arithmetic ที่ทุกคนยกมาอ้าง
ลิงก์ไปยังส่วน: word2vec, GloVe และ arithmetic ที่ทุกคนยกมาอ้างถ้าส่วนที่มีประโยชน์คือตาราง คุณก็ไล่ตามมันโดยตรงได้ นั่นคือ word2vec: เก็บ embedding lookup ไว้ ทิ้ง language model ไป5
Objective ของ skip-gram with negative sampling มีหนึ่งบรรทัด สำหรับคู่ (centre, context) จริงที่ดึงจาก corpus ให้ดัน dot product ของคู่นั้นขึ้น; สำหรับคู่ปลอม คู่ที่ดึงจาก noise distribution ให้ดันลง:6
นี่คือ binary classification — “สองคำนี้เกิดร่วมกันจริงไหม?” — และมันถูก precisamente เพราะไม่เคยแตะ vocabulary ทั้งหมด ซึ่งเป็นสิ่งที่ทำให้การ train บนคำหลายพันล้านคำทำได้จริงในปี 2013 GloVe มาถึง vectors ที่คล้ายกันจากอีกทางหนึ่ง โดย factorise matrix ของ global co-occurrence counts แทนการ stream ผ่าน examples7 ทั้งคู่ fit เข้ากับ statistic เดียวกับที่ count table ถูกสร้างจากมัน พวกมันคือการนับที่ถูกบีบอัด
เมื่อ train บน text8 — English Wikipedia 17,005,207 words, 71,290 คำที่เกิดอย่างน้อยห้าครั้ง, 100 dimensions, สาม passes — vectors ออกมาพร้อมคุณสมบัติที่ทำให้มันโด่งดัง:
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ไม่มีใครให้ category สำหรับ instruments หรือ numerals ต่อไปคือส่วนที่โด่งดัง: เอา king ลบ man บวก woman แล้วหา vector ที่ใกล้ผลลัพธ์ที่สุด
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)vector ที่ใกล้ king - man + woman ที่สุดคือ king นี่ไม่ใช่ความบังเอิญของตัวอย่างเดียว evaluation set ของ Mikolov ตั้งคำถามในรูป a : b :: c : ? — semantic 8,869 ข้อ (paris : france :: rome : italy) และ syntactic 10,675 ข้อ (walking : walked :: swimming : swam) — และจาก semantic questions 4,103 ข้อที่ vocabulary นี้ตอบได้ ผู้ชนะคือหนึ่งในสาม input words 99.8 % ของเวลา demonstrations ที่ตีพิมพ์ไม่พูดถึงเรื่องนี้ เพราะ scoring rule มาตรฐานลบ a, b และ c ออกก่อนดู เป็นกฎที่ชอบธรรม และมันทำงานมากกว่า arithmetic:
| how the answer is chosen | semantic | syntactic |
|---|---|---|
| offset, with the inputs excluded (standard) | 17.0 % | 11.9 % |
| offset, with nothing excluded | 0.1 % | 0.4 % |
nearest neighbour of c alone, inputs excluded | 13.1 % | 9.3 % |
nearest neighbour of b alone, inputs excluded | 2.3 % | 0.4 % |
แถวที่สามคือแถวที่ควรนั่งคิด ทิ้ง a และ b ไม่ทำ arithmetic เลย คืนสิ่งที่อยู่ใกล้ c ที่สุด — แล้วคุณยังรักษา semantic score ไว้ได้ 77 % สิ่งส่วนใหญ่ที่ดูเหมือน analogical reasoning คือ proximity บวกกฎที่ห้ามคำตอบชัดเจน ซึ่งเป็นสิ่งที่ Linzen วัดบน vectors ที่ train อย่างถูกต้อง และ baselines ข้างบน replicate ได้8 vectors ชุดนี้เล็ก — 17 ล้านคำเทียบกับหลายพันล้านคำเบื้องหลัง published models — ดังนั้นให้อ่าน percentages เป็นรูปทรง ไม่ใช่ state of the art รูปทรงนี้คือสิ่งที่คงอยู่ทุก scale: arithmetic เป็นของจริง และอ่อนกว่าการสาธิตหนึ่งเดียวที่ทุกคนยกมาอ้างมาก
Static และ contextual: หนึ่ง vector ต่อหนึ่งคำ หรือหนึ่ง vector ต่อหนึ่ง occurrence
ลิงก์ไปยังส่วน: Static และ contextual: หนึ่ง vector ต่อหนึ่งคำ หรือหนึ่ง vector ต่อหนึ่ง occurrenceทุกอย่างจนถึงตอนนี้มีขีดจำกัดแข็ง ๆ อยู่ใน data structure ตารางมีหนึ่งแถวต่อหนึ่ง token คำว่า bank ได้ vector เดียว เป็น vector เดียวกันในประโยคเกี่ยวกับแม่น้ำและประโยคเกี่ยวกับ mortgage — จำเป็นต้องเป็นอย่างนั้น เพราะ lookup ด้วย id ไม่สามารถขึ้นกับอย่างอื่นได้
วิธีแก้คือหยุดอ่าน vector ออกจากตาราง แล้วเริ่ม คำนวณ มันจากประโยค นั่นคือ contextual embedding ที่ ELMo นำเสนอในปี 2018 และ BERT ทำให้เป็นมาตรฐานในปีเดียวกัน910 เมื่อวัดบน model จริง ตัวเลขคมกว่าคำอธิบาย:
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แถวแรกถูกต้องพอดี ไม่ใช่ค่าประมาณ: static vector สำหรับ bank คือ 768 ตัวเลขเดียวกันในทั้งสองประโยค ดังนั้น cosine เป็น 1 โดย construction เก้า layers ต่อมา occurrence ทั้งสองอยู่ที่ 0.43 ขณะที่ bank ในสองประโยคเกี่ยวกับแม่น้ำต่างกันยังอยู่ที่ 0.87 ไม่มีใคร label sense ไว้ที่ไหนในกระบวนการนี้; senses แยกออกจากกันเพราะการแยกพวกมันทำให้ training objective — เดา token ที่ถูกซ่อนจาก neighbours ของมัน — ทำได้ง่ายขึ้น
รายละเอียดสองอย่างควรให้ความสนใจ Layer 0 เป็น 0.9512 แล้ว ไม่ใช่ 1.0 เพราะมี position embeddings ถูกบวกเข้าไปและคำอยู่คนละตำแหน่งในแต่ละประโยค และ similarity สูงขึ้นอีกครั้ง ที่ layers 11 และ 12: layers สุดท้ายของ pretrained model ถูก specialise ให้กับ training objective ของมัน และมัก ไม่ใช่ ที่ที่ดีที่สุดสำหรับดึง representation ออกมา
แสดงรายละเอียด
Optional: weight tying.
ใน bert-base-uncased embedding table คือ — 23,440,896 ตัวเลข หรือ 21.4 % ของ parameters 109,482,240 ตัวของ model ใน language model ขนาดเล็ก สัดส่วนนี้ยิ่งใหญ่กว่า ซึ่งเป็นเหตุผลที่มี trick หนึ่งแทบจะเป็นสากล: input table และ output layer ที่สร้าง logits คือ matrix เดียวกัน ใช้ครั้งหนึ่งด้วย row lookup และอีกครั้งแบบ transposed11 output layer กำหนด vector ให้ทุก vocabulary entry อยู่แล้ว — มันทำ dot product กับแต่ละตัว — และ tying บอกว่า vector ที่ใช้ อ่าน token และ vector ที่ใช้ เขียน มันควรเป็นวัตถุเดียวกัน มันลด parameters และปรับปรุง perplexity ได้พร้อมกัน ซึ่งหายากพอให้ต้องสังเกต
Embedding model ไม่ใช่ language model
ลิงก์ไปยังส่วน: Embedding model ไม่ใช่ language modelหากต้องการค้นหา corpus ด้วยความหมาย คุณต้องมีหนึ่ง vector ต่อหนึ่งประโยค เมื่อมีสิ่งเหล่านี้แล้ว การค้นหาก็ตรงไปตรงมา — นี่คือทั้งหมดของ semantic retrieval และ บทที่ 19 ว่าด้วยทุกอย่าง รอบ ๆ มัน:
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]ดังนั้นคำถามจริงข้อเดียวคือ embed มาจากไหน วิธีที่ดูชัดเจนคือเอา pretrained language model รันแต่ละประโยคผ่านมัน แล้ว average token vectors นี่คือวิธีนั้นเทียบกับทางเลือกอีกสี่แบบ ให้คะแนนสองทาง: rank correlation ระหว่าง cosine กับ human similarity judgements บน 1,379 pairs ของ STS benchmark และ top-1 retrieval บน index ที่สร้างจาก 200 pairs ที่ paraphrase กันแรงที่สุดในชุดนั้น — index ฝั่งหนึ่งของแต่ละคู่ แล้วใช้อีกฝั่งเป็น query
| how the sentence is embedded | rank correlation | top-1 on a 200-sentence index |
|---|---|---|
| binary word overlap (no model at all) | 0.5500 | 89.0 % |
| mean of the static vectors trained above | 0.5263 | 85.5 % |
BERT, the [CLS] token | 0.2030 | 67.0 % |
| BERT, mean of token vectors | 0.4729 | 84.0 % |
| MiniLM, trained contrastively | 0.8203 | 92.0 % |
อ่านสามแถวกลางเทียบกับสองแถวแรก pretrained transformer 109 ล้าน parameters เมื่อใช้ในวิธีที่ดูชัดเจน กลับ แย่กว่าการนับว่าประโยคสองประโยคแชร์คำกันกี่คำในการตัดสิน sentence similarity — และแย่กว่าการ average vectors text8 100-dimensional ที่ train ไปเมื่อครู่นี้ token [CLS] ซึ่ง tutorials ยังแนะนำอยู่เพราะ BERT ถูก pretrained โดยมี sentence-level objective ผูกกับมัน แย่กว่าครึ่งหนึ่งของสิ่งนั้นด้วยซ้ำ
นี่ไม่ใช่ defect ใน BERT แต่มันคือ objective language model ถูก train เพื่อให้ hidden states ทำนาย token ไม่มีอะไรในนั้นที่ขอให้ paraphrases สองประโยคไปอยู่ใกล้กัน และไม่มีอะไร reward geometry ที่ cosine หมายถึง “ความหมายเดียวกัน” แถวสุดท้ายคือ model ที่มีขนาดหนึ่งในห้า (22,713,216 parameters) ซึ่ง train บน loss ที่ต่างออกไปโดยสิ้นเชิง: contrastive learning โดย examples เป็น pairs — คำถามกับคำตอบของมัน ประโยคกับ paraphrase ของมัน — และ objective ดึง pairs ที่จริงเข้าหากันขณะผลัก sampled negatives ออกห่าง นั่นคือ contribution ของ Sentence-BERT และต้นกำเนิดของอุตสาหกรรม embedding-model ทั้งหมด12 Dense Passage Retrieval ใช้สูตรเดียวกันกับ search โดยตรง ด้วย encoder หนึ่งตัวสำหรับ queries และอีกตัวสำหรับ passages13
ดังนั้น กฎใช้งานจริงคือ:
Embedding model ไม่ใช่ language model ที่ถอด layer สุดท้ายออก มันคือ model อีกแบบบน objective อีกแบบ โดยมักเล็กกว่ามาก และ cosine ของมันหมายถึงสิ่งที่คุณต้องการให้หมายถึง เพราะมันถูก train บน pairs ที่มีสิ่งนั้นเป็น target ตารางข้างบนคือต้นทุนของการเอาอย่างหนึ่งไปแทนอีกอย่างหนึ่ง
และตระกูลนี้ล้มเหลวกับ word order “The dog bit the man” และ “the man bit the dog” มี bags of words เหมือนกัน ดังนั้น word overlap และ static-vector average ให้ cosine เท่ากับ 1.000000 พอดี และ mean-pooled BERT ซึ่งเห็น position ก็ยังลงเอยใกล้ค่านั้นมาก — แม้แต่ MiniLM ที่ train แบบ contrastive ก็ยังให้ 0.979 หาก retrieval task ของคุณขึ้นกับว่าใครทำอะไรกับใคร ไม่มี cosine threshold ใดช่วยคุณได้
บทที่ 19 สร้าง production retrieval system บนฐานนี้และไปถึง cosine cut-off ที่เป็นรูปธรรม การวัดสุดท้ายในบทนี้คือสิ่งที่ทำให้ตัวเลขแบบนั้น defensible แทนที่จะเป็นเวทมนตร์
คำสาปของมิติ ในตารางเดียว
ลิงก์ไปยังส่วน: คำสาปของมิติ ในตารางเดียวEmbeddings จริงมี components หลายร้อยหรือหลายพันตัว และระยะทางมีพฤติกรรมแปลก ๆ ในที่สูงนั้น ลองเอาจุดสุ่ม 1,000 จุดใน unit cube ของ dimensions แล้วดู ratio ระหว่างระยะที่ใหญ่ที่สุดกับเล็กที่สุดระหว่างจุดคู่ใด ๆ:
| dimensions | nearest pair | farthest pair | ratio |
|---|---|---|---|
| 2 | 0.0007 | 1.3612 | 1921.66 |
| 10 | 0.2361 | 2.3397 | 9.91 |
| 100 | 3.0047 | 5.1752 | 1.72 |
| 1,000 | 11.7809 | 14.0306 | 1.19 |
| 10,000 | 39.6152 | 42.0125 | 1.06 |
ในหนึ่งหมื่น dimensions จุดคู่ที่ไกลที่สุดอยู่ไกลกว่าคู่ที่ใกล้ที่สุดเพียง 6 % ทุกอย่างอยู่ห่างจากทุกอย่างอื่นประมาณเท่า ๆ กัน “nearest neighbour” เริ่มให้ข้อมูลน้อยลง และนั่นคือ curse of dimensionality — รวมถึงเป็นเหตุผลหนึ่งที่ vector databases ขนาดใหญ่ไม่ทำ exact nearest-neighbour search อีกด้านของเหรียญเดียวกันคือสิ่งที่ทำให้ cosine thresholds ใช้ได้: เมื่อวัดบน random unit vectors หนึ่งพันคู่ mean cosine อยู่ที่ ใน 100 dimensions และ ใน 768 โดยมี standard deviations 0.0968 และ 0.0357 — และใน 768 dimensions มีเพียง 0.2 % ของ random pairs ที่เกิน 0.1 ในค่าสัมบูรณ์ ดังนั้น similarity ที่วัดได้ 0.4 ไม่ได้หมายถึง “คล้ายกัน 40 %”; แต่มันอยู่นอกสิ่งที่ความบังเอิญสร้างได้มาก นี่คือเหตุผลที่ thresholds ระหว่าง 0.3 ถึง 0.7 แยก signal ออกจาก noise แทนที่จะนั่งอยู่กลางระหว่างสองอย่าง
ต่อจากนี้ไปที่ไหน
ลิงก์ไปยังส่วน: ต่อจากนี้ไปที่ไหนModel ในบทนี้อ่าน characters ก่อนหน้าจำนวนคงที่ lookup แต่ละตัว แล้วแปะผลลัพธ์เข้าด้วยกันตามลำดับ design นี้มีสองปัญหา และมันคือปัญหาเดียวกัน
ดู context table อีกครั้ง: จากสาม characters ไปแปด characters ทำให้ parameters เกือบเพิ่มเป็นสองเท่าและซื้อได้ 0.06 nats ต้นทุนโตเชิงเส้นกับ context — ทุกตำแหน่งเพิ่มต้องมี slab ของ weight matrix แรกเป็นของตัวเอง — แต่ประโยชน์ไม่ได้โตตาม ดันไปถึงหนึ่งพัน tokens แล้ว layer แรกเพียงอย่างเดียวจะหนักกว่าส่วนที่เหลือของ model โดยส่วนใหญ่ใช้ไปกับตำแหน่งที่ไม่สำคัญต่อการทำนายใด ๆ
ซึ่งเป็นปัญหาที่สอง: model ไม่มีทางตัดสินใจว่า tokens ก่อนหน้าตัวใดสำคัญ ตำแหน่งสองมี weights ของตัวเอง และตำแหน่งเจ็ดก็มีของตัวเองอย่างถาวร ไม่ว่าข้างในจะเป็นอะไร เมื่อ model กำลังสะกด nell character ที่ชี้ขาดคือ character ที่อยู่ก่อนหน้าทันที เมื่อประโยคมี pronoun คำที่กำหนด referent ของมันอาจอยู่ย้อนหลังไปสี่สิบ tokens — และไม่มี fixed slot ใดถูกกำหนดให้ “ย้อนหลังไปสี่สิบ” ได้ เพราะครั้งถัดไปมันอาจเป็นหก
สิ่งที่เราต้องการคือ model ที่คำนวณสำหรับแต่ละการทำนายว่า token ก่อนหน้าแต่ละตัวควรมีน้ำหนักแค่ไหน — weights เหนือ context ที่สร้างจาก content แทนที่จะถูก fixed โดย layout เขียนสิ่งนั้นอย่างระมัดระวังแล้วมันเริ่มจากสิ่งธรรมดามาก: average เหนือ tokens ก่อนหน้า จากนั้นให้ weights ของ average นั้นถูกเรียนรู้ และให้มันขึ้นกับ token ที่กำลังถาม
นั่นคือ attention และนั่นคือ บทที่ 9
แหล่งที่มาและวิธีการ
ลิงก์ไปยังส่วน: แหล่งที่มาและวิธีการควรอ่านควบคู่ด้วย: บทที่ 3 ของ Speech and Language Processing โดย Jurafsky และ Martin ซึ่งอธิบาย n-gram models, smoothing และ perplexity อย่างละเอียดกว่าพื้นที่ในบทนี้มาก รวมถึงเหตุผลที่ interpolation และ back-off ชนะการบวกหนึ่ง; notes Stanford CS229 §17.1–17.2 สำหรับ language modelling จากด้าน probabilistic; และ paper ของ Linzen ข้างต้น ซึ่งสั้นและคุ้มค่าที่จะอ่านเต็มฉบับ
รายการอ้างอิง
ลิงก์ไปยังส่วน: รายการอ้างอิง-
ตัวอย่างการสร้างชื่อ dataset และลำดับจาก count table ไปสู่ network แบบ Bengio ติดตามซีรีส์ building makemore ของ Andrej Karpathy ซึ่งสองส่วนแรกเป็นคู่ประกอบที่ดีที่สุดสำหรับบทนี้ ↩
-
Shannon, C. E. Prediction and Entropy of Printed English. Bell System Technical Journal 30(1), pp. 50–64 (1951). มนุษย์เดาตัวอักษรถัดไปของภาษาอังกฤษ และการวัด bits-per-character ดั้งเดิม ↩
-
Shannon, C. E. A Mathematical Theory of Communication. Bell System Technical Journal 27 (1948). Source coding theorem และการระบุว่าการทำนายกับ compression เป็นสิ่งเดียวกัน ↩
-
Bengio, Y., Ducharme, R., Vincent, P. and Jauvin, C. A Neural Probabilistic Language Model. Journal of Machine Learning Research 3, pp. 1137–1155 (2003). Architecture ที่ใช้ข้างต้น: embedding ต่อหนึ่งคำ concatenate เหนือ fixed window ผ่าน hidden layer ไปยัง softmax เหนือ vocabulary ↩
-
Mikolov, T., Chen, K., Corrado, G. and Dean, J. Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781 (2013). CBOW และ skip-gram รวมถึง analogy set ที่ใช้ข้างต้น ↩
-
Mikolov, T., Sutskever, I., Chen, K., Corrado, G. and Dean, J. Distributed Representations of Words and Phrases and their Compositionality. arXiv:1310.4546 (2013). Negative sampling, subsampling ของคำที่พบบ่อย และ noise distribution ยกกำลัง 3/4 ที่ใช้ข้างต้น ↩
-
Pennington, J., Socher, R. and Manning, C. GloVe: Global Vectors for Word Representation. EMNLP 2014. Word vectors จากการ factorisation ของ global co-occurrence matrix แทน streamed local windows ↩
-
Linzen, T. Issues in evaluating semantic spaces using word analogies. RepEval 2016, arXiv:1606.07736. แหล่งที่มาของ offset-free baselines ที่ replicate ข้างต้น ↩
-
Peters, M. et al. Deep contextualized word representations. arXiv:1802.05365 (2018). ELMo: หนึ่ง vector ต่อหนึ่ง occurrence คำนวณโดย bidirectional language model ↩
-
Devlin, J., Chang, M.-W., Lee, K. and Toutanova, K. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv:1810.04805 (2018). Model ที่วัดในการทดลอง bank ↩
-
Press, O. and Wolf, L. Using the Output Embedding to Improve Language Models. arXiv:1608.05859 (2016), and Inan, H., Khosravi, K. and Socher, R. Tying Word Vectors and Word Classifiers. arXiv:1611.01462 (2016). สองเหตุผลอิสระสำหรับ trick เดียวกัน ↩
-
Reimers, N. and Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. arXiv:1908.10084 (2019). การวัดเปิดเรื่องของ paper — mean-pooled BERT ทำได้แย่กว่า averaged static vectors บน sentence similarity — คือสิ่งที่ตารางข้างต้น reproduce ↩
-
Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906 (2020). Contrastive training ของ two-encoder retriever; บรรพบุรุษโดยตรงของ retrieval stack ในบทที่ 19 ↩