Trong phần 4, chúng ta gặp phải một vấn đề là giá trị của tham số mô hình \(\vec{\theta}\) liên tục tăng với số bước xuống dốc. Trong bài viết kỳ này, chúng ta sẽ giải quyết vấn đề đó.

Nhắc lại vấn đề

Ở phần 4, chúng ta xác lập hàm mất mát như sau:

\begin{equation*} \mathcal{L}_\vec{\theta} = -\log \mathcal{P}_\vec{\theta} \end{equation*}

Sau đó ta tính dốc \(\nabla \mathcal{L}_\vec{\theta}\) và dùng kỹ thuật xuống dốc để tìm \(\vec{\theta}\) sao cho hàm mất mát đạt giá trị cực tiểu. Càng qua nhiều bước xuống dốc, ta nhận thấy giá trị của \(\vec{\theta}\) liên tục thay đổi mà không có vẻ như sẽ hội tụ tại một điểm cụ thể.

Mô hình chúng ta đặt ra lúc đó là:

\begin{equation*} \hat{y}_\vec{\theta}(\vec{x}) = S(\vec{\theta} \cdot \vec{x}) \end{equation*}

Do đó, nếu \(\vec{\theta} \cdot \vec{x}\) càng lớn thì giá trị của \(\hat{y}\) sẽ càng gần với 1. Đó chính là lý do tại sao qua mỗi bước xuống dốc, giá trị của \(\vec{\theta}\) sẽ càng hướng về phía làm cho \(S(\vec{\theta} \cdot \vec{x})\) càng gần 1.

Điều này có vẻ không cần thiết (vì 0.8 cũng tốt như 0.99999 trong trường hợp này, cả hai đều dẫn đến kết luận là 1), và sẽ dẫn đến mô hình quá khớp (overfitting) với dữ liệu.

Giải quyết vấn đề

Để giải quyết vấn đề này, người ta hay sử dụng kỹ thuật chính quy hóa (regularization) để "phạt" những mô hình quá khớp.

\begin{equation*} \mathcal{L}_{R\vec{\theta}} = \mathcal{L}_\vec{\theta} + \lambda R(\vec{\theta}) \end{equation*}

Có hai điểm chúng ta cần bàn qua:

  1. Toán hạng \(\lambda R(\vec{\theta})\) được thêm vào trong hàm mất mát. Toán hạng này được gọi là toán hạng chính quy (regularization term). Hàm \(R\) là một hàm chỉ phụ thuộc vào mô hình mà không phụ thuộc vào dữ liệu đầu vào của mô hình. Tham số \(\lambda \ge 0\) quyết định sự quan trọng của toán hạng chính quy, \(\lambda = 0\) cũng đồng nghĩa với mô hình không sử dụng toán hạng chính quy.
  2. Toán hạng chính quy thường cũng là một hàm lồi. Điều này giúp cho hàm mất mát vẫn giữ được tính lồi của nó vì tổng của các hàm lồi cũng là một hàm lồi. Do đó chúng ta vẫn có thể sử dụng phương pháp xuống dốc để tìm giá trị tối ưu.

Hai hàm thường được sử dụng trong vai trò của \(R\) là:

  1. \(R(\vec{\theta}) = \Sigma_{i=0}^{n-1} \left| \theta_i \right|\). Hàm này có tên gọi là L1-norm, hay thường được viết \(\left \lVert \vec{\theta} \right \rVert_1\) và còn được biết đến là khoảng cách Mã Nhật Tân (Manhattan distance), khoảng cách bàn cờ. Ta gọi đây là phương pháp chính quy L1 (L1 regularization).
  2. \(R(\vec{\theta}) = \Sigma_{i=0}^{n-1} \theta_i^2\). Hàm này thường được viết \(\left \lVert \vec{\theta} \right \rVert_2^2\) và căn bậc 2 của nó (tức là \(\left \lVert \vec{\theta} \right \rVert_2\)) là L2-norm, hay còn gọi là khoảng cách Ơ-clít (Euclidean distance), khoảng cách chim bay. Ta gọi đây là phương pháp chính quy L2 (L2 regularization).

Vì giá trị tuyệt đối không liên tục ở 0 nên L1-norm không có đạo hàm tại đó. Do đó, loạt bài Máy học phổ thông sẽ không sử dụng L1-norm. Bạn đọc có thể tự thử với sklearn.

Cài đặt

Giả sử chúng ta sẽ sử dụng phương pháp chính quy L2 để "phạt" mô hình khi nó phình to ra, việc đầu tiên là chúng ta sẽ tìm dốc của \(\left \lVert \vec{\theta} \right \rVert_2^2\). Điều này khá dễ dàng, vì \((x^2)^{\prime} = 2x\) nên:

\begin{align*} \nabla R(\vec{\theta}) &= 2 \vec{\theta} \\ \nabla \mathcal{L}_{R\vec{\theta}} &= \nabla \mathcal{L}_\vec{\theta} + 2 \lambda \vec{\theta} \end{align*}

