LoginSignup
0
0

More than 1 year has passed since last update.

カテゴリ値を含むデータでのLightGBMを使った分析

Posted at

はじめに

時系列データを使った回帰問題にチャレンジしたことはあったのですが、分類問題はまだない。。。

というわけで、本稿では、二値分類問題にチャレンジします。
しかも、(カテゴリ値の処理方法も体験してみたかったので)カテゴリ値を含むデータで。

過去の投稿記事でもLightGBMを利用しているので、同じくLightGBMを利用して分析してゆきます。

本稿で紹介すること

  • データの収集および整形
  • 予測モデルの作成
  • 予測精度の確認
  • 特徴量重要度の確認(可視化)

本稿で紹介しないこと

  • 特徴量設計
  • ハイパーパラメータのチューニング

分析環境の各種情報

筆者は、いつもながらDockerコンテナとしてJupyterを起動しています。

  • Python 3.8.8
  • pip 21.3.1
  • pandas 1.4.0
  • numpy 1.22.1
  • scikit-learn 1.0.2
  • lightgbm 3.3.2

データの収集

こちらのサイトで公開されているCSVファイルを利用しました。

以下、上述サイトから抜粋、データに関する記載です。目的変数はIncome列、二値です。

An individual’s annual income results from various factors. Intuitively, it is influenced by the individual’s education level, age, gender, occupation, and etc.

This is a widely cited KNN dataset. I encountered it during my course, and I wish to share it here because it is a good starter example for data pre-processing and machine learning practices.

Fields
The dataset contains 16 columns
Target filed: Income
-- The income is divide into two classes: <=50K and >50K
Number of attributes: 14
-- These are the demographics and other features to describe a person

We can explore the possibility in predicting income level based on the individual’s personal information.

データの整形

最も時間を割いたのは、このStepでした。有志の記事をいくつか参考にしながら、何とか動かせたというのが正直な感想です。

結局、どうするべきか否かがいまいち分からず。。。途方に暮れました。

いろいろ調べる過程1で、Categorical Feature、one-hot encoding、Label Encodingというキーワードは出てきたのですが、、、

最終的な方針として、「手作業でカテゴリ変数(String)を0始まりの整数(Int)に変換」としました。

例えば、性別に対しての整形処理はこんな感じ。これをベースに説明変数の計8カラムを整形しました。

gender = ['Female', 'Male']
{v:i for i, v in enumerate(gender)}
#{'Female': 0, 'Male': 1}

df['gender'] = df['gender'].map({v:i for i, v in enumerate(gender)}).astype(int)

ちなみに、目的変数も考え方は同じ。正解('>50K'なら1、'<=50K'なら0)を割り当てました。

income_map = {'<=50K':0, '>50K':1}
df['income'] = df['income'].map(income_map).astype(int)

予測モデルの作成

データの分割ですが、時系列データではないためsklearn.model_selection.train_test_splitを使いました。

LightGBMを利用した大きな流れは、以下の記事に掲載のPythonコード2を参考にさせていただきました。

X = df.drop(['income'], axis=1)
y = df['income']

X_train, X_test , y_train, y_test  = train_test_split(X      , y      , test_size=0.2, shuffle=False)
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.2, shuffle=False)

# データセットを生成する
lgb_train = lgb.Dataset(X_train, y_train)
lgb_valid = lgb.Dataset(X_valid, y_valid, reference=lgb_train)

# LightGBM のハイパーパラメータ
params = {
    # 二値分類問題
    'objective': 'binary',
    # AUC の最大化を目指す
    'metric': 'auc',
    # Fatal の場合出力
    'verbosity': -1,
    # 乱数シード
    'seed': 31,
    # 学習率
    'learning_rate': 0.02,
}

# 上記のパラメータでモデルを学習する
model = lgb.train(params, lgb_train, valid_sets=lgb_valid,
                  verbose_eval=50,  # 50イテレーション毎に学習結果出力
                  num_boost_round=10000,  # 最大イテレーション回数指定
                  early_stopping_rounds=100
                 )

予測精度の確認

こちらのサイトで公開されているPythonコード3を参考にしました。

以下のPythonコードでROC曲線と混合行列を確認しました。
ほぼ原文ままで、変数名とAPI参照を直したのみです。

# テストデータを予測する
y_pred = model.predict(X_test, num_iteration=model.best_iteration)

# AUCを計算
fpr, tpr, thresholds = roc_curve(np.asarray(y_test), y_pred)
print("AUC", auc(fpr, tpr))

# ROC曲線をプロット
plt.plot(fpr, tpr, label='ROC curve (area = %.2f)' %auc(fpr, tpr))
plt.legend()
plt.title('ROC curve')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.grid(True)
plt.show()

# accuracy, precisionを計算
acc = accuracy_score(np.asarray(y_test), np.round(y_pred))
precision = precision_score(np.asarray(y_test), np.round(y_pred))
print("accuracy", acc)
print("precision", precision)

# 混同行列をプロット
y_pred = np.round(y_pred)
cm = confusion_matrix(np.asarray(y_test), np.where(y_pred < 0.5, 0, 1))
cmp = ConfusionMatrixDisplay(cm, display_labels=[0,1])
cmp.plot(cmap=plt.cm.Blues)
plt.show()

実行すると、こんな感じ。想像以上に精度が良かった。
image.png

特徴量重要度の確認(可視化)

特徴量重要度の確認ですが、lightgbm.plot_importanceを使いました。

以下のPythonコードで重要度の高い特徴量から可視化しました。

# 重要度としては「特徴量が分岐(ノード)の条件式で使用された回数」(=デフォルト)
lgb.plot_importance(model, figsize=(30, 15), max_num_features=30, importance_type='split')

image.png

# 重要度としては「特徴量がある分岐(ノード)において目的関数の改善に寄与した度合い」
lgb.plot_importance(model, figsize=(30, 15), max_num_features=30, importance_type='gain')

image.png

特に、2つ目の方式、gainの方をもう少し詳しく確認しました4
image.png

結果について少し考察

上位の特徴(説明変数5)についてです。
改めてデータの概説を確認すると、家族構成(婚姻関係の有無)が大まかに分かる様子です。つまり、家庭があるから収入が高いのではなく、収入が高いから家庭をもてたのだろうな、、、と思いました。

relationship: Wife, Own-child, Husband, Not-in-family, Other-relative, Unmarried.
marital-status: Married-civ-spouse, Divorced, Never-married, Separated, Widowed, Married-spouse-absent, Married-AF-spouse.

他だと、
投資で利益を得ている人が多いのかな、、、と思いました。(これも、収入があるから投資に回しているのでは。。。)
教育を受けた期間が長いほど、収入の高い職に就いた人が多いのかな、、、これは当然というか納得感がありますね。(学歴社会だから。。。)

Notebook(Pythonコード)

githubで公開しました。このタイミングですが、参考サイトの方々に感謝申し上げます。

まとめ

カテゴリ値を含むデータでのLightGBMを使った分析の例をご紹介。
別ケース、「手作業でカテゴリ変数(String)を0始まりの整数(Int)に変換」後に「DataFrameの当該列のデータ型をカテゴリ(category)に変更」した場合でも予測モデルを作成しましたが、優位な精度差はありませんでした。(LightGBMの動きとしてはどうなんでしょう。。。もう少し深追いしてみた方が良さそう。)

Pythonプログラムだけでデータ分析を試みていますが、正直手間ですね。だからこそ、AutoMLという製品・サービスに一定の需要があるのだろうな、、、と感じました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0