第一篇博客,淺談自己對高斯過程和貝葉斯優化的理解,有誤處歡迎指正。
一. 高斯過程回歸
1. 高斯過程到底是個什么東西?!
簡單來說,高斯過程可以看成是一個函數,函數的輸入是x,函數的輸出是高斯分布的均值和方差。
對於一些X值有對應的Y值,從X到Y存在映射關系f,即f(X)=Y,這里我們假設所有的Y是服從正態分布的!而高斯過程可以擬合出這個函數f的分布。
下圖1中的兩個黑點是我們已知的二維平面上的(x,y)對,我們需要通過這些點去擬合、評估、估計、猜測X與Y間真實的映射關系,通過不同的構造方法我們可以得到無數種不同的可能性。前文提到Y服從正太分布,可以這樣理解:在X取某一值的時候,根據擬合的函數不同可以得到不同的Y值,但是這些不同的Y是服從正太分布的,亦如圖2所示。所以高斯過程得到的是Y的分布,而非具體的Y值,Y的方差覺得了圖2中陰影的寬度(y軸方向)。隨着已知(x,y)對的增加,陰影面積會減小,即方差變小,函數f結構會越來越確定。

圖1

圖2
2. 聯合正太分布
高斯過程通過假設Y值服從聯合正態分布,來考慮yn和yn+1之間的關系,因此需要給定參數包括:均值向量和協方差矩陣,即:

其中協方差矩陣又叫做 核矩陣, 記為K,僅和特征x有關,和y無關。
高斯過程的思想是: 假設Y服從高維正態分布(先驗),而根據訓練集可以得到最優的核矩陣 ,從而得到后驗以估計測試集Y*,我們有后驗:

其中:

有了聯合分布接下來就可以比較容易的求出預測數據y*的條件分布p(y*|y)了,經過推導其條件分布也服務高斯分布,如下:

對y*估計,則可使用均值作為其估計值,即

3. 為什么要使用核函數
上文提到假設Y值服從聯合正態分布,需要給定參數包括:均值向量和協方差矩陣,這里我們先回顧一下什么是協方差矩陣,以及為什么可以用核矩陣代替協方差矩陣。
首先協方差矩陣的相關定義如下:

補充:相關系數和協方差的關系。

引入核函數的目的是為了擬合的函數更加光滑,在計算協方差時,我們先0均值化,可發現其就是兩個向量內積的形式,由此我們可以聯想到支持向量機中對核函數的使用,
核函數在低維計算的結果可以完全等價於兩個變量高維映射后的內積,如圖4,在低位擬合得到的曲線是不光滑的,若增加維數肯定可以使擬合曲線更加光滑。這個過程可以拆分為X1和X2求內積,轉化到先將X1和X2映射到高維后再求內積,而核函數的使用可以直接取代這個過程,且可以映射到無限高的維度上。

