iancr
FastAPI : Déploie ton modèle d'IA en 20 minutes (de zéro à héro)

FastAPI : Déploie ton modèle d'IA en 20 minutes (de zéro à héro)

Tu as passé des heures à entraîner un modèle dans un notebook Jupyter. Il est performant, il est beau, tu es fier. Mais maintenant, comment tu le rends utile ? Comment un autre développeur, une app mobile ou un site web peut-il l'utiliser sans ouvrir ton notebook ? La réponse : une API REST. Et le framework le plus rapide pour ça en Python, c'est FastAPI.

Dans ce tutoriel, on va construire ensemble une API complète qui sert un modèle d'IA. Tu vas apprendre à :

  1. Créer un endpoint REST qui reçoit des données et renvoie une prédiction
  2. Gérer la validation automatique des entrées
  3. Documenter ton API automatiquement
  4. La rendre prête pour la production

À la fin, tu auras une API qui tourne, accessible depuis n'importe où, et que tu pourras déployer sur un serveur en une commande.

Pourquoi FastAPI plutôt que Flask ?

Si tu as déjà touché à Flask, tu te demandes peut-être pourquoi changer. Voici les trois raisons qui font la différence :

  • Validation automatique : FastAPI utilise les types Python natifs (str, int, bool) et Pydantic pour valider toutes les entrées. Plus besoin d'écrire request.json.get("nom") en croisant les doigts.
  • Documentation auto-générée : Swagger UI et ReDoc sont inclus sans une ligne de configuration. Tes endpoints sont documentés en temps réel.
  • Performances : FastAPI est basé sur Starlette et est aussi rapide que Node.js ou Go. Pour un modèle d'IA où la latence compte, c'est crucial.

Étape 1 : Installation de l'environnement

Ouvre ton terminal. On va créer un dossier propre et un environnement virtuel.

bash
# Créer le dossier du projet mkdir mon-api-ia && cd mon-api-ia # Créer un environnement virtuel python3 -m venv venv # Activer l'environnement source venv/bin/activate # Linux/Mac # ou sur Windows : venv\Scripts\activate # Installer FastAPI et ses dépendances pip install fastapi uvicorn sentence-transformers numpy

💡 Ce que tu dois voir : Une série de packages qui s'installent. L'installation prend environ 30 secondes. Vérifie que tout est ok avec :

bash
python -c "import fastapi; print(f'FastAPI {fastapi.__version__} installé ✓')"

Étape 2 : Ton premier endpoint Hello World

Avant de brancher un modèle, voyons la base. Crée un fichier main.py :

python
from fastapi import FastAPI # Créer l'application app = FastAPI( title="Mon API IA", description="API pour servir mon modèle d'intelligence artificielle", version="1.0.0" ) @app.get("/") def racine(): """Endpoint racine — vérifie que l'API tourne.""" return { "message": "🚀 API IA opérationnelle !", "status": "ok", "version": "1.0.0" } @app.get("/health") def sante(): """Endpoint de santé pour le monitoring.""" return {"status": "healthy"}

Lance le serveur :

bash
uvicorn main:app --reload --host 0.0.0.0 --port 8000

💡 Ce que tu dois voir :

plaintext
INFO: Uvicorn running on http://0.0.0.0:8000 INFO: Started reloader process INFO: Started server process

Ouvre maintenant ton navigateur et va sur http://localhost:8000/docs. Tu vois une interface Swagger complète, générée automatiquement ! Teste l'endpoint / directement depuis cette page en cliquant sur "Try it out" puis "Execute".

🎉 Félicitations ! Tu viens de créer ta première API documentée automatiquement. Et on n'a pas écrit une seule ligne de documentation manuelle.

Étape 3 : Brancher un vrai modèle d'IA

Maintenant, passons aux choses sérieuses. On va intégrer un modèle de sentence-transformers qui convertit du texte en vecteurs (embeddings). C'est un modèle très utilisé pour la recherche sémantique, le clustering, et les recommandations.

Crée un fichier modele.py qui gère le chargement du modèle :

