多数決アンサンブル分類器(VotingClassifier)の使った精度向上テクニック
機械学習では、予測精度を向上させるために複数の予測モデルを組み合わせるアンサンブル手法が有効です。自力でアンサンブルのコードを作成しても良いですが、ライブラリがあるのならそちらを利用した方が効率的です。この記事では、scikit-learnの「多数決アンサンブル分類器(VotingClassifier)」を使って、複数のモデルを統合する方法について解説します。
VotingClassfierとは
VotingClassifier
は、scikit-learnライブラリに含まれるアンサンブル学習の手法の一つです。アンサンブル学習は、複数の機械学習モデルを組み合わせて、単一のモデルよりも優れた性能を達成することを目的としています。
VotingClassifier
では、複数の分類器(クラス分類器)の予測結果を組み合わせ、最終的な予測を決定します。具体的には、異なる種類の分類器(例: ロジスティック回帰、決定木、ランダムフォレストなど)を用意し、各分類機の結果投票によって予測結果を決定します。
VotingClassifier
では、主に以下の2つの方法で投票を行います
- ハード
各分類器の予測の多数決による投票 - ソフト
各分類器のクラス確率の平均を計算し、最も確率の高いクラスを選択
この記事では、VotingClassifierの使い方を、具体例を交えて解説します。
VotingClassifierの使い方を、実際に動作するコードで確認していきます。
Votingclassfierの使い方①
データセット(iris)
利用するデータセットは、おなじみのscikit-learnのirisデータセットです。
このデータセットを使って、花びら(petal)/がく片(sepal)の長さと幅(cm)という4つの特徴量から、あやめの種類(setosa, versicolor, virginicaの3種類)を予測します。
まずは、load_iris()
でデータを読み込んで、訓練データ(train
)とテストデータ(test
)に分けます。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.25)
KNNで予測
1つ目の予測器はKNN(k近傍法)にしました。KNNはscikit-learnで用意されているものを利用します。
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
y_pred1 = knn.predict(X_test)
print(y_pred1)
# [0 2 0 1 0 1 0 2 1 2 2 2 1 1 0 2 2 1 1 0 2 1 2 1 0 0 1 2 1 2 0 1 0 2 1 2 0 1]
SVCで予測
2つ目の予測器はSVM(サポートベクターマシン)です。こちらもscikit-learnで用意されているものを利用します。probability=True
のオプションは、VotingClassifierでsoft
を使う場合に必要になるため設定しています。
from sklearn.svm import SVC
svc = SVC(probability=True)
svc.fit(X_train, y_train)
y_pred2 = svc.predict(X_test)
print(y_pred2)
# [0 2 0 1 0 1 0 2 1 2 2 2 1 1 0 2 2 1 1 0 2 1 2 1 0 0 1 2 1 2 0 1 0 2 1 2 0 1]
LightGBMで予測
3つ目はkaggleなどでもよく利用されているLightGBMです。ここでは、scikit-learnの形式のインタフェースを利用しています。
import lightgbm as lgb
lgbm = lgb.LGBMClassifier(verbose=-1)
lgbm.fit(X_train, y_train)
y_pred3 = lgbm.predict(X_test)
print(y_pred3)
# [0 2 0 1 0 1 0 2 1 2 2 2 1 1 0 2 2 1 1 0 2 1 2 1 0 0 1 2 1 2 0 1 0 2 1 2 0 1]
予測精度を確認
3つの予測器の予測精度を表示してみます。
# 3つの結果を表示
print(accuracy_score(y_test, y_pred1))
print(accuracy_score(y_test, y_pred2))
print(accuracy_score(y_test, y_pred3))
題材の問題か、予測精度は全ての予測機で同じでした。
予測結果がばらつかないとアンサンブルが面白くないかも
0.9473684210526315
0.9473684210526315
0.9473684210526315
VotingClassifierで予測
3つの予測器を用意しましたので、VotingClassifierで3つの予測器を使って予測してみます。
使い方
VotingClassifier
では、estimators
パラメータで予測機の(名前と予測器)のリストを渡します。VotingClassifier
の主な引数は以下になります。
VotingClassifier
の主な引数引数名 | 説明 |
---|---|
estimators | 分類器を設定するパラメータ。[('name', model), …] のように分類器の名前と分類器のオブジェクトのペアをリスト形式で渡します |
voting | 投票方法を設定するパラメータ。hard の場合は多数決。soft の場合は、書く予測器の予測値(確率)の平均をとって、最も確率が高いクラスを返します |
weights | 各予測機の重みのリスト |
n_jobs | 並列実行の数。-1を設定した場合はすべてのプロセッサを利用 |
verbose | verboseのon/off |
後の使い方は、他の分類器と同じでfit()
→predict()
の順で呼び出します。
VotingClassifier(HARD)で予測
voting='hard'
で予測した結果です。3つの予測器の結果が同じなので、投票結果も同じです。
from sklearn.ensemble import VotingClassifier
voting_classifier = VotingClassifier(estimators=[('knn', knn), ('lgb', lgbm), ('svc', svc)], voting='hard')
voting_classifier.fit(X_train, y_train)
y_pred = voting_classifier.predict(X_test)
print(y_pred)
print(accuracy_score(y_test, y_pred))
[0 2 0 1 0 1 0 2 1 2 2 2 1 1 0 2 2 1 1 0 2 1 2 1 0 0 1 2 1 2 0 1 0 2 1 2 0 1]
0.9473684210526315
VotingClassifier(SOFT)で予測
voting='hard'
で予測した結果です。こちらも同じ結果になりました。
from sklearn.ensemble import VotingClassifier
voting_classifier = VotingClassifier(estimators=[('knn', knn), ('lgb', lgbm), ('svc', svc)], voting='soft')
voting_classifier.fit(X_train, y_train)
y_pred = voting_classifier.predict(X_test)
print(y_pred)
print(accuracy_score(y_test, y_pred))
[0 2 0 1 0 1 0 2 1 2 2 2 1 1 0 2 2 1 1 0 2 1 2 1 0 0 1 2 1 2 0 1 0 2 1 2 0 1] 0.9473684210526315
Votingclassfierの使い方②
irisデータの結果が面白くなかったので、別のデータセットでVotingClassifier
を試してみます。今回は、データセットをmake_classification
を使って生成します。
make_classificationを使ってデータセットを作成
scikit-learnのmake_classification
関数を使うと、「ランダムな n クラス分類問題」を生成することができます。ここでは、これを使ってデータセットを作ってVotingClassification
の動作を確認してみます。
使い方
使い方は簡単です。以下のように呼び出すだけで、データXとラベルyを作成してくれます。データポイントはガウス分布に従い生成されるため、実際に分類問題のサンプルとして利用することができます。
今回は、この擬似データを使って分類を試します。
X, y = make_classification(n_samples=1000, n_features=20, random_state=1234)
この関数の引数は以下になります(一部省略しています)。
make_classification
の代表的な引数引数 | 内容 |
---|---|
n_samples | サンプル数 |
n_features | 特徴量の数 |
n_informative | 目的変数のラベルと相関が強い特徴量の数 |
random_state | 乱数のシード |
VotingClassifier(2クラス分類)サンプル
make_classification
で2クラスのデータセットを作成して予測を行ってみます。
ここでは、XGBoost, LightGBM, KNNの3つを組み合わせています。
LightGBMとXGBoostはどちらもブースティングなので、アンサンブルするには向かないかもしれませんが、私が頻繁に利用しているのでサンプルに組み入れました。
from sklearn.ensemble import VotingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
import lightgbm as lgb
from xgboost import XGBClassifier
# lgbm = lgb.LGBMClassifier(verbose=-1)
# サンプルデータの生成
# n_samples サンプル数
# n_features 特徴量の数
# n_informative 目的変数のラベルと相関が強い特徴量の数
X, y = make_classification(n_samples=1000, n_features=20, random_state=42, n_informative=12)
# モデルの定義
model1 = XGBClassifier()
model2 = lgb.LGBMClassifier(verbose=-1)
model3 = KNeighborsClassifier()
# 投票分類器の構築
voting_classifier = VotingClassifier(estimators=[('xgb', model1), ('lgb', model2), ('knn', model3)], voting='hard')
# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# モデルの訓練
model1.fit(X_train, y_train)
model2.fit(X_train, y_train)
model3.fit(X_train, y_train)
voting_classifier.fit(X_train, y_train)
# 予測
y_pred1 = model1.predict(X_test)
y_pred2 = model2.predict(X_test)
y_pred3 = model3.predict(X_test)
y_pred = voting_classifier.predict(X_test)
# 精度の評価
accuracy1 = accuracy_score(y_test, y_pred1)
accuracy2 = accuracy_score(y_test, y_pred2)
accuracy3 = accuracy_score(y_test, y_pred3)
accuracy = accuracy_score(y_test, y_pred)
print(f'Accuracy1: {accuracy1}')
print(f'Accuracy2: {accuracy2}')
print(f'Accuracy3: {accuracy3}')
print(f'Voting Accuracy: {accuracy}')
結果は、それぞれの予測器単体より、投票した結果が良くなっています(期待通りの結果となりました)。
Accuracy1: 0.935
Accuracy2: 0.935
Accuracy3: 0.93
Voting Accuracy: 0.94
VotingClassifier(5クラス分類)サンプル
同様に5クラスのデータセットを作成して予測してみました。プログラム的にはほぼ同じです。
from sklearn.ensemble import VotingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
import lightgbm as lgb
from xgboost import XGBClassifier
# サンプルデータの生成
X, y = make_classification(n_samples=1000, n_features=20, random_state=42, n_classes=5, n_informative=12)
# モデルの定義
model1 = XGBClassifier()
model2 = lgb.LGBMClassifier(verbose=-1)
model3 = KNeighborsClassifier()
# 投票分類器の構築
voting_classifier = VotingClassifier(estimators=[('xgb', model1), ('lgb', model2), ('knn', model3)], voting='hard')
# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# モデルの訓練
model1.fit(X_train, y_train)
model2.fit(X_train, y_train)
model3.fit(X_train, y_train)
voting_classifier.fit(X_train, y_train)
# 予測
y_pred1 = model1.predict(X_test)
y_pred2 = model2.predict(X_test)
y_pred3 = model3.predict(X_test)
y_pred = voting_classifier.predict(X_test)
# 精度の評価
accuracy1 = accuracy_score(y_test, y_pred1)
accuracy2 = accuracy_score(y_test, y_pred2)
accuracy3 = accuracy_score(y_test, y_pred3)
accuracy = accuracy_score(y_test, y_pred)
print(f'Accuracy1: {accuracy1}')
print(f'Accuracy2: {accuracy2}')
print(f'Accuracy3: {accuracy3}')
print(f'Voting Accuracy: {accuracy}')
こちらも、個別の予測器よりも良い結果を得ることができました。
Accuracy1: 0.78
Accuracy2: 0.74
Accuracy3: 0.78
Voting Accuracy: 0.795
データセットによっては、単体の方がよい結果を出すこともありました。常にVotingの方が良い結果になるわけではないので、評価実験が必要です。
まとめ
scikit-learnのVotingClassifierについて解説しました。簡単にアンサンブルできるので、複数の予測器の結果を組み合わせるのに使ってみてはどうでしょうか。
特徴量エンジニアリングなどの手法については以下の記事を参考にしてください