phần trước, chúng ta đã tự mình lập nên hàm mất mát cho phương pháp hồi quy hậu cần. Tuy rằng chúng ta có thể tìm ra kết quả đúng, chúng ta cũng gặp một số vấn đề quan trọng. Bài viết này sẽ bàn về các vấn đề đó.

Bàn về hàm mất mát và kỹ thuật xuống dốc

Dưới đây là hàm mất mát mà chúng ta lập ra ở bài viết trước.

\begin{equation*} \mathcal{L}(\vec{\theta}) = \sum_{i=0}^3 \left[ y^{(i)} + (1 - 2y^{(i)}) \times S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) \right] \end{equation*}

Chúng ta đã sử dụng kỹ thuật xuống dốc để tìm điểm cực tiểu của hàm mất mát này. Tuy nhiên, hàm mất mát như trên không phải là hàm lồi (convex function, hàm có đạo hàm bậc hai luôn dương) trong miền xác định. Vì thế kỹ thuật xuống dốc có thể sẽ rơi vào cực tiểu nội bộ. Đó là lý do vì sao trong bài viết trước có lúc ta nhận được kết quả tốt, có lúc ta nhận được kết quả xấu. Tất cả phụ thuộc vào điểm khởi đầu khi ta thực hiện việc xuống dốc.

Điều tôi muốn nhấn mạnh ở đây là hàm mất mát chúng ta đã lập ra trong bài viết trước hoàn toàn hợp lý, không có gì sai sót cả. Vấn đề là ở việc tìm điểm cực tiểu của hàm đó.

Thiết lập hàm mất mát theo ý nghĩa xác suất

Để việc tìm điểm cực tiểu của hàm mất mát dễ hơn với kỹ thuật xuống dốc, chúng ta sẽ cần thiết lập một hàm mất mát phù hợp hơn.

Xác lập ý nghĩa xác suất

Nhớ lại rằng giá trị của biến phụ thuộc bị giới hạn trong khoảng \([0, 1]\), và nếu \(y = 1\) thì \(y\) không thể có giá trị \(0\) và ngược lại. Nhận xét này dẫn đến một ý tưởng là ta có thể giả sử giá trị phỏng đoán \(\hat{y}\) thể hiện xác suất mà giá trị thực \(y = 1\) khi giá trị đầu vào là \(\vec{x}\) và tham số mô hình là \(\vec{\theta}\).

Nói một cách khác, đây là giả sử của chúng ta:

\begin{align*} \hat{y}_\vec{\theta}(\vec{x}) &= S(\vec{\theta} \cdot \vec{x}) \\ P_\vec{\theta}(y=1 \mid \vec{x}) &= \hat{y}_\vec{\theta}(\vec{x}) \\ P_\vec{\theta}(y=0 \mid \vec{x}) &= 1 - P_\vec{\theta}(y=1 \mid \vec{x}) = 1 - \hat{y}_\vec{\theta}(\vec{x}) \end{align*}

Xác lập hàm mất mát

Với dữ liệu đầu vào ở dòng \(i\), xác suất của biến phụ thuộc trùng với giá trị thực là:

\begin{equation*} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) = \begin{cases} \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right) & \mbox{khi } y^{(i)} = 1 \\ 1 - \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right) & \mbox{khi } y^{(i)} = 0 \\ \end{cases} \end{equation*}

Với một chút sáng tạo, chúng ta có thể gộp hai trường hợp này làm một:

\begin{align*} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) &= \begin{cases} \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right)^{y^{(i)}} & \mbox{khi } y^{(i)} = 1 \\ 1 - \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right)^{1 - y^{(i)}} & \mbox{khi } y^{(i)} = 0 \\ \end{cases} \\ &= \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) ^ {y^{(i)}} \times \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) ^ {1 - y^{(i)}} \end{align*}

Từ đó dẫn đến xác suất của mô hình hồi quy khớp với toàn bộ bảng dữ liệu đầu vào:

\begin{equation*} \mathcal{P}_\vec{\theta} = \prod_{i=0}^{3} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \end{equation*}

Mục tiêu của chúng ta chính là tìm điểm cực đại của \(\mathcal{P}_\vec{\theta}\). Hàm \(\mathcal{P}_\vec{\theta}\) còn được biết đến với tên gọi hàm hợp lý (likelihood).

Trong miền xác định \([0, 1]\), hàm \(\log \circ f\) đồng biến với \(f\). Điều đó cho phép ta tìm điểm cực đại của \(\mathcal{P}_\vec{\theta}\) theo \(\log \mathcal{P}_\vec{\theta}\).

