GBDT のパラメータ空間とハイパーパラメータの最適化

        

目次

1. デフォルトパラメータでの GBDT と他のアルゴリズムの比較

2. TPE に基づいて GBDT を最適化する

ステップ1: ベンチマークを確立する

step2: パラメータ init で必要なアルゴリズムを定義する

step3: 目的関数、パラメータ空間、最適化関数、検証関数を定義する

ステップ 4: ベイジアン オプティマイザーをトレーニングする

step5: サーチスペースを変更する

ステップ6: サーチスペースの変更を続けます


豊富なハイパーパラメータは、統合されたアルゴリズムに無限の可能性をもたらします. バイアスを減らすことを目的としたブースティング アルゴリズムのパフォーマンスは、パラメータ調整後にさらに無敵になります. したがって、GBDT ハイパーパラメータの自動最適化も重要なトピックです. アンサンブル アルゴリズムのハイパーパラメーターを最適化する前に、2 つの基本的な事実を明確にする必要があります: ① アルゴリズムの結果に対するさまざまなパラメーターの影響; ② 検索に使用されるパラメーター空間を決定します。GBDT の場合、アルゴリズムに対する各パラメーターの影響は次のように大まかに整理できます。

影響 パラメータ
⭐⭐⭐⭐⭐
ほぼ常に大きな影響力を持っています
n_estimators (全体的な学習能力)
learning_rate (全体的な学習率)
max_features (ランダム性)
⭐⭐⭐⭐
ほとんどの場合、影響力を持つ
init (初期化)
サブサンプル (ランダム性)
損失 (全体的な学習能力)
⭐⭐
大きな影響を与えることもあります
が、ほとんどの場合、その影響は明らかではありません。
max_ Depth (粗い枝刈り)
min_samples_split (細かい枝刈り)
min_impurity_decrease (細かい枝刈り)
max_leaf_nodes (細かい枝刈り)
基準 (分岐感度)

データ量が十分に大きい場合、影響はほとんどありません
random_state
ccp_alpha (構造的リスク)

        ほとんどのツリー アンサンブル モデルには、過学習防止、枝刈りに使用されるパラメーター グループ ( など)、サンプル/特徴をサンプリングするためのパラメーター ( など) などの同様のハイパーパラメーターがありますmax_depthこれらmin_samples_splitハイパーパラメーターは、さまざまなアンサンブル モデルにあり、同様の点でモデルに影響します。したがって、原則として、ランダム フォレストに大きな影響を与えるパラメータは、GBDT にも大きな影響を与えます。ただし、ランダム フォレストで非常に重要なものは、GBDT には存在せず、Boosting の独自の反復パラメーター学習率に置き換えられますランダム フォレストでは、モデルの複雑さ ( ) とモデルの全体的な学習能力 ( )の間のバランスを常に気にします。単一の弱い評価器の複雑さが大きいほど、単一の弱い評価器のモデルへの全体的な寄与も大きくなります。必須 木が少なくなりました。Boosting アルゴリズムでは、アルゴリズム全体に対する 1 つの弱い評価器の寄与は、弱い評価器の複雑さを置き換える学習率パラメーターによって制御されます。したがって、Boosting アルゴリズムで探しているのは、 と のバランスです同時に、Boosting アルゴリズムは本質的に、単一の弱推定量の能力が非常に弱く、パラメーターのデフォルト値が小さいことが多い (GBDT のデフォルト値は 3) ため、低いに依存することはできません。大規模なモデルの複雑さを軽減するため、過学習の制御に依存することがより困難になり、自然な影響が小さくなります。ほとんどのツリー アンサンブル アルゴリズムは同じハイパーパラメータを共有しますが、異なるアルゴリズムを構築するときの原理の仮定が異なるため、異なるアルゴリズムでは同じパラメータのデフォルト値が異なるように設定される可能性があるため、異なるアルゴリズムでは同じパラメータが重要であることがわかりますアルゴリズムの性質やパラメータ調整の考え方も異なりますsubsamplemax_featuresmax_depthlearning_ratemax_depthn_estimatorslearning_ratelearning_raten_estimatorsmax_depthmax_depthmax_depthmax_depthmax_depth

        ランダム フォレストを解決する場合、細かい剪​​定ツールの有効性は限られており、一般に大幅な粗剪定の方が効果的です。GBDTでは、max_depthこの粗枝刈りツールのデフォルト値が3であるため、Boostingアルゴリズムでモデルの複雑さを軽減して過学習を制御するという考え方は採用できません。特にパラメータはinitGBDT に大きな影響を与え、initパラメータに特定のアルゴリズムを埋め込むと過学習が深刻化する可能性があるため、過学習の抑制と複雑さの制御に努める必要があります。弱い評価器を枝刈りできない場合、過学習を制御する最善の方法は、ランダム性/多様性を高めることです。したがって、合計はブースティングアルゴリズムで過学習を制御するための中心的な武器max_featuresになりますsubsample。これが、バギングのアイデアが GBDT に追加される理由です。主な理由。過学習に対抗するために弱い推定器構造ではなくランダム性に依存することにより、Boosting アルゴリズムに予期せぬ利点がもたらされます。Bagging と比較して、Boosting は小さなサンプルの高次元データの処理に優れています。これは、Bagging データは小さなサンプル データセットで簡単に過学習するためです。

