Logistic regression 是一個資料分析技術,被用來做分類(classification)。它大概是分類演算法中最簡單的模型。因此,很適合當初學者的第一個分類演算法。
Table of Contents
邏輯斯回歸(Logistic Regression)
S 函數(Sigmoid Function)
在介紹 logistic regression 之前,我們先來了解 sigmoid function。Sigmoid function 如下,它可以將 z 轉換成 0 和 1 之間的值。
如下圖所示,sigmoid function 的兩端會非常接近 0 和 1。
以下的程式碼中,sigmoid()
實作了 sigmoid function。
def sigmoid(z): """ Sigmoid function Parameters ---------- z: (ndarray (m,)) or (scalar) m is the number of samples Returns ------- (ndarray (m,)) or (scalar) """ return 1 / (1 + np.exp(-z))
邏輯斯回歸模型(Logistic Regression Model)
Logistic regression 是用於分類的 regression,而談到分類時,自然就與機率有關。Sigmoid function 的輸出值介於 0 到 1 之間,符合機率必須介於 0% 到 100% 之間的基本要求。Logistic regression 的 model 是,先將 x
帶入到 linear regression model,得到 y
。再將 y
帶入到 sigmoid function,得到一個介於 0 和 1 的值。Logistic regression 的 model function 如下。
假設 x
是腫瘤的大小,當 y
是 0 表示非惡性腫瘤,當 y
是 1 表示惡性腫瘤。如果,將 x
帶入 logistic regression model 並得到 0.7,這表示有 70% 的機率 y
是 1。因為 y
是 0 的機率加上 y
是 1 機率總和為 1,所以當有 70% 的機率 y
是 1,那代表有 30% 的機率 y
是 0。
以下的程式碼中,f_wb()
實作了 logistic regression model。
def f_wb(x, w, b): """ Logistic regression model Parameters ---------- x: (ndarray (m,)) or (scalar) m is the number of samples w: (scalar) b: (scalar) Returns ------- (ndarray (m,)) or (scalar) """ return sigmoid(w * x + b)
決策邊界(Decision Boundary)
剛剛提到 logistic regression 的輸出值是介於 0 到 1 之間,其代表著 y
是 1 的機率。因此,我們需要設定一個 threshold。當 fw,b
大於等於 threshold 時,ŷ
為 1,反之當 fw,b
小於 threshold 時,ŷ
為 0。
當 z
大於等於 0 時,fw,b
也就是 g(z)
就會大於等於 threshold 0.5。所以,z
為 0 的左邊是 ŷ
為 0,右邊是 ŷ
為 1。此界線稱為 decision boundary。
下面的例子中,我們可以透過 z = 0
來計算出 decision boundary。
成本函數(Cost Function)
概似函數與最大概似估計法(Likelihood Function and Maximum Likelihood Estimation)
Likelihood function 是一種統計方法,用於描述在給定參數下觀測到的數據出現的可能性。假設我們擲 4 次硬幣,結果如下,其中每次擲正面的機率為 。
xi | Result | Probability |
---|---|---|
x1 | Font | |
x2 | Font | |
x3 | Front | |
x4 | Back | 1 – |
根據上面的結果,其 likelihood function 如下。 是條件機率(conditional probability),指在 機率下 xi
的機率。
假設擲正面的機率 是 0.6,則 likelihood function 為 0.0846。
得到 likelihood function 後,對 likelihood function 做微分並令其等於 0 來算出其最大值的點。最後,算出擲正面的機率 為 。這就是 maximum likelihood estimation。
成本函數與損失函數(Cost Function and Loss Function)
Cost function 是用來衡量 fw,b
的準確率。有了 cost function,我們就可以衡量調整後的 w
和 b
是否比原來的好。那我們要用什麼樣的 cost function 來衡量 fw,b
呢?
以之前擲硬幣的例子來看,每一次擲硬幣的機率如下:
為了方便,我們可以將 的兩個式子合併成一個式子,如下:
那擲 n
次硬幣時的 likelihood function 如下。依據 maximum likelihood estimation,我們可以利用對 likelihood function 求取最大值,來作為 cost function。
由於式子中是乘法不利計算,我們先將它化簡一下,對式子兩邊取對數。
式子中的 可展開如下。
最後,我們的 likelihood function 變成對數概似函數(log-likelihood function)如下。
依據 maximum likelihood estimation,我們必須要對 likelihood function 求取最大值。然而,慣例上,我們習慣對 cost function 求取最小值。因此,我們對 likelihood function 乘上 -1,這樣就可以改為求取最小值。
在 logistic regression 中,機率 會由 fw,b
算出,所以將 fw,b
代入 就可以得出 logistic regression 的 loss function。
最終,logistic regression 的 cost function 如下:
以下的程式碼中,compute_cost()
實作了 cost function J(w,b)
。
def compute_cost(x, y, w, b): """ Compute cost Parameters ---------- x: (ndarray (m,)) m is the number of samples y: (ndarray (m,)) w: (scalar) b: (scalar) Returns ------- (scalar) """ m = x.shape[0] y_hat = f_wb(x, w, b) cost = 1 / m * np.sum(-y * np.log(y_hat) - (1 - y) * np.log(1 - y_hat)) return cost
梯度下降(Gradient Descent)
Gradient descent 是一個最佳化演算法,用來找到一個函數的局部值。讀者可以先參考以下的。linear regression 來了解 gradient descent。
以下為 logistic regression 的 gradient descent 演算法。首先,先隨機選一組 w
和 b
,或直接設為零。Cost function 的導數乘上一個 learning rate 會是 w
和 b
要下降的梯度。此外,我們還會給定一個 iteration 次數,代表執行 gradient descent 的次數。因此,我們也許不會得到最佳的 w
和 b
。
J(w, b)
對 w
和 b
的偏導數(partial derivative)計算方法如下:
Logistic regression 和 linear regression 的 cost function 的導數計算方式看起來一樣,但注意他們的 fw,b
不一樣。
以下的程式碼實作 J(w,b)
對 w
和 b
的偏導數計算。
def compute_gradient(x, y, w, b): """ Compute the gradient for logistic regression Parameters ---------- x: (ndarray (m,)) m is the number of samples y: (ndarray (m,)) w: (scalar) b: (scalar) Returns ------- dj_dw: (scalar) dj_db: (scalar) """ m = x.shape[0] dj_dw = 1 / m * np.sum((f_wb(x, w, b) - y) * x) dj_db = 1 / m * np.sum(f_wb(x, w, b) - y) return dj_dw, dj_db
以下的程式碼實作 gradient descent。其中參數 alpha
是 learning rate,而 epochs
是 iteration 次數。
def perform_gradient_descent(x, y, w_init, b_init, alpha, epochs): """ Perform gradient descent Parameters ---------- x: (ndarray (m)) m is the number of samples y: (ndarray (m,)) w_init: (scalar) b_init: (scalar) alpha: (float) epochs: (int) Returns ------- w: (scalar) b: (scalar) """ w = w_init b = b_init for i in range(epochs): dj_dw, dj_db = compute_gradient(x, y, w, b) w -= alpha * dj_dw b -= alpha * dj_db return w, b
範例
接下來,我們會用一個例子來講解如何使用 logistic regression。以下的範例中有 20 個數值,小於 10 給予標籤 0,大於等於 10 的給予標籤 1。
x_train = np.array([1, 0, 17, 8, 13, 19, 15, 10, 8, 7, 3, 6, 17, 3, 4, 17, 11, 12, 16, 13]) y_train = np.int_(x_train[:] >= 10) plt.scatter(x_train[y_train == 0], y_train[y_train == 0], 60, marker='^', c='c', label='y_train == 0') plt.scatter(x_train[y_train == 1], y_train[y_train == 1], 60, marker='o', c='m', label='y_train == 1') plt.xlabel('x_train[:,0]') plt.ylabel('x_train[:,1]') plt.legend()
以下程式碼中,我們令 learning rate 為 0.01,iteration 次數為 10000 次。
w, b = perform_gradient_descent(x_train, y_train, 0, 0, 0.01, 10000) w, b # Output (np.float64(0.6199251592044446), np.float64(-5.42258186823073))
之後,用算出來的 w
和 b
來預測 x_train
。得到的 prediction 是一連串的機率。我們將 decision boundary 設為 0.5,所以機率小於 0.5 的給予標籤 0,機率大於等於 0.5 的給予標籤 1。
prediction = f_wb(x_train, w, b) y_hat = np.int_(prediction >= 0.5) (y_train, prediction, y_hat)
將 y_hat
畫在座標圖上,可以上面 y_train
的座標圖比較。
plt.scatter(x_train[y_hat == 0], y_train[y_hat == 0], 60, marker='^', c='c', label='y_hat == 0') plt.scatter(x_train[y_hat == 1], y_train[y_hat == 1], 60, marker='o', c='m', label='y_hat == 1') plt.xlabel('x_train[:,0]') plt.ylabel('x_train[:,1]') plt.legend()
多元邏輯斯回歸(Multiple Logistic Regress)
至目前為止,我們介紹的是 simple logistic regression,只有一個變數。接下來,我們將介紹 multiple logistic regression。
多元邏輯斯回歸模型(Multiple Logistic Regression Model)
Multiple logistic regression 的 model function 如下。
向量化的 model function 則如下,其中 是 vectors。
如果我們將所有的 training examples 放在一起,就會形成一個陣列(matrix)。一般我們會用大寫的 X
來代表 training examples 形成的 matrix。因此,model function 可寫成如下:
向量化的 fw,b
和 simple logistic regression 的 model function 很像,它們的實作也差不多。差別在於,向量化的 fw,b
使用內積(dot product)來相乘 X
和 w
。
def sigmoid(z): """ Sigmoid function Parameters ---------- z: (ndarray (m,)) or (scalar) m is the number of samples Returns ------- (ndarray (m,)) or (scalar) """ return 1 / (1 + np.exp(-z)) def f_wb(X, w, b): """ Logistic regression model Parameters ---------- X: (ndarray (m, n)) m is the number of samples, n is the number of features w: (ndarray (n,)) b: (scalar) Returns ------- (ndarray (m,)) """ return sigmoid(np.dot(X, w) + b)
成本函數(Cost Function)
Multiple logistic regression 的 cost function 如下。
以下程式碼中,compute_cost()
實作了向量化的 cost function。
def compute_cost(X, y, w, b): """ Compute cost Parameters ---------- X: (ndarray (m, n)) m is the number of samples, n is the number of features y: (ndarray (m,)) w: (ndarray (n,)) b: (scalar) Returns ------- (scalar) """ m = X.shape[0] y_hat = f_wb(X, w, b) cost = 1 / m * np.sum(-y * np.log(y_hat) - (1 - y) * np.log(1 - y_hat)) return cost
梯度下降(Gradient Descent)
Multiple logistic regression 的 gradient descent 如下。從式子中可以了解到,我們要計算每一個 feature 的參數 wj
。然後,計算 J(w,b)
對每一個 wj
的偏導數。
J(w,b)
對每一個 wj
和 b
的偏導數如下:
以下的程式碼實作 J(w,b)
對 wj
和 b
的偏導數計算。
def compute_gradient(X, y, w, b): """ Compute the gradient for logistic regression Parameters ---------- X: (ndarray (m, n)) m is the number of samples, n is the number of features y: (ndarray (m,)) w: (ndarray (n,)) b: (scalar) Returns ------- dj_dw: (ndarray (n,)) dj_db: (scalar) """ m = X.shape[0] y_hat = f_wb(X, w, b) dj_dw = 1 / m * np.dot(X.T, y_hat - y) dj_db = 1 / m * np.sum(y_hat - y) return dj_dw, dj_db
以下的程式碼實作 gradient descent。
def perform_gradient_descent(X, y, w_init, b_init, alpha, epochs): """ Perform gradient descent Parameters ---------- X: (ndarray (m, n)) m is the number of samples, n is the number of features y: (ndarray (m,)) w_init: (ndarray (n,)) b_init: (scalar) alpha: (float) epochs: (int) Returns ------- w: (ndarray (n,)) b: (scalar) """ w = w_init b = b_init for i in range(epochs): dj_dw, dj_db = compute_gradient(X, y, w, b) w -= alpha * dj_dw b -= alpha * dj_db print(f'Epoch {i + 1:4}, Cost: {float(compute_cost(X, y, w, b)):8.2f}') return w, b
範例
以下的範例中,在座標平面上有 6 個點,並將它們分成兩群。
X_train = np.array([[10, 10], [10, 16], [20, 8], [10, 24], [20, 16], [30, 30]]) y_train = np.array([0, 0, 0, 1, 1, 1]) plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 60, marker='^', c='c', label='y_train == 0') plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 60, marker='o', c='m', label='y_train == 1') plt.xlabel('x_train[:,0]') plt.ylabel('x_train[:,1]') plt.legend()
用 gradient descent 來訓練模型找出 w
和 b
。
np.random.seed(1) initial_w = 0.01 * (np.random.rand(2) - 0.5) initial_b = -8 w, b = perform_gradient_descent(X_train, y_train, initial_w, initial_b, 0.001, 10000) w, b # Output Epoch 1, Cost: 3.75 Epoch 1001, Cost: 0.17 Epoch 2001, Cost: 0.17 Epoch 3001, Cost: 0.17 Epoch 4001, Cost: 0.17 Epoch 5001, Cost: 0.17 Epoch 6001, Cost: 0.17 Epoch 7001, Cost: 0.17 Epoch 8001, Cost: 0.17 Epoch 9001, Cost: 0.17 (array([0.17254296, 0.36047453]), np.float64(-8.219025094555942))
算出 w 和 b 後,我們可以畫出 decision boundary。
plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 60, marker='^', c='c', label='y_train == 0') plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 60, marker='o', c='m', label='y_train == 1') plot_x = np.array([min(X_train[:, 0]), max(X_train[:, 0])]) plot_y = (-1. / w[1]) * (w[0] * plot_x + b) plt.plot(plot_x, plot_y, c='g') plt.xlabel('x_train[:,0]') plt.ylabel('x_train[:,1]') plt.legend()
最後,我們新增一個點 (25, 25),並利用模型來將它分類。
plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], 60, marker='^', c='c', label='y_train == 0') plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], 60, marker='o', c='m', label='y_train == 1') plot_x = np.array([min(X_train[:, 0]), max(X_train[:, 0])]) plot_y = (-1. / w[1]) * (w[0] * plot_x + b) plt.plot(plot_x, plot_y, c='g') x = np.array([25, 25]) y_hat = f_wb(x, w, b) plt.scatter(x[0], x[1], 60, marker='*', c='m' if y_hat >= 0.5 else 'c', label=f'Prediction: {y_hat:.2f}') plt.xlabel('x_train[:,0]') plt.ylabel('x_train[:,1]') plt.legend()
結語
Logistic regression 模型和 linear regression 很像。它使用 linear regression model function 算出一個值,再用 sigmoid function 將該值巧妙地轉換成機率。雖然 logistic regression 概念蠻講單,但是卻常常被用在實務上。
參考
- Andrew Ng, Machine Learning Specialization, Coursera.
- 西内啓,機器學習的數學基礎 : AI、深度學習打底必讀,旗標。