勾配ブースティング分類器は、多くの弱い学習モデルを組み合わせて強い予測モデルを作る機械学習アルゴリズムの一群である。通常,gradient boosting を行う際には決定木が使われる.勾配ブースティングは複雑なデータセットを分類するのに有効であるため人気があり、最近では多くのKaggleデータサイエンスコンペティションで優勝するために使用されています。
Pythonの機械学習ライブラリであるScikit-Learnは、XGBoostを含む様々なgradient boosting分類器の実装をサポートしています。
この記事では、gradient boostingモデル/分類器の背後にある理論について説明し、Scikit-Learnでgradient boosting分類器を用いて分類を行う2つの異なる方法について見ていきたいと思います。
用語の定義
まず、機械学習とgradient boosting分類器に関連する用語の定義から始めましょう。
まず,分類とは何だろうか?機械学習における教師あり学習には、分類と回帰の2種類がある。
分類とは、機械学習アルゴリズムに特徴を与え、そのアルゴリズムがインスタンス/データ点を多くの離散クラスのうちの1つに分類することを指します。クラスは本質的に分類的であり、あるインスタンスが部分的にあるクラスに分類され、部分的に別のクラスに分類されることはありえない。分類タスクの典型的な例は、電子メールを「スパム」か「非スパム」かのどちらかに分類することで、「ちょっとスパムっぽい」電子メールは存在しません。
回帰は、機械学習モデルの出力が実数値または連続値である場合に行われる。このような連続値の例としては、「重さ」や「長さ」などがある。回帰タスクの例としては、身長、体重、収入などの特徴量から年齢を予測することなどが挙げられる。
勾配ブースティング分類器は、その名前が示すように、分類タスクに使用される特定のタイプのアルゴリズムである。
特徴量とは、機械学習アルゴリズムに与える入力であり、出力値を計算するために使われる入力である。数学的に言えば、データセットの特徴量は方程式を解くための変数である。方程式のもう一つの部分は、インスタンスが分類されるクラスであるラベルまたはターゲットである。ラベルには機械学習分類器の目標値が含まれているため、分類器を学習する際には、データをトレーニングセットとテストセットに分割する必要があります。トレーニングセットにはターゲット/ラベルが含まれ、テストセットにはこれらの値が含まれません。
Scikit-Learn(sklearn)はPythonの機械学習ライブラリで、機械学習アルゴリズムの実装を容易にし、機械学習のタスクを促進することを目的として作成されました。データを学習セットとテストセットに分割し、モデルの学習、予測、モデルの評価などを支援する使いやすい関数が用意されている。
グラデーションブースティングができるまで
グラディエント・ブースティングとは、弱い仮説や弱い学習アルゴリズムに手を加えて、仮説・学習者の強度を向上させようというものである。このような仮説ブースティングは、PAC(Probability Approx Correct Learning)の考えに基づいている。
このPAC学習法は、機械学習の問題を調査して、その複雑さを解釈するものですが、同様の方法を仮説ブースティングに適用しています。
仮説ブースティングでは、機械学習アルゴリズムが学習したすべての観測値を調べ、機械学習法が分類に成功した観測値だけを残し、他の観測値を削除する。新しい弱い学習器を作り、分類がうまくいかなかったデータセットでテストし、分類がうまくいった例だけを残す。
この考え方はAdaptive Boosting (AdaBoost) アルゴリズムで実現された。AdaBoostでは、下の画像の「切り株」のように、1つの分岐しか持たない決定木アルゴリズムを多数初期化することで、多くの弱い学習器が作られる。
学習セットのインスタンス/オブザベーションはアルゴリズムによって重み付けされ、分類が困難なインスタンスにはより多くの重みが割り当てられる。弱い学習器は順次システムに追加され、最も困難な訓練インスタンスに割り当てられる。
AdaBoostの予測は多数決で行われ、弱い学習器から最も多くの票を得たクラスによってインスタンスが分類される。
勾配ブースティング分類器は,AdaBoost 法と重み付き最小化法を組み合わせ,分類器と重み付き入力を再計算したものです.勾配ブースティング分類器の目的は,学習例の実際のクラス値と予測されるクラス値との差である損失を最小化することです.分類器の損失を減らすためのプロセスを理解する必要はありませんが、ニューラルネットワークにおける勾配降下と似たような動作をします。
この処理に改良を加え、Gradient Boosting Machinesが作られた。
Gradient Boosting Machinesの場合、新しい弱い学習器がモデルに追加されるたびに、以前の学習器の重みが凍結または固着され、新しいレイヤーが導入されても変更されないままとなります。これは、AdaBoostingのように新しい学習器を追加するたびに値を調整するアプローチとは異なる。
勾配ブースティング機械は、2値分類問題だけでなく、多値分類問題や回帰問題にも利用できることが大きな特徴です。
グラデーションブーストの理論
グラディエント・ブースティング分類器は損失関数に依存します。カスタム損失関数を使用することもできますし、多くの標準的な損失関数がグラディエント・ブースティング分類器によってサポートされていますが、損失関数は微分可能でなければなりません。
分類アルゴリズムでは対数損失がよく使われますが、回帰アルゴリズムでは二乗誤差が使われることがあります。勾配ブースティング・システムは、ブースティング・アルゴリズムが追加されるたびに新しい損失関数を導き出す必要はなく、むしろ微分可能な損失関数であればどのようなものでもシステムに適用することができます。
また、gradient boostingシステムには、弱学習器と加算器という2つの必要部品がある。勾配ブースティングは弱い学習器として決定木を用いる。弱い学習器には回帰木が使われ、回帰木は実数値を出力する。出力が実数値であるため、新しい学習器をモデルに追加する際に、回帰木の出力を足し合わせて予測値の誤差を補正することができる。
勾配ブースティング・モデルの加算要素は、時間の経過とともに木がモデルに追加され、その際、既存の木は操作されず、その値は固定されたままであるという事実に由来している。
与えられたパラメータ間の誤差を最小化するために、勾配降下法に似た手順が使用される。これは、計算された損失を取り、その損失を減らすために勾配降下法を実行することで行われる。その後、残りの損失を減らすために、木のパラメータを修正する。
そして、新しい木の出力が、モデルで使用された以前の木の出力に追加される。このプロセスは、あらかじめ指定された木の本数に達するか、損失がある閾値以下に減少するまで繰り返される。
グラディエントブースティングのステップ
勾配ブースティング分類器を実装するためには、いくつかのステップを実行する必要があります。そのために必要なことは
- モデルのフィット
- モデルのパラメータとハイパーパラメータを調整する
- 予測を行う
- 結果を解釈する
Scikit-Learnでのモデルのフィットはかなり簡単で、通常はモデルをセットアップした後に fit()
コマンドを呼び出すだけです。
しかし、モデルのハイパーパラメータを調整するためには、私たちの側で能動的に意思決定する必要があります。モデルの精度を上げるために、様々な引数やハイパーパラメータを調整することができます。この方法の1つは、モデルの学習速度を変更することです。学習率を変えてトレーニングセットでのモデルのパフォーマンスをチェックし、最適な学習率で予測を行うようにしたい。
Scikit-Learn では、分類器をフィッティングした後に predict()
関数を使うことで、非常に簡単に予測を行うことができます。テストデータセットの特徴量について予測し、その予測値と実際のラベルを比較することになるでしょう。分類器を評価するプロセスでは、一般的に分類器の精度をチェックし、ユーザーが満足する精度になるまでモデルのパラメータ/ハイパーパラメータを調整します。
改良型勾配ブースティング分類器の違い
勾配ブースティング・アルゴリズムは学習データセットに容易にオーバーフィットするため、アルゴリズムの性能を向上させ、オーバーフィットに対処するために、さまざまな制約や正則化手法を利用することができる。オーバーフィッティングに対処するために、ペナルティ学習、ツリー制約、ランダム化サンプリング、および収縮を利用することができる。
罰則付き学習
決定木の構造によっては、オーバーフィッティングを防ぐために、ある種の制約を利用することができる。勾配ブースティングで用いられる決定木は回帰木であり、葉や重みに数値が用いられる。これらの重みの値は、L1やL2正則化重みのような様々な正則化手法を用いて正則化することができ、radiant boostingアルゴリズムにペナルティを与えることができる。
ツリーの制約
決定木は,木の深さの制限,木の葉またはノードの数の制限,分割ごとのオブザベーションの数の制限,および学習されるオブザベーションの数の制限など,多くの方法で制約を与えることができる.一般に,木を作成するときに多くの制約を使用すればするほど,モデルがデータに適切に適合するために,より多くの木が必要になる.
ランダムサンプリング/ストキャスティックブースティング
確率的勾配ブースティングと呼ばれる手法で、学習データセットのランダムなサブサンプルを取ることも、オーバーフィットを防ぐのに有効である。この手法は、基本的に樹木間の相関の強さを弱めるものである。
データセットのサブサンプリングには、各分割の前に列をサブサンプリングする方法、ツリーを作成する前に列をサブサンプリングする方法、ツリーを作成する前に行をサブサンプリングする方法など、複数の方法がある。一般に、データの50%を超えない大きな割合でサブサンプリングすると、モデルにとって有益であると思われる。
シュリンク・ウェイトアップデート
各ツリーの予測値は合計されるため、シュリンケージと呼ばれる手法により、ツリーの寄与を抑制または減速させることができる。学習率」を調整し、学習率を下げると、より多くの木をモデルに追加しなければならない。このため、モデルの学習に時間がかかるようになる。
学習率と必要な木の本数はトレードオフの関係にあるので、それぞれのパラメータについて最適な値を実験的に見つける必要があるが、0.1未満の小さな値や0.1〜0.3程度の値であればうまくいくことが多いだろう。
XGBoost
XGBoostは、勾配ブースティング決定木システムを改良・カスタマイズしたもので、性能と速度を念頭に置いて作成されています。XGBoostは “eXtreme Gradient Boosting “の略で、gradient boostingアルゴリズムで可能なことの限界に挑戦するためにアルゴリズムと方法がカスタマイズされているという事実を表しています。
次の章では、通常のブースティング分類器と XGBoost 分類器を比較します。
勾配ブースティング分類器の実装
ここでは,単純なグラディエント・ブースティング分類器と XGBoost 分類器の実装について説明します.まず、単純なブースティング分類器から始めます。
正規ブースティング分類器
まず始めに、作業するデータセットを選択する必要があります。この例では、Titanic Datasetを使用することにします。データはここからダウンロードできます。
まず、すべてのライブラリをインポートすることからはじめましょう。
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.ensemble import GradientBoostingClassifier
次に学習データをロードします。
train_data = pd.read_csv("train.csv")
test_data = pd.read_csv("test.csv")
データの前処理が必要かもしれない。まず、インデックスをPassengerId
に設定し、特徴量とラベルを選択します。ラベルデータである y
データは Survived
カラムです。これを独立したデータフレームにして、特徴量から削除します。
y_train = train_data["Survived"]
train_data.drop(labels="Survived", axis=1, inplace=True)
次に、連結された新しいデータセットを作成します。
full_data = train_data.append(test_data)
トレーニングに不要なカラムは削除してしまいましょう。ただし、そのままにしておいて、どのような影響が出るかを見ることもできます。
drop_columns = ["Name", "Age", "SibSp", "Ticket", "Cabin", "Parch", "Embarked"]
full_data.drop(labels=drop_columns, axis=1, inplace=True)
テキストデータはモデルが使用できる数値に変換する必要があるので、今変更しましょう。また、空のセルには0を入力します。
full_data = pd.get_dummies(full_data, columns=["Sex"])
full_data.fillna(value=0.0, inplace=True)
データをトレーニングセットとテストセットに分割しましょう。
X_train = full_data.values[0:891]
X_test = full_data.values[891:]
スケーラのインスタンスを作成して、データをスケーリングしてみましょう。
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
これで、データをトレーニングセットとテストセットに分割することができる。また、シードを設定し(結果を再現できるように)、テスト用のデータのパーセンテージを選択しましょう。
state = 12
test_size = 0.30
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train,
test_size=test_size, random_state=state)
ここで、異なる学習率を設定してみて、異なる学習率での分類器の性能を比較することができます。
lr_list = [0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1]
for learning_rate in lr_list:
gb_clf = GradientBoostingClassifier(n_estimators=20, learning_rate=learning_rate, max_features=2, max_depth=2, random_state=0)
gb_clf.fit(X_train, y_train)
print("Learning rate: ", learning_rate)
print("Accuracy score (training): {0:.3f}".format(gb_clf.score(X_train, y_train)))
print("Accuracy score (validation): {0:.3f}".format(gb_clf.score(X_val, y_val)))
学習率を変えた場合の性能を見てみましょう。
Learning rate: 0.05
Accuracy score (training): 0.801
Accuracy score (validation): 0.731
Learning rate: 0.075
Accuracy score (training): 0.814
Accuracy score (validation): 0.731
Learning rate: 0.1
Accuracy score (training): 0.812
Accuracy score (validation): 0.724
Learning rate: 0.25
Accuracy score (training): 0.835
Accuracy score (validation): 0.750
Learning rate: 0.5
Accuracy score (training): 0.864
Accuracy score (validation): 0.772
Learning rate: 0.75
Accuracy score (training): 0.875
Accuracy score (validation): 0.754
Learning rate: 1
Accuracy score (training): 0.875
Accuracy score (validation): 0.739
我々は主に検証集合における分類器の精度に興味がありますが、学習率0.5が検証集合での最高のパフォーマンスと学習集合での良好なパフォーマンスを与えているようです。
これで、分類器の精度を確認し、混同行列を作成することで、分類器を評価することができます。新しい分類器を作成し、発見した最適な学習率を指定してみましょう。
gb_clf2 = GradientBoostingClassifier(n_estimators=20, learning_rate=0.5, max_features=2, max_depth=2, random_state=0)
gb_clf2.fit(X_train, y_train)
predictions = gb_clf2.predict(X_val)
print("Confusion Matrix:")
print(confusion_matrix(y_val, predictions))
print("Classification Report")
print(classification_report(y_val, predictions))
ここで、調整された分類器の出力が表示されます。
Confusion Matrix:
[[142 19]
[ 42 65]]
Classification Report
precision recall f1-score support
0 0.77 0.88 0.82 161
1 0.77 0.61 0.68 107
accuracy 0.77 268
macro avg 0.77 0.74 0.75 268
weighted avg 0.77 0.77 0.77 268
XGBoost分類器
では、XGBoost分類器を実験してみましょう。
前回と同様、まずは必要なライブラリをインポートします。
from xgboost import XGBClassifier
データはすでに用意されているので、あとは学習データを使って分類器をフィットさせるだけです。
xgb_clf = XGBClassifier()
xgb_clf.fit(X_train, y_train)
分類器の適合と学習が完了したので、 score
コマンドを用いて検証集合に対する分類器のスコアを確認します。
score = xgb_clf.score(X_val, y_val)
print(score)
出力は以下のとおりです。
0.7761194029850746
あるいは、X_val
データを予測し、accuracy_score
を使ってy_val
に対する精度をチェックすることもできます。同じような結果が得られるはずです。
XGboostの精度と通常の勾配分類器の精度を比較すると、この場合は非常に似たような結果であることがわかります。しかし、これは常にそうであるとは限りません。異なる状況では、どちらかの分類器が他よりも優れた性能を発揮することも容易にあり得ます。このモデルの引数を変えてみて、結果がどう違うか見てみましょう。
結論
勾配ブースティングモデルは、分類と回帰の両方のタスクに使用できる強力なアルゴリズムです。勾配ブースティングモデルは、非常に複雑なデータセットに対して非常に良い性能を発揮しますが、オーバーフィッティングを起こしやすいという欠点もあります。Scikit-Learnでは、勾配ブースティング分類器を簡単に実装することができます。
通常のブースティング分類器と XGBoost 分類器の両方を実装したので、同じデータセットに両方実装して、2つの分類器の性能を比較してみてください。
Gradient Boostingの理論についてもっと知りたい方は、こちらをご覧ください。また、Scikit-Learnがサポートする他の分類器についても詳しく知りたい場合は、そのパフォーマンスを比較することができます。Scikit-Learnの分類器について詳しくはこちらをご覧ください。
このコードはGitHubにアップされています。