[機器學習] --- 參數優化與模型選擇

 

 

一 交叉驗證

交叉驗證的目的

在實際訓練中,模型通常對訓練數據好,但是對訓練數據之外的數據擬合程度差。用於評價模型的泛化能力,從而進行模型選擇。

交叉驗證的基本思想

把在某種意義下將原始數據(dataset)進行分組,一部分做爲訓練集(train set),另一部分做爲驗證集(validation set or test set),首先用訓練集對模型進行訓練,再利用驗證集來測試模型的泛化誤差。另外,現實中數據總是有限的,爲了對數據形成重用,從而提出k-摺疊交叉驗證。

對於個分類或迴歸問題,假設可選的模型爲M={M1,M2,M3……Md}。k-摺疊交叉驗證就是將訓練集的1/k作爲測試集,每個模型訓練k次,測試k次,錯誤率爲k次的平均,最終選擇平均率最小的模型Mi。

1、 將全部訓練集S分成k個不相交的子集,假設S中的訓練樣例個數爲m,那麼每一個子集有m/k個訓練樣例,相應的子集稱作{S1,S2,S3……Sk}。

2、 每次從模型集合M中拿出來一個Mi,然後在訓練子集中選擇出k-1個

{S1,S2,Sj-1,Sj+1,Sk}(也就是每次只留下一個Sj),使用這k-1個子集訓練Mi後,得到假設函數hij。最後使用剩下的一份Sj作測試,得到經驗錯誤。

3、 由於我們每次留下一個Sj(j從1到k),因此會得到k個經驗錯誤,那麼對於一個Mi,它的經驗錯誤是這k個經驗錯誤的平均。

4、 選出平均經驗錯誤率最小的Mi,然後使用全部的S再做一次訓練,得到最後的hi。

 

K折交叉驗證

  1. 將數據集平均分割成K個等份
  2. 使用1份數據作爲測試數據,其餘作爲訓練數據
  3. 計算測試準確率
  4. 使用不同的測試集,重複2、3步驟
  5. 測試準確率做平均,作爲對未知數據預測準確率的估計

 

 

網格搜索GridSearchCV

GridSearchCV用於系統地遍歷多種參數組合,通過交叉驗證確定最佳效果參數。

classsklearn.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=True)

常用參數解讀

estimator:所使用的分類器,如estimator=RandomForestClassifier(min_samples_split=100, min_samples_leaf=20, max_depth=8, max_features='sqrt', random_state=10) 並且傳入除需要確定最佳的參數之外的其他參數。每一個分類器都需要一個scoring參數,或者score方法。

param_grid:值爲字典或者列表,即需要最優化的參數的取值,param_item = {'n_estimators':range(10,71,10)}  param_grid =param_item

scoring :準確度評價標準,默認None,這時需要使用score函數;或者如scoring=’roc_auc’,根據所選模型不同,評價準則不同。字符串(函數名),或是可調用對象,需要其函數簽名形如:scorer(estimator, X, y);如果是None,則使用estimator的誤差估計函數。

cv :交叉驗證參數,默認None,使用三折交叉驗證。指定fold數量,默認爲3,也可以是yield訓練/測試數據的生成器。

refit :默認爲True,程序將會以交叉驗證訓練集得到的最佳參數,重新對所有可用的訓練集與開發集進行,作爲最終用於性能評估的最佳模型參數。即在搜索參數結束後,用最佳參數結果再次fit一遍全部數據集。

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

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

n_jobs: 並行數,int:個數,-1:跟CPU核數一致, 1:默認值。

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

 

隨機參數優化RandomizedSearchCV

儘管使用參數設置的網格法是目前最廣泛使用的參數優化方法, 其他搜索方法也具有更有利的性能。 RandomizedSearchCV 實現了對參數的隨機搜索, 其中每個設置都是從可能的參數值的分佈中進行取樣。 這對於窮舉搜索有兩個主要優勢:

  • 可以選擇獨立於參數個數和可能值的預算
  • 添加不影響性能的參數不會降低效率

指定如何取樣的參數是使用字典完成的, 非常類似於爲 GridSearchCV 指定參數。 此外, 通過 n_iter 參數指定計算預算, 即取樣候選項數或取樣迭代次數。 對於每個參數, 可以指定在可能值上的分佈或離散選擇的列表 (均勻取樣):

