簡介
在前兩篇文章中,我們詳細探討了如何利用取樣資料來估計迴歸曲線。接下來,在本節中,我們將深入討論如何處理分類問題。
章節安排
- 背景介紹
- 數學方法
- 程式實現
背景介紹
線性可分
線性可分是指在多維空間\(\mathbb{R}^D\)中,對於任意兩個類別的資料,總是存在一個超平面,可以將這兩個類別的資料點完全分開。
在二分類問題中,如果資料集是線性可分的,那麼可以找到一個超平面,使得螢幕的一側的所有點屬於一個類別,而另一側的所有點都屬於另一個類別。
設資料集\(D=\{\text{X},\text{y}\}\),其中\(\text{X}\)為輸入特徵向量,\(\text{y}\)為類別標籤。
如果存在一個超平面\(L:z=Xw+b\),使得:
則稱該資料集\(\text{X}\)是線性可分的;其中\(w\)為權重向量,\(b\)為偏置項,\(\text{x}_i\)為第\(i\)組資料,是矩陣\(X\)的第\(i\)行.
在一些情形下,並不是嚴格線性可分的,也就是說不存在一個超平面能夠將所有不同類別的點完全分隔開來。這種情況下,我們可能會考慮使用“寬鬆的線性可分”(Soft Margin)的概念。
在寬鬆線性可分中,定義鬆弛變數\(\xi\),原條件改為
注意到,對於任何一個超平面\(L\),總是存在一個足夠大的鬆弛變數\(\xi\)使得該超平面滿足寬鬆線性可分條件。
因此,一般認為,對於某一個超平面\(L_i\),使得其滿足寬鬆線性條件的最小常數\(\xi_i\)越小,則說明該直線劃分效果越好。
初識啟用函式
超平面\(L\)是一個從\(\mathbb{R}^D\)到\(\mathbb{R}\)的對映(函式)。其值域為\((-\infty,\infty)\)。然而,在實際應用中,通常希望輸出的範圍現在\([0,1]\)之間,以便於解釋和處理。為了實現這一目標,通常會引入啟用函式。
Sigmoid函式是一個經典的啟用函式,因其連續性和較低的計算複雜度而在機器學習中得到了廣泛的應用。Sigmoid 函式的定義如下:
主要特點
-
連續性和可導性:
Sigmoid 函式及其導數都是連續的,這使得它非常適合用於基於梯度下降的最佳化演算法。 -
輸出範圍:
Sigmoid 函式的輸出範圍是 ((0, 1)),這使其在二分類問題中特別有用。它可以將線性組合的輸出轉換為一個機率值,從而更容易解釋模型的預測結果。 -
計算複雜度:
Sigmoid 函式的計算相對簡單,不涉及複雜的數學運算,這有助於提高模型的訓練速度。同時,其導函式可以方便的從原函式計算,即:\(\sigma'(z) = \sigma(z) \cdot (1 - \sigma(z))\)
邏輯迴歸
邏輯迴歸(Logistic Regression)是一種廣泛應用於分類問題的統計模型和機器學習演算法。儘管名稱中包含“迴歸”,但它實際上主要用於解決分類問題,特別是二分類問題。
工作原理
-
線性組合:
首先,邏輯迴歸模型對輸入特徵進行線性組合,也稱對輸入進行評估:\[z = \mathbf{w}^T \mathbf{x} + b \] -
Sigmoid變換:
然後,將評估的結果\(z\)透過Sigmoid函式進行變換:\[\hat{y} = \sigma(z) = \frac{1}{1 + e^{-(\mathbf{w}^T \mathbf{x} + b)}} \]Sigmoid函式的輸出 \(\hat{y}\) 可以解釋為樣本屬於正類的機率。
-
決策邊界:
通常,選擇一個閾值(例如\(0.5\))來決定分類結果:\[y = \begin{cases} 1 & \text{如果 } \hat{y} \geq 0.5 \\ 0 & \text{如果 } \hat{y} < 0.5 \end{cases} \]
損失函式
邏輯迴歸的損失函式通常採用對數損失(Log Loss)或稱交叉熵損失(Cross-Entropy Loss):
其中,\(N\)是樣本數量,\(y_i\)是真實標籤,\(\hat{y}_i\)是預測的機率值。
最佳化
本文將介紹如何採用梯度下降法最佳化邏輯迴歸模型。
在梯度下降法中,核心的部分是計算損失\(\text{LOSS}\)關於引數\(\text{w}\)和\(b\)的梯度,其反應了引數更新的方向和步長。
通常採用鏈式法則計算梯度,以引數\(\text{w}\)為例,有:
梯度下降法中,採用梯度的反方向作為更新方向,其公式為:
其中,\(\lambda\)為學習率。
程式實現
在上一篇文章《機器學習:線性迴歸(下)》中已經講述了超平面\(L\)的實現方法;因此,本文中將討論諸如啟用函式、對數損失等上一章為設計的部分的程式實現。
啟用函式
下述函式用於計算輸入矩陣或向量的每個元素的Sigmoid函式值。
MatrixXd Sigmoid::cal(const MatrixXd& input) {
return input.unaryExpr([](double x) { return 1.0 / (1.0 + exp(-x)); });
}
這段程式碼是一個簡短的函式實現,程式碼解釋如下:
-
input.unaryExpr
:
unaryExpr
是Eigen庫中的一個函式,用於對矩陣或向量的每個元素應用一個給定的單變數函式。在這裡,input
是一個Eigen矩陣或向量。 -
Lambda函式:
[](double x) { return 1.0 / (1.0 + exp(-x)); }
是一個Lambda函式,它定義了一個匿名函式,接受一個double
型別的引數x
,並返回1.0 / (1.0 + exp(-x))
。這個函式實現了Sigmoid函式的計算。
MatrixXd Sigmoid::grad(const MatrixXd& input) {
Matrix temp = input.unaryExpr([](double x) { return 1.0 / (1.0 + exp(-x)); });
return temp.cwiseProduct((1 - temp.array()).matrix());
}
這段程式碼實現了啟用函式的梯度的計算,類似與Sigmoid::cal()
,先計算啟用函式\(\sigma(z)\)的值,再採用逐個元素相乘cwiseProduct
計算(即Hadamard乘積)
對數損失
下述函式分別採用Eigen
的矩陣計算方法,實現了對數損失及對數損失的梯度的計算
double LogisticLoss::computeLoss(const MatrixXd& predicted, const MatrixXd& actual) {
MatrixXd log_predicted = predicted.unaryExpr([](double p) { return log(p); });
MatrixXd log_1_minus_predicted = predicted.unaryExpr([](double p) { return log(1 - p); });
MatrixXd term1 = actual.cwiseProduct(log_predicted);
// MatrixXd term2 = (1 - actual).cwiseProduct(log_1_minus_predicted);
MatrixXd term2 = (1 - actual.array()).matrix().cwiseProduct(log_1_minus_predicted);
double loss = -(term1 + term2).mean();
return loss;
}
MatrixXd LogisticLoss::computeGradient(const MatrixXd& predicted, const MatrixXd& actual) {
MatrixXd temp1 = predicted - actual;
MatrixXd temp2 = predicted.cwiseProduct((1 - predicted.array()).matrix());
return (temp1).cwiseQuotient(temp2);
}
為了便於讀者理解、學習,下面給出了LogisticLoss::computeLoss()
函式的標量方法實現(採用矩陣索引):
double computeLoss(const MatrixXd& predicted, const MatrixXd& actual) {
int n = predicted.rows();
double loss = 0.0;
for (int i = 0; i < n; ++i) {
double p = predicted(i, 0);
double y = actual(i, 0);
loss += -(y * log(p) + (1 - y) * log(1 - p));
}
return loss / n;
}