โมเดลขนาดใหญ่อย่าง Autoregressive เช่น GPT-4 และ Claude จะสร้างโค้ดออกมาทีละ token โดยไล่จากซ้ายไปขวาตลอดทั้งไฟล์ พวกมันจัดการกับโค้ดสั้นๆ ได้ดี แต่จะเริ่มติดขัดเมื่อนักพัฒนาต้องการแก้ไขบรรทัดที่ฝังลึกอยู่ใน codebase ขนาดใหญ่ เนื่องจากโมเดลต้องประเมิน token ทั้งหมดที่ตามมาใหม่ทั้งหมด ทำให้การรักษาความสอดคล้องของทั้งไฟล์กลายเป็นเรื่องที่ยากลำบาก

ในขณะที่ Diffusion models จะเริ่มต้นจากกลุ่มของ token แบบสุ่ม และค่อยๆ ทำการลดสัญญาณรบกวน (denoise) ซ้ำๆ จนกระทั่งโปรแกรมที่สมบูรณ์ปรากฏขึ้น เนื่องจากการปรับปรุงนั้นครอบคลุมทั้งลำดับข้อมูลในคราวเดียว โมเดลจึงสามารถแทรก ลบ หรือเขียนโค้ดส่วนใดก็ได้ใหม่ โดยไม่ต้องคำนวณส่วนที่เหลือใหม่ทั้งหมด

ทำไมกระบวนทัศน์ (paradigm) ในปัจจุบันถึงประสบปัญหาในการวิศวกรรมซอฟต์แวร์

Autoregression บังคับให้โมเดลมองโค้ดเป็นลำดับเส้นตรง ซึ่งหมายความว่ามัน:

  • มองย้อนกลับไปข้างหลังเท่านั้น มันไม่เคยเห็น token ในอนาคต จึงไม่สามารถคาดการณ์ได้ว่าการเปลี่ยนแปลงจะส่งผลต่อบรรทัดถัดๆ ไปอย่างไร
  • ต้องคำนวณข้อความส่วนที่ตามมาใหม่ การแก้ไขเพียงจุดเดียวจะกระตุ้นให้เกิดการทำนายใหม่เป็นทอดๆ ซึ่งเพิ่มความหน่วง (latency) ในการทำ refactoring หรือการแก้บั๊ก
  • สะสมข้อผิดพลาดในระยะยาว ความผิดพลาดในช่วงแรกจะขยายตัวเหมือนลูกบอลหิมะ (snowball) ทำให้ไฟล์สุดท้ายมีไวยากรณ์ที่ผิดพลาดหรือขาดความสอดคล้องทางตรรกะ

เมื่อคุณต้องการทำ infill เช่น การแทรกเนื้อหาฟังก์ชันไว้กลางไฟล์ หรือการอัปเดต API signature ลักษณะการทำงานแบบลำดับของโมเดลแบบ autoregressive จะให้ความรู้สึกที่เทอะทะและเกิดข้อผิดพลาดได้ง่าย

ทางเลือกแบบ Diffusion: การปรับปรุงแบบวนซ้ำ ไม่ใช่การทำนายทีละขั้นตอน

เรามองไฟล์โค้ดเป็นสัญญาณที่มีสัญญาณรบกวน (noisy signal) โดยการสร้างจะดำเนินไปในหลายรอบดังนี้:

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

เนื่องจากการทำงานในแต่ละขั้นตอนจะกลับไปตรวจสอบทั้งลำดับข้อมูล โมเดลจึงสามารถปรับแต่ง token ใดก็ได้ในทุกขั้นตอน มุมมองแบบองค์รวม (global view) นี้ช่วยให้มันสามารถเพิ่ม import ที่ขาดหายไป, เปลี่ยนชื่อตัวแปร หรือจัดย่อหน้า (re-indent) ของบล็อกโค้ดใหม่ได้ โดยไม่ต้องสร้างส่วนที่เหลือใหม่ทั้งหมด

การออกแบบโมเดล Diffusion ที่เน้นโค้ดเป็นหลัก

การนำ diffusion จากรูปภาพมาใช้กับข้อความไม่ใช่เรื่องที่ทำได้ทันที (plug-and-play) มีการเปลี่ยนแปลงทางเทคนิค 3 ประการที่สำคัญ

1. Discrete diffusion

พิกเซลของรูปภาพสามารถรับสัญญาณรบกวนที่เป็นเศษส่วนได้ แต่ token ของโค้ดนั้นเป็นแบบหมวดหมู่ (categorical) ซึ่งอาจเป็น keyword, identifier หรือสัญลักษณ์ที่เฉพาะเจาะจง ดังนั้นเราจึงใช้ Markov chain ที่แทนที่ token ด้วย placeholder ที่เป็นกลาง (มักจะเป็น [MASK]) ซ้ำๆ จนกระทั่งลำดับข้อมูลกลายเป็นแบบสุ่มอย่างแท้จริง กระบวนการย้อนกลับจะเรียนรู้วิธีการกู้คืน token ดั้งเดิมกลับมาทีละขั้นตอน

