1 คะแนน โดย GN⁺ 2023-07-23 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • llama.cpp PR #1773 เสนอฟีเจอร์ที่เพิ่ม API สำหรับจำกัดตัวเลือกการสุ่มตัวอย่างด้วย ไวยากรณ์แบบไม่ขึ้นกับบริบท เพื่อให้ผลลัพธ์ที่สร้างไม่หลุดออกจากรูปแบบที่กำหนด
  • API รับโครงสร้างข้อมูลไวยากรณ์ที่อิงกับโค้ดพอยต์ 32 บิต ใช้ llama_sample_grammar กรองโทเค็นตัวเลือก จากนั้นใช้ llama_grammar_accept_token สะท้อนโทเค็นที่เลือกกลับเข้าไปยังสถานะของไวยากรณ์
  • ใน main มีการเพิ่มอาร์กิวเมนต์ --grammar และ --grammar-file เพื่อรับไวยากรณ์ในรูปแบบ BNF แบบขยาย โดยมีตัวอย่างอย่าง Chess, นิพจน์เลขคณิต, JSON และการสร้างช่วงอักขระภาษาญี่ปุ่น
  • การทดสอบทำบน M2 Max และโมเดล 30B Q4_0 โดยเมื่อใช้ไวยากรณ์ เอาต์พุตจะถูกจำกัดให้อยู่ในรูปแบบบันทึกหมากรุก นิพจน์เลขคณิต JSON และช่วงอักขระภาษาญี่ปุ่น ขณะที่หากไม่มีไวยากรณ์จะสร้างข้อความทั่วไปหรือโค้ดที่ไม่ตรงกับพรอมป์ต์
  • ในการอภิปราย มีการพูดถึง ข้อจำกัดในการใช้งานจริง อย่างเข้มข้น เช่น การรองรับไฟล์ไวยากรณ์ การจัดการบรรทัดว่างและคอมเมนต์ การจะรวมพาร์เซอร์ไว้ใน API หรือไม่ การมาสก์ logit บน GPU การประมวลผลโทเค็นคงที่แบบแบตช์ และโอเวอร์เฮดด้านประสิทธิภาพ

การเปลี่ยนแปลงหลักของ PR

  • เป็น PR ที่เพิ่ม การสุ่มตัวอย่างแบบอิงไวยากรณ์ ให้กับ llama.cpp
  • อ้างอิงงานก่อนหน้าอย่าง #1397 และ งาน CFG ของ grantslatton
  • API ใหม่รับ ไวยากรณ์แบบไม่ขึ้นกับบริบท ที่ซีเรียลไลซ์แล้ว เพื่อชี้นำและจำกัดการสุ่มตัวอย่าง
  • ใน main มีการเพิ่มตัวอย่างไวยากรณ์คล้าย BNF สำหรับกำหนดไวยากรณ์ของผลลัพธ์ที่สร้าง

โครงสร้างของ Grammar API

  • API ของ llama รับโครงสร้างข้อมูลไวยากรณ์แบบไม่ขึ้นกับบริบทสำหรับโค้ดพอยต์ 32 บิต
  • ประเภทขององค์ประกอบไวยากรณ์ใช้แทนการสิ้นสุดกฎ การเริ่มทางเลือก การอ้างอิงกฎ อักขระ ค่าสูงสุดของช่วงอักขระ และการเพิ่มอักขระทางเลือก
  • ฟังก์ชันเริ่มต้นรับข้อมูลดังต่อไปนี้
    • อาร์เรย์ของกฎ
    • จำนวนกฎ
    • ดัชนีกฎเริ่มต้น
  • llama_grammar_element มี type และ value โดย value ใช้เป็น Unicode code point หรือ rule ID

วิธีการสุ่มตัวอย่าง

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

