Spaces:
Sleeping
Sleeping
| """Train and persist the used-car price model.""" | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| # Allow running via: python src/train_price_model.py | |
| PROJECT_ROOT = Path(__file__).resolve().parent.parent | |
| if str(PROJECT_ROOT) not in sys.path: | |
| sys.path.insert(0, str(PROJECT_ROOT)) | |
| import joblib | |
| import numpy as np | |
| from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor | |
| from sklearn.linear_model import LinearRegression | |
| from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score | |
| from src.config import MODEL_DIR, MODEL_METADATA_PATH, PREPROCESSOR_PATH, PRICE_MODEL_PATH | |
| from src.data_preprocessing import prepare_train_test_data | |
| def evaluate_regression(y_true: np.ndarray, y_pred: np.ndarray) -> dict[str, float]: | |
| rmse = float(np.sqrt(mean_squared_error(y_true, y_pred))) | |
| mae = float(mean_absolute_error(y_true, y_pred)) | |
| r2 = float(r2_score(y_true, y_pred)) | |
| return {"mae": mae, "rmse": rmse, "r2": r2} | |
| def main() -> None: | |
| MODEL_DIR.mkdir(parents=True, exist_ok=True) | |
| prepared = prepare_train_test_data() | |
| X_train = prepared["X_train"] | |
| X_test = prepared["X_test"] | |
| y_train = prepared["y_train"].values | |
| y_test = prepared["y_test"].values | |
| preprocessor = prepared["preprocessor"] | |
| X_train_transformed = preprocessor.fit_transform(X_train) | |
| X_test_transformed = preprocessor.transform(X_test) | |
| model_candidates = { | |
| "LinearRegression": LinearRegression(), | |
| "RandomForestRegressor": RandomForestRegressor( | |
| n_estimators=300, | |
| random_state=42, | |
| n_jobs=-1, | |
| min_samples_leaf=2, | |
| ), | |
| "GradientBoostingRegressor": GradientBoostingRegressor(random_state=42), | |
| } | |
| metrics = {} | |
| fitted_models = {} | |
| for name, model in model_candidates.items(): | |
| model.fit(X_train_transformed, y_train) | |
| preds = model.predict(X_test_transformed) | |
| metrics[name] = evaluate_regression(y_test, preds) | |
| fitted_models[name] = model | |
| best_model_name = min(metrics.keys(), key=lambda m: metrics[m]["rmse"]) | |
| best_model = fitted_models[best_model_name] | |
| joblib.dump(best_model, PRICE_MODEL_PATH) | |
| joblib.dump(preprocessor, PREPROCESSOR_PATH) | |
| metadata = { | |
| "created_at": datetime.now(timezone.utc).isoformat(), | |
| "data_source": str(prepared["data_path"]), | |
| "model_name": best_model_name, | |
| "metrics": metrics, | |
| "best_metrics": metrics[best_model_name], | |
| "n_train": int(len(X_train)), | |
| "n_test": int(len(X_test)), | |
| "training_columns": list(X_train.columns), | |
| "numeric_features": prepared["numeric_features"], | |
| "categorical_features": prepared["categorical_features"], | |
| } | |
| MODEL_METADATA_PATH.write_text(json.dumps(metadata, indent=2), encoding="utf-8") | |
| print("Training completed.") | |
| print(f"Best model: {best_model_name}") | |
| print(f"Saved model to: {PRICE_MODEL_PATH}") | |
| print(f"Saved preprocessor to: {PREPROCESSOR_PATH}") | |
| print(f"Saved metadata to: {MODEL_METADATA_PATH}") | |
| for name, result in metrics.items(): | |
| print( | |
| f"{name}: MAE={result['mae']:.2f}, RMSE={result['rmse']:.2f}, R2={result['r2']:.4f}" | |
| ) | |
| if __name__ == "__main__": | |
| main() | |