Lecture4 (deep_learning_part2_1.pdf, 35 หน้า) · 2603520 Deep Learning

Deep Neural Network: จาก 2 Layer สู่ L Layer, และ Optimizer ที่เร็วกว่า Gradient Descent

บทนี้ทำ 2 อย่าง: (1) ยืนยันว่าสูตร forward/backprop จาก Lesson 2–3 ใช้ได้กับเครือข่ายกี่ layer ก็ได้ แค่โซ่ chain rule ยาวขึ้น (2) พาไปทีละหน้าสไลด์ตั้งแต่ "ทำไม deep network ถึงเทรนยาก" → Batch/SGD/Mini-batch → Momentum → RMSProp/Adam/AdamW → วิธีกัน overfitting (regularization, dropout, early stopping, data augmentation). ทุกสูตรมีตัวอย่างตัวเลขทีละขั้นที่คุณคำนวณตามมือได้ และทุกโค้ดบนสไลด์มีคำอธิบายบรรทัดต่อบรรทัด.

อ่านเพิ่มก่อนเริ่ม (แนะนำ)
Sebastian Ruder, An overview of gradient descent optimization algorithms — ลิงก์นี้อยู่มุมล่างของสไลด์หน้า 6 เอง ("More optimizers ->") ครอบคลุม Momentum, RMSProp, Adam ทุกสูตรที่เรียนในบทนี้.
ภาพรวมก่อนเริ่ม — เรื่องเล่าของทั้งบท

อาจารย์เล่าเป็นลำดับตรรกะแบบนี้ ให้จับเรื่องเล่านี้ให้ได้ก่อน แล้วสูตรจะตามมาเอง:

  1. หน้า 2–4: deep network ไม่ใช่ของใหม่ — สูตรเดิมจาก Lesson 2–3 ทุกตัว แค่เพิ่มจำนวน layer
  2. หน้า 5: แต่พอลึกแล้วเกิดปัญหา 4 ข้อ (เทรนช้า, overfit, vanishing/exploding gradient, hyperparameter เยอะ)
  3. หน้า 6–25: แก้ปัญหา "เทรนช้า" ด้วย optimizer ที่เร็วกว่า — ไล่จาก Mini-Batch → Momentum → RMSProp → Adam → AdamW
  4. หน้า 26–34: แก้ปัญหา "overfit" ด้วย regularization, dropout, early stopping, data augmentation
  5. หน้า 35: การบ้าน — เอา ReLU + dropout ไปลองกับ Circle Dataset

1. Deep Neural Network: สูตรเดิม แค่ L Layer

ปัญหาที่คุณกำลังจะเจอ: Lesson 2–3 เราทำงานกับเครือข่ายที่มี hidden layer เดียว เขียนสูตรได้เพราะมี layer ให้ไล่แค่ 2–3 ชั้น. พอมี 20 ชั้น คุณจะเขียนสูตรยังไง? คำตอบของอาจารย์คือ "ไม่ต้องเขียนใหม่" — เปลี่ยนเลขชั้นตายตัว (1, 2) เป็นตัวแปร \(l\) แล้วสูตรเดิมใช้ได้หมด. สามหน้าแรกของบทนี้คือการยืนยันข้อนั้น.

Slide defining a deep neural network as a neural network with many hidden layers, with a fully connected network diagram from inputs x1 x2 through Layer 1, Layer 2, ... Layer L-2, Layer L-1 to the single output node h(x)
Slide: Deep Neural Network — นิยาม (Lecture4.pdf, p.2)

อาจารย์นิยามสั้นมากและทำสีไว้ทั้งสองคำ: Deep neural network คือ neural network ที่มี many hidden layers — จบแค่นั้น ไม่มีเงื่อนไขอื่น. ไม่มีเกณฑ์ว่า "ต้องกี่ชั้นถึงเรียกว่า deep" ในสไลด์ สิ่งที่อาจารย์เน้นคือเหตุผลที่ซ้อนหลายชั้น: ชั้นที่ซ้อนกันทำให้โมเดลเรียน "high-level representations" จากข้อมูล เพื่อรับมืองานที่ซับซ้อน. แปลเป็นภาษาคน: layer แรก ๆ เรียนของง่าย ๆ (เส้น ขอบ) layer ถัดไปเอาของง่ายมาประกอบเป็นของยาก (รูปทรง หน้าคน) — แต่ละชั้นใช้ผลของชั้นก่อนเป็นวัตถุดิบ. โครงข่ายในภาพยังเป็น Fully Connected Neural Network เหมือนเดิม คือทุก node ในชั้นหนึ่งต่อกับทุก node ในชั้นถัดไป และมี input \(x_1, x_2\) กับ output \(h_\theta(x)\) ตัวเดียวเท่านั้น — ความ "ลึก" อยู่ตรงกลาง ไม่ได้เปลี่ยนหน้าตาของ input/output.

1.1 Forward: คำนวณจาก layer 1 ถึง layer L (หน้า 3)

Slide How Deep Neural Network Works: model representation diagram plus the layer-1 computation z1[1]=theta10[1]+theta11[1]x1+theta12[1]x2 and a1[1]=g(z1[1]), the layer-L computation z1[L]=theta10[L]+theta11[L]a1[L-1]+theta12[L]a2[L-1] and h(x)=a1[L]=g(z1[L]), and the two cost functions for regression and binary classification
Slide: How Deep Neural Network Works — Model Representation + Cost Function (Lecture4.pdf, p.3)

หน้านี้อาจารย์วาง 3 ก้อนไว้ให้เทียบกัน: Model Representation, Computation on layer 1 กับ Computation on layer L, และ Cost Function. สองสมการกลางคือหัวใจ เพราะมันหน้าตาเหมือนกันเป๊ะ ต่างแค่ว่าอะไรเป็น "ขาเข้า":

$$z_1^{[1]} = \theta_{10}^{[1]} + \theta_{11}^{[1]}x_1 + \theta_{12}^{[1]}x_2,\qquad a_1^{[1]} = g\!\left(z_1^{[1]}\right)$$ $$z_1^{[L]} = \theta_{10}^{[L]} + \theta_{11}^{[L]}a_1^{[L-1]} + \theta_{12}^{[L]}a_2^{[L-1]},\qquad h_\theta(x) = a_1^{[L]} = g\!\left(z_1^{[L]}\right)$$

layer 1 เอา \(x_1,x_2\) มาคูณ weight แล้วบวก bias; layer L เอา \(a_1^{[L-1]},a_2^{[L-1]}\) (ผลจากชั้นก่อนหน้า) มาคูณ weight แล้วบวก bias — โครงเดียวกัน เปลี่ยนแค่ชื่อขาเข้า

นิยามสัญลักษณ์ให้ครบ (ครั้งแรกที่ใช้ในบทนี้): \(\theta_{ji}^{[l]}\) = weight ของเส้นที่วิ่งจาก node \(i\) ของชั้น \(l-1\) เข้าสู่ node \(j\) ของชั้น \(l\); \(\theta_{j0}^{[l]}\) = bias ของ node \(j\) ชั้น \(l\); \(z\) = ผลรวมเชิงเส้นก่อนผ่าน activation; \(a = g(z)\) = ค่าที่ออกจาก node หลังผ่าน activation; \(g\) = activation function (sigmoid/ReLU ฯลฯ); \(L\) = หมายเลขชั้นสุดท้าย. ถ้าเรานิยามเพิ่มว่า \(a^{[0]} := x\) (มองว่า input คือ "ผลลัพธ์ของชั้นที่ 0") สองสมการข้างบนก็ยุบเหลือสมการเดียวที่ใช้ได้กับทุกชั้น:

$$z_j^{[l]} = \theta_{j0}^{[l]} + \sum_{i}\theta_{ji}^{[l]}\,a_i^{[l-1]},\qquad a_j^{[l]} = g\!\left(z_j^{[l]}\right)$$

ส่วน Cost Function อาจารย์เขียนไว้สองแบบตามชนิดปัญหา และเป็นสูตรเดิมจาก Lesson 3 ไม่เปลี่ยนเลยแม้เครือข่ายจะลึกขึ้น — ปัญหา regression ใช้ squared error, ปัญหา classification (binary) ใช้ cross-entropy รวมทุก output \(k=1..K\):

$$J(\theta) = \frac{1}{m}\sum_{i=1}^{m}\left(h_\theta(x^{(i)}) - y^{(i)}\right)^2$$ $$\begin{aligned} J(\theta) = -\frac{1}{m}\sum_{i=1}^{m}\sum_{k=1}^{K}\Big[&y_k^{(i)}\log\big(h_\theta(x^{(i)})_k\big)\\ +\ &\big(1-y_k^{(i)}\big)\log\big(1-h_\theta(x^{(i)})_k\big)\Big] \end{aligned}$$

ข้อสังเกตที่ต้องพูดได้: สูตร cost ไม่มี \(L\) โผล่มาเลยสักตัว — cost สนใจแค่ "ทำนายออกมาเท่าไร (\(h_\theta(x)\))" กับ "คำตอบจริงเท่าไร (\(y\))" ไม่สนใจว่าข้างในมีกี่ชั้น. ความลึกมีผลต่อวิธีคำนวณ \(h_\theta(x)\) และวิธีหา gradient เท่านั้น. (สไลด์เขียน \(\frac{1}{m}\) หน้า squared error ขณะที่ Lesson 1 ใช้ \(\frac{1}{2m}\) — ตัวประกอบ \(\tfrac12\) เป็นแค่ความสะดวกตอนหาอนุพันธ์ ไม่เปลี่ยนตำแหน่งจุดต่ำสุด. ในข้อสอบให้ยึดตามที่โจทย์เขียนมา.)

คำนวณ: นับพารามิเตอร์ของโครงข่ายลึก \([2,10,10,10,1]\) (โครงข่ายตัวอย่างที่อาจารย์ใช้ทดลองหน้า 24)
ให้กฎ: เมทริกซ์ \(\theta^{[l]}\) มีขนาด \(s_l\times(s_{l-1}+1)\) เมื่อ \(s_l\) = จำนวน node ใน layer \(l\) และ "+1" คือคอลัมน์ของ bias
  1. ขั้นที่ 1 — ไล่ทีละ layer ว่ามีเส้นกี่เส้น
    $$\begin{aligned} \theta^{[1]}&:\ 10\times(2+1)=30\\ \theta^{[2]}&:\ 10\times(10+1)=110\\ \theta^{[3]}&:\ 10\times(10+1)=110\\ \theta^{[4]}&:\ 1\times(10+1)=11 \end{aligned}$$

    ทำไมต้อง \(+1\): แต่ละ node มี bias ของตัวเอง 1 ตัวเสมอ ไม่ว่าจะมีขาเข้ากี่เส้น

  2. ขั้นที่ 2 — รวมทุกชั้น
    $$30+110+110+11 = 261$$

261 พารามิเตอร์. สังเกตว่า layer กลาง ๆ ที่เชื่อม \(10\times10\) กินพารามิเตอร์ไป 110+110 = 220 ตัว หรือ 84% ของทั้งหมด — จำนวนพารามิเตอร์โตตามผลคูณของความกว้างสองชั้นที่ติดกัน ไม่ใช่ตามจำนวน node เฉย ๆ.

1.2 Backward: chain rule ที่ยาวขึ้นอีกหนึ่งทอด (หน้า 4)

Slide How Deep Neural Network Works (2): Parameter Learning by backpropagation, with Forward Computation and Backward Computation arrows, the chain-rule derivation of dJ/dtheta_1,1^(L) = delta^(L) a_1^(L-1) and dJ/dtheta_1,1^(L-1) = delta^(L) theta_1,1^(L) g'(z1[L-1]) a1^(L-2), delta^(L) = h(x) - y, plus the update rule box and an Interpretation note
Slide: How Deep Neural Network Works (2) — Parameter Learning (Backpropagation) (Lecture4.pdf, p.4)

อาจารย์วางลูกศรสองทิศ (Forward Computation ไปขวา, Backward Computation กลับซ้าย) แล้วไล่ chain rule ให้ดูสองระดับ. ระดับแรกคือ weight ที่อยู่ติดกับ output:

$$\frac{\partial J(\theta)}{\partial\theta_{1,1}^{(L)}} = \frac{\partial J(\theta)}{\partial z_1^{(L)}}\cdot\frac{\partial z_1^{(L)}}{\partial\theta_{1,1}^{(L)}} = \delta^{(L)}\,a_1^{(L-1)},\qquad \delta^{(L)} = h_\theta(x)-y$$

ระดับที่สองคือ weight ที่อยู่ลึกเข้าไปอีกหนึ่งชั้น — ตรงนี้เองที่อาจารย์ทำสีไว้ เพราะเป็นบรรทัดที่ต้องเขียนเองได้ในข้อสอบ:

$$\begin{aligned} \frac{\partial J(\theta)}{\partial\theta_{1,1}^{(L-1)}} &= \frac{\partial J}{\partial z_1^{(L)}}\cdot\frac{\partial z_1^{(L)}}{\partial a_1^{(L-1)}}\cdot\frac{\partial a_1^{(L-1)}}{\partial z_1^{(L-1)}}\cdot\frac{\partial z_1^{(L-1)}}{\partial\theta_{1,1}^{(L-1)}}\\[2pt] &= \delta^{(L)}\;\theta_{1,1}^{(L)}\;g'\!\left(z_1^{[L-1]}\right)\;a_1^{(L-2)} \end{aligned}$$

อ่านจากขวาไปซ้าย: "ค่าที่เข้าเส้นนี้" × "อนุพันธ์ของ activation ที่ปลายเส้น" × "weight ที่ต้องเดินผ่านต่อไปข้างหน้า" × "ความผิดพลาดที่ปลายทาง"

สี่เทอมนี้คือของเดิมทั้งหมดจาก Lesson 3 — \(\delta\) (ความผิดที่ปลายทาง), \(\theta\) (weight ที่ต้องข้าม), \(g'\) (ประตูของ activation), \(a\) (ค่าที่ป้อนเข้าเส้นนั้น). สิ่งเดียวที่เปลี่ยนคือ superscript กลายเป็น \([L],[L-1],[L-2]\) แทน \([1],[2]\). ยิ่งย้อนลึก โซ่ยิ่งยาว: อยากได้ gradient ของ weight ที่ชั้น \(l\) ต้องคูณ \(\theta\cdot g'\) เพิ่มอีก 1 คู่ต่อทุก ๆ ชั้นที่ห่างจาก output.

Interpretation (กล่องคำอธิบายบนสไลด์ ซึ่งเป็นประโยคที่ออกสอบง่ายมาก): ถ้า weight เส้นหนึ่งวิ่งไปยัง node ที่ "มีส่วนทำให้ loss สุดท้ายสูงมาก" (นั่นคือ \(\delta_{\text{destination}}\) มีค่ามาก) gradient ของเส้นนั้นก็จะใหญ่ตาม อัลกอริทึมจึงปรับ weight เส้นนั้นแรงกว่าเส้นอื่น เพื่อกด loss ลง — พูดสั้น ๆ คือ "ปรับมากตรงที่ผิดมาก". และกฎอัปเดตก็ยังเป็นสูตรเดิมที่วนซ้ำจนลู่เข้า:

$$\text{Repeat until convergence}\ \{\ \ \theta_{i,j}^{(l)} := \theta_{i,j}^{(l)} - \alpha\,\frac{\partial J(\theta)}{\partial\theta_{i,j}^{(l)}}\ \ \}$$
คำนวณ: ส่ง \(\delta\) ย้อนผ่าน 3 layer ด้วยตัวเลขจริง (ให้เห็นว่า gradient ของชั้นต้น ๆ เล็กลงอย่างไร)
ให้เครือข่ายโซ่ 1 node ต่อชั้น (ตัดbias ทิ้งเพื่อความสั้น): \(x=1\to a^{[1]}\to a^{[2]}\to a^{[3]}\), ทุก weight = 1, sigmoid ทุก node, \(y=1\)
  1. ขั้นที่ 1 — Forward (คำนวณไปข้างหน้าก่อนเสมอ เพราะ backward ต้องใช้ \(a\))
    $$\begin{aligned} z^{[1]}&=1\ \Rightarrow\ a^{[1]}=g(1)=0.731\\ z^{[2]}&=0.731\ \Rightarrow\ a^{[2]}=g(0.731)=0.675\\ z^{[3]}&=0.675\ \Rightarrow\ a^{[3]}=g(0.675)=0.663 \end{aligned}$$
  2. ขั้นที่ 2 — \(\delta\) ที่ output (จากสูตรบนสไลด์)
    $$\delta^{[3]} = a^{[3]}-y = 0.663-1 = -0.337$$
  3. ขั้นที่ 3 — ส่งย้อนทีละชั้นด้วย \(\delta^{[l-1]} = \theta^{[l]}\,\delta^{[l]}\,g'(z^{[l-1]})\) โดย \(g'(z)=a(1-a)\)
    $$\begin{aligned} \delta^{[2]} &= 1\times(-0.337)\times0.675(1-0.675) = -0.0740\\ \delta^{[1]} &= 1\times(-0.0740)\times0.731(1-0.731) = -0.0146 \end{aligned}$$
  4. ขั้นที่ 4 — gradient ของ weight แต่ละชั้น = \(\delta\) ของปลายทาง × \(a\) ของต้นทาง
    $$\frac{\partial J}{\partial\theta^{[3]}} = -0.2277,\quad \frac{\partial J}{\partial\theta^{[2]}} = -0.0541,\quad \frac{\partial J}{\partial\theta^{[1]}} = -0.0146$$

