Attention Is All You Need

น้ำหนัก 0.70 มาจากไหน: แกะสูตร Self-Attention ทีละขั้นด้วยมือ (ตอนที่ 2)

ชุด Attention Is All You Need · บทที่ 2 จาก 12 ·

ตอนที่ 1 กำหนดทั้งน้ำหนักกับ value มาให้ เราเลยเห็นแล้วว่ามันผสมออกมาอย่างไร แต่ยังไม่ได้ให้ query กับ key ที่ใช้คิดน้ำหนัก 0.70 บทนี้จะเติมกลไกส่วนนั้นด้วยเวกเตอร์ชุดใหม่ ไม่ได้แกะย้อนว่าค่า 0.70 เดิมเกิดจากคู่ไหน

Attention(Q, K, V) = softmax(QKᵀ / √d_k) V

เริ่มจาก RNN seq2seq ที่ส่งสถานะทีละตำแหน่ง งาน Bahdanau ปี 2014 เพิ่ม additive attention ให้ decoder เลือกดูสถานะของ encoder ได้ แต่ทั้งคู่ยังเป็น RNN จากนั้น Transformer ปี 2017 จึงแทน recurrence ด้วย attention โดยยังมี feed-forward, residual และ layer norm อยู่ สี่ท่อนในสูตรคือวัดว่าเข้ากัน ปรับสเกล แปลงเป็นสัดส่วน แล้วผสมตามสัดส่วน

แวะนิดหนึ่ง — หน้า Self-Attention คืออะไร ใช้ประโยค “แมว มอง หนู บน พรม” ให้เลื่อน T เอง ตัวเลขคนละชุดกับของเล่นในบทนี้

1. คำถามตั้งต้น

ในสูตร softmax(QKᵀ / √d_k) V น้ำหนักที่แต่ละคำให้กับคำอื่นคำนวณมาอย่างไร ถูกนำไปใช้อย่างไร และทำไมภายในลำดับเดียวทั้งหมดนี้ถึงทำพร้อมกันได้ โดยไม่ต้องส่งบันทึกทีละตำแหน่งแบบ RNN?

ถ้าอ่านจบแล้วอธิบายได้ว่า Q, K, V แต่ละตัวทำอะไร ทำไมต้องหารด้วย √d_k และทำไมน้ำหนักในแต่ละแถวรวมกันได้ 1 ก็ได้แกนของบทนี้แล้ว ของเล่นสามคำเป็น encoder ไม่มี mask ที่ค่าเริ่มต้น query ของแมวคือ (1, 0) น้ำหนักมากสุดของแถวนั้นตกที่ตัวเอง ไม่ใช่ 0.70 รูปด้านล่างเริ่มที่ค่านี้ แล้วเลขในรูปตามแถบที่เลื่อน

2. ปูพื้นฐานทีละขั้น

ขั้นที่ 0: เส้นทางขากลับ

ตอนที่ 1 มองความจางของข้อมูลจากทางที่เดินไปข้างหน้า คำต้น ๆ ต้องถูกส่งต่อผ่านบันทึกทีละขั้นกว่าจะถึงคำท้าย ในส่วน Why Self-Attention ผู้เขียนเขียนว่าสิ่งที่สำคัญต่อการเรียนรู้ความสัมพันธ์ระยะไกลคือความยาวของเส้นทางที่สัญญาณทั้งขาไปและขากลับต้องเดิน การจางนี้เป็นการถูกผสมไปตามเส้นทางยาว LSTM มี forget gate ส่วน GRU ใช้ update gate กับ reset gate RNN ธรรมดาไม่มีประตูพวกนั้น

ขากลับคือสัญญาณที่ใช้สอน ถ้าคำสุดท้ายของประโยคยาว 50 คำแปลผิดเพราะไม่ได้ใช้ข้อมูลจากคำที่ 2 สัญญาณแก้ต้องย้อนเกือบ 50 ช่วง และระหว่างทางมันถูกผสมกับเรื่องของคำอื่น สิ่งที่ไปถึงต้นทางจึงอาจอ่อนหรือเพี้ยนจนแทบใช้สอนไม่ได้ ปัญหาจึงไม่จบที่ “ข้อมูลเก่าจาง” มันรวมถึง “บทเรียนจากความผิดพลาดส่งไปไม่ถึงต้นเหตุ” ทั้งสองอย่างมาจากเส้นทางที่ยาวตามระยะห่าง

paper อ้างงานของ Hochreiter และคณะ เป็นอ้างอิง [12] ในฉบับ arXiv เรื่อง gradient flow ใน recurrent nets บทนี้ไม่ได้อ่านงานนั้นเอง จึงถือตามที่ Vaswani และคณะเขียนอ้าง ไม่ได้ตรวจหลักฐานต้นทาง

ถ้าทุกคู่ตำแหน่งเหลือเส้นทางหนึ่งก้าว ทั้งข้อมูลขาไปและสัญญาณสอนขากลับถึงกันได้ในก้าวเดียว นั่นคือสิ่งที่ self-attention ทำ หัวข้อต่อจากนี้คือหนึ่งก้าวหน้าตาเป็นตัวเลขอย่างไร

ขั้นที่ 1: vector คือรายการตัวเลข

ตอนที่ 1 บอกแล้วว่าแต่ละ token ถูกแปลงเป็น vector คือรายการตัวเลข รุ่น base ใน paper ยาว 512 ตัว บทนี้ย่อเหลือ 2 ตัวเพื่อให้คิดในหัวได้ เช่น (1, 0) หรือ (0.5, 0.5) สิ่งที่ต้องใช้มีแค่ว่าเอามันมาบวก คูณ และเปรียบเทียบกันได้

