前面三天推送機器學習線性迴歸算法之最小二乘法,從假設到原理,詳細分析了直接求解和梯度下降兩種算法,接下來手動編寫python代碼實現線性迴歸的算法吧。
1 數據預處理
在拿到一個數據集後,往往需要經過漫長的預處理過程,不要忽視這個看似與建立模型,求解模型無關的步驟,它其實非常重要的,爲後續工作做好準備的一步。現在這節的重點不是在論述預處理的方法,所以在此不詳細介紹預處理的過程,而是重點例子模擬線性迴歸最小二乘法的兩個求解方法。
獲得了數據集後,經過預處理後得到的數據前10條展示如下,其中第一列爲房屋的面積,第二列爲房屋使用年限,第三列爲房屋的價值,是標籤值,這些值都已經經過預處理了。
房屋面積 使用年限 價值
[[ 0.35291809, 0.16468428, 0.35774628],
[-0.55106013, -0.10981663, 0.25468008],
[-0.65439632, -0.71406955, 0.1061582 ],
[-0.19790689, 0.61536205, 0.43122894],
[-0.00171825, 0.66827656, 0.44198075],
[-0.2739687 , -1.16342739, 0.01195186],
[ 0.11592071, -0.18320789, 0.29397728],
[-0.02707248, -0.53269863, 0.21784183],
[ 0.7321352 , 0.27868019, 0.42643361],
[-0.76680149, -0.89838545, 0.06411818]]
下面用直接求法和梯度下降法求解線性迴歸。
首先介紹下使用的庫:
'導入numpy庫'
import numpy as np
'導入pyplot'
import matplotlib.pyplot as plt
'導入時間模塊'
import time
本次模擬取數據集的前100條數據進行迭代計算,即 m = 100 。
做一個偏移量和2個特徵的組合,這樣與前面推送的理論部分銜接在一起,組合的代碼
如下所示:
'偏移量 b shape=(100,1)'
b = np.array([1])
b=b.repeat(100)
'將偏移量與2個特徵值組合 shape = (100,3)'
X = np.column_stack((b,X))
2 直接求解參數
我們知道當我們建立線性迴歸的模型時,因爲是線性的,並且誤差項滿足高斯分佈,此時藉助最大似然估計可以直接拿到權重參數的計算公式,如果想看下理論部分,請參考 直接求解 :
'這是一個求矩陣的逆所用到的模塊'
from numpy.linalg import linalg as la
xtx = X.transpose().dot(X)
xtx = la.inv(xtx)
'直接求出參數'
theta = xtx.dot(X.transpose()).dot(y)
這個解法很簡單,直接套用公式,求出權重參數如下:
array([0.29348374, 0.10224818, 0.19596799])
即偏移量爲 0.29,第一個特徵的權重參數爲0.10,第二個特徵的權重參數爲0.195 。
下面用梯度下降法求解,這纔是我們論述的重點,這個思路與機器學習的其他算法,比如邏輯迴歸等思路是一致的,因此有必要好好研究下。
3 梯度下降求參數
梯度下降的詳細介紹,請參考梯度下降求解權重參數部分,下面我們論述如何由理論兌現爲代碼。
首先列舉梯度下降的思路步驟,採取線性迴歸模型,求出代價函數,進而求出梯度,求偏導是重要的一步,然後設定一個學習率迭代參數,當與前一時步的代價函數與當前的代價函數的差小於閾值時,計算結束,如下思路:
'model' 建立的線性迴歸模型
'cost' 代價函數
'gradient' 梯度公式
'theta update' 參數更新公式
'stop stratege' 迭代停止策略:代價函數小於閾值法
下面分別寫出以上五步的具體實現代碼,
'model'
def model(theta,X):
theta = np.array(theta)
return X.dot(theta)
'cost'
def cost(m,theta,X,y):
'print(theta)'
ele = y - model(theta,X)
item = ele**2
item_sum = np.sum(item)
return item_sum/2/m
'gradient'
def gradient(m,theta,X,y,cols):
grad_theta = []
for j in range(cols):
grad = (y-model(theta,X)).dot(X[:,j])
grad_sum = np.sum(grad)
grad_theta.append(-grad_sum/m)
return np.array(grad_theta)
'theta update'
def theta_update(grad_theta,theta,sigma):
return theta - sigma * grad_theta
'stop stratege'
def stop_stratege(cost,cost_update,threshold):
return cost-cost_update < threshold
'OLS algorithm'
def OLS(X,y,threshold):
start = time.clock()
'樣本個數'
m=100
'設置權重參數的初始值'
theta = [0,0,0]
'迭代步數'
iters = 0;
'記錄代價函數的值'
cost_record=[]
'學習率'
sigma = 0.0001
cost_val = cost(m,theta,X,y)
cost_record.append(cost_val)
while True:
grad = gradient(m,theta,X,y,3)
'參數更新'
theta = theta_update(grad,theta,sigma)
cost_update = cost(m,theta,X,y)
if stop_stratege(cost_val,cost_update,threshold):
break
iters=iters+1
cost_val = cost_update
cost_record.append(cost_val)
end = time.clock()
print("OLS convergence duration: %f s" % (end - start))
return cost_record, iters,theta
結果顯示經過,OLS梯度下降經過如下時間得到初步收斂,OLS convergence duration: 7.456927 s,經過3萬多個時步迭代,每個時步計算代價函數的取值,如下圖所示:
收斂時,得到的權重參數爲:
array([ 0.29921652, 0.09754371, 0.1867609 ])
可以看到梯度下降得到的權重參數與直接求出法得出的基本相似,這其中的誤差是因爲沒有進一步再迭代。
4 總結
以上就是最小二乘法的兩種解法的代碼實現,至此我們已經將線性迴歸算法的最基本的OLS從理論,假設,到現在的代碼兌現都闡述完了。讓我們看一下遠邊的大海,和巍峨的高山,放鬆一下吧!
然而,有些數據集的某兩列或多列存在強相關性,當面對這樣的數據集,OLS還能勝任嗎? 如果不能勝任,這其中的原因又是什麼呢?
請看明天的推送,OLS算法的缺陷及原理。
歡迎關注「 算法channel 」
交流思想,注重分析,看重過程,包含但不限於:經典算法,機器學習,深度學習,LeetCode 題解,Kaggle 實戰,英語沙龍,定期邀請專家發推。期待您的到來!