Python - Bài 29

 

🔬 Bài 29: Gradient Descent — Thuật toán giúp máy tự vặn núm xuống đáy

Chặng 7 · Toán & Tư duy ML (Bài 26 → 30) · đây là bài 4/5 của chặng.

🎯 Mục tiêu: hiểu trọn vẹn công thức xuống dốc $w_{mới} = w_{cũ} - \alpha \cdot \text{đạo hàm}$, làm chủ hằng số Tốc độ học (Learning Rate), và tự viết vòng lặp huấn luyện (Training Loop) hoàn chỉnh.

👥 Đối tượng: học sinh cấp 2–3, sinh viên, người mới học Python. Cần biết trước: Đạo hàm (Bài 27) và Hàm mất mát MSE (Bài 28).

📖 Cách đọc 3 lớp: mỗi phần có ba khối màu song song — 🟢 Lớp A trực giác, 🔵 Lớp B cơ chế, 🟣 Lớp C nghiên cứu.

Sau bài này bạn sẽ:

  • Giải thích được vì sao công thức Gradient Descent bắt buộc phải dùng dấu TRỪ.
  • Hiểu rõ vai trò của Learning Rate ($\alpha$) và tác hại khi chọn quá to hoặc quá nhỏ.
  • Tự tay viết một Vòng lặp huấn luyện (Training Loop) trong Python từ con số 0.
  • Quan sát được cảnh mô hình "thông minh lên" qua từng bước lặp.

Ở Bài 27, ta học cách đo độ dốc (đạo hàm) tại một điểm bất kỳ. Ở Bài 28, ta dựng ra chiếc bát sai số (Hàm mất mát MSE). Bài 29 là khoảnh khắc kỳ diệu nhất: ta ghép hai mảnh ghép lại để tạo nên Gradient Descent — thuật toán cốt lõi giúp hàng triệu mô hình AI hiện nay tự động học tập mà không cần con người can thiệp thủ công.

Vì sao lại dùng DẤU TRỪ trong Gradient Descent? Độ dốc > 0 (Đang đi lên) Trừ đi → Lùi sang trái Độ dốc < 0 (Đang đi xuống) Trừ số âm → Tiến sang phải ĐÁY BÁT (Loss = 0) w_mới = w_cũ - lr * đạo_hàm

Hình 1 — Dấu TRỪ có tác dụng tự điều hướng: ở bên phải (độ dốc dương) nó ép $w$ lùi lại; ở bên trái (độ dốc âm) nó ép $w$ tiến tới.

1. Công thức xuống dốc — Vì sao lại là dấu TRỪ?

🟢 Lớp A · Trực giác

Hãy tưởng tượng bạn bị bịt mắt đứng trên sườn núi lửa hình chiếc bát và muốn đi xuống đáy:

  • Nếu chân bạn cảm nhận mặt đất dốc lên về phía trước (độ dốc dương $\mathbf{> 0}$): Muốn xuống thấp hơn, bạn phải LÙI LẠI.
  • Nếu chân bạn cảm nhận mặt đất dốc xuống về phía trước (độ dốc âm $\mathbf{< 0}$): Muốn xuống thấp hơn, bạn phải TIẾN TỚI.

Phép toán tuyệt vời ở chỗ: Dấu TRỪ tự động làm cả hai việc này mà không cần dùng câu lệnh if/else!

Trừ đi một số dương $\rightarrow$ giảm $w$ (lùi lại). Trừ đi một số âm $\rightarrow$ cộng thêm vào $w$ (tiến tới). Dấu trừ đảm bảo ta luôn luôn đi ngược chiều độ dốc để lao xuống đáy.

🔵 Lớp B · Cơ chế

Công thức Gradient Descent cập nhật tham số $w$ chỉ vỏn vẹn trong 1 dòng code:

# lr (Learning Rate): Tốc độ học (ví dụ: 0.1)
w = w - lr * dao_ham(f_loss, w)

Ví dụ cụ thể bằng số:

  • Giả sử $w = 4$, độ dốc tại đó là $+6.0$. Áp dụng công thức: $w_{mới} = 4 - 0.1 \times 6.0 = 3.4$ (giảm từ 4 xuống 3.4 → dịch gần về đáy).
  • Giả sử $w = -1$, độ dốc tại đó là $-4.0$. Áp dụng công thức: $w_{mới} = -1 - 0.1 \times (-4.0) = -0.6$ (tăng từ -1 lên -0.6 → dịch gần về đáy).
🟣 Lớp C · Nghiên cứu

Đạo hàm không chỉ chỉ ra hướng đi mà còn cho biết độ dốc lớn hay nhỏ:

  • Khi đứng ở trên cao (xa đáy), độ dốc rất lớn → bước đi dài → xuống nhanh.
  • Khi càng tới gần đáy, độ dốc nhỏ dần về 0 → bước đi tự động ngắn lại → tiếp đất nhẹ nhàng mà không bị văng qua đáy.

Tính chất tự thu nhỏ bước đi này giúp Gradient Descent hội tụ (converge) mượt mà vào điểm tối ưu mà không bị rồ ga quá đà.