gradient ของชั้นแรกเล็กกว่าชั้นสุดท้ายราว 15.7 เท่า (\(0.2277/0.0146\)) ทั้งที่เครือข่ายลึกแค่ 3 ชั้น — เพราะทุกชั้นที่ย้อนผ่านต้องคูณ \(g'\le0.25\) เพิ่มอีกหนึ่งตัว. นี่คือตัวอย่างขนาดจิ๋วของ vanishing gradient ที่ Lesson 5 จะลงรายละเอียดเต็ม.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
สูตรทั่วไปใช้ superscript \(l\) แทนเลขตายตัว เพราะ backprop เป็น local operation (Lesson 3 §8) — ไม่ว่ากี่ layer แต่ละ node คำนวณ \(\delta\) ด้วยสูตรเดียวกัน ต่างแค่ "\(l\) เท่าไร" จึงเขียนโปรแกรมเป็นลูปเดียวได้.
เปลี่ยนแล้วเกิดอะไร
ถ้า \(L=5\) การหา \(\partial J/\partial\theta^{[3]}\) (กลางเครือข่าย) ต้องไล่ผ่าน layer 3→4→5 รวม 3 ทอด — จำนวนทอด = "ระยะทาง" \(L-l\) จากชั้นนั้นถึง output ไม่ใช่ค่าคงที่ (ตัวอย่างโซ่ 3 ชั้นข้างบน: ชั้น 1 ผ่าน \(g'\) สองทอดจึงเล็กกว่าชั้น 3 ที่ผ่านทอดเดียว).
เชื่อมกับ
Lesson 3 §2 (จำนวนขั้น = จำนวน layer ที่ต้องผ่าน) — บทนี้แค่ทำให้เป็นสูตรทั่วไป ไม่ใช่หลักการใหม่; และเป็นต้นทางของ Lesson 5 §1 เรื่อง vanishing gradient.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 1Deep NN Backprop
สูตร ∂J/∂θ₁,₁⁽ᴸ⁻¹⁾ = δ⁽ᴸ⁾·θ₁,₁⁽ᴸ⁾·g'(z₁⁽ᴸ⁻¹⁾)·a₁⁽ᴸ⁻²⁾ บนสไลด์หน้า 4 ต่างจากสูตรของเครือข่าย 2 layer ใน Lesson 3 อย่างไร?

2. ทำไม Deep Network ถึงเทรนยาก: 4 ปัญหา และรายชื่อ Optimizer ที่เร็วกว่า

เรื่องเล่าก่อนเข้าเนื้อ: สมมติคุณเพิ่งต่อเครือข่าย 20 ชั้นเสร็จ กด train แล้วเดินไปกินข้าว กลับมาอีกสองชั่วโมง cost ยังแทบไม่ลด — หรือ cost บน training set ลงสวยแต่ผลกับข้อมูลใหม่ห่วย. หน้า 5 คือรายการ "สิ่งที่จะไปผิดพลาด" ทั้งหมด 4 ข้อ ที่อาจารย์บอกล่วงหน้าว่าจะแก้อย่างไรในหน้าถัด ๆ ไป. หน้านี้ทำสีเยอะที่สุดหน้าหนึ่งของบท — เพราะแทบทุกคำเป็นชื่อปัญหาที่จะถูกถามในข้อสอบ.

Slide Challenging in Training Deep Neural Networks listing four problems: slow training and convergence with a large volume of data needing faster optimizers with reliable convergence; overfitting due to millions of parameters; vanishing and exploding gradients; and many hyperparameters to tune
Slide: Challenging in Training Deep Neural Networks (Lecture4.pdf, p.5)

ไล่ทีละข้อพร้อมเหตุผลที่อาจารย์ให้ไว้:

  1. เทรนช้าและลู่เข้าช้า. Deep learning ทำงานกับ "large volume of data" และเป็นกระบวนการวนซ้ำ (iterative) จึงมักเจอปัญหา slow training และ convergence. โจทย์คือการหา faster optimizers ที่ยัง reliable convergence คือเร็วขึ้นแต่ยังลงถึงจุดต่ำสุดจริง ๆ — เร็วอย่างเดียวไม่พอ ถ้าเร็วแล้วพุ่งเลยเป้าก็ไม่มีประโยชน์. นี่คือปัญหาที่ §3–§5 ทั้งหมดของบทนี้ตอบ.
  2. overfitting โดยธรรมชาติ. โมเดล deep learning เป็น overfitting in nature เพราะมีพารามิเตอร์เยอะมาก. อาจารย์ขยายว่าโมเดลที่มี "millions of parameters" เสี่ยง overfit training set อย่างรุนแรง โดยเฉพาะเมื่อตัวอย่างไม่พอ หรือข้อมูลมี noise มาก — สังเกตว่าเงื่อนไขสองข้อนี้เป็นตัวขยายความเสี่ยง ไม่ใช่ตัวต้นเหตุ ต้นเหตุคือจำนวนพารามิเตอร์. ตอบด้วย §6–§7.
  3. Vanishing/Exploding gradients problem. อาจารย์แยกผลเสียของสองฝั่งไว้ชัดเจน: Vanishing gradients ทำให้การเรียนรู้ช้าลง "โดยเฉพาะในชั้นต้น ๆ" (early layers) ทำให้เครือข่ายลึกเทรนยาก — ตรงกับตัวเลข 15.7 เท่าที่เราคำนวณใน §1.2 พอดี ส่วน Exploding gradients ทำให้การเทรน "ไม่เสถียรอย่างมาก" นำไปสู่การที่ weight แกว่ง (oscillations) หรือพุ่งออกนอกลู่ (divergence). ตอบเต็มใน Lesson 5.
  4. hyperparameter เยอะ. อาจารย์บอกว่าความยืดหยุ่นของ neural network เป็นข้อเสียหลักข้อหนึ่งด้วย เพราะมี many hyperparameters ให้ปรับ และ Tuning hyperparameters ให้เหมาะสมคือ "หนึ่งในงานที่ท้าทายที่สุด". ตอบด้วย Lesson 6 (Keras Tuner).
คำนวณ: "ลึก" กับ "กว้าง" — พารามิเตอร์ต่างกันแค่ไหน (ทำให้ปัญหาข้อ 2 เป็นรูปธรรม)
ให้เทียบ 2 โมเดลที่รับ input 2 ตัวและให้ output 1 ตัวเหมือนกัน
  1. ขั้นที่ 1 — ลึก \([2,10,10,10,1]\) (hidden node รวม 30 ตัว)
    $$30+110+110+11 = 261$$
  2. ขั้นที่ 2 — กว้างแต่ตื้น \([2,50,1]\) (hidden node รวม 50 ตัว)
    $$50\times(2+1) + 1\times(50+1) = 150 + 51 = 201$$

โมเดลลึกมี hidden node น้อยกว่า (30 vs 50) แต่พารามิเตอร์ มากกว่า (261 vs 201) เพราะการต่อ \(10\times10\) สองครั้งแพงกว่าการต่อ \(2\times50\) ครั้งเดียว. บทเรียน: "จำนวนพารามิเตอร์" (ตัวขับ overfit) กับ "ความลึก" (ตัวขับ vanishing) เป็นคนละปริมาณ แม้จะมาด้วยกันบ่อย ๆ.

Slide Faster Optimizers: training an extremely large deep neural network can be remarkably slow leading to convergence issues; speed can be improved with optimizers faster than gradient descent while ensuring the correct trajectory; list of Mini-Batch Gradient Descent, SGD with momentum, RMSProp, Adam, AdamW, with a link to arxiv 1609.04747
Slide: Faster Optimizers — สารบัญของ §3 ถึง §5 (Lecture4.pdf, p.6)

หน้านี้คือสารบัญของครึ่งแรกของบท และย้ำปัญหาข้อ 1 อีกรอบด้วยคำที่ทำสี: Training เครือข่ายใหญ่มาก ๆ อาจ slow อย่างน่าตกใจ และนำไปสู่ปัญหาเรื่อง convergence. ทางแก้คือใช้ optimizers ที่ faster กว่า Gradient Descent ธรรมดา โดยยังรักษา correct trajectory คือ "เส้นทางที่ถูกต้อง" ในการหาจุดต่ำสุด. สองเงื่อนไขนี้ต้องมาคู่กันเสมอในคำตอบข้อสอบ — เร็วขึ้น และ ยังเดินถูกทาง. รายชื่อที่อาจารย์ลิสต์ไว้เรียงตามลำดับที่จะสอน และตรงกับหัวข้อในบทนี้พอดี:

Optimizer ที่สไลด์หน้า 6 ลิสต์ไว้แนวคิดหลักในหนึ่งประโยคสอนที่
Mini-Batch Gradient Descentอัปเดตทุก ๆ กลุ่มย่อยของข้อมูล แทนที่จะรอทั้งชุด§3
SGD (Gradient Descent with Momentum)ใช้ค่าเฉลี่ยของ gradient ที่ผ่านมาเป็นแรงส่ง§4
RMSPropหาร gradient ด้วยรากของค่าเฉลี่ย gradient กำลังสอง§5.1
Adamรวม Momentum + RMSProp เข้าด้วยกัน§5.2
AdamWAdam ที่แยก weight decay ออกมาเป็นขั้นต่างหาก§5.5

สังเกตว่าอาจารย์เขียน "SGD (Gradient Descent with Momentum)" ติดกันในบรรทัดเดียว ทั้งที่ในหน้า 9–10 คำว่า SGD หมายถึง "อัปเดตทีละ 1 ตัวอย่าง". นี่ไม่ใช่ความผิดพลาด แต่เป็นการใช้คำแบบที่เจอในทางปฏิบัติ: ในไลบรารีอย่าง Keras คลาส SGD คือ optimizer ที่รับ mini-batch มาอัปเดต และมีพารามิเตอร์ momentum ให้เปิดปิด — อาจารย์เองก็ย้ำในหน้า 18 ว่า "mini-batch gradient descent (often called stochastic gradient descent or SGD)". อ่านให้ปลอดภัยในข้อสอบ: ถ้าโจทย์พูดถึงสูตร ให้ยึดนิยามหน้า 9 (1 ตัวอย่าง/1 อัปเดต); ถ้าโจทย์พูดถึงโค้ด Keras ให้เข้าใจว่า SGD คือตัวที่รับ mini-batch.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
ปัญหา (2) overfit และ (3) vanishing เชื่อมกันผ่านความลึก: ยิ่งลึกยิ่งได้ความสามารถในการแทนค่า (expressiveness) แต่ก็ยิ่งมีพารามิเตอร์มาก (เสี่ยง overfit) และยิ่งมีโซ่ chain rule ยาว (เสี่ยง vanishing) — จึงต้องมีทั้งชุดเครื่องมือ ไม่ใช่วิธีเดียว.
เปลี่ยนแล้วเกิดอะไร
ลดความลึกแต่เพิ่มความกว้างจนพารามิเตอร์รวมเท่าเดิม ⇒ vanishing เบาลง (โซ่สั้นลง) แต่ความเสี่ยง overfit ไม่ลด เพราะจำนวนพารามิเตอร์เท่าเดิม — ยืนยันว่าเป็นคนละกลไก.
เชื่อมกับ
§3–§5 แก้ปัญหา (1), §6–§7 แก้ (2), Lesson 5 แก้ (3), Lesson 6 แก้ (4) — ทั้งหลักสูตรครึ่งหลังคือการไล่แก้ 4 ข้อนี้ตามลำดับ.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 2ปัญหาของ Deep Network
ตามสไลด์หน้า 5 ข้อความใดอธิบายผลของ vanishing gradients กับ exploding gradients ได้ตรงตามที่อาจารย์ระบุ?

3. Batch, Stochastic, Mini-Batch Gradient Descent

ปัญหาที่อยู่ตรงหน้า: gradient ของ cost คือค่าเฉลี่ยของ gradient จากแต่ละตัวอย่าง (Lesson 3 §4). ถ้าคุณมีข้อมูล 100 ล้านแถว การจะก้าว 1 ก้าวต้องรวมทั้ง 100 ล้านแถวก่อน — ก้าวเดียวใช้เวลานานมาก. คำถามของอาจารย์ในหน้า 7–13 จึงเป็นคำถามเดียว: "ต้องเฉลี่ยกี่ตัวอย่างก่อนถึงจะยอมก้าว 1 ครั้ง?" คำตอบสามแบบ (ทั้งชุด / 1 ตัวอย่าง / กลุ่มย่อย) คือ Batch GD, SGD และ Mini-batch GD — สามอัลกอริทึมนี้ต่างกันแค่ตัวเลขตัวเดียว คือ batch size \(B\) (โดยให้ \(m\) = จำนวนตัวอย่างทั้งหมด).

ต้องรู้ก่อน — ทำไม "เฉลี่ยจากตัวอย่างน้อย" ถึงแกว่ง

สมมติน้ำหนักนักศึกษาในห้องมี 4 คน: 50, 70, 60, 80 กก. ค่าเฉลี่ยจริง \(=\frac{50+70+60+80}{4}=65\).

กฎทางสถิติที่ใช้ตลอดหัวข้อนี้: ความคลาดเคลื่อนมาตรฐานของ "ค่าเฉลี่ยจากกลุ่มตัวอย่างขนาด \(B\)" แปรผันกับ \(1/\sqrt{B}\) — ใช้ตัวอย่างมากขึ้น 100 เท่า ความแกว่งลดลง \(\sqrt{100}=10\) เท่า (ไม่ใช่ 100 เท่า). gradient ก็คือค่าเฉลี่ยตัวหนึ่ง ดังนั้นคำว่า "noisy gradient" ที่อาจารย์พูดถึงในหน้า 10 ก็คือปรากฏการณ์เดียวกันนี้เป๊ะ ๆ.

3.1 Batch Gradient Descent — วิธีที่เราใช้มาตลอด (หน้า 7)

Slide Batch Gradient Descent (Our current approach): update weights after training all training examples completes one iteration; Pros relatively low noise and larger steps towards the minimum; Cons1 computationally expensive and slow; note the algorithm is termed batch because it uses the entire training data in one batch; with the repeat-until-convergence update rule and a contour plot of a smooth path to the minimum
Slide: Batch Gradient Descent (Our current approach) (Lecture4.pdf, p.7)

อาจารย์นิยามว่า Batch GD คือการอัปเดต weight หลังจากเทรนด้วย all training examples ครบแล้ว (นับเป็น 1 iteration เต็ม) แล้วแจงข้อดีข้อเสียเป็นหัวข้อที่ทำสีไว้ทั้งคู่:

คำนวณ: ตัวอย่าง 100 ล้านแถวของอาจารย์แพงแค่ไหน
ให้ \(m = 100{,}000{,}000\) แถว, สมมติเครื่องประมวลผลได้ 10 ล้านแถวต่อวินาที, ต้องการเดิน 1,000 ก้าวของ gradient descent
  1. ขั้นที่ 1 — เวลาต่อ 1 ก้าวของ Batch GD (ต้องอ่านครบทุกแถว)
    $$\frac{100{,}000{,}000}{10{,}000{,}000} = 10\ \text{วินาที/ก้าว}$$
  2. ขั้นที่ 2 — เวลารวม 1,000 ก้าว
    $$1{,}000\times10 = 10{,}000\ \text{วินาที} \approx 2.8\ \text{ชั่วโมง}$$
  3. ขั้นที่ 3 — ถ้าใช้ mini-batch ขนาด 1,000 (ล่วงหน้าไปหน้า 11)

    อ่านแค่ 1,000 แถวต่อก้าว ⇒ ก้าวละ 0.0001 วินาที ⇒ 1,000 ก้าวใช้เวลา 0.1 วินาที ทั้งที่อ่านข้อมูลไปแค่ 1 ล้านแถว (1% ของ 1 epoch)

ต้นทุนของ Batch GD ไม่ได้อยู่ที่ "กี่ก้าว" แต่อยู่ที่ "ก้าวละกี่แถว" — จำนวนแถวที่อ่านต่อก้าวคือสิ่งที่ mini-batch ไปลด. นี่คือความหมายเชิงตัวเลขของคำว่า computationally expensive ในสไลด์.

3.2 Batch GD ข้อเสียข้อที่สอง: ปัญหา Local Minima (หน้า 8)

Slide Batch Gradient Descent (2): Cons2 local minima problems, explaining reliance on minimal local information to infer the global structure of the error surface, that most local minima share similar error rates and generalization traits with global minima, and that significantly higher errors create significant local minima issues; with a loss curve showing a shallow local minimum and a deeper global minimum
Slide: Batch Gradient Descent (2) — Local minima problems (Lecture4.pdf, p.8)

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

  1. ทำไม local minima ถึงเป็นปัญหาตั้งแต่ต้น: ความท้าทายหลักของการ optimize โมเดล deep learning มาจากการที่เราถูกบังคับให้พึ่ง minimal local information เพื่ออนุมานโครงสร้างระดับภาพรวมของ error surface. แปลว่า: gradient บอกได้แค่ "ตรงจุดที่ยืนอยู่ พื้นเอียงไปทางไหน" เหมือนคนตาบอดคลำทางลงเขาในหมอก — ไม่มีใครบอกได้ว่าหุบเขาที่กำลังลงอยู่นี้เป็นหุบที่ลึกที่สุดของทั้งภูเขาหรือเปล่า. Batch GD ซึ่งเดินตาม gradient ที่แม่นและไม่มี noise เลย จึงเดินลงหุบแรกที่เจอแล้วหยุดนิ่งอยู่ตรงนั้น
  2. แต่ในทางปฏิบัติกลับไม่แย่อย่างที่กลัว: "In theory, local minima pose a problem. Yet, recent studies suggest that..." — งานวิจัยใหม่ ๆ ชี้ว่า local minima ส่วนใหญ่ มี error rate ใกล้เคียงกัน และมี generalization traits with global minima คือคุณสมบัติด้านการ generalize พอ ๆ กับ global minimum. พูดง่าย ๆ: ในเครือข่ายที่ใหญ่พอ หุบเขาส่วนใหญ่ลึกพอ ๆ กัน ตกหุบไหนก็ได้โมเดลที่ดีพอ ๆ กัน
  3. ยกเว้นกรณีที่แย่จริง ๆ: ถ้า local minimum ที่ติดอยู่ให้ higher errors สูงกว่า global minimum อย่างมีนัยสำคัญ เมื่อนั้นเราจึงเจอ significant local minima issues ของจริง — ซึ่งคือสิ่งที่ SGD (หน้า 9) และ Momentum (หน้า 17) จะเข้ามาช่วย

สรุปแบบที่ตอบข้อสอบได้: local minima เป็นปัญหา "ในทฤษฎี" แต่โดยส่วนใหญ่ไม่ร้ายแรงในทางปฏิบัติ — จะร้ายแรงก็ต่อเมื่อ error ของมันสูงกว่า global อย่างมีนัยสำคัญ. อย่าตอบสุดโต่งไปข้างใดข้างหนึ่ง เพราะสไลด์พูดครบทั้งสองด้าน. บนภาพ loss curve ของสไลด์มีหุบสองหุบ: หุบซ้าย (Local minimum) ตื้นกว่าหุบขวา (Global minimum) — ถ้าเริ่มจากทางซ้ายแล้วเดินตาม gradient อย่างซื่อสัตย์ คุณจะจอดที่หุบซ้ายและไม่มีวันรู้เลยว่าหุบขวามีอยู่.

3.3 Stochastic Gradient Descent — อีกสุดขั้วหนึ่ง (หน้า 9–10)

Slide Stochastic Gradient Descent: update weights after one training instance (also called online learning); Pros 1 faster training using one example every iteration; Pros 2 enhances navigation through local optima and flat regions (saddle points); with a contour plot of a jagged path and two error-curve diagrams of a local optimum and a flat saddle region
Slide: Stochastic Gradient Descent — Pros 1 และ Pros 2 (Lecture4.pdf, p.9)

SGD กลับด้านกับ Batch GD สุดทาง: อัปเดต weight หลังเห็น one training instance เพียงตัวเดียว (นับเป็น 1 iteration) — อาจารย์วงเล็บไว้ว่าวิธีนี้ยังเรียกว่า online learning เพราะเหมาะกับสถานการณ์ที่ข้อมูลไหลเข้ามาทีละชิ้นแบบเรียลไทม์ เรียนทันทีที่ข้อมูลมาถึงโดยไม่ต้องเก็บสะสม. ข้อดีสองข้อที่ทำสี:

saddle point คืออะไร (คำที่สไลด์ใช้แต่ไม่ได้นิยาม): จุดที่ gradient เป็นศูนย์ แต่ไม่ใช่จุดต่ำสุด — มันต่ำสุดในบางทิศและสูงสุดในอีกทิศ เหมือนอานม้า: นั่งอยู่ตรงกลางอาน หน้า-หลังลาดลง แต่ซ้าย-ขวาลาดขึ้น. บริเวณ "flat region" รอบ ๆ saddle point อันตรายกว่าตัวจุดเอง เพราะ gradient เล็กมากจนก้าวแทบไม่ขยับ — ภาพคู่ด้านล่างของสไลด์แสดงสองสถานการณ์นี้: ภาพซ้ายเส้นทางจอดค้างที่หลุมตื้น (เครื่องหมาย ✗) ขณะที่จุดต่ำสุดจริง (★) อยู่อีกฟาก; ภาพขวาเส้นทางไต่ผ่านพื้นราบยาว ๆ กลางกราฟแล้วยังไปถึง ★ ได้.

Slide Stochastic Gradient Descent (2): Cons 1 gradient is so noisy so weights may not move precisely down the cost surface at each iteration, sometimes hitting the wrong direction and might not converge all the time; Cons 2 loses the speed up from not utilizing matrix multiplication; with a contour plot of a highly jagged path
Slide: Stochastic Gradient Descent (2) — Cons 1 และ Cons 2 (Lecture4.pdf, p.10)

ราคาที่ต้องจ่ายมีสองข้อ และอาจารย์ทำสีหัวข้อทั้งคู่:

3.4 Mini-Batch Gradient Descent — ทางสายกลาง (หน้า 11)

Slide Mini-Batch Gradient Descent: update weights every some training set size (also called a batch size); example dividing 10,000 training examples into 10 mini-batches randomly after shuffling, each holding 10 percent of the data; more gradient steps per epoch means weights are updated faster; takes advantage of matrix multiplication to speed up training; avoids local minima and flat regions
Slide: Mini-Batch Gradient Descent (Lecture4.pdf, p.11)

นิยาม: อัปเดต weight ทุก ๆ training set size จำนวนหนึ่ง ซึ่งเรียกอีกชื่อว่า batch size. ตัวอย่างที่อาจารย์ยกคือตัวอย่างที่ต้องจำให้ได้ เพราะเป็นตัวเลขที่ใช้ถามในข้อสอบได้ทันที: มีข้อมูล 10,000 ตัวอย่าง แบ่งแบบสุ่มเป็น 10 mini-batch ⇒ แต่ละ mini-batch มี 1,000 ตัวอย่าง หรือ 10% ของข้อมูลทั้งหมด (ตัวเลข 10% คือตัวเลขที่อาจารย์เขียนกำกับไว้ข้างแต่ละก้อนบนสไลด์). ก่อนแบ่งต้องทำ Shuffling คือสลับลำดับข้อมูลก่อน — บนสไลด์คอลัมน์ซ้ายเรียงเลข 1,2,3,4,5,…,10000 ตามลำดับเดิม แต่หลังสลับแล้วก้อนแรกมีสมาชิกอย่าง 2 และ 300, ก้อนที่สองมี 7101 กับ 3030, ก้อนสุดท้ายมี 100 กับ 76 — คือเลขกระโดดไปมาไม่เรียงกันแล้ว.

ทำไมต้อง shuffle (สไลด์เขียนคำนี้ไว้แต่ไม่อธิบายเหตุผล — เสริมความเข้าใจ นอกสไลด์): ถ้าข้อมูลถูกจัดเรียงตาม label เช่น 5,000 แถวแรกเป็นคลาส A และ 5,000 แถวหลังเป็นคลาส B การแบ่งโดยไม่สลับจะทำให้ mini-batch ที่ 1–5 เห็นแต่คลาส A ล้วน ๆ. โมเดลจะถูกดึงไปทางคลาส A ห้าก้าวติด แล้วถูกดึงกลับมาทางคลาส B อีกห้าก้าว — เหมือนถูกกระชากไปมา. การสลับทำให้ทุก mini-batch เป็น "ตัวแทนย่อ" ของข้อมูลทั้งชุด ทิศของ gradient แต่ละก้อนจึงใกล้เคียงทิศจริง.

ข้อดีที่อาจารย์ทำสีไว้ในหน้านี้มี 3 กลุ่ม:

epoch vs iteration — แยกให้ขาดก่อนไปต่อ

epoch = จำนวนรอบที่โมเดล "ดูข้อมูลครบทั้งชุด"  ·  iteration = จำนวนครั้งที่ weight ถูกอัปเดต

ในนิยามของ Batch GD หนึ่ง iteration = หนึ่ง epoch (เพราะอ่านครบชุดแล้วอัปเดตทีเดียว) แต่พอเป็น mini-batch หนึ่ง epoch มีหลาย iteration. คำถามคลาสสิก: "ข้อมูล 10,000 แถว batch size 1,000 เทรน 30 epoch มีการอัปเดต weight กี่ครั้ง?" ⇒ \(10\times30=300\) ครั้ง.

คำนวณ: จำนวน iteration ต่อ 1 epoch เมื่อ \(m = 10{,}000\) (ตัวอย่างตรงจากสไลด์หน้า 11)
ให้สูตร: จำนวน iteration ต่อ epoch \(= \lceil m/B \rceil\) (ปัดขึ้น เพราะเศษที่เหลือก็ยังนับเป็น 1 batch)
  1. ขั้นที่ 1 — กรณีของสไลด์: แบ่ง 10,000 เป็นก้อนละ 1,000
    $$\frac{10{,}000}{1{,}000} = 10\ \text{iteration ต่อ epoch}$$

    ตรงกับคำว่า "10 mini-batch" และ "10%" ต่อก้อนบนสไลด์พอดี

  2. ขั้นที่ 2 — เลื่อนค่า \(B\) ไปค่าอื่น ๆ (สามอัลกอริทึมอยู่ในตารางเดียวกัน)
    \(B\)เรียกว่าอะไรiteration/epoch
    10,000Batch GD (\(B=m\))1
    1,000Mini-batch (ตัวอย่างสไลด์)10
    256Mini-batch\(\lceil 39.06\rceil = 40\)
    64Mini-batch (ค่าที่ใช้ทดลองหน้า 24)\(\lceil156.25\rceil = 157\)
    32Mini-batch\(\lceil312.5\rceil = 313\)
    1SGD (\(B=1\))10,000
  3. ขั้นที่ 3 — ความ noisy ของ gradient เทียบกัน

    ใช้กฎ \(1/\sqrt{B}\) จากกล่อง "ต้องรู้ก่อน": เทียบ \(B=1\) กับ \(B=1{,}000\) ความแกว่งต่างกัน \(\sqrt{1000}\approx 31.6\) เท่า — ไม่ใช่ 1,000 เท่า. นี่คือเหตุผลที่ mini-batch เล็ก ๆ ก็ "นิ่งพอ" แล้ว ไม่ต้องใช้ทั้งชุด

\(B\) เล็ก ⇒ อัปเดตบ่อย (ความคืบหน้าต่อ epoch สูง) แต่ทิศ noisy; \(B\) ใหญ่ ⇒ ทิศนิ่ง แต่อัปเดตน้อยและกินหน่วยความจำ. สามอัลกอริทึมของหน้า 7–11 คือ ค่า \(B\) สามค่าบนแกนเดียวกัน ไม่ใช่สามความคิดที่ไม่เกี่ยวกัน — จำแบบนี้แล้วตอบข้อสอบเปรียบเทียบได้ทุกแบบ.

3.5 เลือก Batch Size อย่างไร (หน้า 12)

Slide Choosing an Appropriate Batch Size: for small datasets under 2000 use batch size equal to m; for large datasets use values between one and m with common starting points 32 to 256 depending on memory, hardware utilization, optimization and task; powers of 2 such as 32 64 128 256 are common for GPU efficiency but not a requirement; a contour plot compares the paths of batch, mini-batch and stochastic gradient descent, with a note that SGD and mini-batch also reach the minimum with a good learning schedule
Slide: Choosing an Appropriate Batch Size (Lecture4.pdf, p.12)

คำแนะนำของอาจารย์เป็นเงื่อนไขแบบ "ถ้า…ให้…" ซึ่งข้อสอบชอบถามแบบสลับเงื่อนไข ดังนั้นจำเป็นคู่:

สถานการณ์สไลด์บอกให้ทำอะไรเหตุผล
ข้อมูลเล็ก (i.e. \(< 2{,}000\))ใช้ batch size = \(m\) ไปเลย (คือ Batch GD เต็ม)ข้อมูลน้อยพอที่จะคำนวณครบทั้งชุดต่อก้าวโดยไม่ช้า จึงไม่มีเหตุผลต้องยอมรับ noise
ข้อมูลใหญ่ใช้ค่าระหว่าง 1 ถึง \(m\); จุดเริ่มต้นที่นิยมคือ 32–256ค่าที่เหมาะขึ้นกับหน่วยความจำ, การใช้งานฮาร์ดแวร์, การ optimize และตัวงานเอง
เลือกเลขไหนในช่วงนั้นเลขที่เป็นกำลังของ 2 (32, 64, 128, 256) เป็นที่นิยมมักทำงานได้มีประสิทธิภาพกับฮาร์ดแวร์ GPU — แต่ไม่ใช่ข้อบังคับ (สไลด์ขีดเส้นใต้ "but this is not a requirement" ไว้เอง)

ภาพ contour ที่มุมขวาของหน้านี้เทียบเส้นทางสามสีบนพื้นผิว cost เดียวกัน: เส้นน้ำเงิน (Batch GD) เดินเป็นเส้นตรงเรียบ ๆ เข้าหาจุดแดงกลางภาพ, เส้นเขียว (Mini-batch) หยักเล็กน้อยแต่ยังมุ่งตรง, เส้นม่วง (SGD) หยักถี่และกว้างมากจนวนรอบจุดหมายหลายรอบ — ความหยักของทั้งสามเส้นเรียงตามขนาด \(B\) จากใหญ่ไปเล็กเป๊ะ ๆ ตามกฎ \(1/\sqrt{B}\). และ Note ในกรอบท้ายสไลด์เตือนว่า Stochastic GD กับ Mini-batch GD ก็ไปถึงจุดต่ำสุดได้เหมือนกัน ถ้าใช้ learning schedule ที่ดี — ประโยคนี้แก้ความเข้าใจผิดว่า "SGD ไม่มีวันลู่เข้า" ซึ่งไม่จริง มันแค่ต้องลด learning rate ลงตามเวลา (หัวข้อ §4.4).

3.6 แบบฝึกหัดในสไลด์: วาดกราฟ cost เอง (หน้า 13)

Slide Cost in Training Mini-Batch Gradient Descent showing two blank sets of axes, J versus number of epochs, one labelled Batch Gradient Descent and one labelled Mini-Batch Gradient Descent, left empty as an exercise
Slide: Cost in Training Mini-Batch Gradient Descent — แกนเปล่าให้นักศึกษาร่างเอง (Lecture4.pdf, p.13)

นี่คือแบบฝึกหัดที่อาจารย์เว้นคำตอบไว้ — มีแค่แกน \(J\) กับ #epochs สองคู่ คู่ซ้ายชื่อ Batch Gradient Descent คู่ขวาชื่อ Mini-Batch Gradient Descent. เฉลยเต็ม:

 Batch GDMini-Batch GD
รูปร่างเส้นลดลงเรียบทุก epoch ไม่มีเด้งขึ้นลดลงโดยรวมแต่ ขึ้น ๆ ลง ๆ ระหว่างทาง
เหตุผลทุกก้าวคำนวณจากข้อมูลชุดเดิมทั้งหมด ⇒ \(J\) ที่วัดคือฟังก์ชันเดียวกันเสมอ และ gradient descent รับประกันว่าลดลง (ถ้า \(\alpha\) ไม่ใหญ่เกิน)แต่ละก้าววัด cost จาก mini-batch คนละก้อน ⇒ ก้อนที่ "ยาก" ให้ cost สูงกว่าก้อนที่ "ง่าย" แม้โมเดลจะดีขึ้นจริง
จุดที่ห้ามสรุปผิดถ้าเส้นเด้งขึ้น แปลว่า \(\alpha\) สูงเกินไป (เป็นสัญญาณผิดปกติ)เส้นเด้งขึ้นเป็นเรื่องปกติ ไม่ใช่ bug — ให้ดูแนวโน้มเฉลี่ยแทน

หลักฐานตัวเลขจริงของอาจารย์เองอยู่ที่หน้า 24: กราฟ cost ของ Mini-Batch GD แกว่งอยู่ระหว่างประมาณ 0.27 ถึง 0.58 ตลอด 50 epoch โดยมีแนวโน้มลดลงช้า ๆ — ตรงกับช่องขวาของตารางนี้ทุกประการ. ถ้าคุณเจอกราฟแบบนั้นในข้อสอบแล้วตอบว่า "โมเดลไม่เรียนรู้" จะผิด คำตอบที่ถูกคือ "เป็นลักษณะปกติของ mini-batch เพราะแต่ละก้าวเห็นข้อมูลคนละก้อน".

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
ทั้งสามวิธีประมาณ gradient ตัวเดียวกัน (ค่าคาดหวังเท่ากันหมด) ต่างกันแค่ "ใช้กี่ตัวอย่างในการประมาณ" — ยิ่งน้อยยิ่งไม่แม่น (noise \(\propto1/\sqrt B\)) แต่ยิ่งอัปเดตบ่อย. Mini-batch อยู่ตรงกลางจึงได้ทั้งความถี่และความนิ่งพอประมาณ บวกกับได้ใช้การคูณเมทริกซ์.
เปลี่ยนแล้วเกิดอะไร
ที่ \(m=10{,}000\): \(B=1\) ได้ 10,000 iteration/epoch (noisy สุด), \(B=1{,}000\) ได้ 10, \(B=10{,}000\) ได้ 1 (นิ่งสุดแต่คืบหน้าช้าสุดต่อ epoch). ถ้าเพิ่ม \(B\) เป็นสองเท่าโดยไม่แตะ \(\alpha\) คุณจะได้ทิศที่นิ่งขึ้น \(\sqrt2\) เท่าแต่ก้าวน้อยลงครึ่งหนึ่ง.
เชื่อมกับ
\(D=\frac1m\Delta\) ใน Lesson 3 §4 คือค่าเฉลี่ยของ gradient — ตรงนี้แค่เปลี่ยน \(m\) เป็น \(B\); และ Lesson 6 §8 แนะนำ \(B\) เริ่มที่ 32–256 ตามสไลด์หน้า 12 หน้าเดียวกันนี้.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 3Mini-batch GD
มี training set 10,000 ตัวอย่าง แบ่งเป็น mini-batch ขนาด 1,000 ตัวอย่าง จำนวน iteration ต่อ 1 epoch เท่ากับเท่าไหร่ (ตามนิยามที่อาจารย์ให้ในหน้า 11)?
คำถาม 4Batch GD vs SGD
ตามหน้า 9–10 ข้อได้เปรียบและข้อเสียเปรียบของ SGD เทียบกับ Batch GD ข้อใดตรงกับที่อาจารย์ทำสีไว้?
คำถาม 5Batch Size
คุณมีข้อมูลเพียง 1,500 ตัวอย่าง คำแนะนำของสไลด์หน้า 12 คือข้อใด?

4. Momentum: ใช้ประวัติของ Gradient ช่วยเร่งความเร็ว

ปัญหาที่เหลืออยู่: mini-batch แก้เรื่อง "ก้าวถี่" ได้แล้ว แต่ยังมีอาการที่แก้ไม่ได้ — เดินซิกแซก. ลองนึกถึงหุบเขาแคบยาวรูปตัว U: ความชันด้านข้าง (ผนังหุบ) สูงมาก ส่วนความชันตามแนวยาวของหุบ (ทิศที่เราอยากไป) ต่ำมาก. gradient descent จึงกระเด้งซ้าย-ขวาระหว่างผนังทั้งสองข้างรุนแรงกว่าที่มันจะไหลไปตามหุบ — เสียเวลาไปกับทิศที่ไม่ได้พาเข้าใกล้เป้าหมายเลย. Momentum คือคำตอบของอาจารย์สำหรับอาการนี้.

Slide Gradient Descent with Momentum: momentum optimization (Polyak 1964) accelerates faster convergence in the right direction by computing an exponential weighted average of the gradients, giving a better estimate closer to the actual derivative than noisy calculations; the picture is a top-down view of a 3D loss surface with unwanted oscillations in the vertical direction and progress towards the minimum in the horizontal direction
Slide: Gradient Descent with Momentum (Lecture4.pdf, p.14)

อาจารย์อ้างอิง Momentum optimization (Polyak, 1964) ว่าช่วย accelerate faster convergence ไปใน right direction โดยวิธีคือ "นำ gradient ที่ผ่านมาในแต่ละ iteration มาพิจารณาด้วย" ผ่านการคำนวณ exponential weighted averages of the gradients แล้วใช้ค่าเฉลี่ยตัวนั้นไปอัปเดต weight แทนที่จะใช้ gradient สด ๆ. เหตุผลที่ทำแบบนี้ได้ผลอยู่ในบรรทัดสุดท้ายที่ทำสีเช่นกัน: ค่าเฉลี่ยแบบเอ็กซ์โพเนนเชียลให้ค่าประมาณที่ "ใกล้ actual derivate มากกว่าการคำนวณแบบ noisy ของเรา" — คือถือว่า gradient จาก mini-batch เดียวเป็นค่าที่ผิดพลาดได้ แต่ค่าเฉลี่ยของหลาย ๆ ก้อนย้อนหลังใกล้ "อนุพันธ์จริง" มากกว่า.

ภาพประกอบหน้านี้อาจารย์กำกับไว้เองว่าเป็น top-down view ของ 3D loss surface คือมองวงรีซ้อนกันจากด้านบนเหมือนแผนที่เส้นชั้นความสูง. เส้นทางที่วาดเป็นฟันเลื่อยถี่ ๆ มีป้ายกำกับสองป้าย: "Unwanted oscillations in the vertical direction" (การแกว่งที่ไม่ต้องการในแนวตั้ง) กับ "Progress towards the minimum in the horizontal direction" (ความคืบหน้าจริงอยู่ในแนวนอน) ปลายทางคือ Target minimum. อ่านความหมายเป็นตัวเลขได้แบบนี้: ถ้าใน 10 ก้าว gradient แนวตั้งสลับเครื่องหมาย \(+,-,+,-,\dots\) ตลอด ผลรวมของมันเกือบเป็นศูนย์ (เสียเปล่า) ในขณะที่ gradient แนวนอนเป็นบวกเล็ก ๆ ทั้ง 10 ก้าว ผลรวมจึงสะสมเป็นค่ามาก — Momentum คือเครื่องมือที่ทำให้ทิศที่ "ไปทางเดิมสม่ำเสมอ" ได้ก้าวใหญ่ขึ้น ส่วนทิศที่ "สลับไปมา" หักล้างกันเองจนเหลือเกือบศูนย์.

4.1 Exponential Weighted Moving Averages คืออะไร (หน้า 15)

ต้องรู้ก่อน — ค่าเฉลี่ย และ "ค่าเฉลี่ยถ่วงน้ำหนัก"

ค่าเฉลี่ยธรรมดา (average): มีตัวเลข 3 ตัว 10, 20, 30 ⇒ เอามาบวกกันแล้วหารจำนวนตัว \(\frac{10+20+30}{3}=20\). มองอีกแบบ: ทุกตัวได้ "น้ำหนัก" เท่ากันคือ \(\tfrac13\) แล้วเอาน้ำหนักคูณค่าแล้วบวกกัน \(\tfrac13(10)+\tfrac13(20)+\tfrac13(30)=20\).

ค่าเฉลี่ยถ่วงน้ำหนัก (weighted average): เปลี่ยนน้ำหนักให้ไม่เท่ากัน เช่นให้ตัวล่าสุดสำคัญกว่า — ใช้น้ำหนัก 0.2, 0.3, 0.5 (ต้องรวมกันได้ 1 เสมอ):

$$0.2(10)+0.3(20)+0.5(30) = 2+6+15 = 23$$

ได้ 23 ซึ่งเอนไปทาง 30 (ตัวที่ได้น้ำหนักมากสุด) มากกว่าค่าเฉลี่ยธรรมดา 20. เงื่อนไขเดียวที่ต้องจำ: ถ้าน้ำหนักรวมกันได้ 1 ค่าเฉลี่ยจะอยู่ในช่วงเดียวกับข้อมูล; ถ้าน้ำหนักรวมได้น้อยกว่า 1 ผลลัพธ์จะถูกดึงลงต่ำกว่าความจริง — จำข้อนี้ไว้ให้ดี เพราะมันคือต้นตอของ "bias" ที่ Adam ต้องแก้ในหน้า 23.

Exponential weighted average ก็คือ weighted average ที่น้ำหนักถูกกำหนดด้วยสูตรเดียว: ตัวล่าสุดได้มากสุด แล้วลดลงเป็นทวีคูณเมื่อย้อนไปอดีต.

Slide Exponential Weighted Moving Averages: the exponential weighted average gives a weighted average of the last n values where the weighting decreases exponentially with each previous value; formula v_t = beta*v_(t-1) + (1-beta)*value_t with beta in [0,1]; the definition expanded for consecutive v at t, t-1 and t-2 and combined into a single expression; with a price chart comparing a 30 day SMA and a 30 day EMA
Slide: Exponential Weighted Moving Averages — ขยายสูตรแบบ recursive (Lecture4.pdf, p.15)

อาจารย์เริ่มจากประโยคนิยามที่เน้นไว้: EWA ให้ "weighted average ของ \(n\) ค่าล่าสุด โดยน้ำหนักลดลงแบบเอ็กซ์โพเนนเชียลในทุก ๆ ค่าที่ย้อนไป" แล้วให้สูตรตัวเดียว:

$$v_t = \beta\,v_{t-1} + (1-\beta)\,\text{value}_t\qquad\text{where }\beta\in[0,1]$$

ค่าเฉลี่ยใหม่ = (สัดส่วน \(\beta\) ของค่าเฉลี่ยเก่า) + (สัดส่วน \(1-\beta\) ของค่าใหม่ที่เพิ่งเห็น)

สังเกตว่าสูตรนี้เก็บตัวเลขไว้แค่ ตัวเดียว คือ \(v\) — ไม่ต้องจำค่าย้อนหลัง 30 ค่าเหมือนค่าเฉลี่ยเคลื่อนที่ธรรมดา. กราฟราคาหุ้นที่อาจารย์แปะไว้เทียบให้เห็นความต่างนี้: เส้นน้ำเงินคือราคาจริงที่กระโดดขึ้นลงรายวัน ส่วนเส้นแดง (30 day SMA) กับเส้นเหลือง (30 day EMA) เรียบทั้งคู่ แต่ EMA ขยับตามราคาจริงไวกว่า SMA เล็กน้อยในช่วงที่ราคาพุ่ง (ราวช่วงกลางกราฟที่ราคาเด้งขึ้นแตะ 125) เพราะ EMA ให้น้ำหนักค่าล่าสุดมากกว่า — คุณสมบัติ "เรียบแต่ยังไว" ตัวนี้เองที่ Momentum ต้องการจาก gradient.

คำนวณ: สร้าง EWA จากศูนย์ด้วยตัวเลขจริง 4 ตัว (\(\beta=0.5\) เพื่อให้คำนวณในใจได้)
ให้ค่าที่วัดได้ตามเวลา: \(\text{value}_1=30,\ \text{value}_2=32,\ \text{value}_3=28,\ \text{value}_4=34\) และเริ่มที่ \(v_0=0\) (ตามที่อัลกอริทึมจริงทำ)
  1. ขั้นที่ 1 — \(t=1\): เอา \(v_0=0\) มาถ่วง 0.5 แล้วบวกค่าใหม่ถ่วง 0.5
    $$v_1 = 0.5(0) + 0.5(30) = 15.0$$

    ได้ 15 ทั้งที่ค่าจริงคือ 30 — ต่ำไปครึ่งหนึ่ง เพราะครึ่งหนึ่งของน้ำหนักถูกจ่ายให้ \(v_0\) ที่ยังเป็นศูนย์

  2. ขั้นที่ 2 — \(t=2\)
    $$v_2 = 0.5(15.0) + 0.5(32) = 7.5+16 = 23.5$$
  3. ขั้นที่ 3 — \(t=3\)
    $$v_3 = 0.5(23.5) + 0.5(28) = 11.75+14 = 25.75$$
  4. ขั้นที่ 4 — \(t=4\)
    $$v_4 = 0.5(25.75) + 0.5(34) = 12.875+17 = 29.875$$
  5. ขั้นที่ 5 — เทียบกับค่าเฉลี่ยธรรมดาของสี่ตัว
    $$\frac{30+32+28+34}{4} = 31.0$$

สองข้อสังเกตที่ต้องจำ: (1) \(v_4=29.875\) ยังต่ำกว่าค่าเฉลี่ยจริง 31 อยู่เล็กน้อย — นี่คือ bias ที่ตกค้างจากการเริ่มที่ 0 และจะยิ่งหนักเมื่อ \(\beta\) ใกล้ 1. (2) \(v\) เรียบกว่าข้อมูลดิบ: ข้อมูลกระโดด 30→32→28→34 (สวิง 6 หน่วย) แต่ \(v\) ไต่ขึ้นทีละน้อย 15→23.5→25.75→29.875 ไม่ตอบสนองต่อการดิ่งลงที่ \(t=3\) มากนัก. นี่คือสิ่งที่เราต้องการจาก gradient ที่ noisy.

พิสูจน์: ทำไมน้ำหนัก "ลดลงแบบเอ็กซ์โพเนนเชียล" (กางสูตรตามที่สไลด์ทำทุกบรรทัด)
ให้สามสมการที่สไลด์เขียนเรียงกัน: \(v_t=\beta v_{t-1}+(1-\beta)\text{value}_t\), \(v_{t-1}=\beta v_{t-2}+(1-\beta)\text{value}_{t-1}\), \(v_{t-2}=\beta v_{t-3}+(1-\beta)\text{value}_{t-2}\)
  1. ขั้นที่ 1 — แทนสมการที่สองลงในสมการแรก (กำจัด \(v_{t-1}\))
    $$\begin{aligned} v_t &= \beta\big[\beta v_{t-2} + (1-\beta)\text{value}_{t-1}\big] + (1-\beta)\text{value}_t\\ &= \beta^2 v_{t-2} + \beta(1-\beta)\text{value}_{t-1} + (1-\beta)\text{value}_t \end{aligned}$$
  2. ขั้นที่ 2 — แทนสมการที่สามต่อ (กำจัด \(v_{t-2}\)) ได้บรรทัดสุดท้ายของสไลด์
    $$v_t = \beta^3 v_{t-3} + \beta^2(1-\beta)\,\text{value}_{t-2} + \beta(1-\beta)\,\text{value}_{t-1} + (1-\beta)\,\text{value}_t$$
  3. ขั้นที่ 3 — อ่านน้ำหนักของแต่ละค่าออกมา เมื่อ \(\beta=0.9\)
    $$\text{value}_t: 0.1,\quad \text{value}_{t-1}: 0.09,\quad \text{value}_{t-2}: 0.081,\quad \text{value}_{t-3}: 0.0729$$

    ทุกครั้งที่ย้อนไปอีกหนึ่งขั้น น้ำหนักถูกคูณ \(\beta=0.9\) เพิ่ม — การคูณค่าคงที่ซ้ำ ๆ คือนิยามของ "ลดแบบเอ็กซ์โพเนนเชียล" จึงเป็นที่มาของชื่อ

  4. ขั้นที่ 4 — รวมน้ำหนักทั้งหมดที่จ่ายไปจริงหลังผ่าน \(t\) ก้าว
    $$(1-\beta)\big(1+\beta+\beta^2+\dots+\beta^{t-1}\big) = 1-\beta^{t}$$
    \(t\)\(1-\beta^t\) เมื่อ \(\beta=0.5\)\(1-\beta^t\) เมื่อ \(\beta=0.9\)
    10.50000.1000
    20.75000.1900
    40.93750.3439
    100.99900.6513

น้ำหนักรวมเป็น \(1-\beta^t\) ซึ่งน้อยกว่า 1 เสมอในช่วงแรก และเข้าใกล้ 1 เมื่อ \(t\) โตขึ้น. ตามกฎในกล่อง "ต้องรู้ก่อน" น้ำหนักรวมไม่ถึง 1 แปลว่าค่าที่ได้ถูกดึงลงต่ำกว่าความจริง — ที่ \(\beta=0.9,\ t=1\) จ่ายน้ำหนักไปแค่ 10% ค่าที่ได้จึงเล็กกว่าของจริง 10 เท่า! นี่คือคำอธิบายเชิงตัวเลขว่าทำไม Adam ต้องมี bias correction ที่หารด้วย \(1-\beta^t\) พอดีเป๊ะ (หน้า 23) — หารด้วยน้ำหนักรวมที่จ่ายไปจริง ก็ได้ค่าเฉลี่ยที่ "เต็ม 100%" กลับคืนมา.

4.2 สูตรของ Momentum และโค้ด Keras (หน้า 16)

Slide Implementation: repeat until convergence with v_i := beta*v_i + (1-beta)*dJ(theta_i)/dtheta_i and the highlighted update theta_i := theta_i - alpha*v_i; hyperparameters are now alpha and beta with beta normally 0.9; a note that := means computing the right-hand side using the old v then assigning back; and the Keras line optimizer = keras.optimizers.SGD(lr=0.001, momentum=0.9)
Slide: Implementation — pseudocode ของ Momentum + บรรทัด Keras (Lecture4.pdf, p.16)

อาจารย์เขียน pseudocode สองบรรทัด โดยไฮไลต์สีเหลืองไว้ที่บรรทัดอัปเดต \(\theta\) เพราะเป็นบรรทัดที่เปลี่ยนไปจาก gradient descent เดิม:

$$\text{Repeat until convergence}\ \Big\{\ \ v_i := \beta v_i + (1-\beta)\frac{\partial J(\theta_i)}{\partial\theta_i};\qquad \theta_i := \theta_i - \alpha\,v_i\ \ \Big\}$$

เทียบกับ gradient descent เดิม \(\theta_i := \theta_i - \alpha\frac{\partial J}{\partial\theta_i}\) จะเห็นว่าจุดเดียวที่ต่างคือ เอา \(v_i\) มาแทนที่ gradient สด — โครงอื่นเหมือนเดิมหมด. และอาจารย์ทิ้ง Note ไว้ข้าง ๆ ว่าเครื่องหมาย \(:=\) หมายถึง "คำนวณฝั่งขวาโดยใช้ \(v\) ตัวเก่า แล้วค่อยเอาผลลัพธ์ไปทับ \(v\)" — ฟังดูเหมือนจู้จี้ แต่สำคัญมากเวลาเขียนโค้ดเอง เพราะถ้าคุณอัปเดต \(v\) ทีละตัวโดยใช้ค่าที่เพิ่งอัปเดตไปแล้ว ผลจะผิด. hyperparameter ของ Momentum จึงมี 2 ตัว คือ \(\alpha\) (learning rate) และ \(\beta\) (โดยปกติ 0.9) — เพิ่มจาก gradient descent ธรรมดาที่มีแค่ \(\alpha\).

คำนวณ: Momentum ขยายทิศที่สม่ำเสมอ และหักล้างทิศที่แกว่ง (\(\beta=0.9\), เริ่ม \(v_0=0\))
ให้เทียบ gradient สองรูปแบบ 4 ก้าว: ก) ทิศคงที่ \(+1,+1,+1,+1\) (แกนนอนในภาพหน้า 14)  |  ข) แกว่งสลับ \(+1,-1,+1,-1\) (แกนตั้ง)
  1. ขั้นที่ 1 — แบบ ก) ไปทางเดิมต่อเนื่อง
    $$\begin{aligned} v_1&=0.9(0)+0.1(1)=0.100\\ v_2&=0.9(0.100)+0.1(1)=0.190\\ v_3&=0.9(0.190)+0.1(1)=0.271\\ v_4&=0.9(0.271)+0.1(1)=0.3439 \end{aligned}$$

    \(v\) สะสมโตขึ้นเรื่อย ๆ (ค่าเหล่านี้คือ \(1-0.9^t\) พอดี ตามที่พิสูจน์ไว้ใน §4.1; ที่ \(t=10\) จะได้ 0.6513)

  2. ขั้นที่ 2 — แบบ ข) สลับทิศไปมา
    $$\begin{aligned} v_1&=0.9(0)+0.1(1)=0.100\\ v_2&=0.9(0.100)+0.1(-1)=-0.010\\ v_3&=0.9(-0.010)+0.1(1)=0.091\\ v_4&=0.9(0.091)+0.1(-1)=-0.0181 \end{aligned}$$

    \(v\) วนเวียนใกล้ศูนย์ เพราะค่าบวกกับค่าลบหักล้างกันทุกก้าว

