用scikit-learn和pandas學習Ridge迴歸

劉建平Pinard發表於2016-11-02

    本文將用一個例子來講述怎麼用scikit-learn和pandas來學習Ridge迴歸。

1. Ridge迴歸的損失函式

    在我的另外一遍講線性迴歸的文章中,對Ridge迴歸做了一些介紹,以及什麼時候適合用 Ridge迴歸。如果對什麼是Ridge迴歸還完全不清楚的建議閱讀我這篇文章。

    線性迴歸原理小結

    Ridge迴歸的損失函式表達形式是:    

    \(J(\mathbf\theta) = \frac{1}{2}(\mathbf{X\theta} - \mathbf{Y})^T(\mathbf{X\theta} - \mathbf{Y}) + \frac{1}{2}\alpha||\theta||_2^2\)

    其中\(\alpha\)為常數係數,需要進行調優。\(||\theta||_2\)為L2範數。

    演算法需要解決的就是在找到一個合適的超引數\(\alpha\)情況下,求出使\(J(\mathbf\theta)\)最小的\(\theta\)。一般可以用梯度下降法和最小二乘法來解決這個問題。scikit-learn用的是最小二乘法。

2. 資料獲取與預處理

    這裡我們仍然用UCI大學公開的機器學習資料來跑Ridge迴歸。

    資料的介紹在這: http://archive.ics.uci.edu/ml/datasets/Combined+Cycle+Power+Plant

    資料的下載地址在這: http://archive.ics.uci.edu/ml/machine-learning-databases/00294/

    完整的程式碼見我的github: https://github.com/ljpzzz/machinelearning/blob/master/classic-machine-learning/ridge_regression_1.ipynb

    裡面是一個迴圈發電場的資料,共有9568個樣本資料,每個資料有5列,分別是:AT(溫度), V(壓力), AP(溼度), RH(壓強), PE(輸出電力)。我們不用糾結於每項具體的意思。

    我們的問題是得到一個線性的關係,對應PE是樣本輸出,而AT/V/AP/RH這4個是樣本特徵, 機器學習的目的就是通過調節超引數\(\alpha\)得到一個線性迴歸模型,即:

    \(PE = \theta_0 + \theta_1*AT + \theta_2*V + \theta_3*AP + \theta_4*RH\)

    使損失函式\(J(\mathbf\theta)\)最小。而需要學習的,就是\(\theta_0, \theta_1, \theta_2, \theta_3, \theta_4\)這5個引數。

    下載後的資料可以發現是一個壓縮檔案,解壓後可以看到裡面有一個xlsx檔案,我們先用excel把它開啟,接著“另存為“”csv格式,儲存下來,後面我們就用這個csv來執行Ridge迴歸。

     這組資料並不一定適合用Ridge迴歸模型,實際上這組資料是高度線性的,使用正則化的Ridge迴歸僅僅只是為了講解方便。

3. 資料讀取與訓練集測試集劃分

    我們先開啟ipython notebook,新建一個notebook。當然也可以直接在python的互動式命令列裡面輸入,不過還是推薦用notebook。下面的例子和輸出我都是在notebook裡面跑的。

    先把要匯入的庫宣告瞭:

import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import pandas as pd
from sklearn import datasets, linear_model

    接著用pandas讀取資料:

# read_csv裡面的引數是csv在你電腦上的路徑,此處csv檔案放在notebook執行目錄下面的CCPP目錄裡
data = pd.read_csv('.\CCPP\ccpp.csv')

    我們用AT, V,AP和RH這4個列作為樣本特徵。用PE作為樣本輸出:

X = data[['AT', 'V', 'AP', 'RH']]
y = data[['PE']]

    接著把資料集劃分為訓練集和測試集:

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

    

4. 用scikit-learn執行Ridge迴歸

    要執行Ridge迴歸,我們必須要指定超引數\(\alpha\)。你也許會問:“我也不知道超引數是多少啊?” 我也不知道,那麼我們隨機指定一個(比如1),後面我們會講到用交叉驗證從多個輸入超引數\(\alpha\)中快速選擇最優超引數的辦法。

from sklearn.linear_model import Ridge
ridge = Ridge(alpha=1)
ridge.fit(X_train, y_train)

    訓練完了,可以看看模型引數是多少:

print ridge.coef_
print ridge.intercept_

    輸出結果如下:

[[-1.97373209 -0.2323016   0.06935852 -0.15806479]]
[ 447.05552892]

    也就是說我們得到的模型是:

\(PE = 447.05552892 - 1.97373209*AT - 0.2323016*V + 0.06935852*AP - 0.15806479*RH\)

    但是這樣還沒有完?為什麼呢,因為我們假設了超引數\(\alpha\)為1, 實際上我們並不知道超引數\(\alpha\)取多少最好,實際研究是需要在多組自選的\(\alpha\)中選擇一個最優的。

    那麼我們是不是要把上面這段程式在N種\(\alpha\)的值情況下,跑N遍,然後再比較結果的優劣程度呢? 可以這麼做,但是scikit-learn提供了另外一個交叉驗證選擇最優\(\alpha\)的API,下面我們就用這個API來選擇\(\alpha\)。

5. 用scikit-learn選擇Ridge迴歸超引數\(\alpha\)

    這裡我們假設我們想在這10個\(\alpha\)值中選擇一個最優的值。程式碼如下:

from sklearn.linear_model import RidgeCV
ridgecv = RidgeCV(alphas=[0.01, 0.1, 0.5, 1, 3, 5, 7, 10, 20, 100])
ridgecv.fit(X_train, y_train)
ridgecv.alpha_  

    輸出結果為:7.0,說明在我們給定的這組超引數中, 7是最優的\(\alpha\)值。

6. 用scikit-learn研究超引數\(\alpha\)和迴歸係數\(\theta\)的關係

    通過Ridge迴歸的損失函式表示式可以看到,\(\alpha\)越大,那麼正則項懲罰的就越厲害,得到迴歸係數\(\theta\)就越小,最終趨近與0。而如果\(\alpha\)越小,即正則化項越小,那麼迴歸係數\(\theta\)就越來越接近於普通的線性迴歸係數。

    這裡我們用scikit-learn來研究這種Ridge迴歸的變化,例子參考了scikit-learn的官網例子。我們單獨啟動一個notebook或者python shell來執行這個例子。

    完整的程式碼見我的github: https://github.com/ljpzzz/machinelearning/blob/master/classic-machine-learning/ridge_regression.ipynb

    首先還是載入類庫:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
%matplotlib inline

    接著我們自己生成一個10x10的矩陣X,表示一組有10個樣本,每個樣本有10個特徵的資料。生成一個10x1的向量y代表樣本輸出。

# X is a 10x10 matrix
X = 1. / (np.arange(1, 11) + np.arange(0, 10)[:, np.newaxis])
# y is a 10 x 1 vector
y = np.ones(10)

    這樣我們的資料有了,接著就是準備超引數\(\alpha\)了。我們準備了200個超引數,來分別跑 Ridge迴歸。準備這麼多的目的是為了後面畫圖看\(\alpha\)和\(\theta\)的關係

n_alphas = 200
# alphas count is 200, 都在10的-10次方和10的-2次方之間
alphas = np.logspace(-10, -2, n_alphas)

    有了這200個超引數\(\alpha\),我們做200次迴圈,分別求出各個超引數對應的\(\theta\)(10個維度),存起來後面畫圖用。

clf = linear_model.Ridge(fit_intercept=False)
coefs = []
# 迴圈200次
for a in alphas:
    #設定本次迴圈的超引數
    clf.set_params(alpha=a)
    #針對每個alpha做ridge迴歸
    clf.fit(X, y)
    # 把每一個超引數alpha對應的theta存下來
    coefs.append(clf.coef_)

    好了,有了200個超引數\(\alpha\),以及對應的\(\theta\),我們可以畫圖了。我們的圖是以\(\alpha\)為x軸,\(\theta\)的10個維度為y軸畫的。程式碼如下:

ax = plt.gca()

ax.plot(alphas, coefs)
#將alpha的值取對數便於畫圖
ax.set_xscale('log')
#翻轉x軸的大小方向,讓alpha從大到小顯示
ax.set_xlim(ax.get_xlim()[::-1]) 
plt.xlabel('alpha')
plt.ylabel('weights')
plt.title('Ridge coefficients as a function of the regularization')
plt.axis('tight')
plt.show()

    最後得到的圖如下:

  

   從圖上也可以看出,當\(\alpha\)比較大,接近於\(10^{-2}\)的時候,\(\theta\)的10個維度都趨於0。而當\(\alpha\)比較小,接近於\(10^{-10}\)的時候,\(\theta\)的10個維度都趨於線性迴歸的迴歸係數。

 

(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com) 

相關文章