Danh sách bài viết

Bài 26: Loss function là gì? Mục tiêu mà model cần tối ưu

Loss function đo độ sai giữa prediction \( \hat{y} \) và ground truth \( y \). Training = bài toán tối ưu hoá: tìm tham số \( \theta \) để \( L(\theta) \) nhỏ nhất. Bài này đi qua MSE, MAE, Huber cho regression; Binary và Categorical Cross-Entropy cho classification. Loss surface, tính chất khả vi, trade-off MSE vs MAE. Code Python tính các loss bằng list thuần.

24/05/2026
14 phút đọc
0 lượt xem
1

Mục tiêu bài học

Sau bài học, bạn sẽ:

  • Trả lời được câu hỏi: "loss function là gì và vì sao mọi training đều cần nó".
  • Viết được công thức MSE, MAE và biết khi nào nên chọn cái nào.
  • Hiểu Huber loss như một cây cầu giữa MSE và MAE.
  • Viết và đọc được Binary Cross-Entropy (BCE) và Categorical Cross-Entropy (CCE).
  • Giải thích vì sao log loss phạt cực nặng khi model "tự tin sai".
  • Hình dung được loss surface như một bề mặt trong không gian tham số — và mục tiêu là đi xuống đáy.
  • Cài đặt MSE, MAE, BCE bằng Python list thuần.

Bài 25 đã trang bị đạo hàm. Bài này định nghĩa thứ cần đạo hàm. Bài 27 sẽ dùng đạo hàm của loss để cập nhật tham số — đó là gradient descent.

2

Vì sao cần loss function

Model là một hàm \( f_\theta(x) \) phụ thuộc tham số \( \theta \) (weight, bias). Với input \( x \), model trả về prediction \( \hat{y} = f_\theta(x) \). Trong dataset, ta có ground truth \( y \) — đáp án đúng.

Để học được, model cần một con số duy nhất cho biết mình đang đoán tệ thế nào. Con số đó là loss. Quy ước:

  • Loss = 0: prediction trùng khớp ground truth.
  • Loss càng lớn: model đoán càng sai.
  • Loss luôn không âm (\( L \ge 0 \)) ở hầu hết các loss function tiêu chuẩn.

Loss function \( L(\hat{y}, y) \) là quy tắc tính loss. Khi tính loss trên cả dataset (gồm \( n \) sample), ta lấy trung bình:

\[ L(\theta) = \frac{1}{n} \sum_{i=1}^{n} L\!\big(f_\theta(x_i), \; y_i\big) \]

\( L(\theta) \) là hàm theo tham số \( \theta \). Dataset cố định, nên thay \( \theta \) là thay loss. Câu hỏi của training trở thành: chọn \( \theta \) nào để \( L(\theta) \) nhỏ nhất?

3

Training là bài toán tối ưu

Toàn bộ machine learning có thể gói gọn trong một dòng:

\[ \theta^{*} = \arg\min_{\theta} \; L(\theta) \]

Đọc: "\( \theta^{*} \) là giá trị tham số làm cho loss nhỏ nhất." Đây là một bài toán tối ưu hoá. Phần lớn lý thuyết và công cụ trong ML xoay quanh việc giải bài toán này một cách thực tế:

  • Loss có thể không lồi (non-convex) — nhiều cực tiểu địa phương.
  • Không gian tham số có thể hàng tỉ chiều (LLM lớn).
  • Không tính được \( L(\theta) \) trên toàn bộ dataset trong 1 bước (quá đắt).

Vì những lý do đó, training thực tế dùng stochastic gradient descent (Bài 27) — đi từng bước nhỏ theo hướng \( -\nabla L \), tính trên một mini-batch chứ không phải toàn dataset.

Lựa chọn loss function là một quyết định thiết kế: nó quy định "model đang được kéo về đâu". Loss khác → tối ưu đến tham số khác → model cư xử khác. Ví dụ MSE kéo prediction về giá trị trung bình (mean), còn MAE kéo về median — cùng dataset, kết quả khác.

4

Loss cho regression — MSE

Trong bài toán regression, output là một số thực — ví dụ giá nhà, nhiệt độ ngày mai, doanh thu tháng. Loss phổ biến nhất là Mean Squared Error:

\[ \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^{2} \]

