學習器模型中一般有兩個參數:一類參數可以從數據中學習估計得到,還有一類參數無法從數據中估計,只能靠人的經驗進行指定,后一類參數就叫超參數
比如,支持向量機里的C,Kernel,gama,朴素貝葉斯里的alpha等,在學習其模型的設計中,我們要搜索超參數空間為學習器模型找到最合理的超參數,可以通過以下方法獲得學習器模型的參數列表和當前取值:estimator.get_params()
sklearn 提供了兩種通用的參數優化方法:網絡搜索和隨機采樣,
- 網格搜索交叉驗證(GridSearchCV):以窮舉的方式遍歷所有可能的參數組合
- 隨機采樣交叉驗證(RandomizedSearchCV):依據某種分布對參數空間采樣,隨機的得到一些候選參數組合方案
sklearn.model_selection:GridSearchCV,RandomizedSearchCV,ParameterGrid,ParameterSampler,fit_grid_point
①GridSearchCV:
該方法提供了在參數網格上窮舉候選參數組合的方法。參數網格由參數param_grid來指定,比如,下面展示了設置網格參數param_grid的一個例子:
param_grid=[
{'C':[1,10,100,1000],'kernel':['linear']},
{'C':[1,10,100,1000],'gamma':[0.001,0.0001],'kernel':['rbf']}
]
上面的參數指定了要搜索的兩個網格(每個網格就是一個字典):第一個里面有4個參數組合節點,第二個里面有4*2=8個參數組合節點
GridSearchCV的實例實現了通用的estimator API:當在數據集上訓練的時候,所有可能的參數組合將會被評估,訓練完成后選組最優的參數組合對應的estimator。
from sklearn import svm,datasets from sklearn.model_selection import GridSearchCV iris=datasets.load_iris() parameters={'kernel':('rbf','linear'),'C':[1,5,10]} svr=svm.SVC() clf=GridSearchCV(svr,parameters) clf.fit(iris.data,iris.target) print(clf.best_estimator_)
最終結果:
SVC(C=1, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape=None, degree=3, gamma='auto', kernel='linear', max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)
②RandomizedSearchCV
RandomizedSearchCV類實現了在參數空間上進行隨機搜索的機制,其中參數的取值是從某種概率分布中抽取的,這個概率分布描述了對應的參數的所有取值情況的可能性,這種隨機采樣機制與網格窮舉搜索相比,有兩大優點:
- 相比於整體參數空間,可以選擇相對較少的參數組合數量
- 添加參數節點不影響性能,不會降低效率
指定參數的采樣范圍和分布可以用一個字典開完成,跟網格搜索很像,另外,計算預算(總共要采樣多少參數組合或者迭代做多少次)可以用參數n_iter來指定,針對每一個參數,既可以使用可能取值范圍內的概率分布,也可以指定一個離散的取值列表(離散的列表將被均勻采樣)
{'C':scpiy.stats.expon(scale=100),'gamma':scipy.stats.expon(scale=.1),'kernel':['rbf'],'class_weight':['balanced':None]}
上邊的例子中:C服從指數分布,gamma服從指數分布,這個例子使用了scipy.stats模塊,其中包含了很多有用的分布用來產生參數采樣點,像expon,gamma,uniform or randint,原則上,任何函數都可以傳遞進去,只要他提供一個rvs(random variate sample)方法來返回采樣值,rvs函數的連續調用應該能夠保證產生獨立同分布的樣本值。
import numpy as np from time import time from scipy.stats import randint as sp_randint from sklearn.model_selection import RandomizedSearchCV from sklearn.datasets import load_digits from sklearn.ensemble import RandomForestClassifier def report(results,n_top=3): for i in range(1,n_top+1): candidates=np.flatnonzero(results['rank_test_score']==i) for candidate in candidates: print("Model with rank:{0}".format(i)) print("Mean validation score:{0:.3f}(std:{1:.3f})".format( results['mean_test_score'][candidate], results['std_test_score'][candidate])) print("Parameters:{0}".format(results['params'][candidate])) print("") digis=load_digits() X,y=digis.data,digis.target clf=RandomForestClassifier(n_estimators=20) #設置想要優化的超參數以及他們的取值分布 param_dist={"max_depth":[3,None], "max_features":sp_randint(1,11), "min_samples_split":sp_randint(2,11), "min_samples_leaf":sp_randint(1,11), "bootstrap":[True,False], "criterion":['gini','entropy'] } #開啟超參數空間的隨機搜索 n_iter_search=20 random_search=RandomizedSearchCV(clf,param_distributions=param_dist,n_iter=n_iter_search) start=time() random_search.fit(X,y) print("RandomizedSearchCV took %.3f seconds for %d candidates" "parameter settings."%((time()-start),n_iter_search)) report(random_search.cv_results_)
最終結果:
RandomizedSearchCV took 3.652 seconds for 20 candidatesparameter settings. Model with rank:1 Mean validation score:0.930(std:0.019) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'entropy', 'max_features': 8, 'bootstrap': False, 'min_samples_split': 10} Model with rank:2 Mean validation score:0.928(std:0.009) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'gini', 'max_features': 4, 'bootstrap': False, 'min_samples_split': 10} Model with rank:3 Mean validation score:0.924(std:0.009) Parameters:{'max_depth': None, 'min_samples_leaf': 1, 'criterion': 'gini', 'max_features': 9, 'bootstrap': True, 'min_samples_split': 5}
③超參數優化中的隨機搜索和網格搜索對比試驗以隨機森林分類器為優化對象。所有影響分類器學習的參數都被搜索了,除了樹的數量之外,隨機搜索和網格優化都在同一個超參數空間上對隨機森林分類器進行優化,雖然得到的超參數設置組合比較相似,但是隨機搜索的運行時間卻比網絡搜索顯著的少,隨機搜索得到的超參數組合的性能稍微差一點,但這很大程度上由噪聲引起的,在實踐中,我們只能挑幾個比較重要的參數組合來進行優化。
import numpy as np from time import time from scipy.stats import randint as sp_randint from sklearn.model_selection import RandomizedSearchCV from sklearn.model_selection import GridSearchCV from sklearn.datasets import load_digits from sklearn.ensemble import RandomForestClassifier def report(results,n_top=3): for i in range(1,n_top+1): candidates=np.flatnonzero(results['rank_test_score']==i) for candidate in candidates: print("Model with rank:{0}".format(i)) print("Mean validation score:{0:.3f}(std:{1:.3f})".format( results['mean_test_score'][candidate], results['std_test_score'][candidate])) print("Parameters:{0}".format(results['params'][candidate])) print("") digis=load_digits() X,y=digis.data,digis.target clf=RandomForestClassifier(n_estimators=20) print("==========下面是RandomizedSearchCV的測試結果===============") #設置想要優化的超參數以及他們的取值分布 param_dist={"max_depth":[3,None], "max_features":sp_randint(1,11), "min_samples_split":sp_randint(2,11), "min_samples_leaf":sp_randint(1,11), "bootstrap":[True,False], "criterion":['gini','entropy'] } #開啟超參數空間的隨機搜索 n_iter_search=20 random_search=RandomizedSearchCV(clf,param_distributions=param_dist,n_iter=n_iter_search) start=time() random_search.fit(X,y) print("RandomizedSearchCV took %.3f seconds for %d candidates" "parameter settings."%((time()-start),n_iter_search)) report(random_search.cv_results_) print("==========下面是GridSearchCV的測試結果===============") #在所有參數上搜索,找遍所有網絡節點 param_grid={"max_depth":[3,None], "max_features":[1,3,10], "min_samples_split":[2,3,10], "min_samples_leaf":[1,3,10], "bootstrap":[True,False], "criterion":['gini','entropy'] } #開啟超參數空間的網格搜索 grid_search=GridSearchCV(clf,param_grid=param_grid) start=time() grid_search.fit(X,y) print("GridSearchCV took %.2f seconds for %d candidates parameter settings." %(time()-start,len(grid_search.cv_results_['params']))) report(random_search.cv_results_)
最終結果:
==========下面是RandomizedSearchCV的測試結果=============== RandomizedSearchCV took 3.874 seconds for 20 candidatesparameter settings. Model with rank:1 Mean validation score:0.928(std:0.010) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'entropy', 'max_features': 10, 'bootstrap': False, 'min_samples_split': 2} Model with rank:2 Mean validation score:0.920(std:0.007) Parameters:{'max_depth': None, 'min_samples_leaf': 4, 'criterion': 'gini', 'max_features': 6, 'bootstrap': False, 'min_samples_split': 2} Model with rank:2 Mean validation score:0.920(std:0.009) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'entropy', 'max_features': 7, 'bootstrap': True, 'min_samples_split': 10} ==========下面是GridSearchCV的測試結果=============== GridSearchCV took 37.64 seconds for 216 candidates parameter settings. Model with rank:1 Mean validation score:0.928(std:0.010) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'entropy', 'max_features': 10, 'bootstrap': False, 'min_samples_split': 2} Model with rank:2 Mean validation score:0.920(std:0.007) Parameters:{'max_depth': None, 'min_samples_leaf': 4, 'criterion': 'gini', 'max_features': 6, 'bootstrap': False, 'min_samples_split': 2} Model with rank:2 Mean validation score:0.920(std:0.009) Parameters:{'max_depth': None, 'min_samples_leaf': 2, 'criterion': 'entropy', 'max_features': 7, 'bootstrap': True, 'min_samples_split': 10}
超參數空間的搜索技巧
- 技巧一,指定一個合適的目標測度對模型進行估計
默認情況下,參數搜索使用estimator的score函數來評估模型在某種參數配置下的性能:
分類器對應於 sklearn.metrics.accuracy_score
回歸器對應於sklearn.metrics.r2_score
但是在某些應用中,其他的評分函數獲取更加的合適。(比如在非平衡的分類問題中,准確率sccuracy_score通常不管用。這時,可以通過參數scoring來指定GridSearchCV類或者RandomizedSearchCV類內 部用我們自定義的評分函數)
- 技巧二、使用SKlearn的PipeLine將estimators和他們的參數空間組合起來
- 技巧三、合理划分數據集:開發集(用於GridSearchCV)+測試集(Test)使用model_selection.train_test_split()函數來搞定!
- 技巧四、並行化:(GridSearchCV)和(RandomizedSearchCV)在參數節點的計算上可以做到並行計算,這個通過參數”n_jobs“來指定。
- 技巧五、提高在某些參數節點上發生錯誤時的魯棒性:在出錯節點上只是提示警告。可以通過設置參數error_score=0(or=np.NaN)來搞定!