2. Structural awareness

ภาษาโปรแกรมมีกฎทางไวยากรณ์ที่เข้มงวด เช่น การย่อหน้า (indentation) กำหนดขอบเขต (scope) ใน Python, การใช้ปีกกาเพื่อแบ่งบล็อกในภาษาตระกูล C และตัวแปรต้องถูกประกาศก่อนใช้งาน โมเดล Diffusion ที่มองโค้ดเป็นเพียงข้อความธรรมดาจะสร้างผลลัพธ์ที่ผิดไวยากรณ์ เพื่อป้องกันปัญหานี้ นักวิจัยจึงได้เพิ่ม syntax-aware attention masks เพื่อจำกัดวิธีการที่ token แต่ละตัวจะเชื่อมโยงกัน (attend to each other) ซึ่งเป็นการบังคับให้โมเดลเคารพลำดับชั้น (hierarchy), การซ้อนกัน (nesting) และข้อจำกัดเฉพาะของแต่ละภาษา

3. Hierarchical refinement

ขั้นตอนแรกๆ ของ diffusion จะเป็นการร่างโครงสร้างคร่าวๆ เช่น function signatures, การนิยาม class และการจัดระเบียบ module ส่วนขั้นตอนหลังๆ จะเป็นการขัดเกลารายละเอียดเล็กน้อย เช่น เครื่องหมายวรรคตอน, ช่องว่าง (whitespace) และรูปแบบการตั้งชื่อ (naming conventions) กลยุทธ์แบบ "จากหยาบไปละเอียด" (coarse-to-fine) นี้เลียนแบบวิธีการที่มนุษย์ร่างโครงสร้างโปรแกรมก่อนที่จะขัดเกลาเนื้อหาภายใน และยังช่วยจัดสรรทรัพยากรการคำนวณไปยังจุดที่สำคัญที่สุดในแต่ละขั้นตอน

เปรียบเทียบ Autoregressive กับ Diffusion แบบเคียงข้างกัน

หัวข้อ Autoregressive Diffusion
รูปแบบการสร้าง แบบลำดับ, จากซ้ายไปขวา แบบขนาน, การลดสัญญาณรบกวนแบบวนซ้ำ
ความสอดคล้องในภาพรวม มีแนวโน้มที่จะคลาดเคลื่อนเมื่อทำงานระยะยาว มองเห็นภาพรวมของทุก token
กรณีการใช้งานทั่วไป แชท, การอธิบาย, โค้ดสั้นๆ การทำ refactoring, การแก้บั๊ก, การสังเคราะห์โค้ดขนาดใหญ่

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

สิ่งที่ควรติดตามต่อไป

  • Hybrid pipelines. ระบบในอนาคตอาจอนุญาตให้โมเดลแบบ autoregressive ร่างเวอร์ชันแรกอย่างรวดเร็ว จากนั้นจึงส่งต่อให้โมเดลแบบ diffusion เพื่อทำการขัดเกลา
  • Tooling for structural masks. นักวิจัยยังคงพัฒนา syntax-aware attention masks อย่างต่อเนื่อง เพื่อช่วยให้โมเดลแบบ diffusion สามารถปฏิบัติตามข้อกำหนดเฉพาะของภาษาได้

บทสรุป

Diffusion models กำลังพลิกโฉมการสร้างโค้ด: แทนที่จะไล่ไปทีละโทเคน พวกมันกลับปั้นโปรแกรมทั้งโปรแกรมขึ้นมาผ่านกระบวนการ iterative denoising แนวทางนี้ช่วยแก้ปัญหาจุดอ่อนสำคัญของระบบแบบ autoregressive นั่นคือการรักษาความสอดคล้องในระดับภาพรวม (global consistency) ในฐานโค้ดขนาดใหญ่ที่สามารถเปลี่ยนแปลงได้ แม้ว่าจะทำงานช้ากว่าและใช้ทรัพยากรการคำนวณสูงกว่า แต่ diffusion ก็เป็นเครื่องมือที่ทรงพลังสำหรับการ refactoring, การแก้ไขบั๊ก และสถานการณ์ใดก็ตามที่ผลลัพธ์ที่สะอาดและถูกต้องตามไวยากรณ์มีความสำคัญมากกว่าความเร็วเพียงอย่างเดียว แนวทางที่มีอนาคตไกลที่สุดดูเหมือนจะเป็นเวิร์กโฟลว์แบบไฮบริดที่จับคู่ความสามารถในการร่างโค้ดอย่างรวดเร็วของ autoregression เข้ากับความสามารถในการขัดเกลาแบบองค์รวมของ diffusion