nath13huggingface commited on
Commit
af576c2
·
1 Parent(s): 12a14f1

Deploy FastAPI

Browse files
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copier et installer les dépendances
6
+ COPY requirements.txt ./
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # Copier le reste des fichiers
10
+ COPY . .
11
+
12
+ # Exposer le port utilisé par Hugging Face pour les APIs
13
+ EXPOSE 7860
14
+
15
+ # Commande pour lancer l'API
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
__pycache__/app.cpython-311.pyc ADDED
Binary file (1.88 kB). View file
 
__pycache__/app.cpython-312.pyc ADDED
Binary file (1.62 kB). View file
 
app.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from pydantic import BaseModel
3
+ import pandas as pd
4
+ import joblib
5
+ import os
6
+
7
+ app = FastAPI()
8
+
9
+ MODEL_PATH = os.path.join("model", "modele_xgb_getaround.pkl")
10
+ model = joblib.load(MODEL_PATH)
11
+
12
+ # donnée d'entrée
13
+ class InputData(BaseModel):
14
+ model_key: str
15
+ mileage: int
16
+ engine_power: int
17
+ fuel: str
18
+ paint_color: str
19
+ car_type: str
20
+ private_parking_available: bool
21
+ has_gps: bool
22
+ has_air_conditioning: bool
23
+ automatic_car: bool
24
+ has_getaround_connect: bool
25
+ has_speed_regulator: bool
26
+ winter_tires: bool
27
+
28
+ @app.post("/predict")
29
+ def predict(data: InputData):
30
+ # Convertir l'entrée en DataFrame
31
+ df = pd.DataFrame([data.dict()])
32
+
33
+ # Faire la prédiction
34
+ prediction = model.predict(df)
35
+
36
+ # Retourner la prédiction sous forme JSON
37
+ return {"prediction": prediction.tolist()}
data/get_around_delay_analysis.csv ADDED
The diff for this file is too large to render. See raw diff
 
data/get_around_pricing_project.csv ADDED
The diff for this file is too large to render. See raw diff
 