{'C': scipy.stats.expon(scale=100), 'gamma': scipy.stats.expon(scale=.1),
  'kernel': ['rbf'], 'class_weight':['balanced', None]}

搜索的輸出值

cv_results_:給出不同參數情況下的評價結果的記錄

best_params_:描述了已取得最佳結果的參數的組合

best_score_:成員提供優化過程期間觀察到的最好的評分

from sklearn.datasets import load_iris  # 自帶的樣本數據集
from sklearn.neighbors import KNeighborsClassifier  # 要估計的是knn裏面的參數,包括k的取值和樣本權重分佈方式
import matplotlib.pyplot as plt  # 可視化繪圖
from sklearn.model_selection import GridSearchCV,RandomizedSearchCV  # 網格搜索和隨機搜索

iris = load_iris()

X = iris.data  # 150個樣本,4個屬性
y = iris.target # 150個類標號

#######################################################

k_range = range(1, 30)  # 優化參數k的取值範圍
weight_options = ['uniform', 'distance']  # 代估參數權重的取值範圍。uniform爲統一取權值,distance表示距離倒數取權值
# 下面是構建parameter grid,其結構是key爲參數名稱,value是待搜索的數值列表的一個字典結構
params = {'n_neighbors':k_range,'weights':weight_options}  # 定義優化參數字典,字典中的key值必須是分類算法的函數的參數名
knn = KNeighborsClassifier(n_neighbors=5)  # 定義分類算法。n_neighbors和weights的參數名稱和params字典中的key名對應

# ================================網格搜索=======================================
# 這裏GridSearchCV的參數形式和cross_val_score的形式差不多,其中params是 parameter grid所對應的參數
# GridSearchCV中的n_jobs設置爲-1時,可以實現並行計算(如果你的電腦支持的情況下)
grid = GridSearchCV(estimator = knn, param_grid = params, cv=10, scoring='accuracy') #針對每個參數對進行了10次交叉驗證。scoring='accuracy'使用準確率爲結果的度量指標。可以添加多個度量指標
grid.fit(X, y)

#print('網格搜索-度量記錄:',grid.cv_results_)  # 包含每次訓練的相關信息
print('網格搜索-最佳度量值:',grid.best_score_)  # 獲取最佳度量值
print('網格搜索-最佳參數:',grid.best_params_)  # 獲取最佳度量值時的代定參數的值。是一個字典
print('網格搜索-最佳模型:',grid.best_estimator_)  # 獲取最佳度量時的分類器模型


# 使用獲取的最佳參數生成模型,預測數據
knn_grid = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights'])  # 取出最佳參數進行建模
knn_grid.fit(X, y)  # 訓練模型
print(knn_grid.predict([[3, 5, 4, 2]]))  # 預測新對象

# =====================================隨機搜索===========================================
rand = RandomizedSearchCV(knn, params, cv=10, scoring='accuracy', n_iter=10, random_state=5)  #
rand.fit(X, y)

#print('隨機搜索-度量記錄:',grid.cv_results_)  # 包含每次訓練的相關信息
print('隨機搜索-最佳度量值:',grid.best_score_)  # 獲取最佳度量值
print('隨機搜索-最佳參數:',grid.best_params_)  # 獲取最佳度量值時的代定參數的值。是一個字典
print('隨機搜索-最佳模型:',grid.best_estimator_)  # 獲取最佳度量時的分類器模型


# 使用獲取的最佳參數生成模型,預測數據
knn_random = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights'])  # 取出最佳參數進行建模
knn_random.fit(X, y)  # 訓練模型
print(knn_random.predict([[3, 5, 4, 2]]))  # 預測新對象



 

當你的調節參數是連續的,比如迴歸問題的正則化參數,有必要指定一個連續分佈而不是可能值的列表,這樣RandomizeSearchCV就可以執行更好的grid search。

 

