Giới thiệu về Hồi Quy Logistic
Hồi quy Logistic là một thuật toán học máy có giám sát được sử dụng rộng rãi cho các bài toán phân loại, đặc biệt là phân loại nhị phân (chẳng hạn như 0 hoặc 1, "có" hoặc "không", "thành công" hoặc "thất bại"). Mặc dù tên gọi có từ "hồi quy", đây thực chất là một phương pháp phân loại dựa trên việc ước tính xác suất.
Hàm Sigmoid
Để biến đổi đầu ra tuyến tính thành một giá trị xác suất nằm trong khoảng (0, 1), Hồi quy Logistic sử dụng hàm Sigmoid (còn được gọi là hàm logistic). Công thức của hàm Sigmoid được định nghĩa như sau:
\[ \sigma(z) = \frac{1}{1 + e^{-z}} \]
Trong đó \(z\) là một giá trị số thực. Các đặc điểm chính của hàm Sigmoid bao gồm:
- Khi \(z = 0\), giá trị của hàm là \(0.5\).
- Khi \(z\) tiến về âm vô cùng, giá trị của hàm tiến về \(0\).
- Khi \(z\) tiến về dương vô cùng, giá trị của hàm tiến về \(1\).
Đây là một triển khai cơ bản của hàm Sigmoid trong Python:
import math
def calculate_sigmoid(input_value):
"""Tính toán giá trị của hàm sigmoid cho một đầu vào."""
return 1.0 / (1.0 + math.exp(-input_value))
Chức năng của hàm Sigmoid trong mô hình này là ánh xạ kết quả tuyến tính từ các đặc trưng đầu vào sang một xác suất. Nếu xác suất dự đoán này vượt quá một ngưỡng nhất định (thường là 0.5), mẫu được phân loại là lớp dương (ví dụ: 1); ngược lại, là lớp âm (ví dụ: 0).
Mô hình Phân loại Hồi quy Logistic
Mô hình Hồi quy Logistic hoạt động bằng cách kết hợp tuyến tính các đặc trưng đầu vào, sau đó áp dụng hàm Sigmoid cho tổng có trọng số này. Cụ thể, đối với một vector đặc trưng \(x = [x_0, x_1, \ldots, x_n]\) (với \(x_0 = 1\) thường là đặc trưng giả cho hệ số chặn) và một vector trọng số \(w = [w_0, w_1, \ldots, w_n]\), đầu ra tuyến tính được tính là \(z = w^T x = w_0x_0 + w_1x_1 + \ldots + w_nx_n\). Xác suất để một mẫu thuộc về lớp 1 được dự đoán là \(P(y=1|x) = \sigma(z)\). Nếu \(P(y=1|x) \ge 0.5\), mẫu được phân loại là lớp 1; ngược lại, là lớp 0.
Tìm kiếm Hệ số Hồi quy tối ưu
Mục tiêu chính của việc huấn luyện một mô hình Hồi quy Logistic là tìm ra bộ hệ số (trọng số) tối ưu \(w\) sao cho mô hình có thể dự đoán xác suất và phân loại dữ liệu một cách chính xác nhất. Một trong những thuật toán phổ biến để đạt được điều này là Thuật toán Tăng cường Độ dốc (Gradient Ascent).
Độ dốc (Gradient)
Độ dốc của một hàm đa biến là một vector chứa các đạo hàm riêng theo từng biến, chỉ ra hướng mà hàm tăng nhanh nhất. Đối với một hàm mục tiêu \(f(w)\) mà chúng ta muốn tối đa hóa, độ dốc được ký hiệu là \(\nabla f(w)\).
Công thức cập nhật trọng số trong thuật toán Tăng cường Độ dốc là:
\[ w_{mới} = w_{cũ} + \alpha \nabla f(w_{cũ}) \]
Trong đó:
- \(w\) là vector trọng số của mô hình.
- \(\alpha\) (alpha) là tốc độ học (learning rate), kiểm soát kích thước của bước nhảy trong mỗi lần cập nhật trọng số.
- \(\nabla f(w)\) là độ dốc của hàm mục tiêu (thường là hàm log-likelihood trong Hồi quy Logistic) tại điểm \(w\) hiện tại.
Quá trình này lặp đi lặp lại, di chuyển trọng số theo hướng độ dốc để tìm điểm cực đại, cho đến khi đạt được một tiêu chí dừng nào đó, ví dụ như số lần lặp tối đa hoặc khi sự thay đổi của \(w\) trở nên không đáng kể.
Các thuật toán huấn luyện Hồi Quy Logistic
Tăng cường Độ dốc (Gradient Ascent) để tìm hệ số tối ưu
Thuật toán Tăng cường Độ dốc (Gradient Ascent) là một phương pháp tối ưu hóa mà chúng ta sử dụng để tìm các hệ số \(w\) tối ưu bằng cách di chuyển theo hướng độ dốc của hàm log-likelihood. Đây là cách triển khai cơ bản của Gradient Ascent:
import numpy as np
def train_gradient_ascent(feature_data_matrix, labels_vector, num_iterations=500, alpha_rate=0.001):
"""
Huấn luyện mô hình Hồi quy Logistic bằng thuật toán Tăng cường Độ dốc (Gradient Ascent).
Args:
feature_data_matrix (np.array): Ma trận dữ liệu đặc trưng, mỗi hàng là một mẫu.
Cột đầu tiên nên là 1 cho hệ số chặn.
labels_vector (np.array): Vector nhãn lớp (0 hoặc 1) tương ứng với các mẫu.
num_iterations (int): Số lần lặp để cập nhật trọng số.
alpha_rate (float): Tốc độ học của thuật toán.
Returns:
np.array: Vector trọng số tối ưu sau khi huấn luyện.
"""
features = np.array(feature_data_matrix)
targets = np.array(labels_vector).reshape(-1, 1) # Chuyển thành vector cột
num_samples, num_features = features.shape
# Khởi tạo vector trọng số ban đầu bằng 1
coefficients = np.ones((num_features, 1))
for _ in range(num_iterations):
# Tính toán đầu ra dự đoán (xác suất) bằng hàm sigmoid
linear_combination = np.dot(features, coefficients)
predicted_probabilities = 1.0 / (1.0 + np.exp(-linear_combination))
# Tính toán sai số giữa nhãn thực tế và xác suất dự đoán
prediction_error = targets - predicted_probabilities
# Cập nhật trọng số theo hướng độ dốc
# Công thức cập nhật: w = w + alpha * X.T * (y - h(Xw))
coefficients = coefficients + alpha_rate * np.dot(features.T, prediction_error)
return coefficients
```
Mặc dù hiệu quả, cách tiếp cận này sử dụng toàn bộ tập dữ liệu trong mỗi lần cập nhật trọng số, điều này có thể tốn kém về mặt tính toán và chậm đối với các tập dữ liệu rất lớn.
Tăng cường Độ dốc Ngẫu nhiên (Stochastic Gradient Ascent - SGA)
Để giải quyết vấn đề hiệu suất của Gradient Ascent với dữ liệu lớn, Thuật toán Tăng cường Độ dốc Ngẫu nhiên (Stochastic Gradient Ascent - SGA) đã được đề xuất. SGA chỉ sử dụng một mẫu dữ liệu tại một thời điểm để cập nhật trọng số, giúp quá trình huấn luyện nhanh hơn đáng kể. Mặc dù đường dẫn hội tụ có thể "ồn ào" hơn do nhiễu từ từng mẫu riêng lẻ, SGA thường đạt được hội tụ nhanh hơn trong thực tế.
import numpy as np
def train_stochastic_gradient_ascent(input_features, output_labels, initial_learning_rate=0.01):
"""
Huấn luyện mô hình Hồi quy Logistic bằng thuật toán Tăng cường Độ dốc Ngẫu nhiên.
Args:
input_features (np.array): Mảng dữ liệu đặc trưng.
output_labels (list): Danh sách nhãn lớp (0 hoặc 1) cho mỗi mẫu.
initial_learning_rate (float): Tốc độ học ban đầu.
Returns:
np.array: Vector trọng số đã huấn luyện.
"""
data_points = np.array(input_features)
num_samples, num_features = data_points.shape
# Khởi tạo vector trọng số ban đầu
current_weights = np.ones(num_features)
for i in range(num_samples):
# Tính toán đầu ra tuyến tính cho mẫu hiện tại
linear_output = np.dot(data_points[i], current_weights)
# Áp dụng hàm sigmoid để có xác suất dự đoán
predicted_probability = 1.0 / (1.0 + np.exp(-linear_output))
# Tính toán sai số cho mẫu hiện tại
sample_error = output_labels[i] - predicted_probability
# Cập nhật trọng số dựa trên sai số của mẫu hiện tại
current_weights = current_weights + initial_learning_rate * data_points[i] * sample_error
return current_weights
```
Cải tiến Thuật toán Tăng cường Độ dốc Ngẫu nhiên
Để tối ưu hóa hơn nữa SGA, đặc biệt là để giảm các dao động và tăng tốc độ hội tụ, có thể áp dụng các cải tiến như điều chỉnh động tốc độ học và lấy mẫu ngẫu nhiên (chứ không phải tuần tự) các điểm dữ liệu. Việc điều chỉnh tốc độ học giảm dần theo thời gian giúp mô hình tinh chỉnh trọng số tốt hơn khi tiến gần đến điểm tối ưu, trong khi lấy mẫu ngẫu nhiên giúp tránh các chu kỳ và đảm bảo mô hình học từ nhiều khía cạnh khác nhau của dữ liệu.
import numpy as np
import random
def train_improved_stochastic_gradient_ascent(feature_data, class_targets, num_epochs=150):
"""
Huấn luyện mô hình Hồi quy Logistic bằng SGA cải tiến.
Tốc độ học thay đổi động và chọn mẫu ngẫu nhiên không lặp lại trong mỗi epoch.
Args:
feature_data (np.array): Mảng dữ liệu đặc trưng.
class_targets (list): Danh sách nhãn lớp (0 hoặc 1).
num_epochs (int): Số kỷ nguyên (lần lặp qua toàn bộ tập dữ liệu).
Returns:
np.array: Vector trọng số đã huấn luyện.
"""
input_samples = np.array(feature_data)
num_observations, num_features = input_samples.shape
# Khởi tạo vector trọng số
final_weights = np.ones(num_features)
for i in range(num_epochs):
# Tạo danh sách chỉ mục và xáo trộn để chọn mẫu ngẫu nhiên không lặp lại trong epoch
shuffled_indices = list(range(num_observations))
random.shuffle(shuffled_indices)
for j in range(num_observations):
# Tốc độ học giảm dần để hội tụ tốt hơn và tránh dao động
# Công thức điều chỉnh: alpha = C1 / (C2 + epoch + index_in_epoch) + C3
current_alpha = 4.0 / (1.0 + i + j) + 0.01
# Chọn một chỉ mục mẫu từ danh sách đã xáo trộn
sample_index = shuffled_indices[j]
# Tính toán đầu ra tuyến tính và xác suất dự đoán cho mẫu được chọn
linear_combination = np.dot(input_samples[sample_index], final_weights)
predicted_prob = 1.0 / (1.0 + np.exp(-linear_combination)) # Hàm sigmoid
# Tính toán sai số
sample_error = class_targets[sample_index] - predicted_prob
# Cập nhật trọng số
final_weights = final_weights + current_alpha * input_samples[sample_index] * sample_error
return final_weights
```