- เริ่มจาก softmax attention แล้วค่อย ๆ อนุมานไปยัง linear attention ที่ใช้ สถานะขนาดคงที่, DeltaNet ที่บันทึกเฉพาะข้อผิดพลาด, Gated DeltaNet ที่ลดทอนสถานะทั้งหมด และ Kimi Delta Attention(KDA) ที่ลดทอนแยกตาม channel
- linear attention พื้นฐานเก็บผลรวมของ outer product ของ key-value ในอดีตไว้ในสถานะ (S_t) จึงทำงานเป็นเชิงเส้นตามความยาว sequence แต่เกิด additive write interference คือไม่ได้แทนที่ค่าใหม่ แต่บวกเพิ่มเข้าไปในความสัมพันธ์เดิม
- DeltaNet บันทึกส่วนต่างระหว่างค่าที่ทำนายจาก key ปัจจุบันกับ value เป้าหมาย โดยคูณด้วย (\beta_t) และการตีความทั้งสามแบบ—เงื่อนไขการสร้างคืนทันที, online gradient descent และการอัปเดตสถานะ rank-1—นำไปสู่สมการเดียวกัน
- Gated DeltaNet ลดทอนสถานะทั้งหมดก่อนด้วยสเกลาร์ (\alpha_t) ส่วน KDA ขยายสิ่งนี้เป็นเมทริกซ์ทแยง (D_t=\operatorname{Diag}(\alpha_t)) เพื่อคงหรือ删除ข้อมูลในแต่ละ key channel ด้วยอัตราที่ต่างกัน
- เรียกใช้ recurrence ของ KDA เดียวกันได้ทั้งด้วย fused recurrent Triton kernel สำหรับ decode และแบบ chunk สำหรับการฝึก/long prefill โดยวิธี chunk จะกู้คืนการพึ่งพาภายใน token ด้วย triangular solve แล้วประกอบกลับด้วย matrix multiplication
สัญกรณ์และลำดับการอธิบาย
- ในสัญกรณ์ bra-ket, (\lvert q\rangle) คือเวกเตอร์คอลัมน์, (\langle k\rvert) คือเวกเตอร์แถว, (\langle k\vert q\rangle) คือสเกลาร์ และ (\lvert v\rangle\langle k\rvert) คือเมทริกซ์
- ใช้ causal attention head หนึ่งตัวและเวกเตอร์จำนวนจริง โดยสมมติว่า key ของ DeltaNet ถูก normalize แล้ว และสถานะแมปจาก key space ไปยัง value space
- ลำดับการอธิบายคือ softmax attention → linear attention → DeltaNet → Gated DeltaNet → KDA และปิดท้ายด้วยการเชื่อมไปยัง implementation แบบ recurrent และ chunk ใน Triton
- ในตระกูล DeltaNet มีสอง variant ที่ถูกใช้ใน โมเดลตระกูล Qwen และ Kimi รุ่นล่าสุด
จาก attention ความซับซ้อนกำลังสองสู่สถานะแบบเชิงเส้น
- causal softmax attention ทั่วไปคำนวณความคล้ายกันของ key กับ query, normalize คะแนนของ key ในอดีตทั้งหมดให้เป็น distribution แล้วส่งออก weighted sum ของ value vector
- สำหรับ sequence ความยาว (T) จะมีคู่ key-query จำนวน (T^2) คู่
- ใน autoregressive inference สามารถ cache key และ value ได้ แต่ขนาด cache จะเพิ่มตาม sequence
- query ใหม่ก็ยังต้องตรวจสอบอดีตทั้งหมด
- ตัวส่วนของ softmax ขึ้นกับ query ปัจจุบันและ key ก่อนหน้าทั้งหมดร่วมกัน จึงยากที่จะจัดลำดับการคำนวณใหม่อย่างง่าย ๆ
- หากเอา softmax ออก จะสามารถรวม output เป็นผลรวมของ outer product ของ key-value ในอดีตได้
- (S_t=\sum_{i\le t}\lvert v_i\rangle\langle k_i\rvert)
- (S_t=S_{t-1}+\lvert v_t\rangle\langle k_t\rvert)
- (\lvert o_t\rangle=S_t\lvert q_t\rangle)
- เอกลักษณ์สำคัญคือ ((\lvert v\rangle\langle k\rvert)\lvert q\rangle=\langle k\vert q\rangle\lvert v\rangle) โดยแทนที่จะเก็บ key และ value ในอดีตทั้งหมด จะเก็บ outer product ที่รวมแล้วไว้ใน สถานะขนาดคงที่ (d_v\times d_k)
- เพราะไล่ผ่าน token เพียงครั้งเดียว จึงทำงานเป็นเชิงเส้นตามความยาว sequence แต่แลกกับการสูญเสีย normalization และ selectivity ของ softmax
- linear attention ที่ซับซ้อนกว่านี้จะใช้ feature map และ term สำหรับ normalization
ปัญหา additive write ของ linear attention
- หลังจากบันทึก (\lvert v_t\rangle\langle k_t\rvert) ลงบน key ปัจจุบันที่ normalize แล้ว หากอ่านด้วย key เดิมทันที จะได้ (S_t\lvert k_t\rangle=S_{t-1}\lvert k_t\rangle+\lvert v_t\rangle)
- การเขียนใหม่ไม่ได้แทนที่ให้หน่วยความจำคืนค่า (v_t) แต่ บวกเพิ่มแบบ
+=เข้าไปในค่าที่คืนเดิม - หากสถานะก่อนหน้าคืนค่าที่ถูกต้องอยู่แล้ว value เดิมจะถูกเพิ่มเป็นสองเท่า และเพราะ key ไม่ได้ตั้งฉากกันทั้งหมด การเขียนแต่ละครั้งจึงอาจรบกวนการเขียนเดิมได้
- linear attention ให้หน่วยความจำเชิงสัมพันธ์แบบบีบอัด แต่ทำการอัปเดตแบบบวก แทนที่จะเป็นการอัปเดตที่ใกล้เคียง
=ตามที่ต้องการ
DeltaNet: เขียนข้อผิดพลาดการทำนายแทนค่า
- DeltaNet อ่านค่าทำนายเดิมสำหรับ key ใหม่ (\widehat v_t=S_{t-1}k_t) ก่อน แล้วบันทึกเฉพาะส่วนต่างแทน value ทั้งหมด
- (e_t=\beta_t(v_t-S_{t-1}k_t))
- (S_t=S_{t-1}+e_tk_t^\mathsf T)
- ความแรงในการเขียนที่เรียนรู้ได้ (\beta_t) อยู่ในช่วง ([0,1])
- หากอ่านซ้ำทันทีด้วย key เดิม จะได้ ((1-\beta_t)S_{t-1}k_t+\beta_tv_t)
- ถ้า (\beta_t=1) จะคืนค่า (v_t) อย่างแม่นยำ
- ค่าที่เล็กกว่าจะขยับการทำนายเดิมไปทางเป้าหมายเพียงบางส่วน
- การอัปเดตเป็นแบบ เฉพาะที่ใน key space
- ในทิศทาง query ที่ตั้งฉากกับ key ปัจจุบัน การอัปเดต outer product เป็น 0 ดังนั้น response จึงไม่เปลี่ยน
- แทนที่เฉพาะความสัมพันธ์ในทิศทาง key ปัจจุบันอย่างเลือกสรร
-
อนุมานจาก reconstruction loss
- มองสถานะ (S) เป็น linear map และกำหนด loss ของคู่ key-value ปัจจุบันเป็น (\frac12\lVert Sk_t-v_t\rVert_2^2) จะได้ gradient เป็น ((Sk_t-v_t)k_t^\mathsf T)
- หากทำ gradient descent หนึ่งขั้นจาก (S_{t-1}) ด้วยขนาด (\beta_t) จะได้สมการอัปเดตของ DeltaNet พอดี
- การอัปเดตเดียวกันตีความได้สามแบบ
- ใน memory operation, (\beta_t) คือ ความแรงในการแทนที่ความสัมพันธ์เดิม
- ใน online learning, (\beta_t) คือ learning rate
- ใน linear algebra คือ outer product แบบ rank-1 ของข้อผิดพลาดการทำนายกับ key
-
การเปลี่ยนสถานะแบบมีโครงสร้าง
- เมื่อคลี่การอัปเดต จะได้ (S_t=S_{t-1}(I-\beta_tk_tk_t^\mathsf T)+\beta_tv_tk_t^\mathsf T)
- สำหรับ key หน่วย (I-\beta_tk_tk_t^\mathsf T) มี eigenvalue (1-\beta_t) ในทิศทาง key ปัจจุบัน และ eigenvalue 1 ในทิศทางตั้งฉากทั้งหมด
- แม้จะลบความสัมพันธ์ในทิศทาง key เดิมก่อนแล้วเพิ่มความสัมพันธ์ใหม่ แต่ยังไม่ได้แก้ การจัดการอายุของสถานะทั้งหมด
Gated DeltaNet: ลืมสถานะทั้งหมดก่อน
- เมื่อบีบอัดอดีตทั้งหมดไว้ในเมทริกซ์เดียว จะไม่สามารถเลือกข้ามเฉพาะ token แต่ละตัวที่ถูกรวมเข้าไปในสถานะแล้วได้
- DeltaNet แก้ไขบริเวณรอบ key ปัจจุบัน แต่ข้อมูลเก่าในทิศทางอื่นยังคงอยู่และอาจมีส่วนต่อการอ่านในอนาคตต่อไป
- Gated DeltaNet ใช้ gate การคงไว้แบบสเกลาร์ที่เรียนรู้ได้ (\alpha_t\in[0,1])
- ลืมด้วย (\widetilde S_t=\alpha_tS_{t-1})
- ทำนายด้วย (\widehat v_t=\widetilde S_tk_t)
- แก้ไขด้วย (e_t=\beta_t(v_t-\widehat v_t))
- บันทึกด้วย (S_t=\widetilde S_t+e_tk_t^\mathsf T)
- ลำดับ ลืม → ทำนาย → แก้ไข → เขียน มีความสำคัญ
- หากทำนายก่อนการลดทอน หน่วยความจำที่ใช้คำนวณข้อผิดพลาดจะต่างจากหน่วยความจำที่อัปเดตจริง
- delta rule รับผิดชอบการแทนที่สำหรับ key เป้าหมาย ส่วน scalar gate รับผิดชอบการลบแบบ global จึงแก้ปัญหาคนละแบบ
- อย่างไรก็ตาม (\alpha_t) ค่าเดียวถูกใช้กับทั้งเมทริกซ์ จึงต้องคงหรือลืม key channel ทั้งหมดในอัตราเดียวกัน
Kimi Delta Attention: การลดทอนแยกตาม channel
- Kimi Delta Attention เปลี่ยนสเกลาร์ (\alpha_t) เป็นเวกเตอร์มิติ (d_k) และสร้าง (D_t=\operatorname{Diag}(\alpha_t))
- เพราะสถานะแมปจาก key space ไปยัง value space, key channel จึงสอดคล้องกับคอลัมน์ของ (S) และการคูณด้านขวา (S_{t-1}D_t) จะใช้อัตราการคงไว้ที่ต่างกันกับแต่ละคอลัมน์
- KDA ทำงานตามลำดับต่อไปนี้
- ลดทอนแยกตาม key channel ด้วย (\widetilde S_t=S_{t-1}D_t)
- ทำนายด้วย (\widehat v_t=\widetilde S_tk_t)
- แก้ไขด้วย (e_t=\beta_t(v_t-\widehat v_t))
- บันทึกด้วย (S_t=\widetilde S_t+e_tk_t^\mathsf T)
- อ่านด้วย (o_t=S_t(d_k^{-1/2}q_t))
- การเปลี่ยนแปลงเชิงแนวคิดจาก Gated DeltaNet ไปเป็น KDA มีเพียงการยกระดับ (\alpha_t) เป็น (D_t) แต่สามารถลบ channel หนึ่งในขณะที่ยังคงอีก channel ไว้ได้
-
การเปลี่ยนผ่านแบบ diagonal-low-rank
- เมื่อคลี่ KDA จะได้ (S_t=S_{t-1}A_t+\beta_tv_tk_t^\mathsf T) โดย (A_t=D_t(I-\beta_tk_tk_t^\mathsf T))
- เขียนได้เป็น (A_t=D_t-b_ta_t^\mathsf T), (b_t=D_tk_t), (a_t^\mathsf T=\beta_tk_t^\mathsf T) จึงเป็นการเปลี่ยนผ่านแบบ diagonal-low-rank(DPLR)
- DPLR หมายถึงการเปลี่ยนผ่านขนาด (d_k\times d_k) ที่ทำงานใน key space ส่วนสถานะหน่วยความจำเองยังคงเป็นเมทริกซ์ (d_v\times d_k)
- แต่ละตระกูลเพิ่มความสามารถดังนี้
- linear attention: recurrent memory ขนาดคงที่
- DeltaNet: การแทนที่อย่างเลือกสรรในทิศทางเป้าหมาย
- Gated DeltaNet: การลดทอนสถานะทั้งหมด
- KDA: การลดทอนแยกตาม key channel
- implementation มักเก็บ (g_t=\log\alpha_t\le0) แล้วหาอัตราการคงไว้ด้วย (\exp(g_t))
- ดู reference implementation 5 ขั้นตอนใน layout แบบ transpose ขนาด (d_k\times d_v) ได้ที่
naive_recurrent_kda
Fused recurrent Triton kernel สำหรับ decode
- KDA มีวิธี execution หลักสองแบบ
- fused recurrent: เหมาะกับ decode, sequence สั้น และการให้บริการแบบรักษาสถานะ
- chunk: เหมาะกับการฝึกและ long prefill
fused_recurrent_kda_fwdรัน Triton program หนึ่งตัวต่อ sequence, value head และ value tile กว้าง 32BKครอบคลุมมิติ key ใน configuration ที่รองรับทั่วไป- แต่ละ program เป็นเจ้าของ tile
[BK, BV]ของสถานะที่ transpose แล้ว และไล่ผ่าน token ตามลำดับ - value tile, head และ sequence ที่ต่างกันจะรันอย่างอิสระต่อกัน
- kernel ทำตาม recurrence โดยตรง: ลดทอนสถานะ, reduce การทำนายสำหรับ key, คำนวณ residual, เขียน outer product และ reduce การอ่านด้วย query
- เหมาะกับ decode ที่มี token ใหม่เข้ามาทีละตัว แต่ไม่สามารถแปลง vector operation ให้เป็น matrix multiplication ขนาดใหญ่ที่มีประสิทธิภาพกับ Tensor Core ได้ จึง เสียเปรียบสำหรับการฝึกและ long prefill
Chunkwise KDA: จัด recurrence ใหม่เป็น matrix multiplication
- Chunkwise KDA ต้องประมวลผล token (C) ตัวพร้อมกัน พร้อมสร้างสถานะและ output ที่เหมือนกับวิธี recurrent ทีละ token ทุกประการ
- แต่ละ chunk คำนวณผลลัพธ์สองอย่าง
- (S_{c+1}) หลังจากประมวลผลทั้ง chunk จากสถานะขาเข้า (S_c)
- causal output ของ token ทั้งหมดภายใน chunk
- ความยากหลักคือ delta error ของแต่ละ token ขึ้นกับการเขียนก่อนหน้าใน chunk เดียวกัน
-
การลดทอนสะสมและข้อผิดพลาดชั่วคราว
- ให้การลดทอนทแยงของ token (i) เป็น (D_i), และการลดทอนสะสมจากขอบเขต chunk ถึง token (i) เป็น (D_{0:i}=D_0D_1\cdots D_i)
- เมื่อการเขียนของ token (j) ถูกส่งต่อไปถึง token (i) จะใช้ (D_{j+1:i}) และเพราะเป็นเมทริกซ์ทแยง เมทริกซ์ลดทอนจึงสลับลำดับกันได้
- ขั้นแรกคำนวณข้อผิดพลาดชั่วคราวแบบขนาน โดยไม่สนใจการเขียนอื่นภายใน chunk
- (\bar e_i=\beta_i(v_i-S_cD_{0:i}k_i))
- ข้อผิดพลาดชั่วคราวยกเว้น token แรกยังขาดผลกระทบจากการเขียนก่อนหน้าใน chunk เดียวกัน จึงใช้ตามเดิมไม่ได้
-
กู้คืน causal dependency
- นิยามสัมประสิทธิ์ที่ token ก่อนหน้า (j) มีผลต่อข้อผิดพลาดของ token ปัจจุบัน (i) เป็น (\rho_{ij}=\beta_i k_j^\mathsf TD_{j+1:i}k_i)
- ข้อผิดพลาดจริงมี dependency แบบลำดับในรูป (e_i=\bar e_i-\sum_{j<i}\rho_{ij}e_j)
- เมื่อนำ (\rho_{ij}) ใส่ในเมทริกซ์ lower triangular แบบเข้มงวด (R_c) เมทริกซ์ข้อผิดพลาดที่ stack ไว้จะคำนวณได้เป็น (E_c=\bar E_c(A_c^{kk})^\mathsf T), (A_c^{kk}=(I+R_c)^{-1})
- ไม่จำเป็นต้องใช้ inverse ของเมทริกซ์หนาแน่นทั่วไป
- (I+R_c) เป็นเมทริกซ์สามเหลี่ยมที่มีสมาชิกแนวทแยงเป็น 1
- เพียงทำ causal triangular solve สำหรับแต่ละ value channel ก็พอ
-
การคำนวณสถานะเมื่อจบ chunk
- สถานะขาเข้าผ่านการลดทอนทั้งหมดของ chunk และการเขียนภายใน chunk แต่ละครั้งผ่านเฉพาะการลดทอนที่อยู่ถัดจากตัวเอง
- หาก stack key ที่ถูกลดทอนจนถึงท้าย chunk ไว้เป็นแถวใน (K_c^{\mathrm{end}}) จะจัดสถานะเป็น matrix multiplication ต่อไปนี้ได้
- (S_{c+1}=S_cD_{0:C-1}+E_cK_c^{\mathrm{end}})
- รวมการเขียน outer product แบบ rank-1 หลายครั้งเป็น matrix multiplication เดียว เพื่อ เดินหน้าสถานะทั้ง chunk ในครั้งเดียว
-
คำนวณ output ทั้งหมดภายใน chunk
- KDA เขียน token ปัจจุบันแล้วค่อยอ่าน ดังนั้น output ของ token (i) จึงรวมการเขียนของตัวเองด้วย
- นิยามสัมประสิทธิ์ที่การเขียนก่อนหน้า (j) มีผลต่อ query (i) เป็น (\chi_{ij}=s,k_j^\mathsf TD_{j+1:i}q_i), (j\le i)
- วางสัมประสิทธิ์ไว้ในเมทริกซ์อ่าน lower triangular (A_c^{qk})
- ศูนย์ใน upper triangular ตัดการมีส่วนร่วมของ token อนาคต
- สมาชิกแนวทแยงสะท้อนพฤติกรรมที่ token ปัจจุบันอ่านหลังจากเขียนตัวเองแล้ว
- หาก stack เวกเตอร์ที่ถูกลดทอนจากขอบเขตไปถึงแต่ละ query ไว้ใน (Q_c^{\mathrm{boundary}}) output ทั้งหมดจะเป็นดังนี้
- (O_c=sS_cQ_c^{\mathrm{boundary}}+E_c(A_c^{qk})^\mathsf T)
- matrix multiplication แรกอ่านสถานะตอนเข้า chunk ที่ถูกลดทอนแล้ว ส่วน multiplication ที่สองบวกการมีส่วนร่วมจากการเขียนแบบ causal ภายใน chunk
Chunkwise Triton pipeline
- implementation แบบ chunk ไม่ใช่ kernel ขนาดยักษ์ตัวเดียว แต่เป็น pipeline ที่ต่อเนื่องจากการเรียก kernel หลายครั้ง
- เริ่มจากคำนวณ cumulative log decay ภายใน chunk
- แทนค่า (D_{j+1:i}) ด้วยผลต่างของ prefix sum สองชุด โดยไม่ต้องคูณเวกเตอร์การคงไว้ยาว ๆ
- จากนั้นสร้างเมทริกซ์ interaction แบบ causal (A^{qk}) และ (A^{kk}) และใช้ (A^{kk}) สร้างรูปแบบ WY สำหรับการเขียนที่ถูกแก้ไขของ chunk
- state kernel ทำการไล่ข้าม chunk เพียงจุดเดียว
- สร้างสถานะขาเข้าของแต่ละ chunk
- resolve delta error ของ chunk
- หลังจากคำนวณสถานะขาเข้าแล้ว output kernel สามารถประมวลผล token ของ chunk และ tile ต่าง ๆ แบบขนานได้
- implementation จริงจะคำนวณ interaction block แนวทแยงขนาด 16 token ก่อน แล้วจึงรัน kernel แบบ fused สำหรับส่วนไม่อยู่บนแนวทแยงและ triangular solve
chunk_kda_fwdทำหน้าที่จัดลำดับขั้น โดย entry point หลักคือchunk_kda_fwd_intra,chunk_gated_delta_rule_fwd_h,chunk_gla_fwd_o_gkv_newในโค้ดคือข้อผิดพลาดที่ถูก resolve แล้วhคือสถานะขาเข้า chunkkgคือ key ที่ถูกลดทอนจนถึงท้าย chunk
- วิธี recurrent และวิธี chunk ไม่ใช่ attention คนละแบบ แต่เป็น execution schedule สองแบบของ recurrence KDA เดียวกัน
- วิธี recurrent คือ vector operation แบบ serial สำหรับ decode latency ต่ำ
- วิธี chunk คือ matrix operation สำหรับการฝึกและ prefill ที่เน้น Tensor Core
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ตลอด 15 ปีที่ผ่านมา วงการแมชชีนเลิร์นนิงจำเป็นต้องมี สัญกรณ์ทางคณิตศาสตร์ที่เป็นมาตรฐานเดียวกัน และน่าจะยังจำเป็นต่อไปด้วย สมัยก่อนยิ่งหนักกว่านี้ เพราะในเปเปอร์ของนักวิจัยจากทั่วโลกมีสัญกรณ์ประหลาด ๆ โผล่มาทุกแบบ
เมื่อสัญกรณ์เปลี่ยนไปในแต่ละเปเปอร์ ก็ทำให้เกิดแรงเสียดทานต่อความเข้าใจ อย่างน้อยบทความนี้ก็อธิบายสัญกรณ์อย่างชัดเจนตั้งแต่ต้น ซึ่งเปเปอร์ที่ทำแบบนี้มีไม่มาก ตอนแรกผมยังไม่ทันสังเกตฟีเจอร์สลับสัญกรณ์ด้วยซ้ำ แต่พบว่ามีประโยชน์มาก
∣q⟩แทนสัญลักษณ์ตัวอักษรเดียวหรือชนิดข้อมูลที่ระบุชัดเจน มันอาจมีข้อดีตรงความกระชับ แต่ถ้าเขียนสมการเป็น pseudocode หรือภาษาโปรแกรมจริงอย่าง Python น่าจะเข้าใจง่ายกว่ามากk,q,Sคืออะไร แต่ถ้าไม่มีพื้นฐานที่เกี่ยวข้อง เนื้อหาส่วนใหญ่ก็จะดูไม่ชัดเจนถึงจะบอกว่า “เป็นสิ่งที่อาจคิดขึ้นเองได้…” แต่การสร้างหรือผสมผสานอะไรบางอย่างที่ไม่เคยมีอยู่มาก่อนนั้น ยากมาก ๆ
พอมีใครทำงานยาก ๆ สำเร็จและเผยแพร่ออกมา ก็จะมีปฏิกิริยาแบบ “ก็ไม่ได้ยากเท่าไรนี่”, “ฉันก็ทำได้” ทันที และทุกอย่างก็เริ่มดูเรียบง่ายไปหมด เวลาเขียนโปรแกรมแล้วคิดว่าตัวเองประดิษฐ์สิ่งใหม่ได้ แต่ภายหลังพบว่ามันถูกสร้างและใช้กันแพร่หลายมาตั้งแต่ทศวรรษ 1970 แล้ว ก็เป็นเรื่องที่เกิดขึ้นได้บ่อย แค่เส้นทางของผมไม่เคยไปเจอมัน เลยไม่รู้ว่ามันมีอยู่
สำหรับผม สัญกรณ์ bra-ket ทำให้ทุกอย่างเรียบง่ายและเข้าใจได้ตามสัญชาตญาณ ในสัญกรณ์เวกเตอร์ ผมมักสับสนว่าด้านไหนเป็นแนวนอนหรือแนวตั้ง แล้วก็ได้แต่ไล่ตามก้อนสัญลักษณ์จนเสียสมาธิ แต่พอเป็น bra-ket แล้วภาพรวมทั้งหมดชัดเจนมาก
ผมคิดว่าจะลองแปลงบทความอื่น ๆ เป็นสัญกรณ์นี้ด้วย เพราะน่าจะพลาดบทความดี ๆ ไปหลายชิ้น อนึ่ง ผมเป็นดุษฎีบัณฑิตด้านฟิสิกส์และมีภาวะดิสเล็กเซียเล็กน้อย
พอเห็นสำนวนอย่าง “outer product เป็นเมทริกซ์ ส่วน inner product เป็นตัวเลข แทนที่จะเก็บคีย์และค่าทั้งหมดในอดีต เราเก็บผลรวมของ outer product ไว้ในสถานะขนาดคงที่
S_t” ก็ทำให้มั่นใจว่าเป็น บทความที่ LLM เขียน–) ก็จะได้ผลลัพธ์แบบนี้มีทิวทอเรียลแบบภาพประกอบด้วย: https://snowchord.com/blog/linear-attention-visualized/
ทุกครั้งที่เห็นบทความและชื่อเรื่องแบบนี้ ผมรู้สึก ซาบซึ้งและถ่อมตัวอย่างลึกซึ้ง ต่อผู้คนจำนวนมากที่ฉลาดกว่าผมมาก ๆ ตอนอยู่มัธยมและปริญญาตรี ผมถูกมองว่าเป็นคนฉลาดมาก และแม้จะฉลาดกว่าค่าเฉลี่ย แต่ก็แน่นอนว่ามีคนอีกเป็นล้าน ๆ ที่ทำให้ผมดูเหมือนมือใหม่ได้
ในที่นี้ ความฉลาดหมายถึงความสามารถในการเก็บแนวคิดและระบบที่ใหญ่และซับซ้อนไว้ในหัวแล้วใช้เหตุผลกับมัน ซึ่งดูเป็นพรสวรรค์ที่สำคัญเป็นพิเศษสำหรับนักคณิตศาสตร์
การทดลองทางความคิดที่เคยคุยกับเพื่อนตอนดื่มด้วยกันคือ แยกเด็ก ๆ ออกจากหน้าจอและคอนเทนต์มวลชนที่อัลกอริทึมป้อนให้ แล้วเลี้ยงดูพวกเขาในสภาพแวดล้อมที่เอื้อต่อการเรียนรู้ โดยควบคุมคุณภาพของสื่อและข้อมูลอย่างเข้มงวดเหมือนฝึกโมเดลระดับแนวหน้า ทำให้เหมือนอารามสำหรับเด็ก และสอนความรู้ล่าสุดเกี่ยวกับความเป็นจริงผ่านคณิตศาสตร์ วิศวกรรม วิทยาการคอมพิวเตอร์ ดีปเลิร์นนิง และอื่น ๆ
ท้ายที่สุดแล้ว หากจะใช้เครื่องมือ AI ขั้นสูงเพื่อขยายพรมแดนความรู้ ก็ยังต้องมีคนที่ฉลาดมากและความคิดยังไม่ถูกปนเปื้อนมากเกินไป แนวคิดที่ว่า AI จะมาแทนมนุษย์ได้ทั้งหมดนั้นไปผิดทาง
อนึ่ง ชื่อ สัญกรณ์ bra-ket มาจากคำว่า bracket (วงเล็บ) จริง ๆ
https://en.wikipedia.org/wiki/Bra-ket_notation
ตอนแรกผมลังเล แต่ สัญกรณ์ ket ทำให้การดำเนินการชัดเจนขึ้นมาก เลยชอบ อย่างไรก็ตาม น่าจะดีถ้ามีการทบทวนสั้น ๆ เกี่ยวกับตัวแปรบางตัวด้วย เช่น
d_kใน quadratic attentionตอนแรกผมท้อใจที่ตัวเองนึกวิธีแก้นี้ไม่ออก แต่พอรู้ตัวว่าแม้แต่การเขียน binary search เองด้วย JavaScript ก็ยังลำบาก ผมก็สบายใจทันที โอกาสที่ผมจะคิด Kimi Delta Attention ขึ้นเองนั้นไม่มีเลย
ลูปก็มักไม่ลึกเกินสองหรือสามชั้น และถ้าซับซ้อนกว่านั้น ยังไงก็ส่งต่อให้ไลบรารีจัดการจะดีกว่า