算法原理
模型
邏輯迴歸模型是一個二分類的對數線性模型。令樣本數據集爲
其中,是樣本的特徵,是樣本的類別,限定爲0或1。邏輯迴歸模型的數學表達式爲
單單看這個表達式可以發現,它跟上節講到的感知機模型是很類似的,感知機使用sign函數對線性表達式進行轉化,而邏輯迴歸是用了sigmoid函數,這也是爲什麼感知機中的類別標籤是1和-1,而邏輯迴歸的類別標籤是0和1。但是在算法思想上兩者卻完全不同,感知機利用了錯誤驅動,通過錯誤分類集來構造損失函數,然後使用梯度下降法進行求解。而接下來我們會講到,邏輯迴歸是通過構造概率模型並最大化概率的方式來進行分類。
原理
首先,模型的前提是所有樣本數據都是獨立同分布的。
單個樣本
現在已知有一個樣本數據(,),我們的目的是希望將代入模型
得到的剛好等於。所以在這裏要先定義一個條件概率,表示在已知樣本是的條件下,類別是1的概率
當類別等於1時,要儘可能的大。由的公式可以得到在已知樣本是的條件下,類別是0的概率爲
當類別等於0時,要儘可能的大。
可以將和整合起來,令
這樣,無論是0還是1,都是要讓儘可能的大,即最大化。
整個數據集
對於單個樣本數據(,),需要讓儘可能的大,那推廣到整個數據集上,就是要求解
這裏之所以將每個樣本數據對應的相乘,是因爲每個樣本數據都是獨立同分布的。另外,由於每個的數值都在0和1之間,因此它們的積也是在0和1之間。爲了將乘法拆開,需要做一個負對數的轉換(一般採用自然對數)
爲了避免誤會,箭頭代表問題的等價轉換。第一個箭頭轉換是因爲對數函數是增函數,第二個箭頭是因爲添加負號,同時變成最小化問題,第三個箭頭是根據對數的性質。進一步,我們就得到了邏輯迴歸的損失函數,也就是第三個箭頭右邊的公式的均值化,詳細展開如下。
模型求解
採用梯度下降法求解,更新和的值,不斷迭代,從而最小化損失函數。
其中,是學習步長
程序實現
sigmoid函數
def sigmoid(X):
y = 1 / (1+np.exp(-X))
return y
初始化函數
def __init__(self, alpha=0.01, iteration=1000):
"""
alpha 學習步長
w 權重向量
iteration 最大迭代次數
error 記錄損失函數值
"""
self.alpha = alpha
self.iteration = iteration
訓練函數
def fit(self, X, y):
[data_num, fea_num] = X.shape
X_ = np.vstack((np.ones(data_num).reshape(1,-1), X.T))
y_ = y.reshape(-1,1)
self.w = np.zeros(fea_num+1).reshape(-1,1)
self.error = []
i = 1
while i<self.iteration:
# 迭代w值
dif_y = y_ - sigmoid(np.dot(self.w.T,X_)).reshape(-1,1)
self.w = self.w + self.alpha * 1/data_num * np.dot(X_, dif_y)
# 記錄損失
p1 = sigmoid(np.dot(self.w.T,X_).T)
temp = y_*np.log(p1) + (1-y_)*np.log(1-p1)
self.error.append(-temp.mean())
# 迭代次數
if (i+1)%100==0:
print('epoch--', i+1)
i += 1
訓練函數的核心代碼在第十一和第十二行,是參數的迭代更新公式,參考模型求解部分的公式,代碼中將權重參數和偏置參數合併在一起進行計算。可轉化成矩陣計算的形式如下圖(*代表矩陣乘法)。
圖中顯示的各矩陣的維數分別爲:
其中,包含了偏置,所以需要再加上一維變成維。另外,邏輯迴歸不一定能將樣本全部正確分類,所以需要設置迭代次數。
預測函數
def predict(self, X):
X_ = np.vstack((np.ones(X.shape[0]).reshape(1,-1), X.T))
sig = sigmoid(np.dot(self.w.T, X_))[0]
return (sig>=0.5)*1
整合全部代碼
class LogisticRegression:
def __init__(self, alpha=0.01, iteration=1000):
"""
alpha 學習步長
w 權重向量
iteration 最大迭代次數
error 記錄損失函數值
"""
self.alpha = alpha
self.iteration = iteration
def fit(self, X, y):
[data_num, fea_num] = X.shape
X_ = np.vstack((np.ones(data_num).reshape(1,-1), X.T))
y_ = y.reshape(-1,1)
self.w = np.zeros(fea_num+1).reshape(-1,1)
self.error = []
i = 1
while i<self.iteration:
# 迭代w值
dif_y = y_ - sigmoid(np.dot(self.w.T,X_)).reshape(-1,1)
self.w = self.w + self.alpha * 1/data_num * np.dot(X_, dif_y)
# 記錄損失
p1 = sigmoid(np.dot(self.w.T,X_).T)
temp = y_*np.log(p1) + (1-y_)*np.log(1-p1)
self.error.append(-temp.mean())
# 迭代次數
if (i+1)%100==0:
print('epoch--', i+1)
i += 1
def predict(self, X):
X_ = np.vstack((np.ones(X.shape[0]).reshape(1,-1), X.T))
sig = sigmoid(np.dot(self.w.T, X_))[0]
return (sig>=0.5)*1
實例化演示
導入相關庫和數據並查看數據分佈
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data[:100,:2]
y = (iris.target[:100]==1)*1
plt.scatter(X[y==0,0], X[y==0,1], color='red')
plt.scatter(X[y==1,0], X[y==1,1], color='green')
實例化函數並畫出劃分平面
# 訓練
clf=LogisticRegression()
clf.fit(X,y)
line1 = X[:,0]
line2 = -(clf.w[0]+clf.w[1]*line1)/clf.w[2]
# 畫圖
plt.scatter(X[y==0,0],X[y==0,1],color='red')
plt.scatter(X[y==1,0],X[y==1,1],color='green')
plt.plot(line1,line2)
至此,邏輯迴歸算法講解完畢,以上是個人對邏輯迴歸算法的一些理解,如有錯誤歡迎指出。