Mỗi sample đóng góp \( (y_i - \hat{y}_i)^{2} \) — bình phương của residual (sai lệch). Tính chất:

  • Luôn không âm; bằng 0 khi và chỉ khi mọi \( \hat{y}_i = y_i \).
  • Đơn vị là bình phương đơn vị target. Để diễn giải dễ, người ta hay báo RMSE = \( \sqrt{\text{MSE}} \) — cùng đơn vị với \( y \).
  • Liên tục, khả vi mọi nơi, gradient có công thức gọn: \( \frac{\partial L}{\partial \hat{y}_i} = \frac{2}{n}(\hat{y}_i - y_i) \).

Vì bình phương sai lệch, MSE phạt outlier rất nặng. Một sample sai 10 đóng góp 100, trong khi 10 sample sai 1 chỉ đóng góp tổng 10. Hệ quả: model huấn luyện với MSE sẽ ưu tiên giảm sai lệch ở các sample lệch nhất, đôi khi kéo prediction lệch khỏi vùng "dân số chính".

Liên hệ thống kê: tối thiểu MSE tương đương với maximum likelihood dưới giả thiết noise Gaussian. Điều này lý giải vì sao MSE là mặc định trong linear regression cổ điển.

5

Loss cho regression — MAE

Mean Absolute Error thay bình phương bằng giá trị tuyệt đối:

\[ \text{MAE} = \frac{1}{n} \sum_{i=1}^{n} \big| y_i - \hat{y}_i \big| \]

Tính chất:

  • Cùng đơn vị với \( y \) — diễn giải trực tiếp ("trung bình lệch 3.5 đơn vị").
  • Mỗi sample đóng góp tuyến tính theo sai lệch, không bình phương. Outlier không bị khuếch đại — MAE robust với outlier.
  • Khả vi mọi nơi trừ tại \( \hat{y} = y \) (chỗ \( |x| \) gãy). Trong thực tế subgradient được dùng — gradient là \( +1 \) khi \( \hat{y} > y \), \( -1 \) khi \( \hat{y} < y \).

Liên hệ thống kê: tối thiểu MAE tương đương MLE dưới giả thiết noise Laplace. Nghiệm của MAE là median của target có điều kiện, trong khi MSE là mean. Đây cũng là lý do MAE robust: median không bị outlier kéo lệch như mean.

Khi nào chọn MAE: dữ liệu có nhiều outlier hoặc "đuôi nặng" (heavy-tailed), và bạn không muốn model bị các điểm cực trị chi phối.

6

Huber Loss — kết hợp MSE và MAE

Đặt residual \( r = y - \hat{y} \). Huber loss hành xử như MSE khi \( |r| \) nhỏ và như MAE khi \( |r| \) lớn:

\[ L_\delta(r) = \begin{cases} \dfrac{1}{2} r^{2} & \text{nếu } |r| \le \delta \\[4pt] \delta \big( |r| - \dfrac{1}{2} \delta \big) & \text{nếu } |r| > \delta \end{cases} \]

Trong đó \( \delta > 0 \) là ngưỡng do bạn chọn (hyperparameter). Tính chất:

  • Khả vi mọi nơi (kể cả tại \( |r| = \delta \), nhờ hằng số \( \frac{1}{2}\delta \) cộng vào nhánh tuyến tính).
  • Phần trung tâm (\( |r| \le \delta \)) là parabol — gradient mượt như MSE, lợi cho hội tụ.
  • Phần đuôi (\( |r| > \delta \)) là tuyến tính — robust với outlier như MAE.

Huber là lựa chọn an toàn khi data có outlier nhưng bạn vẫn muốn gradient mượt gần đáy. Một biến thể phổ biến trong DL là Smooth L1 (PyTorch SmoothL1Loss) — gần như Huber với \( \delta = 1 \), dùng nhiều trong object detection (RCNN family).

Bài này chỉ giới thiệu Huber như preview. Khi học tới regression robust hoặc object detection, bạn sẽ gặp lại.

7

Trade-off MSE vs MAE

Bảng so sánh ngắn:

                    MSE                MAE
gradient            tuyến tính theo r   ±1 (constant)
smoothness          mượt mọi nơi        gãy tại r=0
outlier             phạt nặng           robust
nghiệm thống kê     mean                median
hội tụ              nhanh khi gần đáy   ổn định nhưng chậm
dạng noise giả định Gaussian            Laplace