หลัง 4 ก้าว ทิศที่สม่ำเสมอได้ขนาดก้าว 0.3439 ส่วนทิศที่แกว่งเหลือ 0.0181 — ต่างกัน 19 เท่า ทั้งที่ gradient ดิบมีขนาดเท่ากัน (\(|{\pm}1|\)) ทุกก้าว. นี่คือความหมายเชิงตัวเลขของป้าย "Unwanted oscillations" กับ "Progress towards the minimum" บนภาพหน้า 14: Momentum ไม่ได้ลบการแกว่งทิ้ง แต่ทำให้การแกว่งหักล้างกันเองจนเหลือน้อย ขณะที่ทิศที่ถูกต้องถูกทบไปเรื่อย ๆ.

สำหรับการใช้งานจริง อาจารย์บอกว่า "Implementing Momentum optimization in Keras is just use the SGD optimizer and set its momentum hyperparameter \(\beta\)" ซึ่งต้องตั้งค่าระหว่าง 0 (high friction — ฝืดมาก เหมือนไม่มี momentum) ถึง 1 (no friction — ไร้แรงเสียดทาน) แล้วให้โค้ดบรรทัดเดียวไว้ท้ายสไลด์:

optimizer = keras.optimizers.SGD(lr=0.001, momentum=0.9)
โค้ดทำอะไร
keras.optimizers.SGD(...)สร้างอ็อบเจ็กต์ optimizer ตระกูล gradient descent ของ Keras. ชื่อคลาสคือ SGD แต่ตัวมันรับ mini-batch มาอัปเดต (ขนาดก้อนถูกกำหนดตอน model.fit(..., batch_size=...) ไม่ใช่ที่นี่) — ตรงกับที่อาจารย์เขียนในหน้า 18 ว่า mini-batch GD "often called SGD"
lr=0.001learning rate \(\alpha\) = 0.001 คือตัวคูณขนาดก้าวในบรรทัด \(\theta_i := \theta_i - \alpha v_i\). lr เป็นชื่อย่อเดิมของอาร์กิวเมนต์ learning_rate
momentum=0.9คือ \(\beta\) ในสูตร. ค่า 0 = ไม่มี momentum (กลายเป็น gradient descent ธรรมดา), ค่าใกล้ 1 = จำอดีตยาว "ลื่น" มาก. 0.9 คือค่าปกติที่อาจารย์ระบุ
ผลของบรรทัดนี้ทั้งหมดยังไม่มีการเทรนเกิดขึ้น — แค่เตรียมอ็อบเจ็กต์ไว้ส่งให้ model.compile(optimizer=optimizer, loss=...) ในขั้นถัดไป

