Cours
Imaginez la situation suivante :
Vous avez construit un modèle de machine learning ultra performant qui prédit si une transaction est frauduleuse ou non. Un·e ami·e développe une application Android pour des activités bancaires courantes et souhaite intégrer votre modèle dans son application pour atteindre cet objectif.
Problème : vous avez codé votre modèle en Python tandis que votre ami·e développe en Java. Alors ? Impossible d’intégrer votre modèle dans son application ?
Heureusement, vous avez le pouvoir des API. Et ce cas de figure est l’un des nombreux scénarios où transformer vos modèles de machine learning en API devient crucial. De plus en plus d’entreprises recherchent des Data Scientists capables de le faire. Emballer un modèle de machine learning dans une API n’a rien de sorcier, et c’est précisément ce que vous allez faire dans ce tutoriel : transformer votre modèle de machine learning en API.
Options pour déployer des modèles de machine learning
Le véritable usage d’un modèle de machine learning se trouve souvent au cœur d’un produit intelligent : un composant d’un système de recommandation, un chatbot, etc. C’est souvent à ce stade que les barrières techniques semblent les plus difficiles à franchir.
Par exemple, la majorité des praticiens ML utilisent R/Python pour leurs expérimentations. Mais les consommateurs de ces modèles sont souvent des ingénieurs logiciels avec une pile technologique totalement différente. Deux approches permettent de résoudre ce décalage :
- Réécrire tout le code dans le langage des équipes d’ingénierie. Cela peut paraître séduisant, mais le temps et l’énergie nécessaires pour répliquer des modèles complexes seraient largement gaspillés. La plupart des langages comme JavaScript ne disposent pas de bibliothèques ML très abouties. Mieux vaut éviter.
- Approche « API-first » : les API web facilitent grandement la collaboration entre langages. Si un développeur frontend a besoin d’utiliser votre modèle ML pour créer une application web dopée au ML, il lui suffit d’obtenir l’URL de l’endpoint d’où l’API est servie.
Avant d’aller plus loin, voyons ce qu’est réellement une API.
Qu’est-ce qu’une API ?
"En termes simples, une API est un contrat (hypothétique) entre deux logiciels : si le logiciel client fournit une entrée dans un format prédéfini, l’autre étend ses fonctionnalités et renvoie un résultat au logiciel client." - Analytics Vidhya
Pour comprendre pourquoi les API sont le choix privilégié des développeurs, lisez :
En substance, les API ressemblent à des applications web, mais au lieu de renvoyer une page HTML soignée, elles renvoient des données dans un format standard d’échange comme JSON, XML, etc. Une fois que le développeur dispose de la sortie souhaitée, il peut l’afficher comme il l’entend. Il existe aussi de nombreuses API ML populaires, par exemple l’API ML d’IBM Watson, capable de :
- Traduction automatique : traduire du texte entre différentes paires de langues.
- Message Resonance : estimer la popularité d’une phrase ou d’un mot auprès d’une audience donnée.
- Questions/Réponses : fournir des réponses directes à des requêtes à partir de sources documentaires principales.
- Modélisation utilisateur : prédire des caractéristiques sociales d’une personne à partir d’un texte donné.
Google Vision API est également un excellent exemple, avec des services dédiés aux tâches de vision par ordinateur. Cliquez ici pour voir ce qu’il est possible de faire avec Google Vision API.
Concrètement, la plupart des fournisseurs cloud et des sociétés spécialisées en machine learning proposent des API prêtes à l’emploi. Elles répondent aux besoins de développeurs/entreprises qui n’ont pas d’expertise ML mais souhaitent intégrer le ML dans leurs processus ou leurs produits.
Parmi les exemples d’API ML adaptées au développement web : DialogFlow, Microsoft's Cognitive Toolkit, TensorFlow.js, etc.
Maintenant que vous voyez ce qu’est une API, voyons comment emballer un modèle de machine learning (développé en Python) dans une API en Python.
Maîtrisez vos compétences en matière de données avec DataCamp
Plus de 10 millions de personnes apprennent Python, R, SQL et d'autres compétences techniques grâce à nos cours pratiques élaborés par des experts du secteur.

