【機器學習系列2】邏輯迴歸LogisticRegression——數學推導和純Numpy實現


這一系列是學習公衆號“機器學習實驗室”的筆記,跟着大佬的腳步一個個實現,邏輯迴歸和線性迴歸的實現很像,主要是公式推導那有些費時間。

數學推導

notations

X={xi,i=1,2,...,m}XRm×dX=\{\vec{x_i},i=1,2,...,m\} \quad X\subseteq \mathbb{R}^{m\times d}
xi=(xi1,xi2,...,xid)xiR1×d\quad \vec{x_i}=(x_{i1},x_{i2},...,x_{id}) \quad x_i\subseteq \mathbb{R}^{1\times d}
Y={yi,i=1,2,...,m}YRm×1Y=\{y_i,i=1,2,...,m\} \quad Y\subseteq \mathbb{R}^{m\times 1}
yiR\quad \vec{y_i}\subseteq \mathbb{R}

objective function

最大化似然函數:

(W,b)=argmax(w,b)i=1mlogp(yixi;w,b)(W^{\star},b^{\star})=\mathop{argmax}\limits_{(w,b)}\sum_{i=1}^{m}\log{p(y_i|x_i;w,b)}

思想推導

廣義線性模型:

y=g1(wTx+b)y=g^{-1}(w^Tx+b),這個函數是可微的,有f=g1f=g^{-1}

我們取sigmoid函數 y=11+exy=\frac{1}{1+e^{-x}}
在這裏插入圖片描述

注意:對數線性迴歸不等於對數機率迴歸。

對數線性迴歸 對數機率迴歸
log-linear regression logistic regression
lny=wTx+bln{y}=w^Tx+b lny1y=wTx+bln{\frac{y}{1-y}=w^Tx+b}
y=ewTx+by=e^{w^Tx+b} y=11+e(wTx+b)y=\frac{1}{1+e^{-(w^{T}x+b)}}

推導還是比較簡單的:
y=11+e(wTx+b)y=\frac{1}{1+e^{-(w^{T}x+b)}}

1+e(wTx+b)=1y1+e^{-(w^Tx+b)}=\frac{1}{y}

e(wTx+b)=1yye^{-(w^Tx+b)}=\frac{1-y}{y}

兩邊去對數:

wTx+b=lny1yw^Tx+b=ln{\frac{y}{1-y}}

yy 爲類後驗概率 p(y=1x)p(y=1|x)1y1-y 則爲 p(y=0x)p(y=0|x)

  1. 對一個樣本來說,p(yixi)=yip(y=1xi)+(1yi)p(y=0xi)p(y_i|x_i)=y_i*p(y=1|x_i)+(1-y_i)*p(y=0|x_i),標籤爲1時,計算p(y=1x)p(y=1|x),標籤爲0時,計算p(y=0x)p(y=0|x)

  2. 對於整個樣本集來說,想要求使得樣本聯合概率最大的參數。(W,b)=argmax(w,b)L(w,b)(W^{\star},b^{\star})=\mathop{argmax}\limits_{(w,b)}L(w,b)

    L(w,b)=i=1mpw,b(yixi)L(w,b)=\prod_{i=1}^{m}p_{w,b}(y_i|x_i)

    事情就變得很奇妙了,取對數後有

    lnL(w,b)=i=1mlnpw,b(yixi)ln{L(w,b)}=\sum_{i=1}^{m}\ln{p_{w,b}(y_i|x_i)}

    看一下log函數,發現是單調遞增的,也就是說最大化聯合概率就轉換成最大化每個樣本概率的對數和。

    這就是最大似然概率的公式,最大似然法的優化思想。

在這裏插入圖片描述
3. 把1帶入2的 L(w,b)L(w,b)i=1m(yi(wTx+b)ln(1+ewTx+b))\sum_{i=1}^{m}{(y_i(w^Tx+b)-ln(1+e^{w^Tx+b}))},最大化這個式子就等於最小化

i=1m(yi(wTx+b)ln(1+ewTx+b))\sum_{i=1}^{m}{(-y_i(w^Tx+b)ln(1+e^{w^Tx+b}))}

partial derivative

E(w,b)w=i=1myixi+xiewTx+b1+ewTx+b\frac{\partial{E(w,b)}}{\partial{w}}=\sum_{i=1}^{m}{-y_ix_i+x_i\frac{e^{w^Tx+b}}{1+e^{w^Tx+b}}}

E(w,b)b=i=1myi+ewTx+b1+ewTx+b\frac{\partial{E(w,b)}}{\partial{b}}=\sum_{i=1}^{m}-y_i+\frac{e^{w^Tx+b}}{1+e^{w^Tx+b}}

matrix version

我們利用Numpy實現,數據存放爲二維數組,在代碼實現的時候需要看一下矩陣相乘合不合法。

手動實現邏輯迴歸

