Spaces:
Running
Running
| """ | |
| Data loading dan tf.data.Dataset builder untuk Two-Tower training. | |
| """ | |
| import numpy as np | |
| import pandas as pd | |
| import tensorflow as tf | |
| from src.utils.feature_engineering import ( | |
| encode_scholarship, | |
| encode_student, | |
| normalize_json_columns, | |
| ) | |
| AUTOTUNE = tf.data.AUTOTUNE | |
| SEED = 42 | |
| # ββ Load raw data βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def load_data(cfg: dict): | |
| """Baca feedback.csv, sort by timestamp, split 70/15/15. | |
| Returns: | |
| train_df, val_df, test_df: DataFrame dengan kolom | |
| [student_id, scholarship_id, feedback_type, timestamp] | |
| """ | |
| raw_path = cfg["data"]["raw_path"] | |
| feedback_df = pd.read_csv(f"{raw_path}/feedback.csv") | |
| feedback_df = feedback_df.sort_values("timestamp").reset_index(drop=True) | |
| n = len(feedback_df) | |
| n_train = int(cfg["data"]["train_split"] * n) | |
| n_val = int(cfg["data"]["val_split"] * n) | |
| train_df = feedback_df.iloc[:n_train] | |
| val_df = feedback_df.iloc[n_train : n_train + n_val] | |
| test_df = feedback_df.iloc[n_train + n_val :] | |
| print(f"Data split β train:{len(train_df):,} val:{len(val_df):,} test:{len(test_df):,}") | |
| return train_df, val_df, test_df | |
| # ββ Pre-compute structured features ββββββββββββββββββββββββββββββββββββββββββ | |
| def load_precomputed_features(cfg: dict): | |
| """Encode semua student + scholarship structured features dan load text embeddings. | |
| Returns: | |
| stu_struct : np.ndarray (N_stu, 122) | |
| sch_struct : np.ndarray (N_sch, 125) | |
| stu_text_emb: np.ndarray (N_stu, 384) | |
| sch_text_emb: np.ndarray (N_sch, 384) | |
| stu_id_to_idx: dict {student_id: row_index} | |
| sch_id_to_idx: dict {scholarship_id: row_index} | |
| """ | |
| raw_path = cfg["data"]["raw_path"] | |
| emb_path = cfg["data"]["text_embeddings_path"] | |
| students_df = pd.read_csv(f"{raw_path}/students.csv") | |
| scholarships_df = pd.read_csv(f"{raw_path}/scholarships.csv") | |
| # Normalize JSON columns (language_proficiency, olympiad_subjects, target_countries) | |
| students_df = normalize_json_columns(students_df, ["language_proficiency", "olympiad_subjects", "target_countries"]) | |
| # Normalize scholarship JSON columns | |
| scholarships_df = normalize_json_columns( | |
| scholarships_df, | |
| ["eligible_nationalities", "eligible_degree_levels", | |
| "eligible_high_school_tracks", "eligible_fields", | |
| "language_requirements", "selection_criteria"], | |
| ) | |
| # Encode structured features | |
| print("Encoding structured features...") | |
| stu_struct = np.array( | |
| [encode_student(r) for _, r in students_df.iterrows()], dtype=np.float32 | |
| ) | |
| sch_struct = np.array( | |
| [encode_scholarship(r) for _, r in scholarships_df.iterrows()], dtype=np.float32 | |
| ) | |
| # Load pre-computed text embeddings | |
| stu_text_emb = np.load(f"{emb_path}/students.npy").astype(np.float32) | |
| sch_text_emb = np.load(f"{emb_path}/scholarships.npy").astype(np.float32) | |
| stu_id_to_idx = {sid: i for i, sid in enumerate(students_df["student_id"])} | |
| sch_id_to_idx = {sid: i for i, sid in enumerate(scholarships_df["scholarship_id"])} | |
| print(f" stu_struct : {stu_struct.shape} stu_text_emb : {stu_text_emb.shape}") | |
| print(f" sch_struct : {sch_struct.shape} sch_text_emb : {sch_text_emb.shape}") | |
| return stu_struct, sch_struct, stu_text_emb, sch_text_emb, stu_id_to_idx, sch_id_to_idx | |
| # ββ Build tf.data.Dataset βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def make_dataset( | |
| df: pd.DataFrame, | |
| stu_struct: np.ndarray, | |
| sch_struct: np.ndarray, | |
| stu_text_emb: np.ndarray, | |
| sch_text_emb: np.ndarray, | |
| stu_id_to_idx: dict, | |
| sch_id_to_idx: dict, | |
| feedback_weights: dict, | |
| batch_size: int = 256, | |
| shuffle: bool = False, | |
| ) -> tf.data.Dataset: | |
| """Build tf.data.Dataset yielding (stu_feat, sch_feat, weights) triples. | |
| stu_feat shape: (batch, 506) β concat(struct, text_emb) | |
| sch_feat shape: (batch, 509) | |
| weights shape: (batch,) β feedback type weights | |
| """ | |
| stu_indices = np.array([stu_id_to_idx[sid] for sid in df["student_id"]], dtype=np.int32) | |
| sch_indices = np.array([sch_id_to_idx[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) | |
| stu_feat = np.concatenate([stu_struct[stu_indices], stu_text_emb[stu_indices]], axis=1) | |
| sch_feat = np.concatenate([sch_struct[sch_indices], sch_text_emb[sch_indices]], axis=1) | |
| ds = tf.data.Dataset.from_tensor_slices((stu_feat, sch_feat, weights)) | |
| if shuffle: | |
| ds = ds.shuffle(buffer_size=10_000, seed=SEED) | |
| ds = ds.batch(batch_size, drop_remainder=True) | |
| ds = ds.prefetch(AUTOTUNE) | |
| return ds | |