ここでは、pythonやモデル手法を学び、モデル作成をこれから始めるような方に向けて、基本的な実装手順を記載します。

前提: モデリング内容

kaggle等で有名な「タイタニック」のデータを用いて、「生存した人(survived=1)」を予測するモデルを、「lightGBM」で作りたいと思います。

データはkaggle(https://www.kaggle.com/c/titanic/data)でも取得できますし、seabornライブラリより取得することもできます。
今回は下記の形で、seabornよりデータを取得して話を進めたいと思います。

import pandas as pd
import seaborn as sns
in_df = sns.load_dataset('titanic')
in_df

モデル作成

1.データ俯瞰

データ全体を俯瞰してみます。
具体的には、各列について「数値か文字列か?」(※1)や「欠損がどのくらい存在するか?」(※2)を確認します。
関数「describe(include=”all”)」を用いる事で簡単に俯瞰結果を確認できます。

(※1)文字列や名義尺度の場合はダミー変数化(one-hot)等にする
(※2)欠損が多い場合は、「モデルに使わない」等を検討する

desc_df = in_df.describe(include="all").T
desc_df["null_ratio"] = (len(in_df) - desc_df["count"]) / len(in_df)
desc_df

2.データ加工

モデリングでは基本的に数値データにする必要があります。
データ俯瞰結果を踏まえて、(今回は簡単に)下記の加工を行ってみます。

  • 文字列データ: ダミー変数化(get_dummies)
  • boolデータ: 0,1にする(True ⇒ 1)
  • 欠損が多い変数: 除外(今回は変数「deck」を除外)
  • (俯瞰結果ではないが、)aliveは目的変数(survived)と同じ意味らしいので除外
print(f"in_df len: {len(in_df)}")
print(f"in_df cols len: {len(in_df.columns)}")
mldata_df = in_df.copy()

# 変数除外:欠損が多い変数 + alive
NULL_RATIO_MAX = 0.7
remove_cols = ["alive"] + list(desc_df[desc_df["null_ratio"] > NULL_RATIO_MAX].index)
print(f"remove_cols: {remove_cols}")
mldata_df = mldata_df.drop(columns=remove_cols)

# 文字列データをダミー変数化
str_cols = [
    col for col in list(desc_df[desc_df["mean"].isnull()].index)
    if col in mldata_df.columns
]
print(f"str_cols: {str_cols}")
dummy_df = pd.get_dummies(mldata_df[str_cols], dummy_na=False, prefix_sep="__")
mldata_df = pd.concat(
    [
        mldata_df.drop(columns=str_cols),
        dummy_df,
    ],
    axis=1,
)
mldata_df.columns = [
    col.lower() for col in mldata_df.columns
]

# adult_male, alone	は bool値なので 0,1にする
for col in ["adult_male", "alone"]:
    mldata_df[col] = mldata_df[col].apply(lambda _bool: 1 if _bool else 0)
    
print(f"mldata_df len: {len(mldata_df)}")
print(f"mldata_df cols len: {len(mldata_df.columns)}")
mldata_df.head(3)

3.データ分割(train, valid)

汎化性能を持たせるために、モデルで学習させるデータ(train)と検証に使うデータ(valid)に分割します。

from sklearn.model_selection import train_test_split
TARGET_COL = "survived"
FEATURE_COLS = [col for col in mldata_df.columns if col != TARGET_COL]

# train + valid
X = mldata_df[FEATURE_COLS].values
y = mldata_df[TARGET_COL].values

X_train, X_valid, y_train, y_valid = train_test_split(
    X, y,
    test_size=0.3,
    random_state=1,
    shuffle=True,
    stratify=y,
)
print(f"X: {X.shape}")
print(f"y: {y.shape}")
print(f"X_train: {X_train.shape}")
print(f"y_train: {y_train.shape}")
print(f"X_valid: {X_valid.shape}")
print(f"y_valid: {y_valid.shape}")
print(f"target=1 ratio (train): {y_train.sum()/len(y_train):.2%}")
print(f"target=1 ratio (valid): {y_valid.sum()/len(y_valid):.2%}")

4.モデル学習

lightGBMを使ってモデル学習を行います。

from lightgbm import LGBMClassifier

model = LGBMClassifier()
model.fit(X_train, y_train)

5.モデル検証

検証用データ(valid)を用いて、モデル精度を検証します。
指標値を踏まえると下記の事が分かります。

・accuracyより、80%程のデータについて予測が正解
・precisionより、生き残った(survived=1)と予測したデータの内、正解だったものが77%程。
・recallより、実際生き残った(survived=1)データの内、予測が正解だったものが72%程

精度を上げるにあたって、ドメイン知識等を踏まえてデータ加工を見直したり、(optuna等で)モデルのハイパーパラメータをチューニングしたりします。

from sklearn.metrics import (
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
    confusion_matrix,
    roc_auc_score,
)

# predict
pred_valid = model.predict(X_valid)
score_valid = model.predict_proba(X_valid)[:, 1]
print(f"pred_valid: {pred_valid.shape}, data: {pred_valid[:5]}")
print(f"score_valid: {score_valid.shape}, data: {score_valid[:5]}")
print()

# 精度チェック
print(f"accuracy_score: {accuracy_score(y_valid, pred_valid):.2%}")
print(f"precision_score: {precision_score(y_valid, pred_valid):.2%}")
print(f"recall_score: {recall_score(y_valid, pred_valid):.2%}")
print(f"f1_score: {f1_score(y_valid, pred_valid):.2%}")
print(f"roc_auc: {roc_auc_score(y_valid, score_valid):.2%}")

sns.heatmap(confusion_matrix(y_valid, pred_valid), annot=True, cmap='Blues')

6.深堀り

色々な深堀り方がありますが、一例としては下記記事をご参照ください。

By clear

データエンジニア・機械学習・分析等を主とし、Webアプリ開発も行っているフリーランスです。