ขั้นที่ 2: dot product คือคะแนนความเข้ากัน

dot product ของสอง vector คือเอาตัวเลขตำแหน่งเดียวกันมาคูณกัน แล้วบวก ตัวอย่างนี้เป็นนิยามของการคูณ ไม่ใช่น้ำหนัก attention และยังไม่มี softmax:

ถ้าสอง vector ชี้ทางเดียวกัน ผลเป็นบวกมาก ถ้าตั้งฉาก ผลใกล้ 0 ถ้าชี้สวน ผลติดลบ มันจึงใช้เป็นคะแนนความเข้ากันแบบหยาบได้ และมันมีแค่คูณกับบวก จึงเขียนเป็นการคูณเมทริกซ์ได้ ล็อก q = (1, 0) ไว้ k = (1, 0) ได้คะแนน 1 แต่ k = (3, 0) ได้ 3 ทั้งที่ชี้ทางเดียวกัน vector ที่ยาวกว่าให้คะแนนสูงกว่า ข้อนี้กลับมาตอนหารด้วย √d_k

ขั้นที่ 3: Query, Key, Value

ในส่วน 3.2 paper นิยาม attention ว่าเป็นการแปลง query หนึ่งตัวกับชุดของคู่ key–value ให้เป็น output ทั้งหมดเป็น vector และ output คือผลรวมถ่วงน้ำหนักของ value น้ำหนักของแต่ละ value มาจากฟังก์ชันวัดความเข้ากันระหว่าง query กับ key ที่คู่กับ value นั้น

ทุก token สวมสามบทบาทพร้อมกัน

หัวข้ออีเมลช่วยให้เราตัดสินใจว่าจะเปิดไหม แต่สิ่งที่ได้หลังเปิดคือเนื้อหาข้างใน key กับ value ก็แยกหน้าที่กันแบบนี้ ป้ายที่ใช้เลือกไม่จำเป็นต้องเหมือนของที่ส่งต่อ

แล้ว Q, K, V มาจากไหน? ในส่วน 3.2.2 มีการฉายด้วยเมทริกซ์ที่เรียนรู้ได้ W^Q, W^K, W^V token เดียวกันถูกคูณสามชุด ได้ q, k, v สามตัว ตัวเลขในเมทริกซ์ไม่มีใครตั้งมือ โมเดลเรียนรู้ระหว่างเทรน ในส่วน 3.2.3 self-attention ของ encoder ใช้ key, value และ query จากที่เดียวกัน คือ output ของ layer ก่อนหน้า การฉายหลายชุดพร้อมกันเป็นเรื่อง multi-head ของตอนถัดไป ของเล่นในบทนี้เริ่มหลังการฉายแล้ว คือให้ q, k, v มาเลย

น้ำหนักใหญ่ได้เมื่อ query ของฝ่ายถามเข้ากับ key ของอีกคำ และความเข้ากันวัดด้วย dot product ตอนที่ 1 มีน้ำหนักกับ value แล้ว แต่ยังไม่มี query กับ key ของเลข 0.70 บทนี้จึงไม่แกะย้อนคู่นั้น

ขั้นที่ 4: คะแนนทุกคู่ใน QKᵀ

ถ้ามี n token คะแนนที่ต้องคิดคือ query ของคำที่ i กับ key ของคำที่ j ครบ n × n คู่ paper แพ็ก query ทุกตัวเป็นเมทริกซ์ Q แต่ละแถวคือ query ของหนึ่ง token แล้วแพ็ก key กับ value เป็น K กับ V จากนั้นคำนวณ QKᵀ ช่องแถว i คอลัมน์ j คือคะแนนระหว่าง query i กับ key j การคูณเมทริกซ์ครั้งเดียวจึงเท่ากับ dot product ครบทุกคู่

ขั้นที่ 5: หารด้วย √d_k

d_k คือจำนวนตัวเลขใน query และใน key แต่ละตัว รุ่น base ของ paper ใช้ d_k = 64 ต่อ head ก่อนส่งคะแนนเข้า softmax ให้หารทุกช่องด้วย √d_k รูปคณิตเหมือน softmax ที่ตั้ง T = √d_k แต่ในสูตรนี้ค่าถูกกำหนดจากมิติของ key ไม่ใช่ปุ่มอุณหภูมิที่เลือกได้อิสระ ส่วนหน้า Self-Attention คืออะไร ให้ลองเปลี่ยน T เองและใช้ตัวเลขคนละชุด

เหตุผลในส่วน 3.2.1 มีสองชั้น

  1. ผู้เขียนอ้างข้อสังเกตจากงานของ Britz และคณะ อ้างอิง [3] ว่าเมื่อ d_k เล็ก dot-product attention กับ additive attention ทำงานได้ใกล้กัน แต่เมื่อ d_k ใหญ่ additive attention ทำได้ดีกว่า dot-product แบบไม่ปรับสเกล บทนี้ไม่ได้อ่านงานของ Britz เอง
  2. คำอธิบายที่ผู้เขียนสงสัยว่าเป็นสาเหตุ ผู้เขียนใช้คำว่า “We suspect” เมื่อ d_k ใหญ่ dot product จะมีขนาดใหญ่ ซึ่งดัน softmax เข้าไปในบริเวณที่ gradient เล็กมาก จึงหารด้วย √d_k เพื่อหักล้าง นี่เป็นข้อเสนอ ไม่ใช่บทพิสูจน์