max_depthオーバーフィッティングの制御にはあまり寄与しませんが、パラメーターを調整する際にはこのパラメーターを保持する必要があることに        注意してください。パラメーターを使用してmax_featuresランダムsubsample性を構築し、各ツリーの差を増やすと、モデルの学習能力に影響が出る可能性があるため、単一の弱推定器の複雑さを増やす必要がある場合があります。したがって、GBDT では、max_depthパラメータ調整の最適な方向は、モデルがより高い単一推定器の複雑さを必要とするかどうかを調査するために拡大/深化することです。対照的に、ランダム フォレストでは、max_depthパラメーター調整の方向は、過剰適合を軽減するための削減/枝刈りです。

        では、パラメータを調整する際にはどのようなパラメータを選択すればよいのでしょうか? まず、大きな影響力を持つすべてのパラメータを検討します。計算能力が十分であるか、最適化アルゴリズムが高速に実行される場合、ほとんどの時間に影響を与えるパラメータをパラメータ空間に追加することを検討できます。サンプルサイズが小さい場合は、選択しない可能性があります。subsampleこれに加えて、弱い評価器の複雑さに影響を与えるいくつかのパラメータも必要ですmax_depth計算能力が十分であれば、criterion効果がありそうなパラメータを追加することもできます。この基本的な考え方に基づいて、ハードウェアと実行時間の要素を考慮して、次のパラメータが調整のために選択され、TPE ベースのベイジアン最適化 (HyperOpt) が GBDT の最適化に使用されます。

パラメータ 範囲
loss 回帰損失の 4 つのオプションの損失関数
["squared_error"、"absolute_error"、"huber"、"quantile"]
criterion 4 つのオプションの不純物評価インジケーターすべて
["friedman_mse"、"squared_error"]
init HyperOpt は検索、手動パラメータ調整をサポートしていません
n_estimators 途中で停止して中間の数値 50 を確認した後、最終範囲は (25,200,25) に設定されます。
learning_rate 1.0を中心として両側に広げた場合、最終的な範囲は(0.05, 2.05, 0.05)となりますが、
計算能力が限られている場合は(0.1, 2.1, 0.1)とすることもできます。
max_features すべての文字列と、sqrt と auto の間の値
subsample サブサンプルパラメータの値の範囲は(0,1]なので、固定範囲(0.1,0.8,0.1)が
制限されている場合は、(0.5,0.8,0.1)のように設定することもできます。
max_depth 3を中心として両側に伸ばし、右側の方が大きく設定されています。最終確認(2,30,2)
min_impurity_decrease 拡大のみ可能で縮小はできないパラメータの場合は、まず (0,5,1) の範囲を試してください。

一般に、最初の検索では、より大きく疎なパラメーター空間を設定し、その後、複数回の検索中に徐々に範囲を狭め、パラメーター空間の次元を減らします。initパラメーターに入力する必要があるエバリュエーター オブジェクトは HyperOpt ライブラリでは認識できないため、initパラメーターを手動で調整するしかないことに注意してください。

1. デフォルトパラメータでの GBDT と他のアルゴリズムの比較

必要なライブラリとデータをインポートします。

