2 คะแนน โดย GN⁺ 2024-12-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • สร้างตัวรันอนุมาน GPT-2 ด้วย โค้ด C ราว 3000 ไบต์ เท่านั้น โดยครอบคลุมตั้งแต่การโหลดน้ำหนัก การทำโทเค็นไนซ์ การรัน Transformer ไปจนถึงการแปลงผลลัพธ์ในลำดับการทำงานเดียว
  • แม้จะคงขนาดโค้ดให้เล็ก แต่ก็ยังมี KV caching, การคูณเมทริกซ์แบบรวดเร็ว และการทำงานขนานด้วย OMP แบบเลือกเปิดได้ ทำให้ GPT-2 Small สร้างคำตอบได้ภายในไม่กี่วินาทีบนเครื่องสมัยใหม่
  • คุณภาพผลลัพธ์อยู่ในระดับที่ “แย่อย่างเห็นได้ชัดในเชิงวัตถุวิสัย” และยังมีข้อจำกัดเชิงปฏิบัติ เช่น การจัดการ UTF-8 และความต้องการหน่วยความจำเมื่อรันโมเดลขนาดใหญ่
  • การอิมพลีเมนต์แบ่งเป็นส่วนของการคำนวณเมทริกซ์ เลเยอร์โครงข่ายประสาท Transformer, Byte Pair Encoding, I/O และการโหลดน้ำหนักกับ BPE ซึ่งแสดงโครงสร้างทั้งหมดของตัวรันอนุมานขนาดเล็ก
  • แม้ GPT-2 จะเป็นโมเดลโอเพนซอร์สจากปี 2019 ที่อ่อนกว่า GPT-4 มาก แต่ชิ้นส่วนหลักที่ใช้รันโมเดลภาษาแบบสมัยใหม่ก็ยังสามารถถ่ายทอดออกมาเป็นโค้ด C ขนาดเล็กได้

ตัวรัน GPT-2 ที่ทำด้วย C ขนาด 3000 ไบต์

  • โปรแกรมนี้เป็น อิมพลีเมนเตชัน GPT-2 แบบไม่มี dependency ที่อ่านเมทริกซ์น้ำหนักและไฟล์ BPE จากไฟล์ TensorFlow ต้นฉบับ
  • อินพุตถูกแปลงเป็นโทเค็นด้วยตัวเข้ารหัส Byte Pair Encoding(BPE) แบบง่าย และเอาต์พุตจะถูกแปลงกลับเป็นสตริงด้วยตัวถอดรหัส BPE
  • โครงสร้างภายในไล่ตั้งแต่แพ็กเกจพีชคณิตเชิงเส้นพื้นฐาน การคำนวณเมทริกซ์ สถาปัตยกรรม Transformer ไปจนถึงโค้ดอนุมาน
  • โค้ดเผยแพร่อยู่บน GitHub
  • GPT-2 Small สามารถสร้างคำตอบหนึ่งรายการได้ภายในไม่กี่วินาทีบนเครื่องสมัยใหม่
    • อิมพลีเมนต์ KV caching
    • ใช้การคูณเมทริกซ์ที่มีประสิทธิภาพ
    • สามารถเปิดใช้การทำงานขนานด้วย OMP ได้ตามต้องการ

เงื่อนไขการรันและข้อจำกัด

  • แม้จะสามารถใช้การอิมพลีเมนต์นี้สร้างโปรแกรมสนทนาแบบ ChatGPT ได้ แต่คุณภาพผลลัพธ์ยังไม่ดี
  • การจัดการอักขระ UTF-8 มีพฤติกรรมเฉพาะบางอย่าง
  • หากรันโมเดลขนาด XL พร้อม context ที่ยาว อาจต้องใช้ RAM ประมาณ 100GB
  • หากใช้อินพุตแบบ ASCII ร่วมกับ GPT-2 Small ก็แทบจะรันได้เกือบทุกที่

