Python 環境下的自動化機器學習超引數調優

機器之心發表於2018-08-08

機器學習演算法的效能高度依賴於引數的選擇,對機器學習引數進行調優是一項繁瑣但卻至關重要的任務。本文介紹了一個使用「Hyperopt」庫對梯度提升機(GBM)進行貝葉斯引數調優的完整示例,並著重介紹了其實現過程。

由於機器學習演算法的效能高度依賴於引數的選擇,對機器學習引數進行調優是一項繁瑣但至關重要的任務。手動調優佔用了機器學習演算法流程中一些關鍵步驟(如特徵工程和結果解釋)的時間。網格搜尋和隨機搜尋則不會干涉這些步驟,但是需要大量的執行時間,因為它們浪費了時間去評估搜尋空間中並不太可能找到最優點的區域。如今越來越多的引數調優過程都是通過自動化的方法完成的,它們旨在使用帶有策略的啟發式搜尋(informed search)在更短的時間內找到最優引數,除了初始設定之外,並不需要額外的手動操作。

貝葉斯優化是一種基於模型的用於尋找函式最小值的方法。近段時間以來,貝葉斯優化開始被用於機器學習引數調優,結果表明,該方法在測試集上的表現更加優異,但需要的迭代次數小於隨機搜尋。此外,現在一些 Python 庫的出現使得對任意的機器學習模型實現貝葉斯引數調優變得更加簡單。

本文將介紹一個使用「Hyperopt」庫對梯度提升機(GBM)進行貝葉斯引數調優的完整示例。在本文作者早先的一篇文章中,他已經對這個方法背後的概念進行了概述,所以本文將著重介紹實現過程。和大多數機器學習的主題類似,讀者並不需要理解所有的細節,但是瞭解基本的原理可以幫助讀者更有效地使用這項技術!

