import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import joblib import os from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier def ejecutar_fine_tuning(): print("========================================================================") print("⚙️ INICIANDO SINTONIZACIÓN FINA (FINE-TUNING) DE MODELOS TABULARES") print("========================================================================\n") # 1. Cargar el dataset final preparado ruta_datos = 'datos_procesados/dataset_preparado_ml.csv' if not os.path.exists(ruta_datos): print(f"[ERROR] No se encuentra el dataset en {ruta_datos}") return df_ml = pd.read_csv(ruta_datos) # 2. Separar variables # Asumimos que el dataset ya está limpio de chivatos gracias al pipeline anterior X = df_ml.drop(columns=['churn'], errors='ignore') y = df_ml['churn'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=42, stratify=y) # 3. Definir los finalistas y sus cuadrículas de hiperparámetros top_3_nombres = ['Gradient Boosting', 'RF - Profundo (Ramas ∞, Hojas 1)', 'RF - Intermedio (Ramas 12, Hojas 10)'] diccionario_grids = { "Gradient Boosting": ( GradientBoostingClassifier(random_state=42), { 'n_estimators': [100, 200], 'learning_rate': [0.05, 0.1, 0.2], 'max_depth': [3, 5, 7] } ), "RF - Profundo (Ramas ∞, Hojas 1)": ( RandomForestClassifier(random_state=42, n_jobs=-1), { 'n_estimators': [100, 200, 300], 'max_depth': [None, 20, 30], 'min_samples_split': [2, 5], 'min_samples_leaf': [1, 2] } ), "RF - Intermedio (Ramas 12, Hojas 10)": ( RandomForestClassifier(random_state=42, n_jobs=-1), { 'n_estimators': [100, 200], 'max_depth': [10, 15, None], 'min_samples_split': [2, 5], 'min_samples_leaf': [2, 10] } ) } resultados_finetuning = [] mejores_modelos = {} # 4. Bucle de Optimización for nombre in top_3_nombres: print(f"🔬 Optimizando: {nombre}...") modelo_base, parametros = diccionario_grids[nombre] grid_search = GridSearchCV( estimator=modelo_base, param_grid=parametros, scoring='f1', cv=3, n_jobs=-1, verbose=0 ) grid_search.fit(X_train, y_train) mejor_modelo = grid_search.best_estimator_ mejores_modelos[nombre] = mejor_modelo y_pred_opt = mejor_modelo.predict(X_test) f1_opt = f1_score(y_test, y_pred_opt, zero_division=0) acc_opt = accuracy_score(y_test, y_pred_opt) prec_opt = precision_score(y_test, y_pred_opt, zero_division=0) rec_opt = recall_score(y_test, y_pred_opt, zero_division=0) print(f" -> Mejores parámetros: {grid_search.best_params_}") print(f" -> Nuevo F1-Score: {f1_opt:.4f}\n") resultados_finetuning.append({ "Modelo Optimizado": nombre + " (Tuned)", "Accuracy": acc_opt, "Precision": prec_opt, "Recall": rec_opt, "F1-Score": f1_opt }) # 5. Generar y guardar la gráfica final df_final = pd.DataFrame(resultados_finetuning).sort_values(by="F1-Score", ascending=False) df_melted = df_final.melt(id_vars="Modelo Optimizado", var_name="Métrica", value_name="Puntuación") plt.figure(figsize=(14, 7)) ax = sns.barplot(data=df_melted, x="Modelo Optimizado", y="Puntuación", hue="Métrica", palette="Set2") plt.title("Comparativa de Rendimiento: Modelos Finalistas Optimizados", fontsize=16, fontweight='bold') plt.ylim(0, 1.1) plt.ylabel("Puntuación (0 a 1)") plt.xlabel("") for p in ax.patches: altura = p.get_height() if altura > 0: ax.annotate(f"{altura:.3f}", (p.get_x() + p.get_width() / 2., altura), ha='center', va='bottom', fontsize=10, fontweight='bold', color='#333333', xytext=(0, 5), textcoords='offset points') plt.legend(title="Métricas de Evaluación", bbox_to_anchor=(1.02, 1), loc='upper left') plt.tight_layout() # Guardamos la gráfica en lugar de solo mostrarla os.makedirs('outputs/charts', exist_ok=True) ruta_grafica = 'outputs/charts/comparativa_finetuning.png' plt.savefig(ruta_grafica, dpi=300) print(f"📊 Gráfica guardada en: {ruta_grafica}") # 6. Guardar el Campeón Absoluto nombre_campeon = df_final.iloc[0]["Modelo Optimizado"].replace(" (Tuned)", "") modelo_campeon = mejores_modelos[nombre_campeon] ruta_modelo = 'modelos/modelo_rf_optimizado.pkl' os.makedirs(os.path.dirname(ruta_modelo), exist_ok=True) joblib.dump(modelo_campeon, ruta_modelo) print(f"\n👑 CAMPEÓN ABSOLUTO: {nombre_campeon}") print(f"💾 Modelo guardado y listo para producción en: '{ruta_modelo}'") if __name__ == "__main__": ejecutar_fine_tuning()