Hệ quả thực tế:

  • MSE được ưu tiên khi data sạch, noise gần Gaussian. Gradient \( 2(\hat{y} - y)/n \) tỉ lệ với sai lệch — khi gần đáy, bước cập nhật tự động nhỏ lại, giúp hội tụ mượt.
  • MAE hợp khi data có outlier hoặc noise có đuôi dày. Nhưng gradient luôn \( \pm 1 \) bất kể sai lệch lớn hay nhỏ — gần đáy, optimizer dễ "nhảy quanh" không hội tụ chuẩn nếu learning rate cố định.
  • Huber ăn cả hai: parabol gần đáy + tuyến tính ở đuôi.

Quan sát thực hành: nếu thấy MSE bị một vài outlier kéo training đi sai, thử MAE hoặc Huber. Đây là một trong những knob đơn giản nhất giúp cải thiện model regression.

8

Loss cho classification — Binary Cross-Entropy

Trong bài toán phân loại nhị phân, label \( y \in \{0, 1\} \) và model output \( \hat{y} \in (0, 1) \) là xác suất dự đoán nhãn 1 (thường lấy từ sigmoid). MSE về kỹ thuật vẫn dùng được, nhưng kém — vì khi sigmoid bão hoà, gradient của MSE rất nhỏ, training chậm.

Loss tiêu chuẩn là Binary Cross-Entropy (còn gọi Log Loss):

\[ \text{BCE} = -\frac{1}{n} \sum_{i=1}^{n} \Big[ y_i \log \hat{y}_i + (1 - y_i) \log(1 - \hat{y}_i) \Big] \]

Đọc từng sample: nếu \( y_i = 1 \), chỉ còn \( -\log \hat{y}_i \); nếu \( y_i = 0 \), chỉ còn \( -\log(1 - \hat{y}_i) \). Tóm gọn:

  • Khi label đúng và model tự tin: \( \hat{y}_i \) gần 1 (hoặc gần 0 đúng phía) → \( \log \) gần 0 → loss nhỏ.
  • Khi label đúng nhưng model do dự (\( \hat{y}_i \) gần 0.5): \( -\log 0.5 \approx 0.693 \) — loss vừa phải.
  • Khi label đúng và model tự tin sai (\( \hat{y}_i \) gần 0 mà \( y = 1 \)): \( -\log \epsilon \to +\infty \) — loss bùng nổ.

BCE kết hợp với sigmoid có gradient cực kỳ đẹp: \( \frac{\partial L}{\partial z} = \hat{y} - y \) (với \( z \) là input của sigmoid). Đây cũng là lý do hai cái này luôn đi cùng nhau trong DL. Trong PyTorch, BCEWithLogitsLoss gộp sigmoid + BCE để ổn định số học.

9

Categorical Cross-Entropy cho multi-class

Với \( K \) class (\( K \ge 3 \)), label được biểu diễn one-hot: vector \( \mathbf{y} \) có một phần tử bằng 1 (class đúng), các phần tử còn lại bằng 0. Model output \( \hat{\mathbf{y}} = (\hat{y}_1, \ldots, \hat{y}_K) \) là phân phối xác suất (tổng bằng 1, thường lấy từ softmax).

Categorical Cross-Entropy cho một sample:

\[ \text{CCE} = - \sum_{c=1}^{K} y_c \log \hat{y}_c \]

Vì \( \mathbf{y} \) là one-hot, tổng rút gọn còn duy nhất số hạng tương ứng class đúng:

\[ \text{CCE} = - \log \hat{y}_{\text{class đúng}} \]

Trên cả batch:

\[ L = - \frac{1}{n} \sum_{i=1}^{n} \log \hat{y}_{i, \, c_i} \]

với \( c_i \) là chỉ số class đúng của sample \( i \).

BCE là trường hợp riêng của CCE khi \( K = 2 \). Trong code, BCE và CCE là 2 hàm riêng vì biểu diễn khác nhau (1 số vs vector). Trong PyTorch, nn.CrossEntropyLoss kết hợp softmax + CCE và nhận class index (không cần one-hot) — gọn và ổn định.

10

Vì sao dùng log — phạt model "tự tin sai"

