作為一個愛學習,愛分享,愛裝逼,愛鬥圖,愛吃辣條,有夢想的年輕人,小天今天決定好好嘮一嘮超參數與模型驗證。感興趣的同學可以回顧一下之前分享的Scikit-Learn【上篇】、【下篇】,準備好辣條,車要開了~~
有監督機器學習模型的基本步驟:
(1) 選擇模型類;
(2) 選擇模型超參數;
(3) 用模型擬合訓練數據;
(4) 用模型預測新數據的標籤。
模型選擇和超參數選擇——可能是有效使用各種機器學習工具和技術的最重要階段。
模型驗證(model validation)其實很簡單,就是在選擇模型和超參數之後,通過對訓練數據進行學習,對比模型對已知數據的預測值與實際值的差異。
鳶尾花數據來演示一個簡單的模型驗證方法:
from sklearn.datasets import load_irisiris = load_iris()X = iris.datay = iris.target使用一個k 近鄰分類器,超參數為n_neighbors=1。這是一個 非常簡單直觀的模型,「新數據的標籤與其最接近的訓練數據的標籤相同」:
from sklearn.neighbors import KNeighborsClassifiermodel = KNeighborsClassifier(n_neighbors=1)訓練模型,並用它來預測已知標籤的數據:
model.fit(X, y)y_model = model.predict(X)計算模型的準確率:
from sklearn.metrics import accuracy_scoreaccuracy_score(y, y_model)準確得分是1.0,也就是說模型識別標籤的正確率是100% !但是這樣測量的準確率可靠嗎?我們真的有一個在任何時候準確率都是100% 的模型嗎?
答案是否定的。其實這個方法有個根本缺陷:它用同一套數據訓練和評估模型。另外,最近鄰模型是一種與距離相關的評估器,只會簡單地存儲訓練數據,然後把新數據與存儲的已知數據進行對比來預測標籤。在理想情況下,模型的準確率總是100%。
留出集可以更好地評估模型性能,也就是說,先從訓練模型 的數據中留出一部分,然後用這部分留出來的數據來檢驗模型性能。在Scikit-Learn 裡面用train_test_split 工具就可以實現:
from sklearn.cross_validation import train_test_split# split the data with 50% in each setX1, X2, y1, y2 = train_test_split(X, y, random_state=0, train_size=0.5)
# fit the model on one set of datamodel.fit(X1, y1)
# evaluate the model on the second set of datay2_model = model.predict(X2)accuracy_score(y2, y2_model)
最近鄰分類器在這份留出集上的準確率是90%。這裡的留出集類似新數據,因為模型之前沒有「接觸」過它們。
用留出集進行模型驗證一個缺點,就是模型失去了一部分訓練機會。有一半數據都沒有為模型訓練做出貢獻。這顯然不是最優解,而且可能還會出現問題——尤其是在訓練數據集規模比較小的時候。
解決這個問題的方法是交叉檢驗,也就是做一組擬合,讓數據的每個子集既是訓練集,又是驗證集。
進行兩輪驗證實驗,輪流用一半數據作為留出集;
y2_model = model.fit(X1, y1).predict(X2)y1_model = model.fit(X2, y2).predict(X1)accuracy_score(y1, y1_model), accuracy_score(y2, y2_model)這樣就可以獲得兩個準確率,將二者結合(例如求均值)獲取一個更準確的模型總體性能。這種形式的交叉檢驗被稱為兩輪交叉檢驗——將數據集分成兩個子集,依次將每個子集作為驗證集。
五輪交叉檢驗
from sklearn.cross_validation import cross_val_scorecross_val_score(model, X, y, cv=5)Scikit-Learn 為不同應用場景提供了各種交叉檢驗方法,都以迭代器(iterator)形式在cross_validation 模塊中實現。例如,我們可能會遇到交叉檢驗的輪數與樣本數相同的極端情況,也就是說我們每次只有一個樣本做測試,其他樣本全用於訓練。 這種交叉檢驗。這種交叉檢驗類型被稱為LOO(leave-one-out,只留一個)交叉檢驗。
from sklearn.cross_validation import LeaveOneOutscores = cross_val_score(model, X, y, cv=LeaveOneOut(len(X)))scores
由於我們有150 個樣本,留一法交叉檢驗會生成150 輪試驗,每次試驗的預測結果要麼成功(得分1.0),要麼失敗(得分0.0)。計算所有試驗準確率的均值就可以得到模型的預測 準確性了:
了解更多關於Scikit-Learn 交叉檢驗的內容,可以用IPython 探索sklearn.cross_validation 子模塊,也可以瀏覽Scikit-Learn 的交叉檢驗文 (http://scikitlearn.org/stable/modules/cross_validatio檔n.html)。
關鍵問題是:假如模型效果不好,應該如何改善?答案可能有以下幾種。
• 用更複雜/ 更靈活的模型。
• 用更簡單/ 更確定的模型。
• 採集更多的訓練樣本。
• 為每個樣本採集更多的特徵。問題的答案往往與直覺相悖。換一種更複雜的模型有時可能產生更差的結果,增加更多的訓練樣本也未必能改善性能!改善模型能力的高低,是區分機器學習實踐者成功與否的標誌。
「最優模型」的問題基本可以看成是找出偏差與方差平衡點的問題:
(高偏差與高方差回歸模型)
左邊的模型希望從數據中找到一條直線。但由於數據本質上比直線要複雜,直線永遠不可能很好地描述這份數據。這樣的模型被認為是對數據欠擬合;也就是說,模型沒有足夠的靈活性來適應數據的所有特徵。另一種說法就是模型具有高偏差。
右邊的模型希望用高階多項式擬合數據。雖然這個模型有足夠的靈活性可以近乎完美地適應數據的所有特徵,但與其說它是十分準確地描述了訓練數據,不如說它是過多地學習了數據的噪音,而不是數據的本質屬性。這樣的模型被認為是對數據過擬合,也就是模型過於靈活,在適應數據所有特徵的同時,也適應了隨機誤差。另一種說法就是模型具有高方差。
換個角度,如果用兩個模型分別預測y 軸的數據,看看是什麼效果,淺紅色的點是被預測數據集遺漏的點。
(高偏差與高方差模型的訓練得分與驗證得分)
這個分數是R2,也稱為判定係數 (https://en.wikipedia.org/wiki/Coefficient_of_determination), 用來衡量模型與目標值均值的對比結果。 R2 = 1 表示模型與數據完全吻合,R2 = 0 表示模型不比簡單取均值好,R2 為負表示模型性能很差。
• 對於高偏差模型,模型在驗證集的表現與在訓練集的表現類似。
• 對於高方差模型,模型在驗證集的表現遠遠不如在訓練集的表現。
驗證曲線,具有以下特徵:
• 訓練得分肯定高於驗證得分。一般情況下,模型擬合自己接觸過的數據,比擬合沒接觸過的數據效果要好。
• 使用複雜度較低的模型(高偏差)時,訓練數據往往欠擬合,說明模型對訓練數據和新數據都缺乏預測能力。
• 而使用複雜度較高的模型(高方差)時,訓練數據往往過擬合,說明模型對訓練數據預測能力很強,但是對新數據的預測能力很差。
• 當使用複雜度適中的模型時,驗證曲線得分最高。說明在該模型複雜度條件下,偏差與方差達到均衡狀態。
(模型複雜度、訓練得分與驗證得分的方法關係圖)
用多項式回歸模型,它是 線性回歸模型的一般形式,其多項式的次數是一個可調參數。例如,多項式次數為1 其實就是將數據擬合成一條直線。若模型有參數a 和b,則模型為:
多項式次數為3,則是將數據擬合成一條三次曲線。若模型有參數a、b、c、d,則模型為:
在Scikit-Learn 中,可以用一個帶多項式預處 理器的簡單線性回歸模型實現。我們將用一個管道命令來組合這兩種操作。
from sklearn.preprocessing import PolynomialFeaturesfrom sklearn.linear_model import LinearRegressionfrom sklearn.pipeline import make_pipeline
def PolynomialRegression(degree=2, **kwargs): return make_pipeline(PolynomialFeatures(degree), LinearRegression(**kwargs))import numpy as np
def make_data(N, err=1.0, rseed=1): # randomly sample the data rng = np.random.RandomState(rseed) X = rng.rand(N, 1) ** 2 y = 10 - 1. / (X.ravel() + 0.1) if err > 0: y += err * rng.randn(N) return X, y
X, y = make_data(40)數據可視化
%matplotlib inlineimport matplotlib.pyplot as pltimport seaborn; seaborn.set()
X_test = np.linspace(-0.1, 1.1, 500)[:, None]
plt.scatter(X.ravel(), y,s=20, color='black')axis = plt.axis()for degree in [1, 3, 5]: y_test = PolynomialRegression(degree).fit(X, y).predict(X_test) plt.plot(X_test.ravel(), y_test, label='degree={0}'.format(degree))plt.xlim(-0.1, 1.0)plt.ylim(-2, 12)plt.legend(loc='best');
究竟多項式的次數是多少,才能在偏差(欠擬合)與方差(過擬合)間達到平衡?
我們可以通過可視化驗證曲線來回答這個問題——利用Scikit-Learn 的validation_curve函數就可以非常簡單地實現。只要提供模型、數據、參數名稱和驗證範圍信息,函數就會自動計算驗證範圍內的訓練得分和驗證得分。
from sklearn.model_selection import validation_curvedegree = np.arange(0, 21)train_score, val_score = validation_curve(PolynomialRegression(), X, y, 'polynomialfeatures__degree', degree, cv=7)
plt.plot(degree, np.median(train_score, 1), color='blue', label='training score')plt.plot(degree, np.median(val_score, 1), color='red', label='validation score')plt.legend(loc='best')plt.ylim(0, 1)plt.xlabel('degree')plt.ylabel('score');
訓練得分總是比驗證得分高;訓練得分隨著模型複雜度的提升而單調遞增;驗證得分增長到最高點後由於過擬合而開始驟降。
偏差與方差均衡性最好的是三次多項式。我們可以計算結果,並 將模型畫在原始數據上。
plt.scatter(X.ravel(), y, s=20)lim = plt.axis()y_test = PolynomialRegression(3).fit(X, y).predict(X_test)plt.plot(X_test.ravel(), y_test);plt.axis(lim);
影響模型複雜度的另一個重要因素是最優模型往往受到訓練數據量的影響。例如,生成前面5倍的數據(200 個點)
X2, y2 = make_data(200)plt.scatter(X2.ravel(), y2, s=20);
degree = np.arange(21)train_score2, val_score2 = validation_curve(PolynomialRegression(), X2, y2, 'polynomialfeatures__degree', degree, cv=7)
plt.plot(degree, np.median(train_score2, 1), color='blue', label='training score')plt.plot(degree, np.median(val_score2, 1), color='red', label='validation score')plt.plot(degree, np.median(train_score, 1), color='blue', alpha=0.3, linestyle='dashed')plt.plot(degree, np.median(val_score, 1), color='red', alpha=0.3, linestyle='dashed')plt.legend(loc='lower center')plt.ylim(0, 1)plt.xlabel('degree')plt.ylabel('score');
實線是大數據集的驗證曲線,而虛線是前面小數據集的驗證曲線.
大數據集支持更複雜的模型:雖然得分頂點大概是六次多項式,但是即使到了二十次多項式,過擬合情況也不太嚴重——驗證得分與訓練得分依然十分接近。
通過觀察驗證曲線的變化趨勢,可以發現有兩個影響模型效果的因素:模型複雜度和訓練數據集的規模。通常,我們將模型看成是與訓練數據規模相關的函數,通過不斷擴大數據集的規模來擬合模型,以此來觀察模型的行為。反映訓練集規模的訓練得分/ 驗證得分曲線被稱為學習曲線(learning curve)。
學習曲線的特徵包括以下三點:
特定複雜度的模型對較小的數據集容易過擬合:此時訓練得分較高,驗證得分較低。
特定複雜度的模型對較大的數據集容易欠擬合:隨著數據的增大,訓練得分會不斷降低,而驗證得分會不斷升高。
模型的驗證集得分永遠不會高於訓練集得分:兩條曲線一直在靠近,但永遠不會交叉。
(學習曲線原理圖)
學習曲線最重要的特徵是,隨著訓練樣本數量的增加,分數會收斂到定值。因此,一旦你的數據多到使模型得分已經收斂,那麼增加更多的訓練樣本也無濟於事!改善模型性能的唯一方法就是換模型(通常也是換成更複雜的模型)。
計算前面數據集的二次多項式模型和九次多項式模型的學習曲線
from sklearn.learning_curve import learning_curve
fig, ax = plt.subplots(1, 2, figsize=(16, 6))fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
for i, degree in enumerate([2, 9]): N, train_lc, val_lc = learning_curve(PolynomialRegression(degree), X, y, cv=7, train_sizes=np.linspace(0.3, 1, 25))
ax[i].plot(N, np.mean(train_lc, 1), color='blue', label='training score') ax[i].plot(N, np.mean(val_lc, 1), color='red', label='validation score') ax[i].hlines(np.mean([train_lc[-1], val_lc[-1]]), N[0], N[-1], color='gray', linestyle='dashed')
ax[i].set_ylim(0, 1) ax[i].set_xlim(N[0], N[-1]) ax[i].set_xlabel('training size') ax[i].set_ylabel('score') ax[i].set_title('degree = {0}'.format(degree), size=14) ax[i].legend(loc='best')
這幅圖展現了模型得分隨著訓練數據規模的變化而變化。 當你的學習曲線已經收斂時,再增加訓練數據也不能再顯著改善擬合效果(類似左圖二次多項式)!
提高收斂得分的唯一辦法就是換模型,如右圖採用複雜度更高的模型之後,雖然學習曲線的收斂得分提高了(對比虛線所在位置)。
模型通常會有多個得分轉折點,因此驗證曲線和學習曲線的圖形會從二維曲線變成多維曲面。這種高維可視化很難展現,因此從圖中找出驗證得分的最大值也不是一件簡單的事。
Scikit-Learn 在grid_search 提供了一個自動化工具解決這個問題。
下面是用網格搜索尋找最優多項式回歸模型的示例。
from sklearn.grid_search import GridSearchCV
param_grid = {'polynomialfeatures__degree': np.arange(21), 'linearregression__fit_intercept': [True, False], 'linearregression__normalize': [True, False]}
grid = GridSearchCV(PolynomialRegression(), param_grid, cv=7)
調用fit()方法在每個網格點上擬合模型,並同時記錄每個點的得分模型擬合完成了,這樣就可以獲取最優參數了
還可以用最優參數的模型擬合數據,並畫圖顯示
model = grid.best_estimator_
plt.scatter(X.ravel(), y, s=20)lim = plt.axis()y_test = model.fit(X, y).predict(X_test)plt.plot(X_test.ravel(), y_test, hold=True);plt.axis(lim);
網格搜索提供了許多參數選項,包括自定義得分函數、並行計算,以及隨機化搜索等能力。關於更多內容參考Scikit-Learn 的網格搜索文檔。
(http://scikit-learn.org/stable/modules/grid_search.html)
本節首先探索了模型驗證與超參數優化的概念,重點介紹了偏差與方差均衡的概念,以及如何將這個概念應用在模型擬合過程中。尤其值得注意的是,我們發現通過驗證集或交叉檢驗方法調整參數至關重要,這樣做可以避免較複雜/ 靈活模型引起的過擬合問題。
接下來將介紹一些常用模型的具體細節、可用的優化方法,以及自由參數(free parameter)對模型複雜度的影響。
如果你也有想分享的乾貨,可以登錄天池實驗室(notebook),包括賽題的理解、數據分析及可視化、算法模型的分析以及一些核心的思路等內容。
小天會根據你分享內容的數量以及程度,給予豐富的神秘天池大禮以及糧票獎勵。分享成功後你也可以通過下方釘釘群👇主動聯繫我們的社區運營同學(釘釘號:yiwen1991)
天池寶貝們有任何問題,可在戳「留言」評論或加入釘釘群留言,小天會認真傾聽每一個你的建議!
點擊下方圖片即可閱讀
想深入了解線性回歸?用scikit-learn和pandas學習!
那麼如何了解線性回歸的原理和算法呢?
分類算法評估中的「它們」有什麼用處?