Kaggle Titanicに初心者目線で挑戦してみた

# [Kaggle](https://www.kaggle.com/c/titanic/data)とは
Kaggleは、データサイエンスや機械学習に特化したオンラインプラットフォームです。Kaggleでは、データセット、カーネル（Jupyter NotebookやPythonスクリプトなど）、およびモデルを公開して共有することができます。また、Kaggleには、多くのコンペティションも開催されており、参加者は、特定のタスクや問題に対して最も優れたモデルを提供することで、報酬や賞品を獲得することができます。

kaggleの初心者向けのチュートリアルとして用意されている[Titanic](https://www.kaggle.com/c/titanic/data)を題材にします。

# 筆者の経験値と想定する読者
[Kaggleのチュートリアル第６版](https://www.amazon.co.jp/Kaggle%E3%81%AE%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E7%AC%AC%EF%BC%96%E7%89%88-%E3%82%AB%E3%83%AC%E3%83%BC%E3%81%A1%E3%82%83%E3%82%93-ebook/dp/B09P87T6L5)を読み、今回、kaggleに初挑戦です。よって、これからkaggleに挑戦しようと思っている方の参考になればと思います。

# 今回すること
まず、ゴールは`「Titanicコンペティションにsubmitする。」`です。kaggleの雰囲気を味わいます！
そして、この記事ではそれまでに至った道筋を正直に語ります。
また、今回利用したものは下記になります。
* LightGBM
* Optuna
* Xfeat

# 環境構築
kaggleに立ち向かう環境を整えました。
## 英語
日本語のコンペティションになかった言語の壁をkaggleでは感じます。それを取り払いました。翻訳できるものであれば何でも良いと思いますが、筆者は[DeepLのChrome拡張機能](https://www.deepl.com/ja/chrome-extension)を利用しました。
下図のような形で翻訳ツールが使用できるので便利だと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984456/a66bd297-c2aa-e2a2-29ee-9b707d6dbe10.png)

今回は使いませんでしたが、[Kaggle Notebook翻訳ヘルパー](https://chrome.google.com/webstore/detail/kaggle-notebook-translati/pbmmnjfppdiejknmcbibbhpgibhggboc)というKaggle Notebookを機械翻訳できるようにできる拡張機能もあり便利です。

## コーディングの環境
筆者は使い慣れた[Google colaboratory](https://colab.research.google.com/?hl=ja)を利用しました。
よって、kaggleからデータをダウンロードしてGoogle Driveに保存しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984456/fecefb28-7315-9a37-0746-facda2f3f295.png)



# さてKaggleへ！
と、意気込んだのですが、一からフルスクラッチでコーディングするのは大変で手間がかかります。まずは下調べしました。

## 先人の知恵にあやかる
開催されているkaggleコンペティションにおいてもコードがいくつかの公開されており利用することが可能です。本来ならば、kaggleで公開されているコードをベースにするべきですが、初回ということで、日本語の解説記事を利用しました。
まずは、特徴量作成に
* [初心者がKaggle Titanicで上位1.5%(0.83732)以内に入るアプローチ解説_１](https://qiita.com/shiroino11111/items/bc3889fa38ff32d46c13)
* [初心者がKaggle Titanicで上位1.5%(0.83732)以内に入るアプローチ解説_2](https://qiita.com/shiroino11111/items/fb6aa6b7dba2ddc3ce04)
* [初心者がKaggle Titanicで上位1.5%(0.83732)以内に入るアプローチ解説_3](https://qiita.com/shiroino11111/items/21bf1303587eeae0fc30)

を利用しました。[^1]
[github](https://github.com/shiroino11111/taitanic-0.83732-)にコードがあるので実装する際の参考にしてください。

他人のコードを読むのは大変勉強になりました。
[github](https://github.com/shiroino11111/taitanic-0.83732-)を利用し、特徴量を作成した後は下記のコードのように学習できるように準備しておきました。
```python
train_set = dataset_dummies[dataset_dummies['Survived'].notnull()]
test_set = dataset_dummies[dataset_dummies['Survived'].isnull()]
del test_set["Survived"]

# trainデータを変数と正解に分離
X = train_set.values[:, 1:] # Pclass以降の変数
y = train_set.values[:, 0] # 正解データ

# 訓練データと検証データに分割
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42,)
```


## LightGBMでモデルを作成する
いろいろなコンペで活躍するLightGBMを使ってみました。
LightGBMについては下記の解説がわかりやすかったです。よってLightGBMの詳細についての記述は割愛します。
* [LightGBM 徹底入門 – LightGBMの使い方や仕組み、XGBoostとの違いについて](https://www.codexa.net/lightgbm-beginner/)
* [LightGBMのパラメータチューニングまとめ](https://qiita.com/c60evaporator/items/351188110f328ff921b9)
* [機械学習 チュートリアルXGBoost](https://www.codexa.net/xgboost-tutorial/)
* [lightgbmで二値分類の一連の流れをしたメモ](https://qiita.com/d_desuyon/items/807e01311ad08570ee78)

Titanicは二値分類なので[lightgbmで二値分類の一連の流れをしたメモ](https://qiita.com/d_desuyon/items/807e01311ad08570ee78)を参考にLightGBMのモデルを作成します。

```python
import lightgbm as lgb

train_data = lgb.Dataset(X_train, label=y_train)
eval_data = lgb.Dataset(X_valid, label=y_valid, reference= train_data)

# LightGBM のハイパーパラメータ
params = {
    # 二値分類問題
    'objective': 'binary',
    # AUC の最大化を目指す
    'metric': 'binary_logloss',
    # Fatal の場合出力
    'verbosity': -1,
}

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


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

```

特徴量がしっかり考えられているおかげか、何もチューニングしていないLightGBMでもローカルの検証スコアは0.8156、kaggleのPublic Scoreはスコア0.7488がでました。



## Optunaでパラメータを最適化する
　[Optuna](https://optuna.org/)とは、[株式会社Preferred Networks](https://www.preferred.jp/ja/)が公開しているハイパーパラメーター自動最適化フレームワークのことです。
Optunaについては下記が参考になりました。
* [Optunaを使ったRandomforestの設定方法](https://qiita.com/motoyuki1963/items/9b5c1c8950728f949add)
* [optuna入門](https://qiita.com/studio_haneya/items/2dc3ba9d7cafa36ddffa)
* [Optunaを使ってみる](https://knknkn.hatenablog.com/entry/2021/06/14/160302)
* [【9日目】Optunaによるハイパーパラメーターチューニングをやってみる【2021アドベントカレンダー】](https://zenn.dev/megane_otoko/articles/2021ad_09_optuna_optimization)

```python
def objective(trial):
    params = {
        'objective': 'binary',
        'metric': 'binary_logloss',
        'boosting': 'gbdt',
        'max_depth':trial.suggest_int('max_depth', 1, 10),
        'min_data_in_leaf':trial.suggest_int('min_data_in_leaf', 1, 80),
        'learning_rate':trial.suggest_loguniform('learning_rate', 0.001, 0.1),
        'lambda_l1': trial.suggest_loguniform('lambda_l1', 1e-8, 10.0),
        'lambda_l2': trial.suggest_loguniform('lambda_l2', 1e-8, 10.0),
        'num_leaves': trial.suggest_int('num_leaves', 2, 512),
        'feature_fraction': trial.suggest_uniform('feature_fraction', 0.4, 1.0),
        'bagging_fraction': trial.suggest_uniform('bagging_fraction', 0.4, 1.0),
        'bagging_freq': trial.suggest_int('bagging_freq', 0, 10),
        'min_child_samples': trial.suggest_int('min_child_samples', 5, 100),
        'verbosity': -1,
        'random_state': 0,
    }

   
    lgb_train = lgb.Dataset(X_train, y_train)
    lgb_valid = lgb.Dataset(X_valid, y_valid, reference=lgb_train)
    gbm = lgb.train(params, lgb_train, valid_sets=lgb_valid,
                    verbose_eval=False, num_boost_round=1000, early_stopping_rounds=100)

    

    y_prob = gbm.predict(X_valid)
    y_pred = np.round(y_prob)
    return accuracy_score(y_valid, y_pred)

#optunaで学習
study = optuna.create_study(direction="maximize",sampler=optuna.samplers.TPESampler(seed=42))
study.optimize(objective, n_trials=100)
```
optunaで見つけた最適なパラメーター`study.best_params`は以下のようになりました。
```
{'max_depth': 4,
 'min_data_in_leaf': 73,
 'learning_rate': 0.09090766031606874,
 'lambda_l1': 1.0538223942143251e-07,
 'lambda_l2': 0.001093200015848534,
 'num_leaves': 467,
 'feature_fraction': 0.4113382550460581,
 'bagging_fraction': 0.9146079596370836,
 'bagging_freq': 8,
 'min_child_samples': 92}
```


LightGBMのパラメータをOptunaで最適化したときの一番良かったスコアは0.8715でしたが、kaggleのPublic Scoreは0.79816でした。


## カテゴリカルデータのエンコーディングの方法を変える
Target Encordingを使うことにしました。下記が参考になりました。Target Encordingの詳細は割愛します。

* [Target Encodingとは？3種類のターゲットエンコーディングとPython実装方法を徹底解説](https://www.codexa.net/target_encoding/)
* [カテゴリ変数のエンコーディングのまとめ](https://data-analysis-stats.jp/%E3%83%87%E3%83%BC%E3%82%BF%E5%89%8D%E5%87%A6%E7%90%86/%E3%82%AB%E3%83%86%E3%82%B4%E3%83%AA%E5%A4%89%E6%95%B0%E3%81%AE%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81/)
* [Target Encodingで精度向上させた例(Leave One Out)](https://qiita.com/FukuharaYohei/items/88cc562ab7800cbc01cb#4-%E8%A8%93%E7%B7%B4%E5%AE%9F%E6%96%BD)
* [Target Encoding - Beginner's Guide](https://www.kaggle.com/code/anuragbantu/target-encoding-beginner-s-guide)


LightGBMのパラメータをOptunaで最適化したときの一番良かったスコアは0.8492でしたが、kaggleのPublic Scoreは0.78708でした。
今回はOne-Hot-Encordingのほうが有効ということでしょうか。

## xfeatを使う
xfeatとは、[株式会社Preferred Networks](https://www.preferred.jp/ja/)が公開している特徴量エンジニアリングと特徴量探索のためのライブラリです。
自動ですごい特徴量が作れるというコト！？

Target Encordingで変換したデータに対して用いました。
### 特徴量の作成
参考：[特徴量エンジニアリングのライブラリ xfeat を使ってみて便利だったこと](https://acro-engineer.hatenablog.com/entry/2020/12/15/120000)
性別ごとに、年齢、Pclass の平均、最大を集計した特徴量を作成するということをしました。
```python
aggregated_df, aggregated_cols = aggregation(train_df,
                     group_key="Sex",
                     group_values=["Age", "Pclass"],
                     agg_methods=["mean", "max"],
                     )
```

[xfeat](https://github.com/pfnet-research/xfeat)のリポジトリ内の[examples/feature_selection_with_gbdt_and_optuna.py](https://github.com/pfnet-research/xfeat/blob/master/examples/feature_selection_with_gbdt_and_optuna.py)を参考にしました。

* ArithmeticCombinations
ArithmeticCombinations を使うと、複数のカラムを四則演算するといった特徴量が計算できます。
```python
def feature_engineering(df):
    cols = df.columns.tolist()
    encoder = Pipeline([
        #2つの数値データ
        ArithmeticCombinations(input_cols=cols,
                               drop_origin=False,
                               operator="+",
                               r=2,
                               output_suffix="_plus"),
        ArithmeticCombinations(input_cols=cols,
                               drop_origin=False,
                               operator="*",
                               r=2,
                               output_suffix="_mul"),
        ArithmeticCombinations(input_cols=cols,
                               drop_origin=False,
                               operator="-",
                               r=2,
                               output_suffix="_minus"),
        #3つの数値データの加算
        ArithmeticCombinations(input_cols=cols,
                               drop_origin=False,
                               operator="+",
                               r=3,
                               output_suffix="_plus"),
    ])
    return encoder.fit_transform(df)
```

特徴量を作成したことにより9個の特徴量が533個に増えました。

### 特徴量選択
参考：[Python: xfeat を使った特徴量エンジニアリング](https://blog.amedama.jp/entry/xfeat)
* DuplicatedFeatureEliminator：重複した特徴量を削除できる
* ConstantFeatureEliminator：定数になっている特徴量を削除できる
* SpearmanCorrelationEliminator：高い相関を持った特徴量を削除できる

```python
selector = Pipeline([
    xfeat.DuplicatedFeatureEliminator(),
    xfeat.ConstantFeatureEliminator(),
    xfeat.SpearmanCorrelationEliminator(threshold=0.8),
])
```
特徴量選択により20個に特徴量を減らすことができました。



LightGBMのパラメータをOptunaで最適化したときの一番良かったスコアは0.8492でしたが、kaggleのPublic Scoreは0.78708でした。自動で特徴量を作成して簡単に精度が上がるわけではなさそうですね。


# 結果
LightGBMで予測した結果を示します。One-Hotエンコーディング(25個)でLightGBM(Optunaでチューニング)が一番良いスコアでした。
|特徴量|モデル |kaggleのpublic Score |
|---|---|---|
|One-Hotエンコーディング(25個)|LightGBM(チューニングなし) |0.7488|
|One-Hotエンコーディング(25個)|LightGBM(Optunaでチューニング)|0.79816 |
|Target Encording(9個)|LightGBM(Optunaでチューニング)|0.78708|
|Target Encording(9個)に対してxfeatで特徴量作成、選択(20個)|LightGBM(Optunaでチューニング)|0.78708|



# まとめ
参考となるコードの特徴量の利用、`xfeat`を用いて特徴量の作成、選択、`optuna`による`LightGBM`のパラメータ最適化を行いKaggle Titanicにsubmmitしました。

# 今後の課題
* データ分析から特徴量の作成までを自力で実装する練習をする
* アンサンブルモデルを試す
* コード、パラメータのの管理方法について調べて試す

# 感想　
他の人のコードを読むのは大変勉強になりました。過去に開催されたコンペティションだと、多くの解説記事があるので気軽に挑戦できるので、初心者にはおすすめだと感じました。もちろん、開催されているコンペティションに飛び込むことで得られることも多いと思うので、何か参加したいという意欲もわきました。
コンペをしていると、特徴量が増えてきて何がなんだかわからなくなります。さらに、パラメータやモデルなど様々な角度からのトライアンドエラーがあるので、今後はコード管理（実験管理？）の効率的な方法を考えたいと思います。



[^1]: 忠実に再現してもkaggleのスコアは0.78947にしなりませんでした。年齢予測に利用されているrandomforestregressorのseedが固定されていないからだと推測しています。seedの設定でかなりスコアが変化しました（何か情報があれば教えてください）