Cross-entropy có dạng \( -\log \hat{p} \) cho xác suất class đúng. Xét hành vi của \( -\log \) trên \( (0, 1] \):

-log(p)

p = 1.00  ->  0.000   (đúng và tự tin → loss = 0)
p = 0.90  ->  0.105
p = 0.50  ->  0.693   (do dự)
p = 0.10  ->  2.303
p = 0.01  ->  4.605
p = 0.001 ->  6.908   (tự tin sai → loss bùng)

Hàm \( -\log p \) tiến tới vô cùng khi \( p \to 0^{+} \). Diễn giải: nếu model gán xác suất gần 0 cho class đúng (tức "rất tự tin rằng class đúng không xảy ra"), loss tăng không giới hạn. Ngược lại, khi model gán xác suất 1 cho class đúng, loss bằng 0.

So sánh với MSE trong classification: nếu \( y = 1 \), \( \hat{y} = 0.01 \), MSE chỉ là \( (1 - 0.01)^{2} = 0.98 \) — gần như cùng mức với \( \hat{y} = 0.5 \) (\( 0.25 \)). Cross-entropy phân biệt 2 trường hợp này rõ rệt: \( -\log 0.01 = 4.605 \) so với \( -\log 0.5 = 0.693 \). Tín hiệu gradient rõ hơn rất nhiều → training hội tụ tốt hơn.

Liên hệ information theory: cross-entropy \( H(p, q) = -\sum p(x) \log q(x) \) đo "trung bình bao nhiêu bit cần để mã hoá phân phối \( p \) bằng codebook tối ưu cho \( q \)". Khi \( q = p \), nó bằng entropy của \( p \) (tối thiểu). Tối thiểu cross-entropy = ép phân phối dự đoán \( \hat{\mathbf{y}} \) khớp phân phối thực \( \mathbf{y} \). Đây cũng là MLE cho mô hình phân loại — cùng một bài toán, hai góc nhìn.

11

Loss surface và tính chất khả vi

Cố định dataset, \( L(\theta) \) là một hàm theo tham số \( \theta \). Nếu \( \theta \) có 2 thành phần, ta có thể vẽ \( L \) như một bề mặt 3D — đó là loss surface. Mục tiêu training: tìm điểm có độ cao thấp nhất.

θ L(θ) local min global min local min

Với mô hình tuyến tính + MSE, loss surface là parabol lồi — chỉ có 1 cực tiểu toàn cục, gradient descent về đó với mọi điểm khởi đầu. Với deep network, loss surface không lồi — có nhiều cực tiểu địa phương, các điểm yên ngựa (saddle point), thung lũng dài hẹp. Tuy nhiên thực nghiệm cho thấy ở high-dimensional, hầu hết cực tiểu địa phương cho giá trị loss gần nhau, và optimizer hiện đại đủ tốt để training hội tụ tốt.

Tính chất tốt cho một loss function:

  • Không âmbằng 0 khi prediction đúng.
  • Liên tục theo prediction — thay đổi nhỏ ở \( \hat{y} \) gây thay đổi nhỏ ở \( L \).
  • Khả vi (gần như mọi nơi) — để gradient descent dùng được. MAE gãy tại 0, BCE bùng tại biên — đều khắc phục được bằng subgradient hoặc clip.
  • Tính được nhanh — loss được gọi mỗi mini-batch, có thể hàng triệu lần.
  • Phù hợp ngữ nghĩa bài toán — không chỉ là số, mà phản ánh "thế nào là sai" theo cách bạn muốn.

Loss như accuracy (\( = \) số sample đoán đúng \( / \) tổng số sample) là không khả vi và bậc thang theo \( \theta \) — không dùng để training được. Accuracy chỉ dùng như metric đánh giá, không dùng làm loss.

12

Bảng tóm tắt — khi nào dùng loss nào

Bài toán                  Loss mặc định          Ghi chú
-----------------------------------------------------------------
Regression, data sạch     MSE                    Mean-Squared-Error
Regression, có outlier    MAE                    Median-friendly
Regression, balance       Huber                  δ tuỳ dataset
Binary classification     Binary Cross-Entropy   Đi cùng sigmoid
Multi-class               Categorical CE         Đi cùng softmax
Multi-label               BCE cho từng class     Mỗi class độc lập
Imbalanced classes        Weighted / Focal Loss  Học khi cần
Ranking, learn-to-rank    Margin / Pairwise      Học khi cần
Embedding / metric        Contrastive / Triplet  Học khi cần

