CoolFace
Apppublic

utanvir/telco_churn

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
model_trainer.py196 linesDownload Raw Back to components
1# src/components/model_trainer.py2 3import os4import sys5from dataclasses import dataclass6import numpy as np7 8from xgboost import XGBClassifier9from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier10from sklearn.linear_model import LogisticRegression11from sklearn.neighbors import KNeighborsClassifier12from sklearn.tree import DecisionTreeClassifier13from sklearn.svm import SVC14from sklearn.naive_bayes import GaussianNB15from sklearn.neural_network import MLPClassifier16 17from src.exception import CustomException18from src.logger import logging19from src.utils import save_object, evaluate_models  # your utils, course-style API20 21@dataclass22class ModelTrainerConfig:23    trained_model_file_path = os.path.join("artifact", "model.pkl")24 25class ModelTrainer:26    def __init__(self):27        self.model_trainer_config = ModelTrainerConfig()28 29    def initiate_model_trainer(self, train_array, test_array):30        """31        Course-style: separate `models` and `params`, hand both to evaluate_models(..., param=params).32        Hyperparameter tuning depth is intentionally small; you said you'll do full HPO later.33        """34        try:35            logging.info("Split training and test input data")36            X_train, y_train, X_test, y_test = (37                train_array[:, :-1],38                train_array[:, -1],39                test_array[:, :-1],40                test_array[:, -1],41            )42 43            # ---------------------------44            # Models (sane defaults)45            # ---------------------------46            models = {47                "LogisticRegression": LogisticRegression(48                    solver="lbfgs",49                    penalty="l2",50                    class_weight="balanced",51                    max_iter=2000,52                    n_jobs=-1,53                ),54                "RandomForestClassifier": RandomForestClassifier(55                    n_estimators=200,56                    class_weight="balanced_subsample",57                    n_jobs=-1,58                    random_state=42,59                ),60                "GradientBoostingClassifier": GradientBoostingClassifier(61                    random_state=4262                ),63                "AdaBoostClassifier": AdaBoostClassifier(64                    random_state=4265                ),66                "SVC": SVC(67                    kernel="rbf",68                    C=1.0,69                    gamma="scale",70                    probability=False,   # AUC via decision_function in your evaluator71                    random_state=42,72                ),73                "KNN": KNeighborsClassifier(74                    n_neighbors=5,75                    n_jobs=-176                ),77                "DecisionTreeClassifier": DecisionTreeClassifier(78                    class_weight="balanced",79                    random_state=4280                ),81                "GaussianNB": GaussianNB(),82                "MLPClassifier": MLPClassifier(83                    hidden_layer_sizes=(128,),84                    max_iter=500,85                    random_state=4286                ),87                "XGBClassifier": XGBClassifier(88                    eval_metric="logloss",89                    tree_method="hist",90                    n_estimators=300,91                    random_state=4292                ),93            }94 95            # ---------------------------96            # Param grids 97            # Keys must match the model names above.98            # ---------------------------99            params = {100                "LogisticRegression": {101                    "C": [0.1, 1.0, 10.0]102                },103                "RandomForestClassifier": {104                    "n_estimators": [100, 200, 400],105                    "max_depth": [None, 10, 20]106                },107                "GradientBoostingClassifier": {108                    "n_estimators": [100, 200],109                    "learning_rate": [0.05, 0.1],110                    "max_depth": [2, 3]111                },112                "AdaBoostClassifier": {113                    "n_estimators": [100, 300],114                    "learning_rate": [0.1, 1.0]115                },116                "SVC": {117                    "C": [0.5, 1.0, 2.0],118                    "gamma": ["scale", "auto"],119                    "kernel": ["rbf"]120                },121                "KNN": {122                    "n_neighbors": [3, 5, 7],123                    "weights": ["uniform", "distance"]124                },125                "DecisionTreeClassifier": {126                    "max_depth": [None, 10, 20],127                    "min_samples_split": [2, 10]128                },129                "GaussianNB": {130                    # tiny smoothing sweep; cheap131                    "var_smoothing": [1e-9, 1e-8, 1e-7]132                },133                "MLPClassifier": {134                    "hidden_layer_sizes": [(64,), (128,)],135                    "alpha": [1e-4, 1e-3]136                },137                "XGBClassifier": {138                    "learning_rate": [0.05, 0.1],139                    "max_depth": [3, 5],140                    "n_estimators": [200, 300]141                },142            }143 144            logging.info("Evaluating models with course-style evaluator")145            model_report, fitted = evaluate_models(146                X_train=X_train, y_train=y_train,147                X_test=X_test,   y_test=y_test,148                models=models, 149                param=params150            )151            if not model_report:152                raise CustomException("evaluate_models returned empty report", sys)153            154            # Rank by AUC first, f1 as tiebreaker (handles Nan Safely)155            PRIMARY, FALLBACK = "auc_roc", "f1_score"156 157            def score_tuple(m:dict):158                auc = np.nan_to_num(m.get(PRIMARY, np.nan), nan=-1.0)159                f1 = np.nan_to_num(m.get(FALLBACK, np.nan), nan=-1.0)160                return (auc, f1)161 162            163            best_model_name = max(model_report.keys(), key=lambda k: score_tuple(model_report[k]))164            best_metric = model_report[best_model_name]165 166            # Use AUC if present else F1 for theshold/logging167            best_model_score = (168                best_metric[PRIMARY]169                if not np.isnan(best_metric.get(PRIMARY, np.nan))170                else best_metric[FALLBACK]171            )172 173            # Minimal sanity check; adjust to your tolerance174            if best_model_score < 0.6:175                raise CustomException(f"No best model found (best={best_model_name}, score={best_model_score:.4f})", sys)176 177            logging.info(f"Best model: {best_model_name} | score: {best_model_score:.4f}")178 179 180            best_model = fitted[best_model_name]181            save_object(182                file_path=self.model_trainer_config.trained_model_file_path,183                obj=best_model184            )185            logging.info(f"Saved best model to {self.model_trainer_config.trained_model_file_path} | score: {best_model_score:.4f}")186            187            return (188                best_model_score,189                best_model_name190            )191        192 193        except Exception as e:194            logging.error(f"initiate_model_trainer failed: {e}")195            raise CustomException(e, sys)196