【本記事の内容】LightGBMで特徴量重要度の算出(DataFrameでの取得・プロット)
今回はLightGBMを使って特徴量重要度の算出、つまり「どの特徴量がモデルの精度に寄与しているか」を数値化する方法についてわかりやすく説明していきます。(特徴量重要度の算出はデータ分析をする上では重要な作業の1つになります。)
具体的には、LightGBMを使って特徴量重要度を算出して、それを「データフレームで取得」した後に「プロット」してグラフにするという流れについて説明していきます。(特徴量重要度の算出方法について調べてみると、意外と「データフレームで取得」していて、かつ「プロット」も行っているという記事が見つからなかったので書いてみます。)
※ とりあえず「特徴量重要度の算出方法(ソースコード)」を見たい人はこちら
※次に該当する方は以下の記事を参照してください。
・LightGBMの基本的な使い方(回帰分析編)を見たい方
・LightGBMとは何かを知りたい方
・LightGBMの2種類のAPI(文法)について知りたい方
・Pythonおよび周辺パッケージのインストール方法を知りたい方、Pythonおよび基本ライブラリについて勉強したい方

※次に該当する方は以下の記事を参照してください。
・LightGBMの基本的な使い方(2値分類編)を見たい方

※次に該当する方は以下の記事を参照してください。
・LightGBMの基本的な使い方(多クラス分類編)を見たい方