Flask – un framework de services web en Python :
Vous vous demandez peut-être ce qu’est un service web ? Un service web est un type d’API hébergée sur un serveur et consommable par des clients. Web API, Web Service : ces termes sont souvent utilisés indifféremment.
Flask est un framework de développement de services web en Python. Ce n’est pas le seul : Django, Falcon, Hug, etc., existent aussi. Mais vous utiliserez Flask pour ce tutoriel. Pour découvrir Flask, consultez ces tutoriels.
Si vous avez installé la distribution Anaconda, Flask est déjà présent. Sinon, installez-le avec :
pip install flask
Flask est très minimaliste. Les développeurs Python l’apprécient pour de nombreuses raisons. Il intègre un serveur web léger nécessitant très peu de configuration et contrôlable depuis votre code Python. C’est l’une des raisons de sa popularité.
Le code suivant illustre bien cette simplicité. Il crée une Web API basique qui, lorsqu’elle reçoit une URL donnée, produit une sortie spécifique.
from flask import Flask
app = Flask(__name__)
@app.route("")
def hello():
return "Welcome to machine learning model APIs!"
if __name__ == '__main__':
app.run(debug=True)
Une fois exécuté, allez à l’adresse web affichée dans le terminal (saisissez-la dans votre navigateur) et observez le résultat.

Quelques points à noter :
-
Jupyter Notebooks sont excellents pour le markdown, R et Python. Mais pour construire un serveur web, leur comportement peut être instable. Il est donc préférable d’écrire le code Flask dans un éditeur de texte comme Sublime et d’exécuter le code depuis le terminal/l’invite de commande.
-
Veillez à ne pas nommer votre fichier flask.py.
-
Par défaut, Flask tourne sur le port 5000. Parfois, le serveur démarre bien sur ce port mais, lorsque vous appelez l’URL (affichée dans le terminal) dans un navigateur ou un client API comme Postman, rien ne s’affiche. Par exemple :

-
Selon Flask, le serveur a démarré avec succès sur le port 5000, mais l’URL appelée dans le navigateur ne renvoie rien. Il peut s’agir d’un conflit de port. Dans ce cas, changez le port par défaut 5000 pour un autre, par exemple :
app.run(debug=True,port=12345) -
Le serveur Flask ressemblera alors à ceci :