ผลลัพธ์ที่คาด: บรรทัดนี้ไม่พิมพ์อะไรออกมา ตัวแปร optimizer จะเก็บอ็อบเจ็กต์ SGD ที่มี learning_rate = 0.001 และ momentum = 0.9 อยู่ข้างใน (ตรวจได้ด้วย optimizer.momentum). เสริมความเข้าใจ (นอกสไลด์): ใน Keras รุ่นใหม่ ๆ ชื่อ lr= ถูกเลิกใช้และเปลี่ยนเป็น learning_rate= — ถ้าข้อสอบให้เติมโค้ดโดยไม่ได้ระบุเวอร์ชัน ให้ยึดตามที่สไลด์เขียนไว้.

ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 6EWA Expansion
จากสูตร vₜ = β·vₜ₋₁ + (1−β)·valueₜ เมื่อกางแบบ recursive ย้อนไป 2 ขั้นตามสไลด์หน้า 15 สัมประสิทธิ์ที่คูณอยู่หน้า valueₜ₋₂ คืออะไร?

4.3 Momentum ช่วยเรื่อง Local Minima ด้วย (หน้า 17)

Slide More on Momentum: apart from convergence speed momentum also solves the problems of local minima; inspired from physics, a ball at a local minimum with enough momentum will not get stuck and escapes from plateaus much faster than gradient descent; with a contour sketch of a zigzag path and a loss curve showing a shallow local minimum next to a deeper global minimum, plus the v_i update formula
Slide: More on Momentum — หนี local minima ได้ด้วย (Lecture4.pdf, p.17)

นอกจากเรื่องความเร็วแล้ว Momentum ยังแก้ปัญหา local minima ได้ด้วย โดยอาจารย์อธิบายด้วยอุปมาทางฟิสิกส์: ถ้าลูกบอลที่กลิ้งมาถึงจุด local minimum มี โมเมนตัมมากพอ มันจะไม่ติดอยู่ตรงนั้น และจะหลุดออกจาก plateau (พื้นราบ) ได้ "เร็วกว่า gradient descent มาก". ลองคิดเป็นตัวเลขด้วยสูตรเดียวกันที่เพิ่งคำนวณ: สมมติลูกบอลกลิ้งลงเนินมาแล้ว 4 ก้าวโดย gradient เป็น \(+1\) ตลอด ได้ \(v_4=0.3439\); พอถึงก้นหลุมตื้น ๆ ที่ gradient กลายเป็น 0 พอดี ก้าวถัดไปยังเป็น \(v_5 = 0.9(0.3439)+0.1(0)=0.3095\) — ยังเดินต่อได้เกือบเท่าเดิม ทั้งที่ gradient เป็นศูนย์. เทียบกับ gradient descent ธรรมดาที่ \(\alpha\times0=0\) คือหยุดนิ่งสนิททันที. ความต่างนี้คือคำตอบของคำถาม "ทำไม Momentum หนีหลุมได้".

ภาพบนสไลด์แปะไว้สองรูป: รูปซ้ายเป็นเส้นชั้นความสูงรูปวงรีพร้อมเส้นทางหยักที่วิ่งเข้าสู่กลางวง (การซิกแซกที่ลดลง) และรูปขวาเป็นกราฟ Loss ที่มีหลุมตื้น (Local minimum) ก่อนหลุมลึก (Global minimum) — สองรูปนี้คือสองปัญหาที่ Momentum แก้พร้อมกัน: ซิกแซก (จากหน้า 14) และติดหลุม (จากหน้า 8).

4.4 Learning Rate ยังเป็นปัญหา และ Learning Rate Decay (หน้า 18–19)

Slide More on Momentum (2): in mini-batch gradient descent (often called SGD) finding an optimal learning rate can be challenging; too high or too low is a trade-off between speed and performance; a high learning rate may cause oscillations near the minimum; the loss-versus-epoch picture labels curves eta way too high diverges, eta too small slow, eta too high suboptimal, eta just right, and start with a high learning rate then reduce it; momentum needs a reduced learning rate as training progresses
Slide: More on Momentum (2) — ปัญหาของการเลือก learning rate (Lecture4.pdf, p.18)

อาจารย์เริ่มด้วยประโยคที่ยืนยันเรื่องคำศัพท์ที่เราคุยกันไว้ใน §2: "In mini-batch gradient descent (often called stochastic gradient descent or SGD)" — แล้วบอกว่าการหา optimal learning rate (η) เป็นเรื่องท้าทาย. เหตุผล: \(\eta\) สูงหรือต่ำเกินไปคือการแลกกันระหว่าง ความเร็ว กับ ประสิทธิภาพ. และประโยคสำคัญที่มักถูกเข้าใจผิด: ถึงแม้ \(\eta\) สูงจะไม่ทำให้ลู่ออก (diverge) มันก็ยังอาจทำให้แกว่งอยู่รอบ ๆ จุดต่ำสุดแทนที่จะลู่เข้าอย่างนุ่มนวล — คือ "ไม่ระเบิด" ไม่ได้แปลว่า "ดี".

กราฟ Loss เทียบ Epoch บนสไลด์ติดป้ายเส้นไว้ 4 เส้น ซึ่งเป็นชุดคำตอบสำเร็จรูปของข้อสอบเรื่อง learning rate: เส้นที่พุ่งขึ้นคือ "η way too high: diverges"; เส้นส้มที่ลดช้าและยังสูงอยู่ตอนจบคือ "η too small: slow"; เส้นที่ลดเร็วแต่ไปจอดที่ค่าสูงกว่าที่ควรคือ "η too high: suboptimal"; เส้นที่ลดเร็วและลงต่ำสุดคือ "η just right". และข้อความสีม่วงใต้กราฟสรุปกลยุทธ์ที่ดีที่สุดไว้ว่า "Start with a high learning rate then reduce it: perfect!". ประโยคปิดของหน้านี้คือที่มาของหัวข้อถัดไป: Momentum ใช้ learning rate ค่าเดียวและเจอปัญหาเดียวกับ SGD แต่หนักกว่าเพราะขนาดก้าวที่ใหญ่ขึ้นจาก momentum เอง จึงมักต้องการ a reduced learning rate as training progresses.

ต้องรู้ก่อน — "ลดแบบเอ็กซ์โพเนนเชียล" ต่างจาก "ลดแบบเชิงเส้น" อย่างไร

ลดเชิงเส้น (linear) = ลบ จำนวนเท่ากันทุกรอบ หรือ (อย่างที่สไลด์ใช้) หารครึ่งทุกช่วงเวลาที่กำหนด: 100 → 50 → 25 → 12.5

ลดเอ็กซ์โพเนนเชียล (exponential) = คูณ ด้วยตัวเลขเดิมทุกรอบ: คูณ 0.1 ทุกครั้ง ⇒ 100 → 10 → 1 → 0.1

เขียนเป็นสูตรทั่วไป \(\alpha_t = \alpha_0 \cdot r^{\,k}\) เมื่อ \(r\) = ตัวคูณต่อรอบ และ \(k\) = จำนวนรอบที่ผ่านไป. จุดที่ต้องระวัง: การลดแบบคูณไม่มีวันถึงศูนย์ (คูณ 0.1 ไปเรื่อย ๆ ได้ 0.1, 0.01, 0.001, … เล็กลงแต่ยังเป็นบวกเสมอ) แต่มันเล็กลงเร็วมากในช่วงแรก — เร็วกว่าการหารครึ่งทุกครั้งหลายเท่า. สังเกตว่านี่คือกลไกเดียวกับน้ำหนัก \(\beta^k\) ของ EWA ใน §4.1 เป๊ะ ๆ เพียงแต่เอามาใช้กับ learning rate แทนที่จะใช้กับน้ำหนักของ gradient.

Slide More on Momentum (3): one way to reduce the learning rate as training progresses is to set learning rate decay, especially with small minibatches; the ideal scenario has parameters at their optimum slowing down while those farther from optima continue; two step-shaped learning-rate plots over 20 epochs labelled Linear decay (decrease by half every 5 epochs) and Exponential decay (multiply by 0.1 every 8 epochs), with Keras ExponentialDecay code
Slide: More on Momentum (3) — Learning Rate Decay สองแบบ (Lecture4.pdf, p.19)

วิธีหนึ่งที่จะลด learning rate ระหว่างเทรนคือตั้ง learning rate decay โดยเฉพาะเมื่อเทรนด้วย minibatch ขนาดเล็ก (ซึ่ง noisy มากตามกฎ \(1/\sqrt B\) จึงต้องก้าวสั้นลงเมื่อเข้าใกล้เป้า). อาจารย์อธิบายสถานการณ์ในอุดมคติไว้ว่า: พารามิเตอร์ที่ถึงจุดเหมาะสมแล้วควรชะลอหรือหยุดเรียน ส่วนพารามิเตอร์ที่ยังอยู่ไกลจากจุดเหมาะสมควรเดินต่อ — สังเกตให้ดีว่านี่คือคำบรรยายของ "อุดมคติ" ที่ learning rate decay ตัวเดียวทำไม่ได้ เพราะมันลดอัตราให้ทุกพารามิเตอร์พร้อมกันหมด. ความอึดอัดข้อนี้เองที่หน้า 20 จะแก้ด้วย adaptive optimizer ที่ปรับอัตราให้ แต่ละพารามิเตอร์แยกกัน. สองกลยุทธ์ที่สไลด์ยกตัวอย่างพร้อมกราฟ 20 epoch:

คำนวณ: learning rate ตามสองตัวอย่างบนสไลด์ (เริ่มที่ 0.1 — กราฟบนสไลด์เริ่มที่ 1.0 หลักการเดียวกัน)
ให้ Linear decay: "decrease it by half every 5 epochs"  |  Exponential decay: "multiply the learning rate by 0.1 every 8 epochs"
  1. ขั้นที่ 1 — Linear decay: หารสองทุก 5 epoch
    $$\text{epoch }1\text{–}5: 0.1\ \to\ 6\text{–}10: 0.05\ \to\ 11\text{–}15: 0.025\ \to\ 16\text{–}20: 0.0125$$

    ตรงกับกราฟซ้ายของสไลด์ที่เป็นบันไดลง 4 ขั้นใน 20 epoch

  2. ขั้นที่ 2 — Exponential decay: คูณ 0.1 ทุก 8 epoch \(\big(\alpha=\alpha_0\cdot 0.1^{\lfloor \text{epoch}/8\rfloor}\big)\)
    $$\text{epoch }0\text{–}7: 0.1\ \to\ 8\text{–}15: 0.01\ \to\ 16\text{–}23: 0.001$$

    ตรงกับกราฟขวาที่ดิ่งฮวบหลัง epoch 8 แล้วแบนเกือบติดแกนนอน

  3. ขั้นที่ 3 — เทียบกันที่ epoch 16
    $$\text{linear}: 0.0125\qquad\text{vs}\qquad\text{exponential}: 0.001$$

ที่ epoch 16 แบบเอ็กซ์โพเนนเชียลให้ก้าวเล็กกว่าแบบเชิงเส้นถึง 12.5 เท่า — ทั้งที่เริ่มจากค่าเดียวกัน. บทเรียน: ช่วงต้นก้าวใหญ่เพื่อเข้าใกล้เร็ว ช่วงท้ายก้าวเล็กเพื่อไม่กระโดดข้ามจุดต่ำสุด และ "เล็กแค่ไหน" ขึ้นกับว่าเลือกกลยุทธ์ไหน.

โค้ดในกรอบท้ายสไลด์แสดงวิธีทำ exponential decay ใน Keras:

lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate=0.1,
    decay_steps=8,
    decay_rate=0.1
)
optimizer = keras.optimizers.SGD(lr=0.001,
                                 mometum=0.9,
                                 learning_rate=lr_schedule)
โค้ดทำอะไร
keras.optimizers.schedules.ExponentialDecay(...)สร้าง "ตารางเวลา" ของ learning rate ไม่ใช่ตัวเลขตัวเดียว — เป็นอ็อบเจ็กต์ที่ถูกเรียกทุก step แล้วคืนค่า \(\alpha\) ของ step นั้น
initial_learning_rate=0.1ค่า \(\alpha_0\) ตอนเริ่ม ตรงกับจุดเริ่มต้นของกราฟ
decay_steps=8"ทุก ๆ 8 step ให้ลดหนึ่งครั้ง" — ตรงกับคำว่า "every 8 epochs" บนสไลด์ (ระวัง: ในโค้ดจริงหน่วยคือ step/iteration ไม่ใช่ epoch; ถ้า 1 epoch มี 10 mini-batch การตั้ง 8 จะหมายถึงไม่ถึงหนึ่ง epoch ด้วยซ้ำ)
decay_rate=0.1ตัวคูณต่อหนึ่งรอบการลด: \(\alpha = 0.1\times 0.1^{\text{step}/8}\)
keras.optimizers.SGD(...)สร้าง optimizer แบบ momentum เหมือนหน้า 16 แต่คราวนี้ป้อน schedule เข้าไปแทนตัวเลขคงที่
learning_rate=lr_scheduleช่อง learning_rate รับได้ทั้งตัวเลข และ อ็อบเจ็กต์ schedule — นี่คือจุดสำคัญที่สุดของโค้ดนี้ และเป็นจุดที่ข้อสอบเติมโค้ดชอบเจาะ
lr=0.001 ในบรรทัดเดียวกันขัดแย้งกันเอง: lr คือชื่อเดิมของ learning_rate การส่งทั้งสองพร้อมกันเท่ากับกำหนดค่าเดียวกันสองครั้ง ในทางปฏิบัติ lr=0.001 ไม่มีผลเพราะ schedule ทับ
mometum=0.9สไลด์สะกดผิด — ชื่อจริงคือ momentum; ถ้าพิมพ์ตามนี้ Keras จะฟ้อง error เรื่องอาร์กิวเมนต์ที่ไม่รู้จัก

ผลลัพธ์ที่คาด: เมื่อแก้การสะกดเป็น momentum และตัด lr=0.001 ออกแล้ว ค่า learning rate ที่ schedule คืนให้ในแต่ละ step (คำนวณจากสูตร \(0.1\times0.1^{\text{step}/8}\)) คือ step 0: 0.1, step 8: 0.01, step 16: 0.001, step 20: 0.00032. เสริมความเข้าใจ (นอกสไลด์): ค่าเหล่านี้ลดแบบต่อเนื่อง ไม่ใช่บันไดอย่างในกราฟ (เช่น step 4 ได้ 0.0316 ไม่ใช่ 0.1) ถ้าอยากได้บันไดเป๊ะ ๆ ตามภาพต้องใส่ staircase=True เพิ่ม ซึ่งจะให้ step 0–7 = 0.1 ทั้งหมดแล้วกระโดดลง 0.01 ที่ step 8. คำอธิบายบนสไลด์ ("multiply by 0.1 every 8 epochs") ตรงกับโหมด staircase.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
ใช้ EWA แทนค่าเฉลี่ยธรรมดา เพราะ gradient ล่าสุดควรมีอิทธิพลมากกว่า (ภูมิทัศน์ของ cost ที่ตำแหน่งปัจจุบันต่างจากตอนเริ่ม) — ค่าเฉลี่ยธรรมดาจะลากทิศเก่าที่หมดความเกี่ยวข้องมาถ่วงเท่า ๆ กับทิศใหม่ และยังต้องเก็บค่าย้อนหลังทุกตัวไว้ในหน่วยความจำด้วย ขณะที่ EWA เก็บแค่ \(v\) ตัวเดียว.
เปลี่ยนแล้วเกิดอะไร
ใช้ \(\beta=0.5\) แทน 0.9: น้ำหนักของสามค่าล่าสุดกลายเป็น \(0.5,\ 0.25,\ 0.125\) — ลดฮวบเร็วกว่า \(0.1,\ 0.09,\ 0.081\) มาก. \(\beta\) น้อย = "จำสั้น" ปรับตัวไวแต่ยัง noisy; \(\beta\) มาก = "จำยาว" นิ่งแต่ตอบสนองช้า (ที่ \(\beta=0.9\) ต้องผ่าน 10 ก้าวกว่า \(v\) จะขึ้นไปถึง 65% ของ gradient จริง ตามตาราง \(1-\beta^t\)).
เชื่อมกับ
\(\beta\) ตัวนี้คือ \(\beta_1\) ของ Adam (§5.2) ตัวเดียวกัน, ผลรวมน้ำหนัก \(1-\beta^t\) คือตัวหารของ bias correction (§5.3), และหลักการเดียวกับ momentum ของ Batch Normalization ใน Lesson 5 §3 ที่ใช้ moving average เก็บค่าสถิติตอนเทรนไว้ใช้ตอน test.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 7Momentum
gradient ของพารามิเตอร์ตัวหนึ่งเป็น +1, −1, +1, −1 สลับกันสี่ก้าว อีกตัวเป็น +1 ทั้งสี่ก้าว ถ้าใช้ Momentum (β = 0.9, v₀ = 0) ผลเป็นอย่างไร?
คำถาม 8เติมโค้ด Keras
ต้องการใช้ Momentum แบบที่อาจารย์สอน (β = 0.9, α = 0.001) ใน Keras บรรทัดใดถูกต้องตามสไลด์หน้า 16?

5. RMSProp, Adam และ AdamW: ปรับ Learning Rate อัตโนมัติรายพารามิเตอร์

ช่องว่างที่ยังเหลือจาก §4: learning rate decay ลดอัตราให้ทุกพารามิเตอร์พร้อมกัน ทั้งที่อาจารย์เองบอกในหน้า 19 ว่าอุดมคติคือ "ตัวที่ถึงจุดเหมาะสมแล้วให้ชะลอ ส่วนตัวที่ยังไกลให้เดินต่อ". จะทำแบบนั้นได้ต้องมีอัตราการเรียนรู้ของใครของมัน ซึ่งเป็นไปไม่ได้ถ้าเราต้องตั้งเองทีละตัว (เครือข่ายเล็ก ๆ ยังมี 261 ตัว!). ทางออกคือให้อัลกอริทึมตั้งเอง — นั่นคือ adaptive learning rate.

Slide Adaptive Learning Rate: another way to tune the learning rate is to use more clever optimizers; adaptive learning optimizers use a heuristic way that automatically updates the learning rate for each parameter when training; they scale the effective learning rate for each parameter based on recent gradients, shrinking it when gradients are large and in some methods increasing it when gradients are small; examples are RMSProp, Adam and AdamW; the benefit is not needing to tune the learning rate
Slide: Adaptive Learning Rate (Lecture4.pdf, p.20)

อาจารย์เสนอทางเลือกอีกทางว่าคือการใช้ more clever optimizers. กลุ่มนี้เรียกว่า adaptive learning optimizers ซึ่งใช้วิธีแบบ heuristic ที่ updates the learning rate ให้โดยอัตโนมัติ สำหรับแต่ละพารามิเตอร์ ระหว่างเทรน. กลไกที่อาจารย์บรรยายไว้ชัดเจน: พวกมัน scale the effective learning rate ของแต่ละพารามิเตอร์โดยดูจาก recent gradients — หดอัตราลงเมื่อ gradient ใหญ่ และในบางวิธีขยายอัตราขึ้นเมื่อ gradient เล็ก. ตัวอย่างที่ยกไว้คือ RMSProp, Adam และ AdamW ซึ่งเป็นสามหัวข้อย่อยที่ตามมาพอดี. ข้อดีสุดท้ายที่อาจารย์ย้ำ: "คุณจะไม่ต้องจูน learning rate แต่ก็มักได้ผลลัพธ์ที่ดีที่สุดด้วยค่า default" — ข้อนี้คือเหตุผลเชิงปฏิบัติที่คนส่วนใหญ่เปิดโปรเจกต์ใหม่ด้วย Adam โดยไม่คิดมาก.

คำว่า "effective learning rate" หมายถึงอะไร: ในสูตร \(\theta := \theta - \alpha\frac{g}{\sqrt{s+\epsilon}}\) ตัว \(\alpha\) ยังเป็นค่าเดียวสำหรับทุกพารามิเตอร์ แต่สิ่งที่ คูณจริง กับ gradient คือ \(\alpha/(\sqrt{s}+\epsilon)\) ซึ่งต่างกันไปในแต่ละพารามิเตอร์เพราะ \(s\) ของใครของมัน — ค่าประกอบนี้แหละที่เรียกว่า effective learning rate. ข้อสอบชอบถามว่า "RMSProp เปลี่ยน \(\alpha\) หรือไม่" คำตอบคือ ไม่เปลี่ยน \(\alpha\) แต่เปลี่ยนอัตราที่มีผลจริง.

ต้องรู้ก่อน — กำลังสอง และ รากที่สอง (ใช้ทั้ง RMSProp และ Adam)

กำลังสอง คือเอาเลขคูณตัวมันเอง: \(3^2=9\), \((-3)^2=9\), \(0.1^2=0.01\). คุณสมบัติที่ใช้ตรงนี้มีสองข้อ — (1) ผลลัพธ์เป็นบวกเสมอ ไม่ว่าต้นฉบับจะบวกหรือลบ (เครื่องหมายหายไป เหลือแต่ "ขนาด"); (2) มันขยายความต่าง: เลขที่ต่างกัน 10 เท่า (10 กับ 1) พอยกกำลังสองจะต่างกัน 100 เท่า (100 กับ 1).