ทำไม dot product ถึงโตตาม d_k? footnote ที่ 4 บอกไว้ตรง ๆ ว่า ถ้าแต่ละมิติของ q และของ k เป็นอิสระกันและแจกเหมือนกัน ค่าเฉลี่ย 0 ความแปรปรวน 1 แล้ว q · k มีค่าเฉลี่ย 0 และ Var(q · k) = d_k ส่วนเบี่ยงเบนมาตรฐานคือ √d_k การหารด้วย √d_k ทำให้ความแปรปรวนของคะแนนเป็น 1 ภายใต้ข้อสมมตินี้ ไม่ได้รับประกันว่าเวกเตอร์จริงในโมเดลจะเป็นแบบนี้ทุกครั้ง การสุ่มแต่ละชุดแกว่งรอบ d_k และรอบ 1 หลังหาร

Fig. 1

ความแปรปรวน ของ q · k

แต่ละช่องของ q และ k สุ่มแบบอิสระ ค่าเฉลี่ย 0 ความแปรปรวน 1 ด้วย mulberry32 ที่ seed ด้านล่าง 4000 คู่ต่อหนึ่งค่า d_k ความแปรปรวนของตัวอย่างคือค่าเฉลี่ยของ (x − ค่าเฉลี่ย)2 ปุ่มสุ่มใหม่บวก seed ทีละ 1 แล้วคำนวณใหม่

ตัวเลขในรูปเป็นการสุ่มจากข้อสมมติของ footnote ไม่ใช่ตัวเลขจาก paper

seed 1
  1. d_k = 2 · ทฤษฎี 2 · หลังหารทฤษฎี 1
    1. ไม่หาร วัดได้ 1.9705
    2. ไม่หาร ทฤษฎี 2
    3. หาร √d_k วัดได้ 0.9853
    4. หาร √d_k ทฤษฎี 1
  2. d_k = 64 · ทฤษฎี 64 · หลังหารทฤษฎี 1
    1. ไม่หาร วัดได้ 63.3198
    2. ไม่หาร ทฤษฎี 64
    3. หาร √d_k วัดได้ 0.9894
    4. หาร √d_k ทฤษฎี 1
  3. d_k = 512 · ทฤษฎี 512 · หลังหารทฤษฎี 1
    1. ไม่หาร วัดได้ 496.5016
    2. ไม่หาร ทฤษฎี 512
    3. หาร √d_k วัดได้ 0.9697
    4. หาร √d_k ทฤษฎี 1

seed 1 · N = 4000 · d_k 2 วัดได้ 1.9705 ทฤษฎี 2 · หลังหาร วัดได้ 0.9853 ทฤษฎี 1 · d_k 64 วัดได้ 63.3198 ทฤษฎี 64 · หลังหาร วัดได้ 0.9894 ทฤษฎี 1 · d_k 512 วัดได้ 496.5016 ทฤษฎี 512 · หลังหาร วัดได้ 0.9697 ทฤษฎี 1 · แกว่งรอบ d_k

รูปที่ 1. ทฤษฎีคือ Var(q · k) = d_k และหลังหาร √d_k ความแปรปรวนเป็น 1 เลขที่วัดได้ตาม seed ที่โชว์เหนือตาราง กดสุ่มใหม่แล้วทั้ง seed และเลขขยับรอบค่าทฤษฎี

ลองเดาก่อนลาก ฝั่งซ้ายช่องแรกนำอยู่เยอะ ฝั่งขวายังสูสีกัน ถ้าเราเติมให้ช่องสองเท่ากัน ฝั่งไหนจะขยับแรงกว่า ตั้งแถบที่ 0.5 แล้วดูน้ำหนักช่องสองจาก 0.0180 เป็น 0.0293 เทียบกับ 0.3072 เป็น 0.4223 ตัวอย่างนี้เลือกหารคะแนนด้วย 8 เพื่อเทียบความไวของ softmax ไม่ได้ใช้ d_k = 2 ของของเล่นสามคำ การหารนี้เทียบได้กับตั้ง T = 8 แต่ไม่ใช่การเปลี่ยนมิติของเวกเตอร์ในรูปที่ 3 ของเล่นนี้มีแค่คะแนน ไม่มีคำและไม่มี V เลยหยุดที่น้ำหนัก

Fig. 2

คะแนนชุดเดียวกัน คนละสเกล

ฝั่งซ้ายคือ (8, 4, 0) ฝั่งขวาคือคะแนนนั้นหาร 8 แถบเลื่อนบวกจำนวนเดียวกันเข้าช่องสองของทั้งสองฝั่ง แล้ว softmax คำนวณใหม่ทั้งคู่ ไม่มี value ในของเล่นนี้ จึงยังไม่มี output

ตัวเลขตัวอย่าง คนละชุดกับแมว กิน ปลา หารด้วย 8 เทียบได้กับ T = 8

คะแนนใหญ่

(8, 4, 0)

ช่อง 1
0.9817
ช่อง 2
0.0180
ช่อง 3
0.0003

หาร 8 แล้ว

(1, 0.5, 0)

ช่อง 1
0.5065
ช่อง 2
0.3072
ช่อง 3
0.1863

คนละชุดกับแมว กิน ปลา · ไม่มี V จึงหยุดที่น้ำหนัก · บวกช่องสอง +0 · ซ้ายช่องสอง 0.0180 → 0.0180 · ขวาช่องสอง 0.3072 → 0.3072 · มากสุดซ้าย “ช่อง 1” (0.9817) · มากสุดขวา “ช่อง 1” (0.5065) · การหารด้วย 8 เทียบได้กับ T = 8 แต่ไม่ใช่การเปลี่ยนมิติของเวกเตอร์ในรูปที่ 3