特徴量重要度とは ?
まず、特徴量重要度について簡単に説明します。特徴量重要度とは「各特徴量(各変数)がモデルの精度にどれだけ影響を与えたか?」を表します。
そのため、データ分析では始めに特徴量重要度を見ることで「注目するべき特徴量」を見定めることが多いです。(特にKaggle(データ分析コンテスト)では「初手LightGBM」=「まずLightGBMで特徴量重要度の算出などを行う」といった言葉もあるくらいです。)
具体例を出すと、「ある受験生AくんがB大学に合格するか否か」を予測するとします。
このとき特徴量として、「Aくんの名前、Aくんの誕生日、模試(直近)の点数、模試(直近)の偏差値、1週間(直近)の勉強時間、B大学の偏差値など」が与えられたとします。
この場合、「予測に関係のないAくんの名前や誕生日」といった情報は特徴量重要度が低くなり、「予測に関係のある模試の点数や偏差値、大学の偏差値」といった情報は特徴量重要度が高く出ます。
こうした情報は、「新たにモデルの精度に関係する特徴量」を作りだしたり、逆に「モデルにとって予測の妨げになる特徴量」を除いたりするといったことを行う際のヒントになります。
説明の準備
具体的な説明の前に使用データセットとAPIについて軽く触れておきます。
使用データセット・API
使用データセット
・回帰分析 : Bostonデータセット
・2値分類 : Breast Cancer データセット
・多クラス分類 : irisデータセット
使用API
・Python API
※データセットの詳しい説明については下記を参照してください。
Bostonデータセット, Breast Cancer データセット, irisデータセット
※API の詳しい説明については下記を参照してください
scikit-learn API と Python API
では、実際に回帰分析、2値分類、多クラス分類の3つの場合において、LightGBMを用いた特徴量重要度の算出を行っていきます。
特徴量重要度の算出 (回帰分析編)
準備
【ライブラリのインポート、関数の定義】
# ライブラリのインポート
import pandas as pd # 基本ライブラリ
import numpy as np # 基本ライブラリ
import matplotlib.pyplot as plt # グラフ描画用
import seaborn as sns; sns.set() # グラフ描画用
import warnings # 実行に関係ない警告を無視
warnings.filterwarnings('ignore')
import lightgbm as lgb #LightGBM
from sklearn import datasets
from sklearn.model_selection import train_test_split # データセット分割用
# データフレームを綺麗に出力する関数
import IPython
def display(*dfs, head=True):
for df in dfs:
IPython.display.display(df.head() if head else df)
【特徴量重要度の可視化用関数】
# 特徴量重要度を棒グラフでプロットする関数
def plot_feature_importance(df):
n_features = len(df) # 特徴量数(説明変数の個数)
df_plot = df.sort_values('importance') # df_importanceをプロット用に特徴量重要度を昇順ソート
f_importance_plot = df_plot['importance'].values # 特徴量重要度の取得
plt.barh(range(n_features), f_importance_plot, align='center')
cols_plot = df_plot['feature'].values # 特徴量の取得
plt.yticks(np.arange(n_features), cols_plot) # x軸,y軸の値の設定
plt.xlabel('Feature importance') # x軸のタイトル
plt.ylabel('Feature') # y軸のタイトル
【データの読み込み、トレーニングデータ・テストデータの分割】
# Boston データセットの読み込み
boston = datasets.load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names) # データフレームへの格納
# データの確認
print(df.shape) # データサイズの確認(データ数,特徴量数(変数の個数))
display(df) # df.head()に同じ(文中に入れるときはdisplay()を使う)
# 説明変数,目的変数
X = df.drop('CRIM',axis=1).values # 説明変数(CRIM以外の特徴量)
y = df['CRIM'].values # 目的変数(CRIM)
# トレーニングデータ,テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.20, random_state=2)
【モデルの学習】
# 学習に使用するデータを設定
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# LightGBM parameters
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'regression', # 目的 : 回帰
'metric': {'rmse'}, # 評価指標 : rsme(平均二乗誤差の平方根)
}
# モデルの学習
model = lgb.train(params,
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
)
特徴量重要度の算出
ここでは、特徴量重要度の算出方法2種類(「デフォルト」「gain」)を紹介します。「gain」の方がより正確に重要度を捉えることができるとされています。
【特徴量重要度の算出 ① (データフレームで取得)】- デフォルト
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('CRIM',axis=1).columns) # 特徴量名のリスト(目的変数CRIM以外)
f_importance = np.array(model.feature_importance()) # 特徴量重要度の算出
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
※正規化はこの場合、特徴量ごとの「全特徴量重要度の占める特徴量重要度の割合」に変換(合計が1になるような変換)することを指します。 また、各特徴量を最大値1, 最小値0になるようにデータを変換することも「正規化」といいます。
【出力】
【特徴量重要度の可視化 ①】
# 特徴量重要度の可視化
plot_feature_importance(df_importance)
【出力】
※表示する特徴量の数を変更したい場合はこちらを参照してください。
【特徴量重要度の算出 ② (データフレームで取得)】- gain
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('CRIM',axis=1).columns) # 特徴量名のリスト(目的変数CRIM以外)
# 特徴量重要度の算出方法 'gain'(推奨) : トレーニングデータの損失の減少量を評価
f_importance = np.array(model.feature_importance(importance_type='gain')) # 特徴量重要度の算出 //
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
【出力】
feature | importance | |
---|---|---|
7 | RAD | 0.390724 |
6 | DIS | 0.218026 |
3 | NOX | 0.185367 |
11 | LSTAT | 0.079764 |
5 | AGE | 0.055415 |
【特徴量重要度の可視化 ②】
# 特徴量重要度の可視化
plot_feature_importance(df_importance)
【出力】
特徴量重要度の算出 (2値分類編)
準備
【ライブラリのインポート、関数の定義】と【特徴量重要度の可視化用関数】は上記と同じため省略します。
【データの読み込み、トレーニングデータ・テストデータの分割】
# Breast Cancer データセットの読み込み
bc = datasets.load_breast_cancer()
df = pd.DataFrame(bc.data, columns=bc.feature_names) # データフレームへの格納
df['target'] = bc.target # 目的変数の追加
# データの確認
print(df.shape) # データサイズの確認(データ数,特徴量数)
display(df) # df.head()に同じ(文中に入れるときはdisplay()を使う)
# 説明変数,目的変数
X = df.drop('target',axis=1).values # 説明変数(target以外の特徴量)
y = df['target'].values # 目的変数(target)
# トレーニングデータ,テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.20, random_state=2)
【モデルの学習】
# 学習に使用するデータを設定
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# LightGBM parameters
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'binary', # 目的 : 2クラス分類
'metric': {'binary_error'}, # 評価指標 : 誤り率(= 1-正答率)
}
# モデルの学習
model = lgb.train(params,
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
)
特徴量重要度の算出
特徴量重要度の算出 ① (データフレームで取得)】- デフォルト
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('target',axis=1).columns) # 特徴量名のリスト(目的変数target以外)
f_importance = np.array(model.feature_importance()) # 特徴量重要度の算出
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
【出力】
feature | importance | |
---|---|---|
21 | worst texture | 0.103700 |
7 | mean concave points | 0.092489 |
1 | mean texture | 0.060538 |
13 | area error | 0.056054 |
23 | worst area | 0.055493 |
【特徴量重要度の可視化 ①】
# 特徴量重要度の可視化
plot_feature_importance(df_importance)
【出力】
【特徴量重要度の可視化 ①】- 表示する特徴量の個数を指定
# 特徴量重要度の可視化
n_display = 10 # 上位10個に制限
plot_feature_importance(df_importance.iloc[:n_display,:])
【出力】
【特徴量重要度の算出 ② (データフレームで取得)】- gain
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('target',axis=1).columns) # 特徴量名のリスト(目的変数target以外)
# 特徴量重要度の算出方法 'gain'(推奨) : トレーニングデータの損失の減少量を評価
f_importance = np.array(model.feature_importance(importance_type='gain')) # 特徴量重要度の算出 //
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
【出力】
feature | importance | |
---|---|---|
22 | worst perimeter | 0.481092 |
23 | worst area | 0.145065 |
7 | mean concave points | 0.107393 |
27 | worst concave points | 0.088717 |
21 | worst texture | 0.063438 |
【特徴量重要度の可視化 ②】
# 特徴量重要度の可視化
n_display = 10 # 上位10個に制限
plot_feature_importance(df_importance.iloc[:n_display,:])
【出力】
特徴量重要度の算出 (多クラス分類編)
準備
【ライブラリのインポート、関数の定義】と【特徴量重要度の可視化用関数】は上記と同じため省略します。
【データの読み込み、トレーニングデータ・テストデータの分割】
# iris データセットの読み込み
iris = datasets.load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names) # データフレームへの格納
df['target'] = iris.target # 目的変数の追加
# データの確認
print(df.shape) # データサイズの確認(データ数,特徴量数)
display(df) # df.head()に同じ(文中に入れるときはdisplay()を使う)
# 説明変数,目的変数
X = df.drop('target',axis=1).values # 説明変数(target以外の特徴量)
y = df['target'].values # 目的変数(target)
# トレーニングデータ,テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y,test_size=0.20, random_state=2)
【モデルの学習】
# 学習に使用するデータを設定
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)
# LightGBM parameters
params = {
'task': 'train',
'boosting_type': 'gbdt',
'objective': 'multiclass', # 目的 : 多クラス分類
'num_class': 3, # クラス数 : 3
'metric': {'multi_error'}, # 評価指標 : 誤り率(= 1-正答率)
# 他には'multi_logloss'など
}
# モデルの学習
model = lgb.train(params,
train_set=lgb_train, # トレーニングデータの指定
valid_sets=lgb_eval, # 検証データの指定
)
特徴量重要度の算出
特徴量重要度の算出 ① (データフレームで取得)】- デフォルト
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('target',axis=1).columns) # 特徴量名のリスト(目的変数target以外)
f_importance = np.array(model.feature_importance()) # 特徴量重要度の算出
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
【出力】
feature | importance | |
---|---|---|
2 | petal length (cm) | 0.356779 |
3 | petal width (cm) | 0.240571 |
1 | sepal width (cm) | 0.224261 |
0 | sepal length (cm) | 0.178389 |
【特徴量重要度の可視化 ①】
# 特徴量重要度の可視化
plot_feature_importance(df_importance)
【出力】
【特徴量重要度の算出 ② (データフレームで取得)】- gain
# 特徴量重要度の算出 (データフレームで取得)
cols = list(df.drop('target',axis=1).columns) # 特徴量名のリスト(目的変数target以外)
# 特徴量重要度の算出方法 'gain'(推奨) : トレーニングデータの損失の減少量を評価
f_importance = np.array(model.feature_importance(importance_type='gain')) # 特徴量重要度の算出 //
f_importance = f_importance / np.sum(f_importance) # 正規化(必要ない場合はコメントアウト)
df_importance = pd.DataFrame({'feature':cols, 'importance':f_importance})
df_importance = df_importance.sort_values('importance', ascending=False) # 降順ソート
display(df_importance)
【出力】
feature | importance | |
---|---|---|
2 | petal length (cm) | 0.706963 |
3 | petal width (cm) | 0.257667 |
1 | sepal width (cm) | 0.017873 |
0 | sepal length (cm) | 0.017498 |
【特徴量重要度の可視化 ②】
# 特徴量重要度の可視化
plot_feature_importance(df_importance)
【出力】
簡単な実行結果のまとめ
・特徴量重要度の算出方法(デフォルト or gain)によって特徴量重要度の値が大きく違う場合も見られた。
・gainの場合はよりはっきりと(シャープな感じで)した重要度の算出結果であったのに対して、デフォルトの場合はマイルドな感じな重要度の算出結果であった。
補足として、特徴量重要度は「各特徴量がそのモデルの精度に対して」算出されるものなので、モデルが異なれば当然違う結果となります。
したがって、アルゴリズムが違う場合(例えば、LightGBMとRandomForest)では重要度の値はもちろん、大小関係も違ってくる場合があります。
また、アルゴリズムが同じ場合でもパラメータ(コード中のparams)の中身が異なれば違う結果になります。
そして、「精度の良いモデルの特徴量重要度」の方が「そうでないモデルの特徴量重要度」より正確に特徴量を評価できていると考えられます。
おわりに
このblogでは、今後「数学 (主に統計学,微積分,線形代数)・AI機械学習(特にデータ解析系)」の「データサイエンス」に関するトピックを自分の学習の振り返りもかねて投稿していきます。
初学者にもわかりやすいようになるべく丁寧に書いていくのでよろしくお願いします。
何かあればコメントをお願いします。
参考文献
・特徴量重要度の算出(データフレームで取得) :
・特徴量重要度の可視化 : (以下はPythonのplot関係の記事でもかなりの良記事だと思います。)
コメント