รากที่สอง คือการทำย้อนกลับ: \(\sqrt{9}=3\), \(\sqrt{0.01}=0.1\), \(\sqrt{10}\approx3.162\). มันหด: \(\sqrt{100}=10\) — ของที่ต่างกัน 100 เท่ากลับมาต่างกัน 10 เท่า.

ทำไมสองอย่างนี้ต้องมาคู่กันเสมอ: ถ้าเราเอา \(g^2\) มาเฉลี่ยได้ \(s\) แล้ว \(s\) จะมี "หน่วย" เป็น gradient ยกกำลังสอง เทียบกับ \(g\) ตรง ๆ ไม่ได้. พอถอดราก \(\sqrt{s}\) หน่วยกลับมาเท่ากับ \(g\) แล้วอัตราส่วน \(g/\sqrt{s}\) จึงเป็นตัวเลขไร้หน่วยที่มีขนาดราว ๆ 1 — นี่คือเหตุผลที่ทุกสูตรในหัวข้อนี้หารด้วย \(\sqrt{s}\) ไม่ใช่ \(s\). ลองเองเร็ว ๆ: ถ้า \(g=0.1\) ตลอด แล้ว \(s\to0.01\), \(\sqrt{s}=0.1\), ได้ \(g/\sqrt s = 1\) พอดี.

5.1 RMSProp — Root Mean Square Propagation (หน้า 21)

Slide RMSprob (Root Mean Square Propagation): Hinton 2012 maintains an exponential weight average of squared gradients for each parameter and uses it to normalize gradient updates; when a parameter has consistently larger gradients RMSProp reduces its effective learning rate by scaling the update inversely with the square root of the moving average of its squared gradients; smaller gradients get less scaling and a relatively larger effective learning rate; with the s_i update formula, the highlighted theta update divided by sqrt(s+epsilon), hyperparameters alpha 0.001 and rho 0.9, and the Keras RMSprop line
Slide: RMSprob (Root Mean Square Propagation) (Lecture4.pdf, p.21)

RMSProp (Hinton, 2012) รักษา exponential weight average ของ squared gradients ของแต่ละพารามิเตอร์ แล้วใช้มัน normalize การอัปเดตของ gradient เพื่อช่วยให้การเทรนเสถียร. สังเกตว่านี่คือ EWA ตัวเดิมจาก §4.1 เป๊ะ ๆ เพียงแต่ค่าที่เอามาเฉลี่ยเปลี่ยนจาก "gradient" เป็น "gradient ยกกำลังสอง" — และพอยกกำลังสองแล้วเครื่องหมายหายไป \(s_i\) จึงวัด "ขนาดของ gradient" ไม่ใช่ "ทิศ":

$$s_i := \rho\,s_i + (1-\rho)\left(\frac{\partial J(\theta_i)}{\partial\theta_i}\right)^{2},\qquad \theta_i := \theta_i - \alpha\,\frac{\partial J(\theta_i)/\partial\theta_i}{\sqrt{s_i+\epsilon}}$$

\(s_i\) คือค่าเฉลี่ยของ "gradient กำลังสอง" ของพารามิเตอร์ตัวนี้ — ใครมี gradient ใหญ่ต่อเนื่อง \(s_i\) ยิ่งสูง แล้วก้าวของคนนั้นถูกหารด้วยรากของมัน

สไลด์เขียนกำกับในวงเล็บว่า "gradient vector is scaled down by a factor of \(\sqrt{s_i+\epsilon}\)" และกล่องไฮไลต์สีเหลืองมุมขวาคือสูตรอัปเดตเต็ม. อาจารย์อธิบายผลของมันไว้สองกรณีที่ต้องท่องเป็นคู่:

hyperparameter ของ RMSProp ตามที่สไลด์ระบุมี 2 ตัว: \(\alpha\) (ปกติ 0.001) และ \(\rho\) (ปกติ 0.9) — จุดที่ต้องระวังคือตัวที่สองใช้สัญลักษณ์ \(\rho\) (rho) ไม่ใช่ \(\beta\) และในโค้ด Keras ก็ชื่อ rho ตรงตัว.

คำนวณ: RMSProp ทำให้พารามิเตอร์ที่ gradient ต่างกัน 100 เท่า ได้ก้าวเท่ากัน
ให้พารามิเตอร์ A มี gradient คงที่ 10 และ B มี gradient คงที่ 0.1; \(\alpha=0.001,\ \rho=0.9\), เริ่มที่ \(s=0\) (ละ \(\epsilon\) เพื่อความชัด)
  1. ขั้นที่ 1 — ถ้าใช้ gradient descent ธรรมดา ก้าว \(=\alpha g\)
    $$\Delta\theta_A = 0.001\times10 = 0.01,\qquad \Delta\theta_B = 0.001\times0.1 = 0.0001$$

    A ก้าวใหญ่กว่า B ถึง 100 เท่า — B แทบไม่ขยับ ทั้งที่มันอาจยังอยู่ไกลจากค่าที่ดี

  2. ขั้นที่ 2 — RMSProp รอบแรก: หา \(s\) ก่อน (\(s_0=0\))
    $$s_A = 0.9(0)+0.1(10^2) = 10,\qquad s_B = 0.9(0)+0.1(0.1^2) = 0.001$$
  3. ขั้นที่ 3 — ก้าวจริง \(=\alpha\, g/\sqrt{s}\)
    $$\Delta\theta_A = 0.001\times\frac{10}{\sqrt{10}} = 0.00316,\qquad \Delta\theta_B = 0.001\times\frac{0.1}{\sqrt{0.001}} = 0.00316$$

    เท่ากันเป๊ะทั้งที่ gradient ต่างกัน 100 เท่า

  4. ขั้นที่ 4 — เมื่อเทรนไปหลายรอบ \(s\to g^2\) (เพราะ gradient คงที่)
    $$\frac{g}{\sqrt{s}}\ \to\ \frac{g}{|g|} = 1\quad\Rightarrow\quad \Delta\theta = \alpha\ \text{ทั้ง A และ B}$$

RMSProp ทำให้ขนาดก้าวไม่ขึ้นกับขนาดของ gradient อีกต่อไป เหลือแต่ทิศ — A ที่เสียงดังถูกกดลง ส่วน B ที่เสียงเบาถูกดันขึ้นมาให้ก้าวได้พอ ๆ กัน ตรงกับสองบรรทัดที่อาจารย์เขียนไว้ทุกคำ. นี่ยังอธิบายด้วยว่าทำไม \(\alpha\) ของ adaptive optimizer จึงไม่ต้องจูนมาก: ขนาดก้าวถูกตรึงไว้ที่ราว ๆ \(\alpha\) โดยอัตโนมัติ.

เสริมความเข้าใจ (นอกสไลด์): \(\epsilon\) ในตัวส่วนมีไว้กัน "หารด้วยศูนย์" ล้วน ๆ — ถ้าพารามิเตอร์ตัวไหนมี gradient เป็น 0 มาตลอด \(s\) จะเป็น 0 และ \(g/\sqrt{0}\) จะระเบิด. ค่า \(\epsilon\) จึงเล็กมาก (ระดับ \(10^{-7}\)) เพื่อไม่ให้ไปรบกวนกรณีปกติ. โค้ดบรรทัดเดียวท้ายสไลด์คือวิธีเรียกใช้ใน Keras:

optimizer = keras.optimizers.RMSprop(lr=0.001, rho=0.9)
โค้ดทำอะไร
keras.optimizers.RMSprop(...)สร้าง optimizer RMSProp. สะกดในโค้ดคือ RMSprop (ตัว p เล็ก) — คนละแบบกับหัวสไลด์ที่เขียน "RMSprob"
lr=0.001\(\alpha\) ค่า default ที่อาจารย์ระบุว่า "normally 0.001" สำหรับ RMSProp (สังเกตว่าต่างจาก SGD ที่มักใช้ค่าสูงกว่า เพราะที่นี่ก้าวถูก normalize ให้ราว ๆ \(\alpha\) อยู่แล้ว)
rho=0.9คือ \(\rho\) ในสูตร — อัตราการจำอดีตของค่าเฉลี่ย gradient กำลังสอง (normally 0.9). ค่าสูง = \(s\) เปลี่ยนช้า อ้างอิงประวัติยาว
สิ่งที่ ไม่ อยู่ในบรรทัดนี้ไม่มี momentum= เพราะ RMSProp ไม่เก็บค่าเฉลี่ยของ gradient (เก็บแต่กำลังสอง) — การรวมสองอย่างคือหน้าที่ของ Adam ในหน้าถัดไป

ผลลัพธ์ที่คาด: ได้อ็อบเจ็กต์ RMSprop ที่มี learning_rate = 0.001 และ rho = 0.9 พร้อมส่งเข้า model.compile(); ยังไม่มีการคำนวณใด ๆ เกิดขึ้นจากบรรทัดนี้.

5.2 Adam — Adaptive Moment Estimation (หน้า 22)

Slide Adam (Adaptive Moment Estimation): Kingma and Ba 2014 combines RMSprop and gradient descent with momentum; it uses the squared gradients to normalize the gradient magnitude and takes advantage of momentum by using a moving average of the gradient; formulas for v_i with beta_1 and s_i with beta_2, the highlighted update theta minus alpha times v over sqrt(s+epsilon), notes that v and s are first and second moment estimates initialised to 0, beta_1 normally 0.9, beta_2 normally 0.999, epsilon normally 10e-7, and the Keras Adam line
Slide: Adam (Adaptive Moment Estimation) — รวม Momentum กับ RMSProp (Lecture4.pdf, p.22)

Adam (Kingma & Ba, 2014) คือการรวมสองแนวคิดที่เราเพิ่งเรียนเข้าด้วยกันตรง ๆ และอาจารย์แยกให้เห็นเป็นสองบรรทัดที่ทำสีไว้: มันใช้ squared gradients เพื่อ normalize ขนาดของ gradient (ส่วนที่ยืมจาก RMSprop) และใช้ประโยชน์จาก momentum ด้วยการใช้ moving average ของ gradient (ส่วนที่ยืมจาก Momentum). เขียนเป็นสองสมการที่เดินคู่กันทุกก้าว:

$$v_i := \beta_1 v_i + (1-\beta_1)\frac{\partial J(\theta_i)}{\partial\theta_i},\qquad s_i := \beta_2 s_i + (1-\beta_2)\left(\frac{\partial J(\theta_i)}{\partial\theta_i}\right)^{2}$$ $$\theta_i := \theta_i - \alpha\,\frac{v_i}{\sqrt{s_i+\epsilon}}$$

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

สัญลักษณ์ชื่อที่สไลด์ใช้ค่าปกติมาจากไหน
\(v\)first moment estimateเริ่มที่ 0Momentum (§4)
\(s\)second moment estimateเริ่มที่ 0RMSProp (§5.1)
\(\beta_1\)momentum decay hyperparameter0.9เท่ากับ \(\beta\) ของ Momentum
\(\beta_2\)scaling decay hyperparameter0.999เทียบเท่า \(\rho\) ของ RMSProp
\(\epsilon\)parameter for numerical stability\(10^{-7}\)กันหารด้วยศูนย์

คำว่า "moment" มาจากไหน (เสริมความเข้าใจ นอกสไลด์): ในสถิติ "โมเมนต์ที่หนึ่ง" ของตัวแปรสุ่มคือค่าเฉลี่ยของมัน และ "โมเมนต์ที่สอง" คือค่าเฉลี่ยของกำลังสอง. \(v\) เป็นค่าเฉลี่ยของ gradient = โมเมนต์ที่หนึ่ง; \(s\) เป็นค่าเฉลี่ยของ gradient² = โมเมนต์ที่สอง — ชื่อ "Adaptive Moment Estimation" จึงบรรยายสิ่งที่อัลกอริทึมทำตรงตัว: ประมาณโมเมนต์สองตัวแล้วเอามาปรับก้าว. ทำไม \(\beta_2=0.999\) ถึงใหญ่กว่า \(\beta_1=0.9\): เพราะ \(s\) ทำหน้าที่วัด "สเกลของ gradient" ซึ่งควรเป็นค่าที่นิ่งมาก ๆ (จำยาว ~1000 ก้าว) ขณะที่ \(v\) ทำหน้าที่ชี้ "ทิศตอนนี้" ซึ่งควรปรับตัวเร็วกว่า (จำ ~10 ก้าว). สลับสองค่านี้ในข้อสอบคือ distractor ที่พบบ่อย.

optimizer = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999)
โค้ดทำอะไร
keras.optimizers.Adam(...)สร้าง optimizer Adam ซึ่งภายในเก็บตัวแปรเสริม สองชุด ต่อพารามิเตอร์หนึ่งตัว (\(v\) และ \(s\)) — นี่คือเหตุผลที่ Adam ใช้หน่วยความจำมากกว่า SGD ราว 3 เท่า
lr=0.001\(\alpha\) ค่า default ของ Adam. ตามหน้า 20 คุณ "มักไม่ต้องจูนค่านี้" เพราะขนาดก้าวถูก normalize ไว้แล้ว
beta_1=0.9อัตราจำของค่าเฉลี่ย gradient (\(v\)) — ส่วน momentum. ชื่ออาร์กิวเมนต์ใช้ขีดล่าง beta_1 ไม่ใช่ beta1
beta_2=0.999อัตราจำของค่าเฉลี่ย gradient กำลังสอง (\(s\)) — ส่วน RMSProp
อาร์กิวเมนต์ที่ไม่ได้เขียนepsilon ถูกปล่อยเป็นค่า default (สไลด์ระบุ \(10^{-7}\)) และ bias correction ทำให้อัตโนมัติอยู่แล้วโดยไม่มีสวิตช์ให้ปิด

ผลลัพธ์ที่คาด: ได้อ็อบเจ็กต์ Adam ที่มี learning_rate = 0.001, beta_1 = 0.9, beta_2 = 0.999. เมื่อนำไปเทรนจริงบน Spiral Dataset ด้วยโครงข่าย \([2,10,10,10,1]\) ผลที่อาจารย์ได้คือ training accuracy 99.857143% (ดูหน้า 24 ใน §5.4).

5.3 Bias Correction — ซ่อมค่าที่เอียงในช่วงแรก (หน้า 23)

Slide Bias Correction: the first and second moment estimates fail to keep up with the original gradient, known as a biased estimate; to correct this, divide by 1 minus beta raised to the timestep; formulas v_i_corrected = v_i over (1 - beta_1^t) and s_i_corrected = s_i over (1 - beta_2^t) where t is the timestep, plus the highlighted update using the corrected values, with a price chart comparing SMA and EMA
Slide: Bias Correction (Lecture4.pdf, p.23)

อาจารย์อธิบายปัญหาไว้เป็นประโยคเดียว: การประมาณโมเมนต์ที่หนึ่งและที่สอง "จะตามค่า gradient ต้นฉบับไม่ทัน" และเรียกอาการนี้ว่า biased estimate. เราพิสูจน์สาเหตุไปแล้วใน §4.1 ว่าน้ำหนักที่จ่ายไปจริงหลัง \(t\) ก้าวคือ \(1-\beta^t\) ซึ่งน้อยกว่า 1 — วิธีแก้จึงตรงไปตรงมาที่สุดเท่าที่จะเป็นไปได้ คือหารด้วยน้ำหนักที่จ่ายไปจริง (สไลด์เขียนว่า "dividing by 1 - (beta raised to the timestep)"):

$$\hat v_i = \frac{v_i}{1-\beta_1^{\,t}},\qquad \hat s_i = \frac{s_i}{1-\beta_2^{\,t}},\qquad \theta_i := \theta_i - \alpha\,\frac{\hat v_i}{\sqrt{\hat s_i+\epsilon}}$$

ถ้าคุณจ่ายน้ำหนักไปแค่ 10% แล้วได้ค่ามา ก็หารด้วย 0.10 เพื่อให้ได้ค่าที่เทียบเท่ากับการจ่ายเต็ม 100%

โดย \(t\) คือ "timestep parameter for bias correction steps" — เลขลำดับก้าวที่นับตั้งแต่เริ่มเทรน (ก้าวแรก \(t=1\)) ไม่ใช่เลข epoch. กราฟราคาหุ้นที่แปะไว้เป็นรูปเดียวกับหน้า 15 เพื่อย้ำว่า \(v\) และ \(s\) คือ EMA ตัวเดียวกันนั้น.

ตำแหน่งของ \(\epsilon\) (อ่านให้ปลอดภัยในข้อสอบ): สไลด์เขียน \(\epsilon\) ไม่ตรงกันเอง: หน้า 21 (RMSProp), 22 (Adam) และ 23 (Bias Correction) วางไว้ใต้เครื่องหมายราก คือ \(\sqrt{s_i+\epsilon}\) (กล่องเหลืองหน้า 23 สะกดตัวห้อยว่า "crorrect" ซึ่งเป็น typo ของ correct) แต่หน้า 25 (AdamW) วางไว้นอกราก คือ \(\sqrt{s_i}+\epsilon\). ถ้าโจทย์ให้สูตรมา ให้ตอบตามรูปที่โจทย์เขียน. \(\epsilon\approx10^{-7}\) เล็กมากจนมีผลเฉพาะตอน \(s\) เกือบเป็นศูนย์ ซึ่งเป็นหน้าที่เดียวของมันอยู่แล้ว: ถ้าถามว่า \(\epsilon\) มีไว้ทำอะไร คำตอบคือ "numerical stability / กันหารด้วยศูนย์" ตามที่สไลด์หน้า 22 ระบุ ไม่ใช่ "เพื่อปรับขนาดก้าว". เสริมความเข้าใจ (นอกสไลด์): เปเปอร์ต้นฉบับของ Adam (Kingma & Ba) เขียน \(\epsilon\) ไว้นอกราก คือ \(\sqrt{\hat s}+\epsilon\) — ทั้งสองแบบมีใช้จริง และผลตัวเลขแทบไม่ต่างกัน.

คำนวณ: ทำไมต้อง bias correction — ตัวเลขจริงสองก้าวแรกของ Adam
ให้ gradient คงที่ \(g=0.5\), \(\beta_1=0.9\), \(\beta_2=0.999\), \(v_0=s_0=0\) (ละ \(\epsilon\))
  1. ขั้นที่ 1 — \(t=1\): คำนวณ \(v_1\) และ \(s_1\) ตามสูตร
    $$v_1 = 0.9(0)+0.1(0.5) = 0.05,\qquad s_1 = 0.999(0)+0.001(0.5^2) = 0.00025$$

    gradient จริงคือ 0.5 แต่ \(v_1\) ได้แค่ 0.05 (เล็กไป 10 เท่า = \(1/(1-\beta_1)\)) และ \(s_1\) ควรเป็น 0.25 แต่ได้ 0.00025 (เล็กไป 1,000 เท่า = \(1/(1-\beta_2)\)) — \(s\) เอียงหนักกว่ามากเพราะ \(\beta_2\) ใหญ่กว่า

  2. ขั้นที่ 2 — ถ้าไม่แก้ อัตราส่วนที่ใช้ก้าวจะเป็นเท่าไร
    $$\frac{v_1}{\sqrt{s_1}} = \frac{0.05}{\sqrt{0.00025}} = \frac{0.05}{0.01581} = 3.162$$

    ก้าวแรกจะใหญ่เกินจริง 3.16 เท่า — ไม่ใช่เพราะ \(v\) ใหญ่ไป แต่เพราะ \(s\) เล็กไปมากกว่า \(v\) ตัวส่วนจึงเล็กเกิน

  3. ขั้นที่ 3 — แก้ด้วยการหารด้วย \(1-\beta^t\)
    $$\hat v_1 = \frac{0.05}{1-0.9^1} = \frac{0.05}{0.1} = 0.5,\qquad \hat s_1 = \frac{0.00025}{1-0.999^1} = \frac{0.00025}{0.001} = 0.25$$ $$\Rightarrow\ \frac{\hat v_1}{\sqrt{\hat s_1}} = \frac{0.5}{0.5} = 1$$

    ค่าที่แก้แล้วเท่ากับ gradient จริง (0.5) และ gradient จริงกำลังสอง (0.25) พอดีเป๊ะ

  4. ขั้นที่ 4 — ยืนยันที่ \(t=2\)
    $$v_2=0.095,\quad s_2=0.00049975$$ $$\hat v_2 = \frac{0.095}{1-0.81} = 0.5,\quad \hat s_2 = \frac{0.00049975}{1-0.998001} = 0.25\ \Rightarrow\ \text{อัตราส่วน}=1$$

    ถ้าไม่แก้ อัตราส่วนที่ \(t=2\) จะเป็น 4.25 — ยิ่งเพี้ยนกว่าก้าวแรกอีก

หลังแก้แล้ว ก้าวแรกของ Adam มีขนาด \(=\alpha\) พอดี (ไม่ใช่ \(3.16\alpha\)) และคงที่ตลอดช่วงต้น. หลักการอ่านสูตร: ที่ \(t\) เล็ก ตัวหาร \(1-\beta^t\) เล็กมาก จึงขยายค่าชดเชยแรง; ที่ \(t\) ใหญ่ \(\beta^t\to0\) ตัวหาร \(\to1\) การแก้แทบไม่มีผล — bias correction จึง "ค่อย ๆ ปิดตัวเอง" โดยอัตโนมัติ ไม่ต้องมีสวิตช์.

5.4 การทดลองเปรียบเทียบของอาจารย์ (หน้า 24)

Slide Experiments comparing Mini-Batch GD, Momentum and Adam on the Spiral Dataset with 5 layers [2,10,10,10,1], lr 0.03 and mini-batch 64: three cost-versus-epoch curves and three decision-boundary plots with training set accuracy 81.714286, 99.142857 and 99.857143 respectively
Slide: Experiments — Mini-Batch GD vs Momentum vs Adam บน Spiral Dataset (Lecture4.pdf, p.24)

อาจารย์รันการทดลองด้วยเงื่อนไขที่เหมือนกันทุกอย่าง ยกเว้น optimizer: โครงข่าย 5 layers = \([2,10,10,10,1]\) (261 พารามิเตอร์ตามที่เรานับไว้ใน §1.1), \(lr = 0.03\), mini-batch = 64 (ซึ่งเป็นกำลังของ 2 ตามคำแนะนำหน้า 12 และให้ 157 iteration/epoch ถ้าข้อมูลมี 10,000 แถว), บนข้อมูลรูปก้นหอย (Spiral Dataset) ที่แยกไม่ได้ด้วยเส้นตรง. ผลลัพธ์:

OptimizerTraining Set Accuracyพฤติกรรมของ cost ตลอด 50 epoch
Mini-Batch GD81.714286%เริ่มราว 0.50 แกว่งขึ้นลงในช่วง 0.27–0.58 และเมื่อจบยังอยู่แถว 0.35 — ยังไม่ลงเลย
Momentum99.142857%เริ่มราว 0.68 ดิ่งลงเหลือ ~0.2 ภายใน 10 epoch แล้วแกว่งอยู่ช่วง 0.05–0.2 (มีสไปก์ขึ้นถึง ~0.55 ครั้งหนึ่งราว epoch 22)
Adam99.857143%ดิ่งเร็วที่สุด ลงต่ำกว่า 0.1 ตั้งแต่ราว epoch 15 แล้วแบนอยู่แถว 0.0–0.05 (มีสไปก์หนึ่งครั้งราว epoch 38)