รูปที่ 2. ของเล่นคนละชุดกับประโยคสามคำ น้ำหนักตามแถบที่เลื่อนอยู่

เมื่อคะแนนช่องหนึ่งทิ้งห่างช่องอื่นมาก softmax จะเกือบเลือกช่องเดียว การขยับคะแนนเล็กน้อยจึงแทบไม่เปลี่ยนน้ำหนัก อนุพันธ์ของน้ำหนักต่อคะแนนในบริเวณนี้เล็ก นี่คือจุดที่ paper สงสัยว่าจะทำให้ส่งสัญญาณผ่าน attention เรียนรู้ได้ยากขึ้น ไม่ได้หมายความว่า gradient ทุกส่วนของโมเดลหายไป

ขั้นที่ 6: softmax ทำให้แถวรวมได้ 1

คะแนนหลังปรับสเกลยังเป็นอะไรก็ได้ ติดลบก็ได้ จึงยังไม่สะดวกที่จะเอาไปผสม value softmax ทำสองขั้นกับหนึ่งแถว ยก e (ค่าคงที่ประมาณ 2.718) กำลังคะแนนแต่ละช่อง ทุกค่ากลายเป็นบวก และช่องที่คะแนนสูงจะโดดมากกว่าแบบเส้นตรง จากนั้นหารทุกช่องด้วยผลรวมของแถว ทุกช่องอยู่ระหว่าง 0 กับ 1 และรวมกันได้ 1

softmax(s)_j = e^{s_j} / Σ_m e^{s_m}

ทำไมต้องรวมได้ 1? เพราะอยากให้น้ำหนักเป็นสัดส่วนของความสนใจในแถวนั้น ถ้าให้คำหนึ่งมากขึ้น ต้องลดคำที่เหลือ ผลคือ output เป็นค่าเฉลี่ยถ่วงน้ำหนักของ value เสมอ ขนาดของผลจึงไม่พองตามจำนวนคำ งบ 100% นี้เป็นของแต่ละแถว ไม่ใช่งบก้อนเดียวของทั้งประโยค และไม่ใช่เวลาหรืองบคำนวณ paper เรียกขั้นนี้ว่าการใส่ softmax เพื่อให้ได้น้ำหนักของ value

ขั้นที่ 7: ผลรวมถ่วงน้ำหนักของ V

เอาน้ำหนักแถว i ไปคูณ value ของแต่ละคำ แล้วบวก ได้ vector ใหม่ของคำ i ในรูปเมทริกซ์คือคูณตารางน้ำหนัก n × n กับ V ขนาด n × d_v ได้ output n แถว

Figure 2 ฝั่งซ้ายใน paper เรียงจากล่างขึ้นบนว่า Q กับ K เข้า MatMul แล้ว Scale แล้ว Mask (opt.) แล้ว SoftMax แล้ว MatMul กับ V ตรงกับขั้นที่ 4 ถึง 7 กล่อง Mask ที่เขียนว่า opt. เป็นทางเลือก จะเปิดในหัวข้อที่ 6 ของเล่นหลักของบทนี้ปิดกล่องนั้นไว้ คือ encoder ไม่มี mask

3. ของเล่นสามคำ ที่กำหนดครบ

ขั้น A–E บนค่าในตาราง

แถวคือฝ่ายถาม คอลัมน์คือฝ่ายถูกถาม คะแนนดิบคือ q ของแถวจุดกับ k ของคอลัมน์

คะแนนดิบ QKᵀ
คะแนนดิบแมวกินปลา
แมว ถาม201
กิน ถาม011
ปลา ถาม212

ตารางไม่สมมาตร ในค่าเริ่มต้น “แมวถามปลา” ได้ 1 แต่ “ปลาถามแมว” ได้ 2 เพราะใช้ q ของฝ่ายถามกับ k ของฝ่ายถูกถาม คนละ vector ช่องไม่เท่ากันเป็นผลของเวกเตอร์ในตารางนี้ ไม่ได้แปลว่าแมวกับปลาในภาษาคนสนใจกันไม่เท่า รูปที่ 3 ถ้าเลื่อนแถบจะใช้คนละค่า

หารด้วย √2 ซึ่งปัดสามตำแหน่งเป็น 1.414 ได้ 2 → 1.414, 1 → 0.707, 0 → 0 e ยกกำลัง √2 ที่ยังไม่ปัดได้ 4.113 ส่วน e ยกกำลัง 1.414 ที่ปัดแล้วได้ 4.112 รูปใช้ √2 เต็มจึงโชว์ 4.113

คะแนนหลังหาร √d_k
หลังหาร √2แมวกินปลา
แมว ถาม1.41400.707
กิน ถาม00.7070.707
ปลา ถาม1.4140.7071.414

softmax ทีละแถวจากคะแนนเต็มไม่ปัด e^0 = 1, e^{√2/2} ปัดเป็น 2.028, e^{√2} ปัดเป็น 4.113

น้ำหนักหลัง softmax แต่ละแถวรวม 1
น้ำหนักแมวกินปลา
แมว ถาม0.5760.1400.284
กิน ถาม0.1980.4010.401
ปลา ถาม0.4010.1980.401

วิธีอ่านตารางนี้บนของเล่นชุดนี้เท่านั้น

จากนั้นคูณน้ำหนักด้วย V ที่กำหนดไว้ v แมว (1, 0), v กิน (0, 1), v ปลา (0.5, 0.5) output ที่ปัดจากค่าเต็มเป็นสามตำแหน่งคือแมว (0.718, 0.282), กิน (0.398, 0.602), ปลา (0.602, 0.398) ถ้าเอาแต่น้ำหนักที่ปัดแล้วมาบวกมือ แถวกินได้ 0.198 + 0.2005 = 0.3985 ซึ่งอยู่คนละจุดปัดกับ 0.398 ตารางกับรูปใช้ค่าเต็มแล้วค่อยปัด จึงตรงกับ 0.398

