Tracks
เคยเห็นค่า loss เป็น NaN ระหว่างฝึกเครือข่ายประสาทเทียมเชิงลึกบ่อยแค่ไหน?
หลังจากฝึกมาหลายชั่วโมง เส้นโค้งของ loss ก็ดูปกติดี จู่ๆ ก็พุ่งทะยานเป็นอนันต์โดยไม่มีปี่มีขลุ่ย ปกติแล้วสาเหตุคือกราดิเอนต์ระเบิด — ค่ากราดิเอนต์เติบโตใหญ่ระหว่างการถ่ายทอดย้อนกลับจนการอัปเดตพารามิเตอร์ไม่เสถียรและโมเดลพัง ปัญหานี้กระทบเครือข่ายแบบวนซ้ำมากที่สุด แต่ก็เกิดในทรานส์ฟอร์เมอร์และเครือข่ายฟีดฟอร์เวิร์ดลึกๆ ได้เช่นกัน
Gradient clipping แก้ปัญหาโดยจำกัดขนาดของกราดิเอนต์ก่อนจะส่งถึงออปติไมเซอร์ เป็นการเพิ่มโค้ดเพียงบรรทัดเดียวในลูปการฝึกที่ทำให้การอัปเดตถูกจำกัดขอบเขต โดยไม่ต้องเปลี่ยนแปลงตัวโมเดลเลย
ในบทความนี้ จะอธิบายสัญชาตญาณเบื้องหลัง gradient clipping วิธีหลักสองวิธี วิธีเลือกค่า threshold และวิธีทำใน PyTorch และ TensorFlow
แต่จริงๆ แล้ว loss ในดาต้าไซเอนซ์คืออะไร? อ่าน บล็อก Loss Function in Machine Learning ของเราเพื่อหาคำตอบ
Gradient Clipping คืออะไร?
Gradient clipping คือเทคนิคที่จำกัดขนาดของกราดิเอนต์ระหว่างการฝึก เพื่อป้องกันการอัปเดตพารามิเตอร์ที่ไม่เสถียร
เมื่อกราดิเอนต์ใหญ่เกินไป ออปติไมเซอร์จะก้าวครั้งใหญ่ในปริภูมิพารามิเตอร์และผลักน้ำหนักไปยังบริเวณที่ loss ระเบิด การตัดทอนช่วยจำกัดขนาดก้าวนั้นก่อนที่ความเสียหายจะเกิดขึ้น
ควรสังเกตว่า gradient clipping ไม่กระทบสถาปัตยกรรมของโมเดล ไม่ได้เพิ่มเลเยอร์หรือเปลี่ยนฟังก์ชันกระตุ้น แต่ปรับเฉพาะกระบวนการฝึกโดยสกัดกราดิเอนต์ระหว่างการถ่ายทอดย้อนกลับกับขั้นตอนของออปติไมเซอร์
จึงลองใช้ได้ง่ายและถอดออกก็ง่าย อย่างที่เห็นต่อไป โค้ดแค่บรรทัดเดียวเท่านั้น
Gradient Clipping ทำงานอย่างไร
กลไกนั้นเรียบง่าย การตัดทอนอยู่ระหว่างการย้อนผ่าน (backward pass) และขั้นตอนของออปติไมเซอร์ และทำตามสี่ขั้นตอนเดิมในทุกๆ รอบ
- คำนวณกราดิเอนต์: รัน forward pass คำนวณ loss แล้วรัน backpropagation ไม่มีอะไรเปลี่ยน แรงไหลของกราดิเอนต์ในเครือข่ายเหมือนเดิม
- ตรวจขนาดกราดิเอนต์: วัดว่ากราดิเอนต์ใหญ่แค่ไหน แล้วแต่เมธอด อาจดูค่ารายตัวหรือคำนวณนอร์มรวมข้ามทุกพารามิเตอร์
- ลดกราดิเอนต์หากเกินค่า threshold: ถ้าขนาดเกินลิมิตที่ตั้งไว้ ให้สเกลกราดิเอนต์ลง ถ้าไม่เกินก็ปล่อยไว้ตามเดิม
- อัปเดตพารามิเตอร์ของโมเดล: ส่งกราดิเอนต์ที่ถูกตัดทอนไปยังออปติไมเซอร์และปรับน้ำหนัก
ส่วนใหญ่กราดิเอนต์ของคุณจะอยู่ต่ำกว่า threshold และการฝึกจะดำเนินไปเหมือนกับไม่มี gradient clipping เมื่อมีสไปค์เกิดขึ้น การตัดทอนจะจับไว้ก่อนที่ออปติไมเซอร์จะตอบสนอง
ก็แค่นั้น
วิธีการ Gradient Clipping ที่พบบ่อย
มีสองวิธีหลักในการตัดทอนกราดิเอนต์ และความต่างอยู่ที่วัดอะไรและสเกลอะไร
ตัดทอนตามค่า (Clip by value)
การตัดทอนตามค่าจะจำกัดค่าขององค์ประกอบกราดิเอนต์แต่ละตัวแยกกัน
เลือกช่วง เช่น [-1.0, 1.0] แล้วค่ากราดิเอนต์ใดๆ นอกช่วงจะถูกปัดให้เท่าขอบที่ใกล้ที่สุด กราดิเอนต์ 2.5 จะกลายเป็น 1.0 กราดิเอนต์ -2.5 จะกลายเป็น -1.0 ส่วนค่าที่อยู่ในช่วงอยู่แล้วจะไม่ถูกเปลี่ยน