อ่านตัวเลขเหล่านี้ให้เป็นบทเรียน: ช่องว่าง 81.71% → 99.14% ระหว่าง Mini-Batch GD กับ Momentum คือกำไรที่ได้มาจากการเปลี่ยนบรรทัดเดียว (ใส่ momentum=0.9) โดยไม่ได้แตะโครงสร้างเครือข่าย ไม่ได้เพิ่มข้อมูล ไม่ได้เพิ่มเวลาเทรน — นี่คือหลักฐานเชิงประจักษ์ของประโยคหน้า 6 ว่า optimizer ที่เร็วกว่าให้ "significant enhancement in speed". ส่วนช่องว่าง 99.14% → 99.86% จาก Momentum ไป Adam เล็กกว่ามาก (0.7 จุด) แต่ดูที่กราฟ cost จะเห็นว่า Adam ไปถึงจุดต่ำได้ เร็วกว่าและนิ่งกว่า — ประโยชน์ของ Adam จึงอยู่ที่ "ถึงเร็ว + ไม่ต้องจูน" มากกว่าที่ความแม่นยำสุดท้าย. ภาพแถวล่างของสไลด์ยืนยันเรื่องเดียวกันด้วยเส้นแบ่งเขต: เส้นสีเขียวของ Mini-Batch GD แทบเป็นเส้นตรงพาดกลางภาพ แยกก้นหอยสองวงไม่ออก ขณะที่ของ Momentum และ Adam โค้งวนตามก้นหอยได้จริง.

ข้อควรระวังในการอ่านผลนี้: ตัวเลขทั้งสามเป็น training set accuracy ไม่ใช่ test accuracy — สูงกว่าไม่ได้แปลว่า generalize ดีกว่าเสมอไป (99.86% บน training set อาจเป็นสัญญาณของการเริ่ม overfit ด้วยซ้ำ ซึ่งเป็นเหตุผลที่ §6 ต้องมีอยู่). ถ้าข้อสอบถามว่า "สรุปจากหน้า 24 ได้ว่าอะไร" คำตอบที่ปลอดภัยคือ "optimizer ที่ปรับตัวได้ลู่เข้าเร็วกว่าและฟิต training data ได้ดีกว่าภายใต้งบ epoch เท่ากัน" ไม่ใช่ "Adam ให้โมเดลที่ดีที่สุดเสมอ".

5.5 AdamW — Adam ที่แยก Weight Decay ออกมา (หน้า 25)

Slide AdamW (Adam with Decoupled Weight Decay): Loshchilov and Hutter 2017 add weight decay as a separate step from the gradient-based parameter update; AdamW performs the same adaptive update as Adam but additionally shrinks the weights directly; the update formula theta minus alpha v over sqrt(s)+epsilon minus alpha lambda theta; Why AdamW: with Adam plus L2 the regularization term is added to the gradient and is affected by Adam's adaptive scaling, AdamW decouples weight decay giving more direct and predictable control of regularization and often improving generalization; with Keras AdamW code
Slide: AdamW (Adam with Decoupled Weight Decay) (Lecture4.pdf, p.25)

AdamW (Loshchilov & Hutter, 2017) เป็น variant ของ Adam ที่เพิ่ม weight decay เข้ามาเป็น "ขั้นตอนแยกต่างหาก" จากการอัปเดตที่อิง gradient. อาจารย์สรุปความต่างไว้ประโยคเดียว: AdamW ทำ adaptive update เหมือน Adam ทุกประการ (ปรับพารามิเตอร์ด้วย gradient ของ loss) แต่เพิ่มเติมคือ "shrinks the weights directly" — หด weight ลงตรง ๆ:

$$\theta_i := \theta_i - \alpha\,\frac{v_i}{\sqrt{s_i}+\epsilon} - \alpha\lambda\,\theta_i$$

โดย \(\lambda\) คือ weight decay hyperparameter. ส่วน "Why AdamW?" อาจารย์ให้เหตุผลเป็นลูกโซ่สามชั้น:

  1. ถ้าใช้ Adam + L2 regularization แบบเดิม เทอม regularization จะถูกบวกเข้าไปใน gradient ⇒ มันจึงถูก Adam's adaptive scaling (การหารด้วย \(\sqrt{s}\)) กระทำด้วย
  2. AdamW decouples weight decay ออกจาก adaptive gradient update — เทอม \(\alpha\lambda\theta_i\) ไม่ผ่านตัวหาร \(\sqrt{s}\)
  3. ผลคือได้ "more direct and predictable control of regularization" และมักทำให้ generalization ดีขึ้น
คำนวณ: ทำไม "ผ่าน adaptive scaling" ถึงทำให้ regularization เพี้ยน
ให้ \(\alpha=0.001,\ \lambda=0.004\) (ค่าจากโค้ดในสไลด์), \(\theta=2\); เทียบพารามิเตอร์สองตัวที่ \(\sqrt{\hat s}\) ต่างกัน — P มี \(\sqrt{\hat s}=10\) (gradient ดังมาก) และ Q มี \(\sqrt{\hat s}=0.1\) (gradient เบามาก)
  1. ขั้นที่ 1 — AdamW: ขนาดของเทอม decay ต่อก้าว (ไม่ผ่านตัวหาร)
    $$\alpha\lambda\theta = 0.001\times0.004\times2 = 8\times10^{-6}$$

    ได้เท่ากันทั้ง P และ Q เพราะสูตรไม่มี \(\sqrt{\hat s}\) อยู่เลย

  2. ขั้นที่ 2 — Adam + L2: เทอม \(\lambda\theta\) ถูกบวกเข้า gradient แล้วทั้งก้อนถูกหารด้วย \(\sqrt{\hat s}\)
    $$\text{P}:\ \frac{\alpha\lambda\theta}{10} = 8\times10^{-7},\qquad \text{Q}:\ \frac{\alpha\lambda\theta}{0.1} = 8\times10^{-5}$$
  3. ขั้นที่ 3 — เทียบแรง regularization ที่เกิดขึ้นจริงของสองตัว
    $$\frac{8\times10^{-5}}{8\times10^{-7}} = 100\ \text{เท่า}$$

ด้วย Adam + L2 พารามิเตอร์ Q ที่ gradient เบา กลับถูก decay แรงกว่า P ถึง 100 เท่า ทั้งที่เราตั้ง \(\lambda\) ค่าเดียวกัน — "แรง regularization" กลายเป็นค่าที่เดาไม่ได้เพราะขึ้นกับประวัติ gradient ของแต่ละตัว. AdamW ตัดปัญหานี้ทิ้งโดยให้ decay เท่ากันหมด ตรงกับคำว่า more direct and predictable control บนสไลด์.

optimizer = keras.optimizers.AdamW(
    learning_rate=0.001,
    beta_1=0.9,
    beta_2=0.999,
    weight_decay=0.004
)
โค้ดทำอะไร
keras.optimizers.AdamW(เรียกคลาส AdamW — เขียน W พิมพ์ใหญ่ติดกับ Adam ไม่มีขีดกลาง ไม่ใช่ Adam_W
learning_rate=0.001\(\alpha\). สังเกตว่าบรรทัดนี้ใช้ชื่อเต็ม learning_rate ขณะที่สไลด์หน้าก่อน ๆ ใช้ lr — ทั้งสองชื่อชี้ค่าเดียวกัน
beta_1=0.9อัตราจำของโมเมนต์ที่หนึ่ง เหมือน Adam ทุกอย่าง
beta_2=0.999อัตราจำของโมเมนต์ที่สอง เหมือน Adam ทุกอย่าง
weight_decay=0.004คือ \(\lambda\) ในสูตร — อาร์กิวเมนต์เดียวที่ AdamW มีเพิ่มจาก Adam. เพิ่มค่านี้ = regularize แรงขึ้น, ตั้งเป็น 0 = กลายเป็น Adam

ผลลัพธ์ที่คาด: อ็อบเจ็กต์ AdamW ที่แต่ละ step จะทำสองอย่างเรียงกัน — อัปเดตแบบ Adam ก่อน แล้วค่อยหด weight ลงอีก \(\alpha\lambda\theta = 8\times10^{-6}\) ต่อก้าวสำหรับ weight ที่มีค่า 2. ใน Lesson 6 เราจะเห็น weight_decay ตัวนี้ถูกใส่เป็นช่วงค่าให้ tuner ค้นหา.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
RMSProp หารด้วย \(\sqrt{s_i}\) ไม่ใช่ \(s_i\) เพราะ \(s_i\) มีหน่วยเป็น gradient กำลังสอง — การถอดรากทำให้หน่วยกลับมาตรงกับ gradient อัตราส่วน \(g/\sqrt{s}\) จึงเป็นตัวเลขขนาดราว ๆ 1 และขนาดก้าวคุมได้ที่ \(\alpha\). ถ้าหารด้วย \(s\) เฉย ๆ ก้าวจะเพี้ยนไปตามสเกลของ gradient อย่างสิ้นเชิง.
เปลี่ยนแล้วเกิดอะไร
ตั้ง \(\beta_2 = \beta_1 = 0.9\) (แทนที่จะเป็น 0.999): \(s\) จะตอบสนองต่อ gradient ก้อนล่าสุดไวพอ ๆ กับ \(v\) ทำให้ตัวหารกระโดดตาม noise ของ mini-batch — ก้าวจะไม่นิ่ง. และถ้าตั้ง \(\beta_1=0\) Adam จะเสียส่วน momentum ไปเหลือเพียง RMSProp ที่มี bias correction.
เชื่อมกับ
คุณสมบัติ "ก้าวไม่ขึ้นกับขนาด gradient" คือเหตุผลที่ Adam/RMSProp ทนต่อ gradient ขนาดผิดปกติได้ดีกว่า plain GD (Lesson 5 §1: vanishing ทำให้ plain GD แทบไม่ขยับ แต่ RMSProp normalize ขนาดออกไปแล้ว); และการที่ Adam เก็บ \(v,s\) อย่างละชุดคือเหตุผลที่บรรทัด "Optimizer params" ใน model.summary() (Lesson 6 §3) มีขนาดราว 2 เท่าของจำนวนพารามิเตอร์.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 9RMSProp
ใน RMSProp พารามิเตอร์ที่มี gradient ขนาดใหญ่ต่อเนื่องหลาย iteration จะเกิดอะไรขึ้นกับ effective learning rate ของมัน?
คำถาม 10Adam
Adam รวมแนวคิดของ optimizer สองตัวใดเข้าด้วยกัน และเก็บตัวแปรอะไรไว้บ้างตามสไลด์หน้า 22?
คำถาม 11Bias Correction
gradient คงที่ 0.5 ที่ก้าวแรก (t = 1) ด้วย β₁ = 0.9 ได้ v₁ = 0.05 ค่าที่ผ่าน bias correction แล้วเท่ากับเท่าไร และเพราะอะไร?
คำถาม 12AdamW
ตามสไลด์หน้า 25 ข้อใดคือความต่างที่แท้จริงระหว่าง AdamW กับการใช้ Adam ร่วมกับ L2 regularization?

6. กัน Overfitting: Regularization, Dropout, Early Stopping

เปลี่ยนโจทย์: ตั้งแต่ §3 ถึง §5 เราแก้ปัญหา "เทรนช้า" มาตลอด — และแก้ได้ดีจนเกิดปัญหาใหม่. ดูหน้า 24 อีกครั้ง: Adam ฟิต training set ได้ 99.86% ซึ่งแปลว่าโมเดลแทบจำข้อมูลเทรนได้หมดแล้ว. คำถามคือมันจะทำได้ดีแค่ไหนกับข้อมูลที่ไม่เคยเห็น? นี่คือปัญหาข้อ 2 จากหน้า 5 ที่เราค้างไว้ และหน้า 26–34 คือชุดเครื่องมือทั้งหมดที่อาจารย์ให้มาแก้.

Slide Overfitting Handling Strategies: deep neural networks with many layers and neurons face complexity due to numerous parameters which can lead to overfitting where models fit training data well but struggle to generalize; commonly employed techniques listed are regularization (ridge, lasso and elastic net), dropout, early stopping, data augmentation and batch normalization (later)
Slide: Overfitting Handling Strategies — สารบัญของ §6–§7 (Lecture4.pdf, p.26)

อาจารย์อธิบายต้นเหตุก่อน: deep neural network ที่มี layer และ neuron จำนวนมาก เผชิญความซับซ้อนจาก numerous parameters ซึ่งนำไปสู่ overfitting — สภาวะที่โมเดล "ฟิตข้อมูลเทรนได้ดีแต่ลำบากในการ generalize" (คือใช้กับข้อมูลใหม่ไม่ได้). จากนั้นลิสต์เครื่องมือ 5 ชิ้นที่ทำสีไว้ทุกชิ้น ซึ่งเป็นรายการที่ต้องท่องให้ครบเพราะเป็นข้อสอบแบบ "ข้อใดไม่ใช่วิธีจัดการ overfitting":

เทคนิคทำงานที่ไหนสอนที่
regularization (ridge, lasso, elastic net)เพิ่มเทอมลงโทษใน cost function§6.1 (หน้า 27)
dropoutสุ่มปิด node ระหว่างเทรน§6.2 (หน้า 28–31)
early stoppingหยุดเทรนก่อนจะจำข้อมูล§6.3 (หน้า 32–33)
data augmentationสร้างข้อมูลเพิ่มจากของเดิม§7 (หน้า 34)
batch normalizationปรับสถิติของ activation ระหว่างชั้นอาจารย์เขียนว่า "(later)" ⇒ Lesson 5

สังเกตคำในวงเล็บ "(ridge, lasso, and elastic net)" — สามชื่อนี้คือชื่อทางสถิติของ L2, L1 และ L1+L2 ตามลำดับ ที่เราเจอมาตั้งแต่ Lesson 1. อาจารย์จงใจใช้ชื่อคู่เพื่อให้เชื่อมกับความรู้เดิม ไม่ได้เป็นเทคนิคใหม่สามอย่าง.

6.1 L1 / L2 / Max-Norm Regularization (หน้า 27)

Slide Regularization: use L1 or L2 Regularization as in shallow networks; L1 for a sparse model with many weights equal to zero, L2 to constrain all weights (preferred choice), keras.regularizers.l1_l2() for both; max-norm regularization constrains the incoming weights of each neuron so that the L2 norm of w is at most r, implemented by rescaling w to w times r over the norm after each training step, with reducing r increasing regularization; two Keras Dense layer code snippets are shown
Slide: Regularization — L1, L2 และ max-norm (Lecture4.pdf, p.27)

อาจารย์เปิดว่า "เหมือนที่เราทำใน shallow neural network" คุณใช้ L1 หรือ L2 Regularization ได้ แล้วให้เกณฑ์เลือกเป็นคู่เงื่อนไขที่ต้องจำแม่น:

เลือกตัวไหนเมื่อไร (คำของสไลด์)ผลที่ได้
L1 (lasso)เมื่อต้องการ "sparse model"weight หลายตัวถูกบีบให้เท่ากับ 0 พอดี เท่ากับตัดฟีเจอร์ทิ้งไปเลย
L2 (ridge)เมื่อต้องการ "constrain all the weights"ทุก weight ถูกบีบให้เล็กลงแต่ไม่ถึงศูนย์ — สไลด์วงเล็บว่า preferred choice
ทั้งคู่ (elastic net)เมื่อต้องการทั้ง L1 และ L2ใช้ keras.regularizers.l1_l2() แทน

เทคนิคที่สามซึ่งเป็นของใหม่สำหรับเครือข่ายลึกคือ max-norm regularization. นิยาม: สำหรับ neuron แต่ละตัว มันบังคับ weight ของเส้นขาเข้า (incoming connection) ให้ \(\|w\|_2 \le r\) โดย \(r\) คือ max-norm hyperparameter และ \(\|\cdot\|_2\) คือ L2 norm. จุดที่อาจารย์เน้นและออกสอบบ่อยคือ วิธีทำงานที่ต่างจาก L1/L2 โดยสิ้นเชิง: max-norm ไม่เพิ่มเทอม regularization เข้าไปใน cost function แต่ทำโดยคำนวณ \(\|w\|_2\) หลังทุก training step แล้ว rescale ถ้ามันเกิน \(r\):

$$w := w\cdot\frac{r}{\|w\|_2}\qquad\text{เมื่อ }\|w\|_2 > r$$

และอาจารย์ตั้งข้อสังเกตกำกับไว้ว่า scale factor \(r/\|w\|_2\) มีค่า \(\le 1\) เสมอ (ในกรณีที่ต้อง rescale) — ซึ่งจริงโดยนิยาม เพราะเราจะ rescale ก็ต่อเมื่อ \(\|w\|_2 > r\) ทำให้เศษน้อยกว่าส่วน. บรรทัดปิดท้าย: ลด \(r\) ⇒ regularization แรงขึ้น ⇒ ช่วยลด overfitting (ทิศทางนี้ตรงข้ามกับ \(\lambda\) ของ L2 ที่ต้องเพิ่มเพื่อให้แรงขึ้น — เป็นจุดที่สลับกันในข้อสอบได้ง่ายมาก).

คำนวณ: Max-Norm กับ \(w=(3,4)\) และ \(r=1\)
ให้ neuron ตัวหนึ่งมี weight ขาเข้าสองเส้นคือ 3 และ 4 และตั้ง max-norm hyperparameter \(r=1\)
  1. ขั้นที่ 1 — หา L2 norm (ความยาวของเวกเตอร์ ใช้ทฤษฎีบทพีทาโกรัส)
    $$\|w\|_2 = \sqrt{3^2+4^2} = \sqrt{9+16} = \sqrt{25} = 5$$

    เทียบกับ \(r=1\): \(5 > 1\) ⇒ เกินขีดจำกัด ต้อง rescale

  2. ขั้นที่ 2 — หาตัวคูณ
    $$\frac{r}{\|w\|_2} = \frac{1}{5} = 0.2\qquad(\le 1\ \text{ตามที่สไลด์ระบุ})$$
  3. ขั้นที่ 3 — rescale แล้วตรวจสอบ
    $$w := (3,4)\times0.2 = (0.6,\ 0.8),\qquad \|w\|_2 = \sqrt{0.36+0.64} = \sqrt{1} = 1$$
  4. ขั้นที่ 4 — ถ้าเปลี่ยนเป็น \(r=10\) ล่ะ

    \(\|w\|_2 = 5 \le 10\) ⇒ ไม่ต้องทำอะไรเลย — max-norm ทำงานเฉพาะเมื่อเกินขีด ไม่ได้แตะ weight ทุกก้าวเหมือน L2

ทิศทางของ \(w\) ไม่เปลี่ยน (\(0.6:0.8\) = \(3:4\) เท่าเดิม) เปลี่ยนแค่ความยาวให้พอดี \(r\) — max-norm จึงเป็นการ "ตัดยอด" ไม่ใช่การ "ดึงเข้าหาศูนย์ทุกก้าว" แบบ L2. และเห็นชัดว่า \(r\) เล็กลง (เช่นจาก 10 เหลือ 1) หมายถึงกรอบแคบลง = regularize แรงขึ้น ตรงตามบรรทัดสุดท้ายของสไลด์.

สไลด์แปะโค้ด Keras ไว้สองชิ้น ชิ้นแรกคือวิธีใส่ L2 ให้ layer:

keras.layers.Dense(100, activation="elu",
                   kernel_initializer="he_normal",
                   kernel_regularizer=keras.regularizers.l2(0.01))
โค้ดทำอะไร
keras.layers.Dense(100, ...)สร้าง fully-connected layer ที่มี 100 neuron — "Dense" คือชื่อของ layer แบบที่ทุก node ต่อกับทุก node ของชั้นก่อน ตรงกับ Fully Connected Neural Network ในหน้า 2
activation="elu"ใช้ ELU เป็น activation ของชั้นนี้ (ญาติของ ReLU ที่ให้ค่าลบเล็ก ๆ ได้ — รายละเอียดอยู่ Lesson 5)
kernel_initializer="he_normal"วิธีสุ่มค่าเริ่มต้นของ weight แบบ He — คนละเรื่องกับ regularization แต่มักมาคู่กับ ELU/ReLU (Lesson 5 §4)
kernel_regularizer=keras.regularizers.l2(0.01)บรรทัดที่เป็นพระเอก: เพิ่มเทอม \(\lambda\sum w^2\) เข้าไปใน cost โดย \(\lambda=0.01\). คำว่า kernel ใน Keras หมายถึงเมทริกซ์ weight (ไม่รวม bias ซึ่งใช้ bias_regularizer แยก)
เปลี่ยนเป็น L1 หรือทั้งคู่keras.regularizers.l1(0.01) หรือ keras.regularizers.l1_l2() ตามที่สไลด์ระบุในบรรทัดข้างบน

ผลลัพธ์ที่คาด: layer นี้มีพารามิเตอร์ \(100\times(\text{จำนวน input}+1)\) ตัว และตอนเทรน ค่า loss ที่รายงานจะสูงกว่า loss จริงของข้อมูล เพราะรวมเทอม \(0.01\sum w^2\) เข้าไปด้วย — ข้อนี้ทำให้หลายคนสับสนเวลาเทียบ loss ระหว่างโมเดลที่มีและไม่มี regularizer. ชิ้นที่สองคือวิธีใส่ max-norm:

keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal",
                   kernel_constraint=keras.constraints.max_norm(1.))
โค้ดทำอะไร
kernel_constraint=ช่องคนละช่องกับ kernel_regularizer — ตรงกับที่สไลด์บอกว่า max-norm ไม่ได้เพิ่มเทอมใน cost แต่เป็น "ข้อจำกัด" ที่ถูกบังคับใช้หลังทุก training step
keras.constraints.max_norm(1.)ตั้ง \(r=1\) — เขียนเป็น 1. (มีจุด) เพื่อให้เป็น float. นี่คือค่า \(r\) ตัวเดียวกับในตัวอย่างคำนวณข้างบนที่หด \((3,4)\) เหลือ \((0.6,0.8)\)
ลด 1. เป็น 0.5บีบแรงขึ้น (regularize มากขึ้น) ตามบรรทัดสุดท้ายของสไลด์ — ลด r = เพิ่ม regularization
ใช้ร่วมกับ kernel_regularizerทำได้ ใส่พร้อมกันในบรรทัดเดียวได้ เพราะทำงานคนละกลไก (เทอมใน cost vs การ rescale หลัง step)

ผลลัพธ์ที่คาด: ทุก ๆ ครั้งที่ optimizer อัปเดต weight เสร็จ Keras จะคำนวณ \(\|w\|_2\) ของแต่ละ neuron แล้วหดตัวที่เกิน 1 กลับมาให้พอดี 1 — ค่าที่รายงานใน loss ไม่เปลี่ยน มีแต่ตัว weight เองที่ถูกตัดยอด.

6.2 Dropout (หน้า 28–31)

Slide Dropout: dropout applied to a layer (usually hidden layers) consists of randomly dropping out certain nodes, deactivating them by setting their activations to zero, during training; the dropout rate is the fraction of nodes temporarily zeroed out, usually between 0.1 and 0.5; two network diagrams show the full network and the same network after dropping nodes, with dropout rate 0.33 for layers 1 and 2 and 0.5 for layer 3 in iteration 1
Slide: Dropout — สุ่มปิด node ระหว่างเทรน (Lecture4.pdf, p.28)

นิยามของอาจารย์: dropout ซึ่งใช้กับ layer หนึ่ง ๆ (โดยปกติคือ hidden layer) คือการ randomly dropping out nodes บางตัวของ layer นั้น — "deactivating them by setting their activations to zero" คือปิดโดยตั้งค่า activation เป็นศูนย์ — เฉพาะตอน training เท่านั้น. ส่วน dropout rate คือ "สัดส่วนของ node ที่ถูกทำให้เป็นศูนย์ชั่วคราว" ซึ่งมักตั้งระหว่าง 0.1 ถึง 0.5.