import pandas as pd
import numpy as np
import sklearn
import matplotlib as mlp
import matplotlib.pyplot as plt
import time
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.ensemble import GradientBoostingRegressor as GBR
from sklearn.ensemble import AdaBoostRegressor as ABR
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.model_selection import cross_validate, KFold

#导入优化算法
import hyperopt
from hyperopt import hp, fmin, tpe, Trials, partial
from hyperopt.early_stop import no_progress_loss

data = pd.read_csv(r"F:\\Jupyter Files\\机器学习进阶\\datasets\\House Price\\train_encode.csv",index_col=0)
X = data.iloc[:,:-1]
y = data.iloc[:,-1]
X.shape #(1460, 80)
cv = KFold(n_splits=5,shuffle=True,random_state=1412)

def RMSE(result,name):
    return abs(result[name].mean())
modelname = ["GBDT","RF","AdaBoost","RF-TPE","Ada-TPE"]

models = [GBR(random_state=1412)
         ,RFR(random_state=1412)
         ,ABR(random_state=1412)
         ,RFR(n_estimators=89, max_depth=22, max_features=14, min_impurity_decrease=0
              ,random_state=1412, verbose=False)
         ,ABR(n_estimators=39, learning_rate=0.94,loss="exponential"
              ,random_state=1412)]

colors = ["green","gray","orange","red","blue"]
for name,model in zip(modelname,models):
    start = time.time()
    result = cross_validate(model,X,y,cv=cv,scoring="neg_root_mean_squared_error"
                            ,return_train_score=True
                            ,verbose=False)
    end = time.time()-start
    print(name)
    print("\t train_score:{:.3f}".format(RMSE(result,"train_score")))
    print("\t test_score:{:.3f}".format(RMSE(result,"test_score")))
    print("\t time:{:.2f}s".format(end))
    print("\n")
--------------------------------------------------------------------------------------
GBDT
	 train_score:13990.791
	 test_score:28783.954
	 time:2.16s


RF
	 train_score:11177.272
	 test_score:30571.267
	 time:5.35s


AdaBoost
	 train_score:27062.107
	 test_score:35345.931
	 time:0.99s


RF-TPE
	 train_score:11208.818
	 test_score:28346.673
	 time:1.36s


Ada-TPE
	 train_score:27401.542
	 test_score:35169.730
	 time:0.83s

 

2. TPE に基づいて GBDT を最適化する

ステップ1: ベンチマークを確立する
アルゴリズム RF エイダブースト GBDT RF
(TPE)
アダブースト
(TPE)

検証実行時間が50% オフ
5.35秒 0.99秒 2.16秒 1.36秒 0.83秒
最適スコア
(RMSE)
30571.267 35345.931 28783.954 28346.673 35169.73
step2:パラメータinitに必要なアルゴリズムを定義する
rf = RFR(n_estimators=89, max_depth=22, max_features=14,min_impurity_decrease=0
         ,random_state=1412, verbose=False)
step3: 目的関数、パラメータ空間、最適化関数、検証関数を定義する

①目的関数

def hyperopt_objective(params):
    reg = GBR(n_estimators = int(params["n_estimators"])
              ,learning_rate = params["lr"]
              ,criterion = params["criterion"]
              ,loss = params["loss"]
              ,max_depth = int(params["max_depth"])
              ,max_features = params["max_features"]
              ,subsample = params["subsample"]
              ,min_impurity_decrease = params["min_impurity_decrease"]
              ,init = rf
              ,random_state=1412
              ,verbose=False)
    
    cv = KFold(n_splits=5,shuffle=True,random_state=1412)
    validation_loss = cross_validate(reg,X,y
                                     ,scoring="neg_root_mean_squared_error"
                                     ,cv=cv
                                     ,verbose=False
                                     ,error_score='raise'
                                    )
    return np.mean(abs(validation_loss["test_score"]))

②パラメータ空間