ตัวอย่าง Clip by value
จุดเด่นคือความเรียบง่าย ไม่ต้องคำนวณเกินกว่าการหา min/max และรันได้เร็ว
แต่มีข้อเสีย การตัดทอนค่ารายตัวทำให้ทิศทางของเวกเตอร์กราดิเอนต์เปลี่ยนได้ ถ้าบางคอมโพเนนต์ถูกตัดทอนแต่ตัวอื่นไม่ เวกเตอร์ที่อัปเดตจะไม่ชี้ไปทิศที่ backpropagation ระบุไว้ ออปติไมเซอร์จึงก้าวไปในทิศทางที่คลาดเคลื่อนเล็กน้อย
นี่จึงเป็นเหตุผลที่การตัดทอนตามค่าพบได้น้อยกว่าในทางปฏิบัติ
ตัดทอนตามนอร์ม (Clip by norm)
การตัดทอนตามนอร์มจะสเกลเวกเตอร์กราดิเอนต์ทั้งหมดเมื่อขนาดรวมเกินค่า threshold
แทนที่จะดูค่ารายตัว จะคำนวณนอร์มของกราดิเอนต์ทั้งหมดรวมกัน (โดยมากคือนอร์ม L2) แล้วเปรียบเทียบกับค่าสูงสุด หากนอร์มต่ำกว่า threshold ก็ไม่ทำอะไร หากเกิน ทุกกราดิเอนต์จะถูกคูณด้วยตัวคูณเดียวกันเพื่อลดนอร์มลงมาที่ลิมิต