\begin{align*} \log \mathcal{P}_\vec{\theta} &= \log \left[ \prod_{i=0}^{3} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \right] \\ &= \sum_{i=0}^{3} \log P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \\ &= \sum_{i=0}^{3} \log \left[ \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) ^ {y^{(i)}} \times \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) ^ {1 - y^{(i)}} \right] \\ &= \sum_{i=0}^{3} \left[ y^{(i)} \times \log \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) + \left( 1 - y^{(i)} \right) \times \log \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \right] \\ \end{align*}

Có hai nhận xét bên lề mà tôi muốn bạn đọc chú ý với kỹ thuật chuyển qua sử dụng \(\log \circ f\) thay cho \(f\):

  1. Nếu \(f\) là một tích, \(\log \circ f\) sẽ là một tổng.
  2. Việc chuyển đổi đó dẫn đến việc tính toán dễ dàng hơn, bởi vì tích của các số trong \([0, 1]\) sẽ dẫn đến một số cực kỳ nhỏ, khó có thể được biểu diễn dễ dàng trong máy tính.

Nên nhớ rằng trong miền xác định \([0, 1]\), hàm \(\log\) là hàm lõm. Tích của một hàm với một số không âm không làm thay đổi tính lồi/lõm của hàm. Tổng của các hàm lõm cũng sẽ là một hàm lõm. Do đó, \(\log \mathcal{P}_\vec{\theta}\) là một hàm lõm, và \(-\log \mathcal{P}_\vec{\theta}\) dĩ nhiên sẽ là một hàm lồi!

Để tìm điểm cực đại của \(\log \mathcal{P}_\vec{\theta}\), chúng ta sẽ tìm điểm cực tiểu của \(-\log \mathcal{P}_\vec{\theta}\). Đó chính là hàm mất mát của chúng ta!

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

Để đơn giản việc tìm dốc của hàm mất mát, ta sẽ tìm đạo hàm bậc nhất của một dòng đầu vào \(\left( \vec{x^{(i)}}, y^{(i)} \right)\) cụ thể:

\begin{align*} & \cfrac{\partial}{\partial \vec{\theta}} \left[ -y^{(i)} \times \log \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) - \left( 1 - y^{(i)} \right) \times \log \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \right] \\ = & \left[ -y^{(i)} \cfrac{1}{\hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)} + \left( 1 - y^{(i)} \right) \cfrac{1}{1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)} \right] \cfrac{\partial}{\partial \vec{\theta}} \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \\ = & \left[ \cfrac{-y^{(i)} \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) + \left( 1 - y^{(i)} \right) \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)} {\hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right)} \right] \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \cfrac{\partial}{\partial \vec{\theta}} \vec{\theta} \cdot \vec{x^{(i)}} \\ = & \left[ \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}} \\ = & \left[ S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}} \end{align*}

Sau đó, ta có thể ráp lại vào hàm mất mát để tính dốc như sau:

\begin{equation*} \nabla \mathcal{L}_\vec{\theta} = \sum_{i=0}^{3} \left[ S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}} \end{equation*}

Cài đặt với hàm mất mát mới

# 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, 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)
    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

# 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]))]
    # 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, 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 nhiều lần, chúng ta đều nhận được kết quả tương tự như sau:

('Theta', [-20.37171666579356, 13.468848927513633, 13.46884892751371])
('Loss', 0.003415244070062777)
((0, 0), 0)
((0, 1), 0)
((1, 0), 0)
((1, 1), 1)

Điều này cho thấy rằng kỹ thuật xuống dốc với hàm mất mát mới đã có thể hội tụ tốt hơn so với hàm mất mát trong bài viết trước.

Tuy nhiên, nếu chúng ta thay đổi số bước xuống dốc, chúng ta thấy rằng tham số mô hình vẫn trở nên lớn hơn khi số bước xuống dốc tăng lên.

Tóm tắt

Bài viết kỳ này giải thích tại sao hàm mất mát đã lập trong bài viết trước không hội tụ tốt. Từ đó, bài viết này giới thiệu tới bạn đọc một hàm mất mát khác phù hợp hơn với kỹ thuật xuống dốc. Đau lòng mà nói, việc xác lập nên hàm mất mát hiệu quả phụ thuộc rất nhiều vào trực giác tinh tế, kinh nghiệm phong phú, và sức sáng tạo cao của người làm việc. Có bao nhiêu người nghĩ đến việc thiết lập hàm mất mát trong ngữ cảnh của xác suất? Trong bài viết kỳ sau, chúng ta sẽ khảo sát sự ảnh hưởng của dữ liệu đầu vào \(y \in \{ 1, -1 \}\) (thay vì \(y \in \{ 0, 1 \}\)) đến hàm mất mát như là một ví dụ khác về các tố chất đã nêu.

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

  1. Tài liệu giảng trong môn CS229 Machine Learning do Dan Boneh và Andrew Ng dạy ở đại học Stanford.
  2. Bài 10: Logistic Regression ở 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ỳ.