調參神器hyperopt

         http://hyperopt.github.io/hyperopt-sklearn/     

       Hyperopt庫爲python中的模型選擇和參數優化提供了算法和並行方案。機器學習常見的模型有KNN, SVM,PCA,決策樹,GBDT等一系列的算法,但是在實際應用中,我們需要選取合適的模型,並對模型調參,得到一組合適的參數。尤其是在模型的調參階段,需要花費大量的時間和精力,卻又效率低下。但是我們可以換一個角度來看待這個問題,模型的選取,以及模型中需要調節的參數,可以看做是一組變量,模型的質量標準(比如正確率,AUC)等等可以看做是目標函數,這個問題就是超參數的優化的問題。我們可以使用搜索算法來解決。

如果我們要確定某個算法模型的最佳參數,通常會使用網格搜索(GridSearch),即假如有兩個參數A和B,它們分別有NANB個取值(人爲設定),那麼我們則需要依次枚舉這些取值的所有組合情況(在這裏是共NANB

種),然後取準確率最高的那一個作爲最終這個算法模型的最優參數。

顯然,如果要枚舉的組合情況非常多,網格搜索將變得十分低效,甚至不可接受。那麼本文要介紹的hyperopt可以理解爲一個智能化的網格搜索,能大大縮短調參所需的時間。

#!/usr/bin/env python  
# encoding: utf-8  

""" 
@version: v1.0 
@author: zwqjoy 
@contact: [email protected] 
@site: https://blog.csdn.net/zwqjoy 
@file: para
@time: 2018/7/7 17:00 
""" 

from hyperopt import fmin, tpe, hp, rand
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn import svm
from sklearn import datasets

# SVM的三個超參數:C爲懲罰因子,kernel爲核函數類型,gamma爲核函數的額外參數(對於不同類型的核函數有不同的含義)
# 有別於傳統的網格搜索(GridSearch),這裏只需要給出最優參數的概率分佈即可,而不需要按照步長把具體的值給一個個枚舉出來
parameter_space_svc ={
    # loguniform表示該參數取對數後符合均勻分佈
    'C':hp.loguniform("C", np.log(1), np.log(100)),
    'kernel':hp.choice('kernel',['rbf','poly']),
    'gamma': hp.loguniform("gamma", np.log(0.001), np.log(0.1)),
}

# 鳶尾花卉數據集,是一類多重變量分析的數據集
# 通過花萼長度,花萼寬度,花瓣長度,花瓣寬度4個屬性預測鳶尾花卉屬於(Setosa,Versicolour,Virginica)三個種類中的哪一類
iris = datasets.load_digits()

#--------------------劃分訓練集和測試集--------------------
train_data = iris.data[0:1300]
train_target = iris.target[0:1300]
test_data = iris.data[1300:-1]
test_target = iris.target[1300:-1]
#-----------------------------------------------------------

# 計數器,每一次參數組合的枚舉都會使它加1
count = 0

def function(args):
    print(args)

    # **可以把dict轉換爲關鍵字參數,可以大大簡化複雜的函數調用
    clf = svm.SVC(**args)

    # 訓練模型
    clf.fit(train_data,train_target)

    # 預測測試集
    prediction = clf.predict(test_data)

    global count
    count = count + 1
    score = accuracy_score(test_target,prediction)
    print("[{0}], Test acc: {1})".format(str(count), score))

    # 由於hyperopt僅提供fmin接口,因此如果要求最大值,則需要取相反數
    return -score

# algo指定搜索算法,目前支持以下算法:
# ①隨機搜索(hyperopt.rand.suggest)
# ②模擬退火(hyperopt.anneal.suggest)
# ③TPE算法(hyperopt.tpe.suggest,算法全稱爲Tree-structured Parzen Estimator Approach)
# max_evals指定枚舉次數上限,即使第max_evals次枚舉仍未能確定全局最優解,也要結束搜索,返回目前搜索到的最優解
best = fmin(function, parameter_space_svc, algo=tpe.suggest, max_evals=100)

# best["kernel"]返回的是數組下標,因此需要把它還原回來
kernel_list = ['rbf','poly']
best["kernel"] = kernel_list[best["kernel"]]

print("best params: ",best)

clf = svm.SVC(**best)
print(clf)


 

注意:

     tuning with Hyperopt- TypeError: 'generator' object has no attribute '__getitem__'

      The issue is incompatibility of Hyperopt with networkxx2. One needs to downgrade to "networkx 1.11". (pip intall networkx==1.11)

 

參考:

python機器學習模型選擇&調參工具Hyperopt-sklearn

python調參神器hyperopt

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章