Download train_model_v2.py from Qamro/predicting-smartphone-addiction-binary-classification: direct link, hf CLI and curl.
- Browser
- Download file 6.44 kB
-
https://huggingface.co/Qamro/predicting-smartphone-addiction-binary-classification/resolve/main/train_model_v2.py
- Command line
-
hf download hf://Qamro/predicting-smartphone-addiction-binary-classification/train_model_v2.py
-
curl -L -o train_model_v2.py https://huggingface.co/Qamro/predicting-smartphone-addiction-binary-classification/resolve/main/train_model_v2.py
6.44 kB
| import pandas as pd, numpy as np | |
| from sklearn.model_selection import StratifiedKFold | |
| from sklearn.metrics import roc_auc_score | |
| import lightgbm as lgb | |
| import xgboost as xgb | |
| from catboost import CatBoostClassifier | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| train = pd.read_csv('train.csv') | |
| test = pd.read_csv('test.csv') | |
| target = 'addicted_label' | |
| cat_cols = ['gender','stress_level','academic_work_impact'] | |
| base_num_cols = ['age','daily_screen_time_hours','social_media_hours','gaming_hours', | |
| 'work_study_hours','sleep_hours','notifications_per_day', | |
| 'app_opens_per_day','weekend_screen_time'] | |
| def feature_engineer(df): | |
| df = df.copy() | |
| # missing indicators (missingness itself may be predictive) | |
| for c in base_num_cols + cat_cols: | |
| df[f'{c}_missing'] = df[c].isna().astype(int) | |
| df['missing_count'] = df[[f'{c}_missing' for c in base_num_cols+cat_cols]].sum(axis=1) | |
| # ratios / interactions (safe against div by zero -> inf handled by replacing later) | |
| df['social_to_screen'] = df['social_media_hours'] / df['daily_screen_time_hours'] | |
| df['gaming_to_screen'] = df['gaming_hours'] / df['daily_screen_time_hours'] | |
| df['work_to_screen'] = df['work_study_hours'] / df['daily_screen_time_hours'] | |
| df['weekend_to_screen'] = df['weekend_screen_time'] / df['daily_screen_time_hours'] | |
| df['notif_per_open'] = df['notifications_per_day'] / df['app_opens_per_day'] | |
| df['screen_plus_work'] = df['daily_screen_time_hours'] + df['work_study_hours'] | |
| df['leisure_screen'] = df['social_media_hours'] + df['gaming_hours'] | |
| df['sleep_deficit'] = 24 - df['sleep_hours'] - df['daily_screen_time_hours'] - df['work_study_hours'] | |
| df['screen_per_age'] = df['daily_screen_time_hours'] / df['age'] | |
| df['weekday_weekend_diff'] = df['weekend_screen_time'] - df['daily_screen_time_hours'] | |
| ratio_cols = ['social_to_screen','gaming_to_screen','work_to_screen','weekend_to_screen', | |
| 'notif_per_open','screen_plus_work','leisure_screen','sleep_deficit', | |
| 'screen_per_age','weekday_weekend_diff'] | |
| df[ratio_cols] = df[ratio_cols].replace([np.inf,-np.inf], np.nan) | |
| return df, ratio_cols | |
| train, ratio_cols = feature_engineer(train) | |
| test, _ = feature_engineer(test) | |
| num_cols = base_num_cols + ratio_cols + [f'{c}_missing' for c in base_num_cols+cat_cols] + ['missing_count'] | |
| for c in cat_cols: | |
| train[c] = train[c].astype('category') | |
| test[c] = test[c].astype('category') | |
| cats = train[c].cat.categories | |
| test[c] = test[c].cat.set_categories(cats) | |
| feat_cols = num_cols + cat_cols | |
| X = train[feat_cols] | |
| y = train[target] | |
| X_test = test[feat_cols] | |
| # For xgboost/catboost we need cats encoded as codes (with -1 for missing / NaN handled natively by catboost) | |
| X_cb = X.copy() | |
| for c in cat_cols: | |
| X_cb[c] = X_cb[c].astype(object).where(X_cb[c].notna(), 'missing').astype(str) | |
| X_test_cb = X_test.copy() | |
| for c in cat_cols: | |
| X_test_cb[c] = X_test_cb[c].astype(object).where(X_test_cb[c].notna(), 'missing').astype(str) | |
| X_xgb = X.copy() | |
| for c in cat_cols: | |
| X_xgb[c] = X_xgb[c].cat.codes.replace(-1, np.nan) | |
| X_test_xgb = X_test.copy() | |
| for c in cat_cols: | |
| X_test_xgb[c] = X_test_xgb[c].cat.codes.replace(-1, np.nan) | |
| n_splits = 3 | |
| skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42) | |
| oof_lgb = np.zeros(len(X)); pred_lgb = np.zeros(len(X_test)) | |
| oof_xgb = np.zeros(len(X)); pred_xgb = np.zeros(len(X_test)) | |
| oof_cb = np.zeros(len(X)); pred_cb = np.zeros(len(X_test)) | |
| lgb_params = dict( | |
| objective='binary', metric='auc', learning_rate=0.05, num_leaves=63, | |
| max_depth=-1, min_child_samples=20, subsample=0.85, colsample_bytree=0.7, | |
| reg_alpha=0.2, reg_lambda=0.5, n_estimators=700, verbosity=-1, n_jobs=1 | |
| ) | |
| xgb_params = dict( | |
| objective='binary:logistic', eval_metric='auc', learning_rate=0.06, | |
| max_depth=6, min_child_weight=5, subsample=0.85, colsample_bytree=0.7, | |
| reg_alpha=0.2, reg_lambda=1.0, n_estimators=500, tree_method='hist', | |
| enable_categorical=False, n_jobs=1 | |
| ) | |
| for fold,(tr_idx,va_idx) in enumerate(skf.split(X,y)): | |
| y_tr,y_va = y.iloc[tr_idx], y.iloc[va_idx] | |
| # LightGBM | |
| X_tr,X_va = X.iloc[tr_idx], X.iloc[va_idx] | |
| m_lgb = lgb.LGBMClassifier(**lgb_params) | |
| m_lgb.fit(X_tr,y_tr, eval_set=[(X_va,y_va)], | |
| callbacks=[lgb.early_stopping(50, verbose=False)], | |
| categorical_feature=cat_cols) | |
| p_va = m_lgb.predict_proba(X_va)[:,1] | |
| oof_lgb[va_idx] = p_va | |
| pred_lgb += m_lgb.predict_proba(X_test)[:,1] / n_splits | |
| auc_lgb = roc_auc_score(y_va, p_va) | |
| # XGBoost | |
| X_tr_x, X_va_x = X_xgb.iloc[tr_idx], X_xgb.iloc[va_idx] | |
| m_xgb = xgb.XGBClassifier(**xgb_params, early_stopping_rounds=50) | |
| m_xgb.fit(X_tr_x, y_tr, eval_set=[(X_va_x, y_va)], verbose=False) | |
| p_va_x = m_xgb.predict_proba(X_va_x)[:,1] | |
| oof_xgb[va_idx] = p_va_x | |
| pred_xgb += m_xgb.predict_proba(X_test_xgb)[:,1] / n_splits | |
| auc_xgb = roc_auc_score(y_va, p_va_x) | |
| # CatBoost | |
| X_tr_c, X_va_c = X_cb.iloc[tr_idx], X_cb.iloc[va_idx] | |
| m_cb = CatBoostClassifier(iterations=500, learning_rate=0.08, depth=6, | |
| l2_leaf_reg=3, loss_function='Logloss', eval_metric='AUC', | |
| cat_features=cat_cols, verbose=False, thread_count=1, | |
| early_stopping_rounds=50, random_seed=42) | |
| m_cb.fit(X_tr_c, y_tr, eval_set=(X_va_c, y_va)) | |
| p_va_c = m_cb.predict_proba(X_va_c)[:,1] | |
| oof_cb[va_idx] = p_va_c | |
| pred_cb += m_cb.predict_proba(X_test_cb)[:,1] / n_splits | |
| auc_cb = roc_auc_score(y_va, p_va_c) | |
| print(f"Fold {fold}: LGB {auc_lgb:.5f} | XGB {auc_xgb:.5f} | CB {auc_cb:.5f}", flush=True) | |
| print("OOF LGB:", roc_auc_score(y, oof_lgb)) | |
| print("OOF XGB:", roc_auc_score(y, oof_xgb)) | |
| print("OOF CB :", roc_auc_score(y, oof_cb)) | |
| # Weighted blend - search simple weights | |
| best_auc, best_w = 0, None | |
| for w1 in np.arange(0,1.01,0.1): | |
| for w2 in np.arange(0,1.01-w1,0.1): | |
| w3 = 1-w1-w2 | |
| blend = w1*oof_lgb + w2*oof_xgb + w3*oof_cb | |
| auc = roc_auc_score(y, blend) | |
| if auc > best_auc: | |
| best_auc, best_w = auc, (w1,w2,w3) | |
| print("Best blend weights (lgb,xgb,cb):", best_w, "OOF AUC:", best_auc) | |
| w1,w2,w3 = best_w | |
| final_pred = w1*pred_lgb + w2*pred_xgb + w3*pred_cb | |
| sub = pd.DataFrame({'id': test['id'], 'addicted_label': final_pred}) | |
| sub.to_csv('submission_v2.csv', index=False) | |
| print(sub.head()) | |
| print("DONE") | |