パラメータ 範囲
loss 回帰損失の 4 つのオプションの損失関数
["squared_error"、"absolute_error"、"huber"、"quantile"]
criterion 4 つのオプションの不純物評価インジケーターすべて
["friedman_mse"、"squared_error"]
init HyperOpt は検索、手動パラメータ調整をサポートしていません
n_estimators 途中で停止して中間の数値 50 を確認した後、最終範囲は (25,200,25) に設定されます。
learning_rate 1.0を中心として両側に広げた場合、最終的な範囲は(0.05, 2.05, 0.05)となりますが、
計算能力が限られている場合は(0.1, 2.1, 0.1)とすることもできます。
max_features 所有字符串,外加sqrt与auto中间的值
subsample subsample参数的取值范围为(0,1],因此定范围(0.1,0.8,0.1)
如果算力有限,也可定为(0.5,0.8,0.1)
max_depth 以3为中心向两边延展,右侧范围定得更大。最后确认(2,30,2)
min_impurity_decrease 只能放大、不能缩小的参数,先尝试(0,5,1)范围
param_grid_simple = {'n_estimators': hp.quniform("n_estimators",25,200,25)
                  ,"lr": hp.quniform("learning_rate",0.05,2.05,0.05)
                  ,"criterion": hp.choice("criterion",["friedman_mse", "squared_error"])
                  ,"loss":hp.choice("loss",["squared_error","absolute_error", "huber", "quantile"])
                  ,"max_depth": hp.quniform("max_depth",2,30,2)
                  ,"subsample": hp.quniform("subsample",0.1,0.8,0.1)
                  ,"max_features": hp.choice("max_features",["log2","sqrt",16,32,64,1.0])
                  ,"min_impurity_decrease":hp.quniform("min_impurity_decrease",0,5,1)
                 }

③ 优化函数

def param_hyperopt(max_evals=100):
    
    #保存迭代过程
    trials = Trials()
    
    #设置提前停止
    early_stop_fn = no_progress_loss(100)
    
    #定义代理模型
    params_best = fmin(hyperopt_objective
                       , space = param_grid_simple
                       , algo = tpe.suggest
                       , max_evals = max_evals
                       , verbose=True
                       , trials = trials
                       , early_stop_fn = early_stop_fn
                      )
    
    #打印最优参数,fmin会自动打印最佳分数
    print("\n","\n","best params: ", params_best,
          "\n")
    return params_best, trials

④ 验证函数

def hyperopt_validation(params):    
    reg = GBR(n_estimators = int(params["n_estimators"])
              ,learning_rate = params["learning_rate"]
              ,criterion = params["criterion"]
              ,loss = params["loss"]
              ,max_depth = int(params["max_depth"])
              ,max_features = params["max_features"]
              ,subsample = params["subsample"]
              ,min_impurity_decrease = params["min_impurity_decrease"]
              ,init = rf
              ,random_state=1412 #GBR中的random_state只能够控制特征抽样,不能控制样本抽样
              ,verbose=False)
    cv = KFold(n_splits=5,shuffle=True,random_state=1412)
    validation_loss = cross_validate(reg,X,y
                                     ,scoring="neg_root_mean_squared_error"
                                     ,cv=cv
                                     ,verbose=False
                                    )
    return np.mean(abs(validation_loss["test_score"]))
step4:训练贝叶斯优化器
params_best, trials = param_hyperopt(30) #使用小于0.1%的空间进行训练
100%|████████████████████████████████████████████████| 30/30 [02:43<00:00,  5.45s/trial, best loss: 26847.550613053456]
 
 best params:  {'criterion': 0, 'learning_rate': 0.05, 'loss': 0, 'max_depth': 14.0, 'max_features': 2, 'min_impurity_decrease': 3.0, 'n_estimators': 125.0, 'subsample': 0.5} 
params_best #注意hp.choice返回的结果是索引,而不是具体的数字
{'criterion': 0,
 'learning_rate': 0.05,
 'loss': 0,
 'max_depth': 14.0,
 'max_features': 2,
 'min_impurity_decrease': 3.0,
 'n_estimators': 125.0,
 'subsample': 0.5}
hyperopt_validation({'criterion': "friedman_mse",
                     'learning_rate': 0.05,
                     'loss': "squared_error",
                     'max_depth': 14.0,
                     'max_features': 16,
                     'min_impurity_decrease': 3.0,
                     'n_estimators': 125.0,
                     'subsample': 0.5})
26847.550613053456

