Attention Is All You Need
ทำไมการอ่านทีละคำถึงช้าและขี้ลืม: จุดเริ่มต้นของไอเดีย Attention (ตอนที่ 1)
ลองนึกถึงนิยายเล่มหนา ๆ สักเล่มก่อน จะได้เห็นปัญหาทั้งก้อนโดยยังไม่ต้องเปิดสูตร กติกามีสองข้อ ห้ามย้อนกลับไปดูหน้าก่อน และทุกอย่างที่อ่านมาต้องสรุปลงกระดาษโน้ตแผ่นเดียว พอเดินถึงบทสุดท้าย ชื่อตัวละครที่โผล่มาในบทแรกก็อาจเหลือแค่รอยจาง ๆ บนโน้ตใบนั้น แถมเร่งให้เร็วขึ้นก็ไม่ได้ เพราะต้องอ่านทีละหน้าเรียงกันไปเท่านั้น
แวะนิดหนึ่ง — กระดาษโน้ตแผ่นเดียวใบนี้จะกลับมาอีกทีตอนพูดถึงคอขวดของ encoder–decoder รุ่นที่ยังไม่มี attention เก็บภาพนั้นไว้ก่อนได้
กติกาโน้ตใบเดียวตรงกับ encoder–decoder รุ่นพื้นฐานที่ยังไม่มี attention: คนอ่านคือ encoder โน้ตขนาดเดิมคือ context vector และคนที่ต้องเล่าต่ออีกภาษาคือ decoder แต่ก่อน Transformer ก็มีคนหาทางเลิกพึ่งโน้ตใบสุดท้ายแล้วนะ งานของ Bahdanau และคณะ (2014) ให้ decoder เลือกผสมการ์ดสรุปจากแต่ละตำแหน่งได้ โดยยังใช้ RNN อ่านต้นทางอยู่ ส่วน paper ปี 2017 ชื่อ “Attention Is All You Need” (Vaswani และคณะ) ขยับอีกขั้น เปลี่ยนการส่งข้อมูลข้ามตำแหน่งให้ใช้ attention โดยไม่ต้องเดิน recurrence ทีละคำแบบเดิม สถาปัตยกรรมที่ได้มีชื่อว่า Transformer
บทความตอนแรกนี้จะยังไม่แตะสูตร แต่จะค่อย ๆ ปูพื้นตั้งแต่ศูนย์ ว่าปัญหาเดิมคืออะไร ทำไมมันถึงเป็นปัญหา และไอเดีย attention เข้ามาแก้ตรงไหน — พร้อมบอกให้ชัดว่า paper นี้พิสูจน์อะไรแล้ว และอะไรที่ยัง ไม่ได้ พิสูจน์
ระหว่างทางมีรูปให้เลื่อนได้ สองฝั่งของรูปแรกใช้ความยาว n เดียวกัน งานเดียวกัน และสูตรนับเดียวกัน ป้าย ตัวเลขตัวอย่าง หมายถึงของเล่นในรูป ตัวเลข BLEU เวลาเทรน และ FLOPs อยู่ในหัวข้อผลจาก paper และมีหมายเหตุแหล่งที่มาครั้งเดียวตรงนั้น
มีอีกหน้าหนึ่งใน logbook ชื่อ Self-Attention คืออะไร หน้านั้นให้คลิกคำบนประโยค “แมว มอง หนู บน พรม” แล้วดูน้ำหนักตัวอย่าง พร้อมสไลเดอร์อุณหภูมิ นั่นคนละประโยค ตัวเลขคนละชุด และยังไม่ใช่สูตรของน้ำหนัก 0.70 ที่ตอนนี้จะค้างไว้ให้ตอนที่ 2 ตอนนี้หยุดที่คำถามว่า ทำไมการอ่านทีละคำถึงทั้งช้าและขี้ลืม
1. คำถามตั้งต้น
ถ้าคอมพิวเตอร์ต้องอ่านประโยคทีละคำจากซ้ายไปขวา ทำไมมันถึงทั้ง ช้า และ ขี้ลืม — แล้วไอเดีย “attention” แก้ตรงไหน?
คำถามนี้คือแกนของทั้งบทความ ถ้าอ่านจบแล้วตอบได้ด้วยภาษาของตัวเองในสองสามประโยค ก็ถือว่าได้สิ่งที่บทความนี้ตั้งใจจะให้แล้ว
คำว่าช้าในตอนนี้คือจำนวนขั้นที่ต้องรอในลำดับเดียว คำว่าขี้ลืมคือเส้นทางของข้อมูลในลำดับนั้น คะแนนแปลอยู่คนละหัวข้อ
2. ปูพื้นฐานทีละขั้น
เพื่อจะเข้าใจว่า “อ่านทีละคำ” มีปัญหาอย่างไร ต้องเริ่มจากว่าคอมพิวเตอร์ “เห็น” ประโยคเป็นอะไร ห้าขั้นข้างล่างเรียงจากของที่จับต้องได้ไปหาของที่เริ่มเป็นกลไก ข้ามขั้นได้ถ้าคุ้นอยู่แล้ว แต่ละขั้นสั้น และมีภาพให้วางคู่กับคำ
ขั้นที่ 1: คำต้องกลายเป็นตัวเลขก่อน
คอมพิวเตอร์ไม่รู้จักคำว่า “แมว” แบบที่มนุษย์รู้จัก มันรู้จักแค่ตัวเลข ขั้นแรกจึงต้องตัดประโยคเป็นชิ้นเล็ก ๆ เรียกว่า token (อาจเป็นทั้งคำหรือเศษของคำ) แล้วแปลงแต่ละชิ้นเป็น รายการตัวเลขยาว ๆ ชุดหนึ่ง เรียกว่า vector
วิธีคิดที่ช่วยได้คือ มองว่าทุกคำได้ “พิกัด” ในแผนที่ความหมาย คำที่ความหมายใกล้กัน เช่น แมว กับ หมา ก็จะอยู่ใกล้กันมากกว่า แมว กับ รถไฟ ที่สำคัญคือไม่มีใครมานั่งกำหนดพิกัดเหล่านี้ด้วยมือ โมเดลเรียนรู้เองจากข้อมูลจำนวนมาก ใน paper นี้ แต่ละ token ถูกแทนด้วย vector ขนาด 512 ตัวเลขในรุ่น base
512 ในที่นี้คือขนาด vector ของรุ่น base พิกัดจริงไม่มีป้ายเขียนว่า “ความเป็นสัตว์” ป้ายแบบนั้นจะโผล่ทีหลังเฉพาะในของเล่นสองแกน เพื่อให้บวกเลขในหัวได้
ขั้นที่ 2: ประโยคคือ “ลำดับ” และลำดับมีความหมาย
ประโยคหนึ่งประโยคคือ vector หลายตัวเรียงต่อกัน เรียกว่า sequence และลำดับสำคัญมาก ลองเทียบ “หมากัดคน” กับ “คนกัดหมา” — ใช้คำชุดเดียวกันทุกคำ แต่เป็นข่าวคนละระดับ โมเดลที่ดีจึงต้องรู้ทั้ง “มีคำอะไรบ้าง” และ “คำไหนอยู่ตรงไหน” ข้อนี้จะกลับมามีความสำคัญมากเมื่อพูดถึงข้อจำกัดของ attention ในหัวข้อถัดไป
ลองหยิบป้ายสามใบ “หมา | กัด | คน” แล้วสลับใบแรกกับใบสุดท้าย ป้ายยังครบสามใบ กริยายังเป็นกัด แต่คนที่ต้องไปหาหมอเปลี่ยนแล้ว ลำดับจึงไม่ใช่ของประดับ ต่อให้มีพิกัดความหมายของทุกคำครบ ก็ยังต้องมีอะไรบอกว่าใครอยู่ก่อนใคร
ขั้นที่ 3: layer ของ neural network ทำอะไร (แบบไม่ใช้สูตร)
ให้คิดว่า layer หนึ่งชั้นคือ เครื่องแปลงตัวเลข รับ vector เข้าไป ผสมและถ่วงน้ำหนักตัวเลขพวกนั้นตามค่าที่เรียนรู้มา แล้วส่ง vector ใหม่ออกมา ซึ่ง “บอกเรื่องที่มีประโยชน์” ต่องานมากขึ้น เมื่อวางหลายชั้นซ้อนกัน ชั้นหลัง ๆ ก็จับเรื่องที่ซับซ้อนขึ้นได้ เช่น จากระดับคำ ไปสู่ระดับความสัมพันธ์ระหว่างคำ
ยังไม่มีสูตรในขั้นนี้ จุดที่ต้องเก็บคือ ชั้นไม่ได้อ่านประโยคด้วยตัวเอง ชั้นรับของที่ส่งมาแล้วแปลงต่อ ของที่ส่งมาจะเป็นบันทึกทีละคำ หรือเป็นของที่ทุกคำมองกันจบแล้ว เป็นคนละสถาปัตยกรรม และเป็นเรื่องของขั้นถัดไป
ขั้นที่ 4: RNN — วิธีมาตรฐานก่อนหน้า ที่อ่านทีละคำ
ก่อน paper นี้ วิธีมาตรฐานในงานอย่างการแปลภาษาคือ RNN (recurrent neural network) และรุ่นปรับปรุงอย่าง LSTM หลักการทำงานเรียบง่าย:
- อ่านคำที่ 1 → สรุปออกมาเป็น “บันทึกความจำ” หนึ่งก้อน (เรียกว่า hidden state)
- อ่านคำที่ 2 พร้อมกับบันทึกเดิม → ได้บันทึกก้อนใหม่
- ทำซ้ำไปเรื่อย ๆ จนจบประโยค
ตัว paper เองอธิบายไว้ตรง ๆ ว่า hidden state ที่ตำแหน่ง t คำนวณจาก hidden state ก่อนหน้า ร่วมกับ input ที่ตำแหน่ง t — หมายความว่าแต่ละขั้น ต้องรอ ขั้นก่อนหน้าเสร็จก่อนเสมอ
LSTM เป็นรุ่นที่ออกแบบให้จำได้นานขึ้น ภาพ “ขี้ลืม” ข้างหน้าจึงไม่ใช่ภาพว่า RNN สุ่มลืมแบบเกมกระซิบ ข้อจำกัดที่ paper ชี้คือเส้นทางของข้อมูลยาวตามความยาวประโยค ทำให้เรียนรู้ความสัมพันธ์ระยะไกลได้ยากขึ้น
ขั้นที่ 5: ทำไมถึง “ช้า” “ขี้ลืม” และมี “คอขวด”
สามคำนี้แก้คนละจุด แม้จะเกิดจากสถาปัตยกรรมเดียวกัน แยกไว้จะได้ไม่เอาเวลาเทรนมาตอบคำถามเรื่องความจำ
ช้า — เพราะต้องต่อคิว
ขั้นที่ 10 ต้องรอขั้นที่ 9 ขั้นที่ 9 ต้องรอขั้นที่ 8 ไล่ลงไปเรื่อย ๆ paper บอกว่าธรรมชาติที่ต้องทำตามลำดับนี้ “ขัดขวางการทำงานแบบขนานภายในตัวอย่างเดียวกัน” และยิ่งประโยคยาว ปัญหานี้ยิ่งหนัก
ติดคิวตรง time step ภายในลำดับเดียว คนที่ 7 ในแถวนี้รับคำพร้อมบันทึกจากคนที่ 6 ถ้าบันทึกยังไม่มา ขั้นที่ 7 ทำต่อไม่ได้ ต่อให้ตอนเทรนรู้คำทั้งประโยคแล้วก็ตาม แต่คนละประโยคเปิดหลายแถวทำพร้อมกันได้ และการคูณตัวเลขภายในแต่ละขั้นก็ยังใช้ GPU ได้ สิ่งที่ขนานไม่ได้ตรงนี้คือคิวเวลาในลำดับเดียว ไม่ใช่ทุกงานของ RNN
ขี้ลืม — เพราะเส้นทางยาว
ข้อมูลจากคำแรก ๆ ต้องถูกส่งต่อผ่านบันทึกทีละขั้นยาวไปจนถึงคำท้าย ๆ paper เรียกระยะนี้ว่า path length (ความยาวเส้นทาง) และอธิบายในส่วน “Why Self-Attention” ว่า ยิ่งเส้นทางระหว่างสองตำแหน่งสั้น การเรียนรู้ความสัมพันธ์ระยะไกล (long-range dependencies) ก็ยิ่งง่าย สำหรับ RNN ความยาวเส้นทางนี้โตตามความยาวประโยค
ตอนเทรนก็ต้องส่งสัญญาณย้อนกลับผ่านขั้นพวกนี้ด้วย ลองโซ่เลขง่าย ๆ สัญญาณเริ่ม 1 ถ้าทุกข้อต่อคูณ 0.5 ผ่าน 7 ข้อจะเหลือ 0.0078125 แต่ผ่านข้อเดียวเหลือ 0.5 ตัวเลขนี้เป็นของเล่นให้เห็นว่าการคูณเล็ก ๆ ซ้ำอาจทำให้สัญญาณจาง ไม่ใช่เปอร์เซ็นต์ความจำของ RNN จริง และบางเงื่อนไขสัญญาณก็โตจนระเบิดแทนได้ LSTM มี cell state กับ forget gate ของตัวเองมาช่วยเลือกเก็บหรือทิ้งสถานะ เกตชุดนั้นไม่ได้อยู่ใน RNN ธรรมดา และ GRU ก็ใช้คนละชุดเกต ไม่ใช่ forget gate ของ LSTM ส่วน attention ช่วยย่นเส้นทางให้สั้นลง
คอขวด — เพราะต้องบีบทั้งประโยคลงกล่องเดียว
ใน encoder–decoder รุ่นพื้นฐานที่ยังไม่มี attention ตัวอ่านต้องบีบความหมายของทั้งประโยคให้เหลือ vector ความยาวตายตัวก้อนเดียว แล้วส่งให้ตัวเขียนใช้สร้างคำแปล Bahdanau และคณะ (2014) ตั้งข้อสันนิษฐานว่านี่คือคอขวด และอ้างงานก่อนหน้าที่พบว่าคุณภาพการแปลตกลงอย่างรวดเร็วเมื่อประโยคยาวขึ้น งานของ Bahdanau เองจึงเริ่มนำ attention มาใช้ ควบคู่ กับ RNN
ถ้าต้องยัดเรื่องทั้งเรื่องลงโน้ตใบเดียว รายละเอียดช่วงไหนน่าจะหายก่อน เก็บคำถามนี้ไว้ แล้วค่อยดูอุปมาโพสต์อิทว่า Bahdanau เสนอให้หยิบการ์ดทีละตำแหน่งแทน
ตัวอย่างคิดเลขในหัว: ประโยค 10 คำ
สมมติประโยคมี 10 token ลองนับงานแบบหยาบ ๆ (เป็นการนับเพื่อให้เห็นภาพ ไม่ใช่ตัวเลขจาก paper):
| คำถาม | อ่านทีละคำ (RNN) | ทุกคำมองกันเอง (self-attention) |
|---|---|---|
| ต้องทำ “ทีละขั้นแบบรอกัน” กี่ขั้น? | 10 ขั้น เรียงกัน | คำนวณทุกคู่ได้ในรอบเดียว |
| ข้อมูลจากคำที่ 1 ต้องเดินกี่ช่วงกว่าจะถึงคำที่ 10? | ส่งต่อ 9 ช่วง | 1 ช่วง (มองตรงถึงกันได้) |
| ต้องคิดน้ำหนักกี่คู่? | — (ไม่ได้จับคู่ตรง ๆ) | 10 × 10 = 100 คู่ |
ลองเพิ่มความยาวจาก 8 เป็น 16 ตำแหน่ง ฝั่งต่อคิวต้องรอ 16 ขั้น จากต้นถึงท้าย 15 ช่วง ส่วนฝั่งมองกันเองยังเชื่อมตรงได้ แต่จำนวนคู่เพิ่มจาก 64 เป็น 256 แล้ว ลองเดาก่อนเลื่อนรูปข้างล่าง: ถ้าเพิ่ม n แค่นิดเดียว จำนวนคู่จะโตตามทันคิวหรือแซงไปไกลกว่า? ฝั่ง RNN ในตาราง 10 token ไม่มีช่องคู่ เพราะมันไม่ได้จับคู่ตรง ๆ ช่องนั้นจึงเว้นไว้
ถ้าประโยคยาวเป็น 100 token ฝั่ง RNN ต้องรอกัน 100 ขั้น ส่วนฝั่ง self-attention ยังทำได้ในรอบเดียว แต่จำนวนคู่พุ่งเป็น 100 × 100 = 10,000 คู่ — นี่คือทั้งจุดแข็งและราคาที่ต้องจ่าย ซึ่งจะกลับมาพูดในหัวข้อข้อจำกัด
ภาพนี้สอดคล้องกับ Table 1 ใน paper ซึ่งเปรียบเทียบชนิดของ layer: layer แบบ recurrent ต้องทำงานตามลำดับ O(n) ขั้น มีความยาวเส้นทางสูงสุด O(n) และงานต่อชั้นโตประมาณ O(n · d²) ส่วน self-attention ใช้จำนวนขั้นตามลำดับคงที่ O(1) ความยาวเส้นทางสูงสุด O(1) และงานต่อชั้นโตประมาณ O(n² · d) (n = ความยาวลำดับ, d = ขนาด vector) สัญลักษณ์ O(…) ในที่นี้อ่านคร่าว ๆ ได้ว่า “งานโตตามอะไร” รุ่น base มี d = 512 รูปถัดไปให้เลื่อน n เอง แล้วดูขั้น ช่วง และคู่ขยับบนลำดับเดียวกันทั้งสองฝั่ง
ประโยคเดียวกัน สองวิธีอ่าน
งานเดียวกันคืออ่านลำดับยาว n จนจบ ฝั่งซ้ายเดินทีละตำแหน่ง ฝั่งขวาเห็นครบตั้งแต่รอบแรก เลื่อน n แล้วจำนวนขั้น ช่วง และคู่จะคำนวณใหม่ทั้งสองฝั่ง
ตัวเลขตัวอย่าง เป็นการนับโครงสร้างจาก n ไม่ใช่เวลาบนนาฬิกา และไม่ใช่ตัวเลขจาก paper
ค่าเริ่มคือ 8 คำของประโยคนี้ เลื่อน n ออกจาก 8 แล้วชิปจะกลายเป็นเลขตำแหน่ง กด “ขั้นถัดไป” หรือกดชิปฝั่งซ้ายเพื่อเดินคิว
อ่านทีละคำ (RNN)
แต่ละขั้นรอขั้นก่อนหน้า บันทึกความจำเป็นก้อนเดียวที่ส่งต่อ
บันทึกเพิ่งเริ่มที่ตำแหน่ง 1 (แมว) ยังไม่มีก้อนก่อนหน้า
มองพร้อมกัน (self-attention)
ทุกตำแหน่งอยู่ในรอบเดียวกัน เส้นทางจากตำแหน่งแรกถึงตำแหน่งสุดท้ายยาว 1 ช่วง
ฝั่งนี้เห็นครบ 8 คำตั้งแต่รอบแรก ไม่ต้องรอส่งบันทึกจากตำแหน่งก่อนหน้า
| คำถามบนประโยคนี้ | อ่านทีละคำ (RNN) | มองพร้อมกัน (self-attention) |
|---|---|---|
| ต้องทำทีละขั้นแบบรอกันกี่ขั้นกว่าจะจบ? | 8 ขั้น เรียงกัน | 1 รอบ |
| ตำแหน่งแรกส่งต่อกี่ช่วงกว่าจะถึงตำแหน่งสุดท้าย? | 7 ช่วง | 1 ช่วง |
| จากตำแหน่ง 1 ถึงตำแหน่ง 7: RNN 6 ช่วง · attention 1 ช่วง | ||
| ต้องคิดน้ำหนักกี่คู่? | — (ไม่ได้จับคู่ตรง ๆ) | 8 × 8 = 64 คู่ |
n = 8 ทั้งสองฝั่ง · RNN 8 ขั้นเรียงกัน · ต้นถึงท้าย 7 ช่วง · attention 1 ขั้น · 1 ช่วง · คู่ 8 × 8 = 64 · ตอนนี้อยู่ขั้นที่ 1 (แมว)
รูปที่ 1. นับบนลำดับเดียวกันยาว n: RNN ใช้ n ขั้นและ n−1 ช่วงจากต้นถึงท้าย ส่วน attention ใช้ 1 ขั้น 1 ช่วง และ n×n คู่ รวมการมองตัวเอง เมื่อ n = 8 ชิปคือคำในประโยคตัวอย่าง
3. ศัพท์เทคนิคด้วยภาษาง่าย
ตารางนี้เป็นที่พักคำ เวลากลับไปอ่านส่วนหลังแล้วสะดุด คำในคอลัมน์ซ้ายคือคำที่จะใช้ต่อ ภาษาในคอลัมน์ขวาจงใจสั้น
| คำ | ความหมายแบบง่าย |
|---|---|
| โทเค็น (token) | ชิ้นข้อความเล็ก ๆ ที่โมเดลอ่าน อาจเป็นทั้งคำ หรือเศษคำก็ได้ |
| เอ็มเบดดิง (embedding) | การแปลง token เป็น vector ตัวเลข = “พิกัดความหมาย” ของคำ |
| ลำดับ (sequence) | ของที่เรียงกันตามลำดับ เช่น token ในประโยค |
| อาร์เอ็นเอ็น (RNN / LSTM) | โมเดลที่อ่านทีละ token แล้วส่ง “บันทึกความจำ” ต่อไปเรื่อย ๆ LSTM มี cell state กับ forget gate ของตัวเอง RNN ธรรมดาไม่มีเกตนั้น และ GRU ใช้คนละชุดเกต |
| ขนาน (parallel) | ทำหลายงานพร้อมกัน RNN ขนานข้าม time step ในลำดับเดียวไม่ได้ เพราะแต่ละขั้นต้องรอ hidden state ก่อนหน้า แต่ขนานข้ามหลายลำดับใน batch ได้ และงานตัวเลขในแต่ละขั้นก็ยังไปบน GPU ได้ |
| ความยาวเส้นทาง (path length) | จำนวน “ช่วง” ที่ข้อมูลต้องเดินทางจากตำแหน่งหนึ่งไปอีกตำแหน่งในโมเดล ยิ่งสั้น ยิ่งเรียนรู้ความสัมพันธ์ระยะไกลได้ง่าย |
| ความใส่ใจ (attention) | การแบ่งน้ำหนักให้ข้อมูลแต่ละตำแหน่งที่อนุญาตให้ดู แล้วผสมตามน้ำหนักนั้น ภาพในตอนนี้เป็น encoder self-attention ที่ไม่ใส่ mask จึงมองได้ทุกตำแหน่ง |
| self-attention | attention ที่คำในประโยคเดียวกันมองกันเอง (ต่างจากแบบเดิมที่ decoder มองกลับไปที่ประโยคต้นทาง) |
| ตัวเข้ารหัส / ตัวถอดรหัส (encoder / decoder) | encoder อ่านประโยคต้นทางแล้วแปลงเป็นตัวแทนเชิงตัวเลข decoder เอาตัวแทนนั้นมาสร้างประโยคปลายทางทีละ token |
| BLEU | คะแนนอัตโนมัติที่ดูว่าคำแปลของเครื่องซ้อนทับกับคำแปลอ้างอิงของมนุษย์มากแค่ไหน ยิ่งสูงยิ่งดี |
4. อุปมาและตัวอย่าง (พร้อมขอบเขตของแต่ละอุปมา)
อุปมาช่วยให้เห็นภาพเร็ว แต่ทุกอุปมามีจุดที่ “ใช้ไม่ได้” เสมอ จึงระบุขอบเขตไว้ใต้อุปมาแต่ละอันเพื่อไม่ให้ภาพในหัวผิดเพี้ยน สามภาพข้างล่างจับคนละอาการ เกมกระซิบจับทั้งคิวและเส้นทางยาว ห้องประชุมจับการมองพร้อมกัน โพสต์อิทจับคอขวดของ encoder–decoder ที่ยังไม่มี attention
อุปมา 1: RNN = เกมกระซิบต่อกัน
ให้คนแปดคนถือคำคนละใบ แต่ละคนรับบันทึกจากคนก่อน ผสมกับคำบนใบตัวเอง แล้วส่งบันทึกใหม่ต่อ คนคือหนึ่ง time step ใบคำคือ input และบันทึกคือ hidden state ทั้งแถวต้องรอกันทีละคน ข้อมูลจากคนแรกต้องผ่านการอัปเดตหลายครั้งกว่าจะถึงคนท้าย จุดที่เทียบคือคิวกับความยาวเส้นทาง ไม่ได้กำหนดว่าคนท้ายต้องลืม จะเก็บอะไรไว้ได้ขึ้นกับกติกาที่เรียนรู้ และ LSTM ก็มีตัวช่วยเรื่องนี้
ถ้าเอาประโยคตัวอย่างตอน n = 8 มาเล่นเกมนี้ คนที่ได้ยิน “แมว” ต้องกระซิบผ่าน “ไม่ ยอม กิน ปลา เพราะ” กว่าความนั้นจะถึงคนที่ได้ยิน “มัน” นับช่วงได้ 6 ช่วง คนที่ได้ยิน “เน่า” อยู่ห่างจากคนแรก 7 ช่วง ฝั่ง attention มองถึงกันใน 1 ช่วง
อุปมา 2: attention = ห้องประชุมที่ทุกคนเห็นหน้ากันหมด
แทนที่จะกระซิบต่อกัน ให้ทุกคำนั่งอยู่ในห้องเดียวกัน แต่ละคำถามขึ้นมาว่า “ใครในห้องนี้เกี่ยวกับตัวเองบ้าง?” แล้วตั้งใจฟังคนที่เกี่ยวข้องมาก ฟังคนที่ไม่เกี่ยวน้อย ๆ และทุกคำถามพร้อมกันได้โดยไม่ต้องรอคิว
ในประโยคตัวอย่าง ห้องนี้มี 8 ที่นั่ง หนึ่งที่นั่งแทนหนึ่งตำแหน่ง และ “คู่” หมายถึงคู่มีทิศทาง รวมการมองตัวเอง แต่ละที่นั่งจึงมี 8 คู่ ทั้งห้องเป็น 8×8 = 64 ห้องในรูปนี้เป็น encoder ที่เปิดให้มองได้ทุกตำแหน่ง ห้องของ causal decoder จะปิดคำอนาคตอีกแบบหนึ่ง เลื่อน n ในรูปที่ 1 แล้วจำนวนคู่จะกลายเป็น n×n ของความยาวนั้น
อุปมา 3: คอขวดโน้ตใบเดียว = สรุปหนังทั้งเรื่องลงโพสต์อิทแผ่นเดียว
เทียบหนังเรื่องเดิมและงานเล่าเป็นอีกภาษา แบบแรกส่งโน้ตขนาดตายตัวเพียงใบเดียวให้นักเล่า ไม่ว่าหนังจะยาวแค่ไหน แบบที่มี attention เก็บการ์ดสรุปไว้หนึ่งใบต่อหนึ่งตำแหน่งต้นทาง แล้วให้นักเล่าผสมข้อมูลจากหลายใบตอนจะเขียนแต่ละคำ นักเล่ายังคนเดิม ต่างกันที่จากโน้ตก้อนเดียวกลายเป็นชุด representation ที่เรียกใช้ได้ ไม่ได้กลับไปฉายหนังดิบ และไม่ได้แปลว่าจะเล่าถูกทุกครั้ง
คราวนี้คนเล่าไม่ต้องเดิมพันกับโน้ตใบสุดท้ายแล้ว อยากเล่าถึงใครก็ผสมการ์ดจากตำแหน่งที่เกี่ยวข้องได้ แต่คนทำการ์ดยังเป็น RNN คนเดิมนะ Bahdanau เปลี่ยนวิธีหยิบข้อมูลให้คนเล่า ส่วน Transformer จะเปลี่ยนคิวอ่านด้วย
ตัวอย่างหลัก: “มัน” หมายถึงอะไร?
ประโยค A: “แมวไม่ยอมกินปลา เพราะมันเน่า”
ประโยค B: “แมวไม่ยอมกินปลา เพราะมันอิ่ม”
สองประโยคต่างกันแค่คำสุดท้าย แต่ “มัน” ชี้ไปคนละตัว มนุษย์รู้ทันทีเพราะ “เน่า” เป็นคุณสมบัติของอาหาร ส่วน “อิ่ม” เป็นสภาพของสัตว์ที่กิน คำถามคือ โมเดลจะ “รู้” แบบนี้ได้อย่างไร
นี่เป็นอีกคู่ที่ตั้งให้เทียบกันได้ตรง ๆ ต้นประโยคเดียวกันจนถึงคำว่า “มัน” งานเดียวกันคือให้คำว่า “มัน” แจกน้ำหนักไปที่คำอื่น น้ำหนักรวมกันได้ 1.0 ทั้งสองประโยค ต่างกันที่คำสุดท้าย และที่น้ำหนักซึ่งสมมติขึ้น
สมมติว่าคำว่า “มัน” มองไปรอบ ๆ แล้วให้น้ำหนักกับคำอื่น (น้ำหนักรวมกันได้ 1.0) อาจได้หน้าตาประมาณนี้:
| คำที่ “มัน” มองไป | ประโยค A (…มันเน่า) | ประโยค B (…มันอิ่ม) |
|---|---|---|
| แมว | 0.10 | 0.70 |
| ปลา | 0.70 | 0.10 |
| กิน | 0.05 | 0.05 |
| เน่า / อิ่ม | 0.15 | 0.15 |
จากนั้น vector ใหม่ของ “มัน” = เอา vector ของแต่ละคำมาผสมตามน้ำหนักนี้ สี่แถวนี้ไม่ได้ใส่คำว่า “ไม่” “ยอม” “เพราะ” หรือตัว “มัน” เอง จึงไม่สมมติน้ำหนักให้คำเหล่านั้น และอย่าอนุมานว่าคำที่ไม่ได้วาดมีน้ำหนักศูนย์ในโมเดลจริง ผลรวมของสี่แถวคือ 0.10 + 0.70 + 0.05 + 0.15 = 1.0 ทั้งสองคอลัมน์
อ่านจบทั้งประโยคแล้ว A ชวนให้เข้าใจว่าปลาเน่า ส่วน B ชวนให้นึกว่าแมวอิ่ม รูปถัดไปไม่ได้ให้โมเดลคิดคำตอบสด ๆ น้ำหนักถูกตั้งไว้บนตัวแทนคำที่ได้รับบริบทแล้ว เพื่อดูว่าถ้าแบ่งความสนใจแบบนี้จะผสมข้อมูลได้อะไร ภาพนี้เป็น encoder self-attention ที่ไม่ใส่ mask ทุกตำแหน่งรวมคำท้ายมองถึงกันได้ ไม่ใช่ decoder แบบ causal ที่คำว่า “มัน” ห้ามเห็นคำถัดไป
ของเล่นสองแกนตั้งให้ครบทั้งสองประโยค แกนแรกใช้แทนความเป็นสัตว์ แกนที่สองใช้แทนความเป็นอาหาร เฉพาะในรูปนี้ แมว = (1.0, 0.0), ปลา = (0.2, 1.0), กิน = (0.0, 0.5), เน่า = (0.0, 0.8) และกำหนด “อิ่ม” = (0.8, 0.0) เพื่อให้บวกฝั่ง B ได้ ค่าอิ่มนี้ตั้งขึ้นในหน้านี้ กินตรึงที่ 0.05 คำท้ายตรึงที่ 0.15 แมวกับปลาแบ่งก้อน 0.80 ร่วมกัน
ตอนนี้ปลาในประโยค A ถือส่วนแบ่งอยู่ 70% ลองแบ่งให้แมวอีก 10% โดยกินกับคำท้ายอยู่ที่เดิมก่อน คิดว่าจุด A จะขยับขึ้นหรือลง? เลื่อนไปที่แมว 0.20 แล้วดู พิกัดจาก (0.24, 0.845) กลายเป็น (0.32, 0.745) มันไปทางสัตว์มากขึ้นและทางอาหารน้อยลงตามการ์ดที่ตั้งไว้
เลื่อนน้ำหนัก แล้วดูผลรวมขยับ
เลือกประโยค แล้วเลื่อนน้ำหนักแมว ปลาได้ส่วนที่เหลือของก้อน 0.80 กินกับคำท้ายไม่ขยับ จุดบนแกนคือผลรวมที่คำนวณจากน้ำหนักคูณ value
ตัวเลขตัวอย่าง รวมค่า “อิ่ม” = (0.8, 0.0) ที่ตั้งขึ้นในหน้านี้ ไม่ใช่ embedding จากโมเดลจริง ภาพนี้เป็น encoder ที่ไม่มี mask
ประโยค A · คำท้ายเน่า (0.0, 0.8)
ผลรวม A (0.24, 0.845)
ประโยค B · คำท้ายอิ่ม (0.8, 0.0)
ผลรวม B (0.84, 0.125)
ประโยค A · แมว 0.10 · ปลา 0.70 · กิน 0.05 · เน่า 0.15 · ผลรวม (0.24, 0.845) · encoder ไม่มี mask · ตัวเลขตัวอย่าง
รูปที่ 2. แถบคือส่วนแบ่งความสนใจ จุดคือผลรวมของ value ตามส่วนแบ่งนั้น ชุด A เริ่มที่แมว 0.10 ได้ (0.24, 0.845) ชุด B เริ่มที่แมว 0.70 ได้ (0.84, 0.125) ย้ายน้ำหนัก 0.10 จากปลาไปแมวในชุด A จะได้ (0.32, 0.745)
จุดสำคัญ 2 ข้อที่ตัวอย่างนี้แสดง:
- “มัน” มองถึง “แมว” ได้ ในก้าวเดียว ไม่ต้องส่งต่อผ่านทุกคำตรงกลางแบบ RNN (เส้นทางสั้น)
- น้ำหนักทุกคู่คำนวณ พร้อมกันได้ เพราะไม่มีคำไหนต้องรอคำไหน (ขนานได้)
บนลำดับ 8 ตำแหน่ง ช่วงจากตำแหน่งแรกถึง «มัน» คือ 6 กับ 1 ลองเลื่อน n ในรูปที่ 1 แล้วดูว่าขั้นของ RNN กับจำนวนคู่ของ attention โตคนละแบบบน n เดียวกัน ส่วนคำถามว่า “น้ำหนัก 0.70 มาจากไหน” เป็นเรื่องของ query / key / value ซึ่งจะเปิดในตอนที่ 2
ตัวอย่างจริงจาก paper
ในภาคผนวกของฉบับ arXiv (Figure 4) ผู้เขียนแสดงภาพ attention head สองตัวในชั้นที่ 5 จากทั้งหมด 6 ชั้น ซึ่ง “apparently” (ดูเหมือนว่า) เกี่ยวข้องกับการหาว่าคำว่า “its” ในประโยค “The Law will never be perfect, but its application should be just…” หมายถึงอะไร — เป็นโจทย์ประเภทเดียวกับ “มัน” ข้างบนพอดี สังเกตว่าผู้เขียนเลือกใช้คำระวังตัวว่า “ดูเหมือน” ไม่ได้อ้างว่าพิสูจน์แล้วว่าโมเดลคิดแบบนั้น
Figure 4 เป็นภาพจากโมเดลใน paper ส่วนน้ำหนักในรูปที่ 2 เป็นของเล่น สิ่งที่เชื่อมกันได้คือชนิดของคำถาม: คำสรรพนามชี้ไปที่คำไหน
5. เชื่อมกลับไปที่ paper
ถึงตรงนี้เราเห็นแล้วว่าคิวสั้นลงตรงไหน แต่ถ้าเอาไปแปลจริง คำแปลดีขึ้นด้วยไหม มาดูผลสองทิศทางที่ผู้เขียนลองกัน
ผู้เขียนอ้างว่าอะไร
- เสนอสถาปัตยกรรมชื่อ Transformer ที่ใช้ attention เชื่อมข้อมูลข้ามตำแหน่งแทน recurrence กับ convolution ทั้งเครื่องยังมีชั้น feed-forward แบบทีละตำแหน่ง, residual connection และ layer normalization
- ทำงานขนานได้มากกว่า และใช้เวลาเทรนน้อยกว่ามาก
- ผู้เขียนระบุว่า เท่าที่ทราบ นี่เป็นโมเดลแปลง sequence ตัวแรกที่เชื่อมตำแหน่งด้วย self-attention โดยไม่พึ่ง RNN (ก่อนหน้านี้ attention มักใช้ “ควบคู่” กับ RNN อย่างในงานของ Bahdanau 2014)
ทดสอบอย่างไร
- งานแปลภาษา WMT 2014 อังกฤษ→เยอรมัน (ประมาณ 4.5 ล้านคู่ประโยค) และอังกฤษ→ฝรั่งเศส (36 ล้านประโยค)
- วัดด้วย BLEU (ดูตารางศัพท์ด้านบน)
- ฮาร์ดแวร์: เครื่องเดียวที่มี GPU NVIDIA P100 8 ตัว รุ่น base เทรน 12 ชั่วโมง (100,000 steps) รุ่น big เทรน 3.5 วัน (300,000 steps)
- ลองใช้กับงานอื่นด้วย คือการแยกโครงสร้างประโยคภาษาอังกฤษ (English constituency parsing) เพื่อดูว่าไม่ได้เก่งแค่งานแปล
สองทิศทางนี้เป็นคนละงาน ข้อมูลคนละขนาด เวลาเทียบคะแนนจึงต้องบอกทิศทางให้ครบ และต้องบอกว่าเทียบกับโมเดลเดี่ยวหรือเทียบรวมถึง ensemble
ผลที่ตรวจแล้ว
- อังกฤษ→เยอรมัน: รุ่น big ได้ 28.4 BLEU ดีกว่าผลดีที่สุดก่อนหน้า (รวมถึงแบบ ensemble ที่เอาหลายโมเดลมารวมกัน) มากกว่า 2 BLEU ส่วนรุ่น base ได้ 27.3 ซึ่งก็แซงทุกโมเดลที่ตีพิมพ์มาก่อนแล้ว
- อังกฤษ→ฝรั่งเศส: รุ่น big ได้ 41.8 BLEU ตาม abstract และ Table 2 ของ arXiv v7 ผู้เขียนระบุว่าแซงโมเดลเดี่ยวทุกตัวที่ตีพิมพ์มาก่อน โดยใช้ต้นทุนเทรนไม่ถึง 1/4 ของ state-of-the-art เดิม
- ต้นทุน: Table 2 ประเมินต้นทุนเทรนของรุ่น base ไว้ที่ประมาณ 3.3 × 10^18 FLOPs ซึ่งต่ำกว่าโมเดลคู่แข่งในตาราง (เป็นค่าประมาณจาก เวลา × จำนวน GPU × ความเร็ว GPU ไม่ได้วัดตรง ๆ)
ที่นี่ใช้ 41.8 ตาม abstract และ Table 2 ของ arXiv v7 ส่วน §6.1 ในฉบับเดียวกันยังพิมพ์ 41.0 และฉบับ NeurIPS 2017 ก็รายงาน 41.0
ข้อจำกัดที่ paper ยอมรับเอง
- ราคาของการจับคู่ทุกคำ: Table 1 ให้ต้นทุนต่อชั้นของ self-attention โตประมาณ O(n² · d) ส่วน recurrent โตประมาณ O(n · d²) ถ้าใช้ n กับ d ชุดเดียวกันแล้ว n น้อยกว่า d พจน์แรกได้เปรียบด้านอัตราการเติบโตของงาน ซึ่งเป็นกรณีปกติของงานแปลประโยค (รุ่น base มี d = 512) แต่ยังฟันธงเวลาเป็นวินาทีไม่ได้ สำหรับข้อความยาวมาก ๆ ผู้เขียนเสนอให้จำกัดวงการมองเฉพาะบริเวณใกล้เคียง (เส้นทางยาวขึ้นเป็น O(n/r) ตาม Table 1) และระบุว่าจะศึกษาต่อในอนาคต
- ความละเอียดลดลง: การรวมข้อมูลเป็นค่าเดียวตามน้ำหนักทำให้ความละเอียดลดลง ผู้เขียนใช้ multi-head attention เป็นทางแก้ โดยฉาย query, key และ value คนละ h ชุด ต่อผลเข้าด้วยกัน แล้วคูณด้วย W^O ไม่ใช่การโหวตหรือการเฉลี่ยหัวเข้าหากัน (รายละเอียดอยู่ในตอนถัดไป)
- ยังสร้างคำแปลทีละคำ: ฝั่ง decoder ตอนสร้างคำแปลยังทำทีละ token อยู่ ผู้เขียนเขียนเองว่า “การทำให้การสร้างข้อความเป็นลำดับน้อยลง” (making generation less sequential) เป็นเป้าหมายวิจัยต่อไป แปลว่าความ “ขนาน” ที่ได้นั้น ได้หลัก ๆ ตอน เทรน และตอนอ่านประโยคต้นทาง
ใช้ n กับ d ชุดเดียวกัน: จำนวนรอบที่ต้องรอ ปริมาณงานต่อชั้น และเวลาที่จับนาฬิกาเป็นคนละอย่างกัน ข้อที่สามกันไม่ให้ภาพ “เลิกอ่านทีละตำแหน่ง” ลามไปถึงตอนที่โมเดลกำลังเขียนคำแปลออกมา ตอนเขียน ฝั่ง decoder ยังเดินทีละ token
สิ่งที่ paper นี้ยังไม่ได้พิสูจน์
- ยังไม่ได้พิสูจน์ว่าใช้ได้กับทุกงาน: ทดสอบแค่แปลภาษา 2 คู่ (ทั้งคู่เริ่มจากภาษาอังกฤษ) และ parsing ภาษาอังกฤษ ภาษาอื่น (เช่น ภาษาไทย) รวมถึงรูปภาพ เสียง และวิดีโอ เป็นแผนในอนาคตที่ผู้เขียนเขียนไว้ ยังไม่ได้ทดสอบใน paper นี้
- ยังไม่ได้พิสูจน์ว่า attention = คำอธิบายว่าโมเดลคิดอะไร: ผู้เขียนเพียงบอกว่า “อาจ” ทำให้ตีความโมเดลได้ง่ายขึ้น และภาพในภาคผนวกก็ใช้คำว่า “ดูเหมือน”
- ผลแปลที่เล่าในตอนนี้เป็น BLEU: ไม่พบการให้มนุษย์ประเมินคุณภาพคำแปลในตัว paper และตารางผลหลักไม่ได้รายงานความแปรปรวนจากการเทรนหลายรอบ งาน parsing ใน paper เป็นอีกงานหนึ่ง จึงไม่เอามาปนเป็นคะแนน BLEU
- ข้อความยาวมาก ๆ: ยังไม่ได้แสดงผล (เป็นงานในอนาคตตามที่ผู้เขียนระบุ)
6. สรุปและประเด็นสำคัญ
ย้อนกลับไปที่คำถามตั้งต้น — การอ่านทีละคำแบบ RNN ช้า ในลำดับเดียวเพราะทุกขั้นต้องรอ hidden state ก่อนหน้า จึงขนานข้าม time step ในประโยคนั้นไม่ได้ ทั้งที่ยังแบตช์หลายประโยคและคูณตัวเลขในแต่ละขั้นบน GPU ได้ และ ขี้ลืม ในความหมายของเส้นทาง: ข้อมูลจากคำต้น ๆ ต้องเดินทางไกลกว่าจะถึงคำท้าย ๆ ทำให้เรียนรู้ความสัมพันธ์ระยะไกลได้ยากขึ้น attention แบบ encoder ที่ไม่ใส่ mask ให้ทุกตำแหน่งมองถึงกันได้ในก้าวเดียว และคำนวณน้ำหนักของตำแหน่งที่เปิดให้ดูพร้อมกัน แลกกับต้นทุนที่โตตามจำนวนคู่ n × n และการต้องเติมข้อมูลลำดับคำเข้าไปเอง
กลับไปที่รูปที่ 1 แล้วเลื่อน n จาก 8 ไป 16 อีกครั้ง ขั้นของ RNN เพิ่มเป็นเท่าตัว จำนวนคู่ของ attention เพิ่มกี่เท่า
ประเด็นสำคัญ:
- คอมพิวเตอร์มองประโยคเป็นลำดับของ vector และลำดับคำมีความหมาย
- RNN อ่านทีละคำในลำดับเดียว: ขนานข้าม time step ที่พึ่งกันไม่ได้ แต่ยังแบตช์หลายลำดับได้ และเส้นทางข้อมูลยาวตามความยาวประโยค
- encoder–decoder รุ่นพื้นฐานที่ไม่มี attention มีคอขวดจากการบีบทั้งประโยคลง vector ก้อนเดียว
- attention แบ่งน้ำหนักให้ตำแหน่งที่อนุญาตให้ดู แล้วผสม value ตามน้ำหนัก
- Transformer ใช้ attention เชื่อมตำแหน่งแทน recurrence กับ convolution และยังมี feed-forward, residual connection กับ layer normalization ได้ผลแปลดีกว่าเดิม (28.4 BLEU อังกฤษ→เยอรมัน, 41.8 BLEU อังกฤษ→ฝรั่งเศส) ด้วยต้นทุนเทรนต่ำกว่า
- ข้อจำกัด: ต้นทุน n × n สำหรับข้อความยาว, decoder ยังสร้างทีละคำ, ทดสอบในงานจำกัด และ attention ยังไม่ใช่หลักฐานว่าโมเดล “คิด” อย่างไร
ตอนนี้เรายืนอยู่ตรง paper ปี 2017 ก่อนนะ ยังไม่ได้ตามไปดูว่างานรุ่นหลังแก้ข้อจำกัดเหล่านี้ไปถึงไหนแล้ว
7. แหล่งอ้างอิง
-
Vaswani et al. 2017, Attention Is All You Need — arXiv 1706.03762 (v7, 2 Aug 2023) · PDF
ประเภท preprint
อ่านลึกแค่ไหน อ่านเต็ม (ตัวเนื้อหา ตาราง และภาคผนวกที่เป็นภาพ attention; ไม่ได้ไล่อ่านรายการอ้างอิงทุกชิ้น)
-
Vaswani et al. 2017 — NeurIPS (NIPS 2017) proceedings · PDF
ประเภท peer-reviewed (conference proceedings)
อ่านลึกแค่ไหน ตรวจเทียบเฉพาะจุด: abstract, ตัวเลขผลหลัก, เวลาเทรน (ไม่ได้อ่านใหม่ทั้งฉบับ)
-
ประเภท preprint บน arXiv; ตัว PDF ระบุว่า “Published as a conference paper at ICLR 2015” (peer-reviewed)
อ่านลึกแค่ไหน อ่านบางส่วน: abstract, บทนำ, ส่วน background เรื่อง RNN encoder–decoder (ไม่ได้อ่านส่วนวิธีการและผลการทดลองละเอียด)
8. ตอนถัดไป
ตอนนี้เราหมุนน้ำหนัก 0.70 ได้เอง แต่ยังไม่รู้ว่าโมเดลหามันมาอย่างไร ตอนหน้าเราจะเปิดกล่อง query, key และ value แล้วค่อย ๆ คิดน้ำหนักกับผลผสมด้วยตัวเลขเล็ก ๆ กัน
ตอนถัดไปอยู่ที่ น้ำหนัก 0.70 มาจากไหน
หน้า Self-Attention คืออะไร ยังอยู่ตรงนั้นสำหรับคนที่อยากคลิกดูเส้นน้ำหนักบนอีกประโยคหนึ่งก่อน ตัวเลขบนหน้านั้นไม่ใช่น้ำหนัก 0.10 / 0.70 ของแมวกับปลาในตอนนี้
ถามอธิบายกลับ (ลองเล่าให้เพื่อนฟัง)
สองข้อนี้แยกจากเนื้อเรื่องด้านบน ใช้สำหรับอธิบายกลับด้วยคำของตัวเอง
- ลองอธิบายด้วยคำของตัวเองว่า ทำไมการอ่านทีละคำแบบ RNN ถึงช้า และทำไมถึงจับความสัมพันธ์ของคำที่อยู่ห่างกันได้ยาก (ใบ้: นึกถึงการต่อคิว และเกมกระซิบ)
- ในประโยค “แมวไม่ยอมกินปลา เพราะมันเน่า” attention ช่วยอะไร — ลองตอบให้มีทั้ง “น้ำหนัก” และ “ทำพร้อมกันได้”
ลองตอบในหัวก็พอ ไม่ต้องส่งคำตอบที่ไหน