การทำงานของ GPT-2 และ Transformer

  • ChatGPT คือแอปพลิเคชันที่ใช้โมเดลภาษาเพื่อสนทนาได้เหมือนมนุษย์ และ GPT-4 ถูกอธิบายว่าเป็นโมเดลรุ่นใหม่ที่ใช้ขับเคลื่อน ChatGPT
  • โปรแกรม C นี้สร้างพฤติกรรมคล้าย ChatGPT โดยใช้ GPT-2 ซึ่งเป็นโมเดลจากปี 2019
  • GPT-2 เป็นโมเดลแมชชีนเลิร์นนิงตระกูล Transformer
  • Transformer รับลำดับคำที่มีขนาดคงที่เป็นอินพุต แล้วทำนายคำถัดไป
  • เมื่อทำขั้นตอนเดียวกันซ้ำ ก็สามารถสร้างลำดับที่มีความยาวตามต้องการได้

การคำนวณเมทริกซ์และการบีบโค้ดด้วยแมโคร

  • เนื่องจากโครงข่ายประสาทประกอบด้วยการคำนวณเมทริกซ์ การอิมพลีเมนต์จึงเริ่มจาก struct Matrix ขั้นต่ำ
    • float* dat
    • int rows, cols
  • การดำเนินการที่จำเป็นแบ่งใหญ่ ๆ ได้เป็นสองประเภท
    • การคำนวณระหว่างเมทริกซ์กับค่าคงที่
    • การคำนวณระหว่างเมทริกซ์กับเมทริกซ์
  • ใช้แมโครของ C เพื่อลดโครงสร้างลูปที่ซ้ำกัน และสร้างหลายฟังก์ชันโดยเปลี่ยนแค่โอเปอเรเตอร์เฉพาะส่วน
  • #define ของ C มีลักษณะใกล้เคียงการแทนที่ข้อความอย่างง่าย จึงสามารถใส่ได้ไม่เพียงโอเปอเรเตอร์ทั่วไป แต่รวมถึงนิพจน์ที่มีเครื่องหมายอัฒภาคด้วย เพื่อช่วยลดขนาดโค้ด

การคูณเมทริกซ์แบบรวดเร็ว

  • การคูณเมทริกซ์พื้นฐานเริ่มจากอิมพลีเมนเตชัน O(n³) แบบตรงไปตรงมา ที่ใช้ลูปซ้อนสามชั้น
  • จากนั้นจึงปรับลูปให้คำนึงถึงแคชและรูปแบบการเข้าถึงหน่วยความจำ เพื่อให้อ่านและเขียนหน่วยความจำเดิมซ้ำได้มากขึ้น
  • เวอร์ชันที่เร็วขึ้นจะเพิ่มค่า j และ k ทีละ 4 และใช้ลูป k2, j2 ภายใน
  • ในขั้นตอนอนุมาน มีการเพิ่มวิธีคูณเฉพาะบางส่วนของเมทริกซ์ A กับ B เพื่อใช้ผลที่คำนวณไปแล้วซ้ำ

การอิมพลีเมนต์เลเยอร์โครงข่ายประสาท

  • เพื่อสร้าง Transformer จึงมีการอิมพลีเมนต์เลเยอร์โครงข่ายประสาทบางชนิดขึ้นเอง
  • ฟังก์ชันกระตุ้น GELU ถูกอิมพลีเมนต์ด้วยแมโคร
  • มีฟังก์ชันสำหรับจัดการส่วนล่างรูปสามเหลี่ยมของเมทริกซ์เพื่อทำ causal attention
    • จำกัดเมทริกซ์ attention ให้มองเห็นเฉพาะอดีตโดยไม่มองโทเค็นในอนาคต
  • LayerNorm ใช้ปรับค่าเฉลี่ยและความแปรปรวนของแต่ละเลเยอร์ให้เป็นมาตรฐาน
  • ฟังก์ชัน Linear จะบวก bias แบบ tiling หลังการคูณเมทริกซ์

ตัว Transformer หลัก

  • การอิมพลีเมนต์ Transformer จะทำลำดับต่อไปนี้ซ้ำในแต่ละเลเยอร์
    • ผ่าน LayerNorm และ Linear เพื่อคำนวณ query, key, value พร้อมกัน
    • แยก qkv ตามแต่ละ head
    • คำนวณผลคูณของ query กับ key แล้วใช้การประมวลผลแบบ causal attention
    • นำผล softmax ไปคูณกับเมทริกซ์ value
    • รวมผลลัพธ์แล้วใช้ residual connection
    • ผ่าน GELU และ Linear แล้วใช้ residual connection อีกครั้ง
  • ในตอนท้าย จะผ่าน Final LayerNorm แล้วนำเอาต์พุตของตำแหน่งโทเค็นสุดท้ายไปคูณกับน้ำหนัก embedding เพื่อคำนวณตัวเลือกของโทเค็นถัดไป