Passons en revue le code que vous avez écrit :
-
Vous créez une instance de la classe
Flasken lui passant la variable « name » (renseignée automatiquement par Python). Cette variable vaut « main » si le fichier est exécuté directement comme script. Si le fichier est importé, la valeur de « name » sera le nom du fichier importé. Par exemple, si vous aveztest.pyetrun.pyet que vous importez test.py dans run.py, la valeur de « name » pour test.py sera test (app = Flask(test)). -
Au-dessus de la définition de
hello(), il y a @app.route("").route()est un décorateur qui indique à Flask quelle URL doit déclencher la fonctionhello(). -
La méthode
hello()renvoie une sortie (« Welcome to machine learning model APIs! ») à chaque fois que votre API est correctement appelée. Ici, accéder àlocalhost:5000/dans un navigateur produira la sortie attendue (si le serveur Flask tourne sur le port 5000).
Voyons maintenant quelques points à garder en tête lorsque vous transformez des modèles scikit-learn en API Flask.
Des modèles scikit-learn avec Flask
En Python, créer des modèles de machine learning du plus simple au plus complexe n’a jamais été aussi facile qu’avec scikit-learn. Mais voici quelques points à retenir :
- Scikit-learn est une bibliothèque Python qui fournit des outils simples et efficaces pour l’exploration de données et l’analyse. Ses principaux modules :
- Clustering
- Régression
- Classification
- Réduction de dimension
- Sélection de modèle
- Prétraitement
(N’hésitez pas à suivre le cours de DataCamp Supervised Learning with scikit-learn animé par un des développeurs principaux de scikit-learn, Andreas Müller)
- Scikit-learn prend en charge la sérialisation et la désérialisation des modèles que vous entraînez. Cela vous évite de réentraîner un modèle. Avec une copie sérialisée, vous pouvez exposer votre modèle via une API Flask.
- Les modèles scikit-learn exigent des données numériques. Si votre jeu de données contient des variables catégorielles non numériques, il faut les convertir. Pour cela, scikit-learn propose des utilitaires comme
LabelEncoder,OneHotEncoder, etc., dans le modulesklearn.preprocessing. - Les modèles scikit-learn ne gèrent pas nativement les valeurs manquantes. Vous devez les traiter avant d’entraîner le modèle. Pour cela, de nombreux utilitaires sont disponibles dans
sklearn.preprocessing.
Le codage des labels et la gestion des valeurs manquantes sont des étapes de prétraitement essentielles pour construire un bon modèle. Pour en savoir plus, consultez ce cours DataCamp :
Dans ce tutoriel, vous utiliserez le jeu de données Titanic, très populaire notamment parce qu’il contient différents types de variables et des valeurs manquantes. Ce tutoriel DataCamp en propose une excellente analyse, et vous pouvez télécharger les données ici.
Il s’agit d’un problème de classification : prédire si un passager a survécu ou non à partir de certaines informations le/la concernant.
Remarque : « Variables » et « features » sont utilisés de manière interchangeable dans ce tutoriel.
Pour simplifier, vous n’utiliserez que quatre variables : age, sex, embarked et survived, où survived est la variable cible.
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
« Sex » et « Embarked » sont des variables catégorielles non numériques, d’où la nécessité de les transformer. La variable « Age » comporte des valeurs manquantes. On peut les imputer avec une statistique de synthèse comme la médiane ou la moyenne. Les valeurs manquantes peuvent être riches d’enseignements : il vaut la peine d’examiner ce qu’elles signifient dans le monde réel.
Scikit-learn interprète les cellules vides comme des NaN. Ici, vous allez simplement remplacer les NaN par 0, via une petite fonction auxiliaire.
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
Ce que fait ce code :
- Il itère sur toutes les colonnes du dataframe
dfet ajoute à la listecategoricalscelles qui sont non numériques. -
Si une colonne n’est pas non numérique (ici, uniquement
Age), il vérifie les valeurs manquantes et les remplace par 0.Remplir des NaN par une valeur unique peut avoir des effets indésirables, surtout si cette valeur se situe dans la plage observée. Comme zéro n’est pas une valeur d’âge légitime observée, vous n’introduisez pas de biais, contrairement à un 36 par exemple ! - Source
Maintenant que vous avez traité les valeurs manquantes et isolé les colonnes non numériques, vous pouvez les convertir. Vous utiliserez le One-Hot Encoding. Pandas propose la méthode get_dummies() pour créer les variables OHE à partir d’un dataframe.
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
Avec l’OHE, une nouvelle colonne est créée pour chaque combinaison colonne/valeur, au format colonne_valeur. Par exemple, pour « Embarked », on obtiendra « Embarked_C », « Embarked_Q », « Embarked_S » et « Embarked_nan ».
Le prétraitement étant terminé, vous pouvez entraîner le modèle de machine learning. Vous utiliserez un classifieur de régression logistique.
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
Votre modèle est entraîné. Vous allez maintenant l’enregistrer, autrement dit le sérialiser. En Python, on parle de Pickling.
Enregistrer le modèle : sérialisation et désérialisation
Vous utiliserez joblib de sklearn.
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']
Le modèle de régression logistique est désormais persistant. Vous pouvez le recharger en mémoire en une seule ligne. Le rechargement s’appelle la désérialisation.
lr = joblib.load('model.pkl')
Vous êtes prêt à utiliser Flask pour servir votre modèle persisté. Vous avez déjà vu à quel point Flask est minimaliste pour démarrer.
Créer une API à partir d’un modèle de machine learning avec Flask
Pour servir votre modèle avec Flask, vous ferez deux choses :
- Charger le modèle persisté en mémoire au démarrage de l’application,
- Créer un endpoint API qui prend des variables en entrée, les transforme au bon format et renvoie des prédictions.
Plus précisément, une requête d’exemple envoyée à l’API ressemblera à :
[
{"Age": 85, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": '"female"', "Embarked": "C"},
{"Age": 3, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]
(une liste JSON d’entrées)
et la réponse de l’API sera :
{"prediction": [0, 1, 1, 0]}
Les prédictions indiquent le statut de survie, où 0 signifie Non et 1 signifie Oui.
JSON signifie JavaScript Object Notation, et c’est l’un des formats d’échange de données les plus utilisés. Pour une introduction rapide, consultez ces tutoriels.
Écrivons une fonction predict() qui :
- charge le modèle persisté en mémoire au démarrage,
- crée un endpoint API qui reçoit des variables d’entrée, les met au bon format et renvoie des prédictions.
Vous avez déjà vu comment charger un modèle persisté. Concentrez-vous maintenant sur son utilisation pour prédire le statut de survie à partir des entrées reçues.
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
json_ = request.json
query_df = pd.DataFrame(json_)
query = pd.get_dummies(query_df)
prediction = lr.predict(query)
return jsonify({'prediction': list(prediction)})
Parfait ! Mais il reste un petit problème.
La fonction ne fonctionne que si la requête entrante contient toutes les valeurs possibles des variables catégorielles, ce qui n’est pas garanti en production. Si la requête n’inclut pas toutes les valeurs possibles, avec la définition actuelle de predict(), get_dummies() générera un dataframe avec moins de colonnes que prévu par le classifieur, ce qui provoquera une erreur d’exécution.
Pour résoudre cela, vous allez aussi persister la liste des colonnes lors de l’entraînement. Vous pouvez sérialiser n’importe quel objet Python en .pkl. Utilisez joblib comme précédemment.
(Gardez en tête qu’il est préférable d’écrire tout le code côté serveur dans un éditeur de texte, puis de l’exécuter depuis un terminal.)
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['model_columns.pkl']
Comme vous avez persisté la liste des colonnes, vous pouvez gérer les colonnes manquantes au moment de la prédiction. Chargez les colonnes du modèle au démarrage de l’application.
@app.route('/predict', methods=['POST']) # Your API endpoint URL would consist /predict
def predict():
if lr:
try:
json_ = request.json
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': prediction})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
Vous avez inclus tous les éléments requis dans l’API « /predict ». Il ne reste plus qu’à écrire le bloc principal.
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line argument
except:
port = 12345 # If you don't provide any port then the port will be set to 12345
lr = joblib.load(model_file_name) # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load(model_columns_file_name) # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
Votre API est prête à être hébergée. Avant de continuer, récapitulons :
Récapitulatif
- Vous avez chargé le dataset Titanic et sélectionné quatre features.
- Vous avez effectué le prétraitement nécessaire.
- Vous avez entraîné un classifieur de régression logistique et l’avez sérialisé.
- Vous avez également sérialisé la liste des colonnes d’entraînement pour gérer les différences de colonnes à la prédiction.
- Vous avez ensuite écrit une API Flask simple qui prédit si une personne a survécu au naufrage selon son âge, son sexe et son port d’embarquement.
Mettons tout le code au même endroit pour ne rien oublier. C’est aussi une bonne pratique de séparer le code du modèle de régression logistique et celui de l’API Flask dans des fichiers .py distincts.
Votre model.py devrait ressembler à ceci :
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
# Data Preprocessing
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
# Logistic Regression classifier
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
# Save your model
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
print("Model dumped!")
# Load the model that you just saved
lr = joblib.load('model.pkl')
# Saving the data columns from training
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
print("Models columns dumped!")
Votre api.py devrait ressembler à ceci :
# Dependencies
from flask import Flask, request, jsonify
from sklearn.externals import joblib
import traceback
import pandas as pd
import numpy as np
# Your API definition
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
if lr:
try:
json_ = request.json
print(json_)
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': str(prediction)})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line input
except:
port = 12345 # If you don't provide any port the port will be set to 12345
lr = joblib.load("model.pkl") # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load("model_columns.pkl") # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
Propre et efficace ! Vous allez maintenant tester cette API avec un client tel que Postman. Assurez-vous que model.py et api.py sont dans le même répertoire et qu’ils ont tous deux été exécutés/compilés avant le test. Voyez l’aperçu de terminal ci-dessous, pris après la compilation réussie des deux fichiers .py.

Si tout a été compilé correctement, la structure de répertoires devrait ressembler à ceci : 
Remarque : le fichier IPYNB est facultatif.
Tester votre API dans Postman
Pour tester votre API, vous aurez besoin d’un client API. Postman est sans doute l’un des meilleurs. Vous pouvez le télécharger facilement via le lien ci-dessus.
L’interface Postman ressemble à ceci dans sa dernière version : 
Après avoir démarré le serveur Flask, saisissez dans Postman l’URL correcte avec le bon port. Cela devrait ressembler à :

Félicitations ! Vous venez de créer votre première API de machine learning.
Votre API peut prédire si un passager a survécu au naufrage du Titanic à partir de son age, de son sex et de son embarked. Votre ami·e peut désormais l’appeler depuis son code front-end et mettre en forme la réponse de l’API.
Aller plus loin :
Dans ce tutoriel, vous avez couvert une compétence clé très recherchée chez les data scientists « full-stack » : créer une API à partir d’un modèle de machine learning. Même si l’API est simple, il est toujours judicieux de commencer par la base pour bien maîtriser les fondamentaux.
Vous pouvez aller beaucoup plus loin. Quelques pistes :
- Écrire une API « /train » qui entraîne un classifieur de régression logistique sur des données.
- Coder un réseau de neurones avec
keraset l’exposer en API. - Héberger votre API dans le cloud pour la rendre consommable.
- Pour des niveaux plus avancés, consultez ce billet de Machine Learning Mastery qui présente plusieurs approches industrielles.
Les possibilités et opportunités sont immenses. À vous de sélectionner celles qui vous conviennent le mieux.
Pour approfondir le machine learning en Python, suivez le cours DataCamp Preprocessing for Machine Learning in Python et consultez notre tutoriel Machine Learning Basics - The Norms.
Références :
Les références suivantes ont été utilisées pour rédiger cet article :