目錄
這一系列是學習公衆號“機器學習實驗室”的筆記,跟着大佬的腳步一個個實現,邏輯迴歸和線性迴歸的實現很像,主要是公式推導那有些費時間。
數學推導
notations
objective function
最大化似然函數:
思想推導
廣義線性模型:
,這個函數是可微的,有。
我們取sigmoid函數
注意:對數線性迴歸不等於對數機率迴歸。
對數線性迴歸 | 對數機率迴歸 |
---|---|
log-linear regression | logistic regression |
推導還是比較簡單的:
兩邊去對數:
記 爲類後驗概率 , 則爲 。
-
對一個樣本來說,,標籤爲1時,計算,標籤爲0時,計算。
-
對於整個樣本集來說,想要求使得樣本聯合概率最大的參數。
事情就變得很奇妙了,取對數後有
看一下log函數,發現是單調遞增的,也就是說最大化聯合概率就轉換成最大化每個樣本概率的對數和。
這就是最大似然概率的公式,最大似然法的優化思想。
3. 把1帶入2的 有 ,最大化這個式子就等於最小化
。
partial derivative
matrix version
我們利用Numpy實現,數據存放爲二維數組,在代碼實現的時候需要看一下矩陣相乘合不合法。
手動實現邏輯迴歸
首先分析有幾個部分:LogisticRegression作爲一個類,應該滿足喂入訓練數據x和y,然後擬合好一個線性函數F,參數爲W和b;此外根據這個線性函數通過sigmoid可以得到測試數據屬於類別的概率。
由此我們可以很快想到造一個訓練器train、一個預測器predict和一個評價器evaluation。
因爲思路和線性迴歸是一樣的,僅僅改變了一下predict和evaluation函數,因此這裏重點放這兩個。
訓練器 train
思路
- 訓練器的input是x_train和y_train,output是weight和bias。
- 優化思想是最大似然法,出發點是最大化似然函數,這樣loss就可以確定爲,推導見上。
其中 - 考慮到特徵維數較高,就不用正規方程解法,直接用數值優化方法——梯度下降,對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。
降維可視化
這是比較尷尬的,因爲維數比較高,可視化的話最好是降維處理,比如主成分分析,或者用可視化包,但是我太懶了,選了第二維和第三維特徵,看不出來,但我想表達如果學得好明顯就能把數據在二維平面分開。
完整代碼
之後上傳