Nhiều tác giả sử dụng \(\frac{\lambda}{2}\) trong toán hạng chính quy nhằm loại bỏ thừa số 2 ở kết quả trên.

Cuối cùng, chúng ta chỉ cần chỉnh lại hàm loss và cách tính grad trong đoạn mã ở phần 4 như sau:

# encoding: utf-8
import math
import random

def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))

def dot(x, theta):
    return sum(x_i * theta_i for x_i, theta_i in zip(x, theta))

def f_prime(theta, x, y):
    return [(sigmoid(dot(x, theta)) - y) * x_i for x_i in x]

def loss(theta, lambda_, xs, ys):
    s = 0.0
    for i in range(len(ys)):
        x = xs[i]
        y = ys[i]
        y_hat = sigmoid(dot(x, theta))
        s -= y * math.log(y_hat) + (1 - y) * math.log(1 - y_hat)
    s += lambda_ * sum(theta_i**2 for theta_i in theta)
    return s

# Bảng đầu vào.
xs = (
    (1, 0, 0),
    (1, 0, 1),
    (1, 1, 0),
    (1, 1, 1),
)
ys = (0, 0, 0, 1)
# Khởi tạo theta ngẫu nhiên.
theta = [random.random() * 2 - 1 for _ in range(len(xs[0]))]
# Định tốc độ học.
alpha = 0.5
lambda_ = 0.01

# Lặp xuống dốc.
for _ in range(10000):
    # Đạo hàm riêng đối với mỗi dòng đầu vào.
    grad = [f_prime(theta, x, y) for x, y in zip(xs, ys)]
    # Lấy tổng các đạo hàm riêng lại với nhau.
    grad = [sum(g[c] for g in grad) for c in range(len(xs[0]))]
    # Chính quy hóa.
    grad = [g + 2 * lambda_ * theta_i for theta_i, g in zip(theta, grad)]
    # Cập nhật theta.
    theta = [theta_i - alpha * grad_i for theta_i, grad_i in zip(theta, grad)]

# In kết quả.
print('Theta', theta)
print('Loss', loss(theta, lambda_, xs, ys))
for x in xs:
    print(x[1:], 1 if sigmoid(dot(theta, x)) > 0.5 else 0)

Khi thực thi đoạn mã này, ta nhận được kết quả sau:

Theta [-4.803833560502123, 3.0629812059187365, 3.0629812059187365]
Loss 0.9860477866020279
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 1

Nếu thay số bước xuống dốc thành 500, ta nhận được:

Theta [-4.803822026262376, 3.0629733507737535, 3.0629733507737535]
Loss 0.9860477866082961
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 1

Điều này chứng tỏ tham số mô hình \(\vec\theta\) đã không còn thay đổi theo số bước xuống dốc như trong bài viết trước.

Tuy nhiên, ta lại có thêm một tham số tùy chỉnh \(\lambda\). Nếu giá trị của tham số này quá lớn, mô hình sẽ không khớp được với dữ liệu, nếu tham số này quá nhỏ, mô hình có thể sẽ quá khớp. Ví dụ, nếu ta thay lambda_ = 1 trong đoạn mã trên, mô hình của chúng ta sẽ không còn khớp với trường hợp (1, 1) nữa:

Theta [-0.355517379160847, 0.06436621020883249, 0.06436621020883249]
Loss 2.594999551083938
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 0

Tóm tắt

Bài viết này giải quyết vấn đề về tham số mô hình liên tục thay đổi theo số bước xuống dốc mà chúng ta đã nhận ra trong bài số 4 trước đây. Phương pháp chính quy hóa mô hình có mục đích làm giảm sự quá khớp của mô hình với dữ liệu huấn luyện. Chúng ta nhắc đến hai hàm phổ dụng là L1-norm và L2-norm. Dựa vào công thức của L2-norm, chúng ta đã sửa lại mã của bài viết trước đây và khắc phục tình trạng đã nêu. Qua đó, chúng ta cũng thấy sự ảnh hưởng của tham số \(\lambda\). Một điều quan trọng khác mà chúng ta cần nhớ là hàm "phạt" là một sự sáng tạo, không chỉ dừng lại ở L1-norm, hay L2-norm.

Bài viết này cũng kết thúc loạt bài về máy học có giám sát. Trong bài viết kế chúng ta sẽ chuyển sang phương pháp học không giám sát.

Tài liệu đọc thêm

  1. Bài giảng về chính quy hóa của Andrew Ng trong môn Machine Learning tại https://www.coursera.org/learn/machine-learning.
  2. Bài 15: Overfitting ở trang mạng Machine Learning cơ bản của Vũ Hữu Tiệp ở Đại học bang Pennsylvania (Pennsylvania State University), Hoa Kỳ.