ตัวอย่าง Clip by norm
ข้อดีคือการคงทิศทางไว้ เพราะทุกคอมโพเนนต์หดลงด้วยตัวคูณเดียวกัน เวกเตอร์กราดิเอนต์ยังชี้ทิศเดิม เพียงแค่ทำให้ก้าวสั้นลง ไม่ได้เปลี่ยนทิศ
นี่จึงเป็นมาตรฐาน โดย clip_grad_norm_ ของ PyTorch และ clipnorm ของ TensorFlow ต่างก็ใช้วิธีนี้ และสายการฝึกสมัยใหม่ส่วนใหญ่เปิดใช้งานโดยปริยาย
กราดิเอนต์ระเบิด vs กราดิเอนต์หาย
ทั้งกราดิเอนต์ระเบิดและกราดิเอนต์หายเป็นปัญหาพบได้บ่อยในดีปเลิร์นนิง แต่ gradient clipping แก้ได้เพียงอย่างเดียวเท่านั้น
กราดิเอนต์ระเบิด
เกิดเมื่อค่ากราดิเอนต์โตมากเกินไประหว่างการถ่ายทอดย้อนกลับ
มักเกิดในเครือข่ายลึกหรือสถาปัตยกรรมแบบวนซ้ำ ที่กราดิเอนต์ถูกคูณผ่านหลายเลเยอร์หรือหลายลำดับเวลา หากการคูณสะสมไปในทิศที่ไม่ดี ขนาดกราดิเอนต์จะพองโต ออปติไมเซอร์จึงอัปเดตพารามิเตอร์ครั้งใหญ่ น้ำหนักกระโดดไปค่าสุดโต่ง และ loss มักกลายเป็น NaN หรือ Inf
สังเกตได้จาก loss ที่กระโดดขึ้นทันทีหรือโมเดลที่จู่ๆ ก็ diverge
กราดิเอนต์หาย
เป็นปัญหาตรงข้าม ค่ากราดิเอนต์หดเข้าใกล้ศูนย์เมื่อถ่ายทอดย้อนกลับผ่านเครือข่าย
เมื่อกราดิเอนต์เล็กเกินไป การอัปเดตน้ำหนักก็เล็กจนแทบเป็นศูนย์ เลเยอร์ต้นๆ หยุดเรียนรู้ เลเยอร์ลึกเรียนรู้ช้า และการฝึกแทบจะหยุด เส้นโค้ง loss แบนและไม่ดีขึ้นแม้ฝึกอีกหลาย epoch
นี่คือเหตุผลหลักที่ RNN มีปัญหากับลำดับยาวก่อนที่ LSTM และ GRU จะถูกพัฒนา
บทบาทของ gradient clipping
Gradient clipping แก้ปัญหากราดิเอนต์ระเบิด ไม่ใช่กราดิเอนต์หาย
การตัดทอนลดกราดิเอนต์ที่ใหญ่เกินไป แต่ไม่ทำอะไรเมื่อกราดิเอนต์เล็กเกินไป สำหรับกราดิเอนต์หาย ควรใช้การกำหนดค่าเริ่มต้นของน้ำหนักที่ดีกว่า การเชื่อมต่อแบบ residual การทำ batch normalization หรือสถาปัตยกรรมที่ออกแบบมาเพื่อคงการไหลของกราดิเอนต์
อธิบายการตัดทอนกราดิเอนต์ตามนอร์ม
การตัดทอนตามนอร์มคือวิธีที่ผู้อ่านส่วนใหญ่ต้องการเมื่อค้นหาเรื่อง gradient clipping
กระบวนการมีสามขั้น หนึ่ง คำนวณนอร์มของกราดิเอนต์ทั้งหมดรวมกัน สอง เปรียบเทียบนอร์มนั้นกับ threshold ที่เลือก สาม ปรับสเกลกราดิเอนต์หากนอร์มใหญ่เกินไป
โดยมากใช้นอร์ม L2 ซึ่งหมายถึงยกกำลังสองค่ากราดิเอนต์ทุกตัว บวกเข้าด้วยกัน แล้วถอดรากที่สอง หากมีกราดิเอนต์ g_1, g_2, ..., g_n ข้ามพารามิเตอร์ทั้งหมดของโมเดล นอร์ม L2 คือ:

สูตร Clip by norm
เมื่อได้นอร์มแล้ว ให้นำไปเทียบกับ threshold c หาก ||g|| <= c กราดิเอนต์จะผ่านไปโดยไม่เปลี่ยน หาก ||g|| > c กราดิเอนต์ทุกตัวจะถูกคูณด้วยตัวคูณ c / ||g|| ซึ่งทำให้นอร์มใหม่เท่ากับ c พอดี
สิ่งนี้สำคัญเพราะทุกองค์ประกอบหดลงด้วยตัวคูณเดียวกัน สัดส่วนระหว่างค่ากราดิเอนต์ยังคงเดิม หมายความว่าเวกเตอร์ยังชี้ทิศเดิม คุณเพียงย่อก้าวของออปติไมเซอร์ ไม่ได้เปลี่ยนทิศทาง
คุณสมบัติการคงทิศนี้ทำให้การตัดทอนตามนอร์มเป็นตัวเลือกเริ่มต้น การตัดทอนตามค่าทำให้เวกเตอร์กราดิเอนต์บิดเบี้ยวได้ ส่วนการตัดทอนตามนอร์มเปลี่ยนเพียงความยาว
clip_grad_norm_ ของ PyTorch และ clipnorm ของ TensorFlow ต่างก็ทำสิ่งนี้ เมื่อมีคนบอกว่า “ใช้ gradient clipping” โดยมากหมายถึงการตัดทอนตามนอร์ม
การเลือกค่า Threshold สำหรับ Gradient Clipping
Threshold เป็นไฮเปอร์พารามิเตอร์ จึงไม่มีค่ากลางที่ใช้ได้กับทุกโมเดล
ถ้าตั้งไว้สูงเกินไป แทบจะไม่ถูกใช้งาน กราดิเอนต์มักอยู่ใต้ลิมิต จึงไม่ช่วยอะไร และยังเห็น loss พุ่งเมื่อกราดิเอนต์ระเบิดอยู่ดี
ถ้าตั้งไว้ต่ำเกินไป จะตัดทอนรุนแรงเกินจำเป็น ทุกแบตช์ถูกหดกราดิเอนต์ลง ทำให้การอัปเดตน้ำหนักเล็กกว่าที่ควร การเรียนรู้ช้าลงและโมเดลใช้เวลานานขึ้นกว่าจะคอนเวิร์จ บางครั้งนานมาก
ค่าตั้งต้นที่ใช้บ่อยคือ 1.0 ซึ่งใช้ได้ดีกับหลายสถาปัตยกรรม ค่าระหว่าง 0.5 ถึง 5.0 ครอบคลุมกรณีใช้งานส่วนใหญ่
แนวทางที่ดีกว่าคือเฝ้าดูนอร์มของกราดิเอนต์ระหว่างฝึก ล็อกค่านอร์มก่อนการตัดทอนในแต่ละสเต็ปแล้วดูการกระจาย ถ้านอร์มส่วนใหญ่อยู่แถว 0.3 แต่มีสไปค์ไปถึง 50 ให้ตั้ง threshold เหนือช่วงปกติแต่ต่ำกว่าสไปค์มากๆ — 2.0 หรือ 3.0 เป็นตัวเลือกที่สมเหตุสมผลในกรณีนี้
ปฏิบัติกับมันเหมือนไฮเปอร์พารามิเตอร์อื่นๆ เริ่มที่ 1.0 สังเกตพฤติกรรม แล้วปรับตามอาการระหว่างฝึก
Gradient Clipping ในเครือข่ายประสาทแบบวนซ้ำ
RNN คือจุดที่ gradient clipping กลายเป็นเทคนิคมาตรฐานครั้งแรก
เหตุผลคือวิธีที่ RNN ถ่ายทอดกราดิเอนต์ตามเวลา การถ่ายทอดย้อนกลับตามเวลา (BPTT) คูณเมทริกซ์น้ำหนักเดิมซ้ำๆ หลายสเต็ปเวลา การคูณซ้ำเหล่านี้อาจสะสมจนเป็นค่ามหาศาล ลำดับยาวยิ่งทำให้แย่ลง
LSTM และ GRU ลดปัญหาด้วยกลไก gating แต่ไม่ได้กำจัดหมด ทั้งสองสถาปัตยกรรมยังได้ประโยชน์จากการตัดทอน โดยเฉพาะเมื่อฝึกลำดับยาวหรือใช้ค่าเรียนรู้สูง
สำหรับการฝึก RNN ให้ใช้การตัดทอนตามนอร์มด้วย threshold ระหว่าง 1.0 ถึง 5.0 เป็นค่าเริ่มต้นทั่วไป หากใช้ nn.LSTM หรือ nn.GRU ของ PyTorch แล้ว loss ระเบิดระหว่างฝึก การเพิ่ม clip_grad_norm_ มักเป็นสิ่งแรกที่ควรลอง
Gradient Clipping ในดีปเลิร์นนิงยุคใหม่
เมื่อทรานส์ฟอร์เมอร์มาแทน RNN gradient clipping ก็ยังไม่หายไป
โมเดลภาษาขนาดใหญ่ เช่น GPT และ BERT ใช้การตัดทอนระหว่าง pretraining และ fine-tuning เช่นเดียวกับวิชันทรานส์ฟอร์เมอร์ โมเดล diffusion และสถาปัตยกรรมลึกๆ ส่วนใหญ่ที่มีเป็นร้อยเลเยอร์ ออปติไมเซอร์ Adam และ AdamW ที่ครองการฝึกยุคใหม่ก็มักจับคู่กับการตัดทอนตามนอร์มที่ threshold แถวๆ 1.0 ด้วย
เหตุผลก็เหมือนกับ RNN เครือข่ายลึกคูณกราดิเอนต์ผ่านหลายเลเยอร์ ขนาดแบตช์ใหญ่รวมกับค่าเรียนรู้สูงอาจทำให้เกิดสไปค์เป็นครั้งคราว การตัดทอนจัดการสไปค์เหล่านั้นโดยไม่กระทบสเต็ปปกติของการฝึก
อิมพลีเมนเตชันอ้างอิงส่วนใหญ่เปิดใช้การตัดทอนเป็นค่าเริ่มต้น Trainer ของ Hugging Face, PyTorch Lightning และ DeepSpeed ต่างก็มีออปชันการตัดทอนเป็นคอนฟิกมาตรฐาน หากฝึกโมเดลที่ใหญ่กว่าโมเดลตัวอย่างเล็กๆ การตัดทอนแทบจะเป็นส่วนหนึ่งของสายการทำงานอยู่แล้ว
เป็นการเพิ่มบรรทัดเดียวที่แทบไม่เสียค่าใช้จ่าย แต่ป้องกันงานฝึกจากการล่มหลังใช้คอมพิวต์ไปหลายชั่วโมง นี่คือเหตุผลที่มันยังคงอยู่
Gradient Clipping ใน PyTorch
PyTorch จัดการ gradient clipping ด้วยฟังก์ชันเดียว: torch.nn.utils.clip_grad_norm_.
คำสั่งตัดทอนต้องอยู่ระหว่าง loss.backward() และ optimizer.step() ต้องให้ backpropagation เติมกราดิเอนต์ก่อน จากนั้นตัดทอน (หากจำเป็น) แล้วค่อยให้ออปติไมเซอร์อัปเดต ใส่ที่อื่นจะไม่ทำงาน
นี่คือตัวอย่างสคริปต์ฝึกที่รันได้จริง ฝึก MLP ขนาดเล็กกับข้อมูลรีเกรสชันสังเคราะห์ พร้อมเปิดใช้ gradient clipping:
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
torch.manual_seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
inputs = torch.randn(n_samples, n_features)
targets = (inputs.sum(dim=1, keepdim=True) * 2.0 + torch.randn(n_samples, 1) * 0.1)
dataset = TensorDataset(inputs, targets)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
# Small feedforward network
model = nn.Sequential(
nn.Linear(n_features, 64),
nn.ReLU(),
nn.Linear(64, 64),
nn.ReLU(),
nn.Linear(64, 1),
)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()
# Training loop with gradient clipping
n_epochs = 5
max_grad_norm = 1.0
for epoch in range(n_epochs):
epoch_loss = 0.0
for batch_inputs, batch_targets in dataloader:
optimizer.zero_grad()
predictions = model(batch_inputs)
loss = loss_fn(predictions, batch_targets)
loss.backward()
# Gradient clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_grad_norm)
optimizer.step()
epoch_loss += loss.item()
print(f("Epoch {epoch + 1}: loss = {epoch_loss / len(dataloader):.4f}"))