2. Tốc độ học (Learning Rate) — Bước quá rộng hay quá hẹp?

🟢 Lớp A · Trực giác

Hằng số $\alpha$ (đọc là alpha, trong code viết là lr hoặc learning_rate) chính là kích thước sải bước của bạn khi xuống núi:

  • Quá nhỏ (Ví dụ $\alpha = 0.0001$): Nhích từng mm một. Bạn đi an toàn nhưng mất hàng triệu năm mới tới đáy bát (học quá chậm).
  • Quá lớn (Ví dụ $\alpha = 1.5$): Bước một bước quá dài, bạn bay vọt từ sườn bên này sang sườn bên kia,甚至 văng ra khỏi lòng bát và rơi xuống vực (sai số nổ tung - exploding loss).
  • Vừa đẹp (Ví dụ $\alpha = 0.1$): Lăn nhẹ nhàng xuống đáy sau vài chục bước.
Quá nhỏ (Slow) Rùa bò, tốn vô số bước Vừa đẹp (Optimal) Hội tụ mượt mà về đáy Quá lớn (Divergent) Nhảy vọt ra ngoài, nổ Loss

Hình 2 — Lựa chọn Learning Rate hợp lý quyết định sự sống còn của quá trình huấn luyện mô hình.

🔵 Lớp B · Cơ chế

Chạy thử nghiệm điều gì xảy ra nếu chọn lr không phù hợp:

def mo_phong_hoc(lr, so_buoc=5):
    w = 4.0 # Khoi tao ban dau o xa day (day o w = 0)
    for i in range(so_buoc):
        dao_ham = 2 * w # Dao ham cua w^2 la 2w
        w = w - lr * dao_ham
        print(f"Buoc {i+1}: w = {w:.4f}")

print("--- lr = 0.1 (Vừa đẹp) ---")
mo_phong_hoc(lr=0.1)
# w giam dan deu: 3.2000 -> 2.5600 -> 2.0480 -> 1.6384 -> 1.3107

print("\n--- lr = 1.1 (Quá lớn) ---")
mo_phong_hoc(lr=1.1)
# w vang khoi bat: -4.8000 -> 5.7600 -> -6.9120 -> 8.2944 -> -9.9533
🟣 Lớp C · Nghiên cứu

Trong thực tế, chọn lr là một nghệ thuật (được gọi là tinh chỉnh *Hyperparameter*). Người ta thường dùng các kỹ thuật tiên tiến như:

  • Learning Rate Decay: Ban đầu để lr lớn cho máy chạy nhanh, càng về sau càng hạ nhỏ lr để máy chỉnh chi tiết.
  • Adaptive Optimizers (Adam, RMSprop): Tự động điều chỉnh lr riêng cho từng tham số dựa trên lịch sử các bước đi trước đó. Bạn sẽ gặp Adam trong mọi mạng nơ-ron hiện đại.

3. Vòng lặp Huấn luyện (Training Loop) — Máy tự học trong thực tế

🟢 Lớp A · Trực giác

Một Vòng lặp huấn luyện (Training Loop) của AI đơn giản là thực hiện lặp đi lặp lại 4 bước sau cho đến khi máy thông minh hẳn:

  1. Dự đoán (Forward): Dùng $w$ hiện tại tính ra kết quả $\hat{y}$.
  2. Đo sai (Compute Loss): Tính độ ngốc (MSE) so với thực tế $y$.
  3. Đo dốc (Backward/Derivative): Tính xem vặn $w$ hướng nào thì sai số giảm.
  4. Chỉnh núm (Update): Áp dụng Gradient Descent $w = w - \alpha \cdot \text{đạo hàm}$.
🔵 Lớp B · Cơ chế

Dưới đây là trọn vẹn một Vòng lặp huấn luyện hoàn chỉnh dùng Python thuần và NumPy:

import numpy as np

# 1. Du lieu thuc te: y = 3 * x
x_data = np.array([1, 2, 3, 4], dtype=float)
y_data = np.array([3, 6, 9, 12], dtype=float)

# Khởi tạo w ngẫu nhiên bừa bãi
w = 0.0
lr = 0.05

def loss_function(w):
    y_pred = w * x_data
    return np.mean((y_pred - y_data) ** 2)

def dao_ham_loss(w, h=1e-6):
    return (loss_function(w + h) - loss_function(w - h)) / (2 * h)

# VONG LAP HUAN LUYEN (TRAINING LOOP)
for epoch in range(20):
    loss = loss_function(w)
    dw = dao_ham_loss(w)
    
    # Cap nhat trong so
    w = w - lr * dw
    
    if (epoch + 1) % 5 == 0:
        print(f"Epoch {epoch+1:2d}: w = {w:.4f} | Loss = {loss:.6f}")

# Kết quả: w tự bò từ 0.0 lên sát nút 3.0000!
🟣 Lớp C · Nghiên cứu

Mã nguồn ở Lớp B chính là bản thiết kế thu nhỏ của các thư viện hàng đầu thế giới như PyTorch hay TensorFlow!

