Spaces:
Running
Running
added ml_ridge_regression
Browse files
app.py
CHANGED
|
@@ -15,6 +15,7 @@ from routes.ML_LinearRegression_ChurnPredictor import router as linear_regressio
|
|
| 15 |
from routes.ML_LogisticRegression_ChurnPredictor import router as logistic_regression_router
|
| 16 |
from routes.ML_DecisionTree_IrisClassifier import router as iris_router
|
| 17 |
from routes.ML_RandomForest_TaxiPredictor import router as random_forest_router
|
|
|
|
| 18 |
|
| 19 |
from routes.DL_RNN_StockPricePredictor import router as rnn_stock_router
|
| 20 |
from routes.DL_CNN_NumberRecognition import router as cnn_router
|
|
@@ -50,6 +51,7 @@ app.include_router(sentiment_router, dependencies=_auth)
|
|
| 50 |
app.include_router(linear_regression_router, dependencies=_auth)
|
| 51 |
app.include_router(logistic_regression_router, dependencies=_auth)
|
| 52 |
app.include_router(random_forest_router, dependencies=_auth)
|
|
|
|
| 53 |
app.include_router(cnn_router, dependencies=_auth)
|
| 54 |
app.include_router(iris_router, dependencies=_auth)
|
| 55 |
app.include_router(nlp_collaborative_filter_router, dependencies=_auth)
|
|
|
|
| 15 |
from routes.ML_LogisticRegression_ChurnPredictor import router as logistic_regression_router
|
| 16 |
from routes.ML_DecisionTree_IrisClassifier import router as iris_router
|
| 17 |
from routes.ML_RandomForest_TaxiPredictor import router as random_forest_router
|
| 18 |
+
from routes.ML_RidgeRegression_HousePricePredictor import router as ridge_regression_router
|
| 19 |
|
| 20 |
from routes.DL_RNN_StockPricePredictor import router as rnn_stock_router
|
| 21 |
from routes.DL_CNN_NumberRecognition import router as cnn_router
|
|
|
|
| 51 |
app.include_router(linear_regression_router, dependencies=_auth)
|
| 52 |
app.include_router(logistic_regression_router, dependencies=_auth)
|
| 53 |
app.include_router(random_forest_router, dependencies=_auth)
|
| 54 |
+
app.include_router(ridge_regression_router, dependencies=_auth)
|
| 55 |
app.include_router(cnn_router, dependencies=_auth)
|
| 56 |
app.include_router(iris_router, dependencies=_auth)
|
| 57 |
app.include_router(nlp_collaborative_filter_router, dependencies=_auth)
|
ml/ML_RidgeRegression_HousePricePredictor.ipynb
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 1,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [
|
| 8 |
+
{
|
| 9 |
+
"name": "stdout",
|
| 10 |
+
"output_type": "stream",
|
| 11 |
+
"text": [
|
| 12 |
+
" square_feet bedrooms bathrooms age_years distance_to_center_km \\\n",
|
| 13 |
+
"0 3574 3 1 21 5.4 \n",
|
| 14 |
+
"1 3907 2 3 20 7.6 \n",
|
| 15 |
+
"2 1260 3 2 5 25.1 \n",
|
| 16 |
+
"3 1694 3 3 0 17.5 \n",
|
| 17 |
+
"4 1530 4 3 4 26.5 \n",
|
| 18 |
+
"\n",
|
| 19 |
+
" price_thousands \n",
|
| 20 |
+
"0 736.19 \n",
|
| 21 |
+
"1 729.73 \n",
|
| 22 |
+
"2 169.12 \n",
|
| 23 |
+
"3 353.68 \n",
|
| 24 |
+
"4 297.71 \n",
|
| 25 |
+
"\n",
|
| 26 |
+
"Price range: $-40.7k – $854.1k\n",
|
| 27 |
+
"\n",
|
| 28 |
+
"R² score : 0.9870\n",
|
| 29 |
+
"RMSE : $22.48k\n",
|
| 30 |
+
"\n",
|
| 31 |
+
"Sample house (1800 sqft, 3 bed, 2 bath, 15 yrs, 8 km): $419.2k\n"
|
| 32 |
+
]
|
| 33 |
+
}
|
| 34 |
+
],
|
| 35 |
+
"source": [
|
| 36 |
+
"import numpy as np\n",
|
| 37 |
+
"import pandas as pd\n",
|
| 38 |
+
"from sklearn.linear_model import Ridge\n",
|
| 39 |
+
"from sklearn.preprocessing import StandardScaler\n",
|
| 40 |
+
"from sklearn.pipeline import Pipeline\n",
|
| 41 |
+
"from sklearn.model_selection import train_test_split\n",
|
| 42 |
+
"from sklearn.metrics import mean_squared_error, r2_score\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"np.random.seed(42)\n",
|
| 45 |
+
"n = 300\n",
|
| 46 |
+
"\n",
|
| 47 |
+
"square_feet = np.random.randint(400, 4000, n)\n",
|
| 48 |
+
"bedrooms = np.random.randint(1, 6, n)\n",
|
| 49 |
+
"bathrooms = np.random.randint(1, 4, n)\n",
|
| 50 |
+
"age_years = np.random.randint(0, 60, n)\n",
|
| 51 |
+
"distance_to_center_km = np.round(np.random.uniform(0.5, 35.0, n), 1)\n",
|
| 52 |
+
"\n",
|
| 53 |
+
"# Price formula: size and rooms drive price up, age and distance drive it down\n",
|
| 54 |
+
"price_thousands = (\n",
|
| 55 |
+
" 0.18 * square_feet\n",
|
| 56 |
+
" + 18 * bedrooms\n",
|
| 57 |
+
" + 12 * bathrooms\n",
|
| 58 |
+
" - 1.2 * age_years\n",
|
| 59 |
+
" - 6.5 * distance_to_center_km\n",
|
| 60 |
+
" + np.random.normal(0, 25, n)\n",
|
| 61 |
+
" + 80\n",
|
| 62 |
+
").round(2)\n",
|
| 63 |
+
"\n",
|
| 64 |
+
"df = pd.DataFrame({\n",
|
| 65 |
+
" 'square_feet': square_feet,\n",
|
| 66 |
+
" 'bedrooms': bedrooms,\n",
|
| 67 |
+
" 'bathrooms': bathrooms,\n",
|
| 68 |
+
" 'age_years': age_years,\n",
|
| 69 |
+
" 'distance_to_center_km': distance_to_center_km,\n",
|
| 70 |
+
" 'price_thousands': price_thousands\n",
|
| 71 |
+
"})\n",
|
| 72 |
+
"\n",
|
| 73 |
+
"print(df.head())\n",
|
| 74 |
+
"print(f'\\nPrice range: ${df.price_thousands.min():.1f}k – ${df.price_thousands.max():.1f}k')\n",
|
| 75 |
+
"\n",
|
| 76 |
+
"X = df[['square_feet', 'bedrooms', 'bathrooms', 'age_years', 'distance_to_center_km']]\n",
|
| 77 |
+
"y = df['price_thousands']\n",
|
| 78 |
+
"\n",
|
| 79 |
+
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
|
| 80 |
+
"\n",
|
| 81 |
+
"# Ridge Regression with StandardScaler in a Pipeline\n",
|
| 82 |
+
"pipeline = Pipeline([\n",
|
| 83 |
+
" ('scaler', StandardScaler()),\n",
|
| 84 |
+
" ('ridge', Ridge(alpha=1.0))\n",
|
| 85 |
+
"])\n",
|
| 86 |
+
"pipeline.fit(X_train, y_train)\n",
|
| 87 |
+
"\n",
|
| 88 |
+
"y_pred = pipeline.predict(X_test)\n",
|
| 89 |
+
"r2 = r2_score(y_test, y_pred)\n",
|
| 90 |
+
"mse = mean_squared_error(y_test, y_pred)\n",
|
| 91 |
+
"rmse = np.sqrt(mse)\n",
|
| 92 |
+
"\n",
|
| 93 |
+
"print(f'\\nR\\u00b2 score : {r2:.4f}')\n",
|
| 94 |
+
"print(f'RMSE : ${rmse:.2f}k')\n",
|
| 95 |
+
"\n",
|
| 96 |
+
"# Sample prediction\n",
|
| 97 |
+
"sample = pd.DataFrame(\n",
|
| 98 |
+
" [[1800, 3, 2, 15, 8.0]],\n",
|
| 99 |
+
" columns=['square_feet', 'bedrooms', 'bathrooms', 'age_years', 'distance_to_center_km']\n",
|
| 100 |
+
")\n",
|
| 101 |
+
"pred = pipeline.predict(sample)[0]\n",
|
| 102 |
+
"print(f'\\nSample house (1800 sqft, 3 bed, 2 bath, 15 yrs, 8 km): ${pred:.1f}k')"
|
| 103 |
+
]
|
| 104 |
+
},
|
| 105 |
+
{
|
| 106 |
+
"cell_type": "code",
|
| 107 |
+
"execution_count": 2,
|
| 108 |
+
"metadata": {},
|
| 109 |
+
"outputs": [
|
| 110 |
+
{
|
| 111 |
+
"name": "stderr",
|
| 112 |
+
"output_type": "stream",
|
| 113 |
+
"text": [
|
| 114 |
+
"/usr/local/lib/python3.12/dist-packages/huggingface_hub/utils/_auth.py:122: UserWarning: \n",
|
| 115 |
+
"Error while fetching `HF_TOKEN` secret value from your vault: 'Requesting secret HF_TOKEN timed out. Secrets can only be fetched when running from the Colab UI.'.\n",
|
| 116 |
+
"You are not authenticated with the Hugging Face Hub in this notebook.\n",
|
| 117 |
+
"If the error persists, please let us know by opening an issue on GitHub (https://github.com/huggingface/huggingface_hub/issues/new).\n",
|
| 118 |
+
" warnings.warn(\n"
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"data": {
|
| 123 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 124 |
+
"model_id": "f8b5064bfed74a1ea1fc53a6a3c93bcc",
|
| 125 |
+
"version_major": 2,
|
| 126 |
+
"version_minor": 0
|
| 127 |
+
},
|
| 128 |
+
"text/plain": [
|
| 129 |
+
"VBox(children=(HTML(value='<center> <img\\nsrc=https://huggingface.co/front/assets/huggingface_logo-noborder.sv…"
|
| 130 |
+
]
|
| 131 |
+
},
|
| 132 |
+
"metadata": {},
|
| 133 |
+
"output_type": "display_data"
|
| 134 |
+
}
|
| 135 |
+
],
|
| 136 |
+
"source": [
|
| 137 |
+
"from huggingface_hub import notebook_login\n",
|
| 138 |
+
"\n",
|
| 139 |
+
"notebook_login()"
|
| 140 |
+
]
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"cell_type": "code",
|
| 144 |
+
"execution_count": 4,
|
| 145 |
+
"metadata": {},
|
| 146 |
+
"outputs": [
|
| 147 |
+
{
|
| 148 |
+
"name": "stdout",
|
| 149 |
+
"output_type": "stream",
|
| 150 |
+
"text": [
|
| 151 |
+
"Model saved locally: /content/ML_RidgeRegression_HousePricePredictor.joblib\n"
|
| 152 |
+
]
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"data": {
|
| 156 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 157 |
+
"model_id": "a41c535e27f14258a2c9574e8bedde2b",
|
| 158 |
+
"version_major": 2,
|
| 159 |
+
"version_minor": 0
|
| 160 |
+
},
|
| 161 |
+
"text/plain": [
|
| 162 |
+
"Processing Files (0 / 0) : | | 0.00B / 0.00B "
|
| 163 |
+
]
|
| 164 |
+
},
|
| 165 |
+
"metadata": {},
|
| 166 |
+
"output_type": "display_data"
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"data": {
|
| 170 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 171 |
+
"model_id": "8b8a652bd8eb44ac8c78ac314c382a42",
|
| 172 |
+
"version_major": 2,
|
| 173 |
+
"version_minor": 0
|
| 174 |
+
},
|
| 175 |
+
"text/plain": [
|
| 176 |
+
"New Data Upload : | | 0.00B / 0.00B "
|
| 177 |
+
]
|
| 178 |
+
},
|
| 179 |
+
"metadata": {},
|
| 180 |
+
"output_type": "display_data"
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"data": {
|
| 184 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 185 |
+
"model_id": "359430ce0d1f45309838e54d501de33e",
|
| 186 |
+
"version_major": 2,
|
| 187 |
+
"version_minor": 0
|
| 188 |
+
},
|
| 189 |
+
"text/plain": [
|
| 190 |
+
" ...ousePricePredictor.joblib: 100%|##########| 1.50kB / 1.50kB "
|
| 191 |
+
]
|
| 192 |
+
},
|
| 193 |
+
"metadata": {},
|
| 194 |
+
"output_type": "display_data"
|
| 195 |
+
},
|
| 196 |
+
{
|
| 197 |
+
"name": "stdout",
|
| 198 |
+
"output_type": "stream",
|
| 199 |
+
"text": [
|
| 200 |
+
"Uploaded model to https://huggingface.co/looh2/model\n"
|
| 201 |
+
]
|
| 202 |
+
}
|
| 203 |
+
],
|
| 204 |
+
"source": [
|
| 205 |
+
"import joblib\n",
|
| 206 |
+
"from pathlib import Path\n",
|
| 207 |
+
"from huggingface_hub import HfApi\n",
|
| 208 |
+
"\n",
|
| 209 |
+
"repo_id = \"looh2/model\"\n",
|
| 210 |
+
"model_path = Path(\"ML_RidgeRegression_HousePricePredictor.joblib\")\n",
|
| 211 |
+
"\n",
|
| 212 |
+
"# Save the pipeline (scaler + Ridge model)\n",
|
| 213 |
+
"joblib.dump(pipeline, model_path)\n",
|
| 214 |
+
"print(f\"Model saved locally: {model_path.resolve()}\")\n",
|
| 215 |
+
"\n",
|
| 216 |
+
"api = HfApi()\n",
|
| 217 |
+
"api.create_repo(repo_id=repo_id, repo_type=\"model\", exist_ok=True)\n",
|
| 218 |
+
"api.upload_file(\n",
|
| 219 |
+
" path_or_fileobj=str(model_path),\n",
|
| 220 |
+
" path_in_repo=model_path.name,\n",
|
| 221 |
+
" repo_id=repo_id,\n",
|
| 222 |
+
" repo_type=\"model\",\n",
|
| 223 |
+
")\n",
|
| 224 |
+
"print(f\"Uploaded model to https://huggingface.co/{repo_id}\")"
|
| 225 |
+
]
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"cell_type": "code",
|
| 229 |
+
"execution_count": null,
|
| 230 |
+
"metadata": {},
|
| 231 |
+
"outputs": [],
|
| 232 |
+
"source": [
|
| 233 |
+
"# FastAPI integration reference\n",
|
| 234 |
+
"from fastapi import FastAPI\n",
|
| 235 |
+
"from pydantic import BaseModel\n",
|
| 236 |
+
"import joblib\n",
|
| 237 |
+
"import pandas as pd\n",
|
| 238 |
+
"from huggingface_hub import hf_hub_download\n",
|
| 239 |
+
"\n",
|
| 240 |
+
"model_path = hf_hub_download(\n",
|
| 241 |
+
" repo_id=\"looh2/model\",\n",
|
| 242 |
+
" filename=\"ML_RidgeRegression_HousePricePredictor.joblib\",\n",
|
| 243 |
+
" repo_type=\"model\"\n",
|
| 244 |
+
")\n",
|
| 245 |
+
"pipeline = joblib.load(model_path)\n",
|
| 246 |
+
"\n",
|
| 247 |
+
"app = FastAPI()\n",
|
| 248 |
+
"\n",
|
| 249 |
+
"class HouseFeatures(BaseModel):\n",
|
| 250 |
+
" square_feet: int\n",
|
| 251 |
+
" bedrooms: int\n",
|
| 252 |
+
" bathrooms: int\n",
|
| 253 |
+
" age_years: int\n",
|
| 254 |
+
" distance_to_center_km: float\n",
|
| 255 |
+
"\n",
|
| 256 |
+
"@app.post(\"/predict\")\n",
|
| 257 |
+
"def predict_house_price(features: HouseFeatures):\n",
|
| 258 |
+
" input_df = pd.DataFrame(\n",
|
| 259 |
+
" [[features.square_feet, features.bedrooms, features.bathrooms,\n",
|
| 260 |
+
" features.age_years, features.distance_to_center_km]],\n",
|
| 261 |
+
" columns=['square_feet', 'bedrooms', 'bathrooms', 'age_years', 'distance_to_center_km']\n",
|
| 262 |
+
" )\n",
|
| 263 |
+
" pred = pipeline.predict(input_df)[0]\n",
|
| 264 |
+
" return {\n",
|
| 265 |
+
" \"predicted_price_thousands\": round(float(pred), 2),\n",
|
| 266 |
+
" \"predicted_price_formatted\": f\"${round(float(pred), 1)}k\"\n",
|
| 267 |
+
" }"
|
| 268 |
+
]
|
| 269 |
+
}
|
| 270 |
+
],
|
| 271 |
+
"metadata": {
|
| 272 |
+
"kernelspec": {
|
| 273 |
+
"display_name": "Python 3 (ipykernel)",
|
| 274 |
+
"language": "python",
|
| 275 |
+
"name": "python3"
|
| 276 |
+
},
|
| 277 |
+
"language_info": {
|
| 278 |
+
"codemirror_mode": {
|
| 279 |
+
"name": "ipython",
|
| 280 |
+
"version": 3
|
| 281 |
+
},
|
| 282 |
+
"file_extension": ".py",
|
| 283 |
+
"mimetype": "text/x-python",
|
| 284 |
+
"name": "python",
|
| 285 |
+
"nbconvert_exporter": "python",
|
| 286 |
+
"pygments_lexer": "ipython3",
|
| 287 |
+
"version": "3.12.13"
|
| 288 |
+
}
|
| 289 |
+
},
|
| 290 |
+
"nbformat": 4,
|
| 291 |
+
"nbformat_minor": 5
|
| 292 |
+
}
|
routes/ML_RidgeRegression_HousePricePredictor.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
import joblib
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from typing import Optional, Any
|
| 6 |
+
|
| 7 |
+
from .config_huggingface import build_model_url, download_artifact_if_needed
|
| 8 |
+
|
| 9 |
+
router = APIRouter(tags=["Machine Learning"])
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class RidgeRegressionRequest(BaseModel):
|
| 13 |
+
square_feet: int = 1800
|
| 14 |
+
bedrooms: int = 3
|
| 15 |
+
bathrooms: int = 2
|
| 16 |
+
age_years: int = 15
|
| 17 |
+
distance_to_center_km: float = 8.0
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
MODEL_STATE: dict[str, Optional[Any]] = {
|
| 21 |
+
"model": None,
|
| 22 |
+
"error": None,
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
MODEL_URL = build_model_url("ML_RidgeRegression_HousePricePredictor.joblib")
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def _ensure_model_loaded() -> None:
|
| 29 |
+
if MODEL_STATE["model"] is not None:
|
| 30 |
+
return
|
| 31 |
+
try:
|
| 32 |
+
model_path = download_artifact_if_needed(MODEL_URL)
|
| 33 |
+
MODEL_STATE["model"] = joblib.load(model_path)
|
| 34 |
+
MODEL_STATE["error"] = None
|
| 35 |
+
except Exception as e:
|
| 36 |
+
MODEL_STATE["error"] = str(e)
|
| 37 |
+
raise
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@router.post("/models/ridge_regression", summary="Predict house price with Ridge Regression")
|
| 41 |
+
def predict_ridge_regression(data: RidgeRegressionRequest):
|
| 42 |
+
import traceback
|
| 43 |
+
try:
|
| 44 |
+
_ensure_model_loaded()
|
| 45 |
+
except Exception:
|
| 46 |
+
detail = "Model not loaded."
|
| 47 |
+
if MODEL_STATE["error"]:
|
| 48 |
+
detail = f"Model not loaded: {MODEL_STATE['error']}"
|
| 49 |
+
return {"error": detail, "traceback": traceback.format_exc(), "status": 500}
|
| 50 |
+
|
| 51 |
+
model = MODEL_STATE["model"]
|
| 52 |
+
if model is None:
|
| 53 |
+
return {"error": f"Model is None after loading. Error: {MODEL_STATE['error']}", "status": 500}
|
| 54 |
+
|
| 55 |
+
input_df = pd.DataFrame(
|
| 56 |
+
[[data.square_feet, data.bedrooms, data.bathrooms, data.age_years, data.distance_to_center_km]],
|
| 57 |
+
columns=["square_feet", "bedrooms", "bathrooms", "age_years", "distance_to_center_km"],
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
pred = model.predict(input_df)[0]
|
| 62 |
+
except Exception as e:
|
| 63 |
+
return {"error": f"Prediction failed: {str(e)}", "traceback": traceback.format_exc(), "status": 500}
|
| 64 |
+
|
| 65 |
+
price = round(float(pred), 2)
|
| 66 |
+
return {
|
| 67 |
+
"predicted_price_thousands": price,
|
| 68 |
+
"predicted_price_formatted": f"${price:.1f}k",
|
| 69 |
+
}
|