圖4
二. 貝葉斯優化
貝葉斯優化用於機器學習調參由J. Snoek(2012)提出,主要思想是,給定優化的目標函數(廣義的函數,只需指定輸入和輸出即可,無需知道內部結構以及數學性質),通過不斷地添加樣本點來更新目標函數的后驗分布(高斯過程,直到后驗分布基本貼合於真實分布。簡單的說,就是考慮了上一次參數的信息**,從而更好的調整當前的參數。
他與常規的網格搜索或者隨機搜索的區別是:
- 貝葉斯調參采用高斯過程,考慮之前的參數信息,不斷地更新先驗;網格搜索未考慮之前的參數信息
- 貝葉斯調參迭代次數少,速度快;網格搜索速度慢,參數多時易導致維度爆炸
- 貝葉斯調參針對非凸問題依然穩健;網格搜索針對非凸問題易得到局部優最
貝葉斯優化是基於數據使用貝葉斯定理估計目標函數的后驗分布,然后再根據分布選擇下一個采樣的超參數組合。它充分利用了前一個采樣點的信息,其優化的工作方式是通過對目標函數形狀的學習,並找到使結果向全局最大提升的參數
高斯過程 用於在貝葉斯優化中對目標函數建模,得到其后驗分布
通過高斯過程建模之后,我們嘗試抽樣進行樣本計算,而貝葉斯優化很容易在局部最優解上不斷采樣,這就涉及到了開發和探索之間的權衡。
- 開發 (exploitation): 根據后驗分布,在最可能出現全局最優解的區域進行采樣, 開發高意味着均值高
- 探索 (exploration): 在還未取樣的區域獲取采樣點, 探索高意味着方差高
而如何高效的采樣,即開發和探索,我們需要用到 Acquisition Function, 它是用來尋找下一個 x 的函數。
探測(exploration)就是在還未取樣的區域獲取采樣點。開發(exploitation)就是根據后驗分布,在最可能出現全局最優解的區域進行采樣。我們下一個選取點(x)應該有比較大的均值(開發)和比較高的方差(探索)。

圖5
使用不同的采集函數對比如下:

圖6
貝葉斯優化過程

圖7
上圖可以直觀地解釋貝葉斯優化。其中紅色的曲線為實際的目標函數,並且我們並不知道該函數確切的表達式。所以我們希望使用高斯過程逼近該目標函數。通過采樣點(上圖有 4 個抽樣點),我們能夠得出直觀或置信曲線以擬合觀察到的樣本點。所以上圖綠色的區域為置信域,即目標曲線最有可能處於的區域。從上面的先驗知識中,我們確定了第二個點(f+)為最大的樣本觀察值,所以下一個最大點應該要比它大或至少與之相等。因此,我們繪制出一條藍線,並且下一個最大點應該位於這一條藍線之上。因此,下一個采樣在交叉點 f+和置信域之間,我們能假定在 f+點以下的樣本是可以丟棄的,因為我們只需要搜索令目標函數取極大值的參數。所以現在我們就縮小了觀察區域,我們會迭代這一過程,直到搜索到最優解。
動態化過程:

代碼示例:
1 from sklearn.datasets import make_classification 2 from sklearn.ensemble import RandomForestClassifier 3 from sklearn.cross_validation import cross_val_score 4 from bayes_opt import BayesianOptimization 5 6 # 產生隨機分類數據集,10個特征, 2個類別 7 x, y = make_classification(n_samples=1000,n_features=10,n_classes=2)
我們先看看不調參的結果:
1 rf = RandomForestClassifier() 2 print(np.mean(cross_val_score(rf, x, y, cv=20, scoring='roc_auc'))) 3 4 >>> 0.965162
可以看到,不調參的話模型20此交叉驗證AUC均值是0.965162,算是一個不錯的模型,那么如果用bayes調參結果會怎么樣呢
我們先定義一個目標函數,里面放入我們希望優化的函數。比如此時,函數輸入為隨機森林的所有參數,輸出為模型交叉驗證5次的AUC均值,作為我們的目標函數。因為bayes_opt庫只支持最大值,所以最后的輸出如果是越小越好,那么需要在前面加上負號,以轉為最大值。由於bayes優化只能優化連續超參數,因此要加上int()轉為離散超參數。
1 def rf_cv(n_estimators, min_samples_split, max_features, max_depth): 2 val = cross_val_score( 3 RandomForestClassifier(n_estimators=int(n_estimators), 4 min_samples_split=int(min_samples_split), 5 max_features=min(max_features, 0.999), # float 6 max_depth=int(max_depth), 7 random_state=2 8 ), 9 x, y, scoring='roc_auc', cv=5 10 ).mean() 11 return val
然后我們就可以實例化一個bayes優化對象了:
1 rf_bo = BayesianOptimization( 2 rf_cv, 3 {'n_estimators': (10, 250), 4 'min_samples_split': (2, 25), 5 'max_features': (0.1, 0.999), 6 'max_depth': (5, 15)} 7 )
里面的第一個參數是我們的優化目標函數,第二個參數是我們所需要輸入的超參數名稱,以及其范圍。超參數名稱必須和目標函數的輸入名稱一一對應。
完成上面兩步之后,我們就可以運行bayes優化了!
1 rf_bo.maximize()
完成的時候會不斷地輸出結果,如下圖所示:

等到程序結束,我們可以查看當前最優的參數和結果:
1 rf_bo.res['max'] 2 3 >>> {'max_params': {'max_depth': 5.819908283575526, 4 'max_features': 0.4951745603509127, 5 'min_samples_split': 2.3110014720414958, 6 'n_estimators': 249.73529231990733}, 7 'max_val': 0.9774079407940794}
上面bayes算法得到的參數並不一定最優,當然我們會遇到一種情況,就是我們已經知道有一組或是幾組參數是非常好的了,我們想知道其附近有沒有更好的。這個操作相當於上文bayes優化中的Explore操作,而bayes_opt庫給了我們實現此方法的函數:
1 rf_bo.explore( 2 {'n_estimators': [10, 100, 200], 3 'min_samples_split': [2, 10, 20], 4 'max_features': [0.1, 0.5, 0.9], 5 'max_depth': [5, 10, 15] 6 } 7 )
這里我們添加了三組較優的超參數,讓其在該參數基礎上進行explore,可能會得到更好的結果。
同時,我們還可以修改高斯過程的參數,高斯過程主要參數是核函數(kernel),還有其他參數可以參考sklearn.gaussianprocess
1 gp_param={'kernel':None} 2 rf_bo.maximize(**gp_param)
最終我們的到參數如下:
1 {'max_params': {'max_depth': 5.819908283575526, 2 'max_features': 0.4951745603509127, 3 'min_samples_split': 2.3110014720414958, 4 'n_estimators': 249.73529231990733}, 5 'max_val': 0.9774079407940794}
運行交叉驗證測試一下:
1 rf = RandomForestClassifier(max_depth=6, max_features=0.39517, min_samples_split=2, n_estimators=250) 2 np.mean(cross_val_score(rf, x, y, cv=20, scoring='roc_auc')) 3 >>> 0.9754953
得到最終結果是0.9755,比之前的0.9652提高了約0.01。
Reference
1. https://www.cnblogs.com/yangruiGB2312/p/9374377.html
2. http://www.sohu.com/a/165565029_465975
3. https://zhuanlan.zhihu.com/p/32152162
4. https://baijiahao.baidu.com/s?id=1593374440671439305&wfr=spider&for=pc