Khi bạn gõ optimizer.step() hay loss.backward() trong PyTorch, bên dưới nó chạy đúng chính xác logic này: lấy đạo hàm và lấy tham số trừ đi $(\text{learning\_rate} \times \text{đạo hàm})$. Bằng việc tự viết bằng tay hôm nay, bạn đã hiểu tận gốc rễ cơ chế học của Machine Learning.

⚠️ Cạm bẫy Điểm cực tiểu địa phương (Local Minima): Với hàm MSE đơn giản, chiếc bát chỉ có đúng 1 đáy duy nhất (Global Minimum). Nhưng với các Mạng nơ-ron sâu phức tạp, "địa hình" sai số có hàng ngàn chỗ lồi lõm. Máy có thể bị mắc kẹt ở một "đáy nông" (Local Minimum) thay vì đáy sâu nhất. Các thuật toán tối ưu hiện đại như Adam thêm khái niệm Momentum (Động lượng) để giúp viên bi có đà vọt qua các đáy nông này.

📝 Bảng ghi nhớ

Khái niệmÝ nghĩaTrong Python
Gradient DescentThuật toán tự động xuống dốc tìm đáy Lossw = w - lr * dw
Dấu TRỪ ($-$)Đảm bảo luôn đi ngược chiều độ dốc để lùi/tiến về đáyĐặc điểm cốt lõi của công thức
Learning Rate ($\alpha$)Tốc độ học (kích thước sải bước mỗi lần vặn núm)lr = 0.01 (thường từ 0.1 đến 0.0001)
EpochMột lần máy duyệt qua và cập nhật theo dữ liệufor epoch in range(100):
Hội tụ (Convergence)Trạng thái khi Loss giảm sát về 0 và $w$ ngừng biến độngMáy đã học xong

✎ Kiểm tra nhanh

Câu 1. Nếu đạo hàm tại điểm hiện tại là $\frac{dL}{dw} = -5.0$ và $\alpha = 0.1$, trọng số $w$ mới sẽ biến đổi thế nào?

  • Giảm đi 0.5
  • Tăng thêm 0.5
  • Giảm đi 5.0
  • Giữ nguyên không đổi
Chính xác! Công thức: $w_{mới} = w_{cũ} - 0.1 \times (-5.0) = w_{cũ} + 0.5$. Đạo hàm âm chứng tỏ dốc đang hướng xuống về bên trái, do đó máy tự động tiến sang phải (+0.5).

Câu 2. Điều gì sẽ xảy ra nếu bạn đặt Learning Rate ($\alpha$) quá lớn (ví dụ $\alpha = 10.0$)?

  • Mô hình sẽ học cực kỳ nhanh và chạm đáy ngay lập tức.
  • Giá trị Loss sẽ nhảy vọt ra khỏi bát và bùng nổ (Diverge), mô hình không học được.
  • Mô hình sẽ dừng lại không chạy nữa.
  • Đạo hàm sẽ tự động biến thành 0.
Chính xác. Bước nhảy quá dài khiến mô hình nhảy qua nhảy lại giữa hai bờ sườn bát và văng ra xa, khiến Loss tiến tới vô cùng ($\infty$).

Câu 3. Vì sao khi càng tiến gần về đáy bát, các bước đi của Gradient Descent lại tự động nhỏ dần?

  • Vì ta chủ động cài hàm giảm Learning Rate.
  • Vì máy tính bị mệt nên chạy chậm lại.
  • Vì độ dốc (đạo hàm) ở gần đáy càng ngày càng tiến gần về 0.
  • Vì dữ liệu bị nhỏ lại.
Đúng rồi! Tại đáy bát, đường cong nằm ngang nên đạo hàm bằng 0. Càng gần đáy, đạo hàm càng nhỏ khiến lượng cập nhật $\alpha \cdot \text{đạo hàm}$ tự động bé lại.

💻 Thực hành: Tự mắt nhìn viên bi lăn xuống đáy

▶ Cách dùng: Nhấn Chạy code để xem hoạt cảnh viên bi (trọng số $w$) tự động tìm đường lăn từ trên đỉnh dốc xuống đúng đáy bát ($w=3$) qua từng Epoch!

💡 Thử thách: Trong ô code trên:

  1. Thử thay lr = 0.08 thành lr = 0.005 và chạy lại. Bạn thấy viên bi lăn thế nào?
  2. Thử thay lr = 0.22 và xem điều bất ngờ xảy ra!

Bài tiếp theo: Chúc mừng bạn! Bạn đã nắm trọn vẹn 3 trụ cột toán học của Machine Learning: Vector/Ma trận (Bài 26), Đạo hàm (Bài 27), và Gradient Descent (Bài 29). Bài 30 — Bài tập tốt nghiệp Chặng 7: Ta sẽ tổng lực ráp tất cả kiến thức này để xây dựng trọn vẹn một mô hình Hồi quy tuyến tính (Linear Regression) đa biến từ con số 0 hoàn toàn bằng NumPy mà không cần dùng bất kỳ thư viện AI nào!