Gradient descent và learning rate
Xem thửBài trước, Minh tính được gradient của loss tại w = 5, b = 30 là (-179,1; -27,4). Giờ anh muốn máy tự chỉnh w và b. Ý tưởng chỉ gói trong một dòng: lặp lại "trừ đi learning rate nhân gradient". Để chạy cho nhanh, Minh chọn learning rate 0,03. Sau 10 bước, loss không giảm mà tăng từ 626 lên 565.764. Sau 100 bước, loss là 7,2 × 10 mũ 36 và w bằng -4,1 × 10 mũ 17, một con số vô nghĩa cho "số phút mỗi km".
Lan, đồng nghiệp của Minh, sửa đúng một ký tự: 0,03 thành 0,02. Lần này loss giảm đều về 415,62 và w dừng ở 6,3328, đúng lời giải của thư viện.
Trước khi đọc tiếp, hãy dự đoán: vì sao chỉ đổi 0,02 thành 0,03 mà thuật toán đi từ hội tụ sang nổ tung? Có cách nào tính trước learning rate tối đa từ dữ liệu không?
Sau bài này, bạn có thể
- Viết vòng lặp gradient descent bằng NumPy cho mô hình đường thẳng và đọc được từng bước: gradient, cập nhật, loss mới.
- Nhận ra ba chế độ của learning rate (quá nhỏ, vừa, quá lớn) qua đường loss, và ước lượng ngưỡng phân kỳ từ độ cong của loss.
- Giải thích vì sao cột có thang đo lớn làm gradient descent hỏng, và sửa bằng chuẩn hóa.
- Phân biệt gradient descent trên toàn bộ dữ liệu với mini-batch, và đọc đúng đường loss nhấp nhô của mini-batch.
Cần biết trước: bài Đạo hàm là độ nhạy: đổi tham số thì sai số đổi bao nhiêu (bài trước) và phần chuẩn hóa trong bài Vector: biến một đơn hàng và một câu thành dãy số (Chương 3).
Mức độ: Cơ bản · Thời gian: khoảng 20 phút · Từ khóa: gradient descent, learning rate, hội tụ, phân kỳ, mini-batch
Đi ngược hướng dốc, từng bước nhỏ
Gradient chỉ hướng làm loss tăng nhanh nhất. Muốn loss giảm, ta đi ngược hướng đó. Gradient descent (gradient descent, hạ theo gradient) lặp lại một quy tắc:
w_mới = w - lr × dL/dw
b_mới = b - lr × dL/db
lr là learning rate (tốc độ học), một số dương nhỏ quyết định độ dài mỗi bước. Dấu trừ là "ngược hướng dốc": dL/dw âm thì w tăng, dương thì w giảm. Độ dài bước tỉ lệ với độ dốc: chỗ dốc đi xa, chỗ thoải đi ngắn, nên càng gần đáy bước càng ngắn mà không cần ai giảm lr.
Ví dụ số ở bước đầu với lr = 0,02: w = 5 - 0,02 × (-179,1) = 8,58 và b = 30 - 0,02 × (-27,4) = 30,55.
Mô hình tư duy của bài: người xuống núi trong sương mù. Họ không thấy đáy thung lũng, chỉ cảm được độ dốc dưới chân. Mỗi bước, họ bước xuống theo hướng dốc nhất, độ dài bước theo một quy tắc cố định. Phép so sánh có giới hạn: người thật nhìn được vài mét xung quanh và tự rút ngắn bước khi thấy vực; thuật toán chỉ biết độ dốc tại đúng một điểm, nên một lr quá lớn sẽ bước qua đáy sang sườn bên kia mà không hề biết.
Sơ đồ sau là một vòng lặp huấn luyện đầy đủ, khung chung cho mọi mô hình từ đường thẳng tới mạng nơ-ron.
Đang tải sơ đồ…
Cách đọc hình: bắt đầu từ ô cyan (giá trị khởi đầu). Ba ô tím là một bước: dự đoán và tính loss, tính gradient, cập nhật tham số. Ô cam kiểm sau mỗi bước: loss còn hữu hạn và còn giảm thì quay lại; gradient gần 0 thì dừng vì đã tới đáy (ô xanh lá); loss thành vô cực hoặc nan thì dừng vì phân kỳ (ô đỏ), và cách sửa là giảm lr hoặc đổi thang đo của dữ liệu.
Animation dưới đây chạy các bước đầu của vòng lặp với lr = 0,02. Hãy để ý w sau bước 1: nó vượt qua đáy. Bấm nút biến thể để đổi sang 0,03.
Đang tải Animation…
Với 0,02, w đi 8,58 rồi 6,20 rồi 7,76: mỗi lần vượt qua đáy, khoảng vượt nhỏ đi. Với 0,03, khoảng vượt lớn dần: 10,37; 2,34; 14,26. Đó là khác biệt giữa dao động tắt dần và dao động bùng nổ, và nó quyết định bởi một ngưỡng tính được.
Learning rate: vì sao có một ngưỡng
Hãy xét một tham số và loss dạng parabol L(w) = c × (w - w*)², với w* là đáy và c đo độ cong. Gradient là 2c × (w - w*), nên sau một bước:
w_mới - w* = (1 - 2c × lr) × (w - w*)
Khoảng cách tới đáy được nhân với (1 - 2c × lr) sau mỗi bước. Ba trường hợp:
| Giá trị của 2c × lr | Hệ số nhân | Điều xảy ra |
|---|---|---|
| Nhỏ hơn 1 | Từ 0 tới 1 | Tiến về đáy từ một phía, không vượt |
| Từ 1 tới 2 | Từ -1 tới 0 | Vượt qua đáy rồi quay lại, dao động tắt dần |
| Lớn hơn 2 | Nhỏ hơn -1 | Mỗi lần vượt xa hơn, phân kỳ |
Vậy điều kiện hội tụ là lr < 2 / (2c), tức 2 chia cho độ cong. Với hai tham số, loss là một cái bát bị kéo dẹt theo một hướng; độ cong theo hướng dốc nhất quyết định ngưỡng. Trên dữ liệu đơn hàng, độ cong lớn nhất là 82,96 (code ở phần Làm cùng mình tính con số này), nên ngưỡng là 2 / 82,96 = 0,0241. Đó là câu trả lời cho câu hỏi mở bài: 0,02 nằm dưới ngưỡng, 0,03 nằm trên.
Độ cong lớn đến từ đâu? Từ hướng w, vì mỗi đơn góp km bình phương vào độ cong, trung bình khoảng 40,8. Độ cong theo b chỉ là 2. Cái bát vì thế rất dẹt: dốc đứng theo w, thoải theo b. lr phải đủ nhỏ cho hướng dốc nhất, nên theo hướng thoải mỗi bước đi rất chậm. Bạn sẽ thấy điều này trong kết quả: w gần đúng sau 10 bước, b cần hàng trăm bước.
Thang đo của dữ liệu quyết định learning rate
Nếu thêm cột giá trị đơn (order_value_vnd, độ lệch chuẩn 583.094 đồng) vào mô hình mà không chuẩn hóa, độ cong theo trọng số của cột đó tỉ lệ với bình phương giá trị đơn, cỡ 10 mũ 11. Ngưỡng lr thành cỡ 10 mũ -11. Ở lr nhỏ như vậy, trọng số của km và số món gần như không nhúc nhích. Không có learning rate nào vừa với mọi cột cùng lúc.
Chuẩn hóa (trừ trung bình, chia độ lệch chuẩn, Chương 3) đưa mọi cột về cùng thang đo, nên cái bát gần tròn và một lr dùng được cho mọi hướng. Với km đã chuẩn hóa, độ cong theo cả hai hướng là 2, ngưỡng là 2 / 2 = 1. Đây là lý do thực tế số một để chuẩn hóa đặc trưng trước khi huấn luyện bằng gradient.
Mini-batch: ước lượng gradient từ một phần dữ liệu
Mỗi bước ở trên đọc cả 50.000 đơn để tính gradient. Với hàng triệu mẫu, một bước như vậy rất đắt. Mini-batch gradient descent lấy một lô nhỏ (ví dụ 256 đơn) ngẫu nhiên, tính gradient trên lô đó và cập nhật ngay. Một lượt đi hết dữ liệu gọi là một epoch (epoch). Gradient của lô là ước lượng có nhiễu của gradient thật, nên loss đi xuống nhưng nhấp nhô; đổi lại, mỗi epoch có hàng trăm lần cập nhật thay vì một. Khi lô chỉ có 1 mẫu, thuật toán thường được gọi là stochastic gradient descent (SGD).
Làm cùng mình
Bước 1: gradient descent trên đường thẳng, so với lời giải đóng.
w, b, lr = 5.0, 30.0, 0.02
show = {0, 1, 2, 5, 10, 50, 100, 300, 1000, 3000}
for step in range(3001):
err = w * x + b - y
loss = np.mean(err ** 2)
gw, gb = np.mean(2 * err * x), np.mean(2 * err)
if step in show:
print(f"step {step:4} w {w:7.4f} b {b:8.4f} loss {loss:8.3f} grad ({gw:9.3f}, {gb:8.3f})")
# Đi ngược hướng gradient một bước nhỏ
w -= lr * gw
b -= lr * gb
best_w, best_b = np.linalg.lstsq(np.c_[x, np.ones_like(x)], y, rcond=None)[0]
print(f"lstsq answer: w {best_w:.4f} b {best_b:.4f}")
| Dòng code | Máy làm gì | Vì sao |
|---|---|---|
err = w * x + b - y | Sai số của 50.000 đơn | Dùng cho cả loss và gradient |
gw, gb = ... | Hai đạo hàm riêng theo công thức bài trước | Hướng và độ dốc tại điểm hiện tại |
w -= lr * gw | Bước ngược hướng gradient | Quy tắc cập nhật của gradient descent |
np.linalg.lstsq(...) | Giải bài toán bình phương tối thiểu bằng đại số tuyến tính | Đáp án chuẩn để đối chiếu |
Kết quả [Đã chạy] Python 3.12, NumPy 2.5, pandas 3.1, CPU x86_64, ngày 04/11/2026:
step 0 w 5.0000 b 30.0000 loss 626.025 grad ( -179.115, -27.410)
step 1 w 8.5823 b 30.5482 loss 513.849 grad ( 118.899, 11.652)
step 2 w 6.2043 b 30.3152 loss 464.948 grad ( -77.540, -14.017)
step 5 w 7.3849 b 30.6988 loss 429.820 grad ( 22.826, -0.737)
step 10 w 7.0352 b 31.0166 loss 425.515 grad ( -2.329, -3.808)
step 50 w 6.7823 b 33.2093 loss 419.294 grad ( 0.275, -2.103)
step 100 w 6.5755 b 34.7889 loss 416.693 grad ( 0.149, -1.135)
step 300 w 6.3535 b 36.4850 loss 415.630 grad ( 0.013, -0.096)
step 1000 w 6.3328 b 36.6424 loss 415.622 grad ( 0.000, -0.000)
step 3000 w 6.3328 b 36.6425 loss 415.622 grad ( -0.000, -0.000)
lstsq answer: w 6.3328 b 36.6425
Đọc theo cột: w vượt đáy ở bước 1 (8,58), dao động rồi gần 6,6 sau 100 bước. b đi chậm hơn nhiều: 30,0; 31,0 ở bước 10; 34,8 ở bước 100; 36,49 ở bước 300. Đó là cái bát dẹt. Sau 1.000 bước, cả hai trùng lời giải của lstsq tới chữ số thứ tư: w = 6,3328 phút mỗi km, b = 36,6425 phút. Mô hình học được "mỗi km thêm khoảng 6,3 phút", gần hệ số 6,5 trong quy luật sinh dữ liệu ở DATA_CARD.
Bước 2: so năm learning rate và tính ngưỡng.
def run(lr, steps=100, w=5.0, b=30.0):
losses = []
for _ in range(steps):
err = w * x + b - y
w, b = w - lr * np.mean(2 * err * x), b - lr * np.mean(2 * err)
losses.append(np.mean((w * x + b - y) ** 2))
return w, losses
for lr in [0.001, 0.01, 0.02, 0.025, 0.03]:
w, losses = run(lr)
print(f"lr {lr:<6} loss after 1: {losses[0]:10.4g} 10: {losses[9]:10.4g} 100: {losses[99]:10.4g} w {w:10.4g}")
# Độ cong lớn nhất của mặt loss quyết định learning rate tối đa
H = 2 * np.array([[np.mean(x * x), np.mean(x)], [np.mean(x), 1.0]])
print(f"largest curvature {np.linalg.eigvalsh(H).max():.2f} so lr must stay below {2 / np.linalg.eigvalsh(H).max():.4f}")
Kết quả [Đã chạy] Python 3.12, NumPy 2.5, pandas 3.1, CPU x86_64, ngày 04/11/2026:
lr 0.001 loss after 1: 594.6 10: 463.1 100: 426.8 w 7.115
lr 0.01 loss after 1: 433.8 10: 426.8 100: 419.3 w 6.783
lr 0.02 loss after 1: 513.8 10: 425.5 100: 416.7 w 6.575
lr 0.025 loss after 1: 656 10: 1248 100: 3.087e+08 w -2699
lr 0.03 loss after 1: 866.1 10: 5.658e+05 100: 7.194e+36 w -4.129e+17
largest curvature 82.96 so lr must stay below 0.0241
| Learning rate | Chế độ | Dấu hiệu trên đường loss |
|---|---|---|
| 0,001 | Quá nhỏ | Giảm đều nhưng sau 100 bước vẫn 426,8, còn xa 415,6 |
| 0,01 và 0,02 | Vừa | Giảm nhanh; 0,02 tăng nhẹ ở bước 1 do vượt đáy rồi giảm |
| 0,025 | Vừa vượt ngưỡng 0,0241 | Tăng chậm rồi bùng: 1.248 ở bước 10, 3,1 × 10 mũ 8 ở bước 100 |
| 0,03 | Quá lớn | Bùng ngay từ bước 1 |
Dòng cuối tính ngưỡng từ dữ liệu: np.linalg.eigvalsh(H) trả về độ cong theo hai hướng chính của cái bát (giá trị riêng của ma trận đạo hàm bậc hai, gọi là Hessian), lớn nhất là 82,96. Với mô hình thật bạn không cần tính Hessian; chỉ cần nhớ ngưỡng tồn tại.
Bước 3: tình huống thất bại, cột chưa chuẩn hóa.
def gd(X, lr, steps):
w, b = np.zeros(X.shape[1]), 0.0
for step in range(1, steps + 1):
err = X @ w + b - y
# Gradient cho cả vector trọng số trong một phép nhân ma trận
w, b = w - lr * 2 * X.T @ err / len(y), b - lr * 2 * err.mean()
loss = np.mean((X @ w + b - y) ** 2)
if not np.isfinite(loss):
return step, loss
return steps, loss
np.seterr(all="ignore")
for lr in [0.02, 1e-6, 1e-12]:
step, loss = gd(X, lr, 200)
print(f"raw columns lr {lr:<6g} stopped at step {step:3} loss {loss:.4g}")
Z = (X - X.mean(axis=0)) / X.std(axis=0)
for lr in [0.02, 0.2]:
step, loss = gd(Z, lr, 200)
print(f"standardized lr {lr:<6g} stopped at step {step:3} loss {loss:.4g}")
print("column std:", ", ".join(f"{v:,.2f}" for v in X.std(axis=0)))
| Dòng code | Máy làm gì | Vì sao |
|---|---|---|
X.T @ err / len(y) | Gradient cho cả vector trọng số trong một phép nhân ma trận | (3, 50000) @ (50000,) ra (3,), mỗi cột một đạo hàm riêng (Chương 3) |
if not np.isfinite(loss) | Dừng khi loss thành inf hoặc nan | Phát hiện phân kỳ thay vì chạy tiếp với số rác |
np.seterr(all="ignore") | Tắt cảnh báo tràn số của NumPy | Để kết quả gọn; khi tự gỡ lỗi nên để cảnh báo hiện |
Kết quả [Đã chạy] Python 3.12, NumPy 2.5, pandas 3.1, CPU x86_64, ngày 04/11/2026:
raw columns lr 0.02 stopped at step 15 loss inf
raw columns lr 1e-06 stopped at step 25 loss inf
raw columns lr 1e-12 stopped at step 200 loss 3394
standardized lr 0.02 stopped at step 200 loss 406.6
standardized lr 0.2 stopped at step 200 loss 406.6
column std: 3.56, 2.96, 583,093.59
Với cột tiền thô, lr 0,02 nổ ở bước 15, lr 0,000001 vẫn nổ ở bước 25. Hạ xuống 10 mũ -12 thì hết nổ nhưng sau 200 bước loss vẫn 3.394, gấp tám lần mức tốt, vì trọng số km và số món gần như không đổi. Sau chuẩn hóa, cả lr 0,02 lẫn 0,2 đều tới loss 406,6, tốt hơn mô hình một biến (415,6) nhờ thêm hai cột. Không phải thuật toán kém; là thang đo của dữ liệu không phù hợp với một lr chung.
Bước 4: mini-batch so với toàn bộ dữ liệu, cùng 3 epoch.
for batch in [50000, 256]:
w, b, updates = 0.0, 0.0, 0
for epoch in range(3):
order = rng.permutation(len(z))
for i in range(0, len(z), batch):
idx = order[i:i + batch]
# Mỗi lần cập nhật chỉ nhìn một lô nhỏ đơn hàng
err = w * z[idx] + b - y[idx]
w, b = w - 0.1 * np.mean(2 * err * z[idx]), b - 0.1 * np.mean(2 * err)
updates += 1
loss = np.mean((w * z + b - y) ** 2)
print(f"batch {batch:5} epoch {epoch + 1} updates {updates:4} full loss {loss:8.3f}")
print(f"batch {batch:5} minutes per km after 3 epochs: {w / x.std():.4f}")
Kết quả [Đã chạy] Python 3.12, NumPy 2.5, pandas 3.1, CPU x86_64, ngày 04/11/2026:
batch 50000 epoch 1 updates 1 full loss 3895.695
batch 50000 epoch 2 updates 2 full loss 2642.869
batch 50000 epoch 3 updates 3 full loss 1841.060
batch 50000 minutes per km after 3 epochs: 3.0904
batch 256 epoch 1 updates 196 full loss 416.322
batch 256 epoch 2 updates 392 full loss 416.151
batch 256 epoch 3 updates 588 full loss 416.486
batch 256 minutes per km after 3 epochs: 6.5014
Cùng 3 lượt đọc dữ liệu, cách dùng toàn bộ chỉ cập nhật 3 lần và loss vẫn 1.841. Lô 256 đơn cập nhật 588 lần và tới 416 ngay sau epoch đầu. Nhưng để ý loss của mini-batch: 416,32 rồi 416,15 rồi 416,49, tăng ở epoch 3. Đó là nhiễu của gradient ước lượng, không phải lỗi. Đáy thật là 415,62; mini-batch với lr cố định dao động quanh đáy thay vì dừng hẳn, nên trong thực tế người ta giảm dần lr (Chương 11).
Áp dụng vào Giao Hàng Anh Tí
Khi huấn luyện mô hình thời gian giao ở Chương 13, nhóm của Minh sẽ dùng quy trình rút ra từ bài này:
- Chuẩn hóa mọi cột số bằng trung bình và độ lệch chuẩn tính trên tập huấn luyện.
- Thử vài lr theo cấp số nhân (0,001; 0,01; 0,1) trong vài chục bước, chọn lr lớn nhất mà loss vẫn giảm ở những bước đầu.
- Ghi đường loss mỗi bước hoặc mỗi epoch. Loss tăng ở bước đầu là dấu hiệu vượt ngưỡng; loss thành nan là đã phân kỳ, dừng ngay thay vì lưu mô hình.
- So kết quả với một lời giải đối chiếu khi có (như
lstsqcho mô hình tuyến tính) để biết thuật toán đã thật sự tới đáy.
Hiểu lầm thường gặp
Hiểu lầm 1: "lr lớn thì học nhanh hơn." Đúng cho tới ngưỡng, sai khi vượt. Ở ví dụ, 0,025 chỉ lớn hơn 0,02 một phần tư nhưng làm loss lên 3,1 × 10 mũ 8 sau 100 bước. Ngưỡng do độ cong của loss quyết định, không do mong muốn của người huấn luyện.
Hiểu lầm 2: "Loss tăng ở một bước nghĩa là thuật toán hỏng." Không nhất thiết. Với 0,02, loss vẫn giảm nhưng w vượt đáy rồi quay lại; với mini-batch, loss nhấp nhô vì gradient có nhiễu. Đáng lo là loss tăng liên tục qua nhiều bước, hoặc thành inf, nan.
Hiểu lầm 3: "Phân kỳ thì cứ giảm lr đến khi hết." Giảm lr luôn chặn được phân kỳ, nhưng nếu nguyên nhân là một cột thang đo lớn thì lr đủ nhỏ cho cột đó làm các cột khác đứng yên: ví dụ 10 mũ -12 cho loss 3.394. Sửa đúng là chuẩn hóa.
| Triệu chứng | Nguyên nhân | Cách kiểm chứng | Cách sửa |
|---|---|---|---|
| Loss tăng dần rồi thành inf hoặc nan | lr vượt ngưỡng 2 chia độ cong | In loss 10 bước đầu, xem có tăng đều không | Giảm lr 3 đến 10 lần |
| Phải hạ lr cực nhỏ mới hết nổ, loss vẫn cao | Một cột có thang đo lớn hơn hẳn | In độ lệch chuẩn từng cột | Chuẩn hóa cột trước khi huấn luyện |
| Loss giảm rất chậm, các tham số đổi rất ít | lr quá nhỏ | Thử lr gấp 3 hoặc gấp 10, so loss sau cùng số bước | Tăng lr tới khi các bước đầu bắt đầu dao động rồi lùi lại |
| Loss mini-batch nhấp nhô quanh một mức | Nhiễu của gradient ước lượng | Tính loss trên toàn bộ dữ liệu sau mỗi epoch | Chấp nhận, hoặc giảm lr dần, hoặc tăng kích thước lô |
Điều kiện và giới hạn
Loss của mô hình đường thẳng có đúng một đáy, nên gradient descent chắc chắn tới đó nếu lr nằm dưới ngưỡng. Loss của mạng nơ-ron có nhiều chỗ trũng và vùng phẳng; gradient descent tìm được một chỗ trũng tốt chứ không đảm bảo chỗ thấp nhất. Với mô hình tuyến tính nhỏ như bài này, lời giải đóng (lstsq) nhanh và chính xác hơn; ta dùng gradient descent ở đây vì cùng một vòng lặp sẽ chạy cho mạng nơ-ron, nơi không có lời giải đóng.
Tự kiểm tra (có đáp án)
Câu 1 (giải thích). Với lr = 0,02, sau bước 1 w là 8,58, xa đáy 6,33 hơn điểm xuất phát 5 về khoảng cách. Vì sao loss vẫn giảm từ 626,0 xuống 513,8?
Đáp án: cùng lúc b tăng từ 30 lên 30,55, và quan trọng hơn, loss phụ thuộc cả w và b qua cái bát dẹt: theo hướng dốc, w vượt đáy nhưng vẫn ở vị trí thấp hơn trên thành bát đối diện so với điểm xuất phát. Hệ số nhân theo hướng dốc nhất là 1 - 82,96 × 0,02 = -0,66: đổi dấu (vượt đáy) nhưng độ lớn nhỏ hơn 1 (gần đáy hơn).
Câu 2 (dự đoán). Chuẩn hóa km thành z (trung bình 0, độ lệch chuẩn 1) rồi chạy gradient descent với lr = 0,99 và lr = 1,01 trong 100 bước. Kết quả mỗi trường hợp?
Đáp án: độ cong theo cả hai hướng là 2 nên ngưỡng là 1. lr = 0,99 hội tụ nhưng dao động rất lâu (chạy thật: loss 511,3 sau 100 bước, còn xa 415,6), lr = 1,01 phân kỳ (285.808 sau 100 bước). lr tốt nhất là khoảng 0,5, tới đáy gần như ngay lập tức.
Câu 3 (vận dụng). Bình huấn luyện mô hình có cột weight_kg (độ lệch chuẩn 5,58) và cột shipper_exp_months (đã chuẩn hóa). Loss nổ ở lr = 0,1. Bạn kiểm tra gì trước tiên?
Đáp án: in độ lệch chuẩn từng cột. Cột weight_kg chưa chuẩn hóa có độ cong gấp khoảng 5,58 bình phương, tức 31 lần, so với cột đã chuẩn hóa, nên ngưỡng lr nhỏ hơn khoảng 31 lần. Chuẩn hóa cột đó thay vì giảm lr cho cả mô hình.
Tự tay làm
Sửa lr_compare.py: thay x bằng z = (x - x.mean()) / x.std(), đổi điểm xuất phát thành w = 0, b = 0 và danh sách lr thành [0.1, 0.5, 0.9, 0.99, 1.01]. Trước khi chạy, ghi ra lr nào phân kỳ và lr nào tới đáy nhanh nhất. In thêm ngưỡng tính từ Hessian của z.
Kết quả mong đợi để tự kiểm: ngưỡng in ra là 1,0000; lr 0,5 có loss 415,62 ngay sau bước 1 (với z chuẩn hóa, 0,5 là bước nhảy thẳng tới đáy); 0,9 dao động rồi tới 415,62; 0,99 sau 100 bước vẫn khoảng 511; 1,01 phân kỳ. Đổi w của z về đơn vị km bằng cách chia cho độ lệch chuẩn 3,564: phải ra 6,3328.
Điều cần nhớ
- Gradient descent lặp lại
tham số = tham số - lr × gradient: đi ngược hướng dốc, bước dài tỉ lệ với độ dốc. - Learning rate có ngưỡng bằng 2 chia độ cong lớn nhất của loss; vượt ngưỡng thì mỗi lần vượt đáy lại xa hơn và loss thành inf. Trên dữ liệu đơn hàng thô, ngưỡng là 0,0241.
- Cột có thang đo lớn làm cái bát loss dẹt và hạ ngưỡng xuống gần 0; chuẩn hóa đặc trưng là cách sửa, không phải hạ lr mãi.
- Mini-batch cập nhật nhiều lần mỗi epoch với gradient có nhiễu; loss nhấp nhô quanh đáy là bình thường. Bài tiếp theo trả lời câu hỏi còn lại: với mô hình nhiều tầng, làm sao tính gradient mà không phải viết công thức tay cho từng tham số.
Nguồn tham khảo
- CS231n, Optimization: Stochastic Gradient Descent: https://cs231n.github.io/optimization-1/
- CS231n, Neural Networks Part 3 (learning rate, theo dõi đường loss): https://cs231n.github.io/neural-networks-3/
- scikit-learn, Stochastic Gradient Descent: https://scikit-learn.org/stable/modules/sgd.html
- NumPy,
numpy.linalg.lstsq: https://numpy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html - PyTorch,
torch.optim.SGD: https://pytorch.org/docs/stable/generated/torch.optim.SGD.html - AI Engineering from Scratch (MIT), phase 01, Optimization: https://github.com/rohitg00/ai-engineering-from-scratch/tree/main/phases/01-math-foundations/08-optimization