首先分析有幾個部分:LogisticRegression作爲一個類,應該滿足喂入訓練數據x和y,然後擬合好一個線性函數F,參數爲W和b;此外根據這個線性函數通過sigmoid可以得到測試數據屬於類別的概率
由此我們可以很快想到造一個訓練器train、一個預測器predict和一個評價器evaluation
因爲思路和線性迴歸是一樣的,僅僅改變了一下predict和evaluation函數,因此這裏重點放這兩個。

訓練器 train

思路

  1. 訓練器的input是x_train和y_train,output是weight和bias。
  2. 優化思想是最大似然法,出發點是最大化似然函數,這樣loss就可以確定爲i=1mlogp(yixi;w,b)\sum_{i=1}^{m}\log{p(y_i|x_i;w,b)},推導見上。
    其中p(yixi;w,b)=yip1(xi^;w,b)+(1yi)p0(xi^;w,b)p(y_i|x_i;w,b)=y_ip_1(\hat{x_i};w,b)+(1-y_i)p_0(\hat{x_i};w,b)p0(xi^;w,b)=1p1(xi^;w,b)p_0(\hat{x_i};w,b)=1-p_1(\hat{x_i};w,b)
  3. 考慮到特徵維數較高,就不用正規方程解法,直接用數值優化方法——梯度下降,對loss函數求導見上,這和線性迴歸是一樣的。
    因此我們可以寫出僞代碼:
initialize parameters w, b
for epoch in epochs:
	caculate loss, dw, db
	update w with dw
	update b with db
	return w, b

第一步是初始化參數,權重與特徵維數一致,偏差是個常數。
第二步是求loss和偏導,我們可以通過一個函數完成。這個函數要求輸入x_train,y_train和參數,返回loss,dw,db。
第三步就是利用返回的偏導更新參數,從稍後的圖可以看到loss會震盪,然後平穩。

代碼實現

重點講loss這塊,其他都和線性迴歸差不多,這裏好像寫錯了一點,我之後再修正。

# 求loss,dw,db
def loss(self, X, y, W, b):
	num_train = X.shape[0]
	num_feature = X.shape[1]
	
	beta_x = np.dot(X, W) + b
	y_hat = self.sigmoid(beta_x)
	p_y = y * y_hat + (1 - y) * (1 - y_hat)
	# loss = np.sum(( y * np.log(beta_x) - np.log(1 + beta_x)))
	loss = np.sum(- y + self.sigmoid(beta_x))
	
	dW = np.dot(X.T, - y + self.sigmoid(beta_x)) / num_feature
	db = np.sum(- y + self.sigmoid(beta_x)) / num_train
	# print(dW)
	
	return y_hat, loss, dW, db

預測器 predict

思路

input是存放的使loss最小的parameters和x_test,output是計算得到的y_predict。
概率大於等於0.5判爲1,否則0,直接上代碼。

代碼

def predict(self, X, params):
	W = params['W']
	b = params['b']
	
	y_predict = self.sigmoid(np.dot(X, W) + b)
	y_predict[y_predict >= 0.5] = 1
	y_predict[y_predict < 0.5] = 0

評價器 evaluation

思路

input是預測得到的值y_predict和原值y_test,output是我們定義的準確率,精確率,召回率。

代碼

def evalution(self, y_predict, y):
	y_test = y_test.ravel()
	y_predict = y_predict.ravel()
	
	num_test = y_test.shape[0]
	# succeed: 0  fail: 1,-1
	outcome = y_predict - y_test
	# succeed: 0  fail: 1
	outcome[outcome == -1] = 1
	
	Counter(outcome)
	Counter(y_predict)
	num_tp_tn = sum(outcome==0)
	num_fp = np.dot(y_predict.T, outcome)
	num_tp = sum(y_predict==1) - num_fp
	num_tn = num_tp_tn - num_tp
	num_fn = sum(y_predict==0) - num_tn
	# print(outcome, y_predict, y_test)
	# print(num_tp_tn, num_fp, num_tp, num_tn, num_fn)
	# print(list(y_test).count(0), list(y_test).count(1))
	
	accuracy =  num_tp_tn / num_test
	precision = num_tp / (num_tp + num_fp)
	recall = num_tp / (num_tp + num_fn)
	
	return accuracy, precision, recall

與sklearn比較

我加入sklearn的包來比較模型的效果,在學習率爲1,迭代10000次的時候在這個測試集上達到和sklearn一樣的效果。

ours:  0.9122807017543859 0.9210526315789473 0.9459459459459459
sklearn:  0.9122807017543859 0.9210526315789473 0.9459459459459459

學習率

在0.5學習率、迭代10000次的時候並不好。
在這裏插入圖片描述
學習率爲1時可以看到和sklearn的預測完全一致。
在這裏插入圖片描述

損失

學習過程中loss一直在震盪,這裏其實我沒有完全按照loss函數(因爲值溢出了),而是定義了一個Loss。
在這裏插入圖片描述

降維可視化

這是比較尷尬的,因爲維數比較高,可視化的話最好是降維處理,比如主成分分析,或者用可視化包,但是我太懶了,選了第二維和第三維特徵,看不出來,但我想表達如果學得好明顯就能把數據在二維平面分開。
在這裏插入圖片描述

完整代碼

之後上傳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章