Chương 0·Bài 0.6

Đạo hàm và gradient

Một model đoán điểm bằng y^=wx\hat{y} = wx với một trọng số ww. Đặt w=0w = 0 thì đoán sai nhiều, loss lớn. Nên tăng hay giảm ww, và đổi bao nhiêu? Với một trọng số thì có thể thử từng giá trị, nhưng mạng neural có hàng triệu trọng số. Đạo hàm cho biết đổi ww một chút thì loss đổi bao nhiêu và theo hướng nào. Bài này đi từ đạo hàm tới gradient và gradient descent, thuật toán dùng để huấn luyện gần như mọi model trong khóa học.

Độ dốc tại một điểm

Ở bài 0.4, hàm bậc nhất có độ dốc không đổi: xx tăng 1 thì yy tăng ww. Hàm f(x)=x2f(x) = x^2 thì khác: gần x=0x = 0 đồ thị gần như nằm ngang, càng xa thì càng dốc. Ta cần nói “độ dốc tại x=3x = 3”.

Cách làm: lấy một điểm gần đó, x=3+hx = 3 + h, và chia độ tăng của ff cho độ tăng của xx:

f(3+h)−f(3)h.\frac{f(3 + h) - f(3)}{h}.

Đây là độ dốc của đường thẳng nối hai điểm trên đồ thị. Cho hh nhỏ dần, đường ấy xoay dần về tiếp tuyến, đường thẳng chỉ chạm đồ thị tại đúng điểm x=3x = 3. Độ dốc của tiếp tuyến là đạo hàm của ff tại 3, ký hiệu f′(3)f'(3).

h càng nhỏ, đường nối hai điểm càng gần tiếp tuyến; độ dốc tiến về 6.0510152001234xh = 1.5: độ dốc 7.5h = 0.5: độ dốc 6.5tiếp tuyến: độ dốc 6(3, 9)x²
h càng nhỏ, đường nối hai điểm càng gần tiếp tuyến; độ dốc tiến về 6.
Python
import numpy as np

def f(x):
    return x ** 2

for h in [1, 0.1, 0.01, 0.001]:
    print(h, round((f(3 + h) - f(3)) / h, 6))

Với hh nhỏ dần, tỉ số tiến về 6. Viết tay: (3+h)2−9h=6h+h2h=6+h\frac{(3 + h)^2 - 9}{h} = \frac{6h + h^2}{h} = 6 + h, và h→0h \to 0 cho đúng 6.

Đọc đạo hàm như một lời hứa nhỏ: tăng xx thêm một lượng rất nhỏ Δ\Delta thì ff tăng thêm khoảng f′(x)⋅Δf'(x) \cdot \Delta. Đạo hàm dương thì hàm đang đi lên, âm thì đang đi xuống, bằng 0 thì đang nằm ngang, như ở đáy một cái bát.

Hỏi nhanh

f′(2)=−3f'(2) = -3. Tăng xx từ 2 lên 2.01 thì ff thay đổi khoảng bao nhiêu?

Các quy tắc tính đạo hàm

Không ai tính giới hạn mỗi lần. Vài quy tắc đủ cho phần lớn học máy:

Hàm Đạo hàm
hằng số cc 00
xnx^n nxn−1n x^{n-1}
exe^x exe^x
log⁡x\log x 1/x1/x
c⋅f(x)c \cdot f(x) c⋅f′(x)c \cdot f'(x)
f(x)+g(x)f(x) + g(x) f′(x)+g′(x)f'(x) + g'(x)

Hàng xnx^n cho (x2)′=2x(x^2)' = 2x, khớp con số 6 tại x=3x = 3. Hàng exe^x là lý do bài 0.4 chọn cơ số ee: đạo hàm của nó là chính nó. Hai hàng cuối nói đạo hàm đi qua phép nhân với hằng số và phép cộng: (3x2−4x+1)′=6x−4(3x^2 - 4x + 1)' = 6x - 4.

Kiểm bằng số: so công thức với sai phân tại x=2x = 2.

Python
def numeric(g, x, h=1e-6):
    return (g(x + h) - g(x - h)) / (2 * h)

