ข้ามไปยังเนื้อหาหลัก

[LLM 3/10] RLHF และ PPO: เทรนโมเดลด้วยรางวัลที่หาอนุพันธ์ไม่ได้

· อ่าน 11 นาที
Kobkrit Viriyayudhakorn
CEO, iApp Technology

บทที่ 2 เราสอนโมเดลด้วยการ "เลียนแบบเฉลย" ทีละ token แต่คุณสมบัติที่ทำให้ผู้ช่วย AI ใช้งานได้จริง — ตอบถูก สุภาพ ไม่มั่ว ไม่หลุดเป็นภาษาอังกฤษ — ไม่มีเฉลยให้เลียนแบบ และเขียนเป็น loss function ตรง ๆ ไม่ได้ บทนี้คือคำตอบแบบดั้งเดิมที่สุดของปัญหานั้น: RLHF (Reinforcement Learning from Human Feedback) ด้วย PPO เราจะเทรน reward model จริงจากคู่ preference ภาษาไทย แล้วเขียนลูป PPO เองจากศูนย์ราว 120 บรรทัด และปิดท้ายด้วยการทดลองที่ผมชอบที่สุดในซีรีส์: ถอดสายจูง KL ออก แล้วดูโมเดลโกงรางวัลกันสด ๆ นี่คือบทที่หนักที่สุดของซีรีส์โดยตั้งใจ เพราะบทที่ 4 (DPO) และบทที่ 5 (GRPO) ต่างก็เริ่มจากสมการของบทนี้ แล้วเลือก "ลบ" ชิ้นส่วนออกคนละชิ้น

Open in Colab03_rlhf_ppo.ipynb

1. ปัญหา (Problem statement)

SFT ในบทที่ 2 มีสมมติฐานซ่อนอยู่หนึ่งข้อ: ต้องมีเฉลยให้เลียนแบบ แต่ลองนึกถึงสิ่งที่เราอยากได้จริง ๆ เช่น "ตอบโจทย์เลขให้ถูก และอธิบายเป็นภาษาไทยที่อ่านรู้เรื่อง" ประโยคนี้ไม่มีเฉลยเดียว คำตอบที่ดีมีได้ร้อยแบบ และคำว่า "อ่านรู้เรื่อง" ก็เขียนเป็นสมการไม่ออก

พอพยายามจะ optimize สิ่งเหล่านี้ตรง ๆ เราจะชนกำแพงสองชั้นเสมอ:

กำแพงที่หนึ่ง — คุณภาพเขียนเป็น loss ไม่ได้ "ดีกว่า" นิยามเป็นฟังก์ชันไม่ได้ แต่มนุษย์เปรียบเทียบได้เก่งมาก ให้ดูคำตอบสองอันแล้วชี้ว่าชอบอันไหน ทำได้ทันทีและตรงกันพอสมควร ข้อมูลที่เก็บได้จริงจึงเป็นสามสิ่ง: prompt xx, คำตอบที่ถูกเลือก ywy_w, คำตอบที่ถูกปัด yly_l

กำแพงที่สอง — ต่อให้มีคะแนน ก็ backprop ไม่ได้ สมมติมีฟังก์ชันวิเศษ r(x,y)r(x,y) ให้คะแนนทุกคำตอบ คุณก็ยังเทรนแบบ supervised ไม่ได้อยู่ดี เพราะคำตอบ yy เกิดจากการสุ่ม token ทีละตัว คะแนนมาถึงหลังการสุ่มจบแล้ว และอนุพันธ์เดินทางย้อนผ่านการสุ่มไม่ได้ — เส้นทางจาก rr กลับไปหา weights θ\theta ขาดตอนตรงนั้นพอดี