แต่ละ token ได้ vector ใหม่ที่ยังมีร่องรอยของ value เดิมปนกับ value อื่นตามน้ำหนัก vector ใหม่ของกินเป็นผลบวกของ value ตามน้ำหนัก ไม่ได้แปลว่าคำนั้นเข้าใจแมวแล้ว เลขในรูปที่ 3 ตามแถบที่เลื่อน

Fig. 3

สูตรทั้งเส้น บนเวกเตอร์ชุดนี้

เลือกคำที่ถาม แล้วเดินจาก q, k ไปคะแนน ไปการหาร ไป softmax ไปผลรวมของ v แถบเลื่อนขยับเลขตัวที่สองของ query แมว จาก 0 ซึ่งเป็นค่าในตาราง รูปที่ 4 ใช้สถานะเดียวกัน

ตัวเลขตัวอย่าง ของเล่นที่กำหนดครบด้านบน ไม่ใช่ค่าจากโมเดลจริง

q, k, v ตรงกับตารางของเล่นที่กำหนดครบในบทนี้

แมวถาม → น้ำหนัก (0.576, 0.140, 0.284) → output (0.718, 0.282)

softmax(QKᵀ / √d_k) V · d_k = 2 · √d_k = 1.414 · หารด้วย √d_k เทียบเท่า T = √d_k ที่ล็อกตาม d_k · encoder · ไม่มี mask · ทุกตำแหน่งเห็นทุกตำแหน่ง รวมตัวเอง

q ของแมว = (1, 0)

k แมว = (2, 0) · k กิน = (0, 1) · k ปลา = (1, 1)

v แมว = (1, 0) · v กิน = (0, 1) · v ปลา = (0.5, 0.5)

encoder · ไม่มี mask · ทุกตำแหน่งเห็นทุกตำแหน่ง รวมตัวเอง

คะแนนดิบ

  1. แมว ถาม
    1. แมว 2
    2. กิน 0
    3. ปลา 1
  2. กิน ถาม
    1. แมว 0
    2. กิน 1
    3. ปลา 1
  3. ปลา ถาม
    1. แมว 2
    2. กิน 1
    3. ปลา 2

หลังหาร √d_k และหลัง mask

  1. แมว ถาม
    1. แมว 1.414
    2. กิน 0
    3. ปลา 0.707
  2. กิน ถาม
    1. แมว 0
    2. กิน 0.707
    3. ปลา 0.707
  3. ปลา ถาม
    1. แมว 1.414
    2. กิน 0.707
    3. ปลา 1.414

softmax ทีละแถว

e ยกกำลังคะแนนหลังหารและหลัง mask = 4.113, 1.000, 2.028 · รวม 7.141

น้ำหนักแถวนี้รวม 1.000

  1. แมว ถาม
    1. แมว 0.576
    2. กิน 0.140
    3. ปลา 0.284
  2. กิน ถาม
    1. แมว 0.198
    2. กิน 0.401
    3. ปลา 0.401
  3. ปลา ถาม
    1. แมว 0.401
    2. กิน 0.198
    3. ปลา 0.401
แมว
0.576
กิน
0.140
ปลา
0.284

output = น้ำหนัก × V

  1. แมว: 0.576 × (1, 0) = (0.576, 0.000)
  2. กิน: 0.140 × (0, 1) = (0.000, 0.140)
  3. ปลา: 0.284 × (0.5, 0.5) = (0.142, 0.142)
  4. รวม output ของแมว = (0.718, 0.282)
  1. แมว (0.718, 0.282)
  2. กิน (0.398, 0.602)
  3. ปลา (0.602, 0.398)

ถาม “แมว” · q = (1, 0) · ให้น้ำหนักมากสุด “แมว” (0.576) · ให้ตัวเอง 0.576 · output (0.718, 0.282) · encoder · ไม่มี mask · ทุกตำแหน่งเห็นทุกตำแหน่ง รวมตัวเอง · ตัวเลขตัวอย่าง

รูปที่ 3. มากสุดนับรวมตัวเอง เลขในรูปตามแถบและปุ่มที่กดอยู่ ตอนแถบอยู่ที่ 0 และยังมองได้ทุกคำ แถวแมวจึงให้น้ำหนักตัวเองมากสุด

Fig. 4

ชุดเดียวกัน หารกับไม่หาร

q, k, v, คำที่ถาม, และ mask เป็นชุดเดียวกับรูปที่ 3 ฝั่งซ้ายหารด้วย √d_k ก่อน softmax ฝั่งขวาเอาคะแนนดิบเข้า softmax เลย งานคือหา output ของสูตร เมตริกคือน้ำหนักที่รวมได้ 1 กับเวกเตอร์ output

ตัวเลขตัวอย่าง ฝั่งไม่หารคำนวณจากเวกเตอร์ชุดเดียวกัน

q, k, v ตรงกับตารางของเล่นที่กำหนดครบในบทนี้

แมวถาม → น้ำหนัก (0.576, 0.140, 0.284) → output (0.718, 0.282)

q ของแมว = (1, 0)

หาร √d_k

softmax([1.414, 0, 0.707]) V = (0.718, 0.282)

