【火爐煉AI】機器學習004-嶺迴歸器的構建和模型評估
(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
在我的上一篇文章(【火爐煉AI】機器學習003-簡單線性迴歸器的建立,測試,模型儲存和載入)中,已經詳細的講解了簡單線性迴歸器的構建和測試,簡單線性迴歸器的優勢在“簡單”,執行速度快,但缺點也在於“簡單”,過於簡單以至於難以擬合很多複雜的資料集。
比如,如果資料集中存在一些遠離大部分資料點的異常點,如下圖所示,那麼簡單線性迴歸器就會得到比較差的迴歸直線,這是因為線性迴歸器要擬合資料集中所有的資料點,也包括這些異常點,所以異常點往往會把模型“帶到溝裡去”。
面對這樣的資料集,該怎麼樣得到正確的擬合直線了?可以使用正則化項的係數作為閾值來消除異常值的影響,這個方法被稱為嶺迴歸。
嶺迴歸是專門用於資料集中存在偏差資料(異常點)的迴歸分析方法,本質上是對普通最小二乘法進行改良,放棄最小二乘法的無偏性,以損失部分資訊,降低精度為代價獲得迴歸係數更符合資料集實際情況的迴歸方法,所以嶺迴歸對於有偏差資料的資料集的擬合要明顯強於使用最小二乘法的線性迴歸模型。
線性迴歸模型可以用下列公式表示:
線性迴歸模型所使用的損失函式為:
通過最小二乘法求解得到的最優解為:
前面提到,嶺迴歸的本質是對最小二乘法進行改良,簡單來說,嶺迴歸就是在矩陣XTX上加一個I,加入的這一部分相當於引進了偏差性。所以嶺迴歸的損失函式和最優解分別為:
1. 嶺迴歸器的構建
在程式碼層面上,嶺迴歸器的構建方式和線性迴歸器的構建方法一樣,只是建立一個linear_model. Ridge ()物件,而不是linear_model.LinearRegression()迴歸器物件,如下部分程式碼所示。
# 嶺迴歸器的構建
from sklearn import linear_model
ridge_regressor=linear_model.Ridge(alpha=0.02,fit_intercept=True,max_iter=10000)
# 構建嶺迴歸器物件,使用的偏差係數為alpha=0.02
ridge_regressor.fit(whole_x,whole_y) # 使用嶺迴歸器進行訓練
# 使用訓練完成的嶺迴歸器預測數值
y_train_predict=ridge_regressor.predict(whole_x)
plt.scatter(whole_x,whole_y)
plt.plot(whole_x,y_predict,'-b')
plt.plot(whole_x,y_train_predict,'-r')
複製程式碼
########################小**********結###############################
1,圖中藍色線條為使用線性迴歸器得到的結果,紅色為使用alpha為0.02時得到的擬合直線,兩個重合了,貌似是alpha取值太小所致。
2,alpha引數控制嶺迴歸器的複雜程度,但alpha趨近於0時,嶺迴歸器就相當於普通的最小二乘法(從圖中可以看出,alpha=0.02時,兩條直線幾乎重合),所以,如果希望該嶺迴歸器模型對異常值不那麼敏感,就需要設定一個比較大的alpha值(《Python機器學習經典例項》是這麼說的,但我表示嚴重懷疑,原因後面會講)。所以alpha值的選取是個技術活。
#################################################################
2. 嶺迴歸器模型的評估
同樣的,和線性迴歸器模型一樣,我們也需要對嶺迴歸器模型進行評估,以考察該模型的好壞。同樣的,可以使用均方誤差MSE來評估該模型的好壞。
為了對本模型進行評估,需要有評估所需的測試集,此處我自己用隨機數生成了一些測試集,如下為測試集生成程式碼。
# 嶺迴歸器模型的評估
# 第一步:構建評估資料,即test set
test_x=np.arange(10,20) # 自變數,隨便定義的
shift=np.random.normal(size=test_x.shape)
test_x=test_x+shift # 對test_x進行偏置得到測試集的X
error=np.random.normal(size=x.shape)
test_y=1.8*test_x+5.9+error # 新增隨機數作為噪音
plt.scatter(whole_x,whole_y,color='blue',label='train_set')
plt.scatter(test_x,test_y,color='red',label='test_set')
plt.legend()
# 把train set和test set都繪製到一個圖中,可以看出偏差不大
複製程式碼
從上面資料點的分佈來看,除了train set中異常點的位置沒有test set之外,其餘點都和test set的分佈是一致的,表明這個test set可以用於進行測試。
# 第二步:使用test set計算MSE
y_test_predict=ridge_regressor.predict(test_x.reshape(-1,1))
# 第三步:使用評價指標來評估模型的好壞
import sklearn.metrics as metrics
test_y=test_y.reshape(-1,1)
print('平均絕對誤差:{}'.format(
round(metrics.mean_absolute_error(y_test_predict,test_y),2)))
print('均方誤差MSE:{}'.format(
round(metrics.mean_squared_error(y_test_predict,test_y),2)))
print('中位數絕對誤差:{}'.format(
round(metrics.median_absolute_error(y_test_predict,test_y),2)))
print('解釋方差分:{}'.format(
round(metrics.explained_variance_score(y_test_predict,test_y),2)))
print('R方得分:{}'.format(
round(metrics.r2_score(y_test_predict,test_y),2)))
複製程式碼
-------------------------------------輸---------出--------------------------------
平均絕對誤差:2.29 均方誤差MSE:8.32 中位數絕對誤差:2.0 解釋方差分:0.53 R方得分:0.33
--------------------------------------------完-------------------------------------
########################小**********結###############################
1,在考察train set和test set時需要考慮兩個資料集中資料點的分佈,儘量使得兩者在空間上的分佈一致,這樣才能準確地測試模型。
2,本次模型在test set上的效果並不太理想,雖然得到的MSE比較小,但是R方得分,解釋方差分都偏低,看來還有改進的餘地。
#################################################################
3. 嶺迴歸器模型的改進
從上面得到的嶺迴歸器模型在測試集上的表現可以看出,本模型效果並不太好,可以改進的地方至少有兩個,一個是對train set中資料點進行刪減,比如發現了很明顯偏離的異常點,可以直接將這些異常點刪除,但是這就涉及到異常點的鑑定方法,把哪些點標註為可以刪除的異常點,偏離中心位置多少距離就被認定為異常點,等等。另外一個方法是優化嶺迴歸器的alpha值,下面是我嘗試優化alpha的結果圖。
# 對嶺迴歸器模型進行優化,主要優化alpha的取值
alpha_candidates=[-20,-10,-5.0,-2.0,2.0,5.0,10,20,50]
from sklearn import linear_model
for alpha in alpha_candidates:
ridge_regressor=linear_model.Ridge(alpha=alpha,fit_intercept=True,max_iter=10000)
# 構建嶺迴歸器物件,使用不同的alpha值
ridge_regressor.fit(whole_x,whole_y) # 使用嶺迴歸器進行訓練
# 使用訓練完成的嶺迴歸器預測數值
y_train_predict=ridge_regressor.predict(whole_x)
plt.plot(whole_x,y_train_predict,label='alpha='+str(alpha))
plt.legend()
plt.scatter(whole_x,whole_y)
複製程式碼
從上圖中可以看出,隨著alpha的取值由0到50慢慢變化,擬合直線會進行順時針旋轉,或者是朝向異常點的方向旋轉,貌似是因為alpha越大,異常點對直線越“吸引”,故而擬合直線收到異常點的影響也越來越大。而alpha由0變到負值,且越來越小時,異常點對擬合直線的影響也越來越小。(不知道這麼總結對不對)。雖然linear_model. Ridge的函式說明文件要求alpha取值是positive float,但是此處負值貌似也沒有問題,而且貌似alpha為負值時擬合的更好一些。
下面列印出各種不同alpha值時的各種誤差:
# 對嶺迴歸器模型進行優化,使用不同alpha值優化後得到的模型計算測試集
alpha_candidates=[-20,-10,-5.0,-2.0,2.0,5.0,10,20,50]
from sklearn import linear_model
for alpha in alpha_candidates:
ridge_regressor=linear_model.Ridge(alpha=alpha,fit_intercept=True,max_iter=10000)
# 構建嶺迴歸器物件,使用不同的alpha值
ridge_regressor.fit(whole_x,whole_y) # 使用嶺迴歸器進行訓練
y_test_predict=ridge_regressor.predict(test_x.reshape(-1,1))
print('------------alpha='+str(alpha)+'---------------------->>>')
print('均方誤差MSE:{}'.format(
round(metrics.mean_squared_error(y_test_predict,test_y),2)))
print('中位數絕對誤差:{}'.format(
round(metrics.median_absolute_error(y_test_predict,test_y),2)))
print('解釋方差分:{}'.format(
round(metrics.explained_variance_score(y_test_predict,test_y),2)))
print('R方得分:{}'.format(
round(metrics.r2_score(y_test_predict,test_y),2)))
複製程式碼
-------------------------------------輸---------出--------------------------------
------------alpha=-20----------------------
均方誤差MSE:4.93
中位數絕對誤差:1.1
解釋方差分:0.87
R方得分:0.75
------------alpha=-10----------------------
均方誤差MSE:6.61
中位數絕對誤差:1.62
解釋方差分:0.73
R方得分:0.57
------------alpha=-5.0----------------------
均方誤差MSE:7.47
中位數絕對誤差:1.84
解釋方差分:0.64
R方得分:0.46
------------alpha=-2.0----------------------
均方誤差MSE:7.98
中位數絕對誤差:1.96
解釋方差分:0.57
R方得分:0.38
------------alpha=2.0----------------------
均方誤差MSE:8.66
中位數絕對誤差:2.03
解釋方差分:0.48
R方得分:0.27
------------alpha=5.0----------------------
均方誤差MSE:9.15
中位數絕對誤差:2.06
解釋方差分:0.41
R方得分:0.18
------------alpha=10----------------------
均方誤差MSE:9.95
中位數絕對誤差:2.12
解釋方差分:0.27
R方得分:0.02
------------alpha=20----------------------
均方誤差MSE:11.47
中位數絕對誤差:2.33
解釋方差分:-0.05
R方得分:-0.36
------------alpha=50----------------------
均方誤差MSE:15.31
中位數絕對誤差:2.78
解釋方差分:-1.36
R方得分:-1.87
########################小**********結###############################
1,通過修改alpha值,可以修改模型在test set上的表現,由於test set不包含異常點,所以要求alpha取的越小越好,擬合的直線越偏離異常點。
2,alpha取值越大,嶺迴歸得到的擬合直線越“偏向”異常點,此時異常點對直線的影響越大;alpha越小,擬合直線越“偏離”異常點,此時異常點對直線的影響越小,我們所期望的是異常點對直線影響越小越好,所以貌似alpha應該往越小的方向取值。
#################################################################
注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。
參考資料:
1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