不难发现,我们已经得到了历史最好分数,但GBDT的潜力远不止如此。现在我们可以根据第一次训练出的结果缩小参数空间,继续进行搜索。在多次搜索中,我发现loss参数的最优选项基本都是平方误差"squared_error",因此我们可以将该参数排除出搜索队伍。同样,对于其他参数,我们则根据搜索结果修改空间范围、增加空间密度,一般以被选中的值为中心向两边拓展,并减小步长,同时范围可以向我们认为会被选中的一边倾斜。例如最大深度max_depth被选为14,我们则将原本的范围(2,30,2)修改为(10,25,1)。同样subsample被选为0.5,我们则将新范围调整为(0.3,0.7,0.05),依次类推。

step5:修改搜索空间
param_grid_simple = {'n_estimators': hp.quniform("n_estimators",100,180,5)
                     ,"lr": hp.quniform("learning_rate",0.02,0.2,0.04)
                     ,"criterion": hp.choice("criterion",["friedman_mse", "squared_error"])
                     ,"max_depth": hp.quniform("max_depth",10,25,1)
                     ,"subsample": hp.quniform("subsample",0.3,0.7,0.05)
                     ,"max_features": hp.quniform("max_features",10,20,1)
                     ,"min_impurity_decrease":hp.quniform("min_impurity_decrease",0,5,0.5)
                    }

由于需要修改参数空间,因此目标函数也必须跟着修改:

def hyperopt_objective(params):
    reg = GBR(n_estimators = int(params["n_estimators"])
              ,learning_rate = params["lr"]
              ,criterion = params["criterion"]
              ,max_depth = int(params["max_depth"])
              ,max_features = int(params["max_features"])
              ,subsample = params["subsample"]
              ,min_impurity_decrease = params["min_impurity_decrease"]
              ,loss = "squared_error"
              ,init = rf
              ,random_state=1412
              ,verbose=False)    
    cv = KFold(n_splits=5,shuffle=True,random_state=1412)
    validation_loss = cross_validate(reg,X,y
                                     ,scoring="neg_root_mean_squared_error"
                                     ,cv=cv
                                     ,verbose=False
                                     ,error_score='raise')
    return np.mean(abs(validation_loss["test_score"]))



def param_hyperopt(max_evals=100):    
    #保存迭代过程
    trials = Trials() 
    #设置提前停止
    early_stop_fn = no_progress_loss(100)    
    #定义代理模型
    params_best = fmin(hyperopt_objective
                       , space = param_grid_simple
                       , algo = tpe.suggest
                       , max_evals = max_evals
                       , verbose=True
                       , trials = trials
                       , early_stop_fn = early_stop_fn)    
    #打印最优参数,fmin会自动打印最佳分数
    print("\n","\n","best params: ", params_best,
          "\n")
    return params_best, trials
params_best, trials = param_hyperopt(30) #使用小于0.1%的空间进行训练
100%|█████████████████████████████████████████████████| 30/30 [01:24<00:00,  2.82s/trial, best loss: 26673.75433067303]

 best params:  {'criterion': 0, 'learning_rate': 0.04, 'max_depth': 12.0, 'max_features': 13.0, 'min_impurity_decrease': 3.5, 'n_estimators': 110.0, 'subsample': 0.7000000000000001}
params_best, trials = param_hyperopt(60) #尝试增加搜索次数
100%|█████████████████████████████████████████████████| 60/60 [03:14<00:00,  3.24s/trial, best loss: 26736.01565552259]

 best params:  {'criterion': 0, 'learning_rate': 0.08, 'max_depth': 13.0, 'max_features': 15.0, 'min_impurity_decrease': 1.0, 'n_estimators': 145.0, 'subsample': 0.7000000000000001}

基于该结果,我们又可以确定进一步确定部分参数的值(比如criterion),再次缩小参数范围、增加参数空间的密集程度。

step6:继续修改搜索空间
param_grid_simple = {'n_estimators': hp.quniform("n_estimators",100,150,1)
                     ,"lr": hp.quniform("learning_rate",0.01,0.1,0.005)
                     ,"max_depth": hp.quniform("max_depth",10,16,1)
                     ,"subsample": hp.quniform("subsample",0.65,0.85,0.0025)
                     ,"max_features": hp.quniform("max_features",12,16,1)
                     ,"min_impurity_decrease":hp.quniform("min_impurity_decrease",1,4,0.25)
                    }