แมว
0.576
กิน
0.140
ปลา
0.284
  1. แมว ถาม
    1. แมว 0.576
    2. กิน 0.140
    3. ปลา 0.284
  2. กิน ถาม
    1. แมว 0.198
    2. กิน 0.401
    3. ปลา 0.401
  3. ปลา ถาม
    1. แมว 0.401
    2. กิน 0.198
    3. ปลา 0.401
  1. แมว (0.718, 0.282)
  2. กิน (0.398, 0.602)
  3. ปลา (0.602, 0.398)

ไม่หาร

softmax([2, 0, 1]) V = (0.788, 0.212) · ไม่ได้หาร √d_k

แมว
0.665
กิน
0.090
ปลา
0.245
  1. แมว ถาม
    1. แมว 0.665
    2. กิน 0.090
    3. ปลา 0.245
  2. กิน ถาม
    1. แมว 0.155
    2. กิน 0.422
    3. ปลา 0.422
  3. ปลา ถาม
    1. แมว 0.422
    2. กิน 0.155
    3. ปลา 0.422
  1. แมว (0.788, 0.212)
  2. กิน (0.367, 0.633)
  3. ปลา (0.633, 0.367)

ชุดเดียวกัน · ถาม “แมว” · q แมว = (1, 0) · หาร √d_k มากสุด “แมว” (0.576) ให้ตัวเอง 0.576 output (0.718, 0.282) · ไม่หาร มากสุด “แมว” (0.665) ให้ตัวเอง 0.665 output (0.788, 0.212) · หารด้วย √d_k เทียบเท่า T = √d_k ที่ล็อกตาม d_k · encoder · ไม่มี mask · ทุกตำแหน่งเห็นทุกตำแหน่ง รวมตัวเอง

รูปที่ 4. เทียบบน q, k, v ชุดเดียว งานเดียว เมตริกเดียว สิ่งที่ต่างคือหารด้วย √d_k หรือไม่หาร ฝั่งไม่หารคำนวณจากเวกเตอร์ชุดเดียวกัน

ทำไมทุกแถวคิดพร้อมกันได้

การคิดแถวปลาไม่ต้องรอผลของแถวแมว ทุกแถวใช้แค่ q ของตัวเองกับ K และ V ชุดเดียวกันที่มีอยู่แล้ว เทียบกับ RNN ที่ภายในลำดับเดียวต้องได้บันทึกของตำแหน่งก่อนหน้าก่อนจึงจะเริ่มตำแหน่งถัดไปได้ paper บอกว่าธรรมชาตินี้ขัดขวางการทำงานแบบขนานภายในตัวอย่างเดียวกัน หลายประโยคในแบตช์ยังเดินพร้อมกันที่ดัชนีเวลาเดียวกันได้ สิ่งที่เรียงคือตำแหน่งภายในลำดับเดียว

เพราะไม่มีแถวไหนพึ่งผลของอีกแถว งานทั้งก้อนเขียนเป็นการคูณเมทริกซ์สองครั้ง คือ QKᵀ แล้วตามด้วยน้ำหนักคูณ V โดยมีการหารและ softmax คั่นกลาง ในส่วน 3.2.1 ผู้เขียนบอกว่า dot-product attention ใช้โค้ดคูณเมทริกซ์ที่ปรับแต่งมาได้ จึงเร็วและประหยัดหน่วยความจำกว่า additive attention ในทางปฏิบัติ ส่วนที่อ่านไม่มีตารางจับเวลา ข้อนี้คนละอย่างกับ Table 1 Table 1 ไม่ได้นับวินาที มันบอกว่าปริมาณงานต่อเลเยอร์โตตามอะไร self-attention โตแบบ O(n² · d) ส่วน recurrent โตแบบ O(n · d²) จำนวนขั้นที่ต้องเรียงในเลเยอร์เป็น O(1) เทียบ O(n) และนั่นคือตอน token ของลำดับนั้นมีครบแล้ว เลเยอร์ถัดไปยังรอ output ของเลเยอร์นี้ ตอนสร้างคำแปลฝั่ง decoder ยังออกทีละ token ตามที่ตอนที่ 1 เขียนไว้

ทำไมเส้นทางเหลือหนึ่งก้าว

ในตารางที่ล็อก query แมว = (1, 0) ช่อง “แมวถามปลา” คือ 0.284 ข้อมูลจาก value ของปลาไหลเข้า output ของแมวผ่านการคูณครั้งเดียว ไม่ได้ผ่านกินก่อน ถ้าเลื่อนแถบในรูปที่ 3 ช่องนั้นในรูปเปลี่ยน แต่ทางผ่านยังเป็นการคูณครั้งเดียว ถ้าประโยคยาว 50 คำ คำที่ 1 กับคำที่ 50 ก็มีช่องของตัวเองในตาราง 50 × 50

ในเลเยอร์นี้ output ของแมวใช้ query ของแมว แต่ใช้ key กับ value ของปลาได้โดยตรง ตอนย้อนสัญญาณจึงไปถึง k และ v ของปลา รวมถึง q ของแมวได้ โดยไม่ต้องผ่านคำว่ากินก่อน ไม่ใช่ไปถึง q ของปลาจาก output แถวนี้ เส้นทางจาก value ของปลาถึง output ของแมวจึงยาวหนึ่งก้าว นี่คือ maximum path length O(1) ใน Table 1 เทียบกับ O(n) ของ recurrent layer ราคาในตารางเดียวกันคือปริมาณงานต่อเลเยอร์ O(n² · d) เทียบ recurrent ที่ O(n · d²) ไม่ใช่นาฬิกาจับวินาที

4. ศัพท์