ผลลัพธ์ PyTorch
ฟังก์ชัน clip_grad_norm_ รับอาร์กิวเมนต์หลักสองตัว:
-
parameters: พารามิเตอร์ของโมเดลที่ต้องการตัดทอนกราดิเอนต์ ส่งmodel.parameters()เพื่อครอบคลุมทั้งโมเดล -
max_norm: ค่า threshold สำหรับนอร์มของกราดิเอนต์ ค่า1.0เป็นจุดเริ่มต้นที่พบได้บ่อย
มีอาร์กิวเมนต์เสริม norm_type ที่ค่าเริ่มต้นคือ 2.0 สำหรับนอร์ม L2 ซึ่งแทบไม่ต้องเปลี่ยน
ขีดล่างท้ายชื่อ clip_grad_norm_ บ่งชี้ว่าเป็นการทำงานแบบแก้ไขค่าทันที (in-place) ฟังก์ชันจะปรับกราดิเอนต์โดยตรงในแอตทริบิวต์ .grad ของแต่ละพารามิเตอร์ จึงไม่ต้องใช้ค่าที่รีเทิร์น อย่างไรก็ตาม ฟังก์ชันจะรีเทิร์นนอร์มรวมก่อนการตัดทอน ซึ่งมีประโยชน์ถ้าต้องการล็อกค่า
สำหรับการตัดทอนตามค่าแทนการตามนอร์ม PyTorch มี torch.nn.utils.clip_grad_value_:
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
แต่อย่างที่กล่าวไป ก่อนหน้านี้ แทบจะไม่ค่อยใช้วิธีนี้
ทั้งหมดมีเท่านี้ เพิ่มสองบรรทัดในลูปการฝึก
Gradient Clipping ใน TensorFlow
TensorFlow จัดการการตัดทอนในระดับออปติไมเซอร์ แทนที่จะเป็นการเรียกฟังก์ชันแยก
เมื่อสร้างออปติไมเซอร์ ให้ส่ง clipnorm หรือ clipvalue เป็นอาร์กิวเมนต์ ออปติไมเซอร์จะตัดทอนภายในทุกสเต็ป จึงไม่ต้องแก้ลูปการฝึกเลย
นี่คือตัวอย่างเต็มด้วย Keras API บนข้อมูลรีเกรสชันสังเคราะห์:
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
np.random.seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
x_train = np.random.randn(n_samples, n_features).astype(np.float32)
y_train = (x_train.sum(axis=1, keepdims=True) * 2.0
+ np.random.randn(n_samples, 1).astype(np.float32) * 0.1)
# Small feedforward network
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation="relu", input_shape=(n_features,)),
tf.keras.layers.Dense(64, activation="relu"),
tf.keras.layers.Dense(1),
])
# Optimizer with gradient clipping by norm
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipnorm=1.0)
model.compile(optimizer=optimizer, loss="mse")
model.fit(x_train, y_train, epochs=5, batch_size=32)

