Pipeline · 6 de 10
Modelo definitivo
Con Feast y MLflow ya en pie, el modelo se reentrena leyendo
district/surface/property_type/operation_type desde el offline
store (get_historical_features) — verificado sobre las 6,811 filas
completas, cero discrepancias contra los valores de features.parquet.
El hallazgo central del proyecto
La quinta feature, district_avg_price_per_m2, ya no se lee del valor
estático de Feast — se recalcula por fold de train, de forma honesta. Y
ahí apareció el problema: la versión honesta de esa feature rompía el
modelo, no lo mejoraba.
El leakage real en district_avg_price_per_m2
Con los mismos hiperparámetros del baseline, el modelo de Venta pasó de test R²=0.76 a R²=-0.61. No era un bug: se confirmó con 5 splits distintos y re-tuneo de hiperparámetros por CV, y ninguno hizo que la feature honesta superara simplemente no tenerla. La señal aparente del baseline era la fuga — el modelo definitivo la elimina.
La feature se eliminó del modelo y los hiperparámetros se re-tunearon
por CV contra el set de 3 features (max_depth=2, min_child_weight=20).
El modelo definitivo es menos preciso que el baseline pero notablemente
menos sobreajustado — y ambos modelos siguen superando ampliamente el
baseline trivial.
Registro en MLflow
MLflow Model Registry
Cada corrida se trackea en el experiment arequipa-housing-price (un run
por operation_type: parámetros, métricas train/test/gap, el modelo como
artifact) y se registra como dos modelos separados —
arequipa-price-venta y arequipa-price-alquiler — no dos versiones del
mismo nombre, porque son dos modelos con targets distintos, no la misma
cosa versionada.
Export a ONNX
ONNX
Formato de despliegue elegido para que el modelo entrenado en Python se
sirva desde onnxruntime-node sin depender de un runtime de Python en
producción. Categóricas nativas de XGBoost exportan directo — la
suposición inicial de necesitar un fallback a one-hot resultó incorrecta.
Validado contra el test set completo de cada modelo: diferencia absoluta
máxima de 8.58e-06 (Venta) y 3.81e-06 (Alquiler), muy por debajo de
la tolerancia elegida (1e-3).
El mapeo exacto de categorías a códigos se exporta aparte, como JSON — es lo que la API en Node.js usa para codificar un request sin depender de Python (ver API de inferencia).
Artefactos de este paso
| Artefacto | Tipo | Qué es |
|---|---|---|
ml/training/train.py (versión definitiva) | Script | Ahora lee de Feast (get_historical_features), recalcula district_avg_price_per_m2 por fold, y ya no la usa. Trackea en MLflow. |
ml/training/export_onnx.py | Script | to_onnx, to_onnx_input, validate_onnx, save_category_mapping — export + validación contra el test set completo. |
data/processed/models/{venta,alquiler}_xgb.onnx | Modelo | Formato ONNX, el que sirve la API de inferencia. |
data/processed/models/{venta,alquiler}_categories.json | Mapeo | Lista ordenada de categorías por columna — posición = código. Lo consume encode.js en Node. |
MLflow — experiment arequipa-housing-price | Tracking | Un run por operation_type: parámetros, métricas train/test/gap, modelo como artifact. |
MLflow Model Registry | Registry | Dos modelos registrados: arequipa-price-venta, arequipa-price-alquiler. |