def hyperopt_objective(params):
    reg = GBR(n_estimators = int(params["n_estimators"])
              ,learning_rate = params["lr"]
              ,max_depth = int(params["max_depth"])
              ,max_features = int(params["max_features"])
              ,subsample = params["subsample"]
              ,min_impurity_decrease = params["min_impurity_decrease"]
              ,criterion = "friedman_mse"
              ,loss = "squared_error"
              ,init = rf
              ,random_state=1412
              ,verbose=False)    
    cv = KFold(n_splits=5,shuffle=True,random_state=1412)
    validation_loss = cross_validate(reg,X,y
                                     ,scoring="neg_root_mean_squared_error"
                                     ,cv=cv
                                     ,verbose=False
                                     ,error_score='raise')
    return np.mean(abs(validation_loss["test_score"]))

def param_hyperopt(max_evals=100):    
    #保存迭代过程
    trials = Trials()    
    #设置提前停止
    early_stop_fn = no_progress_loss(100)    
    #定义代理模型
    params_best = fmin(hyperopt_objective
                       , space = param_grid_simple
                       , algo = tpe.suggest
                       , max_evals = max_evals
                       , verbose=True
                       , trials = trials
                       , early_stop_fn = early_stop_fn)    
    #打印最优参数,fmin会自动打印最佳分数
    print("\n","\n","best params: ", params_best,
          "\n")
    return params_best, trials
params_best, trials = param_hyperopt(300) #缩小参数空间的同时增加迭代次数
43%|███████████████████▉                          | 130/300 [06:12<08:06,  2.86s/trial, best loss: 26563.111168263265]

 best params:  {'learning_rate': 0.055, 'max_depth': 10.0, 'max_features': 13.0, 'min_impurity_decrease': 1.25, 'n_estimators': 124.0, 'subsample': 0.7675000000000001} 

关闭提前停止,继续迭代:

def param_hyperopt(max_evals=100):    
    #保存迭代过程
    trials = Trials()
    #定义代理模型
    params_best = fmin(hyperopt_objective
                       , space = param_grid_simple
                       , algo = tpe.suggest
                       , max_evals = max_evals
                       , verbose=True
                       , trials = trials)  
    #打印最优参数,fmin会自动打印最佳分数
    print("\n","\n","best params: ", params_best,
          "\n")
    return params_best, trials
params_best, trials = param_hyperopt(300) #取消提前停止,继续迭代
100%|███████████████████████████████████████████████| 300/300 [13:52<00:00,  2.77s/trial, best loss: 26412.12758959595]

 best params:  {'learning_rate': 0.085, 'max_depth': 10.0, 'max_features': 13.0, 'min_impurity_decrease': 2.75, 'n_estimators': 145.0, 'subsample': 0.675} 
start = time.time()
hyperopt_validation({'criterion': "friedman_mse",
                     'learning_rate': 0.085,
                     'loss': "squared_error",
                     'max_depth': 10.0,
                     'max_features': 13,
                     'min_impurity_decrease': 2.75,
                     'n_estimators': 145.0,
                     'subsample': 0.675})
26412.12758959595
end = (time.time() - start)
print(end)
2.7066071033477783
算法 RF AdaBoost GBDT RF
(TPE)
AdaBoost
(TPE)

GBDT

(TPE)

5折验证
运行时间
5.35s 0.99s 2.16s 1.36s 0.83s 2.70s(↑)
最优分数
(RMSE)
30571.267 35345.931 28783.954 28346.673 35169.73 26412.1278(↓)

GBDT获得了目前为止的最高分,虽然这一组参数最终指向了145棵树,导致GBDT运行所需的时间远远高于其他算法,GBDT上得到的分数是比精细调参后的随机森林还低2000左右,这证明了GBDT在学习能力上的优越性。由于TPE是带有强随机性的过程,因此如果我们多次运行,我们将得到不同的结果,但GBDT的预测分数可以稳定在26500上下。如果算力支持使用更多的迭代次数、或使用更大更密集的参数空间,我们或许可以得到更好的分数。同时,如果能够找到一组大学习率、小迭代次数的参数,那GBDT的训练速度也会随之上升。

 

おすすめ

転載: blog.csdn.net/weixin_60200880/article/details/131968217
おすすめ