ผลคูณจุด (dot product)
คูณตัวเลขตำแหน่งเดียวกันของสอง vector แล้วบวก ใช้เป็นคะแนนว่าชี้ทางเดียวกันแค่ไหน และดูความยาวด้วย
คิวรี (query, Q)
vector ที่ใช้ตอน token เป็นฝ่ายถาม
คีย์ (key, K)
vector ป้ายที่ query อื่นมาเทียบ
แวลู (value, V)
vector เนื้อหาที่ถูกส่งต่อเมื่อได้รับน้ำหนัก
d_k
จำนวนตัวเลขใน query และ key แต่ละตัว รุ่น base ใช้ 64 ต่อ head ของเล่นบทนี้ใช้ 2
เมทริกซ์
ตารางตัวเลข ที่นี่ใช้แพ็ก vector ของทุก token เป็นแถว เพื่อคำนวณพร้อมกัน
Kᵀ
พลิกแถวของ K เป็นคอลัมน์ ทำให้ QKᵀ เป็นตารางคะแนนทุกคู่
ซอฟต์แม็กซ์ (softmax)
ยก e กำลังคะแนนหนึ่งแถว แล้วหารด้วยผลรวม ได้ค่าบวกที่รวมกันเป็น 1
attention weights
น้ำหนักจาก softmax บอกสัดส่วนที่แถวนั้นดึง value จากแต่ละตำแหน่ง รวมตัวเองด้วย
ผลรวมถ่วงน้ำหนัก
คูณแต่ละ value ด้วยน้ำหนักแล้วบวก เมื่อน้ำหนักรวมได้ 1 ก็คือค่าเฉลี่ยถ่วงน้ำหนัก
เกรเดียนต์
สัญญาณว่าควรปรับตัวเลขภายในไปทางไหนเท่าไรจึงจะผิดน้อยลง ถ้าเล็กมาก การเรียนรู้นิ่ง
ความแปรปรวน
ตัววัดว่าค่าสุ่มแกว่งห่างจากค่าเฉลี่ยแค่ไหน รากที่สองของมันคือส่วนเบี่ยงเบนมาตรฐาน
additive attention
อีกแบบที่ใช้เครือข่ายชั้นซ่อนหนึ่งชั้นวัดความเข้ากัน แทน dot product ตามที่ Vaswani และคณะอ้างถึงงานของ Bahdanau และคณะ
mask
ปิดบางช่องก่อน softmax โดยตั้งคะแนนเป็น −∞ แล้ว e ยกกำลังช่องนั้นเป็น 0 น้ำหนักช่องนั้นจึงเป็น 0 ของเล่นหลักของบทนี้ไม่ใส่ mask

5. อุปมา พร้อมขอบเขต

แต่ละภาพจับคนละจุด และใช้ได้ไม่สุดทาง

อุปมา 1: ห้องสมุด

คนค้นมีคำถามในใจ (query) หนังสือแต่ละเล่มมีป้ายสัน (key) เทียบคำถามกับป้ายทุกเล่ม (dot product) แล้วหยิบเนื้อหาข้างใน (value) ของเล่มที่เข้ากัน สิ่งที่ได้ผสมจากหลายเล่ม โดยเล่มที่ตรงที่สุดมีส่วนมาก

อุปมา 2: งบ 100% ของแถว

ทุกคำได้งบความสนใจ 100% ของแถวตัวเอง ต้องแบ่งให้ทุกตำแหน่งในประโยค รวมตัวเอง คะแนนบอกว่าใครควรได้มาก แต่ยกกำลัง e ทำให้คะแนนสูงได้ส่วนแบ่งเพิ่มแบบทวีคูณ ไม่ใช่เพิ่มตามสัดส่วนตรง

อุปมา 3: ปุ่มลดเกนก่อนลำโพง

ในภาพลำโพง ให้คะแนนก่อน softmax เป็นสัญญาณเข้า และให้เกนเป็น 1/√d_k เราลดความต่างระหว่างคะแนนก่อนเข้าช่วงไม่เป็นเส้นตรงของ softmax ภาพนี้ช่วยคิดเรื่องความไวของน้ำหนัก ไม่ได้บอกว่าเสียงดังเท่ากันทุกช่องแล้ว softmax จะอิ่ม

อุปมา 4: จดหมายทอด กับกลุ่มที่เห็นทุกข้อความ

ใน RNN ถ้าตำแหน่งที่ 50 อยากรู้ว่าตำแหน่งที่ 2 ถืออะไรอยู่ ต้องอาศัยบันทึกที่ส่งต่อมา และสัญญาณสอนก็ย้อนกลับตามเส้นเดิม ใน self-attention ที่ไม่มี mask ทุกตำแหน่งมีช่องตรงถึงกันในเลเยอร์นั้น

6. เชื่อมกลับไปที่ paper

ส่วนไหนพูดถึงอะไร

mask ใน decoder

ตอนสร้างคำแปล decoder เดาคำถัดไปจากคำที่สร้างไปแล้ว จะดูคำในอนาคตไม่ได้ paper ตั้งคะแนนของช่องที่ไม่อนุญาตเป็น −∞ ก่อน softmax เพราะ e ยกกำลัง −∞ เป็น 0 น้ำหนักช่องนั้นจึงเป็น 0 ตำแหน่งปัจจุบันยังเห็นตัวเอง ของเล่นสามคำเดิม ถ้าใส่ mask แบบนี้หลังหารสเกล แถวกินเหลือคะแนน (0, 0.707, −∞) น้ำหนักปัดเป็น (0.330, 0.670, 0) แถวแมวซึ่งเป็นคำแรกเหลือให้มองแค่ตัวเอง จึงได้ 1 ที่ตัวเอง แถวปลาอยู่ท้ายสุด ไม่มีตำแหน่งให้ปิด แถวจึงเป็นแถวเดิม รูปที่ 3 มีปุ่ม “ปิดคำถัดไป” ที่ตั้ง −∞ ก่อน softmax จริง ไม่ได้คำนวณเสร็จแล้วค่อยซ่อนเส้น