本文所有的程式碼都可以以 Jupyter Notebook 的形式在 GitHub 上獲取(https://github.com/WillKoehrsen/hyperparameter-optimization)。

本文目錄:

  • 貝葉斯優化方法

  • 優化問題的四個組成部分

  • 目標函式

  • 域空間

  • 優化演算法

  • 結果的歷史資料

  • 優化

  • 搜尋結果

  • 搜尋結果的視覺化

  • 搜尋的演化過程

  • 繼續搜尋

  • 結語

貝葉斯優化方法

簡單地說,貝葉斯優化通過基於過去對目標的評估結果建立一個代理函式(概率模型)找到使得目標函式最小的值。代理函式比目標函式更易於優化,因此下一個待評估的輸入值是通過對代理函式應用某種標準(通常為預期提升)來選擇的。貝葉斯方法不同於隨機搜尋或網格搜尋,後兩者都使用了過去的評估結果來選擇接下來待評估的值。它們的思想是:通過根據過去表現良好的值選擇下一個輸入值來限制評價目標函式的高昂開銷。

對於引數優化來說,其目標函式為使用一組引數機器學習模型的驗證誤差。它的目標是找出在驗證集上產生最小誤差的引數,並希望將這些結果泛化到測試集上去。對目標函式評估的開銷是巨大的,因為它需要訓練帶有一組特定引數機器學習模型。理想情況下,我們希望找到這樣一方法,它既能探索搜尋空間,又能限制耗時的引數評估。貝葉斯引數調優使用一個不斷更新的概率模型,通過從過去的結果中進行推理,使搜尋過程「專注」於有可能達到最優的引數

Python 環境下有一些貝葉斯優化程式庫,它們目標函式的代理演算法有所區別。在本文中,我們將使用「Hyperopt」庫,它使用樹形 Parzen 評估器(TPE,https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf)作為搜尋演算法,其他的 Python 庫還包含「Spearmint」(高斯過程代理)和「SMAC」(隨即森林迴歸)。目前在這個領域有大量有趣的研究,所以如果你對某一個庫不是很滿意,你可以試試其他的選項!針對某個問題的通用結構(本文將使用的結構)可以在各個庫間進行轉換,其句法差異非常小。

優化問題的四個組成部分

貝葉斯優化問題有四個組成部分:

 1. 目標函式:我們想要最小化的物件,這裡指帶引數機器學習模型的驗證誤差

 2. 域空間:待搜尋的引數

 3. 優化演算法:構造代理模型和選擇接下來要評估的引數值的方法

 4. 結果的歷史資料:儲存下來的目標函式評估結果,包含引數和驗證損失

通過以上四個步驟,我們可以對任意實值函式進行優化(找到最小值)。這是一個強大的抽象過程,除了機器學習引數的調優,它還能幫我們解決其他許多問題。

資料集

在本文的示例中,我們將使用 Caravan Insurance 資料集(https://www.kaggle.com/uciml/caravan-insurance-challenge),它的目標是預測客戶是否會購買一份保險產品。這是一個監督分類問題,帶有 5800 個用於訓練的觀測值和 4000 個測試點。由於這是一個不平衡的分類問題,本文使用的評價效能的指標是受試者工作特徵曲線下的面積(ROC AUC),ROC AUC 的值越高越好,其值為 1 代表模型是完美的。資料集如下所示:

Python 環境下的自動化機器學習超引數調優

資料集(CARAVAN)是帶標籤的

由於 Hyperopt 要做的是取最小值,我們將從目標函式中返回「1-ROC AUC」,從而提高 ROC AUC。

梯度提升模型

在本文中,我們對梯度提升機(GBM)的細節知識不做過多探討,下面是我們需要理解的基本知識:GBM 是一種基於使用依次訓練的弱學習器(多為決策樹)構建強學習器的整合增強方法。GBM 中有許多引數,它們控制著整個整合結構和單棵決策樹。我們在這裡使用的一種最有效的選擇決策樹數量的方法(稱為評估器)是早停止(early stopping)。LightGBM 提供了一種 Python 環境下的快速簡單的 GBM 實現。

  • 想了解更多 GBM 的細節,這裡有一篇高屋建瓴的文章:https://medium.com/mlreview/gradient-boosting-from-scratch-1e317ae4587d

  • 一篇技術論文:https://brage.bibsys.no/xmlui/bitstream/handle/11250/2433761/16128_FULLTEXT.pdf

拋開必要的背景知識不談,讓我們將用於引數調優的貝葉斯優化問題的四個組成部分一一列舉出來。

目標函式

我們試圖最小化目標函式。其輸入為一組值——在本例中為 GBM 的引數,輸出為需要最小化的實值——交叉驗證損失。Hyperopt 將目標函式作為黑盒處理,因為這個庫只關心輸入和輸出是什麼。為了找到使損失最小的輸入值,該演算法不需要知道目標函式的內部細節!從一個高度抽象的層次上說(以虛擬碼的形式),我們的目標函式可以表示為:

def objective(hyperparameters):
    """Returns validation score from hyperparameters"""

    model = Classifier(hyperparameters)
    validation_loss = cross_validation(model, training_data)    
    return validation_loss

在對最終的模型進行評價時,我們需要注意的是,不要使用測試集上的損失,因為我們只能使用一次測試集。相對地,我們對驗證集上的引數進行評估。此外,我們使用 K 折交叉驗證而不是將資料劃分到一個獨立的驗證集中,這種驗證方法除了保留了有價值的訓練資料外,還能讓我們在測試集上獲得偏差更小的誤差估計。

不同模型引數調優的目標函式的基本結構是相同的:函式接收引數作為輸入,並返回使用這些引數交叉驗證誤差。儘管本文的示例是針對 GBM 的,但該結構同樣可以被應用於其他的方法。

下圖為 GBM 的完整目標函式,該 GBM 使用帶早停止機制的 10 折交叉驗證

import lightgbm as lgb
from hyperopt import STATUS_OK

N_FOLDS = 10

# Create the dataset
train_set = lgb.Dataset(train_features, train_labels)

def objective(params, n_folds = N_FOLDS):
    """Objective function for Gradient Boosting Machine Hyperparameter Tuning"""

    # Perform n_fold cross validation with hyperparameters
    # Use early stopping and evalute based on ROC AUC
    cv_results = lgb.cv(params, train_set, nfold = n_folds, num_boost_round = 10000, 
                        early_stopping_rounds = 100, metrics = 'auc', seed = 50)

    # Extract the best score
    best_score = max(cv_results['auc-mean'])

    # Loss must be minimized
    loss = 1 - best_score

    # Dictionary with information for evaluation
    return {'loss': loss, 'params': params, 'status': STATUS_OK}

核心的程式碼為「cv_results = lgb.cv(...)」。為了實現帶早停止的交叉驗證,我們使用了 LightGBM 的函式「cv」,向該函式傳入的引數包含引數、一個訓練集、交叉驗證中使用的許多折,以及一些其它的引數。我們將評估器的數量(num_boost_round)設定為 10000,但是由於我們使用了「early_stopping_rounds」,當 100 個評估器的驗證得分沒有提高時訓練會被停止,所以實際上使用的評估器不會達到這個數量。早停止是一種有效的選擇評估器數量的方法,而不是將其設定為另一個需要調優的引數

交叉驗證完成後,我們將得到最高得分(ROC AUC)。之後,由於我們想要得到的是最小值,我們將採用「1-最高得分」。該值將在返回的字典資料結構中作為「loss」關鍵字返回。

這個目標函式實際上比它所需的結構複雜一些,因為我們將返回一個值的字典。對於 Hyperopt 中的目標函式,我們可以返回一個單一的值(即損失),或者返回一個帶有最小值的關鍵字「loss」和「status」的字典。返回引數的值使我們能夠檢視每組引數得到的損失。

域空間

域空間表示我們想要對每個引數進行評估的值的範圍。在每一輪搜尋迭代中,貝葉斯優化演算法將從域空間中為每個引數選定一個值。當我們進行隨機搜尋或網格搜尋時,域空間就是一個網格。貝葉斯優化中也是如此,只是這個域空間對每個引數來說是一個概率分佈而不是離散的值。

然而,在貝葉斯優化問題中,確定域空間是最難的部分。如果有機器學習方法的相關經驗,我們可以將更大的概率賦予我們認為最佳值可能存在的點,以此來啟發對引數分佈的選擇。但是,不同的資料集之間的最佳模型設定是不同的,並且具有高維度的問題(大量的引數),這會使我們很難弄清引數之間的相互作用。在不確定最佳值的情況下,我們可以使用更大範圍的概率分佈,通過貝葉斯演算法進行推理。

首先,我們應該瞭解一個 GBM 中所有的引數

import lgb
# Default gradient boosting machine classifier
model = lgb.LGBMClassifier()
model
LGBMClassifier(boosting_type='gbdt', n_estimators=100,
               class_weight=None, colsample_bytree=1.0,
               learning_rate=0.1, max_depth=-1,                      
               min_child_samples=20,
               min_child_weight=0.001, min_split_gain=0.0, 
               n_jobs=-1, num_leaves=31, objective=None, 
               random_state=None, reg_alpha=0.0, reg_lambda=0.0, 
               silent=True, subsample=1.0, 
               subsample_for_bin=200000, subsample_freq=1)

我不確定世界上是否真有人知道所有的這些引數是如何相互作用的!而其中有一些引數是不需要調優(如「objective」和「random_state」)。我們將使用早停止方法找到最佳的評估器數量「n_estimators」。儘管如此,我們仍然需要優化 10 個引數!當我們第一次對一個模型進行調優時,我通常建立一個以預設值為中心的大範圍域空間,然後在接下來的搜尋中對其進行優化。

舉個例子,我們不妨在 Hyperopt 中定義一個簡單的域——一個離散均勻分佈,其中離散點的數量為 GBM 中每棵決策樹的葉子結點數:

from hyperopt import hp
# Discrete uniform distribution
num_leaves = {'num_leaves': hp.quniform('num_leaves', 30, 150, 1)}

這裡使用的是一個離散均勻分佈,因為葉子結點的數量必須是一個整數(離散的)並且域中的每個值出現的概率是均等的(均勻)。

概率分佈的另一種選項是對數均勻分佈,在對數尺度上其值的分佈是均勻的。我們將對學習率使用一個對數均勻分佈(域空間從 0.005 到 0.2),因為它的值往往在不同的數量級之間變化:

# Learning rate log uniform distribution
learning_rate = {'learning_rate': hp.loguniform('learning_rate',
                                                 np.log(0.005),
                                                 np.log(0.2)}

由於這是一個對數均勻分佈,所以我們在 exp(low)和 exp(high)之間繪製其值的示意圖。下面左側的示意圖顯示了離散均勻分佈,右側的示意圖則顯示了對數均勻分佈。它們是核密度估計示意圖,所以 y 軸座標為密度而不是計數。

Python 環境下的自動化機器學習超引數調優

現在讓我們定義整個域:

# Define the search space
space = {
    'class_weight': hp.choice('class_weight', [None, 'balanced']),
    'boosting_type': hp.choice('boosting_type', 
                               [{'boosting_type': 'gbdt', 
                                    'subsample': hp.uniform('gdbt_subsample', 0.5, 1)}, 
                                 {'boosting_type': 'dart', 
                                     'subsample': hp.uniform('dart_subsample', 0.5, 1)},
                                 {'boosting_type': 'goss'}]),
    'num_leaves': hp.quniform('num_leaves', 30, 150, 1),
    'learning_rate': hp.loguniform('learning_rate', np.log(0.01), np.log(0.2)),
    'subsample_for_bin': hp.quniform('subsample_for_bin', 20000, 300000, 20000),
    'min_child_samples': hp.quniform('min_child_samples', 20, 500, 5),
    'reg_alpha': hp.uniform('reg_alpha', 0.0, 1.0),
    'reg_lambda': hp.uniform('reg_lambda', 0.0, 1.0),
    'colsample_bytree': hp.uniform('colsample_by_tree', 0.6, 1.0)
}

此處我們使用許多不同種類的域分佈:

  • choice:類別變數

  • quniform:離散均勻分佈(在整數空間上均勻分佈)

  • uniform:連續均勻分佈(在浮點數空間上均勻分佈)

  • loguniform:連續對數均勻分佈(在浮點數空間中的對數尺度上均勻分佈)

當定義提升(boosting)的型別時,有一個要點需要我們注意:

# boosting type domain 
boosting_type = {'boosting_type': hp.choice('boosting_type', 
                                            [{'boosting_type': 'gbdt', 
                                                  'subsample': hp.uniform('subsample', 0.5, 1)}, 
                                             {'boosting_type': 'dart', 
                                                  'subsample': hp.uniform('subsample', 0.5, 1)},
                                             {'boosting_type': 'goss',
                                                  'subsample': 1.0}])}

在這裡,我們使用一個條件域,它意味著一個引數的值依賴於另一個引數的值。對於「goss」型別的提升演算法,GBM 不能使用下采樣技術(選擇一個訓練觀測資料的子樣本部分用於每輪迭代)。因此,如果提升的型別為「goss」,則下采樣率設定為 1.0(不使用下采樣),否則將其設定為 0.5-1.0。這個過程是使用巢狀域實現的。

當我們使用引數完全不同的機器學習模型時,條件巢狀往往是很有用的。條件巢狀讓我們能根據「choice」的不同值使用不同的引數集。

現在已經定義了域空間,我們可以從中提取一個樣本來檢視典型樣本的形式。當我們進行取樣時,因為子樣本最初是巢狀的,所以我們需要將它分配給頂層的關鍵字。這個操作是通過 Python 字典的「get」方法實現的,預設值為 1.0。

# Sample from the full space
example = sample(space)

# Dictionary get method with default
subsample = example['boosting_type'].get('subsample', 1.0)

# Assign top-level keys
example['boosting_type'] = example['boosting_type']['boosting_type']
example['subsample'] = subsample

example

Python 環境下的自動化機器學習超引數調優

重新分配巢狀的關鍵字是必要的,因為梯度提升機不能處理巢狀的引數字典。

優化演算法

儘管從概念上來說,這是貝葉斯優化最難的一部分,但在 Hyperopt 中建立優化演算法只需一行程式碼。使用樹形 Parzen 評估器(Tree Parzen Estimation,以下簡稱 TPE)的程式碼如下:

from hyperopt import tpe
# Algorithm
tpe_algorithm = tpe.suggest

這就是優化演算法的所有程式碼!Hyperopt 目前只支援 TPE 和隨機搜尋,儘管其 GitHub 主頁聲稱將會開發其它方法。在優化過程中,TPE 演算法從過去的搜尋結果中構建出概率模型,並通過最大化預期提升(EI)來決定下一組目標函式中待評估的引數

結果歷史資料

跟蹤這些結果並不是絕對必要的,因為 Hyperopt 會在內部為演算法執行此操作。然而,如果我們想要知道這背後發生了什麼,我們可以使用「Trials」物件,它將儲存基本的訓練資訊,還可以使用目標函式返回的字典(包含損失「loss」和引數「params」)。建立一個「Trials」物件也僅需一行程式碼:

from hyperopt import Trials
# Trials object to track progress
bayes_trials = Trials()

另一個讓我們能夠監控長期訓練進度的做法是,在每輪迭代中向 csv 檔案寫入一行。這樣做將所有的搜尋結果儲存到了磁碟上,以防意外情況發生使得我們丟失「Trails」物件(根據經驗來說)。我們可以使用「csv」庫做到這一點。在開始訓練之前,我們開啟一個新的 csv 檔案並且寫入檔案頭(hearder):

import csv

# File to save first results
out_file = 'gbm_trials.csv'
of_connection = open(out_file, 'w')
writer = csv.writer(of_connection)

# Write the headers to the file
writer.writerow(['loss', 'params', 'iteration', 'estimators', 'train_time'])
of_connection.close()

然後在目標函式中,我們可以新增幾行程式碼,在每輪迭代中寫入 csv 檔案(完整的目標函式可以在 notebook 中獲取。

# Write to the csv file ('a' means append)
  of_connection = open(out_file, 'a')
  writer = csv.writer(of_connection)
  writer.writerow([loss, params, iteration, n_estimators, run_time])
  of_connection.close()

寫入 csv 檔案意味著我們可以在訓練時通過開啟檔案來檢查進度(不是在 Excel 檔案中,因為 Excel 會在 Python 環境下導致錯誤。在 bash 中使用「tail out_file.csv」操作來檢視檔案的最後一行。)

優化

當我們完成了上述四個部分的工作,我們可以通過「fmin」進行優化:

from hyperopt import fmin

MAX_EVALS = 500

# Optimize
best = fmin(fn = objective, space = space, algo = tpe.suggest, 
            max_evals = MAX_EVALS, trials = bayes_trials)

在每一輪迭代中,優化演算法從基於先前結果構造的代理函式中選擇新的引數值,並在目標函式中對這些值進行計算。接著,對目標函式進行「MAX_EVALS」評估,代理函式會不斷根據新的結果進行更新。

結果

從「fmin」返回的最佳物件包含在目標函式上產生最小損失的引數

Python 環境下的自動化機器學習超引數調優

擁有這些引數之後,我們可以使用它們在完整的訓練資料上訓練模型,然後對測試資料進行評估(記住我們只能在評估最終的模型時使用一次測試集)。對於評估器的數量,我們可以使用在交叉驗證提前停止時返回最低損失的評估器數量。最終結果如下:

Python 環境下的自動化機器學習超引數調優

作為參考,500 輪隨機搜尋返回一個在測試集上 ROC AUC 得分為 0.7232、在交叉驗證中得分為 0.76850 的模型。一個沒有經過優化的預設模型在測試集上的 ROC AUC 得分則為 0.7143.

當我們檢視結果時,需要將以下幾點重要事項牢記於心:

  • 最優的引數交叉驗證中表現最好,但並不一定在測試資料上表現最好。當我們使用交叉驗證時,我們希望這些結果能夠泛化至測試資料上。

  • 即使使用 10 折交叉驗證引數調優還是會對訓練資料過度擬合。交叉驗證取得的最佳得分遠遠高於在測試資料上的得分。

  • 隨機搜尋可能由於運氣好而返回更好的引數(重新執行 notebook 就可能改變搜尋結果)。貝葉斯優化不能保證找到更好的引數,並且可能陷入目標函式的區域性最小值。

貝葉斯優化雖然十分有效,但它並不能解決我們所有的調優問題。隨著搜尋的進行,該演算法將從探索——嘗試新的引數值,轉向開發——利用使目標函式損失最低的 引數值。如果演算法找到了目標函式的一個區域性最小值,它可能會專注於搜尋區域性最小值附近的引數值,而不會嘗試域空間中相對於區域性最小值較遠的其他值。隨機搜尋則不會受到這個問題的影響,因為它不會專注於搜尋任何值!

另一個重點是,引數優化的效果將隨著資料集的變化而有所差異。本文使用的是一個相對較小的資料集(大約 6000 條訓練觀測資料),因此對引數進行調優的回報較小(獲取更多的資料將更好地利用時間!)考慮到所有這些注意事項,在這種情況下,通過貝葉斯優化我們可以得到:

  • 在測試集上更好的效能

  • 更少的引數調優迭代次數

貝葉斯方法可以(儘管不是經常)獲得比隨機搜尋更好的調優結果。在接下來的幾節中,我們將檢視貝葉斯引數搜尋的演化過程,並且將其與隨機搜尋進行對比,從而理解貝葉斯優化的工作原理。

搜尋結果視覺化

將結果通過圖表繪製出來可以直觀地理解在引數搜尋過程中發生了什麼。此外,將貝葉斯優化和隨機搜尋進行對比有助於我們看到這些方法之間的差異。如果你想知道這些圖是如何繪製的,以及隨機搜尋是如何實現的,請查閱專案 notebook。但是在這裡我們將直接顯示結果。(請注意,實際的結果會隨著迭代次數增加而發生變化,所以如果你執行了這個 notebook,得到了不同的圖也無需驚訝。本節所有的影像都是經過了 500 輪迭代所得到的)。

首先我們可以繪製隨機搜尋和貝葉斯優化中取樣得到的學習率「learning_rate」的核密度估計圖。作為參考,我們還可以顯示取樣的分佈。垂直的虛線表示學習率最優值(根據交叉驗證得到)。

Python 環境下的自動化機器學習超引數調優

我們將學習率定義為 0.005 到 0.2 之間的對數正態分佈形式,貝葉斯優化的結果與取樣分佈的結果看起來相類似。這說明,我們定義的分佈看上去很適合這個任務,儘管最優值比我們放置最大概率的位置略高。這個結果可以用於通知域空間進一步搜尋。

另一個引數是提升型別,下圖為在隨機搜尋和貝葉斯優化的過程中對每種型別進行評估的直方圖。由於隨機搜尋不關心過去的搜尋結果,我們預計每種提升型別的使用次數大致相同。

Python 環境下的自動化機器學習超引數調優

根據貝葉斯演算法的評估結果直方圖,「gbdt」提升比「dart」或「goss」更有可能找到最優值。同樣地,這有助於為進一步搜尋提供資訊,無論是貝葉斯方法或網格搜尋。如果我們想做一個更精確的網格搜尋,我們可以用這些結果來定義一個更小的網格,集中在最有可能找到最優引數的值周圍。

在大多數情況下(「subsample_for_bin」除外),貝葉斯優化搜尋傾向於專注搜尋(設定更大的概率)能夠在交叉驗證中得到最小損失的引數值附近的值。這體現了使用貝葉斯方法進行引數調優的基本思想:將更多的時間用於評估更有可能達到最優的引數值。

有些結果很有趣,可能會幫助我們以後定義一個用於搜尋的域空間。舉例來說,「reg_alpha」和「reg_lambda」看起來是互補的:如果其中的一個值很高(接近於 1.0),另一個值則會變得較低。不能保證這一點在不同的問題中都適用,但是通過研究這些結果,我們可以得到一些可能在未來的機器學習問題中能夠用到的觀點!

搜尋的演化過程

隨著優化的推進,我們期望貝葉斯方法能夠專注於更有希望達到最優引數的值:那些在交叉驗證中產生最低誤差的值。我們可以繪製引數域迭代次數的關係圖,看看是否存在明顯的趨勢。

Python 環境下的自動化機器學習超引數調優

黑色的星星代表最優值。「colsample_bytree」和「learning_rate」隨著時間的推移而下降,這可以在未來的搜尋中為我們提供指導。

Python 環境下的自動化機器學習超引數調優

最後,如果貝葉斯優化有效,我們預計平均的驗證分數會隨著時間推移越來越高(相反,損失將會越來越小):

Python 環境下的自動化機器學習超引數調優

隨著時間的推移,貝葉斯引數優化的驗證得分會越來越高,這說明該方法正在嘗試「更好」的引數值(值得注意的是,只是根據驗證分數說明這些值是更好的)。隨機搜尋並不會隨著迭代次數增加顯示出效能提升。

繼續搜尋

如果我們對模型的效能不太滿意,可以使用 Hyperopt 從我們上次結束的地方繼續搜尋。我們只需要傳入相同的「Trials」物件,演算法就會繼續進行搜尋。

隨著演算法的執行,它會進行更多的「利用」操作——選用在過去表現得很好的值,進行更少的「探索」操作——選用新的值。因此,與其從上次結束的地方開始,還不如從頭開始搜尋。如果第一次搜尋到的最佳引數確實是「最優值」,我們預計接下來的搜尋會收斂到同樣的值上。考慮到這個高維度問題,以及引數之間複雜的相互作用,另一次搜尋並不太可能產生一組類似的引數

再次進行了 500 輪迭代的訓練後,最終模型在測試集上的 ROC AUC 得分為 0.72736。(我們真的不應該在測試集上對第一個模型進行評估,並且僅僅以驗證得分作為依據。理想情況下,測試集只應使用一次,在將演算法部署在新資料上時測試其效能)。同樣的,由於資料集規模較小,這個問題可能會在未來的引數優化中得到逐漸減小的返回值,並且最終會在驗證誤差上達到一個趨近於穩定不變的值(資料集上任何模型的效能都有一個固有的限制,因為隱藏的變數沒有被測量,並且有噪聲資料,這被稱為貝葉斯誤差)。

結語

我們可以使用貝葉斯優化完成機器學習模型的自動引數調優。不同於隨機搜尋方法,貝葉斯優化通過啟發式方法選擇下一個引數,從而能夠花更多時間評估可能達到最優的值。最終的結果可能是,與隨機搜尋或網格搜尋相比,貝葉斯優化對於目標函式評估的次數更少,並且在測試集上泛化的能力更強。

在本文中,我們使用 Hyperopt 一步一步地實現了 Python 環境下的貝葉斯引數優化。儘管對於訓練資料的過擬合問題需要多加小心,但我們提升了梯度提升機在測試集上的效能,超過了對比基線和隨機搜尋方法。此外,我們還通過檢視結果示意圖看到了隨機搜尋和貝葉斯優化的區別,該圖說明了貝葉斯方法會將更大的概率賦予在交叉驗證中獲得更低損失的引數值。

通過利用最優化問題的四個組成部分,我們可以使用 Hyperopt 解決各種各樣的問題。貝葉斯優化的基本組成部分也適用於大量的實現其他演算法的 Python 庫。從手動調優到隨機搜尋或網格搜尋只是一個小的進步,但如果想要將你的機器學習技術提升到一個新的水平,則需要自動化的引數調優。貝葉斯優化是一種能夠在 Python 環境下使用的方法,並且能返回比隨機搜尋更好的結果。希望你能滿懷信心地開始將這種強大的技術應用到自己的機器學習問題中!

原文地址:https://towardsdatascience.com/automated-machine-learning-hyperparameter-tuning-in-python-dfda59b72f8a

相關文章