0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2026年1月11日~30日の学習記録 ハイパーパラメータ、チューニングツール、分割したデータによるアルゴリズムの学習と検証、アンサンブル学習

0
Posted at

コロナに罹患してしまったため、今回は期間に対して内容がやや少なめです。

【1】.やったこと

 (1).ハイパーパラメータを手動で設定。
  ①.ハイパーパラメータ設定とは?
    ハイパーパラメータは、指定しない限りはデフォルトで実行されるが、自分で
   設定を変更することで、データに対してより最適な学習を行うことができる。
    予測のスコアが向上することがあるが、上り幅を考えると、最後の一押しに
   近い扱い。

  ②.コードを入れる位置と解説

    params = {
    の下に下記のハイパーパラメータを設定。
――――――――
'max_bin': 300,
'learning_rate': 0.05,
'num_leaves': 40,
――――――――
    max_binは各特徴量の最大の分割数
    learning_rateは学習率。小さめを指定すると丁寧に対応関係を学習する 
    num_leavesは1つの決定木における分岐の末端の最大数。過学習に陥る可能性
   が増加するが、表現力を高める
    (ただ、計算量も多くなるため時間がかかる。)

 (2).チューニングツールを使ったハイパーパラメータ調整(Optuna)
  ①.チューニングツールとは?
    最適なパラメータがどれか、試して結論を出してくれる。
    (正し、計算時間がすごく長くなることもあるので、そのあたりの兼ね合いは注意)
    今回はOptunaを使用。

  ②.コードを入れる位置と解説
    チューニングツールのコードを差し込む場所は、実際に予測するセルの前のセル
   で行う。
    (予測の前にチューニングツールが試行錯誤することで、最適なパラメータを探
   る、と考えると覚えやすい)

――――――――
import optuna
study = optuna.create_study(sampler=optuna.samplers.RandomSampler(seed=0))
study.optimize(objective, n_trials=40)
study.best_params
――――――――

  
 (3).分割したデータによるアルゴリズムの学習と検証
  ①.分割したデータによるアルゴリズムの学習と検証とは?
    学習用データを事前に学習用と検証用に分け、アルゴリズムに学習と検証を行
   わせること。
    今取り組んでいる初心者用Kaggleコンペ
   『Titanic - Machine Learning from Disaster』では何度でもsubmitし、予測の
   成功率を確認できるが、本来のKaggleコンペでは一日の提出回数が決まっている。
    そのため、自前のデータで検証することで、スコアが上がる見込みのないsubmit
   で、貴重な提出回数を消費することを防ぐことができる。

     (また、これは予測だが、そもそも現実の課題においては予測に対するフィード
    バックが明確な数値として返ってくることはほとんどないはず。
     そのため、事前に綿密な根拠を持って検証を行うことで、予測の精度がどの
    程度なのかを見積もる必要がある、のではないかと思う)

  ②.Public LBで良いスコアが出ても、一部に過学習した結果の可能性がある。
    submitした時に出てくるスコアは、Public LBと呼ばれる、テストデータ全体の
   一部に対しての予測結果のスコアである。
    そして、最終的な順位を決定するテストデータは別にあり、このデータに対する
   スコアはPrivate LBである。

    データが違う以上、たとえPublic LBで良いデータが出たとしても、それは
   PublicLBだけに過剰に適合している機械学習である可能性を捨てきれない。
    (Private LBがそもそも偏ったデータの可能性もある)

  ③.ホールドアウト検証法
    学習用データセットを、学習用と検証用に分割する。
    (実はLightGBMの時にはやっている)

  ④.CrossValidation(交差検証法)
   ・CrossValidetionとは?
     データを複数にK分割し、一つを検証用のデータに設定し、それ以外を学習用
    データとして、それをKパターン繰り返す学習をすることで、偏りをなくした学
    習と検証が行える。

    ※Kは任意の数

   ・コードの位置
     流れとしては

      K分割するコード
      ↓
      K分割を反映した機械学習のコード
      ↓
      交差検証のコード
      ↓
      交差検証結果の平均を予想とするコード
――――――――
from sklearn.model_selection import StratifiedKFold
import numpy as np
import lightgbm as lgb

y_preds = []
models = []
oof_train = np.zeros(len(X_train))

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)

