学習する(14:学習コマンド)

学習

キャパの異なる2つの自己注意モデルを“学習的に”スタックして、レース予測を実戦仕様で学習・保存・再利用する起動スクリプト

今回は、レース予測モデルの学習/推論パイプラインの起点です。
このコードは、2種類のハイブリッドモデル(LSTM+埋め込み+SetTransformer)を構築してスタッキングし、go に応じて 学習→保存 もしくは 再構築→重み読込→推論モード に切り替えます。

print("✅ train__model開始")
input_dim = X_train_seq.shape[2] 

class_weights_dict = compute_class_weights(y_train)
class_weights_tensor = torch.tensor(
    [class_weights_dict.get(i, 1.0) for i in range(6)],
    dtype=torch.float32
).to("cuda" if torch.cuda.is_available() else "cpu")


model_self_attn = HybridRaceModelWithEmbedding(
    input_dim=input_dim,
    sequence_length=sequence_length,
    lstm_hidden=64,
    embedding_input_dims=embedding_input_dims,
    embedding_output_dims=embedding_output_dims
)
model_self_attn.set_transformer = SetTransformer(
    input_dim=64,
    hidden_dim=16,
    num_heads=1,
    num_blocks=1,
    dropout_p=0.1
)

model_set_transformer = HybridRaceModelWithEmbedding(
    input_dim=input_dim,
    sequence_length=sequence_length,
    lstm_hidden=64,
    embedding_input_dims=embedding_input_dims,
    embedding_output_dims=embedding_output_dims
)
model_set_transformer.set_transformer = SetTransformer(
    input_dim=64,
    hidden_dim=32,
    num_heads=2,
    num_blocks=2,
    dropout_p=0.4
)

# Stacking Ensemble モデルを作成
emodel = StackingEnsembleRaceModel(
    model_self_attn=model_self_attn,
    model_set_transformer=model_set_transformer,
    hidden_dim=32
).to(device)

# ✅ 学習実行
if go:
    print("🚀 Training started...")
    model = train_racewise_regression_model(
        input_dim=input_dim,
        X_sequences=X_train_seq,
        X_cat_dict=X_cat_train,
        y_ranks=y_train,
        race_ids=race_ids_train,
        history_lengths=history_lengths_train,
        val_sequences=X_val_seq,
        val_cat_dict=X_cat_val,
        val_ranks=y_val,
        val_race_ids=race_ids_val,
        val_history_lengths=history_lengths_val,
        sequence_length=sequence_length,
        batch_size=12,
        collate_fn=grouped_race_collate_fn,
        epochs=10,
        lstm_hidden=64,
        class_weights=class_weights_tensor,
        horse_ids=horse_ids_train,
        val_horse_ids=horse_ids_val,
        model=emodel, 
        device=device
    )
    torch.save(model.state_dict(), "transformer_model.pth")
    print("✅ transformer_model 保存済み")
else:
    model_self_attn = HybridRaceModelWithEmbedding(
        input_dim=input_dim,
        sequence_length=sequence_length,
        lstm_hidden=64,
        embedding_input_dims=embedding_input_dims,
        embedding_output_dims=embedding_output_dims
    )
    model_self_attn.set_transformer = SetTransformer(
        input_dim=64,
        hidden_dim=16,
        num_heads=1,
        num_blocks=1,
        dropout_p=0.1
    )

    model_set_transformer = HybridRaceModelWithEmbedding(
        input_dim=input_dim,
        sequence_length=sequence_length,
        lstm_hidden=64,
        embedding_input_dims=embedding_input_dims,
        embedding_output_dims=embedding_output_dims
    )
    model_set_transformer.set_transformer = SetTransformer(
        input_dim=64,
        hidden_dim=32,
        num_heads=2,
        num_blocks=2,
        dropout_p=0.4
    )

    model = StackingEnsembleRaceModel(
        model_self_attn=model_self_attn,
        model_set_transformer=model_set_transformer,
        hidden_dim=32
    ).to(device)

    state_dict = torch.load("transformer_model.pth", map_location=device)
    model.load_state_dict(state_dict)
    model.to(device)
    model.eval()
    print("⏩ Skipped training (go=False). Using preloaded model instead.")

1) 入力次元とクラス重みの準備

input_dim = X_train_seq.shape[2]
class_weights_dict = compute_class_weights(y_train)
class_weights_tensor = torch.tensor(
    [class_weights_dict.get(i, 1.0) for i in range(6)],
    dtype=torch.float32
).to(device)
  • input_dim:数値特徴テンソル X_train_seq のチャネル数(学習前処理で「数値+TAIL(類似度・履歴正規化・pad旗)」を結合済み)。
  • compute_class_weights:学習データの ランク頻度の逆数 を計算(希少クラスを重く)。ここでは 6クラス(例:1〜5位+6=その他)を想定し、無いクラスは既定1.0。
  • class_weights_tensorsoft_top1_loss 等に渡され、不均衡補正に使われます。