x = 2.0
tests = [("x^3", lambda t: t ** 3, 3 * x ** 2),
         ("e^x", np.exp, np.exp(x)),
         ("log x", np.log, 1 / x),
         ("3x^2 - 4x + 1", lambda t: 3 * t ** 2 - 4 * t + 1, 6 * x - 4)]
for name, g, exact in tests:
    print(f"{name:14s} công thức {exact:.6f}  sai phân {numeric(g, x):.6f}")

Hàm numeric lấy hai điểm hai bên, x+hx + h và x−hx - h, gọi là sai phân trung tâm; nó chính xác hơn cách lấy một bên ở mục trước. Mục cuối bài dùng lại hàm này để kiểm gradient.

Hỏi nhanh

g(x)=4x3−2xg(x) = 4x^3 - 2x. g′(1)g'(1) bằng bao nhiêu?

Quy tắc chuỗi

Loss của model hiếm khi là một hàm đơn giản của trọng số. Nó là một chuỗi các bước: trọng số cho ra dự đoán, dự đoán cho ra sai số, sai số cho ra loss. Với hàm hợp f(g(x))f(g(x)), quy tắc chuỗi nói

ddxf(g(x))=f′(g(x))⋅g′(x).\frac{d}{dx} f(g(x)) = f'(g(x)) \cdot g'(x).

Đọc bằng lời hứa ở trên: xx nhích Δ\Delta thì gg nhích khoảng g′(x)Δg'(x)\Delta, và gg nhích chừng ấy thì ff nhích khoảng f′(g)f'(g) nhân lượng đó. Tốc độ đổi qua nhiều bước là tích các tốc độ đổi của từng bước.

Ví dụ h(x)=(2x+1)2h(x) = (2x + 1)^2. Bên trong là g=2x+1g = 2x + 1 với g′=2g' = 2; bên ngoài là f=g2f = g^2 với f′=2gf' = 2g. Nên h′(x)=2(2x+1)⋅2=4(2x+1)h'(x) = 2(2x + 1) \cdot 2 = 4(2x + 1), tại x=1x = 1 bằng 12.

Áp vào model y^=wx\hat{y} = wx với loss L=(y^−y)2L = (\hat{y} - y)^2. Coi ww là biến, xx và yy là số đã biết. Chuỗi gồm hai bước: w→y^w \to \hat{y} có đạo hàm xx, và y^→L\hat{y} \to L có đạo hàm 2(y^−y)2(\hat{y} - y). Nhân lại:

∂L∂w=2(y^−y) x.\frac{\partial L}{\partial w} = 2(\hat{y} - y)\, x.
Đạo hàm của loss theo w bằng tích đạo hàm của từng bước.ww = 1ŷ = wxŷ = 2L = (ŷ − y)²L = 16∂ŷ/∂w = x = 2∂L/∂ŷ = 2(ŷ − y) = −8∂L/∂w = 2 · (−8) = −16
Đạo hàm của loss theo w bằng tích đạo hàm của từng bước.

Mạng neural chỉ là một chuỗi dài hơn. Thuật toán backpropagation ở chương 3 là quy tắc chuỗi áp dụng có hệ thống, từ loss đi ngược về từng trọng số.

Một ví dụ quan trọng là sigmoid σ(z)=(1+e−z)−1\sigma(z) = (1 + e^{-z})^{-1}. Áp quy tắc chuỗi hai lần rồi rút gọn, ta được một công thức rất gọn:

σ′(z)=σ(z)(1−σ(z)).\sigma'(z) = \sigma(z)\big(1 - \sigma(z)\big).

Tại z=0z = 0, σ=0.5\sigma = 0.5 nên σ′=0.25\sigma' = 0.25, lớn nhất có thể. Ở hai đầu chữ S, σ′\sigma' gần 0. Một mạng nhiều lớp sigmoid nhân nhiều thừa số không quá 0.25 với nhau, nên gradient ở các lớp đầu nhỏ dần. Chương 3 gọi đây là vanishing gradient.

Python
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

for z in [0.0, 2.0, 5.0]:
    s = sigmoid(z)
    print(z, round(s * (1 - s), 4), round(numeric(sigmoid, z), 4))

w, x, y = 1.0, 2.0, 6.0
loss = lambda w: (w * x - y) ** 2
print(2 * (w * x - y) * x, round(numeric(loss, w), 4))