Hai dòng cuối là các loss chuyên biệt — không cần nắm bây giờ. Cứ ghi nhớ: regression → MSE/MAE, classification → cross-entropy. 90% bài toán cơ bản dừng ở đó.

13

Code Python

Cài đặt MSE và MAE bằng list thuần, không dùng NumPy. Cho hàm thật \( f(x) = 2x + 1 \) và model dự đoán \( \hat{f}(x) = 2x + 1.5 \) — sai số hằng \( +0.5 \) ở mọi điểm.

import math


def true_f(x):
    return 2 * x + 1


def predicted_f(x):
    return 2 * x + 1.5


def mse(y_true, y_pred):
    n = len(y_true)
    return sum((yt - yp) ** 2 for yt, yp in zip(y_true, y_pred)) / n


def mae(y_true, y_pred):
    n = len(y_true)
    return sum(abs(yt - yp) for yt, yp in zip(y_true, y_pred)) / n


xs = [-2, -1, 0, 1, 2, 3]
y_true = [true_f(x) for x in xs]
y_pred = [predicted_f(x) for x in xs]

print("x       :", xs)
print("y_true  :", y_true)
print("y_pred  :", y_pred)
print(f"MSE = {mse(y_true, y_pred):.4f}")
print(f"MAE = {mae(y_true, y_pred):.4f}")

Output:

x       : [-2, -1, 0, 1, 2, 3]
y_true  : [-3, -1, 1, 3, 5, 7]
y_pred  : [-2.5, -0.5, 1.5, 3.5, 5.5, 7.5]
MSE = 0.2500
MAE = 0.5000

Hiểu kết quả: sai lệch luôn là \( 0.5 \) ở mọi điểm, nên MAE = 0.5. MSE = \( 0.5^{2} = 0.25 \). Khớp công thức.

Thêm 1 outlier để thấy MSE phạt nặng:

y_pred_outlier = y_pred[:]
y_pred_outlier[-1] = 17  # 1 sample lệch 10

print(f"MSE (có outlier) = {mse(y_true, y_pred_outlier):.4f}")
print(f"MAE (có outlier) = {mae(y_true, y_pred_outlier):.4f}")
MSE (có outlier) = 16.8750
MAE (có outlier) = 1.9167

1 outlier nhảy MSE từ 0.25 lên 16.87 (gần x70), trong khi MAE chỉ từ 0.5 lên 1.92 (x4). MAE robust hơn rõ rệt.

Cài Binary Cross-Entropy đơn giản:

def bce(y_true, y_pred, eps=1e-12):
    n = len(y_true)
    total = 0.0
    for yt, yp in zip(y_true, y_pred):
        yp = min(max(yp, eps), 1 - eps)  # clip để tránh log(0)
        total += yt * math.log(yp) + (1 - yt) * math.log(1 - yp)
    return -total / n


# 1 batch 4 sample
y_true = [1, 0, 1, 0]

# Trường hợp A: model dự đoán đúng và tự tin
pred_A = [0.9, 0.1, 0.95, 0.05]
# Trường hợp B: model do dự (gần 0.5)
pred_B = [0.55, 0.45, 0.6, 0.4]
# Trường hợp C: model TỰ TIN SAI
pred_C = [0.05, 0.9, 0.1, 0.95]

print(f"BCE đúng & tự tin : {bce(y_true, pred_A):.4f}")
print(f"BCE do dự         : {bce(y_true, pred_B):.4f}")
print(f"BCE tự tin sai    : {bce(y_true, pred_C):.4f}")
BCE đúng & tự tin : 0.0793
BCE do dự         : 0.6444
BCE tự tin sai    : 2.6593

Quan sát: BCE phạt "tự tin sai" gấp ~33 lần "đúng và tự tin", và gấp ~4 lần "do dự". Đó là cơ chế kéo model tránh tự tin lệch hướng.

14