ตัวอย่างบนสไลด์ใช้เครือข่ายที่มี hidden layer 3 ชั้น (3 node, 3 node, 2 node) แล้วกำหนด dropout rate = 0.33 สำหรับ layer 1 และ 2 และ 0.5 สำหรับ layer 3 แล้วสุ่มจริงหนึ่งรอบ ("Iteration1 – training 1 example"). ลองแปลงเป็นตัวเลข: layer 1 มี 3 node × 0.33 ⇒ ปิด 1 node เหลือทำงาน 2 node; layer 2 เหมือนกัน เหลือ 2 node; layer 3 มี 2 node × 0.5 ⇒ ปิด 1 node เหลือ 1 node — เครือข่ายที่เหลือในรอบนั้นจึงผอมลงเป็น 2-2-1 ทั้งที่โครงสร้างจริงคือ 3-3-2. และรอบถัดไปจะสุ่มใหม่ทั้งหมด ได้เครือข่ายผอม ๆ คนละรูปแบบ. นี่คือหัวใจของ dropout: แทนที่จะเทรนเครือข่ายเดียว เรากำลังเทรนเครือข่ายย่อยจำนวนมหาศาลที่แบ่งปัน weight กัน แล้วตอน test จึงใช้ทั้งหมดพร้อมกัน.

ทำไมมันกัน overfit ได้ (เหตุผลเชิงกลไก): ถ้า node A เรียนรู้ที่จะพึ่งพา node B เสมอ พอ B ถูกปิดแบบสุ่ม A ก็ใช้ไม่ได้ — โมเดลจึงถูกบังคับให้กระจายความรู้ ไม่ให้ node ไหนกลายเป็น "ตัวชี้ขาด" ที่จำ training example บางอันไว้เฉพาะตัว. ศัพท์ที่ใช้เรียกอาการที่ dropout ป้องกันคือ co-adaptation (การพึ่งพากันเองของ node).

ต้องรู้ก่อน — ความน่าจะเป็นที่ node หนึ่งจะถูกปิด

ความน่าจะเป็น คือตัวเลขระหว่าง 0 ถึง 1 ที่บอกโอกาสเกิดเหตุการณ์: 0 = ไม่เกิดแน่นอน, 1 = เกิดแน่นอน, 0.5 = ครึ่งต่อครึ่ง. ถ้าโยนเหรียญที่ออกหัวด้วยความน่าจะเป็น 0.2 แล้วโยน 10 ครั้ง คุณคาดหวังว่าจะออกหัวราว \(10\times0.2 = 2\) ครั้ง (จะได้ 1 หรือ 3 ครั้งก็ไม่แปลก — "ค่าคาดหวัง" คือค่าเฉลี่ยระยะยาว ไม่ใช่การรับประกัน)

ใน dropout เหตุการณ์คือ "node นี้ถูกปิดไหม" และมีสองชื่อที่เป็นคู่ตรงข้ามกัน ห้ามสับสน:

ตัวอย่าง: layer หนึ่งมี 10 node, dropout rate 0.2 ⇒ keep_prob = 0.8 ⇒ คาดว่าจะเหลือทำงาน \(10\times0.8=8\) node ต่อรอบ. ทุก node ถูกสุ่มอย่างอิสระต่อกัน ดังนั้นบางรอบอาจเหลือ 7 บางรอบเหลือ 9 — 8 คือค่าเฉลี่ย.

Slide Dropout (2): at training time adjust the activation because some nodes were dropped; example with dropout rate 0.1 gives keep prob 0.9 so increase the activation by 10 percent by dividing by keep prob, called Inverted Dropout; this is crucial because at inference a neuron would be connected to more input neurons than during training if the scaling was not applied; at test time no need to do dropout
Slide: Dropout (2) — Inverted Dropout และพฤติกรรมตอน test (Lecture4.pdf, p.29)

หน้านี้แยกพฤติกรรมของ dropout ออกเป็นสองโหมดชัดเจน ซึ่งเป็นโครงสร้างที่ข้อสอบชอบถาม:

เหตุผลที่การปรับนี้สำคัญ (ประโยคที่อาจารย์เน้นว่า "crucial"): ถ้าไม่ scale ตอนเทรน แล้วพอถึงตอน test/inference neuron ตัวหนึ่งจะ "ถูกเชื่อมกับ input neuron มากกว่าตอนเทรน" — เพราะตอนเทรนมีเพื่อนบ้านแค่ 90% ที่เปิดอยู่ แต่ตอน test เปิดครบ 100%. ผลรวมที่มันได้รับจึงใหญ่เกินกว่าที่มันเคยเรียนมา โมเดลจะทำนายเพี้ยนทั้งที่ weight ถูกต้อง. ตรวจสอบด้วยคณิตศาสตร์สั้น ๆ: ค่าคาดหวังของ activation ที่ผ่าน inverted dropout คือ \(E\!\left[\frac{a\cdot D}{\text{keep\_prob}}\right] = \frac{a\times \text{keep\_prob}}{\text{keep\_prob}} = a\) — เท่ากับค่าเดิมพอดี จึงตรงกับตอน test ที่ใช้ \(a\) เต็ม ๆ.

ข้อสังเกตเรื่องตัวเลข "เพิ่ม 10%": การหารด้วย 0.9 คือการคูณด้วย \(1/0.9 = 1.111\) ซึ่งคือเพิ่ม 11.1% ไม่ใช่ 10% เป๊ะ. อาจารย์พูดแบบปัดให้เข้าใจง่าย — ในข้อสอบถ้าให้คำนวณ ให้ใช้ "หารด้วย keep_prob" เป็นหลักเสมอ อย่าไปบวก 10% ตรง ๆ.

คำนวณ: Dropout ครบหนึ่ง iteration ทั้ง forward และ backward (ตามขั้นตอนหน้า 31)
ให้ hidden layer มี 4 unit, activation \(a=[2.0,\ 5.0,\ 3.0,\ 1.0]\), dropout rate = 0.2 ⇒ keep_prob = 0.8; สุ่มเลขจาก np.random.rand ได้ \(D_{\text{raw}}=[0.37,\ 0.95,\ 0.73,\ 0.60]\)
  1. ขั้นที่ 1 — แปลง \(D\) เป็น 0/1 โดยใช้ keep_prob เป็นเกณฑ์ (เก็บถ้าน้อยกว่า keep_prob)
    $$[0.37,0.95,0.73,0.60] < 0.8\ \Rightarrow\ D = [1,\ 0,\ 1,\ 1]$$

    unit ที่ 2 ถูกปิด เพราะ 0.95 ไม่น้อยกว่า 0.8. จำนวนที่ถูกปิดในรอบนี้คือ 1 จาก 4 (25%) ซึ่งใกล้เคียงแต่ไม่เท่ากับ 20% พอดี — เป็นเรื่องปกติของการสุ่ม

  2. ขั้นที่ 2 — Forward: ปิด node ด้วย np.multiply() ระหว่าง activation กับ D
    $$a\odot D = [2.0,\ 5.0,\ 3.0,\ 1.0]\odot[1,0,1,1] = [2.0,\ 0.0,\ 3.0,\ 1.0]$$
  3. ขั้นที่ 3 — Forward: scale ตัวที่รอดด้วยการหาร keep_prob
    $$[2.0,\ 0.0,\ 3.0,\ 1.0]\div0.8 = [2.5,\ 0.0,\ 3.75,\ 1.25]$$

    ตรวจค่าคาดหวัง: ผลรวมเดิม \(2+5+3+1=11\); ผลรวมหลัง dropout+scale \(=2.5+0+3.75+1.25=7.5\) ซึ่งรอบนี้ต่ำไปเพราะบังเอิญปิด unit ที่ใหญ่ที่สุด แต่เฉลี่ยข้ามหลายรอบจะได้ 11 พอดี (เพราะแต่ละ unit มีโอกาสรอด 0.8 แล้วถูกขยาย 1.25)

  4. ขั้นที่ 4 — Backward: ใช้ mask ตัวเดิม กับ \(\delta\) แล้วหารด้วย keep_prob เดิม
    $$\delta = [0.4,\ -0.2,\ 0.1,\ 0.3]\ \Rightarrow\ \delta\odot D = [0.4,\ 0,\ 0.1,\ 0.3]\ \Rightarrow\ \div0.8 = [0.5,\ 0,\ 0.125,\ 0.375]$$

    unit ที่ 2 ได้ \(\delta=0\) จึงไม่ถูกอัปเดตในรอบนี้ — สมเหตุสมผลเพราะมันไม่ได้มีส่วนร่วมกับผลลัพธ์เลย

  5. ขั้นที่ 5 — ตอน test

    ไม่ทำ dropout: ใช้ \(a=[2.0,5.0,3.0,1.0]\) เต็มทุก unit ไม่มี mask ไม่มีการหาร

ตัวคูณชดเชยตาม keep_prob: 0.9 ⇒ ×1.111, 0.8 ⇒ ×1.25, 0.67 ⇒ ×1.49, 0.5 ⇒ ×2 — dropout rate ยิ่งสูง keep_prob ยิ่งต่ำ ตัวคูณยิ่งใหญ่. และกฎที่ต้องจำสองข้อ: (1) backward ต้องใช้ mask ตัวเดียวกับ forward (ไม่ใช่สุ่มใหม่) (2) backward ต้องหารด้วย keep_prob เหมือนกัน เพราะ chain rule ต้องผ่านการขยาย \(1/0.8\) ที่เราทำไว้ตอน forward.

Slide Dropout (3) Other Issues: dropout is mainly used to reduce overfitting (high variance) and improve generalization; to determine whether dropout is too much or too little compare training and validation performance; during actual training keep dropout ON and monitor both losses; dropout introduces randomness during training so the training cost may fluctuate and is harder to use for debugging convergence; dropout can be temporarily disabled to verify the cost decreases then enabled again; with a very noisy training cost plot over 50000 iterations
Slide: Dropout (3) — Other Issues (Lecture4.pdf, p.30)

หน้า Other Issues เป็นคำแนะนำเชิงปฏิบัติล้วน ๆ 4 ข้อที่ควรอ่านเป็นลำดับขั้นตอนการทำงานจริง:

  1. วัตถุประสงค์: dropout ใช้เพื่อ ลด overfitting (high variance) และ ปรับปรุง generalization เป็นหลัก — ไม่ใช่เพื่อเร่งความเร็ว ไม่ใช่เพื่อแก้ underfitting
  2. จะรู้ได้ไงว่าใส่มาก/น้อยไป: ให้เทียบ performance ของ training กับ validation — ถ้า training ดีมากแต่ validation แย่ = ยัง overfit อยู่ (เพิ่ม dropout ได้); ถ้าทั้งคู่แย่พอ ๆ กัน = อาจใส่มากไปจน underfit
  3. ระหว่างเทรนจริง: ให้เปิด dropout ไว้ (keep dropout ON) และเฝ้าดูทั้ง loss/accuracy ของทั้ง training และ validation
  4. ปัญหาเวลา debug: dropout ใส่ความสุ่มเข้ามาระหว่างเทรน เพราะ neuron ที่ถูกปิดต่างกันทุก iteration ⇒ training cost จึงแกว่ง และ "harder to use for debugging convergence". กราฟที่อาจารย์แปะไว้คือหลักฐาน: cost ดิ่งจาก 0.7 ลงมาแถว 0.4 ภายในราว 10,000 iteration แรก แต่ความหนาของแถบที่แกว่งกว้างราว 0.3–0.5 ยังคงอยู่จนจบ 50,000 iteration — คุณแทบอ่านไม่ออกว่ามันยังดีขึ้นอยู่หรือหยุดแล้ว

วิธีแก้ที่อาจารย์แนะนำ (เป็นขั้นตอนที่ควรท่องได้): ถ้าคุณสงสัยว่า "โมเดลเรียนรู้จริงหรือเปล่า หรือโค้ดฉันพัง" — ให้ปิด dropout ชั่วคราวแล้วดูว่า cost ลดลงตามปกติไหม. ถ้า ปิดแล้ว training loss ก็ยังไม่ลดเลย แสดงว่าปัญหาอยู่ที่อย่างอื่น: learning rate, gradient, หรือ implementation ของคุณ. เมื่อยืนยันว่า cost ลดปกติแล้วจึงเปิด dropout กลับมาเทรนจริง. ตรรกะนี้คือ "ตัดตัวแปรรบกวนออกก่อนแล้วค่อยตรวจ" ซึ่งเป็นวิธี debug ที่ใช้ได้กับทุกเรื่อง ไม่ใช่แค่ dropout.

Slide Dropout Implementation: Forward Propagation steps are initialize matrix D for masking using np.random.rand, convert entries of D to 0 or 1 using keep_prob as the threshold, shutdown some neurons using np.multiply of activation and D, and scale the value of neurons that have not been shut down by activation over keep prob; Backward Propagation steps are shutdown the same neurons by reapplying the same mask to delta and scale delta by the same keep_prob; with a Keras Sequential model using Flatten and three Dropout layers at rate 0.2
Slide: Dropout Implementation — forward/backward ทีละขั้น + โค้ด Keras (Lecture4.pdf, p.31)

อาจารย์แจกแจงขั้นตอนการเขียน dropout เองด้วย numpy ไว้ครบ ซึ่งตรงกับตัวอย่างคำนวณที่เราเพิ่งทำไปทุกขั้น:

ขั้นตอนทำอะไรตรงกับขั้นไหนในตัวอย่างข้างบน
Forward Propagationสร้างเมทริกซ์ \(D\) สำหรับ masking ด้วย np.random.rand(..)ได้ \([0.37,0.95,0.73,0.60]\) — ค่าสุ่มสม่ำเสมอในช่วง 0 ถึง 1
แปลงสมาชิกของ \(D\) เป็น 0 หรือ 1 โดยใช้ keep_prob เป็นเกณฑ์ (threshold)ขั้นที่ 1 ⇒ \(D=[1,0,1,1]\)
ปิด neuron บางตัวด้วย np.multiply() ระหว่าง activation กับ \(D\)ขั้นที่ 2 ⇒ \([2.0,0,3.0,1.0]\)
Scale ค่าของ neuron ที่ยังไม่ถูกปิด: activation หารด้วย keep probขั้นที่ 3 ⇒ \([2.5,0,3.75,1.25]\)
Backward Propagationปิด neuron ตัวเดิม โดยนำ mask อันเดิมมาใช้กับ delta ซ้ำขั้นที่ 4 ส่วนแรก
Scale delta ด้วย keep_prob ตัวเดิมขั้นที่ 4 ส่วนหลัง

คำที่ต้องอ่านให้ละเอียดคือ "the same neurons" และ "the same keep_prob" ในฝั่ง backward — ย้ำสองครั้งว่าห้ามสุ่มใหม่. เหตุผล: backprop คือการหาอนุพันธ์ของสิ่งที่ forward ทำไปจริง ๆ ถ้า forward ปิด unit 2 แล้ว backward ไปปิด unit 3 แทน gradient ที่ได้จะไม่ใช่อนุพันธ์ของอะไรเลย. ส่วนโค้ด Keras ในกรอบล่างแสดงว่าเมื่อใช้ไลบรารี ทุกขั้นตอนข้างบนถูกซ่อนไว้ในบรรทัดเดียว:

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(10, activation="softmax")
])
โค้ดทำอะไร
keras.models.Sequential([...])ต่อ layer เรียงกันเป็นทอด ๆ ตามลำดับในลิสต์ — ข้อมูลไหลจากบนลงล่าง
keras.layers.Flatten(input_shape=[28, 28])รับภาพขนาด 28×28 (รูปแบบ MNIST) แล้วยืดเป็นเวกเตอร์ยาว \(28\times28=784\) ช่อง เพื่อป้อนเข้า Dense. ชั้นนี้ไม่มีพารามิเตอร์เลย
keras.layers.Dropout(rate=0.2) (ตัวแรก)ปิด input 20% แบบสุ่ม — สังเกตว่า Dropout เป็น layer ของตัวเอง ที่วางคั่น ไม่ใช่อาร์กิวเมนต์ของ Dense. rate คือสัดส่วนที่ ปิด (keep_prob = 0.8)
Dense(300, activation="elu", kernel_initializer="he_normal")hidden layer 300 neuron. ได้พารามิเตอร์ \(300\times(784+1)=235{,}500\) ตัว
Dropout(rate=0.2) ตัวที่ 2 และ 3วางคั่นก่อน Dense ถัดไปทุกครั้ง — แปลว่า output ของ 300-neuron layer และของ 100-neuron layer ถูกปิด 20% เช่นกัน
Dense(100, ...)hidden layer ที่สอง 100 neuron
Dense(10, activation="softmax")output layer 10 คลาส (เลข 0–9) ใช้ softmax เพื่อให้ผลรวมความน่าจะเป็นเป็น 1. ไม่มี Dropout หลังชั้นนี้ — ไม่มีใครใส่ dropout ที่ output layer

ผลลัพธ์ที่คาด: โมเดลนี้มี 4 layer ที่มีพารามิเตอร์ (Dense 3 ชั้น) รวม \(235{,}500 + 100\times301 + 10\times101 = 235{,}500+30{,}100+1{,}010 = 266{,}610\) ตัว ส่วน Flatten และ Dropout ทั้งสามชั้นมี 0 พารามิเตอร์ (ไม่มีอะไรให้เรียน มีแต่การสุ่มปิด). ตอน model.fit() ทั้งสามชั้น Dropout จะทำงาน แต่ตอน model.predict() / model.evaluate() Keras ปิดการทำงานของมันให้อัตโนมัติ ตรงกับประโยค "at test time no need to do dropout" ของหน้า 29 — คุณไม่ต้องเขียนอะไรเพิ่ม.

6.3 Early Stopping (หน้า 32–33)

Slide Early Stopping: early stopping stops training early to avoid overfitting; it monitors a model's performance every epoch on a validation set and terminates the training when the validation error reaches a minimum; you can set a large number of epochs and training will automatically stop when there are no more improvements; compared to retraining with an optimal number of epochs, early stopping does not incur much computational cost; with an error-versus-epochs graph where the training curve keeps decreasing while the validation curve turns upward at the marked Stop training point
Slide: Early Stopping (Lecture4.pdf, p.32)

นิยามหนึ่งบรรทัดของอาจารย์: early stopping stops training early เพื่อ avoid overfitting. กลไกที่ละเอียดกว่านั้นอยู่ในหัวข้อย่อยทั้งสามข้อ:

  1. เฝ้าดูอะไร: ติดตามผลของโมเดลทุก epoch บน validation set แล้ว terminate the training when the validation error reaches a minimum — อาจารย์วงเล็บต่อว่า "(for a while or when you're sure the model won't improve)" คือไม่ได้หยุดทันทีที่เห็นค่าขึ้นครั้งเดียว แต่รอดูสักพักให้แน่ใจก่อน (ในทางปฏิบัติคือพารามิเตอร์ patience ที่คำนวณเต็มใน Lesson 6 §2)
  2. ประโยชน์ข้อแรก: คุณไม่ต้องกังวลเรื่องเลือกจำนวน epoch ที่พอดี — ตั้งเลข epoch ไว้เยอะ ๆ ได้เลย แล้วการเทรนจะ automatically stop เมื่อไม่มีการปรับปรุงของ validation error อีกต่อไป. พูดอีกแบบ: early stopping เปลี่ยน hyperparameter ที่เดายาก (จำนวน epoch) ให้กลายเป็นสิ่งที่ตัดสินจากข้อมูลจริง
  3. ประโยชน์ข้อสอง: เทียบกับการ "เทรนใหม่ทั้งหมดด้วยจำนวน epoch ที่เหมาะสม" (คือเทรนรอบแรกเพื่อหาว่าควรหยุดที่ไหน แล้วเทรนใหม่อีกรอบ) early stopping ช่วยไม่ให้เสีย computational cost มากนัก — เพราะทำทุกอย่างจบในรอบเดียว

กราฟบนสไลด์แสดงสิ่งที่เกิดขึ้นตลอดการเทรน: เส้น Training (สีเข้ม) ลดลงเรื่อย ๆ ไม่มีวันขึ้น ขณะที่เส้น Validation (สีชมพู) ลดลงตามมาระยะหนึ่งแล้วเลี้ยวกลับขึ้น. จุดที่เส้นชมพูต่ำสุดคือจุดที่มีป้าย "Stop training" — หลังจุดนั้นโมเดลยัง "เก่งขึ้น" กับข้อมูลเทรนแต่ "แย่ลง" กับข้อมูลที่ไม่เคยเห็น นั่นคือนิยามของ overfitting ที่มองเห็นเป็นรูปธรรม.

คำนวณ: หาจุดหยุดจากตัวเลข validation loss จริง
ให้ validation loss รายรอบ: 0.60, 0.42, 0.35, 0.33, 0.34, 0.37, 0.41 (7 epoch) โดย training loss ลดลงต่อเนื่องทุกรอบ
  1. ขั้นที่ 1 — หาค่าต่ำสุดของ validation

    ไล่เทียบทีละตัว: 0.60 > 0.42 > 0.35 > 0.33 แล้วเริ่มเพิ่ม ⇒ ต่ำสุดคือ 0.33 ที่ epoch 4

  2. ขั้นที่ 2 — ตรวจว่าหลังจากนั้นมันเพิ่มจริงไหม

    0.34 → 0.37 → 0.41 เพิ่มสามรอบติด ขณะที่ training loss ยังลด ⇒ ยืนยันว่า overfitting เริ่มหลัง epoch 4 ไม่ใช่ความบังเอิญของรอบเดียว

  3. ขั้นที่ 3 — ถ้าตั้ง patience = 2 จะหยุดตอนไหน

    นับรอบที่ไม่ดีขึ้นติดต่อกัน: epoch 5 (ครั้งที่ 1), epoch 6 (ครั้งที่ 2) ⇒ หยุดที่ท้าย epoch 6 แต่ weight ที่ควรเก็บคือของ epoch 4

หยุดและคืน weight ของ epoch 4 (ในโค้ดจริงคือ EarlyStopping(patience=2, restore_best_weights=True) ซึ่งลงรายละเอียดใน Lesson 6 §2). จุดสำคัญ: จุดที่หยุด (epoch 6) กับ จุดที่ดีที่สุด (epoch 4) ไม่ใช่จุดเดียวกัน — ต้องมี restore_best_weights ถึงจะได้โมเดลที่ดีที่สุดจริง ๆ.

Slide Early Stopping (2): early stopping simultaneously addresses both bias and variance by stopping the optimization process early; however this deviates from the ideal systematic improvement process which addresses various aspects of the model and data as depicted in the workflow on the right; make sure to use early stopping once all hyperparameters have been chosen; with a scatter plot of a High Bias and High Variance model and a flowchart from define problem through build architecture, collect data, train an epoch, check training and validation error, performs well on training and test data, to We're finished
Slide: Early Stopping (2) — ข้อควรระวังและ workflow ที่ถูกต้อง (Lecture4.pdf, p.33)

หน้านี้เป็นคำเตือน ไม่ใช่คำชม. อาจารย์บอกว่า early stopping แก้ both bias and variance ไปพร้อมกันด้วยการหยุดกระบวนการ optimize ก่อนเวลา — ฟังดูดีแต่นั่นแหละคือปัญหา: วิธีนี้เบี่ยงเบนจาก systematic improvement process ในอุดมคติ ซึ่งควรแก้ทีละด้านของโมเดลและข้อมูลอย่างเป็นระบบ ตามผังงานที่แปะไว้ด้านขวา. เหตุผลที่ "แก้พร้อมกัน" เป็นเรื่องไม่ดี: ถ้าคุณหยุดเทรนเร็ว คุณได้โมเดลที่ยังไม่ฟิตเต็มที่ (bias ยังสูงอยู่บ้าง) และยังไม่ทันจำข้อมูล (variance ต่ำ) — คุณไม่มีทางรู้ว่าปัญหาจริงของโมเดลคือด้านไหน เพราะคุณกลบทั้งสองด้านด้วยการกระทำเดียว.