Công thức và sai phân khớp nhau ở cả ba điểm của sigmoid. Dòng cuối: với w=1w = 1, model đoán 22 trong khi đáp án là 66; đạo hàm −16-16 âm, nghĩa là tăng ww thì loss giảm. Đúng như mong đợi, vì model đang đoán thấp.

Hỏi nhanh

L=(wx−y)2L = (wx - y)^2 với x=3x = 3, y=3y = 3 và w=2w = 2. ∂L/∂w\partial L / \partial w bằng bao nhiêu, và nên đổi ww theo hướng nào?

Đạo hàm riêng và gradient

Loss thường phụ thuộc nhiều tham số cùng lúc. Với hàm hai biến f(x,y)f(x, y), đạo hàm riêng theo xx, viết ∂f/∂x\partial f / \partial x, là đạo hàm khi coi yy như hằng số. Ví dụ f(x,y)=x2y+3xf(x, y) = x^2 y + 3x:

∂f∂x=2xy+3,∂f∂y=x2.\frac{\partial f}{\partial x} = 2xy + 3, \qquad \frac{\partial f}{\partial y} = x^2.

Xếp mọi đạo hàm riêng thành một vector, ta được gradient:

∇f=(∂f∂x, ∂f∂y).\nabla f = \left(\frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y}\right).

Gradient chỉ hướng làm ff tăng nhanh nhất, và độ dài của nó cho biết dốc cỡ nào. Hướng ngược lại, −∇f-\nabla f, làm ff giảm nhanh nhất. Hình dưới là bản đồ đường đồng mức của cái bát f(x,y)=x2+y2f(x, y) = x^2 + y^2; mỗi vòng là một mức cao. Gradient tại mỗi điểm vuông góc với đường đồng mức và chỉ ra ngoài.

Gradient của x² + y² chỉ ra xa đáy; −∇f chỉ về đáy.(1, 2)−∇f∇f = (2x, 2y)chỉ ra xa đáy
Gradient của x² + y² chỉ ra xa đáy; −∇f chỉ về đáy.
Python
def bowl(v):
    return v[0] ** 2 + v[1] ** 2

def grad_numeric(g, v, h=1e-6):
    out = np.zeros_like(v)
    for i in range(len(v)):
        e = np.zeros_like(v)
        e[i] = h
        out[i] = (g(v + e) - g(v - e)) / (2 * h)
    return out

v = np.array([1.0, 2.0])
print(2 * v, np.round(grad_numeric(bowl, v), 6))

grad_numeric làm sai phân trung tâm theo từng tọa độ, giữ các tọa độ khác cố định: đúng định nghĩa đạo hàm riêng. Tại (1,2)(1, 2), gradient là (2,4)(2, 4), chỉ ra xa gốc.

Hỏi nhanh

f(x,y)=3x+y2f(x, y) = 3x + y^2. Gradient tại (5,1)(5, 1) là gì?

Gradient descent

Đã biết hướng làm loss giảm nhanh nhất là −∇L-\nabla L. Gradient descent đi một bước nhỏ theo hướng ấy, rồi tính lại gradient, rồi đi tiếp:

w←w−η ∇L(w).\mathbf{w} \leftarrow \mathbf{w} - \eta\, \nabla L(\mathbf{w}).

η\eta là learning rate, quyết định mỗi bước dài bao nhiêu. Hình ảnh quen thuộc: đi xuống núi trong sương mù, chỉ thấy độ dốc dưới chân, nên mỗi lần bước một bước theo hướng dốc xuống nhất.

Thử với model một trọng số ở trên, x=2x = 2, y=6y = 6, đáp án tốt nhất là w=3w = 3. Bắt đầu từ w=0w = 0, so hai learning rate.

Python
def descend(eta, steps, w=0.0):
    path = []
    for _ in range(steps):
        grad = 2 * (w * x - y) * x
        w = w - eta * grad
        path.append(round(w, 4))
    return path

print(descend(0.05, 6))
print(descend(0.05, 30)[-1])
print(descend(0.3, 6))
η = 0.05 đi dần xuống đáy; η = 0.3 nhảy qua đáy, văng xa dần.036wη = 0.05-3036912wη = 0.32.86 sau 6 bước0 → 7.2 → −2.88 → 11.23
η = 0.05 đi dần xuống đáy; η = 0.3 nhảy qua đáy, văng xa dần.

