3 คะแนน โดย GN⁺ 3 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เริ่มจาก 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 → DeltaNetGated DeltaNetKDA และปิดท้ายด้วยการเชื่อมไปยัง 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])
    1. ลืมด้วย (\widetilde S_t=\alpha_tS_{t-1})
    2. ทำนายด้วย (\widehat v_t=\widetilde S_tk_t)
    3. แก้ไขด้วย (e_t=\beta_t(v_t-\widehat v_t))
    4. บันทึกด้วย (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 ทำงานตามลำดับต่อไปนี้
    1. ลดทอนแยกตาม key channel ด้วย (\widetilde S_t=S_{t-1}D_t)
    2. ทำนายด้วย (\widehat v_t=\widetilde S_tk_t)
    3. แก้ไขด้วย (e_t=\beta_t(v_t-\widehat v_t))
    4. บันทึกด้วย (S_t=\widetilde S_t+e_tk_t^\mathsf T)
    5. อ่านด้วย (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 กว้าง 32
    • BK ครอบคลุมมิติ 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_gk
    • v_new ในโค้ดคือข้อผิดพลาดที่ถูก resolve แล้ว
    • h คือสถานะขาเข้า chunk
    • kg คือ key ที่ถูกลดทอนจนถึงท้าย chunk
  • วิธี recurrent และวิธี chunk ไม่ใช่ attention คนละแบบ แต่เป็น execution schedule สองแบบของ recurrence KDA เดียวกัน
    • วิธี recurrent คือ vector operation แบบ serial สำหรับ decode latency ต่ำ
    • วิธี chunk คือ matrix operation สำหรับการฝึกและ prefill ที่เน้น Tensor Core

1 ความคิดเห็น

 
GN⁺ 3 시간 전
ความคิดเห็นจาก Hacker News
  • ตลอด 15 ปีที่ผ่านมา วงการแมชชีนเลิร์นนิงจำเป็นต้องมี สัญกรณ์ทางคณิตศาสตร์ที่เป็นมาตรฐานเดียวกัน และน่าจะยังจำเป็นต่อไปด้วย สมัยก่อนยิ่งหนักกว่านี้ เพราะในเปเปอร์ของนักวิจัยจากทั่วโลกมีสัญกรณ์ประหลาด ๆ โผล่มาทุกแบบ
    เมื่อสัญกรณ์เปลี่ยนไปในแต่ละเปเปอร์ ก็ทำให้เกิดแรงเสียดทานต่อความเข้าใจ อย่างน้อยบทความนี้ก็อธิบายสัญกรณ์อย่างชัดเจนตั้งแต่ต้น ซึ่งเปเปอร์ที่ทำแบบนี้มีไม่มาก ตอนแรกผมยังไม่ทันสังเกตฟีเจอร์สลับสัญกรณ์ด้วยซ้ำ แต่พบว่ามีประโยชน์มาก

    • ผมไม่เข้าใจเหตุผลที่ชอบ สัญกรณ์คณิตศาสตร์แบบดั้งเดิม ที่ใช้สัญลักษณ์อย่าง ∣q⟩ แทนสัญลักษณ์ตัวอักษรเดียวหรือชนิดข้อมูลที่ระบุชัดเจน มันอาจมีข้อดีตรงความกระชับ แต่ถ้าเขียนสมการเป็น pseudocode หรือภาษาโปรแกรมจริงอย่าง Python น่าจะเข้าใจง่ายกว่ามาก
    • บทความนี้อธิบายเพียงด้านหนึ่งของสัญกรณ์เท่านั้น แต่ไม่ได้ให้ นิยามของตัวแปร ที่ใช้ หากเคยเรียนแมชชีนเลิร์นนิงมาก็คงรู้หรือเดาได้ว่า k, q, S คืออะไร แต่ถ้าไม่มีพื้นฐานที่เกี่ยวข้อง เนื้อหาส่วนใหญ่ก็จะดูไม่ชัดเจน
    • เมื่อก่อนผมก็คิดแบบนั้น แต่เพราะใช้เวลาจ้องสมการมากกว่าโค้ดเยอะมาก พอรู้ความหมายของสัญลักษณ์แล้ว สัญกรณ์ที่กระชับ อ่านง่ายกว่ามาก ถ้าเขียนเป็นคำยังช่วยเลี่ยงปัญหาการตั้งชื่อที่ขึ้นชื่อว่ายากได้ด้วย
  • ถึงจะบอกว่า “เป็นสิ่งที่อาจคิดขึ้นเองได้…” แต่การสร้างหรือผสมผสานอะไรบางอย่างที่ไม่เคยมีอยู่มาก่อนนั้น ยากมาก ๆ
    พอมีใครทำงานยาก ๆ สำเร็จและเผยแพร่ออกมา ก็จะมีปฏิกิริยาแบบ “ก็ไม่ได้ยากเท่าไรนี่”, “ฉันก็ทำได้” ทันที และทุกอย่างก็เริ่มดูเรียบง่ายไปหมด เวลาเขียนโปรแกรมแล้วคิดว่าตัวเองประดิษฐ์สิ่งใหม่ได้ แต่ภายหลังพบว่ามันถูกสร้างและใช้กันแพร่หลายมาตั้งแต่ทศวรรษ 1970 แล้ว ก็เป็นเรื่องที่เกิดขึ้นได้บ่อย แค่เส้นทางของผมไม่เคยไปเจอมัน เลยไม่รู้ว่ามันมีอยู่

  • สำหรับผม สัญกรณ์ bra-ket ทำให้ทุกอย่างเรียบง่ายและเข้าใจได้ตามสัญชาตญาณ ในสัญกรณ์เวกเตอร์ ผมมักสับสนว่าด้านไหนเป็นแนวนอนหรือแนวตั้ง แล้วก็ได้แต่ไล่ตามก้อนสัญลักษณ์จนเสียสมาธิ แต่พอเป็น bra-ket แล้วภาพรวมทั้งหมดชัดเจนมาก
    ผมคิดว่าจะลองแปลงบทความอื่น ๆ เป็นสัญกรณ์นี้ด้วย เพราะน่าจะพลาดบทความดี ๆ ไปหลายชิ้น อนึ่ง ผมเป็นดุษฎีบัณฑิตด้านฟิสิกส์และมีภาวะดิสเล็กเซียเล็กน้อย

  • พอเห็นสำนวนอย่าง “outer product เป็นเมทริกซ์ ส่วน inner product เป็นตัวเลข แทนที่จะเก็บคีย์และค่าทั้งหมดในอดีต เราเก็บผลรวมของ outer product ไว้ในสถานะขนาดคงที่ S_t” ก็ทำให้มั่นใจว่าเป็น บทความที่ LLM เขียน

    • น่าจะเริ่มจากการขอให้ตั้งชื่อที่มีคำฮิตติดกระแสก่อน
    • ถ้าพรอมป์ Claude ว่าอย่าใช้ขีด dash () ก็จะได้ผลลัพธ์แบบนี้
  • มีทิวทอเรียลแบบภาพประกอบด้วย: https://snowchord.com/blog/linear-attention-visualized/

  • ทุกครั้งที่เห็นบทความและชื่อเรื่องแบบนี้ ผมรู้สึก ซาบซึ้งและถ่อมตัวอย่างลึกซึ้ง ต่อผู้คนจำนวนมากที่ฉลาดกว่าผมมาก ๆ ตอนอยู่มัธยมและปริญญาตรี ผมถูกมองว่าเป็นคนฉลาดมาก และแม้จะฉลาดกว่าค่าเฉลี่ย แต่ก็แน่นอนว่ามีคนอีกเป็นล้าน ๆ ที่ทำให้ผมดูเหมือนมือใหม่ได้
    ในที่นี้ ความฉลาดหมายถึงความสามารถในการเก็บแนวคิดและระบบที่ใหญ่และซับซ้อนไว้ในหัวแล้วใช้เหตุผลกับมัน ซึ่งดูเป็นพรสวรรค์ที่สำคัญเป็นพิเศษสำหรับนักคณิตศาสตร์

    • แม้เครื่องมือ AI จะเร่งงานให้เร็วขึ้นเรื่อย ๆ แต่ผมคิดว่า แหล่งที่มาของไอเดียใหม่ส่วนใหญ่ จะยังคงเป็นมนุษย์ต่อไป
      การทดลองทางความคิดที่เคยคุยกับเพื่อนตอนดื่มด้วยกันคือ แยกเด็ก ๆ ออกจากหน้าจอและคอนเทนต์มวลชนที่อัลกอริทึมป้อนให้ แล้วเลี้ยงดูพวกเขาในสภาพแวดล้อมที่เอื้อต่อการเรียนรู้ โดยควบคุมคุณภาพของสื่อและข้อมูลอย่างเข้มงวดเหมือนฝึกโมเดลระดับแนวหน้า ทำให้เหมือนอารามสำหรับเด็ก และสอนความรู้ล่าสุดเกี่ยวกับความเป็นจริงผ่านคณิตศาสตร์ วิศวกรรม วิทยาการคอมพิวเตอร์ ดีปเลิร์นนิง และอื่น ๆ
      ท้ายที่สุดแล้ว หากจะใช้เครื่องมือ AI ขั้นสูงเพื่อขยายพรมแดนความรู้ ก็ยังต้องมีคนที่ฉลาดมากและความคิดยังไม่ถูกปนเปื้อนมากเกินไป แนวคิดที่ว่า AI จะมาแทนมนุษย์ได้ทั้งหมดนั้นไปผิดทาง
  • อนึ่ง ชื่อ สัญกรณ์ bra-ket มาจากคำว่า bracket (วงเล็บ) จริง ๆ
    https://en.wikipedia.org/wiki/Bra-ket_notation

  • ตอนแรกผมลังเล แต่ สัญกรณ์ ket ทำให้การดำเนินการชัดเจนขึ้นมาก เลยชอบ อย่างไรก็ตาม น่าจะดีถ้ามีการทบทวนสั้น ๆ เกี่ยวกับตัวแปรบางตัวด้วย เช่น d_k ใน quadratic attention

  • ตอนแรกผมท้อใจที่ตัวเองนึกวิธีแก้นี้ไม่ออก แต่พอรู้ตัวว่าแม้แต่การเขียน binary search เองด้วย JavaScript ก็ยังลำบาก ผมก็สบายใจทันที โอกาสที่ผมจะคิด Kimi Delta Attention ขึ้นเองนั้นไม่มีเลย

    • โค้ดพีชคณิตเชิงเส้นมีด้านที่เขียนง่ายกว่าที่คิด ไม่ได้มี recursion พันกันซับซ้อนเหมือนโค้ดวิทยาการคอมพิวเตอร์ทั่วไป ตัวแปรทั้งหมดมีความสัมพันธ์ทางคณิตศาสตร์ต่อกัน และแนวคิดคณิตศาสตร์ที่พบบ่อยก็มีไลบรารีที่ทำไว้ดีแล้วให้ใช้
      ลูปก็มักไม่ลึกเกินสองหรือสามชั้น และถ้าซับซ้อนกว่านั้น ยังไงก็ส่งต่อให้ไลบรารีจัดการจะดีกว่า