ผลลัพธ์ TensorFlow
อาร์กิวเมนต์สองตัวนี้ทำงานต่างกัน:
-
clipnormตัดทอนตามนอร์ม L2 ของแต่ละเทนเซอร์กราดิเอนต์ หากนอร์มเกิน threshold เทนเซอร์จะถูกสเกลลงตามสัดส่วน -
clipvalueตัดทอนค่าขององค์ประกอบกราดิเอนต์แต่ละตัวแยกกัน ค่าที่เกิน threshold จะถูกหนีบไว้ที่ threshold และค่าน้อยกว่าลบ threshold จะถูกหนีบไว้ที่ลบ threshold
หากต้องการสลับจากการตัดทอนตามนอร์มเป็นตามค่า เพียงเปลี่ยนอาร์กิวเมนต์:
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipvalue=0.5)
อาร์กิวเมนต์ทั้งสองใช้ได้กับออปติไมเซอร์ Keras ทุกตัว: Adam, SGD, RMSprop, AdamW และอื่นๆ นอกจากนี้ยังมี global_clipnorm ที่ตัดทอนตามนอร์มที่คำนวณรวมข้ามกราดิเอนต์ทั้งหมด แทนที่จะเป็นรายเทนเซอร์ ซึ่งใกล้เคียงกับพฤติกรรมเริ่มต้นของ PyTorch มากกว่า
หากเขียนลูปการฝึกแบบกำหนดเองด้วย tf.GradientTape ออปติไมเซอร์ก็ยังจัดการตัดทอนเมื่อเรียก apply_gradients:
for epoch in range(5):
for batch_x, batch_y in zip(np.array_split(x_train, 32), np.array_split(y_train, 32)):
with tf.GradientTape() as tape:
predictions = model(batch_x, training=True)
loss = tf.reduce_mean(tf.square(predictions - batch_y))
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
นี่คือความแตกต่างระหว่างสองเฟรมเวิร์ก PyTorch ให้คุณควบคุมการตัดทอนภายในลูป ส่วน TensorFlow ผลักเข้าไปในออปติไมเซอร์เอง แต่ตรรกะพื้นฐานเหมือนกัน
Gradient Clipping เทียบกับเทคนิคทำให้การฝึกเสถียรอื่นๆ
Gradient clipping ไม่ใช่วิธีเดียวในการทำให้การฝึกเสถียร และไม่ใช่เครื่องมือที่ถูกต้องเสมอไปสำหรับทุกกรณี
เทคนิคอื่นๆ จัดการปัญหาที่เกี่ยวข้องแต่ต่างกัน บางอันป้องกันไม่ให้กราดิเอนต์โตเกินไปตั้งแต่แรก บางอันกันไม่ให้กราดิเอนต์หาย และบางอันทำให้พื้นผิวของ loss ง่ายต่อการเพิ่มประสิทธิภาพ มาดูสองสามเทคนิคที่ต่างกัน
Batch normalization
Batch normalization ทำให้แอคทิเวชันในแต่ละมินิแบตช์มีการกระจายที่เสถียรระหว่างการฝึก
ช่วยให้เอาต์พุตของเลเยอร์อยู่ในช่วงที่คาดเดาได้ ทำให้ขนาดกราดิเอนต์เสถียรมากขึ้น เครือข่ายที่ฝึกด้วย batch norm รับมือกับค่าเรียนรู้ที่สูงกว่าและคอนเวิร์จเร็วขึ้น อีกทั้งไวต่อการกำหนดค่าน้ำหนักตั้งต้นน้อยลง
แต่ batch norm ไม่ได้หยุดกราดิเอนต์ระเบิดโดยตรง มันลดความถี่ที่เกิด ไม่ได้แก้ตอนเกิดขึ้นแล้ว ด้วยเหตุนี้หลายโมเดลจึงยังใช้ batch norm คู่กับ gradient clipping
การเชื่อมต่อแบบ Residual
Residual connections เพิ่มทางลัดที่ข้ามหนึ่งหรือหลายเลเยอร์ ให้กราดิเอนต์ไหลจากเลเยอร์หลังกลับไปเลเยอร์หน้าได้โดยตรง
แก้ปัญหากราดิเอนต์หายในเครือข่ายลึก หากไม่มี residual การฝึกเครือข่ายที่มากกว่า 20–30 เลเยอร์จะยาก เพราะกราดิเอนต์หดเข้าใกล้ศูนย์เมื่อย้อนกลับ เมื่อมี residual เครือข่ายที่มีหลายร้อยเลเยอร์ก็ฝึกได้โดยไม่มีปัญหา
Residual แก้ปลายอีกด้านของปัญหากราดิเอนต์ตรงข้ามกับการตัดทอน การตัดทอนจัดการกราดิเอนต์ที่ใหญ่เกินไป ส่วน residual จัดการกราดิเอนต์ที่เล็กเกินไป
การกำหนดค่าน้ำหนักตั้งต้นอย่างระมัดระวัง
ค่าน้ำหนักเริ่มต้นกำหนดขนาดเริ่มต้นของแอคทิเวชันและกราดิเอนต์ การกำหนดค่าเริ่มต้นที่ไม่ดีอาจทำให้กราดิเอนต์ระเบิดหรือหายตั้งแต่ก้าวแรก
วิธีอย่าง Xavier และ He initialization จะสเกลน้ำหนักตั้งต้นตามขนาดเลเยอร์ ทำให้ความแปรปรวนของแอคทิเวชันเสถียรข้ามเลเยอร์ตั้งแต่เริ่มฝึก ซึ่งป้องกันปัญหากราดิเอนต์จำนวนมากก่อนเกิด
การกำหนดค่าเริ่มต้นที่ดีช่วยลดโอกาสต้องใช้การตัดทอน แต่ไม่ตัดทิ้งทั้งหมด สไปค์ของกราดิเอนต์ยังเกิดในภายหลังได้ โดยเฉพาะเมื่อใช้ค่าเรียนรู้สูงหรือชุดข้อมูลแปลกๆ
การทำงานร่วมกัน
เทคนิคเหล่านี้ไม่ใช่ตัวเลือกทดแทนกัน แต่เป็นเครื่องมือเสริมที่แก้ส่วนต่างๆ ของปัญหาเดียวกัน
การตั้งค่าการฝึกสมัยใหม่มักใช้การกำหนดค่าเริ่มต้นที่รอบคอบตั้งแต่ต้น โครงสร้างที่มี residual การทำ batch normalization (หรือ layer normalization) ภายในเครือข่าย และ gradient clipping เป็นตาข่ายนิรภัยระหว่างการปรับให้เหมาะสม แต่ละอย่างจัดการโหมดความล้มเหลวเฉพาะ และเมื่อรวมกันจะทำให้เครือข่ายลึกฝึกได้จริง
สรุป
Gradient clipping เป็นหนึ่งในวิธีแก้ที่ง่ายที่สุดในดีปเลิร์นนิง และแก้ปัญหาที่อาจทำให้งานฝึกหลายชั่วโมงพังได้ในก้าวเดียว
ข่าวดีก็คือไม่ต้องเปลี่ยนสถาปัตยกรรมโมเดลหรือเขียนโค้ดการฝึกใหม่ แค่บรรทัดเดียวใน PyTorch หรืออาร์กิวเมนต์เดียวใน TensorFlow ก็เพียงพอในการใช้ gradient clipping
มันทำงานได้ดีที่สุดเมื่อเป็นส่วนหนึ่งของชุดเครื่องมือที่ใหญ่กว่า จับคู่กับการกำหนดค่าน้ำหนักตั้งต้นอย่างระมัดระวัง การเชื่อมต่อแบบ residual และ batch หรือลเยอร์ normalization แล้วคุณจะได้สายการฝึกที่รับมือความไม่เสถียรจากหลายด้าน
ถ้า loss ของคุณระเบิด ให้เริ่มจากการตัดทอน ถ้าหาย ให้มองหาวิธีอื่น และถ้ากำลังฝึกโมเดลที่ใหญ่กว่าขนาดเล็ก ให้ใส่การตัดทอนในสายการทำงานเป็นค่าเริ่มต้นแล้วลืมมันไปได้เลย
Gradient clipping เป็นเพียงหนึ่งในหลายคำที่วิศวกรแมชชีนเลิร์นนิงทุกคนควรรู้ หากอยากเรียนรู้อย่างอื่นและพร้อมทำงานในปี 2026 สมัครเข้าเรียน เส้นทาง Machine Learning Engineer ของเราวันนี้
คำถามที่พบบ่อยเกี่ยวกับ Gradient Clipping
Gradient clipping ในดีปเลิร์นนิงคืออะไร?
Gradient clipping คือเทคนิคที่จำกัดขนาดของกราดิเอนต์ระหว่างการฝึกเครือข่ายประสาท เพื่อป้องกันการอัปเดตพารามิเตอร์ที่ไม่เสถียร เมื่อกราดิเอนต์โตเกินไประหว่างการถ่ายทอดย้อนกลับ ออปติไมเซอร์จะก้าวครั้งใหญ่ผลักน้ำหนักไปยังบริเวณที่ไม่ดีและทำให้ loss ระเบิด การตัดทอนจะจำกัดขนาดกราดิเอนต์ก่อนขั้นตอนของออปติไมเซอร์ ทำให้การอัปเดตถูกจำกัดแม้กราดิเอนต์ดิบจะเกิดสไปค์
ควรใช้ gradient clipping เมื่อใด?
ใช้ gradient clipping ทุกครั้งที่การฝึกไม่เสถียร โดยเฉพาะเมื่อเห็น loss พุ่งเป็นช่วงๆ ค่า NaN หรือ divergence หลังฝึกไปหลายชั่วโมง ถือเป็นมาตรฐานสำหรับเครือข่ายแบบวนซ้ำอย่าง LSTM และ GRU และสถาปัตยกรรมลึกๆ ที่ฝึกด้วยค่าเรียนรู้สูง หากเส้นโค้ง loss ดูปกติดี อาจข้ามได้ แต่เพิ่มไว้เป็นตาข่ายนิรภัยก็ไม่เสียหาย
ความแตกต่างระหว่าง clip by value กับ clip by norm คืออะไร?
ตัดทอนตามค่าจะจำกัดค่าองค์ประกอบกราดิเอนต์รายตัว ซึ่งอาจเปลี่ยนทิศทางของเวกเตอร์กราดิเอนต์ได้ ส่วนตัดทอนตามนอร์มจะสเกลเวกเตอร์ทั้งหมดเมื่อขนาดรวมเกิน threshold โดยคงทิศทางเดิมแต่ทำให้ก้าวสั้นลง การตัดทอนตามนอร์มจึงเป็นมาตรฐานในดีปเลิร์นนิงสมัยใหม่ เพราะไม่บิดเบือนทิศการอัปเดต
จะเลือกค่า threshold สำหรับ gradient clipping อย่างไรดี?
เริ่มที่ 1.0 แล้วปรับตามสิ่งที่เห็นระหว่างการฝึก ล็อกค่านอร์มของกราดิเอนต์ก่อนการตัดทอนข้ามแบตช์และดูการกระจาย ตั้ง threshold ไว้เหนือช่วงนอร์มทั่วไป แต่ต่ำกว่าสไปค์ที่ต้องการจับ หากตั้งสูงไป การตัดทอนจะไม่ทำงานเลย หากตั้งต่ำไป คุณจะทำให้การเรียนรู้ช้าลงโดยไม่จำเป็น
Gradient clipping แก้ปัญหากราดิเอนต์หายด้วยหรือไม่?
ไม่ Gradient clipping แก้ได้เฉพาะกราดิเอนต์ระเบิด ซึ่งค่ากราดิเอนต์โตเกินไป กราดิเอนต์หายเป็นปัญหาตรงข้ามที่ค่าหดเข้าใกล้ศูนย์จนการเรียนรู้แทบหยุด สำหรับกราดิเอนต์หาย ควรใช้การกำหนดค่าน้ำหนักตั้งต้นที่ดีกว่า การเชื่อมต่อแบบ residual การทำ batch normalization หรือสถาปัตยกรรมอย่าง LSTM และ GRU