【機器學習系列之七】模型調優與模型融合(程式碼應用篇)
這是本人對模型的融合的程式碼合集,環境是python3,只要複製過去就可以用了,非常方便。
- 目錄
- 1.交叉驗證
1.1 原理
1.2 GridSearchCV - 2.繪製學習曲線
- 3.stacking
3.1 stacking原理
3.2 程式碼實現不同版本的stacking
3.2.1.官網給的例子(簡單粗暴)
3.2.2 用概率作為第二層模型的特徵
3.2.3 特徵多樣性
3.2.4.引數詳解
1.交叉驗證
1.1 原理
基本思想就是把訓練資料分成幾份,分別為訓練集和測試集,用訓練集和驗證集作為模型選擇。最典型的是k折交叉驗證:
K折交叉驗證,初始取樣分割成K個子樣本,一個單獨的子樣本被保留作為驗證模型的資料,其他K-1個樣本用來訓練。交叉驗證重複K次,每個子樣本驗證一次,平均K次的結果或者使用其它結合方式,最終得到一個單一估測。這個方法的優勢在於,同時重複運用隨機產生的子樣本進行訓練和驗證,每次的結果驗證一次,10次交叉驗證是最常用的。
如圖是5折,因此可以做五次驗證,然後對結果進行平均。
1.2 GridSearchCV
一般使用GridSearchCV來做格點搜尋尋找最優引數,程式碼如下:
import numpy as np
import pandas as pd
from pandas import DataFrame
from patsy import dmatrices
import string
from operator import itemgetter
import json
from sklearn.ensemble import RandomForestClassifier,GradientBoostingClassifier
from sklearn.cross_validation import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.cross_validation import train_test_split,StratifiedShuffleSplit,StratifiedKFold
from sklearn import preprocessing
from sklearn.metrics import classification_report
from sklearn.externals import joblib
seed=0
clf=GradientBoostingClassifier(n_estimators=500)
###grid search找到最好的引數
param_grid = dict( )
##建立分類pipeline
pipeline=Pipeline([ ('clf',clf) ])
grid_search = GridSearchCV(pipeline, param_grid=param_grid, verbose=3,scoring='accuracy',\
cv=StratifiedShuffleSplit(y_train, n_iter=10, test_size=0.2,random_state=seed)).fit(x_train, y_train)
# 對結果打分
print("Best score: %0.3f" % grid_search.best_score_)
print(grid_search.best_estimator_)
print('-----grid search end------------')
print ('on all train set')
scores = cross_val_score(grid_search.best_estimator_, x_train, y_train,cv=3,scoring='accuracy')
print (scores.mean(),scores)
print ('on test set')
scores = cross_val_score(grid_search.best_estimator_, x_test, y_test,cv=3,scoring='accuracy')
print(scores.mean(),scores)
結果如下:
把輸出的引數複製一下就OK了。
2.繪製學習曲線
要是模型太複雜,導致過擬合,對測試集的預測就不會太好,該怎麼判斷模型是否過擬合呢?
對的,就是學習曲線
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
plt.figure()
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
label="Cross-validation score")
plt.legend(loc="best")
return plt
title = "Learning Curves (Random Forest, n_estimators = 100)"
cv = cross_validation.ShuffleSplit(df_train_data.shape[0], n_iter=10,test_size=0.2, random_state=0)
estimator = RandomForestRegressor(n_estimators = 100)
plot_learning_curve(estimator, title, X, y, (0.0, 1.01), cv=cv, n_jobs=4)
plt.show()
一看就是欠擬合了,因為資料,特徵都太少,模型學習的非常容易,所以在訓練集上做的非常好,但是在測試集上做的特別差。其實主要看他們的差距,差距特別大,多半是不正常。
發現他過擬合之後該怎麼辦了呢?
對於過擬合:
1.找更多的資料來學習
2.增大正則化係數
3. 減少特徵個數
對於欠擬合:
1. 找更多的特徵
2. 減少正則化係數
權重分析
對於線性的模型,比如邏輯迴歸,線性迴歸,或者線性的SVM,可以把權重絕對值高/低的特徵拿出來,做特徵組合,或者特徵細分。
3.stacking
3.1 stacking原理
原理也很簡單,就是把很多基分類器,比如邏輯迴歸,隨機森林,GBDT等分類器,預測的結果,作為特徵,再來用分類器預測一波最終的結果。
說詳細點吧。
假設現在有三個分類器,分別是M1,M2,M3.
1.用M1對訓練集訓練,然後預測訓練集和測試集(看清楚哦)的標籤列。分別是
對M2,M3重複上述操作,得到
2.對他們分別組合,得到新的訓練集和測試集:
- 然後用這M4來預測:
但是這樣會造成過擬合,因為我們用訓練集訓練,然後又去預測了訓練集,所以很多時候stacking的效果也不見得會很好,這個需要根據具體情況來看。
一般優化的思路是用交叉驗證:
以2折交叉驗證為例:假設資料集為D1,D2,先用D1訓練模型,然後預測D2,得到
對於T1,有兩種方法可以得到:
A.對D1訓練的時候,預測了D2,同時也可以對整個測試集進行預測得到
B. 第二種就是直接把
該圖是五折交叉驗證,得到P1,T1的過程,通過對訓練集的五次預測,組合得到P1的過程,這個得到的只是一個第二層的輸入中的一個特徵。
3.2 程式碼實現不同版本的stacking
重要的還是看一下怎麼使用,在python中專門集成了用於stacking的庫mlxtend,它能很快的完成模型的stacking。
這個的使用也有幾種方法:
3.2.1.官網給的例子
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import itertools
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from mlxtend.classifier import EnsembleVoteClassifier
from mlxtend.data import iris_data
from mlxtend.plotting import plot_decision_regions
# Initializing Classifiers
clf1 = LogisticRegression(random_state=0)
clf2 = RandomForestClassifier(random_state=0)
clf3 = SVC(random_state=0, probability=True)
eclf = EnsembleVoteClassifier(clfs=[clf1, clf2, clf3], weights=[2, 1, 1], voting='soft')
# Loading some example data
X, y = iris_data()
X = X[:,[0, 2]]
# Plotting Decision Regions
gs = gridspec.GridSpec(2, 2)
fig = plt.figure(figsize=(10, 8))
for clf, lab, grd in zip([clf1, clf2, clf3, eclf],
['Logistic Regression', 'Random Forest', 'RBF kernel SVM', 'Ensemble'],
itertools.product([0, 1], repeat=2)):
clf.fit(X, y)
ax = plt.subplot(gs[grd[0], grd[1]])
fig = plot_decision_regions(X=X, y=y, clf=clf, legend=2)
plt.title(lab)
plt.show()
可得
貌似非常粗暴吧。
3.2.2 用概率作為第二層模型的特徵
用概率值作為最終模型的輸入,這個只要加一個引數,use_probas=True,average_probas=False。這裡的average_probas要設定為False,如果設定為True,概率會被平均,比如:
classifier 1: [0.2, 0.5, 0.3]
classifier 2: [0.3, 0.4, 0.4]
1) average = True :
產生的meta-feature 為:[0.25, 0.45, 0.35]
2) average = False:
產生的meta-feature為:[0.2, 0.5, 0.3, 0.3, 0.4, 0.4]
clf1 = KNeighborsClassifier(n_neighbors=1)
clf2 = RandomForestClassifier(random_state=1)
clf3 = GaussianNB()
lr = LogisticRegression()
sclf = StackingClassifier(classifiers=[clf1, clf2, clf3],
use_probas=True,
average_probas=False,
meta_classifier=lr)
print('3-fold cross validation:\n')
for clf, label in zip([clf1, clf2, clf3, sclf],
['KNN',
'Random Forest',
'Naive Bayes',
'StackingClassifier']):
scores = model_selection.cross_val_score(clf, X, y,
cv=3, scoring='accuracy')
print("Accuracy: %0.2f (+/- %0.2f) [%s]"
% (scores.mean(), scores.std(), label))
3.2.3 特徵多樣性
還有一種操作是給每個基分類器分不同的特徵進行訓練,比如有兩個分類器M1,M2,M1可以訓練前半部分特徵,M2可以訓練後半部分特徵。這個可以通過sklearn中的pipelines實現。
from sklearn.datasets import load_iris
from mlxtend.classifier import StackingClassifier
from mlxtend.feature_selection import ColumnSelector
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
iris = load_iris()
X = iris.data
y = iris.target
pipe1 = make_pipeline(ColumnSelector(cols=(0, 2)),
LogisticRegression())
pipe2 = make_pipeline(ColumnSelector(cols=(1, 2, 3)),
LogisticRegression())
sclf = StackingClassifier(classifiers=[pipe1, pipe2],
meta_classifier=LogisticRegression())
sclf.fit(X, y)
3.2.4.引數詳解
StackingClassifier 使用API及引數解析:
StackingClassifier(classifiers, meta_classifier, use_probas=False, average_probas=False, verbose=0, use_features_in_secondary=False)
引數:
classifiers : 基分類器,陣列形式,[cl1, cl2, cl3]. 每個基分類器的屬性被儲存在類屬性 self.clfs_.
meta_classifier : 目標分類器,即將前面分類器合起來的分類器
use_probas : bool (default: False) ,如果設定為True, 那麼目標分類器的輸入就是前面分類輸出的類別概率值而不是類別標籤
average_probas : bool (default: False),用來設定上一個引數當使用概率值輸出的時候是否使用平均值。
verbose : int, optional (default=0)。用來控制使用過程中的日誌輸出,當 verbose = 0時,什麼也不輸出, verbose = 1,輸出迴歸器的序號和名字。verbose = 2,輸出詳細的引數資訊。verbose > 2, 自動將verbose設定為小於2的,verbose -2.
use_features_in_secondary : bool (default: False). 如果設定為True,那麼最終的目標分類器就被基分類器產生的資料和最初的資料集同時訓練。如果設定為False,最終的分類器只會使用基分類器產生的資料訓練。
方法屬性:
clfs_ : 每個基分類器的屬性,list, shape 為 [n_classifiers]。
meta_clf_ : 最終目標分類器的屬性
方法:
fit(X, y)
fit_transform(X, y=None, fit_params)
get_params(deep=True),如果是使用sklearn的GridSearch方法,那麼返回分類器的各項引數。
predict(X)
predict_proba(X)
score(X, y, sample_weight=None), 對於給定資料集和給定label,返回評價accuracy
set_params(params),設定分類器的引數,params的設定方法和sklearn的格式一樣