Với η=0.05\eta = 0.05, ww tiến dần về 3, bước sau ngắn hơn bước trước vì càng gần đáy thì gradient càng nhỏ. Sau 30 bước đã bằng 3 tới bốn chữ số thập phân. Với η=0.3\eta = 0.3, bước đầu nhảy lên 7.2, vượt qua đáy rất xa; mỗi bước sau lại vượt sang phía bên kia, xa hơn bước trước, và ww lớn dần không giới hạn. Ta nói gradient descent phân kỳ. Khi loss tăng vọt hay thành nan trong lúc huấn luyện, nghi phạm đầu tiên là learning rate quá lớn.

Với nhiều mẫu, loss là trung bình loss của từng mẫu, nên gradient cũng là trung bình gradient của từng mẫu. Lấy hai mẫu (x,y)=(1,2)(x, y) = (1, 2) và (2,5)(2, 5), bắt đầu từ w=0w = 0 với η=0.1\eta = 0.1. Hai dự đoán đều là 0, sai số −2-2 và −5-5, loss (4+25)/2=14.5(4 + 25)/2 = 14.5. Gradient của từng mẫu là 2⋅sai soˆˊ⋅x2 \cdot \text{sai số} \cdot x: −4-4 và −20-20, trung bình −12-12. Bước cập nhật cho w=0−0.1⋅(−12)=1.2w = 0 - 0.1 \cdot (-12) = 1.2. Sai số mới là −0.8-0.8 và −2.6-2.6, loss còn (0.64+6.76)/2=3.7(0.64 + 6.76)/2 = 3.7.

Python
xs = np.array([1.0, 2.0])
ys = np.array([2.0, 5.0])
w = 0.0
for step in range(3):
    err = w * xs - ys
    grad = np.mean(2 * err * xs)
    print(step, round(w, 4), round(np.mean(err ** 2), 4), round(grad, 4))
    w -= 0.1 * grad

Dòng 0 và dòng 1 khớp phép tính tay. Đáp án tốt nhất là w=12/5=2.4w = 12/5 = 2.4, nơi gradient trung bình bằng 0; vài bước nữa ww sẽ tới đó.

Hỏi nhanh

Huấn luyện một model bằng gradient descent, loss sau mỗi lượt chạy hết dữ liệu (gọi là một epoch) lần lượt là 2.1, 5.8, 31.4, 260.0, rồi nan. Nên thử gì trước?

Kiểm gradient bằng sai phân

Tự viết công thức gradient thì dễ sai một hệ số 2 hay một dấu trừ, mà code vẫn chạy. Cách kiểm: so gradient từ công thức với gradient tính bằng sai phân trên một ví dụ nhỏ. Nếu sai số tương đối cỡ 10−610^{-6} trở xuống thì công thức gần như chắc đúng.

Model tuyến tính ba trọng số, loss MSE trên năm mẫu: L(w)=1n∥Xw−y∥2L(\mathbf{w}) = \frac{1}{n}\lVert X\mathbf{w} - \mathbf{y} \rVert^2. Quy tắc chuỗi cho gradient

∇L=2nX⊤(Xw−y).\nabla L = \frac{2}{n} X^\top (X\mathbf{w} - \mathbf{y}).

Đọc bằng shape của bài 0.5: X⊤X^\top là (3,5)(3, 5), phần dư Xw−yX\mathbf{w} - \mathbf{y} là (5,)(5,), tích là (3,)(3,), đúng một số cho mỗi trọng số.

Python
rng = np.random.default_rng(0)
X = rng.normal(size=(5, 3))
y_vec = rng.normal(size=5)
w_vec = rng.normal(size=3)

def mse(w):
    return np.mean((X @ w - y_vec) ** 2)

exact = 2 / len(y_vec) * X.T @ (X @ w_vec - y_vec)
approx = grad_numeric(mse, w_vec)
wrong = 1 / len(y_vec) * X.T @ (X @ w_vec - y_vec)     # quên số 2
rel = lambda a, b: np.linalg.norm(a - b) / np.linalg.norm(a + b)
print(np.round(exact, 6))
print(np.round(approx, 6))
print(f"đúng: {rel(exact, approx):.1e}   quên số 2: {rel(wrong, approx):.1e}")