model/ml.ipynb ADDED
@@ -0,0 +1,743 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "c45bb3eb",
6
+ "metadata": {},
7
+ "source": [
8
+ "### Import des librairies\n",
9
+ "---"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 1,
15
+ "id": "82e566e4",
16
+ "metadata": {},
17
+ "outputs": [],
18
+ "source": [
19
+ "\n",
20
+ "import pandas as pd\n",
21
+ "import numpy as np\n",
22
+ "import mlflow\n",
23
+ "import mlflow.sklearn\n",
24
+ "import xgboost as xgb\n",
25
+ "import joblib\n",
26
+ "\n",
27
+ "from sklearn.model_selection import train_test_split, GridSearchCV\n",
28
+ "from sklearn.linear_model import LinearRegression, Ridge, Lasso\n",
29
+ "from sklearn.ensemble import RandomForestRegressor\n",
30
+ "from sklearn.pipeline import Pipeline\n",
31
+ "from sklearn.impute import SimpleImputer\n",
32
+ "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n",
33
+ "from sklearn.compose import ColumnTransformer\n",
34
+ "from sklearn.metrics import r2_score, mean_squared_error, r2_score\n",
35
+ "\n",
36
+ "import matplotlib.pyplot as plt\n",
37
+ "import seaborn as sns"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "markdown",
42
+ "id": "068ac7c5",
43
+ "metadata": {},
44
+ "source": [
45
+ "### Chargement des données\n",
46
+ "---"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "code",
51
+ "execution_count": 2,
52
+ "id": "6e3f4786",
53
+ "metadata": {},
54
+ "outputs": [
55
+ {
56
+ "data": {
57
+ "text/html": [
58
+ "<div>\n",
59
+ "<style scoped>\n",
60
+ " .dataframe tbody tr th:only-of-type {\n",
61
+ " vertical-align: middle;\n",
62
+ " }\n",
63
+ "\n",
64
+ " .dataframe tbody tr th {\n",
65
+ " vertical-align: top;\n",
66
+ " }\n",
67
+ "\n",
68
+ " .dataframe thead th {\n",
69
+ " text-align: right;\n",
70
+ " }\n",
71
+ "</style>\n",
72
+ "<table border=\"1\" class=\"dataframe\">\n",
73
+ " <thead>\n",
74
+ " <tr style=\"text-align: right;\">\n",
75
+ " <th></th>\n",
76
+ " <th>Unnamed: 0</th>\n",
77
+ " <th>model_key</th>\n",
78
+ " <th>mileage</th>\n",
79
+ " <th>engine_power</th>\n",
80
+ " <th>fuel</th>\n",
81
+ " <th>paint_color</th>\n",
82
+ " <th>car_type</th>\n",
83
+ " <th>private_parking_available</th>\n",
84
+ " <th>has_gps</th>\n",
85
+ " <th>has_air_conditioning</th>\n",
86
+ " <th>automatic_car</th>\n",
87
+ " <th>has_getaround_connect</th>\n",
88
+ " <th>has_speed_regulator</th>\n",
89
+ " <th>winter_tires</th>\n",
90
+ " <th>rental_price_per_day</th>\n",
91
+ " </tr>\n",
92
+ " </thead>\n",
93
+ " <tbody>\n",
94
+ " <tr>\n",
95
+ " <th>0</th>\n",
96
+ " <td>0</td>\n",
97
+ " <td>Citroën</td>\n",
98
+ " <td>140411</td>\n",
99
+ " <td>100</td>\n",
100
+ " <td>diesel</td>\n",
101
+ " <td>black</td>\n",
102
+ " <td>convertible</td>\n",
103
+ " <td>True</td>\n",
104
+ " <td>True</td>\n",
105
+ " <td>False</td>\n",
106
+ " <td>False</td>\n",
107
+ " <td>True</td>\n",
108
+ " <td>True</td>\n",
109
+ " <td>True</td>\n",
110
+ " <td>106</td>\n",
111
+ " </tr>\n",
112
+ " <tr>\n",
113
+ " <th>1</th>\n",
114
+ " <td>1</td>\n",
115
+ " <td>Citroën</td>\n",
116
+ " <td>13929</td>\n",
117
+ " <td>317</td>\n",
118
+ " <td>petrol</td>\n",
119
+ " <td>grey</td>\n",
120
+ " <td>convertible</td>\n",
121
+ " <td>True</td>\n",
122
+ " <td>True</td>\n",
123
+ " <td>False</td>\n",
124
+ " <td>False</td>\n",
125
+ " <td>False</td>\n",
126
+ " <td>True</td>\n",
127
+ " <td>True</td>\n",
128
+ " <td>264</td>\n",
129
+ " </tr>\n",
130
+ " <tr>\n",
131
+ " <th>2</th>\n",
132
+ " <td>2</td>\n",
133
+ " <td>Citroën</td>\n",
134
+ " <td>183297</td>\n",
135
+ " <td>120</td>\n",
136
+ " <td>diesel</td>\n",
137
+ " <td>white</td>\n",
138
+ " <td>convertible</td>\n",
139
+ " <td>False</td>\n",
140
+ " <td>False</td>\n",
141
+ " <td>False</td>\n",
142
+ " <td>False</td>\n",
143
+ " <td>True</td>\n",
144
+ " <td>False</td>\n",
145
+ " <td>True</td>\n",
146
+ " <td>101</td>\n",
147
+ " </tr>\n",
148
+ " <tr>\n",
149
+ " <th>3</th>\n",
150
+ " <td>3</td>\n",
151
+ " <td>Citroën</td>\n",
152
+ " <td>128035</td>\n",
153
+ " <td>135</td>\n",
154
+ " <td>diesel</td>\n",
155
+ " <td>red</td>\n",
156
+ " <td>convertible</td>\n",
157
+ " <td>True</td>\n",
158
+ " <td>True</td>\n",
159
+ " <td>False</td>\n",
160
+ " <td>False</td>\n",
161
+ " <td>True</td>\n",
162
+ " <td>True</td>\n",
163
+ " <td>True</td>\n",
164
+ " <td>158</td>\n",
165
+ " </tr>\n",
166
+ " <tr>\n",
167
+ " <th>4</th>\n",
168
+ " <td>4</td>\n",
169
+ " <td>Citroën</td>\n",
170
+ " <td>97097</td>\n",
171
+ " <td>160</td>\n",
172
+ " <td>diesel</td>\n",
173
+ " <td>silver</td>\n",
174
+ " <td>convertible</td>\n",
175
+ " <td>True</td>\n",
176
+ " <td>True</td>\n",
177
+ " <td>False</td>\n",
178
+ " <td>False</td>\n",
179
+ " <td>False</td>\n",
180
+ " <td>True</td>\n",
181
+ " <td>True</td>\n",
182
+ " <td>183</td>\n",
183
+ " </tr>\n",
184
+ " </tbody>\n",
185
+ "</table>\n",
186
+ "</div>"
187
+ ],
188
+ "text/plain": [
189
+ " Unnamed: 0 model_key mileage engine_power fuel paint_color \\\n",
190
+ "0 0 Citroën 140411 100 diesel black \n",
191
+ "1 1 Citroën 13929 317 petrol grey \n",
192
+ "2 2 Citroën 183297 120 diesel white \n",
193
+ "3 3 Citroën 128035 135 diesel red \n",
194
+ "4 4 Citroën 97097 160 diesel silver \n",
195
+ "\n",
196
+ " car_type private_parking_available has_gps has_air_conditioning \\\n",
197
+ "0 convertible True True False \n",
198
+ "1 convertible True True False \n",
199
+ "2 convertible False False False \n",
200
+ "3 convertible True True False \n",
201
+ "4 convertible True True False \n",
202
+ "\n",
203
+ " automatic_car has_getaround_connect has_speed_regulator winter_tires \\\n",
204
+ "0 False True True True \n",
205
+ "1 False False True True \n",
206
+ "2 False True False True \n",
207
+ "3 False True True True \n",
208
+ "4 False False True True \n",
209
+ "\n",
210
+ " rental_price_per_day \n",
211
+ "0 106 \n",
212
+ "1 264 \n",
213
+ "2 101 \n",
214
+ "3 158 \n",
215
+ "4 183 "
216
+ ]
217
+ },
218
+ "execution_count": 2,
219
+ "metadata": {},
220
+ "output_type": "execute_result"
221
+ }
222
+ ],
223
+ "source": [
224
+ "path = 'data/get_around_pricing_project.csv'\n",
225
+ "df = pd.read_csv(path, encoding='utf-8')\n",
226
+ "df.head()"
227
+ ]
228
+ },
229
+ {
230
+ "cell_type": "code",
231
+ "execution_count": 3,
232
+ "id": "3723f076",
233
+ "metadata": {},
234
+ "outputs": [],
235
+ "source": [
236
+ "df = df.drop(columns=['Unnamed: 0'])"
237
+ ]
238
+ },
239
+ {
240
+ "cell_type": "markdown",
241
+ "id": "8386246c",
242
+ "metadata": {},
243
+ "source": [
244
+ "### Modéle Linéaire Regression Baseline\n",
245
+ "---"
246
+ ]
247
+ },
248
+ {
249
+ "cell_type": "code",
250
+ "execution_count": 4,
251
+ "id": "b36cec22",
252
+ "metadata": {},
253
+ "outputs": [
254
+ {
255
+ "name": "stdout",
256
+ "output_type": "stream",
257
+ "text": [
258
+ "=== Score R2 ===\n",
259
+ "R2 Score (Train): 0.7140\n",
260
+ "R2 Score (Test) : 0.6937\n",
261
+ "\n",
262
+ "=== RMSE ===\n",
263
+ "RMSE : 322.59\n",
264
+ "Prix moyen : 121.21 €\n",
265
+ "Median AE : 8.19 €\n"
266
+ ]
267
+ }
268
+ ],
269
+ "source": [
270
+ "# === 1. Séparation des données ===\n",
271
+ "target = \"rental_price_per_day\"\n",
272
+ "X = df.drop(columns=target)\n",
273
+ "y = df[target]\n",
274
+ "\n",
275
+ "# === 2. Split du jeu de données ===\n",
276
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
277
+ "\n",
278
+ "# === 3. Pipeline de prétraitement ===\n",
279
+ "numerical_columns = ['mileage', 'engine_power']\n",
280
+ "categorical_columns = ['model_key', 'fuel', 'paint_color', 'car_type',\n",
281
+ " 'private_parking_available', 'has_gps', 'has_air_conditioning',\n",
282
+ " 'automatic_car', 'has_getaround_connect', 'has_speed_regulator', 'winter_tires']\n",
283
+ "\n",
284
+ "numerical_pipeline = Pipeline(steps=[\n",
285
+ " (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
286
+ " (\"standardization\", StandardScaler())\n",
287
+ "])\n",
288
+ "\n",
289
+ "categorical_pipeline = Pipeline(steps=[\n",
290
+ " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
291
+ " (\"encoder\", OneHotEncoder(drop=\"first\"))\n",
292
+ "])\n",
293
+ "\n",
294
+ "feature_encoder = ColumnTransformer(transformers=[\n",
295
+ " (\"num\", numerical_pipeline, numerical_columns),\n",
296
+ " (\"cat\", categorical_pipeline, categorical_columns)\n",
297
+ "])\n",
298
+ "\n",
299
+ "# Transformation des données\n",
300
+ "X_train = feature_encoder.fit_transform(X_train)\n",
301
+ "X_test = feature_encoder.transform(X_test)\n",
302
+ "\n",
303
+ "# === 4. Entraînement des modèles de régression === \n",
304
+ "lin_reg = LinearRegression() \n",
305
+ "lin_reg.fit(X_train, y_train)\n",
306
+ "\n",
307
+ "# === 5. Prédictions ===\n",
308
+ "y_train_pred = lin_reg.predict(X_train)\n",
309
+ "y_test_pred = lin_reg.predict(X_test)\n",
310
+ "\n",
311
+ "# === 6. Évaluation des performances ===\n",
312
+ "print(\"=== Score R2 ===\")\n",
313
+ "print(f\"R2 Score (Train): {r2_score(y_train, y_train_pred):.4f}\")\n",
314
+ "print(f\"R2 Score (Test) : {r2_score(y_test, y_test_pred):.4f}\\n\")\n",
315
+ "\n",
316
+ "print(\"=== RMSE ===\")\n",
317
+ "rmse = mean_squared_error(y_test, y_test_pred)\n",
318
+ "print(f\"RMSE : {rmse:.2f}\")\n",
319
+ "\n",
320
+ "# Prix moyen sur tout le dataset\n",
321
+ "mean_price = df[\"rental_price_per_day\"].mean()\n",
322
+ "print(f\"Prix moyen : {mean_price:.2f} €\")\n",
323
+ "\n",
324
+ "from sklearn.metrics import median_absolute_error\n",
325
+ "\n",
326
+ "medae = median_absolute_error(y_test, y_test_pred)\n",
327
+ "print(f\"Median AE : {medae:.2f} €\")\n",
328
+ "\n",
329
+ "\n"
330
+ ]
331
+ },
332
+ {
333
+ "cell_type": "markdown",
334
+ "id": "99c0d82e",
335
+ "metadata": {},
336
+ "source": [
337
+ "### Modéle RandomForestRegressor\n",
338
+ "---\n",
339
+ "#### RandomForestRegressor prédit un prix en construisant plusieurs arbres de décision indépendants et en moyennant leurs prédictions, ce qui rend la prédiction plus stable et robuste."
340
+ ]
341
+ },
342
+ {
343
+ "cell_type": "code",
344
+ "execution_count": 5,
345
+ "id": "4daa18cd",
346
+ "metadata": {},
347
+ "outputs": [
348
+ {
349
+ "name": "stdout",
350
+ "output_type": "stream",
351
+ "text": [
352
+ "=== Score R2 ===\n",
353
+ "R2 Score (Train): 0.9273\n",
354
+ "R2 Score (Test) : 0.7386\n",
355
+ "\n",
356
+ "=== RMSE ===\n",
357
+ "RMSE : 275.32\n",
358
+ "Prix moyen : 121.21 €\n",
359
+ "Median AE : 7.18 €\n",
360
+ "\n",
361
+ "Le modèle prédit très bien la tendance générale (R² test = 0,72).\n",
362
+ "Le RMSE est élevé car il est sensible aux voitures très chères ou très bon marché,\n",
363
+ "mais la Median Absolute Error de 7,1€ montre que pour la majorité des véhicules,\n",
364
+ "nos prédictions sont très proches du prix réel.\n"
365
+ ]
366
+ }
367
+ ],
368
+ "source": [
369
+ "\n",
370
+ "target = \"rental_price_per_day\"\n",
371
+ "X = df.drop(columns=target)\n",
372
+ "y = df[target]\n",
373
+ "\n",
374
+ "# Split du jeu de données ===\n",
375
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
376
+ "\n",
377
+ "# Pipeline de prétraitement ===\n",
378
+ "numerical_columns = ['mileage', 'engine_power']\n",
379
+ "categorical_columns = ['model_key', 'fuel', 'paint_color', 'car_type',\n",
380
+ " 'private_parking_available', 'has_gps', 'has_air_conditioning',\n",
381
+ " 'automatic_car', 'has_getaround_connect', 'has_speed_regulator', 'winter_tires']\n",
382
+ "\n",
383
+ "numeric_transformer = Pipeline(steps=[\n",
384
+ " (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
385
+ " (\"standardization\", StandardScaler())\n",
386
+ "])\n",
387
+ "\n",
388
+ "categorical_transformer = Pipeline(steps=[\n",
389
+ " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
390
+ " (\"onehot\", OneHotEncoder(handle_unknown=\"ignore\"))\n",
391
+ "])\n",
392
+ "\n",
393
+ "preprocessor = ColumnTransformer(\n",
394
+ " transformers=[\n",
395
+ " (\"num\", numeric_transformer, numerical_columns),\n",
396
+ " (\"cat\", categorical_transformer, categorical_columns)\n",
397
+ " ])\n",
398
+ "\n",
399
+ "# Création du pipeline complet\n",
400
+ "# n_estimators=100 → assez d’arbres pour stabiliser le modèle.\n",
401
+ "# max_depth=9 → limite la complexité des arbres pour mieux généraliser sur le test.\n",
402
+ "# random_state=42 → assure que les résultats sont toujours identiques.\n",
403
+ "model = RandomForestRegressor(n_estimators=100, max_depth=12, random_state=42) #100 arbres dans la foret - chaque arbre à max 9 niveaux de decisions - \n",
404
+ "pipeline = Pipeline(steps=[\n",
405
+ " (\"preprocessor\", preprocessor),\n",
406
+ " (\"regressor\", model) \n",
407
+ "])\n",
408
+ "\n",
409
+ "# Entraînement\n",
410
+ "pipeline.fit(X_train, y_train)\n",
411
+ "\n",
412
+ "# Prédiction \n",
413
+ "y_pred = pipeline.predict(X_test)\n",
414
+ "\n",
415
+ "y_train_pred = pipeline.predict(X_train)\n",
416
+ "y_test_pred = pipeline.predict(X_test)\n",
417
+ "\n",
418
+ "# Évaluation des performances\n",
419
+ "\n",
420
+ "print(\"=== Score R2 ===\")\n",
421
+ "print(f\"R2 Score (Train): {r2_score(y_train, y_train_pred):.4f}\")\n",
422
+ "print(f\"R2 Score (Test) : {r2_score(y_test, y_test_pred):.4f}\\n\")\n",
423
+ "\n",
424
+ "print(\"=== RMSE ===\")\n",
425
+ "rmse = mean_squared_error(y_test, y_test_pred)\n",
426
+ "print(f\"RMSE : {rmse:.2f}\")\n",
427
+ "\n",
428
+ "# Prix moyen sur tout le dataset\n",
429
+ "mean_price = df[\"rental_price_per_day\"].mean()\n",
430
+ "print(f\"Prix moyen : {mean_price:.2f} €\")\n",
431
+ "\n",
432
+ "medae = median_absolute_error(y_test, y_test_pred)\n",
433
+ "print(f\"Median AE : {medae:.2f} €\")\n",
434
+ "\n",
435
+ "print(\"\\nLe modèle prédit très bien la tendance générale (R² test = 0,72).\\nLe RMSE est élevé car il est sensible aux voitures très chères ou très bon marché,\\nmais la Median Absolute Error de 7,1€ montre que pour la majorité des véhicules,\\nnos prédictions sont très proches du prix réel.\")\n"
436
+ ]
437
+ },
438
+ {
439
+ "cell_type": "markdown",
440
+ "id": "48f1c12b",
441
+ "metadata": {},
442
+ "source": [
443
+ "### Modéle XGBoost\n",
444
+ "---\n",
445
+ "#### XGBoost (eXtreme Gradient Boosting) est un algorithme de Gradient Boosting basé sur des arbres de décision, optimisé pour la vitesse et la performance, où chaque nouvel arbre corrige les erreurs des arbres précédents."
446
+ ]
447
+ },
448
+ {
449
+ "cell_type": "code",
450
+ "execution_count": 6,
451
+ "id": "59ba531d",
452
+ "metadata": {},
453
+ "outputs": [
454
+ {
455
+ "name": "stdout",
456
+ "output_type": "stream",
457
+ "text": [
458
+ "=== Score R2 ===\n",
459
+ "R2 Score (Train): 0.9886\n",
460
+ "R2 Score (Test) : 0.7406\n",
461
+ "\n",
462
+ "=== RMSE ===\n",
463
+ "RMSE : 273.23\n",
464
+ "Prix moyen : 121.21 €\n",
465
+ "Median AE : 6.44 €\n",
466
+ "\n",
467
+ "Le modèle XGBoost améliore le R2 test avec 0.74, cependant il overfite avec un train à 0.98 (écart à 0.16)\n"
468
+ ]
469
+ }
470
+ ],
471
+ "source": [
472
+ "# Target et features\n",
473
+ "target = \"rental_price_per_day\"\n",
474
+ "X = df.drop(columns=target)\n",
475
+ "y = df[target]\n",
476
+ "\n",
477
+ "# Split train/test\n",
478
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
479
+ "\n",
480
+ "# Colonnes numériques et catégorielles\n",
481
+ "numerical_columns = ['mileage', 'engine_power']\n",
482
+ "categorical_columns = ['model_key', 'fuel', 'paint_color', 'car_type',\n",
483
+ " 'private_parking_available', 'has_gps', 'has_air_conditioning',\n",
484
+ " 'automatic_car', 'has_getaround_connect', 'has_speed_regulator', 'winter_tires']\n",
485
+ "\n",
486
+ "# Prétraitement numérique\n",
487
+ "numeric_transformer = Pipeline(steps=[\n",
488
+ " (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
489
+ " (\"scaler\", StandardScaler())\n",
490
+ "])\n",
491
+ "\n",
492
+ "# Prétraitement catégoriel\n",
493
+ "categorical_transformer = Pipeline(steps=[\n",
494
+ " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
495
+ " (\"onehot\", OneHotEncoder(handle_unknown=\"ignore\"))\n",
496
+ "])\n",
497
+ "\n",
498
+ "# Combine transformations\n",
499
+ "preprocessor = ColumnTransformer(transformers=[\n",
500
+ " (\"num\", numeric_transformer, numerical_columns),\n",
501
+ " (\"cat\", categorical_transformer, categorical_columns)\n",
502
+ "])\n",
503
+ "\n",
504
+ "# Modèle XGBoost\n",
505
+ "xgb_model = xgb.XGBRegressor(\n",
506
+ " n_estimators=200, # nombre d'arbres\n",
507
+ " max_depth=9, # profondeur maximale\n",
508
+ " learning_rate=0.1, # taux d'apprentissage : Contrôle combien chaque arbre corrige l’erreur du précédent. Plus petit = apprentissage plus lent et stable.\n",
509
+ " subsample=0.8, # échantillonnage pour régularisation : fraction des échantillons utilisés pour chaque arbre.Introduit un peu d’aléatoire pour réduire l’overfitting.\n",
510
+ ")\n",
511
+ "\n",
512
+ "# Pipeline complet\n",
513
+ "pipeline = Pipeline(steps=[\n",
514
+ " (\"preprocessor\", preprocessor),\n",
515
+ " (\"regressor\", xgb_model)\n",
516
+ "])\n",
517
+ "\n",
518
+ "# Entraînement\n",
519
+ "pipeline.fit(X_train, y_train)\n",
520
+ "\n",
521
+ "# Prédiction\n",
522
+ "y_train_pred = pipeline.predict(X_train)\n",
523
+ "y_test_pred = pipeline.predict(X_test)\n",
524
+ "\n",
525
+ "# Évaluation\n",
526
+ "print(\"=== Score R2 ===\")\n",
527
+ "print(f\"R2 Score (Train): {r2_score(y_train, y_train_pred):.4f}\")\n",
528
+ "print(f\"R2 Score (Test) : {r2_score(y_test, y_test_pred):.4f}\\n\")\n",
529
+ "\n",
530
+ "print(\"=== RMSE ===\")\n",
531
+ "rmse = mean_squared_error(y_test, y_test_pred)\n",
532
+ "print(f\"RMSE : {rmse:.2f}\")\n",
533
+ "\n",
534
+ "mean_price = df[\"rental_price_per_day\"].mean()\n",
535
+ "print(f\"Prix moyen : {mean_price:.2f} €\")\n",
536
+ "\n",
537
+ "medae = median_absolute_error(y_test, y_test_pred)\n",
538
+ "print(f\"Median AE : {medae:.2f} €\")\n",
539
+ "\n",
540
+ "print(\"\\nLe modèle XGBoost améliore le R2 test avec 0.74, cependant il overfite avec un train à 0.98 (écart à 0.16)\")\n",
541
+ "\n"
542
+ ]
543
+ },
544
+ {
545
+ "cell_type": "markdown",
546
+ "id": "820ec65a",
547
+ "metadata": {},
548
+ "source": [
549
+ "### Modéle XGBoost + GridSearch\n",
550
+ "---\n",
551
+ "#### GridSearchCV est un outil qui permet de chercher les meilleures combinaisons de paramètres pour un modèle, afin d’optimiser sa performance. Après avoir entraîné le GridSearch, je récupère best_estimator_, qui contient le pipeline complet optimisé, et je le sauvegarde immédiatement avec joblib.dump. Cela me permet de le recharger plus tard pour prédire sur de nouvelles données sans refaire le prétraitement ni réentraîner le modèle"
552
+ ]
553
+ },
554
+ {
555
+ "cell_type": "markdown",
556
+ "id": "04e2ae9c",
557
+ "metadata": {},
558
+ "source": []
559
+ },
560
+ {
561
+ "cell_type": "code",
562
+ "execution_count": 7,
563
+ "id": "3f8b0cbe",
564
+ "metadata": {},
565
+ "outputs": [
566
+ {
567
+ "name": "stdout",
568
+ "output_type": "stream",
569
+ "text": [
570
+ "Fitting 3 folds for each of 8 candidates, totalling 24 fits\n",
571
+ "Meilleurs paramètres : {'regressor__learning_rate': 0.05, 'regressor__max_depth': 4, 'regressor__n_estimators': 200, 'regressor__subsample': 0.8}\n",
572
+ "Meilleur R2 CV : 0.7517865300178528\n",
573
+ "✅ Modèle sauvegardé sous : modele_xgb_getaround.pkl\n",
574
+ "=== Score R2 ===\n",
575
+ "R2 Score (Train): 0.8234\n",
576
+ "R2 Score (Test) : 0.7470\n",
577
+ "\n",
578
+ "=== RMSE ===\n",
579
+ "RMSE : 266.42\n",
580
+ "Prix moyen : 121.21 €\n",
581
+ "Median AE : 6.84 €\n",
582
+ "\n",
583
+ "Après optimisation via GridSearch, le modèle présente une meilleure performance avec un R² test de 0.75,\n",
584
+ "tout en réduisant l’overfitting avec le R² train de 0.82 (écart de 0.07).\n",
585
+ "Le modèle est ainsi plus robuste et stable.\n"
586
+ ]
587
+ }
588
+ ],
589
+ "source": [
590
+ "# Target et features\n",
591
+ "target = \"rental_price_per_day\"\n",
592
+ "X = df.drop(columns=target)\n",
593
+ "y = df[target]\n",
594
+ "\n",
595
+ "# Split train/test\n",
596
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
597
+ "\n",
598
+ "# Colonnes numériques et catégorielles\n",
599
+ "numerical_columns = ['mileage', 'engine_power']\n",
600
+ "categorical_columns = ['model_key', 'fuel', 'paint_color', 'car_type',\n",
601
+ " 'private_parking_available', 'has_gps', 'has_air_conditioning',\n",
602
+ " 'automatic_car', 'has_getaround_connect', 'has_speed_regulator', 'winter_tires']\n",
603
+ "\n",
604
+ "# Prétraitement numérique\n",
605
+ "numeric_transformer = Pipeline(steps=[\n",
606
+ " (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
607
+ " (\"scaler\", StandardScaler())\n",
608
+ "])\n",
609
+ "\n",
610
+ "# Prétraitement catégoriel\n",
611
+ "categorical_transformer = Pipeline(steps=[\n",
612
+ " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n",
613
+ " (\"onehot\", OneHotEncoder(handle_unknown=\"ignore\"))\n",
614
+ "])\n",
615
+ "\n",
616
+ "# Combine transformations\n",
617
+ "preprocessor = ColumnTransformer(transformers=[\n",
618
+ " (\"num\", numeric_transformer, numerical_columns),\n",
619
+ " (\"cat\", categorical_transformer, categorical_columns)\n",
620
+ "])\n",
621
+ "\n",
622
+ "# Modèle XGBoost\n",
623
+ "xgb_model = xgb.XGBRegressor(\n",
624
+ " random_state=42,\n",
625
+ " tree_method=\"hist\"\n",
626
+ ")\n",
627
+ "\n",
628
+ "# Pipeline complet\n",
629
+ "pipeline = Pipeline(steps=[\n",
630
+ " (\"preprocessor\", preprocessor),\n",
631
+ " (\"regressor\", xgb_model)\n",
632
+ "])\n",
633
+ "\n",
634
+ "# Paramètres pour GridSearch (réduits pour éviter surcharge)\n",
635
+ "param_grid = {\n",
636
+ " \"regressor__max_depth\": [3, 4],\n",
637
+ " \"regressor__learning_rate\": [0.05, 0.1],\n",
638
+ " \"regressor__n_estimators\": [100, 200],\n",
639
+ " \"regressor__subsample\": [0.8],\n",
640
+ "}\n",
641
+ "\n",
642
+ "# GridSearch avec 3-fold CV pour limiter le temps\n",
643
+ "grid_search = GridSearchCV(\n",
644
+ " pipeline,\n",
645
+ " param_grid,\n",
646
+ " cv=3,\n",
647
+ " scoring=\"r2\",\n",
648
+ " n_jobs=-1,\n",
649
+ " verbose=2\n",
650
+ ")\n",
651
+ "\n",
652
+ "# Entraînement GridSearch\n",
653
+ "grid_search.fit(X_train, y_train)\n",
654
+ "\n",
655
+ "# Meilleurs paramètres et score CV\n",
656
+ "print(\"Meilleurs paramètres : \", grid_search.best_params_)\n",
657
+ "print(\"Meilleur R2 CV : \", grid_search.best_score_)\n",
658
+ "\n",
659
+ "# Sauvegarde du pipeline complet\n",
660
+ "# On récupère le pipeline optimisé (prétraitement + XGBoost avec les meilleurs hyperparamètres)\n",
661
+ "best_model = grid_search.best_estimator_\n",
662
+ "\n",
663
+ "# Sauvegarde au format .pkl\n",
664
+ "joblib.dump(best_model, \"modele_xgb_getaround.pkl\")\n",
665
+ "print(\"✅ Modèle sauvegardé sous : modele_xgb_getaround.pkl\")\n",
666
+ "\n",
667
+ "\n",
668
+ "# Prédictions avec le meilleur modèle\n",
669
+ "y_train_pred = grid_search.best_estimator_.predict(X_train)\n",
670
+ "y_test_pred = grid_search.best_estimator_.predict(X_test)\n",
671
+ "\n",
672
+ "# Évaluation\n",
673
+ "print(\"=== Score R2 ===\")\n",
674
+ "print(f\"R2 Score (Train): {r2_score(y_train, y_train_pred):.4f}\")\n",
675
+ "print(f\"R2 Score (Test) : {r2_score(y_test, y_test_pred):.4f}\\n\")\n",
676
+ "\n",
677
+ "print(\"=== RMSE ===\")\n",
678
+ "rmse = mean_squared_error(y_test, y_test_pred)\n",
679
+ "print(f\"RMSE : {rmse:.2f}\")\n",
680
+ "\n",
681
+ "mean_price = df[\"rental_price_per_day\"].mean()\n",
682
+ "print(f\"Prix moyen : {mean_price:.2f} €\")\n",
683
+ "\n",
684
+ "medae = median_absolute_error(y_test, y_test_pred)\n",
685
+ "print(f\"Median AE : {medae:.2f} €\")\n",
686
+ "\n",
687
+ "print(\"\\nAprès optimisation via GridSearch, le modèle présente une meilleure performance avec un R² test de 0.75,\\ntout en réduisant l’overfitting avec le R² train de 0.82 (écart de 0.07).\\nLe modèle est ainsi plus robuste et stable.\")\n"
688
+ ]
689
+ },
690
+ {
691
+ "cell_type": "markdown",
692
+ "id": "ee3e772b",
693
+ "metadata": {},
694
+ "source": [
695
+ "### Enregistrement du modèle et des métriques dans MLflow\n",
696
+ "---\n",
697
+ "### Après la recherche d’hyperparamètres avec GridSearch, j’ai récupéré grid_search.best_estimator_, qui contient le pipeline complet (prétraitement + XGBoost optimisé). Je l’ai sauvegardé avec joblib.dump. Ensuite, dans mon application, je recharge ce fichier avec joblib.load, ce qui me permet de prédire directement sur de nouvelles données sans refaire tout le preprocessing. »"
698
+ ]
699
+ },
700
+ {
701
+ "cell_type": "code",
702
+ "execution_count": 8,
703
+ "id": "00be5745",
704
+ "metadata": {},
705
+ "outputs": [],
706
+ "source": [
707
+ "# # Démarre une session MLflow\n",
708
+ "# with mlflow.start_run():\n",
709
+ "# # Enregistre le pipeline complet\n",
710
+ "# mlflow.sklearn.log_model(pipeline, \"random_forest_model\")\n",
711
+ " \n",
712
+ "# # Log des métriques calculées\n",
713
+ "# mlflow.log_metric(\"R2_train\", r2_score(y_train, y_train_pred))\n",
714
+ "# mlflow.log_metric(\"R2_test\", r2_score(y_test, y_test_pred))\n",
715
+ "# mlflow.log_metric(\"RMSE\", mean_squared_error(y_test, y_test_pred)) # RMSE = sqrt(MSE)\n",
716
+ "# mlflow.log_metric(\"MedianAE\", median_absolute_error(y_test, y_test_pred))\n",
717
+ " \n",
718
+ "# print(\"Modèle et métriques enregistrés dans MLflow\")\n"
719
+ ]
720
+ }
721
+ ],
722
+ "metadata": {
723
+ "kernelspec": {
724
+ "display_name": "ml_deployment",
725
+ "language": "python",
726
+ "name": "python3"
727
+ },
728
+ "language_info": {
729
+ "codemirror_mode": {
730
+ "name": "ipython",
731
+ "version": 3
732
+ },
733
+ "file_extension": ".py",
734
+ "mimetype": "text/x-python",
735
+ "name": "python",
736
+ "nbconvert_exporter": "python",
737
+ "pygments_lexer": "ipython3",
738
+ "version": "3.11.13"
739
+ }
740
+ },
741
+ "nbformat": 4,
742
+ "nbformat_minor": 5
743
+ }
model/modele_xgb_getaround.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9d920e73b91746406ffe2eaae3c007cca9daaeb3d6f889a7a731711d0218a62c
3
+ size 330597
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ pandas
4
+ scikit-learn
5
+ joblib
6
+ xgboost