microgpt - การฝึกและอนุมาน GPT ที่เขียนด้วย Python ล้วน 200 บรรทัด
(karpathy.github.io)- โปรเจกต์เชิงศิลปะที่ karpathy เผยแพร่ โดยใช้ไฟล์เดียว 200 บรรทัดเพื่อ อิมพลีเมนต์อัลกอริทึม GPT ทั้งหมด โดยไม่มี dependency ภายนอก
- ความแตกต่างจาก LLM สำหรับโปรดักชันมีเพียง ขนาดและประสิทธิภาพ เท่านั้น ส่วนแก่นสำคัญเหมือนกัน และหากเข้าใจโค้ดนี้ก็ถือว่า เข้าใจแก่นเชิงอัลกอริทึมของ GPT
- ครอบคลุมทั้งชุดข้อมูล, โทเคไนเซอร์, เอนจิน autograd, สถาปัตยกรรม Transformer คล้าย GPT-2, ตัวเพิ่มประสิทธิภาพ Adam ตลอดจนลูปฝึกและอนุมาน
- เป็นผลลัพธ์สรุปของงาน ลดความซับซ้อนของ LLM ตลอด 10 ปี จากโปรเจกต์ก่อนหน้าอย่าง micrograd, makemore, nanogpt เป็นต้น โดยถ่ายทอดแก่นของ GPT ในรูปแบบขั้นต่ำที่สุดที่ไม่อาจลดทอนได้อีก
- ฝึกด้วยข้อมูลชื่อ 32,000 รายการเพื่อ สร้างชื่อใหม่ที่ดูสมจริง และคำนวณทุกอย่างด้วย autograd ระดับสเกลาร์ โดยตรง
- กระบวนการฝึกประกอบด้วย คำนวณ loss → backpropagation → อัปเดตด้วย Adam และใช้เวลารันได้ภายในประมาณ 1 นาที
ภาพรวมของ microgpt
- microgpt คือ สคริปต์ Python 200 บรรทัด ที่อิมพลีเมนต์กระบวนการฝึกและอนุมานของโมเดล GPT ได้อย่างครบถ้วน
- โดยไม่มีไลบรารีภายนอก และรวมทั้ง ชุดข้อมูล, โทเคไนเซอร์, autograd, โมเดล, ออปติไมเซอร์, ลูปการฝึก ไว้ทั้งหมด
- รวมแนวคิดจากโปรเจกต์เดิมอย่าง micrograd, makemore, nanogpt เป็นต้น มาไว้ในไฟล์เดียว
- เป็นอิมพลีเมนเตชันที่เหลือไว้เฉพาะแก่นเชิงอัลกอริทึมในระดับ “ลดให้เรียบง่ายกว่านี้ไม่ได้อีกแล้ว”
- โค้ดทั้งหมดมีให้ที่ GitHub Gist, เว็บเพจ, Google Colab
การจัดชุดข้อมูล
- เชื้อเพลิงของโมเดลภาษาขนาดใหญ่คือ สตรีมข้อมูลข้อความ โดยในระบบโปรดักชันจะใช้หน้าเว็บจากอินเทอร์เน็ต แต่ใน microgpt ใช้ตัวอย่างที่เรียบง่ายเป็น ชื่อ 32,000 รายการ ที่เก็บไว้บรรทัดละหนึ่งชื่อ
- แต่ละชื่อถูกมองเป็น “เอกสาร” หนึ่งชิ้น และเป้าหมายของโมเดลคือ เรียนรู้รูปแบบเชิงสถิติ ในข้อมูลเพื่อสร้างเอกสารใหม่ที่คล้ายกัน
- หลังฝึกเสร็จ โมเดลจะ "หลอน(hallucinate)" ชื่อใหม่ที่ดูสมจริง เช่น "kamon", "karai", "vialan"
- ในมุมมองของ ChatGPT การสนทนากับผู้ใช้ก็เป็นเพียง “เอกสารหน้าตาแปลก” แบบหนึ่ง และเมื่อเริ่มเอกสารด้วยพรอมป์ต์ คำตอบของโมเดลก็คือ การเติมเอกสารให้สมบูรณ์ตามสถิติ
โทเคไนเซอร์
- โครงข่ายประสาททำงานกับตัวเลข ไม่ใช่ตัวอักษร จึงต้องมีวิธีแปลงข้อความเป็น ลำดับของโทเคน ID แบบจำนวนเต็ม และแปลงกลับได้
- โทเคไนเซอร์สำหรับโปรดักชันอย่าง tiktoken (ใช้ใน GPT-4) ทำงานระดับกลุ่มตัวอักษรเพื่อประสิทธิภาพ แต่โทเคไนเซอร์ที่ง่ายที่สุดคือการ กำหนดจำนวนเต็มหนึ่งค่าให้กับอักขระที่ไม่ซ้ำแต่ละตัวในชุดข้อมูล
- เรียงตัวอักษรพิมพ์เล็ก a-z แล้วกำหนด ID เป็นดัชนีของแต่ละตัว โดยค่าจำนวนเต็มนั้นไม่ได้มีความหมายในตัวเอง และแต่ละโทเคนคือ สัญลักษณ์ไม่ต่อเนื่องที่แยกจากกัน
- เพิ่มโทเคนพิเศษ BOS(Beginning of Sequence) เพื่อบอกว่า “เอกสารใหม่เริ่ม/จบแล้ว” และคำว่า "emma" จะถูกครอบเป็น
[BOS, e, m, m, a, BOS] - ขนาด vocabulary สุดท้ายคือ 27 โทเคน (ตัวพิมพ์เล็ก 26 ตัว + BOS 1 ตัว)
การหาค่าอนุพันธ์อัตโนมัติ (Autograd)
- การฝึกโครงข่ายประสาทต้องใช้ กราดิเอนต์: สำหรับพารามิเตอร์แต่ละตัว ต้องรู้ว่า “ถ้าเพิ่มค่านี้ขึ้นเล็กน้อย loss จะเพิ่มหรือลด และมากแค่ไหน?”
- กราฟการคำนวณมีอินพุตจำนวนมาก (พารามิเตอร์ของโมเดลและโทเคนอินพุต) แต่จะ ลู่เข้าสู่เอาต์พุตสเกลาร์เดี่ยวคือ loss
- Backpropagation เริ่มจากเอาต์พุตแล้วไล่ย้อนกราฟกลับไป โดยอาศัย กฎลูกโซ่ ของแคลคูลัสเพื่อคำนวณกราดิเอนต์ของ loss ต่ออินพุตทั้งหมด
- อิมพลีเมนต์ด้วย คลาส Value: แต่ละ Value ห่อสเกลาร์เดี่ยว (
.data) และติดตามว่าค่านั้นถูกคำนวณมาอย่างไร- เมื่อมีการบวก คูณ หรือโอเปอเรชันอื่น จะสร้าง Value ใหม่ที่จำอินพุต (
_children) และ อนุพันธ์เฉพาะที่ (_local_grads) ของโอเปอเรชันนั้น - ตัวอย่าง:
__mul__จะบันทึก ∂(a·b)/∂a=b และ ∂(a·b)/∂b=a
- เมื่อมีการบวก คูณ หรือโอเปอเรชันอื่น จะสร้าง Value ใหม่ที่จำอินพุต (
- บล็อกโอเปอเรชันที่รองรับ: การบวก, การคูณ, การยกกำลัง, log, exp, ReLU
- เมธอด
backward()จะไล่กราฟตาม ลำดับทอพอโลยีย้อนกลับ และใช้กฎลูกโซ่ในแต่ละขั้น- เริ่มจากโหนด loss ด้วย
self.grad = 1(∂L/∂L=1) - แล้วคูณกราดิเอนต์เฉพาะที่ไปตามเส้นทางจนส่งต่อถึงพารามิเตอร์
- เริ่มจากโหนด loss ด้วย
- สะสมด้วย += (ไม่ใช่การกำหนดทับ): เมื่อกราฟแตกแขนง กราดิเอนต์จะไหลมาจากแต่ละแขนงอย่างอิสระและต้อง นำมาบวกกัน (เป็นผลจากกฎลูกโซ่สำหรับหลายตัวแปร)
- แม้จะ เหมือนกับ
.backward()ของ PyTorch ในเชิงอัลกอริทึม แต่ทำงานระดับสเกลาร์แทนเทนเซอร์ จึงเรียบง่ายกว่ามากแต่มีประสิทธิภาพต่ำกว่า
การกำหนดค่าเริ่มต้นพารามิเตอร์
- พารามิเตอร์คือ ความรู้ ของโมเดล เป็นชุดตัวเลขทศนิยมขนาดใหญ่ที่เริ่มแบบสุ่มและถูกปรับให้เหมาะสมซ้ำ ๆ ระหว่างการฝึก
- กำหนดค่าเริ่มต้นเป็นค่าสุ่มขนาดเล็กจาก การแจกแจงแบบเกาส์เซียน
- ประกอบด้วยเมทริกซ์ที่ตั้งชื่อใน
state_dict: ตาราง embedding, น้ำหนัก attention, น้ำหนัก MLP, projection เอาต์พุตสุดท้าย - การตั้งค่าไฮเปอร์พารามิเตอร์:
n_embd = 16: มิติของ embeddingn_head = 4: จำนวน attention headsn_layer = 1: จำนวนเลเยอร์block_size = 16: ความยาวลำดับสูงสุด
- สำหรับโมเดลขนาดเล็กนี้มี พารามิเตอร์ 4,192 ตัว (GPT-2 มี 1.6 พันล้านตัว และ LLM สมัยใหม่มีหลายแสนล้านตัว)
สถาปัตยกรรม
- สถาปัตยกรรมของโมเดลเป็น ฟังก์ชันไร้สถานะ: รับโทเค็น ตำแหน่ง พารามิเตอร์ และคีย์/ค่าที่แคชไว้จากตำแหน่งก่อนหน้า แล้วคืนค่า logits (คะแนน) สำหรับโทเค็นถัดไป
- อิงตาม GPT-2 แต่ทำให้ง่ายลงเล็กน้อย: ใช้ RMSNorm (แทน LayerNorm), ไม่มีไบแอส, ใช้ ReLU (แทน GeLU)
-
ฟังก์ชันช่วย
- linear: การคูณเมทริกซ์-เวกเตอร์ที่คำนวณ ดอทโปรดักต์ หนึ่งครั้งต่อแต่ละแถวของเมทริกซ์น้ำหนัก เป็นทรานส์ฟอร์มเชิงเส้นที่เรียนรู้ได้ซึ่งเป็นองค์ประกอบพื้นฐานของโครงข่ายประสาทเทียม
- softmax: แปลงคะแนนดิบ (logits) เป็น การแจกแจงความน่าจะเป็น โดยทุกค่าจะอยู่ในช่วง [0,1] และมีผลรวมเป็น 1 เพื่อความเสถียรเชิงตัวเลขจึงลบค่าสูงสุดออกก่อน
- rmsnorm: ปรับสเกลเวกเตอร์ใหม่ให้มี รากของค่าเฉลี่ยกำลังสอง เป็นหนึ่ง เพื่อป้องกันไม่ให้ activation โตหรือหดเมื่อไหลผ่านเครือข่าย ช่วยให้การฝึกเสถียรขึ้น
-
โครงสร้างโมเดล
- Embedding: token ID และ position ID ใช้อ้างอิงแถวจากตาราง embedding ของตัวเอง (
wte,wpe) แล้วนำเวกเตอร์ทั้งสองมาบวกกัน เพื่อเข้ารหัสพร้อมกันว่าโทเค็นคืออะไรและอยู่ตรงไหนในลำดับ- LLM สมัยใหม่มักข้าม position embedding และใช้ เทคนิคระบุตำแหน่งแบบสัมพัทธ์ เช่น RoPE
- Attention block: โปรเจ็กต์โทเค็นปัจจุบันเป็นเวกเตอร์สามตัวคือ Q (query), K (key), V (value)
- Query: "ฉันกำลังมองหาอะไร?", Key: "ฉันมีอะไรอยู่?", Value: "ถ้าถูกเลือก ฉันจะส่งอะไรให้?"
- ตัวอย่าง: ในคำว่า "emma" เมื่อ "m" ตัวที่สองกำลังทำนายโทเค็นถัดไป มันอาจเรียนรู้ query แบบ "มีสระอะไรเพิ่งมาก่อนหรือไม่?" และ "e" ก่อนหน้าจะตรงกับ query นี้ได้ดี จึงได้ค่าน้ำหนัก attention สูง
- Key และ value จะถูกเพิ่มเข้าไปใน KV cache เพื่อให้สามารถอ้างอิงตำแหน่งก่อนหน้าได้
- แต่ละ attention head จะคำนวณ ดอทโปรดักต์ ระหว่าง query กับ key ที่แคชไว้ทั้งหมด (สเกลด้วย √d_head) ใช้ softmax เพื่อหาน้ำหนัก attention แล้วคำนวณผลรวมถ่วงน้ำหนักของ value ที่แคชไว้
- เอาต์พุตของทุก head จะถูกนำมาต่อกันแล้วโปรเจ็กต์ผ่าน
attn_wo - Attention block คือ จุดเดียวที่โทเค็น ณ ตำแหน่ง t สามารถ "มองเห็น" โทเค็นในอดีต 0..t-1 ได้ โดย attention คือ กลไกการสื่อสารระหว่างโทเค็น
- MLP block: โครงข่ายฟีดฟอร์เวิร์ด 2 ชั้น: ขยายเป็น 4 เท่าของมิติ embedding → ใช้ ReLU → ย่อกลับลงมา
- เป็นจุดที่การ "คิด" รายตำแหน่งส่วนใหญ่เกิดขึ้น
- ต่างจาก attention ตรงที่เป็น การคำนวณเฉพาะที่ สำหรับเวลา t อย่างสมบูรณ์
- Transformer จะ สลับวาง การสื่อสาร (attention) กับการคำนวณ (MLP)
- Residual connection: ทั้ง attention และ MLP block จะนำเอาต์พุตกลับไปบวกกับอินพุตอีกครั้ง
- ทำให้กราเดียนต์ไหลผ่านเครือข่ายได้โดยตรง จึง ฝึกโมเดลที่ลึกได้
- Output: โปรเจ็กต์ hidden state สุดท้ายผ่าน
lm_headไปยังขนาด vocabulary เพื่อสร้าง logit หนึ่งค่าต่อโทเค็น (ที่นี่คือ 27 ค่า) โดย logit สูงหมายถึงโทเค็นนั้นมีโอกาสเป็นตัวถัดไปสูง - ลักษณะเฉพาะของ KV cache: ปกติแล้วการใช้ KV cache ระหว่างการฝึกพบไม่บ่อย แต่เพราะ microgpt ประมวลผลครั้งละหนึ่งโทเค็น จึงสร้างมันอย่างชัดเจน โดย key และ value ที่แคชไว้เป็น โหนด Value ที่ยังทำงานอยู่ ใน computation graph และถูกรวมอยู่ในการ backpropagation
- Embedding: token ID และ position ID ใช้อ้างอิงแถวจากตาราง embedding ของตัวเอง (
ลูปการฝึก
- ลูปการฝึกจะทำซ้ำดังนี้: (1) เลือกเอกสาร → (2) รันโมเดลแบบฟอร์เวิร์ดกับโทเค็น → (3) คำนวณ loss → (4) ทำ backpropagation เพื่อหา gradient → (5) อัปเดตพารามิเตอร์
-
การทำโทเค็นไนซ์
- ในแต่ละสเต็ปการฝึก จะเลือกเอกสารหนึ่งชิ้นแล้วครอบด้วย BOS ทั้งสองด้าน: "emma" →
[BOS, e, m, m, a, BOS] - เป้าหมายของโมเดลคือ ทำนายโทเค็นถัดไปแต่ละตัว เมื่อให้โทเค็นก่อนหน้า
- ในแต่ละสเต็ปการฝึก จะเลือกเอกสารหนึ่งชิ้นแล้วครอบด้วย BOS ทั้งสองด้าน: "emma" →
-
Forward pass และ loss
- ป้อนโทเค็นเข้าโมเดลทีละตัว พร้อมสร้าง KV cache ไปด้วย
- ในแต่ละตำแหน่ง โมเดลจะส่งออก logits 27 ค่า และแปลงเป็นความน่าจะเป็นด้วย softmax
- loss ของแต่ละตำแหน่งคือ ลบลอการิทึมของความน่าจะเป็น ของโทเค็นถัดไปที่ถูกต้อง: −log p(target) ซึ่งเรียกว่า cross-entropy loss
- loss วัดว่าโมเดล ประหลาดใจแค่ไหน กับสิ่งที่จะเกิดขึ้นจริง: ถ้ากำหนดความน่าจะเป็นเป็น 1.0 จะได้ loss เป็น 0 แต่ถ้าความน่าจะเป็นเข้าใกล้ 0 loss จะเข้าใกล้ +∞
- นำ loss ของทุกตำแหน่งในทั้งเอกสารมาเฉลี่ย เพื่อให้ได้ scalar loss ค่าเดียว
-
Backward pass
- การเรียก
loss.backward()เพียงครั้งเดียวจะรัน backpropagation กับ computation graph ทั้งหมด - หลังจากนั้น
.gradของแต่ละพารามิเตอร์จะบอกว่าควร เปลี่ยนอย่างไร เพื่อลด loss
- การเรียก
-
Adam optimizer
- แทนที่จะใช้การไล่ระดับอย่างง่าย (
p.data -= lr * p.grad) จะใช้ Adam - เก็บค่าเฉลี่ยเคลื่อนที่สองชุดต่อหนึ่งพารามิเตอร์:
m: ค่าเฉลี่ยของ gradient ล่าสุด (momentum)v: ค่าเฉลี่ยของกำลังสองของ gradient ล่าสุด (การปรับ learning rate รายพารามิเตอร์)
m_hat,v_hatคือค่า bias correction ของ m และ v ที่เริ่มต้นจากศูนย์- learning rate จะ ลดลงเชิงเส้น ระหว่างการฝึก
- หลังอัปเดตแล้วจะรีเซ็ตด้วย
.grad = 0
- แทนที่จะใช้การไล่ระดับอย่างง่าย (
-
ผลลัพธ์การฝึก
- ตลอด 1,000 สเต็ป loss ลดลงจากประมาณ 3.3 (เดาสุ่มจาก 27 โทเค็น: −log(1/27)≈3.3) เหลือประมาณ 2.37
- ยิ่งต่ำยิ่งดี และค่าต่ำสุดคือ 0 (ทำนายได้สมบูรณ์แบบ) ดังนั้นยังมีพื้นที่ให้ปรับปรุง แต่ก็ชัดเจนว่าโมเดลกำลัง เรียนรู้รูปแบบเชิงสถิติของชื่อ
การอนุมาน
- หลังฝึกเสร็จ สามารถ สุ่มชื่อใหม่ จากโมเดลได้ โดยตรึงพารามิเตอร์ไว้แล้วรัน forward pass แบบลูป พร้อมป้อนโทเค็นที่สร้างได้กลับเป็นอินพุตถัดไป
-
กระบวนการสุ่มตัวอย่าง
- เริ่มแต่ละตัวอย่างด้วยโทเค็น BOS ("เริ่มชื่อใหม่")
- โมเดลสร้าง logits 27 ค่า → แปลงเป็นความน่าจะเป็น → จากนั้น สุ่มหนึ่งโทเค็น ตามความน่าจะเป็นนั้น
- ป้อนโทเค็นนั้นกลับเป็นอินพุตถัดไป แล้วทำซ้ำจนกว่าโมเดลจะสร้าง BOS อีกครั้ง ("จบ") หรือถึงความยาวลำดับสูงสุด
-
Temperature
- ก่อนทำ softmax จะนำ logits ไปหารด้วยค่า temperature
- Temperature 1.0: สุ่มตรงจากการแจกแจงที่โมเดลเรียนรู้มา
- Temperature ต่ำ (เช่น 0.5): ทำให้การแจกแจงคมขึ้น จึงทำให้โมเดลมีแนวโน้มเลือกตัวเลือกอันดับบน ๆ แบบ ระมัดระวัง มากขึ้น
- Temperature ใกล้ 0: จะเลือกโทเค็นที่มีความน่าจะเป็นสูงสุดเพียงตัวเดียวเสมอ (greedy decoding)
- Temperature สูง: ทำให้การแจกแจงแบนลง จึงได้เอาต์พุตที่ หลากหลายกว่าแต่สอดคล้องกันน้อยลง
วิธีรัน
- ต้องใช้แค่ Python (ไม่ต้อง
pip install, ไม่มี dependency):python train.py - ใช้เวลาประมาณ 1 นาที บน MacBook
- พิมพ์ loss ในแต่ละสเต็ป: ลดจาก ~3.3 (สุ่ม) เหลือ ~2.37
- หลังฝึกเสร็จจะสร้าง ชื่อใหม่ที่โมเดลหลอนขึ้นมา เช่น "kamon", "ann", "karai"
- สามารถรันบน Google Colab notebook ได้ด้วย และถาม Gemini ได้
- ลองใช้ dataset อื่น เพิ่ม
num_stepsเพื่อฝึกให้นานขึ้น หรือเพิ่มขนาดโมเดลเพื่อให้ได้ผลลัพธ์ที่ดีขึ้น
ลำดับขั้นของโค้ด
| ไฟล์ | สิ่งที่เพิ่มเข้ามา |
|---|---|
train0.py |
ตารางนับความถี่แบบ bigram — ไม่มี neural network, ไม่มี gradient |
train1.py |
MLP + gradient แบบเขียนเอง (เชิงตัวเลขและเชิงวิเคราะห์) + SGD |
train2.py |
Autograd (คลาส Value) — แทนที่ gradient แบบเขียนเอง |
train3.py |
position embedding + single-head attention + rmsnorm + residual |
train4.py |
multi-head attention + layer loop — สถาปัตยกรรม GPT แบบเต็ม |
train5.py |
Adam optimizer — นี่คือ train.py |
- สามารถดูทุกเวอร์ชันและ diff ระหว่างแต่ละสเต็ปได้ใน Revisions ของ Gist
build_microgpt.py
ความแตกต่างจาก LLM ระดับโปรดักชัน
- microgpt มี แก่นเชิงอัลกอริทึมที่สมบูรณ์ ของการฝึกและการรัน GPT โดยความแตกต่างจาก LLM ระดับโปรดักชันอย่าง ChatGPT ไม่ได้เปลี่ยนอัลกอริทึมหลัก แต่เป็น องค์ประกอบที่ทำให้มันทำงานได้ในระดับสเกล
-
ข้อมูล
- แทนที่จะฝึกด้วยชื่อสั้น ๆ 32K รายการ จะฝึกด้วย โทเค็นข้อความจากอินเทอร์เน็ตนับล้านล้านรายการ (เว็บเพจ หนังสือ โค้ด ฯลฯ)
- มีการลบข้อมูลซ้ำ กรองคุณภาพ และผสมข้อมูลจากหลายโดเมนอย่างระมัดระวัง
-
ตัวตัดโทเค็น
- ใช้ตัวตัดโทเค็นแบบซับเวิร์ด เช่น BPE (Byte Pair Encoding) แทนการใช้ตัวอักษรเดี่ยว
- รวมลำดับอักขระที่มักปรากฏร่วมกันให้เป็นโทเค็นเดียว คำทั่วไปอย่าง "the" จึงเป็นโทเค็นเดียว ส่วนคำที่พบไม่บ่อยมักถูกแยกเป็นชิ้น ๆ
- มีคลังคำราว ~100K โทเค็น เห็นเนื้อหาได้มากขึ้นในแต่ละตำแหน่ง จึง มีประสิทธิภาพกว่ามาก
-
Autograd
- แทนที่จะใช้อ็อบเจ็กต์ Value แบบสเกลาร์ใน Python ล้วน จะใช้ เทนเซอร์ (อาร์เรย์ตัวเลขหลายมิติขนาดใหญ่) และรันบน GPU/TPU ที่ประมวลผลทศนิยมลอยตัวได้หลายพันล้านครั้งต่อวินาที
- PyTorch จัดการ autograd สำหรับเทนเซอร์ และ CUDA kernel อย่าง FlashAttention จะรวมหลายโอเปอเรชันเข้าด้วยกัน
- คณิตศาสตร์เหมือนเดิม แต่มีสเกลาร์จำนวนมากที่ถูก ประมวลผลแบบขนาน
-
สถาปัตยกรรม
- microgpt: พารามิเตอร์ 4,192 ตัว, โมเดลระดับ GPT-4: หลายแสนล้านตัว
- โดยรวมยังเป็นโครงข่ายประสาทแบบ Transformer ที่คล้ายกันมาก แต่กว้างกว่ามาก (มิติ embedding 10,000+) และลึกกว่ามาก (100+ เลเยอร์)
- มีการเพิ่มประเภทของบล็อกเลโก้และเปลี่ยนลำดับ เช่น:
- RoPE (rotary positional embedding) — แทน learned positional embedding
- GQA (grouped-query attention) — ลดขนาด KV cache
- gated linear activation — แทน ReLU
- เลเยอร์ MoE (mixture of experts)
- แต่ โครงสร้างหลัก ที่ attention (การสื่อสาร) และ MLP (การคำนวณ) สลับกันอยู่บน residual stream ยังคงได้รับการรักษาไว้อย่างดี
-
การฝึก
- แทนที่จะใช้เอกสารหนึ่งชิ้นต่อสเต็ป จะใช้ แบตช์ขนาดใหญ่ (หลายล้านโทเค็นต่อสเต็ป), gradient accumulation, mixed precision (float16/bfloat16) และการจูนไฮเปอร์พารามิเตอร์อย่างระมัดระวัง
- การฝึกโมเดล frontier ต้องใช้ GPU หลายพันตัวเป็นเวลาหลายเดือน
-
การปรับให้เหมาะสม
- microgpt: Adam + การลดอัตราการเรียนรู้เชิงเส้นแบบง่าย
- ในสเกลใหญ่ การทำ optimization เป็น ศาสตร์เฉพาะทางของตัวเอง: ใช้ความแม่นยำที่ลดลง (bfloat16, fp8), ฝึกบน GPU cluster ขนาดใหญ่
- ต้องจูนการตั้งค่าของ optimizer (learning rate, weight decay, beta parameter, ตาราง warmup/decay) อย่างละเอียด โดยค่าที่เหมาะสมขึ้นอยู่กับขนาดโมเดล ขนาดแบตช์ และองค์ประกอบของชุดข้อมูล
- scaling law (เช่น Chinchilla) ช่วยชี้นำว่าควรจัดสรรคอมพิวต์งบประมาณคงที่อย่างไรระหว่างขนาดโมเดลกับจำนวนโทเค็นที่ใช้ฝึก
- หากจัดการรายละเอียดเหล่านี้ผิดพลาดในสเกลใหญ่ อาจ สิ้นเปลืองคอมพิวต์มูลค่าหลายล้านดอลลาร์ ได้ ทีมต่าง ๆ จึงทำการทดลองขนาดเล็กอย่างกว้างขวางก่อนเริ่มรันการฝึกเต็มรูปแบบ
-
การฝึกหลังการฝึก (Post-training)
- โมเดลพื้นฐานที่ได้จากการฝึก (โมเดล "pretrained") เป็น ตัวเติมเอกสารให้สมบูรณ์ ไม่ใช่แชตบอต
- กระบวนการทำให้กลายเป็น ChatGPT มี 2 ขั้นตอน:
- SFT (supervised fine-tuning): แทนที่เอกสารด้วยบทสนทนาที่ผ่านการคัดสรร แล้วฝึกต่อ โดยไม่มีการเปลี่ยนแปลงเชิงอัลกอริทึม
- RL (reinforcement learning): โมเดลสร้างคำตอบ → ให้คะแนน (โดยมนุษย์, โมเดล "ผู้ตัดสิน", หรืออัลกอริทึม) → เรียนรู้จากฟีดแบ็ก
- โดยพื้นฐานแล้วมันยังคงเรียนรู้จากเอกสาร เพียงแต่ตอนนี้เอกสารถูก ประกอบขึ้นจากโทเค็นที่โมเดลสร้างเอง
-
การอนุมาน
- การเสิร์ฟโมเดลให้ผู้ใช้นับล้านต้องมี engineering stack ของตัวเอง: การ batch คำขอ, การจัดการและ paging ของ KV cache (เช่น vLLM), speculative decoding เพื่อความเร็ว, quantization เพื่อลดหน่วยความจำ (รันด้วย int8/int4), และการกระจายโมเดลข้าม GPU หลายตัว
- โดยพื้นฐานแล้วยังคงเป็นการทำนายโทเค็นถัดไปของลำดับ แต่มีความพยายามด้านวิศวกรรมจำนวนมากเพื่อ ทำให้มันเร็วขึ้น
FAQ
-
โมเดล "เข้าใจ" อะไรบางอย่างหรือไม่?
- เป็นคำถามเชิงปรัชญา แต่ในเชิงกลไก: ไม่มีเวทมนตร์อะไรเกิดขึ้น
- โมเดลคือ ฟังก์ชันทางคณิตศาสตร์ขนาดใหญ่ ที่แมปโทเค็นอินพุตไปเป็นการแจกแจงความน่าจะเป็นของโทเค็นถัดไป
- ระหว่างการฝึก พารามิเตอร์จะถูกปรับเพื่อทำให้โทเค็นถัดไปที่ถูกต้องมีความน่าจะเป็นสูงขึ้น
- จะถือว่านั่นคือ "ความเข้าใจ" หรือไม่ขึ้นอยู่กับแต่ละคน แต่ กลไกทั้งหมดอยู่ครบใน 200 บรรทัดข้างต้น
-
ทำไมมันถึงใช้งานได้?
- โมเดลมีพารามิเตอร์ที่ปรับได้หลายพันตัว และ optimizer จะค่อย ๆ ขยับมันในแต่ละสเต็ปเพื่อลด loss
- เมื่อผ่านไปหลายสเต็ป พารามิเตอร์จะคงตัวที่ค่าซึ่ง จับความสม่ำเสมอทางสถิติของข้อมูลได้
- ในกรณีของชื่อ: มักขึ้นต้นด้วยพยัญชนะ, "qu" มักปรากฏคู่กัน, พยัญชนะ 3 ตัวติดกันพบได้น้อย ฯลฯ
- โมเดลไม่ได้เรียนรู้เป็นกฎแบบชัดแจ้ง แต่เรียนรู้เป็น การแจกแจงความน่าจะเป็น ที่สะท้อนสิ่งเหล่านี้
-
มันเกี่ยวข้องกับ ChatGPT อย่างไร?
- ChatGPT คือการนำลูปแกนหลักเดียวกันนี้ (การทำนายโทเค็นถัดไป, การสุ่มตัวอย่าง, การทำซ้ำ) ไป ขยายให้ใหญ่ขึ้นมหาศาล และเพิ่ม post-training เพื่อให้โต้ตอบได้
- เวลาแชต system prompt, ข้อความผู้ใช้ และคำตอบ ล้วนเป็นเพียง โทเค็นในลำดับ
- โมเดลเติมเอกสารให้สมบูรณ์ทีละโทเค็น แบบเดียวกับที่ microgpt เติมชื่อให้สมบูรณ์
-
"ภาพหลอน" คืออะไร?
- โมเดลสร้างโทเค็นโดยการสุ่มจากการแจกแจงความน่าจะเป็น
- มัน ไม่มีแนวคิดเรื่องความจริง และรู้เพียงว่าลำดับใดดูน่าเป็นไปได้ในเชิงสถิติเมื่อเทียบกับข้อมูลฝึก
- การที่ microgpt "หลอน" ชื่ออย่าง "karia" ก็เป็น ปรากฏการณ์เดียวกัน กับที่ ChatGPT พูดข้อเท็จจริงผิดอย่างมั่นใจ
- ทั้งสองอย่างคือการเติมต่อที่ ฟังดูน่าเชื่อถือแต่ไม่ใช่เรื่องจริง
-
ทำไมมันช้าขนาดนี้?
- microgpt ประมวลผล ทีละสเกลาร์ ใน Python ล้วน และหนึ่งสเต็ปการฝึกใช้เวลาหลายวินาที
- บน GPU คณิตศาสตร์เดียวกันนี้จะ ประมวลผลสเกลาร์นับล้านแบบขนาน ทำให้เร็วขึ้นหลายลำดับขั้น
-
ทำให้มันสร้างชื่อที่ดีกว่านี้ได้ไหม?
- ได้: ฝึกนานขึ้น (เพิ่ม
num_steps), เพิ่มขนาดโมเดล (n_embd,n_layer,n_head), ใช้ชุดข้อมูลที่ใหญ่ขึ้น - สิ่งเหล่านี้คือ ปุ่มปรับเดียวกันที่สำคัญในสเกลใหญ่
- ได้: ฝึกนานขึ้น (เพิ่ม
-
ถ้าเปลี่ยนชุดข้อมูลล่ะ?
- โมเดลจะเรียนรู้ แพตเทิร์นใดก็ตามที่อยู่ในข้อมูล
- ถ้าแทนที่ด้วยชื่อเมือง ชื่อโปเกมอน คำภาษาอังกฤษ หรือไฟล์บทกวีสั้น ๆ มันก็จะเรียนรู้ให้ สร้างสิ่งเหล่านั้นแทน
- โค้ดส่วนที่เหลือไม่ต้องเปลี่ยน
1 ความคิดเห็น
ขอบคุณสำหรับบทความดี ๆ ครับ