Spaces:
Running
Running
| """ | |
| Entry point for training Two-Tower model. | |
| Usage: | |
| python scripts/train.py --config configs/default.yaml | |
| """ | |
| import argparse | |
| import os | |
| import yaml | |
| import numpy as np | |
| import tensorflow as tf | |
| from src.models import build_student_tower, build_scholarship_tower | |
| from src.trainers.training_loop import run_training | |
| from src.utils.data_loader import load_data, load_precomputed_features | |
| def _build_log_dir(cfg: dict) -> str: | |
| """Build TensorBoard log directory path. | |
| Returns a clean path without trailing underscores. | |
| """ | |
| tb_cfg = cfg.get("tensorboard", {}) | |
| suffix = tb_cfg.get("suffix", "") | |
| if suffix: | |
| return os.path.join( | |
| cfg["output"]["log_dir"], | |
| f"tb_{cfg['experiment']['name']}_{suffix}", | |
| ) | |
| return os.path.join(cfg["output"]["log_dir"], f"tb_{cfg['experiment']['name']}") | |
| def parse_args(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--config", type=str, default="configs/default.yaml") | |
| return parser.parse_args() | |
| def main(): | |
| args = parse_args() | |
| with open(args.config) as f: | |
| cfg = yaml.safe_load(f) | |
| tf.random.set_seed(cfg["experiment"]["seed"]) | |
| np.random.seed(cfg["experiment"]["seed"]) | |
| # ββ Load data βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| train_df, val_df, _test_df = load_data(cfg) | |
| (stu_struct, sch_struct, stu_text_emb, sch_text_emb, | |
| stu_id_to_idx, sch_id_to_idx) = load_precomputed_features(cfg) | |
| sch_ids = list(sch_id_to_idx.keys()) | |
| feedback_weights = cfg["feedback_weights"] | |
| # ββ Build model βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| student_tower = build_student_tower(cfg["model"]["student_tower"]["input_dim"]) | |
| scholarship_tower = build_scholarship_tower(cfg["model"]["scholarship_tower"]["input_dim"]) | |
| # ββ Build full feature matrices for ALL students and scholarships βββββββββ | |
| stu_feat_all = np.concatenate([stu_struct, stu_text_emb], axis=1) # (N_stu, 506) | |
| sch_feat_all = np.concatenate([sch_struct, sch_text_emb], axis=1) # (N_sch, 509) | |
| def _build_sample_features(df): | |
| """Build feature arrays from a DataFrame of feedback rows.""" | |
| stu_id_map = {sid: i for i, sid in enumerate(stu_id_to_idx.keys())} | |
| sch_id_map = {sid: i for i, sid in enumerate(sch_id_to_idx.keys())} | |
| stu_indices = np.array( | |
| [stu_id_map[sid] for sid in df["student_id"]], dtype=np.int32 | |
| ) | |
| sch_indices = np.array( | |
| [sch_id_map[sid] for sid in df["scholarship_id"]], dtype=np.int32 | |
| ) | |
| weights = np.array( | |
| [feedback_weights[ft] for ft in df["feedback_type"]], dtype=np.float32 | |
| ) | |
| train_stu_feat = stu_feat_all[stu_indices] | |
| train_sch_feat = sch_feat_all[sch_indices] | |
| return train_stu_feat, train_sch_feat, weights | |
| train_stu_feat, train_sch_feat, train_weights = _build_sample_features(train_df) | |
| val_stu_feat, val_sch_feat, val_weights = _build_sample_features(val_df) | |
| optimizer = tf.keras.optimizers.Adam(learning_rate=cfg["training"]["learning_rate"]) | |
| epochs = cfg["training"]["epochs"] | |
| k = cfg["evaluation"]["k_values"][0] | |
| print(f"\nTraining {epochs} epochs...\n") | |
| # ββ Unified training loop with shared run_training() βββββββββββββββββββββ | |
| metrics = run_training( | |
| student_tower=student_tower, | |
| scholarship_tower=scholarship_tower, | |
| train_stu_feat=train_stu_feat, | |
| train_sch_feat=train_sch_feat, | |
| train_weights=train_weights, | |
| val_stu_feat=val_stu_feat, | |
| val_sch_feat=val_sch_feat, | |
| val_weights=val_weights, | |
| optimizer=optimizer, | |
| temperature=cfg["model"]["temperature"], | |
| epochs=epochs, | |
| checkpoint_dir=cfg["output"]["checkpoint_dir"], | |
| log_dir=_build_log_dir(cfg), | |
| tb_enabled=True, | |
| k=k, | |
| # ββ Validation metrics (full Recall/NDCG/MRR) βββββββββββββββββββ | |
| val_df=val_df, | |
| stu_struct=stu_struct, | |
| sch_struct=sch_struct, | |
| stu_text_emb=stu_text_emb, | |
| sch_text_emb=sch_text_emb, | |
| stu_id_to_idx=stu_id_to_idx, | |
| sch_ids=sch_ids, | |
| seed=cfg["experiment"]["seed"], | |
| ) | |
| print(f"Checkpoints: {cfg['output']['checkpoint_dir']}") | |
| if __name__ == "__main__": | |
| main() |