path = window.location.pathname.replace(".html", ".qmd");
path_modified = (path.includes('en/content')) ? path.replace('en/content', 'content/en') : path
html`${printBadges({fpath: path_modified})}`
html`<div>${getConditionalHTML(path, true)}</div>`
function getConditionalHTML(path, print) {
if (print === false) return ``
if (isBetweenSeptAndDec()) {
return md`<i>La correction sera visible prochainement sur cette page. En attendant, la liste des corrections déjà acccessibles est [ici](/content/annexes/corrections.html)</i>`; // Return an empty string if not between the dates
else {
} return html`
<details>
<summary>
Pour ouvrir la version corrigée sous forme de <i>notebook</i>
</summary>
${printBadges({ fpath: path, correction: true })}
</details>
`;
} }
function renderCorrection({ fpath, correction }) {
if (correction) {
return html`${printBadges({ fpath: fpath, correction: true })}`;
else {
} return html`<i>La correction sera visible prochainement sur cette page.</i>`;
} }
Ce chapitre présente la deuxième application d’une journée de cours que j’ai donné à l’Université Dauphine dans le cadre des PSL Data Week.
L’objectif de ce chapitre est d’amener à développer une API du type de celle-ci.
Dérouler les slides associées ci-dessous ou cliquer ici pour les afficher en plein écran.
Le chapitre précédent constituait une introduction à la création de pipelines de machine learning. Ce chapitre va aller plus loin en montrant la démarche pour le rendre disponible à plus grande échelle par le biais d’une API pouvant être consommée avec de nouvelles données. L’objectif de celle-ci est de ne pas contraindre les réutilisateurs d’un modèle à disposer d’un environnement technique complexe pour pouvoir utiliser le même modèle que celui entraîné précédemment.
1 Exemple de réutilisation d’un modèle sous forme d’API
Un exemple d’API obtenue à l’issue de ce chapitre est mis à disposition sur https://dvf-simple-api.lab.sspcloud.fr/. La documentation de l’API est disponible ici.
Cette API est utilisable dans plusieurs langages.
En Python
, par exemple, cela donnera:
import requests
= 6
pieces_principales = 50
surface = f"https://dvf-simple-api.lab.sspcloud.fr/predict?month=4&nombre_lots=1&code_type_local=2&nombre_pieces_principales={pieces_principales}&surface={surface}"
url requests.get(url).json()
728358.5461884077
Néanmoins, l’un des intérêts de proposer une API est que les utilisateurs du modèle ne sont pas obligés d’être des pythonistes. Cela accroît grandement la cible des ré-utilisateurs potentiels.
Cette approche ouvre notamment la possibilité de
faire des applications interactives qui utilisent,
en arrière plan, notre modèle entraîné avec Python
.
Voici un exemple, minimaliste, d’une réutilisation de notre modèle avec deux sélecteurs Javascript qui mettent à jour le prix estimé du bien.
2 Etape 1: créer une application en local
Mettre en place une API consiste à gravir une marche dans l’échelle de la reproductibilité par rapport à fournir un notebook. Ces derniers ne sont pas les outils les plus adaptés pour partager autre chose que du code, à faire tourner de son côté.
Il est donc naturel de sortir des notebooks
lorsqu’on commence à aller vers ce niveau de mise à
disposition.
Par le biais de
scripts Python
lancés en ligne de commande,
construits en exportant le code du chapitre précédent
de nos notebooks, on pourra
créer une base de départ propre.
Il est plus naturel de privilégier une interface de développement généraliste comme VSCode à Jupyter lorsqu’on franchit ce rubicon. L’exercice suivant permettra donc de créer cette première application minimale, à exécuter en ligne de commande.
Exercice 1: créer des scripts pour entraîner le modèle
Le dépôt Github
qui permet de construire l’API from scratch
est disponible ici.
Nous allons emprunter quelques éléments, par-ci par-là,
pour faire notre application en local.
- Créer un nouveau service
VSCode
sur leSSPCloud
en paramétrant dans l’ongletNetworking
le port 5000 ; - Utiliser la commande suivante depuis le terminal:
mkdir app
cd app
Depuis le menu des fichiers, créer quatre fichiers dont le contenu suit:
requirements.txt
: récupérer le contenu sur cette page ;getdvf.py
: récupérer le contenu sur cette page ;train.py
: récupérer le contenu sur cette page ;api.py
: récupérer le contenu sur cette page.Exécuter
getdvf.py
puistrain.py
pour stocker en local le modèle entraînéAjouter
model.joblib
au.gitignore
(si vous utilisezGit
)Créer un script
test.py
qui contient la fonction suivante et la teste après avoir importé votre modèle (load('pipe.joblib')
en n’oubliant pasfrom joblib import load
):
import pandas as pd
def predict(
int = 3,
month: int = 1,
nombre_lots: int = 2,
code_type_local: int = 3,
nombre_pieces_principales: float = 75,
surface: -> float:
) """ """
= pd.DataFrame(
df
{"month": [month],
"Nombre_de_lots": [nombre_lots],
"Code_type_local": [code_type_local],
"Nombre_pieces_principales": [nombre_pieces_principales],
"surface": [surface],
}
)
= model.predict(df)
prediction
return prediction
3 Etape 2: créer une API en local
Le script précédent constitue déjà un progrès dans
la reproductibilité. Il rend plus facile le réentraînement
d’un modèle sur le même jeu de données. Néanmoins,
il reste tributaire du fait que la personne désirant
utiliser du modèle utilise Python
et sache réentrainer
le modèle dans les mêmes conditions que vous.
Avec FastAPI
, nous allons très facilement pouvoir
transformer cette application Python
en une API.
Exercice 2: créer des scripts pour entraîner le modèle
- La ligne ci-dessous du script
api.py
récupère un modèle pré-entraîné enregistré sur un espace de stockage
download_file("https://minio.lab.sspcloud.fr/projet-formation/diffusion/python-datascientist/pipe.joblib",
"pipe.joblib",
)
Retirer cette ligne de votre script, pour utiliser le modèle que vous venez d’entraîner.
- Déployer en local l’API avec la commande
uvicorn api:app --reload --host "0.0.0.0" --port 5000
- A partir du
README
du service VSCode, se rendre sur l’URL de déploiement, ajouter/docs/
à celui-ci et observer la documentation de l’API - Se servir de la documentation pour tester les requêtes
/predict
- Récupérer l’URL d’une des requêtes proposées. La tester dans le navigateur
et depuis
Python
avecRequests
(requests.get(url).json()
) - Optionnel: faire tourner le même code dans un autre environnement que le SSPCloud (par exemple une installation de
Python
en local) pour voir que ça fonctionne de manière identique.
4 Aller plus loin: mettre à disposition cette API de manière pérenne
L’étape précédente permettait de créer un point d’accès
à votre modèle depuis n’importe quel type de client. A chaque
requête de l’API, le script api.py
était exécuté et
renvoyait son output.
Ceci est déjà un saut de géant dans l’échelle de la reproductibilité. Néanmoins, cela reste artisanal: si votre serveur local connait un problème (par exemple, vous killez l’application), les clients ne recevront plus de réponse, sans comprendre pourquoi.
Il est donc plus fiable de mettre en production sur des serveurs dédiés, qui tournent 24h/24 et qui peuvent également se répartir la charge de travail s’il y a beaucoup de demandes instantanées.
Ceci dépasse néanmoins le cadre de ce cours et sera l’objet d’un cours dédié en 3e année de l’ENSAE: “Mise en production de projets data science” donné par Romain Avouac et moi.
Informations additionnelles
environment files have been tested on.
Latest built version: 2024-11-20
Python version used:
'3.12.6 | packaged by conda-forge | (main, Sep 30 2024, 18:08:52) [GCC 13.3.0]'
Package | Version |
---|---|
affine | 2.4.0 |
aiobotocore | 2.15.1 |
aiohappyeyeballs | 2.4.3 |
aiohttp | 3.10.8 |
aioitertools | 0.12.0 |
aiosignal | 1.3.1 |
alembic | 1.13.3 |
altair | 5.4.1 |
aniso8601 | 9.0.1 |
annotated-types | 0.7.0 |
appdirs | 1.4.4 |
archspec | 0.2.3 |
asttokens | 2.4.1 |
attrs | 24.2.0 |
babel | 2.16.0 |
bcrypt | 4.2.0 |
beautifulsoup4 | 4.12.3 |
black | 24.8.0 |
blinker | 1.8.2 |
blis | 0.7.11 |
bokeh | 3.5.2 |
boltons | 24.0.0 |
boto3 | 1.35.23 |
botocore | 1.35.23 |
branca | 0.7.2 |
Brotli | 1.1.0 |
cachetools | 5.5.0 |
cartiflette | 0.0.2 |
Cartopy | 0.24.1 |
catalogue | 2.0.10 |
cattrs | 24.1.2 |
certifi | 2024.8.30 |
cffi | 1.17.1 |
charset-normalizer | 3.3.2 |
click | 8.1.7 |
click-plugins | 1.1.1 |
cligj | 0.7.2 |
cloudpathlib | 0.20.0 |
cloudpickle | 3.0.0 |
colorama | 0.4.6 |
comm | 0.2.2 |
commonmark | 0.9.1 |
conda | 24.9.1 |
conda-libmamba-solver | 24.7.0 |
conda-package-handling | 2.3.0 |
conda_package_streaming | 0.10.0 |
confection | 0.1.5 |
contextily | 1.6.2 |
contourpy | 1.3.0 |
cryptography | 43.0.1 |
cycler | 0.12.1 |
cymem | 2.0.8 |
cytoolz | 1.0.0 |
dask | 2024.9.1 |
dask-expr | 1.1.15 |
databricks-sdk | 0.33.0 |
debugpy | 1.8.6 |
decorator | 5.1.1 |
Deprecated | 1.2.14 |
diskcache | 5.6.3 |
distributed | 2024.9.1 |
distro | 1.9.0 |
docker | 7.1.0 |
duckdb | 0.10.1 |
en-core-web-sm | 3.7.1 |
entrypoints | 0.4 |
et_xmlfile | 2.0.0 |
exceptiongroup | 1.2.2 |
executing | 2.1.0 |
fastexcel | 0.11.6 |
fastjsonschema | 2.20.0 |
fiona | 1.10.1 |
Flask | 3.0.3 |
folium | 0.17.0 |
fontawesomefree | 6.6.0 |
fonttools | 4.54.1 |
frozendict | 2.4.4 |
frozenlist | 1.4.1 |
fsspec | 2023.12.2 |
funcy | 2.0 |
gensim | 4.3.2 |
geographiclib | 2.0 |
geopandas | 1.0.1 |
geoplot | 0.5.1 |
geopy | 2.4.1 |
gitdb | 4.0.11 |
GitPython | 3.1.43 |
google-auth | 2.35.0 |
graphene | 3.3 |
graphql-core | 3.2.4 |
graphql-relay | 3.2.0 |
graphviz | 0.20.3 |
great-tables | 0.12.0 |
greenlet | 3.1.1 |
gunicorn | 22.0.0 |
h2 | 4.1.0 |
hpack | 4.0.0 |
htmltools | 0.6.0 |
hyperframe | 6.0.1 |
idna | 3.10 |
imageio | 2.36.0 |
importlib_metadata | 8.5.0 |
importlib_resources | 6.4.5 |
inflate64 | 1.0.0 |
ipykernel | 6.29.5 |
ipython | 8.28.0 |
itsdangerous | 2.2.0 |
jedi | 0.19.1 |
Jinja2 | 3.1.4 |
jmespath | 1.0.1 |
joblib | 1.4.2 |
jsonpatch | 1.33 |
jsonpointer | 3.0.0 |
jsonschema | 4.23.0 |
jsonschema-specifications | 2024.10.1 |
jupyter-cache | 1.0.0 |
jupyter_client | 8.6.3 |
jupyter_core | 5.7.2 |
kaleido | 0.2.1 |
kiwisolver | 1.4.7 |
langcodes | 3.5.0 |
language_data | 1.3.0 |
lazy_loader | 0.4 |
libmambapy | 1.5.9 |
locket | 1.0.0 |
lxml | 5.3.0 |
lz4 | 4.3.3 |
Mako | 1.3.5 |
mamba | 1.5.9 |
mapclassify | 2.8.1 |
marisa-trie | 1.2.1 |
Markdown | 3.6 |
markdown-it-py | 3.0.0 |
MarkupSafe | 2.1.5 |
matplotlib | 3.9.2 |
matplotlib-inline | 0.1.7 |
mdurl | 0.1.2 |
menuinst | 2.1.2 |
mercantile | 1.2.1 |
mizani | 0.11.4 |
mlflow | 2.16.2 |
mlflow-skinny | 2.16.2 |
msgpack | 1.1.0 |
multidict | 6.1.0 |
multivolumefile | 0.2.3 |
munkres | 1.1.4 |
murmurhash | 1.0.10 |
mypy-extensions | 1.0.0 |
narwhals | 1.14.1 |
nbclient | 0.10.0 |
nbformat | 5.10.4 |
nest_asyncio | 1.6.0 |
networkx | 3.3 |
nltk | 3.9.1 |
numexpr | 2.10.1 |
numpy | 1.26.4 |
opencv-python-headless | 4.10.0.84 |
openpyxl | 3.1.5 |
opentelemetry-api | 1.16.0 |
opentelemetry-sdk | 1.16.0 |
opentelemetry-semantic-conventions | 0.37b0 |
OWSLib | 0.28.1 |
packaging | 24.1 |
pandas | 2.2.3 |
paramiko | 3.5.0 |
parso | 0.8.4 |
partd | 1.4.2 |
pathspec | 0.12.1 |
patsy | 0.5.6 |
Pebble | 5.0.7 |
pexpect | 4.9.0 |
pickleshare | 0.7.5 |
pillow | 10.4.0 |
pip | 24.2 |
platformdirs | 4.3.6 |
plotly | 5.24.1 |
plotnine | 0.13.6 |
pluggy | 1.5.0 |
polars | 1.8.2 |
preshed | 3.0.9 |
prometheus_client | 0.21.0 |
prometheus_flask_exporter | 0.23.1 |
prompt_toolkit | 3.0.48 |
protobuf | 4.25.3 |
psutil | 6.0.0 |
ptyprocess | 0.7.0 |
pure_eval | 0.2.3 |
py7zr | 0.20.8 |
pyarrow | 17.0.0 |
pyarrow-hotfix | 0.6 |
pyasn1 | 0.6.1 |
pyasn1_modules | 0.4.1 |
pybcj | 1.0.2 |
pycosat | 0.6.6 |
pycparser | 2.22 |
pycryptodomex | 3.21.0 |
pydantic | 2.9.2 |
pydantic_core | 2.23.4 |
Pygments | 2.18.0 |
pyLDAvis | 3.4.1 |
PyNaCl | 1.5.0 |
pynsee | 0.1.8 |
pyogrio | 0.10.0 |
pyOpenSSL | 24.2.1 |
pyparsing | 3.1.4 |
pyppmd | 1.1.0 |
pyproj | 3.7.0 |
pyshp | 2.3.1 |
PySocks | 1.7.1 |
python-dateutil | 2.9.0 |
python-dotenv | 1.0.1 |
python-magic | 0.4.27 |
pytz | 2024.1 |
pyu2f | 0.1.5 |
pywaffle | 1.1.1 |
PyYAML | 6.0.2 |
pyzmq | 26.2.0 |
pyzstd | 0.16.2 |
querystring_parser | 1.2.4 |
rasterio | 1.4.2 |
referencing | 0.35.1 |
regex | 2024.9.11 |
requests | 2.32.3 |
requests-cache | 1.2.1 |
retrying | 1.3.4 |
rich | 13.9.4 |
rpds-py | 0.21.0 |
rsa | 4.9 |
ruamel.yaml | 0.18.6 |
ruamel.yaml.clib | 0.2.8 |
s3fs | 2023.12.2 |
s3transfer | 0.10.2 |
scikit-image | 0.24.0 |
scikit-learn | 1.5.2 |
scipy | 1.13.0 |
seaborn | 0.13.2 |
setuptools | 74.1.2 |
shapely | 2.0.6 |
shellingham | 1.5.4 |
six | 1.16.0 |
smart-open | 7.0.5 |
smmap | 5.0.0 |
sortedcontainers | 2.4.0 |
soupsieve | 2.5 |
spacy | 3.7.5 |
spacy-legacy | 3.0.12 |
spacy-loggers | 1.0.5 |
SQLAlchemy | 2.0.35 |
sqlparse | 0.5.1 |
srsly | 2.4.8 |
stack-data | 0.6.2 |
statsmodels | 0.14.4 |
tabulate | 0.9.0 |
tblib | 3.0.0 |
tenacity | 9.0.0 |
texttable | 1.7.0 |
thinc | 8.2.5 |
threadpoolctl | 3.5.0 |
tifffile | 2024.9.20 |
toolz | 1.0.0 |
topojson | 1.9 |
tornado | 6.4.1 |
tqdm | 4.66.5 |
traitlets | 5.14.3 |
truststore | 0.9.2 |
typer | 0.13.1 |
typing_extensions | 4.12.2 |
tzdata | 2024.2 |
Unidecode | 1.3.8 |
url-normalize | 1.4.3 |
urllib3 | 1.26.20 |
wasabi | 1.1.3 |
wcwidth | 0.2.13 |
weasel | 0.4.1 |
webdriver-manager | 4.0.2 |
websocket-client | 1.8.0 |
Werkzeug | 3.0.4 |
wheel | 0.44.0 |
wordcloud | 1.9.3 |
wrapt | 1.16.0 |
xgboost | 2.1.1 |
xlrd | 2.0.1 |
xyzservices | 2024.9.0 |
yarl | 1.13.1 |
yellowbrick | 1.5 |
zict | 3.0.0 |
zipp | 3.20.2 |
zstandard | 0.23.0 |
View file history
SHA | Date | Author | Description |
---|---|---|---|
a3dc832 | 2024-06-24 16:15:19 | Lino Galiana | Improve homepage images (#508) |
06d003a | 2024-04-23 10:09:22 | Lino Galiana | Continue la restructuration des sous-parties (#492) |
005d89b | 2023-12-20 17:23:04 | Lino Galiana | Finalise l’affichage des statistiques Git (#478) |
3fba612 | 2023-12-17 18:16:42 | Lino Galiana | Remove some badges from python (#476) |
1f23de2 | 2023-12-01 17:25:36 | Lino Galiana | Stockage des images sur S3 (#466) |
e4642ee | 2023-11-27 17:02:05 | Lino Galiana | Deploy ML model as API (#460) |
Citation
BibTeX
@book{galiana2023,
author = {Galiana, Lino},
title = {Python pour la data science},
date = {2023},
url = {https://pythonds.linogaliana.fr/},
doi = {10.5281/zenodo.8229676},
langid = {fr}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.