邏輯迴歸將樣本特徵和樣本發生的機率聯繫起來,用於解決分類問題。python
在最簡單的二分類中,邏輯迴歸裏樣本發生的機率的值域爲 [0, 1],對於線性迴歸 $\hat{y} = \theta^T·x_b$,爲了將 $\hat y$ 映射到值域 [0, 1] 中,引入了 $\sigma$ 函數獲得了機率函數 $\hat p$,即:git
$$ \hat p=\sigma(\theta^T·x_b), \hat p\in[0, 1] $$github
Sigmoid 函數 $\sigma$ 表示爲:$\sigma(t)=\frac{1}{1+e^{-t}}$,圖示以下:算法
當 t > 0 時,$\sigma$ > 0.5;當 t < 0 時,$\sigma$ < 0.5。所以可對二分類的分類方式爲:dom
$$\hat y=\begin{cases} 1, & \hat p \geq 0.5 \\ 0, & \hat p \leq 0.5 \end{cases}; \hat p=\sigma(\theta^T·x_b)=\frac{1}{1+e^{-\theta^T·x_b}}$$函數
若是實際的分類爲1,p 越小時,損失越大;若是實際的分類爲0,p 越大時,損失越大。引入 log 函數表示則爲:測試
$$ -ylog(\hat p)-(1-y)log(1-\hat p) $$ui
當 y=0 時,損失爲 $-log(1-\hat p)$;當 y=1 時,損失爲 $-log(\hat p)$。spa
對於有 m 樣本的數據集 (X, y),損失函數爲:code
$$ J(\theta)=-\frac{1}{m}\sum_{i=1}^my^{(i)}log(\sigma(X_b^{(i)}\theta))+(1-y^{(i)})log(1-\sigma(X_b^{(i)}\theta)) $$
其中:$X_b^{(i)} = (1,x_{1}^{(i)},x_{2}^{(i)},...,x_{n}^{(i)})$;$\theta = (\theta_{0}, \theta_{1}, \theta_{2},..., \theta_{n})^T$。
爲了獲得在損失儘量的小的狀況下的 $\theta$,能夠對 $J(\theta)$ 使用梯度降低法,結果爲:
$$ \nabla J(\theta) = \frac{1}{m}·\begin{pmatrix} \sum_{i=1}^{m}(\sigma(X_b^{(i)}\theta) - y^{(i)}) \\\ \sum_{i=1}^{m}(\sigma(X_b^{(i)}\theta) - y^{(i)})·X_1^{(i)} \\\ \sum_{i=1}^{m}(\sigma(X_b^{(i)}\theta) - y^{(i)})·X_2^{(i)} \\\ \cdots \\\ \sum_{i=1}^{m}(\sigma(X_b^{(i)}\theta) - y^{(i)})·X_n^{(i)} \end{pmatrix} $$
略去了公式的推導過程。
進行向量化處理後結果爲:
$$ \nabla J(\theta) = \frac{2}{m}·X_b^T·(\sigma(X_b\theta)-y) $$
使用 Scikit Learn 的規範將邏輯迴歸的過程封裝到 LogisticRegression 類中。
_init_()
方法首先初始化邏輯迴歸模型,_theta
表示 $\theta$,interception_
表示截距,chef_
表示迴歸模型中自變量的係數:
class LogisticRegression: def __init__(self): self.coef_ = None self.interceiption_ = None self._theta = None
_sigmoid()
方法實現 Sigmoid 函數:
def _sigmoid(self, t): return 1 / (1 + np.exp(-t))
fit()
方法根據訓練數據集訓練模型,J()
方法計算損失 $J\theta$,dJ()
方法計算損失函數的梯度 $\nabla J(\theta)$,gradient_descent()
方法就是梯度降低的過程,X_b 表示添加了 $x_{0}^{(i)}\equiv1$ 的樣本特徵數據:
def fit(self, X_train, y_train, eta=0.01, n_iters=1e4): def J(theta, X_b, y): y_hat = self._sigmoid(X_b.dot(theta)) try: return - np.sum(y * np.log(y_hat) + (1 - y) * np.log(1- y_hat) ** 2) / len(y) except: return float('inf') def dJ(theta, X_b, y): return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) /len(y) def gradient_descent(X_b, y, initial_theta, eta, n_iters=n_iters, epsilon=1e-8): theta = initial_theta i_ters = 0 while i_ters < n_iters: gradient = dJ(theta, X_b, y) last_theta = theta theta = theta - eta * gradient if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon): break i_ters += 1 return theta X_b = np.hstack([np.ones((len(X_train), 1)), X_train]) initial_theta = np.zeros(X_b.shape[1]) self._theta = gradient_descent(X_b, y_train, initial_theta, eta) self.interception_ = self._theta[0] self.coef_ = self._theta[1:] return self
predict_proba()
將傳入的測試數據與訓練好模型後的 $\theta$ 通過計算後返回該測試數據的機率:
def predict_proba(self, X_predict): X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict]) return self._sigmoid(X_b.dot(self._theta))
predict()
方法將通過 predict_proba()
方法獲得的測試數據的機率以 0.5 爲界轉換成類別(0或1):
def predict(self, X_predict): proba = self.predict_proba(X_predict) return np.array(proba >= 0.5, dtype='int')
score()
將測試數據集的預測分類與實際分類進行比較計算模型準確度:
def score(self, X_test, y_test): y_predict = self.predict(X_test) return sum(y_predict == y_test) / len(y_test)
對於 $\hat p=\sigma(\theta^T·x_b)=\frac{1}{1+e^{-\theta^T·x_b}}$,要使 $\hat p=0.5$ 則 $\theta^T·x_b=0$,這就是決策邊界。
假設 X 數據集只有兩個特徵,則由 $\theta_0+\theta_1x_1+\theta_2x_2=0$ 獲得 $x_2$ 和 $x_1$ 的關係爲:
$$ x_2=\frac{-\theta_0-\theta_1x_1}{\theta_2} $$
如圖所示,圖中的點爲只有兩個特徵的數據,縱軸爲特徵 $x_2$,橫軸爲特徵 $x_1$,梯度降低法獲得的 $\theta$ 與上面公式計算後的決策邊界即爲圖中斜線:
對於多項式迴歸,如對 $y=x_1^2+x_2^2-r$ 進行邏輯迴歸,能夠將 $x_1^2$ 看做一個特徵 $z_1$,將 $x_2^2$ 看做一個特徵 $z_2$,Scikit Learn 提供了 PolynomialFeatures 能夠方便的進行轉換。
舉例以下。首先準備數據:
import numpy as np X = np.random.normal(0, 1, size=(200, 2)) y = np.array(X[:, 0] ** 2 + X[:, 1] ** 2 < 1.5, dtype='int')
數據可視化如圖:
使用前面的 LogisticRegression 類進行邏輯迴歸,而且使用 Scikit Learn 的 Pipeline 將多項式特徵、數據歸一化和邏輯迴歸組合在一塊兒:
from LogisticRegression import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler def PolynomailLogisticRegression(degree): return Pipeline([ ('poly', PolynomialFeatures(degree=degree)), ('std_scaler', StandardScaler()), ('log_reg', LogisticRegression()) ])
設定 PolynomialFeatures 處理後獲得的新的特徵數據最高維度爲2,而後 fit()
方法訓練模型:
poly_log_reg = PolynomailLogisticRegression(degree=2) poly_log_reg.fit(X, y)
獲得模型可視化如圖:
Scikit Learn 中的 linear_model 模塊中也提供了邏輯迴歸的算法,同時也封裝了模型正則化相關的內容。
根據正則化中的正則項的不一樣,正則化的方式主要有四種:
Scikit Learn 中的邏輯迴歸算法的模型正則化採用後兩種的方式。
L1 爲 L1正則項,即 $\sum_{i=1}^n|\theta_i|$,LASSO 迴歸使用了L1;L2 爲 L2正則項,即 $\frac{1}{2}\sum_{i=1}^n\theta_i^2$,嶺迴歸使用了L2;
Scikit Learn 的邏輯迴歸算法中的參數 c
設定 C 的大小,參數 penalty
設定使用哪一種正則項(l1 或 l2)。使用方式以下:
from sklearn.linear_model import LogisticRegression def PolynomailLogisticRegression(degree, C, penalty='l2'): return Pipeline([ ('poly', PolynomialFeatures(degree=degree)), ('std_scaler', StandardScaler()), ('log_reg', LogisticRegression(C=C, penalty=penalty)) ]) poly_log_reg = PolynomailLogisticRegression(degree=20, C=0.1, penalty='l1') poly_log_reg.fit(X_train, y_train)
前面說的都是二分類的邏輯迴歸,若是要進行多分類的邏輯迴歸,有 OvR 和 OvO 兩種方式。
OvR(One vs Rest)將多類別簡化成其中一個類別和其他類別爲一個類別這種二分類,所以 n 個類別就進行 n 次分類,對於新的數據,看它在這 n 個分類結果中哪一個分類得分最高即爲哪一個類別。
OvO(One vs One)在多類別中選取兩個類別做爲二分類,所以 n 個類別就進行 $C_n^2$ 次分類,對於新的數據,看它在這 $C_n^2$ 次分類結果中數量最大即爲哪一個類別。
Scikit Learn 的邏輯迴歸算法中的參數 multi_class
用於設定使用 OvR(參數值爲 ovr)仍是 OvO(參數值爲 multinomial),如:
LogisticRegression(multi_class='ovr') LogisticRegression(multi_class='multinomial')
同時 Scikit Learn 中的 multiclass 模塊中也提供了 OneVsRestClassifier(OvR)類和 OneVsOneClassifier(OvO)類,能夠將任意的二分類算法(要求符合 Scikit Learn 規範)應用在這兩個類上完成多分類。使用方式以下:
# OvR from sklearn.multiclass import OneVsRestClassifier ovr = OneVsRestClassifier(LogisticRegression()) ovr.fit(X, y) # OvO from sklearn.multiclass import OneVsOneClassifier ovo = OneVsOneClassifier(log_reg) ovo.fit(X, y)