ความสามารถรับอินพุตไวยากรณ์ใน main

  • มีการเพิ่มอาร์กิวเมนต์ --grammar และ --grammar-file ใน main
  • ทั้งสองอาร์กิวเมนต์รับไวยากรณ์แบบ BNF แบบขยาย อย่างง่ายเพื่อจำกัดผลลัพธ์ที่สร้าง
  • ตัวพาร์เซอร์ไวยากรณ์ถูกติดตั้งไว้ใน examples/grammar-parser.{h,cpp}
  • ความสามารถของไวยากรณ์ที่รองรับ ได้แก่ ช่วงอักขระ การจัดกลุ่ม และตัวดำเนินการทำซ้ำ
  • กฎ root ใช้ระบุจุดเริ่มต้นของไวยากรณ์
  • ในอัปเดตถัดมา มีการเพิ่มการรองรับไฟล์ไวยากรณ์ ตัวอย่างเพิ่มเติม คอมเมนต์สไตล์เชลล์ บรรทัดว่างระหว่างกฎ และการอนุญาตขึ้นบรรทัดใหม่ภายในกลุ่มวงเล็บ

ตัวอย่างการทดสอบ

  • สภาพแวดล้อมการทดสอบระบุว่าใช้โมเดล M2 Max, 30B
  • ตัวอย่าง Chess
    • เมื่อใช้ --grammar-file grammars/chess.gbnf จะสร้างรูปแบบบันทึกหมากรุกอย่าง 1. e4 e5, 2. Nf3 Nc6
    • หากรันพรอมป์ต์เดียวกันโดยไม่มีไวยากรณ์ จะได้ประโยคทั่วไปเกี่ยวกับ Sir Thomas Gresham
  • ตัวอย่างนิพจน์เลขคณิต
    • ใช้ไวยากรณ์แบบอินไลน์เพื่อบังคับรูปแบบ expr "=" ws num "\n"
    • ผลลัพธ์จะถูกจำกัดให้อยู่ในรูปแบบนิพจน์เลขคณิตอย่าง 10 *a*1 +b*2 =640
    • หากไม่มีไวยากรณ์ จะได้ชิ้นส่วนโค้ด Go
  • ตัวอย่าง JSON
    • เมื่อใช้ grammars/json.gbnf จะสร้างโครงสร้าง JSON ในรูปแบบ { "fullName": ..., "address": ... }
    • หากไม่มีไวยากรณ์ จะได้ร้อยแก้วลักษณะแนะนำตัว
  • ตัวอย่างภาษาญี่ปุ่น
    • grammars/japanese.gbnf อนุญาตฮิรางานะ คาตากานะ เครื่องหมายวรรคตอน และช่วง CJK
    • เมื่อใช้ไวยากรณ์ จะสร้างรายการที่อิงอักขระภาษาญี่ปุ่น
    • หากไม่มีไวยากรณ์ จะสร้างรายการขั้นตอนเป็นภาษาอังกฤษ

การรีวิวและการอภิปรายด้านการออกแบบ

  • มีข้อเสนอให้รับไวยากรณ์จากไฟล์ และภายหลังก็มีการเพิ่มการรองรับ grammar file พร้อมตัวอย่าง
  • ปัญหาที่พบระหว่างการใช้งานช่วงแรก ได้แก่ การชนกับ --prompt-cache และการแครชจากบรรทัดว่างในไวยากรณ์
  • การรองรับบรรทัดว่างและคอมเมนต์ รวมถึงการอนุญาตขึ้นบรรทัดใหม่ในกลุ่มวงเล็บ ถูกเพิ่มในคอมมิตถัดมา
  • มีการถกเถียงว่าจะรวมพาร์เซอร์เข้าไปใน API ของ llama.cpp หรือไม่
    • หากพาร์เซอร์อยู่นอก API ผู้ใช้ downstream อาจต้องคัดลอกพาร์เซอร์ไปเองเพื่อรองรับฟีเจอร์นี้
    • ผู้เขียนพยายามลดการเปลี่ยนแปลงใน llama.cpp เอง แต่ก็เห็นด้วยว่าการใส่พาร์เซอร์ไว้ใน API จะสะดวกกว่า
  • มีข้อเสนอว่า หาก llama_grammar เก็บสำเนาไวยากรณ์แบบไบนารีไว้ ผู้ใช้ก็ไม่จำเป็นต้องคงอายุของสำเนาที่ส่งเข้ามาเอง

