API permettant de prédire la catégorie d'un produit en fonction de sa désignation, description et d'une image.
E-COMMERCE-MLOPS/
├─ .devcontainer/ : fichiers du conteneur de dev
├─ .github/workflow/ : Github Actions
├─ .vscode/tasks.json : tâches du project global
├─ backend/ : Projet - API permettant aux utilisateurs de prédire la catégorie d'un produitg
├─ datascience/ : Projet - entraînement, tracking et mise à disposition des modèles de prédiction
├─ scripts/ : liste des scripts globaux du projet
│ ├─ ressources/ : contient les ressources nécessaires aux scripts
│ ├─ tests/ : sous-scripts utilisés par `run-tests.sh`
├─ dev.env : fichier de configuration de l'environnement de développement
├─ staging.env : fichier de configuration l'environnement de test
├─ template.env : version "de base" du fichier de configuration
.env
avec les informations du projet..env
, vérifier que la valeur de la variable TARGET_ENV
est renseignée.scripts/docker-deploy.sh
pour créer les conteneurs.docker compose -f docker-compose.yaml up -d
Utilisation de la machine locale pour le développement permettant l'utilisation du GPU pour les tâches Tensorflow. Actuellement configuré pour les utilisateurs de MacOS.
Prérequis :
Mise en place :
Cloner le repo :
git clone https://github.com/JoffreyLGT/e-commerce-mlops.git
Ouvrir VSCode
Cliquer sur File
, Open Workspace from File...
Sélectionner le fichier e-commerce-mlops.code-workspace
Ouvrir le fichier .env et vérifier que les variables d'environnement ci-dessous sont configurées avec ces valeurs :
USE_DB_CONTAINER=true
DB_SERVER=localhost
Une fois le projet ouvert, ouvrir un terminal dans le dossier root
, et saisir la commande ci-dessous :
./scripts/environment-setup.sh
Installer les extensions recommandées dans le workspace.
Utilisation d'un conteneur Docker possédant tous les outils de développement.
Voici les prérequis :
Une fois les prérequis installées, veuillez-suivre les étapes suivantes :
Cloner le repo :
git clone https://github.com/JoffreyLGT/e-commerce-mlops.git
Ouvrir le projet dans VSCode.
Ouvrir la palette des commandes (Cmd+Shift+p
).
Saisir dev open workspace
et sélectionner l'option Dev Containers: Open Workspace in Container...
.
Sélectionner le fichier e-commerce-mlops.code-workspace
.
La fenêtre de VSCode va se recharger et le conteneur de développement va se mettre en place.
Les extensions recommandées dans le workspace sont installées automatiquement. Cependant, VSCode peut afficher des notifications lors de leur installation, notamment Pylance indiquant que l'extension Python n'est pas détectée. Il faut simplement les fermer sans les prendre en compte.
Le workspace contient une liste d'extensions recommandées. Pour les installer, ouvrir la Command Palette (CMD + Shift + p
ou via le menu View > Command Palette...
) et rechercher Extensions: Show Recommended Extensions
.
Sur la ligne WORKSPACE RECOMMENDATION
, cliquer sur Install workspace Recommended Extensions
.
Cette section concerne les outils en général. Chaque sous-projet a sa propre section :
Docker stocke les images, containers et volumes dans un disque virtuel. Celui-ci a une taille définie dans les paramêtres de Docker Desktop.
Pour solutionner cette erreur, deux solutions :
Settings
.docker system prune -a --volumes
Ouvrir le moniteur d'activité pour repérer le processus utilisant le CPU.
S'il s'agit de code helper (plugin)
, noter le PID.
Quitter complètement VSCode (Cmd + Q
sur MacOS)
Si le processus disparait, relancer VSCode et attendre quelques minutes. Si le processus ne revient pas, tout est de nouveau en ordre. Sinon, continuer les étapes ci-dessous.
Ouvrir un terminal et saisir la commande suivante en replaçant 67377 par le PID noté précédemment :
ps aux | grep 67377
Le résultat de la commande à le format suivant :
joffrey 67377 110.4 8.1 1587151776 1351248 ?? R 4:35PM 29:40.37 [...] /Users/joffrey/.vscode/extensions/ms-python.vscode-pylance-2023.8.30/dist [...]
Note : [...] indique que le contenu a été tronqué.
En analysant la ligne, nous constatons la mention /.vscode/extensions/ms-python.vscode-pylance-2023.8.30/
: Pylance est donc le coupable.
Insérer la valeur ci-dessous en première ligne du fichier :
#:schema https://json.schemastore.org/ruff.json
# Add your values after
Nous utilisons principalement des machines ARM (Apple Silicon) pour le développement du projet. Afin de pouvoir tester sur des machines de type AMD64, nous utilisons le conteneur de développement.
Pour cela, il faut :
Modifier la première ligne pour y ajouter --platform=linux/amd64
comme ceci :
FROM --platform=linux/amd64 mcr.microsoft.com/devcontainers/python:1-3.11-bullseye
Un workspace VSCode est mis à disposition à la racine du projet sous le nom e-commerce-mlops.code-workspace. Il est fortement recommandé de l'utiliser.
Il s'agit d'un multi-root workspace configuré pour permettre un fonctionnement optimal des sous-projets. Chaque sous-projet contient des tâches VSCode permettant d'exécuter les fonctions principales.
Contient les fichiers commun à tout le projet, les sous-projets ainsi que des fichiers utilisés dans les sous-projets.
sequenceDiagram
box Local
participant A as Developer
participant B as Git hooks
end
box Server
participant C as Git repository<br/>Github
participant D as CI/CD<br/>Github Actions
end
A->>+B: Commit
alt Pre-commit
B-->>+B: Is formating good?
B-->>-A: No, auto-format some files
else Commit-msg
B-->>+B: Message respects<br/>convention?
B-->>-A: No, give reason<br/>Stop commit
end
B->>+A: Commit created
A->>-B: Push
alt Pre-push
B-->>+B: Run formatters
B-->>B: Run linters
B-->>B: Run type checkers
B-->>-A: Fail!
end
B->>C: Push
A->>+C: Create pull request
alt On pull-request
C->>-D: Trigger actions
D-->>+D: Run formatters
D-->>D: Run linters
D-->>D: Run type checkers
D-->>D: Run unit tests
D-->>-C: Send results
end
Attention, certains fichiers sont référencés comme symlinks dans les sous-projets: | File | Sub-projects |
---|---|---|
.env | backend, datascience | |
mypy.ini | backend, datascience | |
ruff.toml | backend, datascience |
Les tâches ci-dessous sont disponibles : | Nom | Description |
---|---|---|
Run all Git hooks | Permet de lancer tous les hook Git sans faire d'action Git. | |
Run Git pre-commit hooks | Permet de lancer les hook git de pre-commit sans avoir à faire de commit. | |
Run Git pre-push hooks | Permet de lancer les hook Git de pre-push sans avoir à faire de commit. |
Rien pour le moment.
Permet de lancer l'API de catégorisation des produits.
Le lancement de l'API en mode développement sur le conteneur se fait avec le script start-reload.sh
:
./scripts/start-reload.sh
VSCode s'occupe automatiquement de la redirection du port $BACKEND_FASTAPI_PORT. Ouvrir l’adresse ci-dessous dans un navigateur Web sur la machine hôte pour afficher la documentation :
http://localhost:$BACKEND_FASTAPI_PORT/docs
Une tâche portant le nom Start API in reload mode
permet de lancer l'API en mode développement.
Le monitoring est mise en place avec la librairie OpenTelemetry permettant l'envoi des évènements sur plusieurs solutions du marché. Dans ce projet, nous utilisons la version Open Source de SigNoz.
Pour démarrer l'application avec la télémétrie, il faut exécuter le script start-with-telemetry.sh
:
./scripts/start-with-telemetry.sh
A noter que Signoz doit être installé sur votre machine et connecté sur le même réseau que le devcontainer.
Les tests sont exécutés par Pytest. Ils se lancent soit via la fonction Testing de VSCode, soit en exécutant la commande suivante :
poetry run ./scripts/start-tests.sh
Les hook sont générés automatiquement dans le dossier .git/hooks par l'utilitaire pre-commit lors de l'exécution de scripts/environment-setup.sh. Ceux-ci ont été sélectionnés pour être rapides et le moins intrusifs possible pour ne pas géner le workflow des développeurs.
Le projet en utilise trois types :
Les tâches ci-dessous sont disponibles : | Nom | Description |
---|---|---|
Start API in reload mode | Lance l'API en mode reload, permettant de recharger automatiquement le code lorsqu'il est modifié. | |
Check Tensorflow GPU support | Affiche un message pour indiquer si Tensorflow supporte des GPU dans l'environnement. | |
Start PostgreSQL DB container | Démarre le conteneur db depuis le fichier docker-compose.yaml. | |
Stop PostgreSQL DB container | Stop le conteneur db démarré depuis la tâche Start PostgreSQL DB container. |
TLDR : Ajouter ou modifier les modèles SQLAlchemy dans
/app/models/
, les schemas Pydantic dans/app/schemas/
et les outils CRUD d'interaction avec la BDD dans/app/crud/
. Attention à bien mettre à jour les fichier__init__.py
de ces modules ! Pour terminer, générer la migration alembic. Utiliser prediction_feedback comme exemple.
Création du modèle SQLAlchemy
Créer un nouveau fichier {{tableobjet}}.py
dans le dossier/app/models
.
Définir la classe en utilisant SQLAlchemy et en la faisant hériter de la classeapp.database.base_class.Base
.
Importer la classe dans le fichier /app/models/__init__.py
. Cela permet d'avoir une meilleure syntaxe d'import dans les autres fichiers.
Importer la classe dans le fichier /app/database/base.py
. L'objectif ici est que la classe soit disponible lors de l'import de la classe Base dans la configuration alembic.
Création du schéma Pydantic
Créer un nouveau fichier {{tableobjet}}.py
dans le dossier /app/schemas
. Ouvrir le fichier /app/schemas/prediction_feedback.py
et copier son contenu dans le nouveau fichier créé. Changer les classes pour qu'elles correspondent aux données du nouvel objet.
Importer la classe dans le fichier /app/schemas/__init__.py
. Cela permet d'avoir une meilleure syntaxe d'import dans les autres fichiers.
Création du CRUD
Ce fichier va contenir les fonctions permettant d'interagir avec BDD.
Créer un nouveau fichier crud_{{tableobjet}}.py
dans le dossier /app/crud
. Ouvrir le fichier /app/crud/crud_prediction_feedback.py
et copier son contenu dans le nouveau fichier créé. Changer les types pour renseigner ceux précédemment créés.
Ne pas oublier de terminer le fichier par l'instanciation de la classe dans une variable.
Importer la variable dans le fichier /app/crud/__init__.py
. Cela permet d'avoir une meilleure syntaxe d'import dans les autres fichiers.
Génération de la migration alembic
Ouvrir un nouveau terminal et saisir la commande suivante :
alembic revision --autogenerate -m "{{description of what you did}}"
La migration s'appliquera automatiquement lors du prochain redémarrage du dev container. Pour l'appliquer directement et, donc, mettre à jour la BDD, saisir la commande suivante :
alembic upgrade head
Si besoin, il est possible de revenir en arrière sur les migrations en utilisant la commande :
alembic downgrade {{identifiant_révision}}
Ou de revenir en arrière sur toutes les migrations via la commande :
alembic downgrade base
En partant du principe que toutes les données ont été stockées dans le dossier data/datasets/original
avec la structure suivante :
original
├── X.csv
├── y.csv
└──── images
├── image_977803476_product_278535420.jpg
├── image_1174586892_product_2940638801.jpg
└── [...]
X.csv
contient les features, à savoir les informations sur le produit dans les colonnes désignation
, description
, productid
et imageid
.y.csv
contient la target, à savoir la catégorie du produit dans une colonne.images
contient toutes les images des produits. Les images doivent respecter la convention image_[imageid]_[productid].jpg
Pour créer un dataset (jeu de données) pour l'entrainement, utiliser le script create_datasets.py.
# Depuis le dossier e-commerce-mlops/datascience
python -m scripts.create_datasets --train-size 0.8 --test-size 0.2 --input-dir "data/originals" --output-dir "data/datasets/example"
Le script va s'occuper de traiter les images pour retirer les bandes blanches inutiles, les redimentionner et va créer les jeux de données d'entrainement (80%) et de test (20%) au format .csv
.
Le projet utilise 3 modèles (image, texte et fusion). De ce fait, 3 scripts sont mis à disposition pour l'entrainement :
Ceux-ci utilisent MLFlow pour le suivi des metrics.
Chaque script est documenté. Pour afficher l'aide, utiliser la commande --help
.
Exemple :
# Affiche l'aide du script train_image_model.py
python -m scripts.train_image_model --help
Ce qui affiche :
usage: train_image_model.py [-h] [--input-dir INPUT_DIR] [--output-dir OUTPUT_DIR] [--batch-size BATCH_SIZE]
[--no-data-augmentation] [--train-patience TRAIN_PATIENCE] [--epochs EPOCHS] [--set-staging]
[--registered-model REGISTERED_MODEL]
Create and train a new image model using dataset provided with --input-dir, then save it to --output-dir with its performance
statistics.
required arguments:
--input-dir INPUT_DIR
Directory containing the datasets to use.
--output-dir OUTPUT_DIR
Directory to save trained model and stats.
optional arguments:
--batch-size BATCH_SIZE
Size of the batches to use for the training. Set as much as your machine allows. (default: 96)
--no-data-augmentation
Add layers of data augmentation to avoid early overfitting. (default: True)
--train-patience TRAIN_PATIENCE
Number of epoch to do without improvements before stopping the model training. (default: 10)
--epochs EPOCHS Number of epochs to reach before stopping. Stops before if train-patience is reached. (default: 100)
--set-staging Set new model version status as staging for 'fusion' model (default: False)
--registered-model REGISTERED_MODEL
(default: image)
help:
-h, --help show this help message and exit
Dans un terminal, lancer l'interface graphique de MLFlow avec la commande suivante :
mlflow ui
Dans l'en-tête, cliquer sur Experiments
.
Rechercher le run du modèle à mettre en production et cliquer dessus.
Dans la liste des artifacts
, cliquer sur le modèle, puis sur Register Model
.
Dans la popup qui s'ouvre, sélectionner le modèle en question, puis cliquer sur Register
.
Dans l'en-tête, cliquer sur le modèle, puis sur la version.
Le champ Stage
permet de changer le statut du modèle (Staging
pour test, Production
pour production.
Utiliser la commande ci-dessous pour lancer le modèle :
# Remplacer image par le nom du modèle, et Staging par Production pour une mise en production
mlflow models serve -m "models:/fusion/Staging" --port 5002 --env-manager local
Rien pour le moment
Les PR doivent obligatoirement être liées à une issue et avoir une description permettant d'aider le validateur dans ses tests.
Nous utilisons la convention Conventional Commits.
Une extension VSCode est recommandée dans le workspace permettant de rédiger les messages façilement. Pour l'utiliser :
Cmd+Shift+p
).Conventional Commits
.Le message de commit sera alors automatiquement ajouté dans la vue Source control
.
Le dev container est configuré pour installer et configurer automatiquement les extensions VSCode.
Doivent être rédigés en anglais.
Parfois, Pylance indique des erreurs de type à cause de décorateurs des librairies. C'est le cas notamment de la propriété __tablename__
de SQLAchemy.
Pour ignorer ces erreurs, ajouter le commentaire suivant en bout de ligne :
__tablename__ = "prediction_feedback" # pyright: ignore
Nous utilisons la convention de Google expliquée ici.
L'extension autoDocstring, intégrée au dev container, permet de générer les templates de docstring via la commande (Cmd + Shift + p) Generate Docstring
.
Ruff effectue l'analyse de code statique du fichier en direct et fait des recommandations. Il combine des règles provenant de plusieurs autres linter du marché et réorganise les imports en utillisant les règles iSort.
Les configurations, incluant les règles activées, se trouvent dans le fichier des configuration Ruff : ruff.toml
.
Certaines recommandations peuvent être érronées. Pour les désactiver, consulter la page Ruff error suppression.
Il est recommandé d'ajouter un message pour expliquer pourquoi la règle est désactivée.
Black formate automatiquement le code lors de l'enregistrement du fichier.
Les configurations se trouvent dans les fichier pyproject.toml
de chaque sous-projet.
Mypy s'occupe de mettre en avant les potentiels problèmes liés aux types.
Il s'exécute à l'enregistrement du fichier.
Les configurations se trouvent dans le fichier mypy.ini
.