一、Logistic Regression 算法
Logistic Regression 算法具有複雜度低、容易實現的優點,我們可以利用 Logistic Regression 算法實現廣告的點擊率估計。Logistic Regression 模型是線性的分類的模型,所謂線性通俗的來說只需要一條直線就可以將不同的類區分開來。這條直線也成爲超平面,使用
表示,其中W爲權重,b爲偏置。
在 Logistic Regression 算法中對樣本進行分類,可以通過對訓練樣本的學習得到超平面,是數據分爲正負兩個類別。也可以使用閾值函數如 Sigmoid,將樣本映射到不同的類別中。
二、Sigmoid 函數
Sigmoid 函數的形式:
Sigmoid 函數的基本性質:
- 定義域:(−∞,+∞)
- 值域:(−1,1)
- 函數在定義域內爲連續和光滑函數
- 處處可導,導數爲:f′(x)=f(x)(1−f(x))
Python 實現 Sigmoid 函數如下:
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
x = np.linspace(-10,10)
y = sigmoid(x)
plt.plot(x,y,label="Sigmoid",color = "blue")
plt.legend()
三、那爲什麼使用 Sigmoid ? 有如下兩種解釋
(一)Logistic Regression 算法的需求
Logistic Regression 算法中屬於正例即輸入向量 X 的概率爲 :
P(y=1|x,w,b)=Sigmoid(Wx+b)
負例的概率爲 :
P(y=0|x,w,b)=1-P(y=1|x,w,b)
對於一個有效的分類器,Wx+b ( w 和 x 的內積)代表了數據 x 屬於正類(y=1)的置信度。Wx+b 越大,這個數據屬於正類的可能性越大, Wx+b 越小,屬於反類的可能性越大。而 Sigmoid 函數恰好能夠將 Wx+b 映射到條件概率P(y=1|x,w,b) 上。Sigmoid 函數的值域是(0,1),滿足概率的要求,同時它是一個單調上升函數。最終, P(y=1|x,w,b)=Sigmoid(Wx+b),sigmoid的這些良好性質恰好能滿足 Logistic Regression 的需求。
(二)Sigmoid 函數和正態分佈函數的積分形式形狀非常類似。但計算正態分佈的積分函數,計算代價非常大,而Sigmoid由於其公式簡單,計算量非常的小。總之是 Sigmoid 函數能滿足分類任務,至於其他的也不要糾結,很多人都在用就不要苦惱了。
四、求參數W和b
爲求參數 W 和 b, 使用極大似然法,通常使用 Log 似然函數的極大值求解參數,在 Logistic Regression 中將負的 Log 似然函數作爲損失函數 。
我們一般使用均方誤差來衡量損失函數,但考慮均方誤差損失函數一般是非凸函數,其在使用梯度下降算法的時候,容易得到局部最優解,不是全局最優解, 如下圖所示:
非凸函數 凸函數
所以要選擇凸函數,再者使用均方誤差其偏導值在輸出概率值接近0或者接近1的時候非常小,這可能會造成模型剛開始訓練時,偏導值幾乎消失,所以 Logistic Regression 算法選擇交叉熵損失函數。
定義如下:
假設訓練數據有 m 個訓練樣本,,則損失函數爲:
所以我們的問題變成:
五、梯度下降法
理清之後我們使用梯度下降的優化算法對損失函數 l 進行優化,尋找最優的參數 W。梯度下降法是一種迭代型的算法,根據初始點在每一次迭代的工程中選擇下降方向,同時改變需要修改的參數。
梯度下降法的過程如下:
- 隨機選擇一個初始點
- 重複下面的步驟:
- 決定梯度下降的方向:
- 選擇步長a
- 更新:
3. 直到滿足終止條件
梯度下降法的更新公式:
公式推導如下圖:
其中a爲步長,選擇太小會導致收斂速度很慢,選擇太大會直接跳過最優解,如下圖所示,所以步長的選擇至關重要,負的梯度方向爲下降方向。
六、 Logistic Regression 算法應用
# -*- coding: utf-8 -*-
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np
iris = load_iris()
data = iris.data
target = iris.target
X = data[0:100,[0,2]]
y = target[0:100]
label = np.array(y)
index_0 = np.where(label==0)
class logistic(object):
def __init__(self):
self.W = None
def train(self,X,y,learn_rate = 0.01,num_iters = 5000):
num_train,num_feature = X.shape
#init the weight
self.W = 0.001*np.random.randn(num_feature,1).reshape((-1,1))
loss = []
for i in range(num_iters):
error,dW = self.compute_loss(X,y)
self.W += -learn_rate*dW
loss.append(error)
return loss
def compute_loss(self,X,y):
num_train = X.shape[0]
h = self.output(X)
loss = -np.sum((y*np.log(h) + (1-y)*np.log((1-h))))
loss = loss / num_train
dW = X.T.dot((h-y)) / num_train
return loss,dW
def output(self,X):
g = np.dot(X,self.W)
return self.sigmod(g)
def sigmod(self,X):
return 1/(1+np.exp(-X))
def predict(self,X_test):
h = self.output(X_test)
y_pred = np.where(h>=0.5,1,0)
return y_pred
y = y.reshape((-1,1))
#add the x0=1
one = np.ones((X.shape[0],1))
X_train = np.hstack((one,X))
classify = logistic()
loss = classify.train(X_train,y)
plt.plot(loss)
plt.xlabel('Iteration number')
plt.ylabel('Loss value')
plt.show()
label = np.array(y)
index_0 = np.where(label==0)
plt.scatter(X[index_0,0],X[index_0,1],marker='x',color = 'y',label = '0',s = 12)
index_1 =np.where(label==1)
plt.scatter(X[index_1,0],X[index_1,1],marker='o',color = 'b',label = '1',s = 12)
x1 = np.arange(4,7.5,0.5)
x2 = (- classify.W[0] - classify.W[1]*x1) / classify.W[2]
plt.plot(x1,x2,color = 'black')
plt.xlabel('X1')
plt.ylabel('X2')
plt.legend(loc = 'upper left')
plt.show()
參考文獻:
趙志勇《Python 機器學習算法》