python
""" Gestion du modèle d'IA. On le charge UNE SEULE FOIS au démarrage (pas à chaque requête). """ from sentence_transformers import SentenceTransformer # Chargement lazy — le modèle est téléchargé uniquement quand on l'appelle _modele = None def charger_modele() -> SentenceTransformer: """Charge le modèle une seule fois (cache).""" global _modele if _modele is None: print("📦 Téléchargement du modèle... (premier appel uniquement)") # all-MiniLM-L6-v2 : petit, rapide, efficace pour commencer _modele = SentenceTransformer("all-MiniLM-L6-v2") print("✅ Modèle chargé !") return _modele def generer_embedding(texte: str) -> list[float]: """Convertit un texte en vecteur de 384 dimensions.""" modele = charger_modele() vecteur = modele.encode(texte, normalize_embeddings=True) return vecteur.tolist()

Maintenant, ajoute les endpoints dans main.py. Remplace le contenu précédent par :

python
from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from modele import generer_embedding app = FastAPI( title="API d'Embeddings IA", description="Transforme du texte en vecteurs sémantiques via Sentence Transformers", version="1.0.0" ) # ==================== SCHÉMAS (Validation automatique) ==================== class RequeteTexte(BaseModel): """Ce que l'utilisateur envoie.""" texte: str = Field( ..., min_length=1, max_length=2000, description="Le texte à vectoriser", example="L'intelligence artificielle transforme le monde." ) class ReponseEmbedding(BaseModel): """Ce que l'API renvoie.""" embedding: list[float] = Field(description="Vecteur de 384 dimensions") dimensions: int = Field(description="Nombre de dimensions du vecteur") texte_original: str = Field(description="Texte source reçu") # ==================== ENDPOINTS ==================== @app.get("/") def racine(): return { "message": "🚀 API d'Embeddings IA opérationnelle", "endpoints": { "docs": "/docs", "embedding": "/embedding (POST)", "batch": "/embedding/batch (POST)" } } @app.get("/health") def sante(): return {"status": "healthy"} @app.post("/embedding", response_model=ReponseEmbedding) def creer_embedding(requete: RequeteTexte): """ Convertit un texte unique en vecteur d'embedding. - **texte** : Le texte à vectoriser (1 à 2000 caractères) """ try: vecteur = generer_embedding(requete.texte) return ReponseEmbedding( embedding=vecteur, dimensions=len(vecteur), texte_original=requete.texte ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) class RequeteBatch(BaseModel): """Requête par lot — plusieurs textes.""" textes: list[str] = Field( ..., min_length=1, max_length=100, description="Liste de textes à vectoriser (max 100)", example=["Bonjour", "Hello world", "L'IA c'est génial"] ) class ReponseBatch(BaseModel): embeddings: list[list[float]] nombre_textes: int dimensions: int @app.post("/embedding/batch", response_model=ReponseBatch) def creer_embeddings_batch(requete: RequeteBatch): """ Convertit plusieurs textes en une seule requête. - **textes** : Liste de 1 à 100 textes """ try: vecteurs = [generer_embedding(t) for t in requete.textes] return ReponseBatch( embeddings=vecteurs, nombre_textes=len(vecteurs), dimensions=len(vecteurs[0]) ) except Exception as e: raise HTTPException(status_code=500, detail=str(e))

💡 Ce que tu dois comprendre :

  • BaseModel de Pydantic définit la forme exacte des données attendues. Si l'utilisateur envoie un nombre à la place d'un texte, FastAPI rejette automatiquement avec un message clair.
  • response_model indique à FastAPI quoi documenter dans Swagger comme réponse.
  • Le modèle n'est chargé qu'une seule fois grâce au pattern de cache dans modele.py. Sans ça, il serait rechargé à chaque requête.

Relance le serveur (il devrait se recharger automatiquement avec --reload) :

bash
uvicorn main:app --reload --host 0.0.0.0 --port 8000

💡 Ce que tu dois voir : Au premier appel POST, le terminal affiche "📦 Téléchargement du modèle..." (environ 15-20 secondes la première fois). Les appels suivants sont instantanés.

Étape 4 : Tester ton API

Test avec Swagger UI

Va sur http://localhost:8000/docs, clique sur le endpoint POST /embedding, puis "Try it out". Colle ce JSON :

json
{ "texte": "FastAPI est le framework le plus rapide pour servir des modèles d'IA en Python." }

Clique "Execute". Tu devrais voir une réponse avec un vecteur de 384 nombres. C'est ton embedding !

Test avec curl

bash
curl -X POST http://localhost:8000/embedding \ -H "Content-Type: application/json" \ -d '{"texte": "Bonjour le monde de l'\''IA !"}'

💡 Ce que tu dois voir : Un JSON avec embedding, dimensions: 384, et texte_original.

Test avec Python