ทางที่อยากเดินติดกำแพงอะไร
เขียน loss ของ "คำตอบที่ดี" ตรง ๆนิยาม "ดี" เป็นสมการไม่ได้ มีแต่การเปรียบเทียบ
ให้คนให้คะแนน แล้ว backpropคะแนนอยู่หลังการสุ่ม token — gradient เดินผ่านการสุ่มไม่ได้
ให้คนนั่งให้คะแนนสดระหว่างเทรนมนุษย์ให้คะแนนไม่ทันแม้แต่เสี้ยวเดียวของ rollout

นี่คือที่มาของชื่อบท: เรากำลังจะ optimize รางวัลที่หาอนุพันธ์ไม่ได้ เครื่องมือที่ทำแบบนั้นได้ชื่อว่า reinforcement learning

2. เราจะทำอะไร (Solution)

RLHF แก้กำแพงทั้งสองชั้นด้วยการเดินสองขั้น:

  • Stage A — Reward Model: เทรนโมเดล rϕ(x,y)r_\phi(x,y) ให้เลียนแบบการเปรียบเทียบของมนุษย์จากคู่ preference (แก้กำแพงที่หนึ่ง และแทนมนุษย์ที่ให้คะแนนไม่ทัน)
  • Stage B — PPO: ใช้ policy-gradient RL ดัน policy ไปหาคะแนนของ rϕr_\phi โดยไม่ต้องหาอนุพันธ์ผ่านการสุ่ม (แก้กำแพงที่สอง) พร้อมสายจูง KL รั้งไม่ให้วิ่งหนีโมเดลตั้งต้น

ราคาที่จ่ายคือความซับซ้อน: ระหว่างเทรนจะมีโมเดลสี่ตัวอยู่ใน VRAM พร้อมกัน — policy πθ\pi_\theta (ตัวที่เทรน), reference πref\pi_{\text{ref}} (ตัวตั้งต้นที่แช่แข็ง), reward model rϕr_\phi และ value network VψV_\psi ที่ยังไม่ได้แนะนำตัว (รอหัวข้อ 3.4)

แนวคิดหลักของบทนี้

RLHF คือการ optimize รางวัลที่คุณหาอนุพันธ์ไม่ได้ ผ่านตัวแทนที่ไม่สมบูรณ์ (reward model) และการ optimize ตัวแทนแรง ๆ โดยไม่มีอะไรรั้ง จะพังตาม Goodhart's law เสมอ: เมื่อตัวชี้วัดกลายเป็นเป้าหมาย มันจะเลิกเป็นตัวชี้วัดที่ดี

พจน์ KL ในสมการ 3.2 จึงไม่ใช่ regularizer ที่ใส่ไว้กันเหนียว — มันคือสิ่งเดียวที่ขวางอยู่ระหว่างคุณกับ reward hacking หัวข้อ 8 จะพิสูจน์ประโยคนี้ด้วยการถอดมันออกให้ดูกับตา

และอีกประโยคที่อยากให้ถือไว้ทั้งซีรีส์: สมการ objective ของบทนี้คือสมการแม่ของครึ่งหลังของซีรีส์ บทที่ 4 (DPO) แก้สมการนี้ในรูปปิดจน reward model และ RL loop ตัดกันหายไป บทที่ 5 (GRPO) เปลี่ยนวิธีประมาณ advantage จน value network หายไป เข้าใจบทนี้บทเดียว อีกสองบทจะกลายเป็น "การลบชิ้นส่วน" ที่อ่านออกทันที

3. สมการ (Equation)

3.1 Reward model: Bradley–Terry

Stage A เทรน rϕr_\phi ด้วย loss เดียวสั้น ๆ:

LRM(ϕ)=E(x,yw,yl)D[logσ(rϕ(x,yw)rϕ(x,yl))]\mathcal{L}_{\text{RM}}(\phi) = -\mathbb{E}_{(x,y_w,y_l)\sim\mathcal{D}}\Big[\log\sigma\big(r_\phi(x,y_w) - r_\phi(x,y_l)\big)\Big]
  • rϕ(x,y)r_\phi(x,y) = คะแนนสเกลาร์หนึ่งตัวต่อหนึ่งข้อความ — ในทางปฏิบัติคือโมเดลภาษาที่เปลี่ยนหัวเป็น linear ชั้นเดียว (num_labels=1)
  • σ\sigma = sigmoid แปลงผลต่างคะแนนเป็นความน่าจะเป็นที่มนุษย์จะเลือก ywy_w (โมเดล Bradley–Terry)
  • ยิ่งผลต่าง rϕ(x,yw)rϕ(x,yl)r_\phi(x,y_w) - r_\phi(x,y_l) กว้าง loss ยิ่งต่ำ

จุดที่คนมองข้ามแล้วไปเจ็บตัวทีหลัง: loss นี้เห็นแค่ผลต่างของคะแนน ลองแทน rϕr_\phi ด้วย rϕ+cr_\phi + c ด้วยค่าคงที่ cc ใดก็ได้ — loss ไม่เปลี่ยนเลย แปลว่าสเกลสัมบูรณ์ของ reward model ไม่มีความหมายและไม่ถูกกำหนดโดยการเทรน รันสองครั้งอาจได้คะแนนเฉลี่ย 3.7 กับ −12.4 ที่จัดอันดับเหมือนกันเป๊ะ นี่คือเหตุผลที่ต้อง standardize reward ก่อนป้อนเข้า PPO เสมอ (ลบ mean หารด้วย std) — จำจุดนี้ไว้ มันจะกลับมาในหัวข้อ 7 และ 9

3.2 สมการแม่: RLHF objective

ถ้าทั้งซีรีส์นี้จะท่องจำได้สมการเดียว จงจำสมการนี้:

maxθ ExD,yπθ(x)[rϕ(x,y)]    βDKL(πθ(x)πref(x))\max_\theta\ \mathbb{E}_{x\sim\mathcal{D},\,y\sim\pi_\theta(\cdot|x)}\big[r_\phi(x,y)\big] \;-\; \beta\,\mathbb{D}_{\text{KL}}\big(\pi_\theta(\cdot|x)\,\|\,\pi_{\text{ref}}(\cdot|x)\big)

อ่านเป็นภาษาคน: "เก็บคะแนน reward ให้มากที่สุด แต่ทุกก้าวที่เดินห่างจากโมเดลตั้งต้น ต้องจ่ายค่าปรับ"

  • πθ\pi_\theta = policy คือโมเดลที่กำลังเทรน — สังเกตว่า yy ถูกสุ่มจาก πθ\pi_\theta เอง นี่คือความต่างเชิงโครงสร้างจาก SFT ที่เรียนจากข้อมูลนิ่ง ๆ ในไฟล์
  • πref\pi_{\text{ref}} = reference คือโมเดลตั้งต้น (โมเดลหลัง SFT จากบทที่ 2) แช่แข็งตลอดการเทรน
  • β\beta = ราคาต่อหนึ่ง nat ของการเดินห่าง — ความตึงของสายจูง
  • DKL\mathbb{D}_{\text{KL}} = ระยะห่างเชิงการกระจายระหว่าง policy กับ reference
ทำไมสมการนี้คือสมการแม่ของครึ่งหลังของซีรีส์

บทที่ 4 (DPO) จะพิสูจน์ว่าสมการนี้มีคำตอบในรูปปิด แล้วพลิกกลับด้านจน rϕr_\phi และ RL loop หายไปทั้งคู่ บทที่ 5 (GRPO) จะเก็บโครง RL ไว้ แต่เปลี่ยนวิธีคำนวณ advantage จน VψV_\psi หายไป ทั้งสองบทไม่ได้เสนอ objective ใหม่ — พวกมันแก้สมการเดียวกันนี้ด้วยเครื่องมือที่ต่างกัน

3.3 PPO clipped surrogate: เครื่องยนต์ของ Stage B