วิธีทำ KV caching

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

การอิมพลีเมนต์ Byte Pair Encoding

  • โมเดลภาษาต้องการอินพุตขนาดคงที่ จึงยากที่จะจัดการคำจำนวนไม่สิ้นสุดแบบเป็นคำทั้งคำโดยตรง
  • โมเดลระดับตัวอักษรต้องเรียนรู้ความหมายของทุกคำใหม่ตั้งแต่ต้น และยังมีปัญหาที่ทำให้ขนาด context ที่ใช้งานได้ลดลงตามความยาวเฉลี่ยของคำ
  • โมเดลอย่าง GPT-2 ใช้ BPE เพื่อสร้างโทเค็นเป็นชิ้นส่วนของคำ
    • คำที่พบบ่อยอาจกลายเป็นโทเค็นเดียว
    • คำที่พบไม่บ่อยจะถูกแยกเป็นชิ้นเล็กลง
    • ตัวอย่างเช่น nicholas อาจถูกแบ่งเป็น nich, o, las
  • อัลกอริทึม BPE โดยทั่วไปจะรวมคู่โทเค็นที่อยู่ติดกันซ้ำ ๆ
  • แต่อิมพลีเมนเตชัน C นี้เลือกใช้วิธี recursive ที่อาจใช้เวลาแบบเลขชี้กำลังแทนอัลกอริทึมเวลาเชิงเส้น เพื่อลดขนาดโค้ด
    • ค้นหารายการใน vocabulary ที่ตรงกับ prefix ของคำปัจจุบัน
    • ทำ tokenization ของสตริงที่เหลือแบบ recursive
    • เลือกการแบ่งโทเค็นที่ดีที่สุดโดยอิงจากความยาวและดัชนีใน vocabulary

การโหลดน้ำหนัก

  • น้ำหนักของโครงข่ายประสาทต้องถูกอ่านจากดิสก์ โดยไฟล์ใช้รูปแบบ serial binary แบบแบนของ float 32 บิต
  • โมเดล GPT-2 แต่ละขนาดใช้สถาปัตยกรรมเดียวกัน และน้ำหนักก็ถูกเก็บในลำดับเดียวกัน จึงสามารถอ่านเมทริกซ์ตามรูปร่างที่ถูกต้องตามลำดับได้เลย
  • อย่างไรก็ตาม ลำดับการเก็บเลเยอร์ต่างจากที่คาดไว้
    • หลังเลเยอร์ 0 และ 1 จะตามด้วย 10
    • เพราะชื่อถูกเรียงตาม lexicographic order
    • ในการเรียงสตริง 10 จะมาก่อน 2
  • การอิมพลีเมนต์จึงใช้โค้ด permutation เพื่อแปลงลำดับนี้กลับเป็นลำดับเลเยอร์จริง

การโหลด BPE vocabulary

  • ก่อนจะรัน BPE ต้องอ่านไฟล์ vocabulary จากดิสก์ก่อน
  • ไฟล์ต้นฉบับเป็นรูปแบบที่ออกแบบมาให้ Python อ่าน ไม่ใช่รูปแบบที่ parse ได้ง่ายด้วยโค้ด C ขนาดเล็ก
  • ไฟล์นี้ไม่ใช่รายการคำ แต่เป็น รายการการ merge ของ BPE
    • ตัวอย่างเช่น แทนที่จะเก็บโทเค็น Hello ตรง ๆ ไฟล์จะเก็บในลักษณะว่าต้อง merge H กับ ello
  • ไฟล์ใช้อีกรูปแบบการเข้ารหัสที่คล้าย UTF-8 แต่ไม่เหมือนกันเสียทีเดียว
    • อักขระ ASCII ที่พิมพ์ได้จะถูกเก็บตรง ๆ
    • อักขระที่พิมพ์ไม่ได้ในช่วง 0~31 จะถูกเข้ารหัสเป็น 188 + ค่าของอักขระ
    • ตัวอย่างเช่น ช่องว่างจะถูกเข้ารหัสเป็นโทเค็น Ġ
  • บนดิสก์ Ġ อยู่ในรูป UTF-8 เป็น 0xc4 0xa0 จึงต้องมีการประมวลผลเพิ่มเติมเพื่อแปลงกลับเป็นช่องว่าง