2) 2系統のハイブリッドモデルを構築(多様性を作る)

model_self_attn = HybridRaceModelWithEmbedding(..., lstm_hidden=64, ...)
model_self_attn.set_transformer = SetTransformer(input_dim=64, hidden_dim=16, num_heads=1, num_blocks=1, dropout_p=0.1)

model_set_transformer = HybridRaceModelWithEmbedding(..., lstm_hidden=64, ...)
model_set_transformer.set_transformer = SetTransformer(input_dim=64, hidden_dim=32, num_heads=2, num_blocks=2, dropout_p=0.4)
  • 共通:数値系列+カテゴリ埋め込みLSTM(64)SetTransformer でレース内の相互作用を表現。
  • 相違点:SetTransformer のキャパ・正則化を変えて多様性を確保。
    • self_attn 系:軽量(H=16, heads=1, blocks=1, dropout 0.1)
    • set_transformer 系:重厚(H=32, heads=2, blocks=2, dropout 0.4)
  • model_xxx.set_transformer = ...内部の集合モジュールを差し替え。同じ LSTM 出力(次元=64)を前提にするため input_dim=64 を指定。

3) スタッキング・アンサンブルの作成

emodel = StackingEnsembleRaceModel(
    model_self_attn=model_self_attn,
    model_set_transformer=model_set_transformer,
    hidden_dim=32
).to(device)
  • 2モデルの 出力スコア([B,S]) を受け取り、
    • 既定では 小さな MLP ヘッドLinear→ReLU→Dropout→Linear)で学習的に融合します(平均ではない)。
  • 意図:異なる帰納バイアス(軽量自己注意 vs. 重装自己注意)の長所を統合し、汎化性能を底上げ

4) 学習フロー(go=True の場合)

model = train_racewise_regression_model(
    input_dim=input_dim,
    X_sequences=X_train_seq, X_cat_dict=X_cat_train,
    y_ranks=y_train, race_ids=race_ids_train, history_lengths=history_lengths_train,
    val_sequences=X_val_seq, val_cat_dict=X_cat_val, val_ranks=y_val, val_race_ids=race_ids_val, val_history_lengths=history_lengths_val,
    sequence_length=sequence_length, batch_size=12, epochs=10, lstm_hidden=64,
    collate_fn=grouped_race_collate_fn, class_weights=class_weights_tensor,
    horse_ids=horse_ids_train, val_horse_ids=horse_ids_val,
    model=emodel, device=device
)
torch.save(model.state_dict(), "transformer_model.pth")
  • 学習関数には、前段で作った スタッキングモデル emodel を渡します。
  • 学習内部(先に説明した関数):
    • LSTM が各馬の履歴(T)を要約 → SetTransformer がレース内(S)の相互作用を学習。
    • 損失は ペアワイズ順位 + Top-1 + nDCG@3 の合成(履歴に基づく重みの導入あり)。
    • ReduceLROnPlateau(検証 nDCG@3 を監視)と EarlyStopping で過学習を抑制。
  • 学習後は 重み(state_dict)を保存。以後の推論で再利用可。

5) 推論フロー(go=False の場合)

# 同一構造で再構築
model = StackingEnsembleRaceModel(...).to(device)
state_dict = torch.load("transformer_model.pth", map_location=device)
model.load_state_dict(state_dict)
model.eval()
  • 学習時と同一構造(LSTM次元・埋め込み入出力次元・SetTransformer 設定)でモデルを再構築し、保存済み重みを読込
  • eval() にして推論モード(Dropout停止、LayerNorm推論挙動)。
  • 注意:**入出力の前処理(列順・スケーリング・カテゴリ写像)**が学習時と一致していることが前提です(*info.json, *maps.json, scale_stats.json 等)。

6) 学習ロジックの意図(要点)

  • 多様性×学習的結合:2系統の自己注意モデルを 学習ヘッドで統合し、分散低減頑健性を獲得。
  • 不均衡耐性class_weights による Top-1 の重み補正で、希少な勝者パターンの学習を助ける。
  • 実運用指標整合nDCG@3 の組み込みで 上位重視の価値関数に合わせて最適化。
  • 再現性:前処理の列順・スケール・カテゴリ辞書を保存/共有し、学習・推論の一貫性を担保。

代表的な変数の意味

  • input_dim:数値入力チャネル数(LSTMへ入る前のD)。
  • class_weights_tensor:Top-1 損失で使うクラス重み(サイズ=6想定)。
  • model_self_attn / model_set_transformer:構成違いの 基礎モデル
  • emodel:2モデルを統合する スタッキング・アンサンブル
  • goTrue=学習→保存False=再構築→読込→推論
  • sequence_length, batch_size, epochs, lstm_hidden:主要ハイパーパラメータ。

コメント

タイトルとURLをコピーしました