policy gradient แบบดิบ (REINFORCE) ใช้ rollout หนึ่งชุดอัปเดตได้ครั้งเดียวแล้วต้องทิ้ง ซึ่งแพงมากเพราะการ generate คือคอขวด PPO อยากรีดค่า rollout ชุดเดิมหลาย epoch จึงต้องมีตัวคูณแก้ทาง (importance sampling ratio):

ρt=πθ(atst)πθold(atst)\rho_t = \frac{\pi_\theta(a_t \mid s_t)}{\pi_{\theta_{\text{old}}}(a_t \mid s_t)}
  • sts_t = สถานะ ณ ตำแหน่ง tt คือ prompt บวก token ที่สุ่มมาแล้วทั้งหมด
  • ata_t = "การกระทำ" คือ token ตัวถัดไปที่ถูกสุ่มไปแล้วตอน rollout
  • πθold\pi_{\theta_{\text{old}}} = snapshot ของ policy ณ ตอน rollout — ค่านี้ถูกคำนวณครั้งเดียวแล้วแช่แข็ง

แล้วหนีบ ρt\rho_t ไว้ด้วย clip:

LCLIP(θ)=Et[min(ρtA^t, clip(ρt,1ϵ,1+ϵ)A^t)]\mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E}_t\Big[\min\big(\rho_t\,\hat A_t,\ \text{clip}(\rho_t,\,1-\epsilon,\,1+\epsilon)\,\hat A_t\big)\Big]
  • A^t\hat A_t = advantage คือ "token นี้ดีกว่าที่คาดไว้เท่าไหร่" (นิยามในข้อถัดไป)
  • ϵ\epsilon = ความกว้างของ trust region (ค่ามาตรฐาน 0.2)

หัวใจอยู่ที่ min + clip ทำงานร่วมกันแบบมองโลกแง่ร้ายอย่างจงใจ: ถ้า A^t\hat A_t เป็นบวก (token ดี) ผลตอบแทนจากการดัน ρt\rho_t ถูกตัดเพดานที่ 1+ϵ1+\epsilon — ดันเกินนั้นไม่ได้อะไรเพิ่ม gradient เป็นศูนย์ แต่ถ้า A^t\hat A_t เป็นลบ (token แย่) ตัว min จะเลือกฝั่งที่แย่กว่าเสมอ — ค่าปรับไม่มีเพดาน สรุปหนึ่งประโยค: ได้จำกัด เสียไม่จำกัด นโยบายจึงขยับทีละก้าวเล็ก ๆ ใกล้ ๆ ตัวเดิม

อย่าสับสน: มี "โมเดลเก่า" สองตัว และมันคนละตัวกัน

πref\pi_{\text{ref}} ในสมการ 3.2 แช่แข็งตลอดการเทรน ทำหน้าที่สายจูง KL πθold\pi_{\theta_{\text{old}}} ในสมการ 3.3 คือ snapshot ณ ตอน rollout ล่าสุด เปลี่ยนทุกรอบ ทำหน้าที่ trust region บั๊กยอดฮิตอันดับหนึ่งของคนเขียน PPO เองคือจับสองตัวนี้ใส่ตัวแปรเดียวกัน

3.4 GAE: คำนวณ advantage อย่างไรไม่ให้จมน้ำเสียง noise

advantage สร้างจาก TD error ของ value network VψV_\psi:

δt=rt+γVψ(st+1)Vψ(st)\delta_t = r_t + \gamma V_\psi(s_{t+1}) - V_\psi(s_t) A^t=l=0(γλ)lδt+l\hat A_t = \sum_{l=0}^{\infty} (\gamma\lambda)^l\,\delta_{t+l}
  • Vψ(st)V_\psi(s_t) = value network ทำนายว่า "จากจุดนี้ไปจนจบ จะเก็บ reward ได้อีกเท่าไหร่" — นี่คือโมเดลตัวที่สี่
  • rtr_t = reward ต่อ token (ในงานของเรา: ค่าปรับ KL ทุกตำแหน่ง บวกคะแนนงานที่ token สุดท้าย)
  • γ\gamma = discount factor (งาน LLM มักใช้ 1.0)
  • λ\lambda = ปุ่มหมุน bias–variance: λ=0\lambda = 0 เชื่อ VψV_\psi สุดใจ (bias สูงถ้า VψV_\psi ทำนายพลาด), λ=1\lambda = 1 ไม่เชื่อเลยและรอดูผลจริงจนจบ (variance สูงเพราะแบก noise ทั้งสาย), ค่าที่นิยมใช้คือ 0.95