ผังงาน "Detailed workflow for training and evaluating a deep learning model" ที่อาจารย์อ้างอิง (จาก Fundamental of Deep Learning) เดินเป็นลำดับแบบนี้: Define problem → Build neural net architecture → Collect (or add more) data → Train for an epoch → แล้วเข้าจุดตัดสินใจสองชั้น: "Is error on training data decreasing?" ถ้า Yes ให้วนกลับไปเทรนต่อ ถ้า No ให้ย้อนกลับไปแก้สถาปัตยกรรม; แล้ว "Is error on validation data decreasing?" ถ้า No ให้ย้อนไปเก็บข้อมูลเพิ่ม; จากนั้น "Performs well on training data?" → "Performs well on test data?" → "We're finished!!!". อ่านผังนี้เป็นหลักคิด: training error ไม่ลด = ปัญหาโครงสร้าง/bias ⇒ แก้ที่สถาปัตยกรรม; validation error ไม่ลดทั้งที่ training ลด = ปัญหา variance ⇒ แก้ที่ข้อมูล — สองเส้นทางนี้คือสิ่งที่ early stopping ทำให้คุณข้ามไป.

ภาพเล็กทางซ้ายที่กำกับว่า "High Bias and High Variance model" ตอกย้ำเรื่องเดียวกัน: เส้นแบ่งเขตสีเขียวในภาพทั้งบิดเบี้ยวไปตามจุดแต่ละจุด (variance สูง) และยังแบ่งผิดอยู่หลายจุด (bias สูง) พร้อมกัน — เป็นไปได้ที่โมเดลจะแย่ทั้งสองด้าน และกรณีแบบนี้การหยุดเร็วไม่ช่วยอะไรเลย. บรรทัดสุดท้ายจึงเป็นคำสั่งเชิงปฏิบัติ: "Make sure using early stopping once all model's hyperparameters have been chosen" — ใช้ early stopping หลังเลือก hyperparameter อื่นครบแล้ว ไม่ใช่ใช้มันแทนการเลือก.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
สามเทคนิคในหัวข้อนี้แก้ overfit คนละจุดกัน: L1/L2 เพิ่มเทอมลงโทษใน cost (แก้ตอนคำนวณ gradient), Max-Norm ตัดยอดความยาวของ \(w\) (แก้หลังอัปเดต), Dropout บีบไม่ให้ node พึ่งพากัน (แก้ตอน forward), Early Stopping หยุดก่อนจะจำ (แก้ที่เวลา). ใช้พร้อมกันได้เพราะไม่ทับซ้อนกลไกกัน. เหตุผลที่ dropout ต้องหารด้วย keep_prob คือผลรวม input ที่ node ถัดไปได้รับลดลงเมื่อเพื่อนบ้านหายไป ถ้าไม่ชดเชย พอถึงตอน test ที่ทุก node กลับมา ผลรวมจะใหญ่กว่าที่โมเดลเคยเห็น.
เปลี่ยนแล้วเกิดอะไร
dropout rate 0.1 ⇒ หารด้วย 0.9 (×1.111); rate 0.5 ⇒ หารด้วย 0.5 (×2) — ยิ่งปิดมากยิ่งขยายมากตามสัดส่วน เพื่อรักษาค่าคาดหวังของผลรวมให้เท่าเดิม. ส่วน max-norm: ลด \(r\) จาก 1 เหลือ 0.5 ⇒ weight \((3,4)\) ถูกหดเป็น \((0.3,0.4)\) แทน \((0.6,0.8)\) คือบีบแรงขึ้นเท่าตัว.
เชื่อมกับ
L1/L2 เป็นสูตรเดียวกับ Lesson 1 §11 และ Lesson 3 §7; สัญญาณ "\(J_{cv}\) เริ่มแย่ลง" ที่ early stopping ใช้ คือสัญญาณเดียวกับที่ Lesson 1 §10 ใช้เลือก degree ของ polynomial ต่างกันแค่ว่าที่นี่เลือกตามเวลา ที่นั่นเลือกตามcapacity.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 13Max-Norm Regularization
Max-Norm Regularization ต่างจาก L1/L2 Regularization อย่างไรในเชิงวิธีการทำงาน?
คำถาม 14Dropout Scaling
เมื่อ dropout rate = 0.1 (keep_prob = 0.9) เพราะเหตุใดจึงต้องหาร activation ที่เหลือด้วย 0.9 ระหว่างเทรน (Inverted Dropout)?
คำถาม 15อ่านโค้ด Dropout
จากโค้ด Sequential หน้า 31 ที่ใช้ Dropout(rate=0.2) คั่นระหว่าง Dense ข้อใดถูกต้อง?
คำถาม 16Early Stopping
Early Stopping ใช้สัญญาณอะไรในการตัดสินใจหยุดเทรน และอาจารย์เตือนอะไรไว้ในหน้า 33?

7. Data Augmentation: เพิ่มข้อมูลโดยไม่ต้องเก็บใหม่

กลับไปที่ต้นเหตุ: §6 ทั้งหัวข้อแก้ overfitting ด้วยการ "ลดความสามารถของโมเดล" (บีบ weight, ปิด node, หยุดเร็ว). หน้า 34 เสนอทางแก้อีกทิศหนึ่งที่ตรงกันข้าม — แทนที่จะลดโมเดล ให้เพิ่มข้อมูล. ซึ่งเป็นทางแก้ที่ตรงจุดกว่า เพราะอาจารย์ระบุไว้ตั้งแต่หน้า 5 แล้วว่า overfit รุนแรงขึ้นเมื่อ "ตัวอย่างไม่พอ".

Slide Data Augmentation: a few training examples cause overfitting; one way to avoid it is to get more data (not always a feasible option); data augmentation artificially generates more training data from existing training examples with some random transformations; during training the model encounters various data facets, mitigating overfitting and improving generalization, and in image classification becomes more adaptable to object position, orientation and size variations; with one mushroom photo turned into six rotated and shifted versions, and a training-versus-validation loss plot
Slide: Data Augmentation (Lecture4.pdf, p.34)

อาจารย์เปิดด้วยข้อเท็จจริง: "ตัวอย่างเทรนที่มีน้อยก่อให้เกิดปัญหา overfitting" ทางหนึ่งที่จะเลี่ยงคือ get more data — แต่วงเล็บสีแดงตามมาทันทีว่า (not always a feasible option) คือ "ไม่ใช่ทางเลือกที่ทำได้เสมอไป" (เก็บข้อมูลเพิ่มอาจแพง ช้า หรือเป็นไปไม่ได้เลย เช่นภาพถ่ายทางการแพทย์ของโรคหายาก). ทางออกคือ Data augmentation ซึ่งนิยามได้ว่า: การ generate training data เพิ่มขึ้นมาแบบสังเคราะห์ (artificially) จาก existing training examples ด้วย random transformations บางอย่าง.

ภาพตัวอย่างบนสไลด์คือเห็ดสีขาวดอกเดียว ที่ถูกแปลงออกมาเป็น 6 ภาพ — แต่ละภาพเอียงคนละมุม เลื่อนตำแหน่งคนละจุด และครอปกรอบไม่เท่ากัน แต่ยังเป็นเห็ดดอกเดิมและยังติดป้ายว่า "เห็ด" เหมือนเดิม. จากข้อมูล 1 ชิ้นกลายเป็น 6 ชิ้นโดยไม่ต้องออกไปถ่ายรูปเพิ่ม. อาจารย์อธิบายผลที่ได้ไว้สองระดับ: ระดับทั่วไป — ระหว่างเทรน โมเดลได้เจอแง่มุมที่หลากหลายของข้อมูล จึงบรรเทา overfitting และ generalize ได้ดีขึ้น; ระดับเฉพาะงานภาพ — ในการจำแนกภาพ โมเดลจะปรับตัวเข้ากับความแปรผันของ ตำแหน่ง (position), ทิศทาง (orientation) และขนาด (size) ของวัตถุได้ดีขึ้น. สามคำนี้ไม่ได้มาลอย ๆ — มันตรงกับการแปลงสามชนิดที่ใช้บ่อยที่สุดพอดี: เลื่อน (shift), หมุน (rotate), ย่อ-ขยาย (scale/zoom).

กราฟเล็กทางขวาของสไลด์ (Training and validation loss) แสดงสถานการณ์ที่ data augmentation มีไว้แก้: จุดสีน้ำเงิน (training loss) ไล่ลงจาก ~0.7 ไปจนต่ำกว่า 0.2 ที่ epoch 20 ขณะที่เส้น validation loss กลับไต่ขึ้นจาก ~0.55 ไปแตะ ~0.72 ในช่วงเดียวกัน — ช่องว่างที่ถ่างออกเรื่อย ๆ ระหว่างสองเส้นคือรูปร่างมาตรฐานของ overfitting และเป็นภาพเดียวกับที่ early stopping ใช้หาจุดหยุดในหน้า 32.

คำนวณ: augmentation ให้โมเดลเห็นข้อมูลมากขึ้นเท่าไร
ให้มีภาพจริง 1,000 ภาพ; ทุก epoch ระบบสุ่มแปลงใหม่ (พลิกซ้าย-ขวา + หมุน −15° ถึง +15° + ครอป) แล้วเทรน 30 epoch
  1. ขั้นที่ 1 — ข้อมูลที่เก็บจริงบนดิสก์

    1,000 ภาพ — ไม่เพิ่มขึ้นเลย (augmentation ทำสด ๆ ตอนโหลดเข้าเทรน ไม่ได้เซฟไฟล์ใหม่)

  2. ขั้นที่ 2 — จำนวนภาพที่โมเดล "เห็น" ตลอดการเทรน
    $$1{,}000\times30 = 30{,}000\ \text{ภาพ}$$

    และส่วนใหญ่ไม่ซ้ำกัน เพราะมุมหมุนสุ่มใหม่ทุกครั้ง (มุมเป็นค่าต่อเนื่องในช่วง −15° ถึง +15°)

  3. ขั้นที่ 3 — เทียบกับการไม่ทำ augmentation

    โมเดลจะเห็นภาพเดิมซ้ำ 30 รอบพอดี ⇒ จำ pixel ได้ง่ายกว่ามาก นั่นคือเส้นทางสู่ overfit

ประเด็นไม่ได้อยู่ที่ตัวเลข 30,000 แต่อยู่ที่ "ไม่ซ้ำ": การเห็นภาพเดิม 30 ครั้งสอนให้โมเดลจำ ส่วนการเห็นภาพเดิมที่แปลงต่างกัน 30 แบบสอนให้โมเดลรู้ว่าอะไรคือสิ่งที่ไม่เปลี่ยน (ตัวเห็ด) และอะไรคือสิ่งที่เปลี่ยนได้ (มุม ตำแหน่ง ขนาด) ซึ่งตรงกับคำว่า position, orientation, size ที่อาจารย์เขียนไว้.

กลไกที่ต้องอธิบายได้
ทำไมต้องมี
ได้ผลเพราะการแปลงที่เลือกใช้ไม่เปลี่ยน label (เห็ดหมุน 15° ก็ยังเป็นเห็ด) แต่เปลี่ยน pixel ที่โมเดลเห็น — เท่ากับเพิ่ม "ความแปรผันที่ label คงที่" ให้โมเดลเรียนว่าอะไรคือสิ่งที่ต้องสนใจและอะไรคือสัญญาณรบกวน.
เปลี่ยนแล้วเกิดอะไร
ถ้าเอาไปใช้กับงานแยกเลข 6 กับ 9 แล้วใส่ "หมุน 180°" เข้าไปในชุดการแปลง จะทำร้ายทันที เพราะ 6 หมุนแล้วกลายเป็น 9 แต่ augmentation ยังติดป้ายว่า "6" — โมเดลจะถูกสอนด้วยข้อมูลผิด. กฎ: การแปลงต้องรักษา label เสมอ และขึ้นกับตัวงาน ไม่ใช่สูตรสำเร็จ.
เชื่อมกับ
เป้าหมายเดียวกับ Dropout/Regularization (ทำให้โมเดลไม่ยึดติดรายละเอียดที่ไม่สำคัญ) แต่ลงมือที่ "ข้อมูล" แทนที่จะที่ "โมเดล" — จึงใช้ร่วมกับทั้งหมดใน §6 ได้พร้อมกัน.
ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 17Data Augmentation
ข้อใดอธิบาย Data Augmentation ได้ตรงตามนิยามที่อาจารย์ทำสีไว้ในหน้า 34?

7A. การบ้านที่อาจารย์สั่ง: ReLU + Dropout บน Circle Dataset (หน้า 35)

Slide Homework 2: try using ReLU as activation function for hidden layers and dropout regularization; two scatter plots of the Circle Dataset with red positive and blue negative points arranged in two concentric rings, each with a green decision boundary, one more jagged and one smoother
Slide: Homework 2 — โจทย์ปิดท้ายบทของอาจารย์ (Lecture4.pdf, p.35)

สไลด์สุดท้ายคือการบ้าน: "Try using ReLU as activation function (hidden layers) and dropout regularization!!!" บน Circle Dataset — ข้อมูลสองมิติที่จุดสีแดง (positive) กับสีน้ำเงิน (negative) วางตัวเป็นวงแหวนสองวงซ้อนกันในช่วง \(x_1, x_2 \in [-1.5, 1.5]\) ซึ่งแยกด้วยเส้นตรงไม่ได้แน่นอน จึงต้องใช้เครือข่ายที่มี hidden layer. ภาพสองภาพแสดงเส้นแบ่งเขต (สีเขียว) สองแบบ: ภาพซ้ายเส้นหยักเป็นเหลี่ยมมุมเยอะ ไล่ตามจุดแต่ละจุดอย่างละเอียด ส่วนภาพขวาเส้นเรียบกว่า เป็นวงรีกว้าง ๆ ที่ยอมให้มีจุดหลุดออกนอกกรอบบ้าง.

อ่านสองภาพนี้ให้เป็น (นี่คือสิ่งที่การบ้านต้องการให้คุณเห็น): เส้นที่หยักมากกว่าคือโมเดลที่ฟิต training set ได้แน่นกว่า — ซึ่งอาจให้ accuracy บน training สูงกว่า แต่ความหยักนั้นคือการตอบสนองต่อจุดเดี่ยว ๆ ที่อาจเป็น noise (สังเกตว่ามีจุดน้ำเงินหลุดเข้ามาอยู่ในวงแดงอยู่จริงในข้อมูล). เส้นที่เรียบกว่าคือผลของ regularization ที่ทำงาน: ยอมผิดบนข้อมูลเทรนไม่กี่จุดเพื่อให้ได้รูปร่างที่ใกล้ความจริงของปัญหา (วงกลม) มากกว่า — และ dropout คือหนึ่งในวิธีที่ผลิตเส้นแบบหลัง. นี่คือภาพ bias–variance trade-off ที่จับต้องได้.

แนวทางทำการบ้าน (ประกอบจากสิ่งที่สอนในบทนี้ทั้งหมด): ใช้โครงข่ายแบบเดียวกับที่อาจารย์ทดลองในหน้า 24 คือ \([2,10,10,10,1]\), เปลี่ยน activation ของ hidden layer เป็น "relu", ใส่ keras.layers.Dropout(rate=...) คั่นระหว่าง Dense ตามรูปแบบโค้ดหน้า 31, เลือก dropout rate ในช่วงที่หน้า 28 แนะนำ (0.1–0.5) โดยเริ่มที่ค่าต่ำ ๆ ก่อนเพราะเครือข่ายนี้เล็ก, output layer ใช้ 1 node กับ "sigmoid" เพราะเป็น binary classification, และใช้ Adam ตามผลการทดลองหน้า 24. จากนั้นเทียบ accuracy ของ training กับ validation ตามคำแนะนำหน้า 30 เพื่อตัดสินว่า dropout ที่ใส่ไปมากหรือน้อยเกินไป. เสริม (นอกสไลด์): กับข้อมูลสองมิติที่มีเพียงไม่กี่ร้อยจุดและเครือข่ายเล็กขนาดนี้ dropout ที่แรงเกินไป (เช่น 0.5 ทุกชั้น) มักทำให้ underfit เห็นได้ชัด เพราะปิดไป 5 จาก 10 node ต่อชั้นเหลือกำลังไม่พอวาดวงกลม — ถ้าเจออาการนั้นให้ลดลงมาที่ 0.1–0.2.

ออกสอบสูง — ข้อความที่อาจารย์ทำสี
คำถาม 18สรุปรวมทั้งบท
โมเดลของคุณได้ training accuracy 99% แต่ validation accuracy เพียง 72% ข้อใดคือชุดเครื่องมือจากบทนี้ที่ตรงกับปัญหา?


ชัยชนะของบทเรียนนี้
คุณควรอธิบายได้ว่าสูตร forward/backprop ขยายจาก 2 layer เป็น \(L\) layer อย่างไร (และเห็นจากตัวเลขว่า gradient ชั้นต้นเล็กลง 15.7 เท่า), ท่องปัญหา 4 ข้อของการเทรน deep network พร้อมทางแก้ของแต่ละข้อได้, นับ iteration ต่อ epoch ของ Batch/SGD/Mini-batch ได้และบอก Pros/Cons ของทั้งสามได้ครบทุกข้อตามสไลด์, กางสูตร EWA ด้วยมือจนเห็นว่าน้ำหนักรวมเป็น \(1-\beta^t\) (ซึ่งอธิบาย bias correction ได้ทันที), คำนวณก้าวของ Momentum/RMSProp/Adam/AdamW ได้เป็นตัวเลข, เขียนบรรทัด Keras ของ optimizer ทั้งสี่ตัวได้ถูกช่อง, และอธิบาย L1/L2/Max-Norm, Dropout (พร้อมขั้นตอน forward/backward และตัวคูณ keep_prob), Early Stopping และ Data Augmentation ได้ว่าแต่ละตัวแก้ overfit ด้วยกลไกใด — Lesson 5 จะรับช่วงต่อที่ปัญหา vanishing/exploding gradient และ batch normalization ที่หน้า 26 ทิ้งท้ายไว้ว่า "(later)".

ภาคผนวก: Slide ทั้งหมดใน Lecture4 (deep_learning_part2_1.pdf, 35 หน้า) — เช็คว่าอ่านครบ

หน้าเนื้อหาสถานะ
1ปก Part 2-Deep Neural Network: Deep Learning (ชื่อวิชา ภาคเรียน เวลาเรียน)etc — สไลด์ปก ไม่มีเนื้อหาวิชาการ
2Deep Neural Network — นิยาม "many hidden layers" + โครงข่าย fully connected✓ หัวข้อ 1 (มีรูป)
3How Deep Neural Network Works — Model Representation, computation layer 1/L, cost function ทั้งสองแบบ✓ หัวข้อ 1.1 (มีรูป + นับพารามิเตอร์)
4How Deep Neural Network Works (2) — backprop ทั่วไป + Interpretation✓ หัวข้อ 1.2 (มีรูป + คำนวณ δ ย้อน 3 ชั้น)
5Challenging in Training Deep Neural Networks — 4 ปัญหาหลัก✓ หัวข้อ 2 (มีรูป)
6Faster Optimizers — เหตุผล + รายชื่อ optimizer ทั้ง 5 ตัว✓ หัวข้อ 2 (มีรูป + ตารางสรุป)
7Batch Gradient Descent — Pros / Cons1 / ที่มาของชื่อ "batch"✓ หัวข้อ 3.1 (มีรูป + คำนวณ 100 ล้านแถว)
8Batch Gradient Descent (2) — Cons2: local minima problems✓ หัวข้อ 3.2 (มีรูป)
9–10Stochastic Gradient Descent (1)(2) — Pros 1/2, Cons 1/2✓ หัวข้อ 3.3 (มีรูปทั้งสองหน้า)
11Mini-Batch Gradient Descent — 10,000/10, shuffling, epoch vs iteration✓ หัวข้อ 3.4 (มีรูป + ตาราง iteration/epoch)
12Choosing an Appropriate Batch Size — เกณฑ์เล็ก/ใหญ่, 32–256, กำลังของ 2✓ หัวข้อ 3.5 (มีรูป + ตารางเงื่อนไข)
13Cost in Training Mini-Batch GD — แบบฝึกหัดวาดกราฟ (แกนเปล่า)✓ หัวข้อ 3.6 (มีรูป + เฉลยเต็ม)
14Gradient Descent with Momentum — EWA ของ gradient, top-down loss surface✓ หัวข้อ 4 (มีรูป)
15Exponential Weighted Moving Averages — สูตรและการกางสูตร✓ หัวข้อ 4.1 (มีรูป + คำนวณสองชุด)
16Implementation — pseudocode Momentum, Note เรื่อง :=, โค้ด Keras SGD✓ หัวข้อ 4.2 (มีรูป + โค้ดอธิบายบรรทัดต่อบรรทัด)
17More on Momentum — หนี local minima ด้วยแรงส่ง✓ หัวข้อ 4.3 (มีรูป)
18–19More on Momentum (2)(3) — เลือก η, learning rate decay + โค้ด ExponentialDecay✓ หัวข้อ 4.4 (มีรูปทั้งสองหน้า + โค้ด)
20Adaptive Learning Rate — นิยาม adaptive optimizer✓ หัวข้อ 5 (มีรูป)
21RMSprob (Root Mean Square Propagation) — สูตร s, α/ρ, โค้ด Keras✓ หัวข้อ 5.1 (มีรูป + คำนวณ + โค้ด)
22Adam (Adaptive Moment Estimation) — v, s, β₁, β₂, ε, โค้ด Keras✓ หัวข้อ 5.2 (มีรูป + ตารางค่า default + โค้ด)
23Bias Correction — biased estimate และการหารด้วย 1−βᵗ✓ หัวข้อ 5.3 (มีรูป + คำนวณสองก้าวแรก)
24Experiments — Mini-Batch GD / Momentum / Adam บน Spiral Dataset✓ หัวข้อ 5.4 (มีรูป + ตารางผลจริง)
25AdamW — decoupled weight decay, Why AdamW, โค้ด Keras✓ หัวข้อ 5.5 (มีรูป + คำนวณ + โค้ด)
26Overfitting Handling Strategies — 5 เทคนิค✓ หัวข้อ 6 (มีรูป + ตารางสรุป)
27Regularization — L1/L2 เลือกเมื่อไร, max-norm, โค้ด Dense สองชุด✓ หัวข้อ 6.1 (มีรูป + คำนวณ + โค้ดสองชุด)
28–29Dropout (1)(2) — นิยาม, dropout rate, Inverted Dropout, test time✓ หัวข้อ 6.2 (มีรูปทั้งสองหน้า + คำนวณ)
30–31Dropout (3) + Implementation — Other Issues, forward/backward, โค้ด Sequential✓ หัวข้อ 6.2 (มีรูปทั้งสองหน้า + โค้ด)
32–33Early Stopping (1)(2) — จุดหยุด, ข้อดี, ข้อควรระวัง, workflow✓ หัวข้อ 6.3 (มีรูปทั้งสองหน้า + คำนวณจุดหยุด)
34Data Augmentation — get more data, random transformations✓ หัวข้อ 7 (มีรูป + คำนวณ)
35Homework 2 — ReLU + dropout บน Circle Dataset✓ หัวข้อ 7A (มีรูป + แนวทางทำ)

สงสัยตรงไหน ถามได้เลยในแชท — ผมเป็นผู้สอนของคุณ ถามซ้ำได้ ถามลึกได้ ไม่ต้องเกรงใจ.