Bài tập

  1. Cho y_true = [3, -0.5, 2, 7]y_pred = [2.5, 0.0, 2, 8]. Tính MSE và MAE bằng tay, sau đó kiểm tra bằng code.
  2. Cùng y_true ở câu 1. Thay sample cuối của y_pred thành 30. So sánh tỉ lệ MSE / MAE mới với câu 1. Loss nào bị outlier ảnh hưởng mạnh hơn?
  3. Cho batch 3 sample với y_true = [1, 0, 1], y_pred = [0.8, 0.2, 0.3]. Tính BCE theo công thức, làm tròn đến 4 chữ số.
  4. Vẽ tay đồ thị \( -\log p \) với \( p \in (0, 1] \). Đánh dấu các điểm \( p = 0.1, 0.5, 0.9, 0.99 \). Loss tại các điểm đó bằng bao nhiêu?
  5. Với multi-class \( K = 3 \), cho y_true = [0, 0, 1] (class đúng là class 2 — chỉ số 2). Model dự đoán y_pred = [0.1, 0.2, 0.7]. Tính Categorical Cross-Entropy cho sample này.
  6. Tự cài Huber loss với delta = 1.0 và áp dụng vào y_true, y_pred của câu 2. So sánh giá trị với MSE và MAE.
Đáp án ngắn
  1. Residual: \( [0.5, -0.5, 0, -1] \). MSE = \( (0.25 + 0.25 + 0 + 1)/4 = 0.375 \). MAE = \( (0.5 + 0.5 + 0 + 1)/4 = 0.5 \).
  2. Residual cuối thành \( -23 \). MSE = \( (0.25 + 0.25 + 0 + 529)/4 = 132.375 \) (x353). MAE = \( (0.5 + 0.5 + 0 + 23)/4 = 6.0 \) (x12). MSE bị outlier kéo lệch mạnh hơn rất nhiều.
  3. Mỗi sample: \( -\log 0.8 = 0.2231 \); \( -\log(1 - 0.2) = -\log 0.8 = 0.2231 \); \( -\log 0.3 = 1.2040 \). Trung bình \( = (0.2231 + 0.2231 + 1.2040)/3 \approx 0.5501 \).
  4. \( -\log 0.1 \approx 2.303 \); \( -\log 0.5 \approx 0.693 \); \( -\log 0.9 \approx 0.105 \); \( -\log 0.99 \approx 0.010 \).
  5. CCE = \( -\log 0.7 \approx 0.3567 \). Chỉ class đúng đóng góp.
  6. Residual: \( [0.5, -0.5, 0, -23] \). Với \( \delta = 1 \): 3 residual đầu \( |r| \le 1 \) → dùng \( r^{2}/2 \). Sample cuối \( |r| = 23 > 1 \) → \( 1 \cdot (23 - 0.5) = 22.5 \). Tổng \( = 0.125 + 0.125 + 0 + 22.5 = 22.75 \). Trung bình \( = 5.6875 \) — nằm giữa MAE (6.0) và MSE (132.375). Đúng bản chất "kết hợp".
15

Tóm tắt

  • Loss function \( L(\hat{y}, y) \) đo độ sai. Training = \( \arg\min_{\theta} L(\theta) \).
  • Regression: MSE \( = \frac{1}{n}\sum (y_i - \hat{y}_i)^{2} \) — phạt outlier nặng, nghiệm là mean. MAE \( = \frac{1}{n}\sum |y_i - \hat{y}_i| \) — robust với outlier, nghiệm là median.
  • Huber loss kết hợp MSE (gần đáy) với MAE (đuôi) qua ngưỡng \( \delta \).
  • Binary classification: BCE \( = -\frac{1}{n}\sum [y \log \hat{y} + (1-y) \log(1 - \hat{y})] \). Đi cùng sigmoid.
  • Multi-class: Categorical Cross-Entropy \( = -\sum y_c \log \hat{y}_c \). Đi cùng softmax.
  • Log loss phạt cực mạnh khi model tự tin sai — \( -\log p \to \infty \) khi \( p \to 0 \). Đây là gradient signal rõ hơn MSE trong classification.
  • Tính chất tốt: liên tục, khả vi (hoặc subgradient được), tính nhanh, phù hợp ngữ nghĩa bài toán.
  • Loss surface là bề mặt \( L(\theta) \) trong không gian tham số. Mục tiêu: đi xuống đáy. Bài 27 sẽ dùng \( \nabla L \) (Bài 25) để cập nhật \( \theta \) — đó là gradient descent.