1. 程式人生 > 實用技巧 >網格搜尋解析GridSearchCV(基於決策樹)

網格搜尋解析GridSearchCV(基於決策樹)

1.1 網格搜尋介紹

機器學習演算法中有兩類引數:從訓練集中學習到的引數,比如邏輯斯蒂迴歸中的權重引數,另一類是模型的超引數,也就是需要人工設定的引數,比如正則項係數或者決策樹的深度。

前一節,我們使用驗證曲線來提高模型的效能,實際上就是找最優引數。這一節我們學習另一種常用的超引數尋優演算法:網格搜尋(grid search)。

網格搜尋聽起來高大上,實際上簡單的一筆,就是暴力搜尋而已,我們事先為每個引數設定一組值,然後窮舉各種引數組合,找到最好的那一組。

GridSearchCV中param_grid引數是字典構成的列表。對於線性SVM,我們只評估引數C;對於RBF核SVM,我們評估C和gamma。

最後, 我們通過best_parmas_得到最優引數組合。

sklearn人性化的一點是,我們可以直接利用最優引數建模(best_estimator_):

更方便的是,我們可以直接得到估計模型,和預測結果,至此我們發現,其實GridSearchCV 是一個很好的類,其不僅將交叉驗證和網格搜尋進行結合,還直接將最優的模型儲存,因此可以進行直接預測,而不是再依據最佳引數去擬合模型。

最後需要指出的是GridSearchCV 還有一個best_score_屬性,這個是交叉驗證的最高得分,而不是上面的grid_search.score(x_test,y_test))的得分,這個是我們最優模型下,對測試集的擬合得分,從另一個角度來說,我們不需要再在乎交叉驗證的最高得分,我們只需要在乎在交叉驗證和網格搜尋後,擬合的最佳模型並運用這個模型進行預測即可。

網格搜尋雖然不錯,但是窮舉過於耗時,sklearn中還實現了隨機搜尋,使用 RandomizedSearchCV類,隨機取樣出不同的引數組合。

GridSearchCV,它存在的意義就是自動調參,只要把引數輸進去,就能給出最優化的結果和引數。但是這個方法適合於小資料集,一旦資料的量級上去了,很難得出結果。這個時候就是需要動腦筋了。資料量比較大的時候可以使用一個快速調優的方法——座標下降。它其實是一種貪心演算法:拿當前對模型影響最大的引數調優,直到最優化;再拿下一個影響最大的引數調優,如此下去,直到所有的引數調整完畢。這個方法的缺點就是可能會調到區域性最優而不是全域性最優,但是省時間省力,巨大的優勢面前,還是試一試吧,後續可以再拿bagging再優化。

通常演算法不夠好,需要除錯引數時必不可少。比如SVM的懲罰因子C,核函式kernel,gamma引數等,對於不同的資料使用不同的引數,結果效果可能差1-5個點,sklearn為我們提供專門除錯引數的函式grid_search。

1.2 引數介紹 常用的5個 estimator param_grid scoring cv n_jobs

class sklearn.model_selection.GridSearchCV(estimator, param_grid, scoring=None, fit_params=None, n_jobs=1, iid=True, refit=True, cv=None, verbose=0, pre_dispatch=‘2*n_jobs’, error_score=’raise’, return_train_score=’warn’)

(1)estimator

選擇使用的分類器,簡而言之就是演算法模型。並且傳入除需要確定最佳的引數之外的其他引數。每一個分類器都需要一個scoring引數,或者score方法:estimator=RandomForestClassifier(min_samples_split=100,min_samples_leaf=20,max_depth=8,max_features='sqrt',random_state=10) --or------------ grid_search=GridSearchCV(Lasso(),params,cv=6),!!,Lasso()一定要有括號!!!

(2)param_grid

需要最優化的引數的取值,值為字典或者列表,例如:param_grid =param_test1,param_test1 = {'n_estimators':range(10,71,10)}。

(3)scoring=None

模型評價標準,預設None,這時需要使用score函式;或者如scoring='roc_auc',根據所選模型不同,評價準則不同。字串(函式名),或是可呼叫物件,需要其函式簽名形如:scorer(estimator, X, y);如果是None,則使用estimator的誤差估計函式。具體值的選取看本篇第三節內容

(4)fit_params=None

(5)n_jobs=1 常常設定為-1

n_jobs:並行數,int:個數,-1:跟CPU核數一致, 1:預設值

(6)iid=True

iid:預設True,為True時,預設為各個樣本fold概率分佈一致,誤差估計為所有樣本之和,而非各個fold的平均。

(7)refit=True

預設為True,程式將會以交叉驗證訓練集得到的最佳引數,重新對所有可用的訓練集與開發集進行,作為最終用於效能評估的最佳模型引數。即在搜尋引數結束後,用最佳引數結果再次fit一遍全部資料集。

(8)cv=None 常常設定為5

交叉驗證引數,預設None,使用三折交叉驗證。指定fold數量,預設為3,也可以是yield訓練/測試資料的生成器。

(9)verbose=0,scoring=None

verbose:日誌冗長度,int:冗長度,0:不輸出訓練過程,1:偶爾輸出,>1:對每個子模型都輸出。