python
import requests reponse = requests.post( "http://localhost:8000/embedding", json={"texte": "Mon premier appel API réussi !"} ) data = reponse.json() print(f"Dimensions : {data['dimensions']}") print(f"Premières valeurs : {data['embedding'][:5]}...")

Étape 5 : Validation automatique magique

Essaie d'envoyer une requête invalide pour voir la validation automatique en action :

bash
# Envoi d'un nombre au lieu d'un texte curl -X POST http://localhost:8000/embedding \ -H "Content-Type: application/json" \ -d '{"texte": 123}'

💡 Ce que tu dois voir : Une erreur 422 avec un message détaillé expliquant exactement ce qui ne va pas. FastAPI ne laisse rien passer et tout ça sans une ligne de validation manuelle.

Étape 6 : Ajouter un middleware de logging

Pour avoir une API digne de la production, ajoutons un middleware qui logue chaque requête. Ajoute en haut de main.py après les imports :

python
import time from fastapi import Request # ... après app = FastAPI(...) ... @app.middleware("http") async def log_requetes(request: Request, call_next): """Middleware qui mesure le temps de chaque requête.""" debut = time.time() reponse = await call_next(request) duree = time.time() - debut print(f"📊 {request.method} {request.url.path} → {reponse.status_code} ({duree:.3f}s)") return reponse

Relance et fais quelques requêtes. Tu verras dans le terminal :

plaintext
📊 GET /docs → 200 (0.002s) 📊 POST /embedding → 200 (0.015s)

C'est un middleware de logging basique, mais tu peux le faire évoluer vers du monitoring Prometheus, du rate limiting, ou de l'authentification.

Pièges classiques

1. Recharger le modèle à chaque requête

C'est l'erreur la plus courante. Si tu mets model = SentenceTransformer(...) directement dans ton endpoint, le modèle sera rechargé à chaque appel mémoire saturée, latence de 20 secondes par requête. Solution : utilise le pattern de cache comme dans modele.py.

2. Oublier le `response_model`

Sans response_model, Swagger ne sait pas quoi afficher comme schéma de réponse. Ta doc sera vide côté réponse. Mets toujours le bon type.

3. Bloquer le serveur avec des traitements lourds

Si ton endpoint fait un traitement de 5 secondes, il bloque toute autre requête. Solution : utilise async def et BackgroundTasks, ou passe par une queue (Celery, Redis) pour les traitements longs.

4. Démarrer sur le port 80 sans droits

Sur Linux, les ports < 1024 nécessitent root. Utilise --port 8000 (ou un autre port > 1024) pour le développement.

5. Oublier de figer les dépendances

Quand tu déploies, assure-toi d'avoir un requirements.txt exact :

bash
pip freeze > requirements.txt

Pour aller plus loin

1. Ajoute l'authentification par token

FastAPI intègre OAuth2 et JWT nativement. En 10 lignes, tu peux protéger tes endpoints avec un simple token API :

python
from fastapi import Security from fastapi.security import APIKeyHeader api_key_header = APIKeyHeader(name="X-API-Key") def verifier_cle(api_key: str = Security(api_key_header)): if api_key != "ma-cle-secrete": # à remplacer par une vraie vérification raise HTTPException(status_code=403, detail="Clé API invalide") return api_key # Puis ajoute dependencies=[Security(verifier_cle)] sur ton endpoint

2. Passe à un vrai serveur de production

Uvicorn avec --reload est parfait pour le dev, mais en production utilise :

bash
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app

Ça lance 4 workers qui gèrent les requêtes en parallèle indispensable pour servir plusieurs utilisateurs simultanément.

3. Déploie sur Hugging Face Spaces (gratuit)

Crée un fichier Dockerfile :

dockerfile
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]

Pousse sur un Space Hugging Face et ton API est en ligne gratuitement en 5 minutes.

💡 Idée d'illustration : Un diagramme montrant le flux complet requête HTTP FastAPI validation Pydantic modèle Sentence Transformers embedding réponse JSON. Avec un petit robot qui passe du texte au vecteur, style « de la phrase au vecteur magique ».


Voilà, tu as une API d'IA professionnelle et documentée ! En 20 minutes, tu es passé d'un notebook statique à un endpoint REST que n'importe qui peut appeler. C'est la différence entre un projet perso et un produit qui peut servir des utilisateurs réels. La prochaine fois que quelqu'un te demande « comment j'utilise ton modèle ? », tu lui enverras juste une URL Swagger.