จำ V_ψ ตัวนี้ไว้ให้ดี — มันคือตัวที่ GRPO จะฆ่าทิ้ง

VψV_\psi เป็นโมเดลขนาดเท่า ๆ policy ที่ต้องเทรนไปพร้อมกันด้วย loss ของมันเอง ถ้า VψV_\psi ทำนายมั่ว advantage ก็มั่ว แล้ว policy ก็เรียนจากสัญญาณมั่ว — จุดพังคลาสสิกของ PPO บทที่ 5 จะตอบคำถามว่า "ถ้าแทน VψV_\psi ด้วยค่าเฉลี่ยของกลุ่มคำตอบที่สุ่มจาก prompt เดียวกันล่ะ?" นั่นแหละคือ GRPO ทั้งอัลกอริทึม — ลบโมเดลตัวที่สี่ทิ้งด้วยค่าเฉลี่ยตัวเดียว

3.5 Loss เต็มของ PPO: สามพจน์ สองโมเดล

รวมทุกชิ้นเป็น loss เดียวที่ optimizer เห็นจริง (เขียนในรูป minimize):

LPPO=LCLIP  +  c1Et[(Vψ(st)R^t)2]    c2Et[H[πθ(st)]]\mathcal{L}_{\text{PPO}} = -\mathcal{L}^{\text{CLIP}} \;+\; c_1\,\mathbb{E}_t\Big[\big(V_\psi(s_t) - \hat R_t\big)^2\Big] \;-\; c_2\,\mathbb{E}_t\Big[\mathcal{H}\big[\pi_\theta(\cdot \mid s_t)\big]\Big]
  • พจน์แรก = clipped surrogate จากข้อ 3.3 (ติดลบเพราะเราต้องการ maximize)
  • พจน์ที่สอง = value loss สอน VψV_\psi ให้ทายเข้าใกล้ return จริง R^t\hat R_t, c1c_1 มักเป็น 0.5
  • พจน์ที่สาม = entropy bonus H\mathcal{H} กันการกระจายยุบเร็วเกินไป, c2c_2 มักเป็น 0.01
  • ส่วนสายจูง KL ของสมการ 3.2 ในทางปฏิบัตินิยมยัดเข้าไปใน reward ต่อ token: rtrtβ(logπθlogπref)r_t \leftarrow r_t - \beta\,(\log\pi_\theta - \log\pi_{\text{ref}}) ซึ่งเป็นวิธีที่เราใช้ในหัวข้อ 7

นับของเล่นทั้งหมดที่ต้องจูน: โมเดล 4 ตัว บวก ϵ,β,γ,λ,c1,c2\epsilon, \beta, \gamma, \lambda, c_1, c_2 และ learning rate อีกสองชุด นี่คือเหตุผลที่ PPO ขึ้นชื่อว่า "รันสองรอบด้วย seed ต่างกัน ได้ผลคนละเรื่อง" และเป็นเหตุผลการมีอยู่ของบทที่ 4 ทั้งบท

เนื้อหาเต็มอยู่ในคอร์ส

บทความนี้คือประมาณ 30% แรกของบทเรียน — ส่วนที่เหลือ (การเตรียมสภาพแวดล้อม, การเตรียมข้อมูล, โค้ดหลัก, ผลลัพธ์จริง และบทสรุป) อยู่ในคอร์ส LLM Finetuning ซึ่งเรียนฟรี เพียงเข้าสู่ระบบด้วย Google

อ่านเนื้อหาเต็มในคอร์ส →