| """ |
| train_model.py |
| ============== |
| Entrena el modelo SVM sobre Fashion-MNIST y guarda los artefactos: |
| - models/scaler.pkl |
| - models/pca.pkl |
| - models/svm_model.pkl |
| |
| Ejecutar UNA SOLA VEZ antes de iniciar la API: |
| python train_model.py |
| |
| Cambios v2 (para superar 90 %): |
| - TRAIN_SIZE subido de 30 000 a 42 000 |
| - param_grid enfocado en C alto (50, 100) y gamma bajo-medio (0.001, 0.005) |
| - Tras GridSearchCV, se reentrena el modelo final con TODOS los datos de |
| entrenamiento (no solo los folds de CV), lo que extrae rendimiento extra. |
| """ |
|
|
| import os |
| import pickle |
|
|
| import numpy as np |
| from sklearn.datasets import fetch_openml |
| from sklearn.decomposition import PCA |
| from sklearn.metrics import classification_report |
| from sklearn.model_selection import GridSearchCV, train_test_split |
| from sklearn.preprocessing import StandardScaler |
| from sklearn.svm import SVC |
|
|
| |
| TRAIN_SIZE = 50_000 |
| PCA_VARIANCE = 0.95 |
| RANDOM_STATE = 42 |
| MODELS_DIR = "models" |
|
|
| CLASS_NAMES = [ |
| "T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", |
| "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot", |
| ] |
|
|
| os.makedirs(MODELS_DIR, exist_ok=True) |
|
|
| |
| print("π₯ Cargando Fashion-MNIST desde OpenML...") |
| X, y = fetch_openml("Fashion-MNIST", version=1, return_X_y=True, as_frame=False) |
|
|
| |
| print(f"βοΈ Seleccionando {TRAIN_SIZE:,} muestras estratificadas...") |
| X_small, _, y_small, _ = train_test_split( |
| X, y, |
| train_size=TRAIN_SIZE, |
| stratify=y, |
| random_state=RANDOM_STATE, |
| ) |
|
|
| |
| print("βοΈ Escalando (StandardScaler)...") |
| scaler = StandardScaler() |
| X_scaled = scaler.fit_transform(X_small) |
|
|
| |
| print(f"π¬ Aplicando PCA (varianza={PCA_VARIANCE})...") |
| pca = PCA(PCA_VARIANCE, random_state=RANDOM_STATE) |
| X_pca = pca.fit_transform(X_scaled) |
| print(f" Componentes resultantes: {pca.n_components_}") |
|
|
| |
| X_train, X_test, y_train, y_test = train_test_split( |
| X_pca, y_small, |
| test_size=0.20, |
| random_state=RANDOM_STATE, |
| stratify=y_small, |
| ) |
| print(f"π Train: {len(X_train):,} | Test: {len(X_test):,}") |
|
|
| |
| |
| print("π Ejecutando GridSearchCV (puede tardar varios minutos)...") |
| param_grid = { |
| "C": [50, 100], |
| "gamma": [0.001, 0.005], |
| "kernel": ["rbf"], |
| } |
| grid = GridSearchCV( |
| SVC(probability=True), |
| param_grid, |
| cv=3, |
| n_jobs=-1, |
| verbose=2, |
| ) |
| grid.fit(X_train, y_train) |
|
|
| best_params = grid.best_params_ |
| print(f"\nβ
Mejores parΓ‘metros: {best_params}") |
| print(f" Mejor CV score: {grid.best_score_:.4f}") |
|
|
| |
| |
| |
| print("\nπ Reentrenando modelo final con 100 % de los datos de entrenamiento...") |
| clf = SVC(probability=True, **best_params) |
| clf.fit(X_train, y_train) |
|
|
| |
| accuracy = clf.score(X_test, y_test) |
| print(f"\nπ― PrecisiΓ³n en test (80/20): {accuracy:.4f} ({accuracy*100:.2f} %)") |
|
|
| if accuracy < 0.90: |
| print("β οΈ La precisiΓ³n es menor al 90 %. Prueba subir TRAIN_SIZE a 50 000.") |
| else: |
| print("π Β‘Modelo supera el umbral del 90 %!") |
|
|
| print("\nπ Reporte completo:") |
| y_pred = clf.predict(X_test) |
| print(classification_report(y_test, y_pred, target_names=CLASS_NAMES)) |
|
|
| |
| scaler_path = os.path.join(MODELS_DIR, "scaler.pkl") |
| pca_path = os.path.join(MODELS_DIR, "pca.pkl") |
| model_path = os.path.join(MODELS_DIR, "svm_model.pkl") |
|
|
| with open(scaler_path, "wb") as f: |
| pickle.dump(scaler, f) |
|
|
| with open(pca_path, "wb") as f: |
| pickle.dump(pca, f) |
|
|
| with open(model_path, "wb") as f: |
| pickle.dump(clf, f) |
|
|
| print(f"\nπΎ Artefactos guardados en '{MODELS_DIR}/':") |
| print(f" β’ {scaler_path}") |
| print(f" β’ {pca_path}") |
| print(f" β’ {model_path}") |
| print("\nπ Ahora puedes iniciar la API con: python app.py") |
|
|