Công thức đúng cho sai số tương đối rất nhỏ; bản quên số 2 cho sai số cỡ 0.3, lộ ngay. Đây cũng là cách PyTorch kiểm các phép tính tự viết (torch.autograd.gradcheck).

Hỏi nhanh

Kiểm gradient tự viết bằng sai phân, sai số tương đối ra 0.50.5. Nhiều khả năng do đâu?

Khi đi thi

  • Tự viết gradient thì kiểm bằng sai phân trung tâm trên ví dụ nhỏ, h≈10−6h \approx 10^{-6}.
  • Gradient phải có đúng shape của tham số.
  • Loss tăng vọt hay thành nan thì giảm learning rate trước.

Tóm tắt

  • Đạo hàm f′(x)f'(x) là giới hạn của f(x+h)−f(x)h\frac{f(x + h) - f(x)}{h}, độ dốc tiếp tuyến; xx nhích Δ\Delta thì ff nhích khoảng f′(x)Δf'(x)\Delta.
  • (xn)′=nxn−1(x^n)' = nx^{n-1}, (ex)′=ex(e^x)' = e^x, (log⁡x)′=1/x(\log x)' = 1/x; đạo hàm đi qua phép cộng và nhân hằng số.
  • Quy tắc chuỗi: (f(g(x)))′=f′(g(x)) g′(x)(f(g(x)))' = f'(g(x))\, g'(x); σ′=σ(1−σ)≤0.25\sigma' = \sigma(1 - \sigma) \le 0.25.
  • Gradient là vector các đạo hàm riêng, chỉ hướng tăng nhanh nhất; −∇f-\nabla f chỉ hướng giảm nhanh nhất.
  • Gradient descent: w←w−η∇L\mathbf{w} \leftarrow \mathbf{w} - \eta \nabla L; η\eta quá lớn thì phân kỳ.
  • MSE tuyến tính có ∇L=2nX⊤(Xw−y)\nabla L = \frac{2}{n} X^\top (X\mathbf{w} - \mathbf{y}); kiểm mọi gradient tự viết bằng sai phân trung tâm.

Kiểm tra cuối bài

Mười câu dưới đây bao quát cả bài. Làm hết rồi xem điểm; câu nào sai thì đọc lại mục tương ứng.

Câu 1

Đạo hàm của f(x)=5x+3f(x) = 5x + 3 là gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 38

Câu 2

Quy tắc chuỗi nói ddxf(g(x))\frac{d}{dx} f(g(x)) bằng gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 39

Câu 3

Gradient của f(x,y)=x2+y2f(x, y) = x^2 + y^2 là gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 41

Câu 4

Đạo hàm của exe^x là gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 42

Câu 5

Trong gradient descent, vector âm của gradient, −∇L-\nabla L, chỉ về đâu?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 44

Câu 6

Đạo hàm của sigmoid σ(x)=1/(1+e−x)\sigma(x) = 1/(1 + e^{-x}) theo xx là gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 45

Câu 7

Với L=(y^−y)2L = (\hat{y} - y)^2 và y^=wx\hat{y} = wx, ∂L/∂w\partial L / \partial w bằng gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 46

Câu 8

Gradient của f(x)=∥x∥2f(\mathbf{x}) = \lVert \mathbf{x} \rVert^2, bình phương chuẩn L2, là gì?

Đề thiNOAI 2026 Singapore · Vòng sơ loại · Câu 53

Câu 9

Loss L(w)=(w−4)2L(w) = (w - 4)^2, đang ở w=0w = 0, learning rate 0.250.25. Sau một bước gradient descent, ww bằng bao nhiêu?

Câu 10

Một học sinh kiểm gradient bằng sai phân một bên f(x+h)−f(x)h\frac{f(x + h) - f(x)}{h} và thấy sai số nhỏ dần khi giảm hh từ 10−210^{-2} xuống 10−610^{-6}, nhưng tăng lên khi h=10−12h = 10^{-12}. Vì sao?

Đọc thêm