(10)pre_dispatch=‘2*n_jobs’

指定總共分發的並行任務數。當n_jobs大於1時,資料將在每個執行點進行復制,這可能導致OOM,而設定pre_dispatch引數,則可以預先劃分總共的job數量,使資料最多被複制pre_dispatch次

(11)error_score=’raise’

(12)return_train_score=’warn’

如果“False”,cv_results_屬性將不包括訓練分數

回到sklearn裡面的GridSearchCV,GridSearchCV用於系統地遍歷多種引數組合,通過交叉驗證確定最佳效果引數。

Scoring parameter:評價標準引數詳細說明 常用 accuracy / f1 / neg_log_loss / roc_auc / neg_mean_squared_error

Model-evaluation tools usingcross-validation(such asmodel_selection.cross_val_scoreandmodel_selection.GridSearchCV) rely on an internalscoringstrategy. This is discussed in the sectionThe scoring parameter: defining model evaluation rules.

For the most common use cases, you can designate a scorer object with thescoringparameter; the table below shows all possible values. All scorer objects follow the convention thathigher return values are better than lower return values. Thus metrics which measure the distance between the model and the data, likemetrics.mean_squared_error, are available as neg_mean_squared_error which return the negated value ofthe metric.

屬性 常用3個 cv_results_ / best_score_ / best_params_

(1)cv_results_: dict of numpy (masked) ndarrays

具有鍵作為列標題和值作為列的dict,可以匯入到DataFrame中。注意,“params”鍵用於儲存所有引數候選項的引數設定列表。

2best_estimator_: estimator

通過搜尋選擇的估計器,即在左側資料上給出最高分數(或指定的最小損失)的估計器。如果refit = False,則不可用。

(3)best_score_ : floatbest_estimator的分數

(4)best_params_: dict在儲存資料上給出最佳結果的引數設定

5best_index_: int對應於最佳候選引數設定的索引(cv_results_陣列)。
search.cv_results _ ['params'] [search.best_index_]中的dict給出了最佳模型的引數設定,給出了最高的平均分數(search.best_score_)。

6scorer_: function

Scorer function used on the held out data to choose the best parameters for the model.

7n_splits_ : int

The number of cross-validation splits (folds/iterations).

(8)grid_scores_:給出不同引數情況下的評價結果

2.1程式碼示例:(決策樹分類)

import pandas as pd 
import numpy as np
from sklearn.datasets import load_iris
from sklearn.datasets import load_boston
from sklearn.model_selection import KFold
from sklearn.model_selection import GridSearchCV
from sklearn import tree
from sklearn.metrics import classification_report

iri=load_iris()
x,y=iri.data,iri.target
x=pd.DataFrame(x,columns=iri.feature_names)
y=pd.DataFrame(y,columns=['label'])
kf=KFold(n_splits=5,shuffle=True,random_state=123)  #後面KFold網格搜尋和交叉驗證用

clf=tree.DecisionTreeClassifier(criterion='entropy',random_state=123)
param1={'max_depth':[i for i in range(3,11)]
        ,'min_samples_leaf':[i for i in range(1,10,2)]
        ,'min_samples_split':[i for i in range(2,20,2)]    
}
gs1=GridSearchCV(clf,param_grid=param1,n_jobs=-1,cv=kf,scoring='accuracy')#搜尋設定
gs1.fit(x,y) #開始搜尋

gs1.best_params_,gs1.best_score_  #獲取值

#({'max_depth': 3, 'min_samples_leaf': 3, #'min_samples_split': 2},
# 0.9600000000000002)

clf=tree.DecisionTreeClassifier(criterion='entropy',random_state=123,max_depth=3,min_samples_leaf=3,min_samples_split=2)#帶入剛才搜尋的值
kf=KFold(n_splits=5,shuffle=True,random_state=123)
for i, (train_index,valid_index) in  enumerate(kf.split(x)):
    x_train,y_train=x.iloc[train_index],y.iloc[train_index]
    x_valid,y_valid=x.iloc[valid_index],y.iloc[valid_index]
    clf.fit(x_train,y_train)
    pres=clf.predict(x_valid)
    scores=clf.score(x_valid,y_valid)
    print(pres)
    print(scores)
    print(classification_report(y_valid,pres))
[0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2]
1.0
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        13
           1       1.00      1.00      1.00         6
           2       1.00      1.00      1.00        11

    accuracy                           1.00        30
   macro avg       1.00      1.00      1.00        30
weighted avg       1.00      1.00      1.00        30

[0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         9
           1       0.91      1.00      0.95        10
           2       1.00      0.91      0.95        11

    accuracy                           0.97        30
   macro avg       0.97      0.97      0.97        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 2 1 1 1 1 1 2 2 2 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        11
           1       1.00      0.90      0.95        10
           2       0.90      1.00      0.95         9

    accuracy                           0.97        30
   macro avg       0.97      0.97      0.96        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 1 1 1 1 1 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        11
           1       1.00      0.92      0.96        13
           2       0.86      1.00      0.92         6

    accuracy                           0.97        30
   macro avg       0.95      0.97      0.96        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 1 1 1 1 1 1 1 2 1 1 1 1 2 2 2 2 2 2 1 2 2 2 2 2]
