貝葉斯優化 (BayesianOptimization)
1 問題提出
神經網咯是有許多超參數決定的,例如網絡深度,學習率,正則等等。如何尋找最好的超參數組合,是一個老人靠經驗,新人靠運氣的任務。
窮舉搜索 Grid Search 效率太低;隨機搜索比窮舉搜索好一點;目前比較好的解決方案是貝葉斯優化
1.1 貝葉斯優化的優點
- 貝葉斯調參采用高斯過程,考慮之前的參數信息,不斷地更新先驗;網格搜索未考慮之前的參數信息
- 貝葉斯調參迭代次數少,速度快;網格搜索速度慢,參數多時易導致維度爆炸
- 貝葉斯調參針對非凸問題依然穩健;網格搜索針對非凸問題易得到局部優最
2 詳細算法
3 python實現
3.1 貝葉斯初步優化
這里本來想用kaggle的lgb貝葉斯優化,但是對新手不太友好,就使用這個博客中的例子
- 安裝
pip install bayesian-optimization
- 准備工作(使用隨機森林作為模型進行參數優化)
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.cross_validation import cross_val_score
from bayes_opt import BayesianOptimization
# 產生隨機分類數據集,10個特征, 2個類別
x, y = make_classification(n_samples=1000,n_features=10,n_classes=2)
sklearn.cross_validation 已經廢棄,改為:sklearn.model_selection
不調參數的結果:
rf = RandomForestClassifier()
print(np.mean(cross_val_score(rf, x, y, cv=20, scoring='roc_auc')))
>>> 0.965162
- 貝葉斯優化
先要定義一個目標函數。比如此時,函數輸入為隨機森林的所有參數,輸出為模型交叉驗證5次的AUC均值,作為我們的目標函數。因為bayes_opt庫只支持最大值,所以最后的輸出如果是越小越好,那么需要在前面加上負號,以轉為最大值。
def rf_cv(n_estimators, min_samples_split, max_features, max_depth):
val = cross_val_score(
RandomForestClassifier(n_estimators=int(n_estimators),
min_samples_split=int(min_samples_split),
max_features=min(max_features, 0.999), # float
max_depth=int(max_depth),
random_state=2
),
x, y, scoring='roc_auc', cv=5
).mean()
return val
建立貝葉斯優化對象
rf_bo = BayesianOptimization(
rf_cv,
{'n_estimators': (10, 250),
'min_samples_split': (2, 25),
'max_features': (0.1, 0.999),
'max_depth': (5, 15)}
)
里面的第一個參數是我們的優化目標函數,第二個參數是我們所需要輸入的超參數名稱,以及其范圍。超參數名稱必須和目標函數的輸入名稱一一對應。
開始優化:
rf_bo.maximize()
優化結果:
| iter | target | max_depth | max_fe... | min_sa... | n_esti... |
-------------------------------------------------------------------------
| 1 | 0.9758 | 7.261 | 0.7852 | 17.35 | 231.3 |
| 2 | 0.9725 | 9.361 | 0.1805 | 12.82 | 185.7 |
| 3 | 0.9761 | 6.576 | 0.6609 | 15.19 | 12.12 |
| 4 | 0.9711 | 6.679 | 0.1358 | 10.07 | 223.1 |
| 5 | 0.9768 | 7.614 | 0.563 | 12.94 | 247.5 |
| 6 | 0.9744 | 14.74 | 0.7134 | 24.53 | 245.1 |
| 7 | 0.953 | 5.275 | 0.1552 | 24.75 | 10.72 |
| 8 | 0.9748 | 5.318 | 0.8218 | 2.634 | 77.82 |
| 9 | 0.9607 | 15.0 | 0.999 | 2.0 | 10.0 |
| 10 | 0.9654 | 5.007 | 0.2314 | 2.315 | 10.24 |
| 11 | 0.9701 | 14.89 | 0.976 | 24.45 | 118.8 |
| 12 | 0.9743 | 14.98 | 0.6535 | 23.94 | 53.86 |
| 13 | 0.972 | 14.09 | 0.7797 | 2.199 | 142.2 |
| 14 | 0.9722 | 14.88 | 0.8909 | 9.471 | 64.05 |
| 15 | 0.976 | 5.195 | 0.8093 | 24.1 | 145.6 |
| 16 | 0.9715 | 5.136 | 0.9308 | 4.38 | 35.74 |
| 17 | 0.9747 | 5.195 | 0.9876 | 24.68 | 78.97 |
| 18 | 0.9747 | 5.249 | 0.9799 | 24.72 | 248.5 |
| 19 | 0.9745 | 5.235 | 0.8297 | 4.737 | 120.6 |
| 20 | 0.9711 | 14.88 | 0.9794 | 23.83 | 179.4 |
| 21 | 0.9739 | 5.495 | 0.9039 | 2.218 | 161.8 |
| 22 | 0.9757 | 8.063 | 0.2147 | 13.45 | 247.3 |
| 23 | 0.9723 | 14.59 | 0.8999 | 2.442 | 249.0 |
| 24 | 0.9741 | 5.035 | 0.8009 | 13.7 | 56.69 |
| 25 | 0.9701 | 13.11 | 0.9747 | 12.36 | 93.52 |
| 26 | 0.9744 | 5.083 | 0.9857 | 24.38 | 205.5 |
| 27 | 0.9741 | 14.99 | 0.7923 | 17.87 | 30.14 |
| 28 | 0.9738 | 5.038 | 0.973 | 13.42 | 142.2 |
| 29 | 0.9724 | 14.81 | 0.7962 | 2.028 | 187.0 |
| 30 | 0.9734 | 5.378 | 0.9614 | 14.71 | 24.68 |
=========================================================================
尋找最大值:
rf_bo.res["max"]
但是我使用這個就會報錯,所以就簡單寫了一個循環尋找最大值的索引:
index = []
for i in rf_bo.res:
index.append(i['target'])
max_index = index.index(max(index))
3.2 貝葉斯進階優化
上面bayes算法得到的參數並不一定最優,當然我們會遇到一種情況,就是我們已經知道有一組或是幾組參數是非常好的了,我們想知道其附近有沒有更好的。這個操作相當於上文bayes優化中的Explore操作,而bayes_opt庫給了我們實現此方法的函數:
rf_bo.explore(
{'n_estimators': [10, 100, 200],
'min_samples_split': [2, 10, 20],
'max_features': [0.1, 0.5, 0.9],
'max_depth': [5, 10, 15]
}
)
這里我們添加了三組較優的超參數,讓其在該參數基礎上進行explore,可能會得到更好的結果。
同時,我們還可以修改高斯過程的參數,高斯過程主要參數是核函數(kernel),還有其他參數可以參考sklearn.gaussianprocess:
gp_param={'kernel':None}
rf_bo.maximize(**gp_param)
如果kernel是None,1.0 * RBF(1.0)被用來當成默認的kernel。
但是從某種角度上來說,貝葉斯優化也是另外一種瞎猜。。。