สิ่งที่โค้ดขนาดเล็กนี้แสดงให้เห็น

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

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

 
GN⁺ 2024-12-13
ความคิดเห็นจาก Hacker News
  • ยังไม่ได้ลองรันโค้ดเอง แต่สิ่งที่น่าประทับใจคือ ขนาดที่เล็ก
    เมื่อนึกว่าโปรแกรม ELIZA ยุคแรก ๆ ยังใหญ่กว่านี้ ก็เท่ากับว่าในช่วง 4 ปีที่ผ่านมา เราสามารถยัดของแบบนี้ลงไปได้ในระดับไบต์แล้ว
    ถ้าใครรู้ว่าเวทมนตร์ซ่อนอยู่ตรงไหนก็อยากให้ช่วยอธิบายหน่อย สงสัยว่าเป็น ฟังก์ชัน GELU หรือเป็นโมเดลที่ดาวน์โหลดผ่านสคริปต์ Bash กันแน่

    • เวทมนตร์ส่วนใหญ่อยู่ใน ไฟล์โมเดลขนาด 475MB ที่ดาวน์โหลดผ่านสคริปต์ Bash
    • ลองรันดูแล้วก็ไม่ได้ประทับใจเท่าไร
      ถามว่า Who are you? มันตอบว่า I am Alice. และถ้าถามเรื่องคอมพิวเตอร์หรือความสามารถ ก็จะพูดซ้ำว่า I am a computer model trained by OpenAI. How can I help you?
      ถ้าขอให้อธิบายการบวก มันกลับอธิบายการคูณ และกับ 2+2 หรือ Sum 2+2 ก็แค่พูดตามเดิมเท่านั้น
  • ยังจำได้ตอนที่ GPT-2 ออกมาใหม่ ๆ แล้วได้ลองเล่น
    ผมกับเพื่อน export log แชตออกมา แล้ว fine-tune GPT-2 ให้มันเลียนแบบบทสนทนาระหว่างเราสองคน ซึ่งตลกมาก แต่บางครั้งก็แม่นจนน่าขนลุก
    สงสัยว่าการ ก้าวกระโดดครั้งใหญ่ จาก GPT-2 ไป GPT-3 เกิดจากอะไร เป็นโมเดลที่ใหญ่ขึ้น ข้อมูลที่มากขึ้น หรือทั้งสองอย่างกันแน่
    รู้ว่า RLHF สร้างความต่างอย่างมาก แต่แม้แต่โมเดล GPT-3 พื้นฐาน ถ้าให้ตัวอย่างมากพอ ก็ใช้งานได้ค่อนข้างดีแค่ด้วย text completion

  • ไม่ค่อยแน่ใจเหมือนกัน แต่มีนิทานที่ GPT-2 เขียนให้ซึ่งผมชอบอยู่
    https://deepdreams.stavros.io/episodes/the-princess-the-fair...

    • ดีมากจริง ๆ สนุกจริง และเหมาะกับการฟังจนหลับด้วย
      สงสัยว่าหน้านี้ทำด้วย GPT-2 หรือเปล่า
    • น่าประทับใจ แปลกประหลาด แต่ก็ สอดคล้องกันราว 90% จนเกิดบรรยากาศพิสดารเฉพาะตัวขึ้นมา
  • ข้อความที่ว่า “ส่วนใหญ่ทำขึ้นเพื่อความสนุก แต่ก็เป็นตัวอย่างที่ดีที่แสดงให้เห็นว่า neural network จริง ๆ แล้วเรียบง่ายแค่ไหน” น่าสนุกดี
    ชู่ว อย่าไปบอกใครนะ ปัญญาประดิษฐ์ คือไสยศาสตร์ดำที่เอาไว้หาเงิน

  • GPT-2 ถูก instruction tuning ไว้จนใช้แชตจริงได้หรือเปล่า?
    ถ้าไม่ใช่ ผมรู้สึกว่าการเรียกสิ่งนี้ว่า ChatGPT clone ก็ค่อนข้างฝืนมาก

    • ในบทความก็เขียนไว้แล้วแบบนี้: ถ้าไม่สนคุณภาพผลลัพธ์ ก็สร้างอะไรคล้าย ChatGPT ได้ และโดยวัตถุวิสัยแล้วผลลัพธ์ค่อนข้างแย่มาก แต่ก็รันได้
      โดยพื้นฐานแล้วแทบใช้งานไม่ได้ และแทบไม่เกี่ยวข้องอะไรนอกจากหยิบชื่อมาใช้ ถึงอย่างนั้นมันก็เป็นโปรแกรมที่คอมไพล์และรันได้จริง
      เมื่อเห็นคนยกย่องประสิทธิภาพของโปรเจกต์ที่ผู้สร้างเองก็ยอมรับว่าทำงานได้ไม่ดีนัก ก็เหมือนว่าแก่นหลักคือการ ดึงความสนใจด้วย buzzword นั่นแหละ
  • ประโยคที่ว่า “ภาษาอื่น ๆ ที่มีมาโครดี ๆ เห็นหรือยัง Lisp ไม่ได้ดีกว่า C เสมอไปนะ!” ครั้งนี้ถือว่ายอมรับได้ เพราะเป็นมุกแซวขึ้นข้างบน
    ถ้าไม่เห็นลิงก์โค้ด มันถูกฝังอยู่ในเนื้อหา: https://github.com/carlini/c-chat-gpt-2

  • เคยเห็น แชตบอตปัญญาประดิษฐ์ แบบคลาสสิกที่ดีกว่านี้ด้วย
    https://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/areas...
    Splotch แก้นิดหน่อยก็ยังคอมไพล์บนระบบตระกูล Unix สมัยใหม่ได้ดี

  • สงสัยว่ามีใครลองรันในเครื่องตัวเองแล้วดูว่า GPT-2 ตัวนี้ให้ผลลัพธ์แบบไหนบ้างหรือยัง

    • รู้สึกว่ามันแทบจะให้ผลลัพธ์เดิมซ้ำ ๆ อยู่เสมอ
      แต่ก็ยังน่าสนใจพอสมควร และอยากลองแกะดูแล้วปรับแต่งเอง ผมอยากลองเล่น GPT-2 บนเครื่องตัวเองมาสักพักแล้ว
    • อ่านดูแล้ว ถ้าใช้อุณหภูมิและ seed เดียวกัน โมเดล GPT-2 ที่โหลดตามปกติกับโมเดลที่โหลดในโปรแกรมนี้น่าจะให้ ผลลัพธ์เหมือนกันทุกประการ
      ผมไม่ได้ตรวจอุณหภูมิและ seed ในโค้ดโดยตรง และตอนนั้นส่วนใหญ่กำลังดูว่าทำไมถึง obfuscate
      ต่อให้แกะ obfuscation ออก โค้ดก็คงไม่ได้ยาวขึ้นมากนัก และถ้าอยู่ราว ๆ 10,000 ตัวอักษร แค่ดูบนหน้าจอก็น่าประทับใจพอแล้ว
  • ทุกวันนี้ถ้าใช้ gptscript ก็สร้าง ChatGPT ของตัวเองได้อย่างรวดเร็ว
    https://github.com/gptscript-ai/gptscript

  • GELU นี่เหมือนเวทมนตร์จริง ๆ:
    UNARY(GELU, b / 2 * (1 + tanh(.7978845 * (b + .044715 * b * b * b))))

    • นี่เป็นแค่ค่าประมาณเชิงปฏิบัติของนิยามทางคณิตศาสตร์จริงของ GELU เท่านั้น
      นิยามคือ GELU(x) := x * Φ(x) โดยที่ Φ(x) คือ ฟังก์ชันแจกแจงสะสมของการแจกแจงแบบเกาส์
    • รูปแบบนี้ทำให้นึกถึง fast inverse square root