0.9
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         6
           1       0.83      0.91      0.87        11
           2       0.92      0.85      0.88        13

    accuracy                           0.90        30
   macro avg       0.92      0.92      0.92        30
weighted avg       0.90      0.90      0.90        30

上面是網格搜尋過後的結果,下面是沒網格搜尋的結果:
clf=tree.DecisionTreeClassifier(criterion='entropy',random_state=123)
kf=KFold(n_splits=5,shuffle=True,random_state=123)
for i, (train_index,valid_index) in  enumerate(kf.split(x)):
    x_train,y_train=x.iloc[train_index],y.iloc[train_index]
    x_valid,y_valid=x.iloc[valid_index],y.iloc[valid_index]
    clf.fit(x_train,y_train)
    pres=clf.predict(x_valid)
    scores=clf.score(x_valid,y_valid)
    print(pres)
    print(scores)
    print(classification_report(y_valid,pres))
[0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 1 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        13
           1       0.86      1.00      0.92         6
           2       1.00      0.91      0.95        11

    accuracy                           0.97        30
   macro avg       0.95      0.97      0.96        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         9
           1       0.91      1.00      0.95        10
           2       1.00      0.91      0.95        11

    accuracy                           0.97        30
   macro avg       0.97      0.97      0.97        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 2 1 1 1 1 1 2 2 2 2 2 2 1 2 2]
0.9333333333333333
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        11
           1       0.90      0.90      0.90        10
           2       0.89      0.89      0.89         9

    accuracy                           0.93        30
   macro avg       0.93      0.93      0.93        30
weighted avg       0.93      0.93      0.93        30

[0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 1 1 1 1 1 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00        11
           1       1.00      0.92      0.96        13
           2       0.86      1.00      0.92         6

    accuracy                           0.97        30
   macro avg       0.95      0.97      0.96        30
weighted avg       0.97      0.97      0.97        30

[0 0 0 0 0 0 1 1 1 1 1 1 1 2 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2]
0.9666666666666667
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         6
           1       1.00      0.91      0.95        11
           2       0.93      1.00      0.96        13

    accuracy                           0.97        30
   macro avg       0.98      0.97      0.97        30
weighted avg       0.97      0.97      0.97        30

------------------------------------------------------------------------------------------------------------------------

程式碼示例:(決策樹迴歸)

import pandas as pd 
import numpy as np
from sklearn.datasets import load_iris
from sklearn.datasets import load_boston
from sklearn.model_selection import KFold
from sklearn.model_selection import GridSearchCV
from sklearn import tree
from sklearn.metrics import mean_squared_error

bos=load_boston()
x,y=bos.data,bos.target
x=pd.DataFrame(x,columns=bos.feature_names)
y=pd.DataFrame(y,columns=['prs'])
kf=KFold(n_splits=5,shuffle=True,random_state=123)  #後面KFold網格搜尋和交叉驗證用

reg=tree.DecisionTreeRegressor(criterion='mse',random_state=123)
param1={'max_depth':[i for i in range(3,11)]
        ,'min_samples_leaf':[i for i in range(1,10,2)]
        ,'min_samples_split':[i for i in range(2,20,2)]    
}
gs1=GridSearchCV(reg,param_grid=param1,n_jobs=-1,cv=kf,scoring='neg_mean_squared_error')#搜尋設定
gs1.fit(x,y) #開始搜尋

gs1.best_score_,gs1.best_params_

#(-16.59066150772413,
#{'max_depth': 10, 'min_samples_leaf': 3, #'min_samples_split': 18})
reg=tree.DecisionTreeRegressor(criterion='mse',random_state=123,max_depth=10,min_samples_leaf=3,min_samples_split=18)#帶入剛才搜尋的值
kf=KFold(n_splits=5,shuffle=True,random_state=123)
for i, (train_index,valid_index) in  enumerate(kf.split(x)):
    x_train,y_train=x.iloc[train_index],y.iloc[train_index]
    x_valid,y_valid=x.iloc[valid_index],y.iloc[valid_index]
    reg.fit(x_train,y_train)
    pres=reg.predict(x_valid)
    print(mean_squared_error(y_valid,pres))

31.21224680135449
13.075327597802536
9.69725036073873
7.410102735212124
21.558380043512773
上面是網格搜尋了的結果,下面是未調參的結果
reg=tree.DecisionTreeRegressor(criterion='mse',random_state=123)#帶入剛才搜尋的值
kf=KFold(n_splits=5,shuffle=True,random_state=123)
for i, (train_index,valid_index) in  enumerate(kf.split(x)):
    x_train,y_train=x.iloc[train_index],y.iloc[train_index]
    x_valid,y_valid=x.iloc[valid_index],y.iloc[valid_index]
    reg.fit(x_train,y_train)
    pres=reg.predict(x_valid)
    print(mean_squared_error(y_valid,pres))

46.62323529411764
25.08435643564356
10.882178217821785
10.506138613861387
21.80415841584158