การอภิปรายเรื่องประสิทธิภาพและการเพิ่มประสิทธิภาพ

  • ปัจจุบันเข้าใจกันว่า แม้โทเค็นถัดไปจำนวน N > 1 จะถูกกำหนดอย่างเป็นเอกลักษณ์ด้วยไวยากรณ์ ระบบก็ยังสุ่มตัวอย่างทีละโทเค็น
  • มีการอภิปรายว่า หาก ประเมินแบบแบตช์ สำหรับโทเค็นคงที่หลายตัวพร้อมกัน อาจเพิ่มความเร็วการอนุมานได้มากในกรณีเช่นนี้
  • ผู้เขียนตอบว่ายังต้องประเมินโทเค็นอยู่ และคอขวดดูเหมือนจะอยู่ที่ฝั่งการประเมิน โดยมองว่าการประเมินแบบแบตช์ในระดับสตริงอาจเป็นจุดสำหรับการเพิ่มประสิทธิภาพ
  • ยังมีการเสนอแนวคิดให้คอมไพล์ไวยากรณ์เป็นเทนเซอร์การเปลี่ยนสถานะบน GPU หรือทำการมาสก์ logit ด้วยไวยากรณ์บน GPU สำหรับชุดโทเค็นทั้งหมด
  • torch-grammar ถูกกล่าวถึงว่าเป็นแนวทางคล้ายกันที่ทำการมาสก์ logit เพื่อบังคับไวยากรณ์บน GPU สำหรับชุดโทเค็นทั้งหมด

ค่าที่สังเกตได้ด้านประสิทธิภาพ

  • ผู้เขียนระบุว่าได้ทดลองเฉพาะการอนุมานบน CPU และในขอบเขตที่ลอง ผลกระทบด้านประสิทธิภาพไม่ได้มากนัก
  • ค่าที่สังเกตบน M2 Max
    • การสุ่มตัวอย่างแบบไม่จำกัดอยู่ที่ประมาณ 0.5ms/token
    • การสุ่มตัวอย่างเมื่อใช้ไวยากรณ์อยู่ที่ประมาณ 6ms/token
    • การประเมินโทเค็นของ 13B Q4_K อยู่ที่ประมาณ 70ms/token
  • ผู้ใช้อีกคนรายงานกรณีที่ 13B ลดลงจากประมาณ 20T/s เหลือ 13T/s
  • ผู้เขียนมองว่าโดยทั่วไปโอเวอร์เฮดของไวยากรณ์อยู่ที่ราว 5ms/token แต่บางไวยากรณ์มีผลกระทบมากกว่านั้น และอาจมีกรณีผิดปกติที่รุนแรงได้