categorical_features = ['Pclass', 'Sex']

params = {
"objective": "binary",
"metric": "binary_logloss",
"max_bin": 390,
"learning_rate": 0.05,
"num_leaves": 101,
}

for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train, y_train)):
X_tr = X_train.iloc[train_index]
X_val = X_train.iloc[valid_index]
y_tr = y_train.iloc[train_index] if hasattr(y_train, "iloc") else y_train[train_index]
y_val = y_train.iloc[valid_index] if hasattr(y_train, "iloc") else y_train[valid_index]

lgb_train = lgb.Dataset(X_tr, y_tr, categorical_feature=categorical_features)
lgb_eval  = lgb.Dataset(X_val, y_val, reference=lgb_train, categorical_feature=categorical_features)

model = lgb.train(
    params,
    lgb_train,
    valid_sets=[lgb_train, lgb_eval],
    valid_names=["train", "valid"],
    num_boost_round=1000,
    callbacks=[
        lgb.log_evaluation(period=10),
        lgb.early_stopping(stopping_rounds=10)
    ]
)

oof_train[valid_index] = model.predict(X_val, num_iteration=model.best_iteration)
y_pred = model.predict(X_test, num_iteration=model.best_iteration)

y_preds.append(y_pred)
models.append(model)

final_test_pred = np.mean(y_preds, axis=0)
――――――――

  ⑤.データセットの分割について
    データセットの分割は、データセットや課題設定の特徴を考える必要がある。
   なぜなら、分割されたときの学習用データがLBのPrivateと同じ構造になることが
   良いスコアのためには重要なため。

    ここまで使っていたKfoldはその点、それらを考慮することなくデータセットを
   分割しているので、偏りが生まれている可能性がある。

    そこで、学習用・検証用データセット内の「y==1」の割合を見てみる。
    (確認用コードはK分割したコードの下に書く)

――――――――
print(f'fold_id: {fold_id}')
print(f'y_tr y==1 rate: {sum(y_tr)/len(y_tr)}')
print(f'y_val y==1 rate: {sum(y_val)/len(y_val)}')
――――――――

    ※KFoldで分割した各1~5のデータの中に、y==1がどれくらいの割合か調べるこ
     とでLBに対してこの分割が適切かどうかを検討している。

    確認してみると、fold_idが4のものだけ、少し割合がおかしくなってる。
  
    LBのPrivateデータセットの割合は誰にも正確にわからないが、少なくとも学習
   用のデータセットと同様の割合だと仮定することはできる。
    その場合は、学習用のデータセットがどれも均等な割合を持った方が良いことに
   なる。
   (均等ではない場合、y==1に対する評価が分割したデータごとに異なってしまい、
    うまく学習できなくなる)

    このことから、同じ割合で分割する必要がある。
    割合を保ったままCrossValidationを実施するには、sklearnのStratifiedKFold
   が利用可能。

