🌓

【12日目】外れ値処理をやってみる【2021アドベントカレンダー】

2021/12/12に公開

2021年1人アドベントカレンダー(機械学習)、12日目の記事になります。

https://qiita.com/advent-calendar/2021/solo_advent_calendar

テーマは 外れ値処理 になります。

scikit-learn の IsolationForest を使います。

https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html

教師なし学習による、決定木を使い外れ値処理を行うようです。

https://blog.amedama.jp/entry/2019/04/20/124220

Colab のコードはこちら Open In Colab

IsolationForest による外れ値処理

IsolationForest は 正常値を 1、異常値を -1 として返します。

また、欠損値があるとエラーになるので欠損値処理をしてやります。

途中処理は割愛していますので、詳細はコードをご確認ください。

model = IsolationForest(
                random_state=SEED
                )

train_anomaly = model.fit_predict(X_train_dropna)
print(np.mean(abs(model.score_samples(X_train_dropna))))
X_train_normal = X_train_dropna[train_anomaly == 1]
X_train_anomaly = X_train_dropna[train_anomaly == -1]

y_train_normal = y_train_dropna[train_anomaly == 1]
gkf = GroupKFold(n_splits=5)

groups = X_train_normal["Genre"]

params = {
          'task': 'train',              # タスクを訓練に設定
          'boosting_type': 'gbdt',      # GBDTを指定
          'objective': 'regression',    # 多クラス分類を指定
          'metric': 'rmse',  # 多クラス分類の損失(誤差)
          'learning_rate': 0.1,         # 学習率
          'seed': SEED                   # シード値
          }

best_params, history = {}, []

cv_result_rm_out = []

for fold, (train_index, test_index) in enumerate(gkf.split(X_train_normal, y_train_normal, groups)):
    X_train_gkf, X_test_gkf = X_train_normal.iloc[train_index], X_train_normal.iloc[test_index]
    y_train_gkf, y_test_gkf =  y_train_normal.iloc[train_index],  y_train_normal.iloc[test_index]

    # 学習、推論
    lgb_train = lgb.Dataset(X_train_gkf, y_train_gkf)
    lgb_test = lgb.Dataset(X_test_gkf, y_test_gkf, reference=lgb_train)

    lgb_results = {}                                    # 学習の履歴を入れる入物

    model = lgb.train(
                    params,                    # ハイパーパラメータをセット
                    lgb_train,              # 訓練データを訓練用にセット
                    valid_sets=[lgb_train, lgb_test], # 訓練データとテストデータをセット
                    valid_names=['Train', 'Test'],    # データセットの名前をそれぞれ設定
                    num_boost_round=100,              # 計算回数
                    early_stopping_rounds=50,         # アーリーストッピング設定
                    evals_result=lgb_results,
                    verbose_eval=-1,                  # ログを最後の1つだけ表示
                    )  
    
    best_params = model.params

   # 損失推移を表示
    loss_train = lgb_results['Train']['rmse']
    loss_test = lgb_results['Test']['rmse']   
    
    fig = plt.figure()
    
    plt.xlabel('Iteration')
    plt.ylabel('logloss')

    plt.title(f"fold:{fold}")
    plt.plot(loss_train, label='train loss')
    plt.plot(loss_test, label='test loss')
    
    plt.legend()
    plt.show()

    # 推論
    y_pred = model.predict(X_test_gkf, num_iteration=model.best_iteration)

    # 評価
    rmse = mean_squared_error(y_test_gkf, y_pred, squared=False)
    cv_result_rm_out.append(rmse)
内訳 RMSE
Optuna最適化 0.182
外れ値処理 + Optuna最適化 0.096

12日目は以上になります、最後までお読みいただきありがとうございました。

Discussion