การอภิปรายส่วนขยายที่เกี่ยวข้อง

  • มีการกล่าวถึงแนวทางนำอินพุต JSON Schema มาแปลงเป็น BNF เพื่อใช้งาน
  • ผู้เขียนระบุว่ามีโลคัลแบรนช์ที่ทำงานได้ตรงกับตัวอย่างใน README ของ jsonformer และมีสคริปต์ Python ที่สร้าง JSON BNF ให้ตรงกับ JSON Schema
  • ต่อมามีการกล่าวถึง examples : generate JSON according to schema #1887 ว่าเชื่อมโยงกับ PR นี้
  • มีคำตอบว่าแนวทางนี้เป็นอิสระจากการดัดแปลงโมเดล และสามารถใช้ร่วมกับโมเดลที่ fine-tune แล้วได้
  • ยังมีการกล่าวถึง PR แยกต่างหากที่เพิ่มการสุ่มตัวอย่างแบบอิงไวยากรณ์ให้กับ whisper.cpp ด้วย

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

 
GN⁺ 2023-07-23
ความคิดเห็นจาก Hacker News
  • เท่าที่ฉันเข้าใจ วิธีทำงานเป็นแบบนี้ โมเดลภาษาจะปล่อย โทเค็นทีละตัว โดยใช้พรอมป์ต์เป็นจุดเริ่มต้น และการคุยกับ LLM ก็อาจมองได้ว่าเป็นการที่ผู้ใช้ส่งลำดับโทเค็นให้ จากนั้นโมเดลสร้างต่อบางส่วน แล้วผู้ใช้ก็เติมต่ออีกที
    เทคนิคไวยากรณ์นี้ทำให้ควบคุมโทเค็นได้ละเอียดกว่ามาก ตัวอย่างเช่น ถ้าให้ Give me the address of the White House as JSON: {"street": " ไปจนถึงตรงนั้น LLM ก็จะคืนค่า 1600 Pennsylvania Ave NW" และทันทีที่เห็นเครื่องหมายอัญประกาศปิด ผู้ใช้ก็แทรก ", "City": " เพื่อรับ Washington, DC" ต่อได้
    แต่เพราะนี่เป็นแบบ อิงไวยากรณ์ มันจึงทำอะไรได้มากกว่าแค่ JSON มาก ก่อนหน้านี้ก็มีข้อเสนอที่เห็นใน Twitter ซึ่งน่าสนใจ คือให้ OpenAI รับ deterministic context-free grammar เป็นอาร์กิวเมนต์ของ API หรือยิ่งไปกว่านั้น รับ WASM ไบนารีขนาดเล็กที่เป็นตัว sampler เอง ขนาดไม่กี่ KB แล้วไปรันด้วยหน่วยความจำไม่กี่ MB ซึ่งอาจขยายความสามารถของ LLM ได้มาก
    https://twitter.com/grantslatton/status/1637692033115762688

    • ไม่ใช่แค่นั้น LLM ไม่ได้ส่งออกโทเค็นแต่ละตัวตรง ๆ แต่จะส่ง รายการตัวเลือกที่มีค่าน้ำหนักกำกับ ออกมา โทเค็นที่น่าจะเป็นไปได้มากที่สุดจะมีน้ำหนักสูงสุด แต่ก็อาจมีตัวเลือกอื่นอีกหลายตัว รวมถึงสัญลักษณ์ JSON อย่างเครื่องหมายอัญประกาศ
      การตั้งค่า temperature ใช้ปรับโอกาสที่โทเค็นซึ่งไม่ได้อยู่อันดับสูงสุดจะถูกเลือก เพื่อลดการสร้างข้อความซ้ำ ๆ การบังคับให้ LLM ทำตามไวยากรณ์ โดยทั่วไปคือการกรองรายการนั้นก่อนเลือกโทเค็น และถึงอย่างนั้นความสุ่มที่ temperature ควบคุมอยู่ก็ยังคงมีได้
      ฟีเจอร์ที่ล้ำกว่านั้นก็เช่นการอนุญาตให้ AI ย้อนกลับ ได้เมื่อมันตันและไม่สามารถสร้างเอาต์พุตที่ถูกต้องได้
    • การทำงานจริงคือจะ ตรวจด้วยไวยากรณ์ ทั้งเอาต์พุตปัจจุบันและโทเค็นถัดไปที่เป็นตัวเลือกในการสุ่ม จากนั้นก็ตัดโทเค็นตัวเลือกทั้งหมดที่ไม่สอดคล้องทิ้งไป แล้วค่อยใช้กลยุทธ์การสุ่มตามปกติกับรายการโทเค็นที่ยังใช้ได้
    • คำอธิบายนี้ดูไม่น่าจะถูก ก่อนหน้านี้ก็สามารถควบคุมเอาต์พุตของ LLM ได้อยู่แล้ว ด้วยการอ่านโทเค็นไปทีละตัวแล้วหยุดเมื่อเจอ อักขระหยุด
      แก่นของ PR การสุ่มแบบอิงไวยากรณ์น่าจะอยู่ที่ llama.cpp ใช้ไวยากรณ์เพื่อจำกัดโทเค็นเอาต์พุตถัดไปให้แคบลงเหลือเพียงชุดโทเค็นที่เป็นไปได้บางส่วน
    • มีคำอธิบายเชิงลึกของการทำสิ่งนี้อยู่เหมือนกัน: https://github.com/normal-computing/outlines/pull/131
      เขียนโดยหนึ่งในผู้พัฒนาไลบรารี Outlines ซึ่ง Outlines ก็เป็นไลบรารีเวิร์กโฟลว์ LLM ที่ใช้ได้ดีเหมือนกัน
    • ยังไม่ค่อยเข้าใจว่ากำลังพูดถึงอะไร โดยเฉพาะตั้งแต่คำว่า “passing up” เป็นต้นไป จนสงสัยว่าคนนี้สร้างศัพท์ขึ้นมาเองหรือเปล่า อินพุตที่ใส่ให้ LLM ได้มีแค่พรอมป์ต์ที่ถูกโทเค็นไนซ์เท่านั้น
      ต่อให้ส่งกฎ DCFG หรือเวอร์ชันที่คอมไพล์แล้วไปเป็นส่วนหนึ่งของคำขอ ก็ยังไม่ชัดว่ามันเปลี่ยนวิธีทำนายโทเค็นอย่างเป็นพื้นฐานได้อย่างไร ถ้าโมเดลทำนายสิ่งที่ไม่ตรงกับไวยากรณ์ที่ต้องการ หมายถึงจะป้อนพรอมป์ต์ใหม่ซ้ำไปเรื่อย ๆ จนกว่าจะได้คำตอบที่ตรงหรือ?
  • ควรชี้ให้เห็นว่านี่เป็นเพียงการใส่ ข้อจำกัดทางไวยากรณ์ ให้กับข้อความที่โมเดลสร้าง ไม่ได้ช่วยเรื่องการจัดแนวเนื้อหาให้ถูกต้องจริง ๆ มันมีประโยชน์เมื่อคุณต้องการรับประกันว่าเซิร์ฟเวอร์จะส่ง JSON ที่จัดรูปแบบถูกต้องออกมา แต่คงไม่ช่วยแก้ปัญหาการจัดแนวหลายอย่างของการสร้างภาษายุคปัจจุบัน
    ตัวอย่างเช่น ตอนนี้ Llama หรือ GPT มักติดป้ายกำกับ Markdown code block ผิด การสุ่มแบบอิงไวยากรณ์สามารถบังคับให้มันใส่ป้ายกำกับได้ แต่บังคับไม่ได้ว่าป้ายนั้นจะถูกต้องหรือไม่ เพราะขึ้นกับบริบท และถ้าสร้างภาษาเฉพาะโดเมนขึ้นมาใหม่โดยไม่ได้จัดแนวโมเดลให้เข้ากับภาษานั้น ก็คงยากจะคาดหวังผลลัพธ์ที่ดี

    • อีกประเด็นสำคัญคือ เมื่อมีสตริงแบบอิสระปรากฏขึ้น มันจะกลายเป็นการเปิดประตูให้ LLM หลุดออกนอกทางไปได้อย่างเต็มที่ ดังนั้นวิธีแบบนี้ควรใช้ร่วมกับการเอนเอียงการสุ่มด้วย heuristic อื่น ๆ เช่น repetition penalty เมื่อเข้าไปอยู่ในช่วงข้อความอิสระ
    • ถึงอย่างนั้นกับ Llama ก็น่าจะฝึกตัวอย่างบางอย่างเข้าไปใน LoRA ได้
      เช่น อาจจินตนาการถึงระบบที่ hot-swap LoRA สำหรับ Markdown และไฟล์ไวยากรณ์ Markdown ตามต้องการ
  • ฉันชอบสิ่งนี้มาก ก่อนหน้านี้เคยลองสร้าง Constrained Text Generation Studio (https://github.com/Hellisotherpeople/Constrained-Text-Genera...) และมีงานเกี่ยวข้องตีพิมพ์ใน COLING 2022 ด้วย (https://paperswithcode.com/paper/most-language-models-can-be...)
    แต่ก็คิดมาตลอดว่าวิธีแบบนี้และไอเดียที่เกี่ยวข้องซึ่งลิสต์ไว้ในงานนี้คือทิศทางที่ควรไป: https://arxiv.org/abs/2306.03081
    ตอนนี้คงต้องลองคิดดูว่าจะสร้างไวยากรณ์ที่บังคับสิ่งอย่าง จำนวนพยางค์ หรือกฎวากยสัมพันธ์ได้อย่างไร เพราะ LLM ปัจจุบันทำงานแนวนี้ได้แย่มากจากวิธีการโทเค็นไนซ์ของมัน

    • น่าแปลกใจเหมือนกัน แต่ Nous Hermes เขียน ไฮกุ ได้ค่อนข้างน่าเชื่อ
  • มีคนทำสิ่งนี้สำหรับ PyTorch ด้วย: https://github.com/Shopify/torch-grammar และยังมีเวอร์ชันดัดแปลงของ text-generation-inference ที่ใช้มันด้วย ถ้าต้องการก็แชร์ได้

    • ถ้าแชร์ได้ก็ดีเลย ฉันกำลังพยายามดัมพ์ เวกเตอร์ความน่าจะเป็นของโทเค็น จาก LLM UI ตัวหนึ่งอยู่ ถ้ามีจุดเริ่มต้นแบบอื่นก็น่าจะช่วยได้มาก
  • สำหรับกรณีที่เป็นการไล่ค่าของสตริงแบบหลายตัวเลือก คล้าย ๆ dropdown โดยพื้นฐานแล้ว สงสัยว่าตอนเลือกคำตอบสุดท้ายจะทำงานดีกว่าไหมถ้าไม่ใช้ greedy algorithm แต่พิจารณา ความน่าจะเป็นร่วมทั้งหมด ภายใต้ logits ที่ให้มา
    เมื่อโทเค็นเริ่มต้นถูกแชร์โดยหลายรายการในลิสต์ มันอาจช่วยให้เอนเอียงไปหาตัวเลือกที่ถูกต้อง แทนที่จะเป็นตัวเลือกที่แค่มีโทเค็นเริ่มต้นที่พบบ่อยที่สุด แน่นอนว่าเมื่อบางส่วนของ logits ถูกทำให้เป็น 0 แล้ว ก็ต้องปรับความน่าจะเป็นใหม่เพื่อให้สมเหตุสมผลจริง ๆ

  • “ไลบรารี” ไวยากรณ์นี้ถูกอ้างถึงเป็นตัวอย่างของรูปแบบ: https://github.com/antlr/grammars-v4
    ในนั้นมีครบตั้งแต่แอสเซมบลีและ C++ ไปจนถึง GLSL, ภาษาสคริปต์, เลขคณิต, เกม, freedesktop shortcuts, LLVM IR, Verilog และรูปแบบแปลก ๆ อื่น ๆ

    • น่าจะสะดวกถ้าใน inference API สามารถระบุ ทางลัดไวยากรณ์มาตรฐาน อย่าง HTML, JSON, Python ได้ เมื่อคิดถึงความพยายามที่ OpenAI ลงไปกับการ fine-tune โมเดล Code Interpreter ก็พูดตรง ๆ ว่าแปลกที่ยังไม่ได้ทำอะไรแบบนี้
    • ถ้ารองรับ ไวยากรณ์แบบ ANTLR4 ได้ก็น่าจะดีมาก เป็นเครื่องมือที่ยอดเยี่ยม
  • มีใครอธิบายแบบง่าย ๆ ได้ไหม? ผมพอรู้เรื่อง LLM อยู่บ้าง แต่ยังไม่ค่อยเข้าใจว่า Georgi กำลังทำอะไรตรงนี้ และทำไมบางคนถึงตื่นเต้นกันมาก

    • LLM ไม่ได้สร้าง “โทเค็นถัดไป” ออกมาตรง ๆ มันสร้าง เวกเตอร์ความน่าจะเป็น ที่แต่ละช่องสอดคล้องกับโทเค็นจากข้อความอินพุต และแต่ละค่าก็พอจะมองได้ว่าเป็นความน่าจะเป็นที่โทเค็นนั้นจะออกมาต่อไป
      โปรแกรมอย่าง ChatGPT จะ “ตีความ” เวกเตอร์ความน่าจะเป็นนั้นแล้วเลือกหนึ่งในโทเค็นอันดับบน ๆ หรือก็คือสุ่มตัวอย่าง เพื่อสร้างข้อความขึ้นมา แต่บางครั้งวิธีนี้ยืดหยุ่นเกินไป จนถึงขั้นต่อให้คุณอยากได้เอาต์พุตเป็น JSON มันก็ยังอาจเลือกโทเค็นที่ไม่ตรงตามไวยากรณ์ของ JSON แล้วสร้าง JSON ที่ผิดรูปแบบได้
      วิธี “บังคับ” ให้ LLM สร้างอะไรอย่าง JSON คือเปลี่ยนกระบวนการ sampling แทนที่จะเลือกจากโทเค็นอันดับบน ๆ แบบไม่จำกัด ก็กรองให้เหลือเฉพาะโทเค็นที่ยังตรงตามไวยากรณ์ของ JSON ก่อน แล้วค่อย sampling จากเซ็ตย่อยนั้น
    • ถ้าสั่งให้ LLM สร้าง JSON หรือภาษาอื่นที่มีไวยากรณ์กำกับ บางครั้งมันจะสร้างไวยากรณ์ที่ผิด PR นี้คือการแก้กระบวนการ sampling เพื่อจำกัดให้ LLM ส่งออกเฉพาะไวยากรณ์ที่ถูกต้อง ตามไวยากรณ์ที่ผู้ใช้ให้มา
      LLM สร้างข้อความทีละโทเค็น โดยเริ่มจากโครงข่ายประสาทขนาดใหญ่ที่ให้ค่าความน่าจะเป็นกับโทเค็นที่เป็นไปได้ทั้งหมด จากนั้นกระบวนการ sampling จะใช้ค่าพวกนั้นเลือกโทเค็นหนึ่งตัว แล้วทำซ้ำไปเรื่อย ๆ
      กระบวนการ sampling ไม่ใช่ตัวโครงข่ายประสาท จึงเปลี่ยนได้หลายแบบ จะใช้ greedy sampling ที่เลือกโทเค็นความน่าจะเป็นสูงสุดทุกครั้งก็ได้ แต่โดยทั่วไปการสุ่มแบบถ่วงน้ำหนักตามความน่าจะเป็นจะดีกว่า เพราะทำให้มีความหลากหลายและลดโอกาสติดลูป อย่างไรก็ตาม โทเค็นใดก็ตามที่มีความน่าจะเป็นไม่เป็นศูนย์ก็ยังอาจถูกเลือกได้ จึงอาจได้ JSON ที่ผิด PR นี้ทำให้ความน่าจะเป็นของโทเค็นทุกตัวที่ผิดตามไวยากรณ์กลายเป็น 0 เพื่อไม่ให้ถูกเลือก
      ยังมีการดัดแปลงกระบวนการ sampling ที่น่าสนใจอีก เช่น ถ้าสุ่มโทเค็นไปทีละตัวแล้วเดินเข้าทางตันจนไม่เหลือตัวเลือก ก็อาจอนุญาตให้ย้อนกลับได้ หรือจะพิจารณาหลายตัวเลือกในแต่ละขั้น สร้างเป็นต้นไม้ของเอาต์พุตที่เป็นไปได้ แล้วค่อยเลือกเส้นทางที่มีความน่าจะเป็นรวมสูงสุดในตอนท้ายก็ได้ ถ้าพิจารณาทุกตัวเลือก ต้นไม้จะเป็นต้นไม้เต็มที่แตกกิ่งตามจำนวนโทเค็นที่เป็นไปได้และโตแบบเอ็กซ์โปเนนเชียล ดังนั้นจึงอาจตัดแต่งให้เหลือ เช่น 5 เส้นทางที่ดีที่สุดในแต่ละขั้น นั่นคือ beam search แม้ใน LLM จะไม่ค่อยใช้กันเพราะต้นทุนการรันโครงข่ายประสาทเพื่อสร้างความน่าจะเป็นนั้นสูงมาก การเพิ่มต้นทุน 5 เท่าจึงหนักพอสมควร แต่ก็ทำได้และผลลัพธ์ก็ดีขึ้นระดับหนึ่ง นอกจากนี้ยังอาจนึกถึงการใช้ Monte Carlo tree search แบบเดียวกับเอนจินหมากรุกได้ด้วย
    • LLM พร้อมจะสร้างสตริงอะไรก็ได้ตามใจ เช่นคุณอยากได้ “Alice: 42” แต่มันอาจตอบกลับมาว่า “สวัสดี ฉันเป็นโมเดลที่พร้อมช่วยเหลือ และในมุมมองของฉัน Alice น่าจะเท่ากับ forty two อย่างถูกต้อง แต่ฉันก็เป็นแค่ language model”
      ดังนั้นคุณจึงให้ไวยากรณ์ที่บอกว่าคำตอบต้องเริ่มด้วยอักษรตัวใหญ่หนึ่งตัว ตามด้วยตัวพิมพ์เล็ก, เครื่องหมายโคลอน, เว้นวรรค, ตัวเลข และจบลง จากนั้นตอนหาโทเค็นแรก ก็จะพิจารณาเฉพาะโทเค็นที่เข้ากันได้กับแพตเทิร์นนั้น และในขั้นถัด ๆ ไปก็จะพิจารณาเฉพาะโทเค็นที่ยังเข้ากันได้กับแพตเทิร์นถัดไป
      ไวยากรณ์แบบนี้ช่วยทำเรื่องดังกล่าวได้อย่างยืดหยุ่นและมีประโยชน์ในเชิงแพตเทิร์น
    • ดูคอมเมนต์ของผมที่นี่ได้: https://news.ycombinator.com/item?id=36820884
  • เรื่องนี้น่าสนใจดี ผมเลยว่าจะลองเอาไปใส่ในงานที่ทำอยู่ แต่ก็รู้สึกว่าในมุมมองของ Bitter Lesson นี่อาจไม่ใช่แนวทางที่ดีที่สุด นอกจากในระยะสั้นมาก: http://www.incompleteideas.net/IncIdeas/BitterLesson.html

    • มันอาจเป็นวิธีเฉพาะหน้า แต่ก็เป็นวิธีเฉพาะหน้าที่สำคัญ ยังไม่ชัดเจนว่าในอีกไม่กี่ปีข้างหน้า LLM จะสามารถแก้ปัญหาการสร้างข้อความที่มีข้อจำกัดได้แบบ “เป็นธรรมชาติ” หรือไม่
    • ผมไม่ใช่ผู้เชี่ยวชาญเลย แต่เข้าใจว่า OpenAI ใช้วิธีลักษณะนี้กับ GPT API บางตัวสำหรับงานเขียนโปรแกรม และผมก็เคยเห็นการคาดเดาว่า ถ้าโยนการจัดการไวยากรณ์ง่าย ๆ ไปให้กระบวนการที่เรียบง่ายและเหมาะกับงานนั้นโดยตรง ก็อาจช่วยให้ LLM ใช้ ทรัพยากรการคำนวณ ไปกับงานที่ซับซ้อนกว่านี้ได้อย่างมีประสิทธิภาพขึ้น ไม่รู้ว่าเป็นจริงแค่ไหน
  • มีโปรเจกต์อื่นที่ใช้วิธีคล้ายกันด้วย: https://github.com/automorphic-ai/trex
    Playground: https://automorphic.ai/playground

  • อยากได้คำแนะนำเกี่ยวกับงานวิจัยหรือบทสรุปที่อธิบายว่า sampling/decoding ทำงานอย่างไรในยุคของโครงข่ายประสาทเทียมแบบ end-to-end รู้แล้วว่าในยุค HMM การทำ decoding ใน machine translation และ speech recognition ทำกันอย่างไร เช่น https://en.wikipedia.org/wiki/Viterbi_algorithm หรือ https://en.wikipedia.org/wiki/Beam_search
    ทุกวันนี้รู้สึกเหมือนผู้คนใช้วิธีแบบ “greedy” กันเฉยๆ แต่ก็ไม่ค่อยแน่ใจนัก ถ้ามีแหล่งข้อมูลแนะนำเกี่ยวกับหัวข้อนี้จะดีมาก

    • ทั้ง greedy และสุ่ม :) แนะนำให้ดูอัลกอริทึมใน implementation ของ LLM เป็นหลักมากกว่าจะอ่านงานวิจัย โดยใน rwkv.cpp มี implementation ภาษา Python ที่ค่อนข้างอ่านง่ายอยู่ที่: https://github.com/saharNooby/rwkv.cpp/blob/master/rwkv/samp...
    • ถ้าอ่านเอกสารของ GPT-4 ก็จะเห็นว่าไม่ได้ต่างจากสิ่งที่กล่าวมามากนัก
      https://platform.openai.com/docs/api-reference/completions/c...
      แน่นอนว่าตอนนี้เรารู้แล้วว่า GPT-4 เป็นสถาปัตยกรรมแบบ mixture of experts ดังนั้นภายในจึงมีการทำให้การคำนวณขนานกัน และยังรวมถึงวิธีปรับ logit ด้วยเทอม presence/frequency penalty ด้วย