――――――――
from sklearn.model_selection import StratifiedKFold

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train,
y_train)):
X_tr = X_train.iloc[train_index, :]
X_val = X_train.iloc[valid_index, :]
y_tr = y_train.iloc[train_index]
y_val = y_train.iloc[valid_index]
――――――――

    またこれを用いて分割を行った後割合を確認すると、きちんとy==1の割合が揃う
   ように分割されていることがわかる。

    あとはこの分割したデータを使って予測すると、スコアが少しだけ変わる。

  ⑥.データセット分割における注意事項
  ・時系列を考慮しなければいけないときがある。
    コンペによっては、データの情報に「いつのデータか」という情報が混じる
   ことがある。
    そして、あるコンペでは、学習用データ→PublicLB→PrivateLBという形で
   データの分割が行われていたことがある。
    この時、CrossValidationを行うなら、学習用データと検証用データの間に
   PublicLBと割合として同じ程度の期間の空白が必要になる。

    このように、分割する時に、時系列的にもPrivateLBと近い形に分割できて
   いるかを考慮する必要がある時がある。らしい。

  ・データセット内にグループが存在しないか。
    データセットについて、例えば同じ人物についての別データ、のように、
   グループとしてくくれる場合がある。
    その時、同じグループのデータが、学習用と検証用に分割されてしまうと
   予測が不当に容易になってしまうことがある。(同じグループのデータは予測しや
   すいことが多いため)

    しかし、実際には、学習用データと無関係の物に対して予測したいわけだから
   それはデータ汚染に近い形になってしまう。
    そのため、学習用と検証のデータにまたぐような形でグループのデータが存在
   しないように分割されているかを気にする必要がある。

 (4).アンサンブル
  ①.アンサンブルとは?
    分析における最後の一押し。
    複数の機械学習モデルを組み合わせることで性能の高い予測を行う手法。
  
    今回は、3つの機械学習モデルによる多数決で予測を決めていくやり方。

  ②.やり方
   ・まず3つの予測結果を取得
     今回は、決定木で予測した時、LightGBMのホールドアウト法でやった時、
    LightGBMのCrossValidationで予測した時の3つのアルゴリズムでアンサンブル
    を行う。
     まずはこの3つのデータをinputする。

   ・複数の予測を合算する。
     まず、3つの予測結果を合算し0~3の値を取る1つのデータにする。
――――――――
sub = pd.read_csv('../input/titanic/gender_submission.csv')
sub['Survived'] = sub_lgbm_sk['Survived'] + sub_lgbm_ho['Survived'] + sub_rf['Survived']
――――――――

   ・閾値を設定する。(後、答えに書き込む)
     今回は、3つの予測の内2つが生存を指し示していたら生存とする。
     生存は1で、合算して2以上の場合は生存とすれば、そのようになる。
――――――――
sub['Survived'] = (sub['Survived'] >= 2).astype(int)
sub.to_csv('submission_lightgbm_ensemble.csv', index=False)
――――――――
     ※アンサンブルは、K分割などとは違って、アンサンブルを行うコードがある
      のではなく、その考え方を計算式などで実現していくことになる。

  以上で、とりあえずTitanicのスコア改善は一旦一区切りとする。

【2】.その他学んだこと

・「Ctrl + /」で、指定した行を一気にコメントアウトにできる。
  1行ずつ#を打ってると大変なので地味に大切。

・試行錯誤の途中、何度も、0.6220になってしまったが、これは予測結果が全部0として
 処理されてしまったから

  0.9も0.1も全部0として処理されてしまった(小数点以下切り捨て)
  なので、csv化の時に、ある閾値で1と0に分ける処理を入れないといけなくなる。

・変数=式は、式が先に評価され、その評価結果(True / False など)が変数に代入
 される

・ブール式(>とか==とか)は、そもそもTrue/Falseの判定になる。

・True/Falseの判定(bool)は、intのサブクラス。
  だから、.astypeでintへ変換すると、1と0になるようにboolは定義されている。

・テーブルデータのsubmitの時で、1か0かの時は、閾値の設定→ブール式で判定
 →astypeで1と0にTrue/Falseを変換

・kaggleでinputする時は、inputするファイルの名前を付けないとinputが進まない。
  名前つけ忘れがちなので注意。

・Loglossはどれくらい自信をもってあてたか

・アンサンブルは下記2点を満たすことで初めて意味を持つ
  予測の精度がその評価指標における「意味のある基準」を超えている
  モデル同士の予測誤差の相関が低い(同じデータで同時に外さない、見ている観点
 過程・構造が違う)

  要するに失敗の平均化。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?