ยกมาเพื่อให้เห็นกล่อง Mask เท่านั้น โครงสร้าง encoder–decoder ทั้งก้อนเป็นเรื่องของตอนถัดไป ของเล่นเริ่มต้นของบทนี้เป็น encoder ไม่มี mask

ผลที่เกี่ยวกับกลไกนี้

ข้อจำกัดที่ผู้เขียนยอมรับ เฉพาะกลไกนี้

สิ่งที่ paper ยังไม่ได้พิสูจน์

7. สรุป

กลับมาที่คำว่า “มัน” ในตอนที่ 1 น้ำหนัก 0.70 กับ value ที่ใช้ผสมมีให้แล้ว สิ่งที่ยังไม่มีคือ query กับ key บนของเล่นแมว กิน ปลา เมื่อ query ของแมวเป็น (1, 0) และยังมองได้ทุกคำ น้ำหนักมากสุดคือ 0.576 ที่แมวเอง และ output คือ (0.718, 0.282)

8. แหล่งอ้างอิง

  1. Vaswani et al. 2017, Attention Is All You Need — arXiv 1706.03762 (v7) · PDF ประเภท preprint อ่านลึกแค่ไหน อ่านละเอียดส่วน 3.2–3.2.3 รวม footnote 4, ส่วน 4 และ Table 1, Figure 2 ซ้าย, ข้อความ Table 3 แถว (A)(B), คำบรรยายภาพในภาคผนวก งาน Britz และ Hochreiter ที่ paper อ้างไม่ได้อ่านต้นฉบับ
  2. Vaswani et al. 2017 — NeurIPS (NIPS 2017) proceedings · PDF ประเภท peer-reviewed (conference proceedings) อ่านลึกแค่ไหน ตรวจเทียบเฉพาะจุด: ส่วน 3.2.1, สมการที่ 1, footnote 4, ส่วน 3.2.3, Table 1, ย่อหน้า interpretability ในส่วน 4 ตรงกับ preprint ทุกจุดที่ตรวจ PDF ไม่มีภาคผนวกภาพ attention
  3. Luong, Pham, Manning 2015, Effective Approaches to Attention-based Neural Machine Translation — arXiv 1508.04025 ประเภท preprint บน arXiv หน้า arXiv ระบุว่าเป็น EMNLP 2015 camera-ready version (peer-reviewed) อ่านลึกแค่ไหน อ่านบางส่วน: ส่วน 3.1 เรื่อง global attention และฟังก์ชันคะแนน dot / general / concat และย่อหน้าผลใน 5.3
  4. Bahdanau, Cho, Bengio 2014 — arXiv 1409.0473 ประเภท preprint บน arXiv PDF ระบุ Published as a conference paper at ICLR 2015 (peer-reviewed) อ่านลึกแค่ไหน ไม่ได้อ่านเพิ่มในรอบนี้ อ่านบางส่วนไว้ในตอนที่ 1 กล่าวถึงในฐานะงานต้นแบบของ additive attention ตามที่ Vaswani และคณะอ้าง

บริบทจากส่วนที่อ่านของ Luong และคณะ งานนั้นเสนอฟังก์ชันคะแนนระหว่าง hidden state ของ decoder กับของ encoder สามแบบ คือ dot ซึ่งเป็น dot product ตรง ๆ, general ซึ่งคั่นด้วยเมทริกซ์ที่เรียนรู้ได้, และ concat ซึ่งใช้เครือข่ายชั้นเดียวคล้ายแนวของ Bahdanau แล้วใช้ softmax แปลงคะแนนเป็นน้ำหนักเหมือนกัน ผู้เขียนงานนั้นรายงานว่า dot ทำงานได้ดีกับ global attention ในงานนั้น ส่วน general ดีกว่าสำหรับ local attention scaled dot-product ของ Transformer จึงอยู่ใกล้แบบ dot ที่เพิ่มการหาร √d_k และเปลี่ยนจาก hidden state ของ RNN มาเป็น q, k, v ที่ฉายจาก token เดียวกัน

9. ตอนถัดไป

ตอนนี้เห็นแล้วว่า attention หนึ่งชุดคิดน้ำหนักและผสม value อย่างไร ยังมีสองเรื่องที่ของเล่นนี้ตอบไม่ได้

ตอนที่ 3 จะเปิดสองเรื่องนี้ บทนั้นยังไม่มีหน้าใน logbook หน้า Self-Attention คืออะไร ยังเป็นรูปอีกประโยคหนึ่ง ตัวเลขคนละชุดกับตารางค่าเริ่มต้นที่ query แมวเป็น (1, 0)

ถามอธิบายกลับ

สองข้อนี้อยู่นอกเนื้อเรื่อง ใช้เช็กว่าเล่าต่อได้ด้วยคำของตัวเอง

  1. Q, K, V แต่ละตัวทำอะไร และทำไมน้ำหนักในแต่ละแถวรวมกันได้ 1 (ใบ้: ห้องสมุด และงบ 100% ของแถวนั้น)
  2. ทำไมต้องหาร QKᵀ ด้วย √d_k ก่อน softmax และทำไมภายในลำดับเดียว self-attention ไม่ต้องรอทีละตำแหน่ง ขณะที่ RNN ต้องรอ ทั้งที่แบตช์ของหลายลำดับยังเดินพร้อมกันได้

ลองตอบในหัวก็พอ