第2回 金融データ活用チャレンジ(全投稿最終LB 14th 解法)

<style>
img {
    border: 1px black solid;
}
</style>

# この記事の内容
[第2回 金融データ活用チャレンジ 全投稿最終LB](https://signate.jp/competitions/1337#otherleaderboard) で14位の解法共有.
自分の直観に反して思いのほかスコアがよかった**不思議体験**を興味のある皆様に共有したいと思い記事を投稿する.

# 直観に反してスコアがよかった！って何？
## スコア
|public|private|
|:--|:--|
|0.6889483|0.6884709|

![image-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687201/5bb09714-f209-dc1b-e47b-e481bdc5dc6a.png)

## 不思議なモデル
不思議なモデルのスペックはCV評価値:0.66(1日5回制限のsubmitでなぜこれを投稿したのか記憶なし)
お恥ずかしながら[百聞は一見にしかず](https://gist.github.com/t-tsue/463f24ab64632e3a5ab451eaaf11fbc8)
* Cross Validation(RepeatedStratifiedKFold(n_repeats=1, **n_splits=10**, random_state=42))で10回，学習 and 評価を繰り返す
* 各回ごとに学習済モデルを保存する
* 繰り返し5回目のモデルの評価は最悪の結果
Y軸:MacroF1, X軸:モデルの予測値(predict_proba)の閾値(閾値以上をMIS_Status=1と判定)
0.66に限りなく近い結果
![image-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687201/dfb1b398-05be-1a7f-348e-dc77cb70a72e.png)

**このモデルが最終評価0.6884709になってしまった**

## 何でこのモデルでpublic, privateが0.688を超えたのか？
わかりません
40年以上前にちょっとかじった熱力学では相転移が起きると秩序が滅茶苦茶になるという記憶(e.g. Isingモデル高温展開)があり，そういえばpublicスコアをX軸，privateスコアをY軸としたときR2回帰は0.99となるがスコアをある値以上で集計するとR2回帰は0.3くらいに低下するのではないか？という噂も耳にしており熱力学的な知見が何かヒントを与えてくれるかもしれないと妄想

# 不思議なモデルの概要
## feature engineeringやモデル概要
[databricksでコンペにチャレンジ](https://qiita.com/t-tsue/items/8705816972c603e077f5)を参照

## cross validation
* 構造は外回り(n_splits=10), 内回り(n_splits=5)の2段階構造
  ![image-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687201/ad01fe87-f4dc-f6f4-429c-51f5635c42fd.png)

* 外回り
  ```
  TARGET_COLUMN='MIS_Status'
  SEED=42
  def train_outer(estimators,train):
    skf = RepeatedStratifiedKFold(n_repeats=1, n_splits=10, random_state=SEED)
    X = train.copy()
    y = X[TARGET_COLUMN]
    :::
    for i, (train_index, test_index) in enumerate(skf.split(X, y)):
        X_train = X.iloc[train_index].copy()
        y_train = y.iloc[train_index].copy()
        X_test = X.iloc[test_index].copy()
        y_test = y.iloc[test_index].copy()
        # 内回り(n_splits=5)で作成された5個の学習済モデルを取得
        score, raw, models = train_model(estimators, X_train, i)
        :::
        for j,[inner_iter, name, model] in enumerate(models):
            pred[j] = model.predict_proba(X_test.drop(columns=[TARGET_COLUMN]))[:,1]
        pred = pd.DataFrame(MinMaxScaler().fit_transform(pred))
        y_pred = pred[pred.columns].apply(np.mean,axis=1)
  ```  
* 内回り
  ```
    def train_model(estimators,train_prepared,iter):
        skf = RepeatedStratifiedKFold(n_repeats=1, n_splits=5, random_state=SEED)
        X = train_prepared.copy()
        y = X[TARGET_COLUMN]
        :::
        for i, (train_index, test_index) in enumerate(skf.split(X, y)):
            X_train = X.iloc[train_index].copy()
            y_train = y.iloc[train_index].copy()
            X_test = X.iloc[test_index].copy()
            y_test = y.iloc[test_index].copy()
            for name, mod in estimators:
                :::
                mod = mod.fit(X_train, y_train)
                y_pred = mod.predict_proba(X_test)[:,1]
            :::
            models.append([i,name,copy.deepcopy(mod)])
      :::
      return df_score,df_raw,models

  ```

## submission.csv
* predict_probaの閾値0.835, models[20:25]には外回り5回目のモデルを格納
  ```
    pred = pd.DataFrame()
    for i,[iter, name, model] in enumerate(models[20:25]):
        pred[i] = model.predict_proba(test_prepared)[:,1]
    pred = pd.DataFrame(MinMaxScaler().fit_transform(pred))
    pred_final = (pred[pred.columns].apply(np.mean,axis=1) > 0.835).astype(int)
    pred_final.value_counts()/len(pred_final)
  ```
   ![image-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3687201/5825af5d-5c11-d079-4c7b-4c103456ee82.png)

## ソース
コメント誤りや読みにくいソースで申し訳ない
[ソース](https://gist.github.com/t-tsue/463f24ab64632e3a5ab451eaaf11fbc8)