Votre « recherche visuelle » est en prod. Les utilisateurs tapent tasses en céramique rouges et votre système renvoie un grille‑pain, une basket et un chien. La confiance s’effondre. Les dirigeants veulent des explications. La vérité : ce n’est pas votre modèle. Ce sont vos choix d’indexation — et votre manque d’hygiène d’évaluation.
Une vague récente de billets a montré comment les équipes indexent des images pour le RAG et la recherche visuelle. Utile — mais la plupart passent à côté des angles opérationnels durs : choix et versioning des embeddings, fusion multi‑vecteurs, compromis des ANN (approximate nearest neighbor), budgets de re‑ranking, et un harness d’évaluation simple à exécuter chaque nuit. Si vous dirigez une organisation d’ingénierie, voici le playbook pour livrer un RAG d’images qui récupère réellement ce que veulent les utilisateurs, à 100+ QPS, avec un coût prévisible.
Commencez par l’intention utilisateur, pas par les modèles
Avant de choisir CLIP vs SigLIP ou FAISS vs Milvus, définissez les intentions que vous devez servir. Des intentions différentes impliquent des embeddings, des index et des filtres différents.
- Similarité visuelle pure : « Montre‑moi des produits qui ressemblent à cette photo. » Un embedding d’image global fonctionne ; des caractéristiques de couleur/texture aident.
- Recherche texte‑vers‑image : « bottines Chelsea en cuir noir ». Vous avez besoin d’un solide alignement cross‑modal et d’une couverture multilingue.
- Rappel au niveau objet : « trouver des images avec un petit logo sur une casquette ». Vous avez besoin de détection/segmentation pour amplifier des signaux faibles.
- Texte dans les images : « emballages où figure gluten‑free ». L’OCR compte plus que la similarité visuelle.
- Conformité/sécurité : les filtres de contenu et les jointures sur métadonnées doivent être de première classe, pas greffés après coup.
Choisissez l’ensemble minimal d’intentions sur lesquelles vous pouvez gagner maintenant et rendez‑les explicites dans votre feuille de KPI. Tout le reste en découle.
Une architecture de référence qui ne vous surprendra pas en prod
1) Ingestion et déduplication
- Stockez les originaux dans un stockage objet (S3/GCS) avec un asset_id stable.
- Calculez un hash perceptuel (pHash ou aivhash) pour dédupliquer les quasi‑identiques et limiter le churn d’indexation.
- Normalisez à une taille standard pour l’embedding (p. ex., 224/256/384 sur le grand côté) et stockez une vignette.
2) Générez des caractéristiques (pas juste un seul embedding)
- Embedding d’image global : commencez avec CLIP ViT‑B/32 (512d) ou SigLIP (typiquement 768d). Les variantes SigLIP performent mieux sur les requêtes multilingues et le détail fin, au prix de vecteurs légèrement plus gros.
- Embedding de légende (caption) : faites tourner un captionneur d’images rapide à l’ingestion (p. ex., BLIP‑base) pour produire 1–2 courtes légendes. Passez ces légendes dans votre modèle d’embedding texte standard (p. ex., E5‑large, 1024d) pour créer un deuxième vecteur par image. Vous gagnez du rappel sur les requêtes texte longue traîne sans dépendre de métadonnées fragiles.
- Labels d’objets : optionnels mais à fort levier pour le rappel au niveau objet. Utilisez un détecteur (YOLOv8/DETR). Conservez les 5–10 meilleurs labels en métadonnées, pas en vecteurs.
- OCR : si le texte dans l’image compte, exécutez PaddleOCR ou Tesseract. Indexez le texte extrait dans votre moteur de recherche classique et stockez‑le comme champ filtrable.
Oui, c’est plus de travail que « juste CLIP ». Cela transforme aussi votre recherche d’une démo en un produit.
3) Stockez avec du versioning dès le premier jour
- Dans une table de métadonnées (Postgres/Spanner), conservez asset_id, tenant_id, indicateurs de sécurité, updated_at, et des champs embedding_version pour chaque type de vecteur (p. ex., image_v1, caption_v2).
- Persistez les vecteurs en blocs colonnaires par shard (p. ex., fichiers Parquet de 100k lignes) pour un réindexage peu coûteux et des déplacements batch faciles.
- Chaque ligne de vecteur porte model_name, dim, dtype, checksum. Sans exception.
4) Choisissez votre index ANN selon l’échelle, pas la hype
Des ordres de grandeur aident à éviter une première reconstruction.
- HNSW sur CPU (FAISS/Milvus) : excellent jusqu’à ~10 M de vecteurs par index avec une latence sous 50 ms à rappel correct. Le budget mémoire est environ 2–3× la mémoire brute des vecteurs. Exemple : 768‑d FP16 = 1,5 KB/vecteur. 10 M de vecteurs ≈ 15 GB de données vecteur + 15–30 GB de surcharge graphe ⇒ 30–45 GB de RAM.
- IVF‑PQ sur CPU : compressez à ~64–128 octets/vecteur grâce à la quantification par produits. Le rappel baisse un peu, mais la dépense mémoire chute d’un ordre de grandeur. Bien pour l’échelle 10–100 M. Reconstruction ou maintenance lourde nécessaires quand la distribution dérive.
- ANN sur GPU (FAISS‑GPU) : utilisez‑le quand vous visez des p95 agressifs à haut QPS, ou un re‑ranking intensif sur le même GPU. Ne brûlez pas de la VRAM pour stocker d’énormes graphes HNSW ; réservez‑la au calcul et utilisez des codes IVF‑PQ pour garder une petite résidence.
Règle empirique : si vous êtes sous 5 M d’assets par tenant, HNSW est la chose la plus simple qui marche. Au‑delà de 10 M, budgétez IVF‑PQ avec une reconstruction périodique. Au‑delà de 50 M, prévoyez un sharding dur par tenant, région et version d’embedding — ou un service managé si votre équipe ne peut pas assumer l’exploitation.
5) Pipeline de requête qui équilibre rappel et latence
- Générez l’embedding de la requête :
- Requête texte : utilisez l’embedding texte correspondant (tête texte de CLIP ou E5). Normalisez les vecteurs pour la similarité cosinus.
- Requête image : calculez l’embedding d’image global. Optionnel : exécutez un OCR rapide et ajoutez des termes à un filtre must‑match si votre domaine l’exige.
- Interrogez plusieurs index : tapez à la fois l’index image globale et l’index légende. Récupérez le top‑k (p. ex., k=400) de chacun.
- Fusionnez et filtrez : somme pondérée des scores (apprenez les poids hors ligne). Appliquez les filtres tenant, sécurité, stock/disponibilité.
- Re‑classez (re‑ranking) : prenez les 200 premiers et appliquez un scorer cross‑modal plus fort pour re‑classer (p. ex., un cross‑encoder CLIP ou un petit re‑ranker vision‑langage). Allouez 40–120 ms sur un GPU de data center unique pour 200 paires, selon le modèle. Si vous ne pouvez pas vous permettre un GPU, utilisez un re‑ranker bilinéaire plus léger pour un gain de 15–30 ms et acceptez une petite perte de qualité.
Visez un p95 de bout en bout sous 200 ms à 100 QPS pour un déploiement mono‑région. Vous pouvez y parvenir avec deux nœuds d’index CPU (64–96 vCPU chacun) et un GPU modeste pour le re‑ranking.
Ce que ça coûte (avec de vrais chiffres)
- Taille des vecteurs : 768‑d FP16 = ~1,5 KB/vecteur ; 512‑d FP16 = ~1,0 KB/vecteur. 1 M d’images = 1–1,5 GB de vecteurs bruts par index.
- Mémoire HNSW : 2–3× la mémoire des vecteurs en pratique. Prévoyez 3–5 GB de RAM par 1 M d’images pour 768‑d FP16, overhead inclus. Confortable sur des serveurs 128–256 GB de RAM à l’échelle 10 M.
- Mémoire IVF‑PQ : 64–128 octets/vecteur. 10 M d’images ≈ 0,6–1,2 GB de codes, plus centroïdes et overhead. Vous échangez un peu de rappel contre d’énormes économies.
- Débit d’embedding : sur un seul GPU de data center milieu de gamme (classe L4/T4), une tour image CLIP‑B/32 à 224 px tourne à ~30–60 img/s. 1 M d’images prend ~5–9 heures‑GPU. Le captioning avec un petit BLIP‑base à 3–5 img/s prendra 55–90 heures‑GPU ; faites‑le une fois, puis incrémentalement.
- Coût du re‑ranking : un petit modèle cross‑modal scorant 200 candidats/requête peut saturer un L4 unique à ~100–200 QPS selon la taille de batch et la précision. Si vos budgets de latence sont serrés, shardez horizontalement les GPUs de re‑ranking.
Les bases de données vectorielles managées rendent la tarification floue (unités de capacité, pods, paliers RPS). Si vous auto‑hébergez FAISS ou Milvus, vos principaux coûts sont la RAM (pour HNSW) ou le CPU (pour les builds IVF‑PQ) et une petite ligne GPU pour le re‑ranking. Vous pouvez faire tourner un déploiement crédible à 10 M d’assets pour quelques milliers d’euros/mois en infra si vous avez déjà une empreinte Kubernetes.
Évaluation : arrêtez de débattre, mesurez recall@k
Vous n’avez pas besoin d’un labo de recherche pour mesurer si la récupération est bonne. Vous avez besoin de 200 à 1 000 paires requête‑asset étiquetées et de deux métriques.
- Recall@10 : fraction des requêtes où une correspondance connue‑bonne apparaît dans le top 10. Pour l’e‑commerce et la modération UGC, visez ≥0,6 avant de claironner la fonctionnalité.
- NDCG@10 : un score graduel qui récompense l’ordre. Définissez des labels simples : parfait/acceptable/mauvais. Vous avez besoin de ≥0,7 pour cesser d’agacer les utilisateurs avec des résultats presque justes en tête.
Montez une tâche nocturne qui :
- Échantillonne 50 nouvelles requêtes des logs de prod (après purge des données personnelles).
- Exécute à la fois les index actuels et canaris.
- Calcule recall@10 et NDCG@10.
- Diff les requêtes qui ont régressé et les poste sur Slack avec vignettes.
Ce fil Slack vous évitera des semaines de débats circulaires. Il vous gardera aussi honnêtes quand vous changez d’embeddings ou de re‑rankers.
Versioning et migration sans interruption
- Versionnez chaque vecteur : image_v1, caption_v1. Quand vous changez de modèles, créez image_v2, construisez les nouveaux index en parallèle et écrivez en double (double‑write) pendant le backfill.
- Backfill par shards : déplacez 5–10 % du trafic vers v2 à mesure que chaque shard est prêt. Comparez les métriques côte à côte. Restaurez si le rappel nocturne baisse.
- Gardez deux générations actives : maintenez v1 et v2 pendant 2–4 semaines. Puis purgez v1 pour récupérer RAM/disque.
Ne pas versionner est la cause n°1 de « nos résultats sont devenus bizarres mardi dernier ».
Multi‑tenancy et conformité, les bloqueurs peu glamour
- Shardez par tenant quand l’union des assets peut faire fuiter des correspondances sensibles entre entreprises (fréquent en B2B SaaS). Les filtres durs coûtent moins cher que les regrets.
- Chiffrez au repos les vecteurs. Ils ne sont pas des images réversibles, mais ce sont des signaux sensibles sur votre corpus.
- Les takedowns doivent être de première classe : gardez une correspondance index_id → asset_id et propagez les suppressions en quelques minutes, pas en jours.
Modes de défaillance courants que nous voyons sans cesse
- Un vecteur pour les gouverner tous : les équipes s’appuient sur un seul embedding global et s’étonnent que les requêtes texte longue traîne soient manquées. Ajoutez des vecteurs basés sur les légendes et votre rappel grimpe sans gros travail modèle.
- Pas de normalisation : oublier la normalisation L2 des vecteurs conduit à un cosinus qui n’en est pas un. Normalisez à l’écriture et à la requête.
- Ignorer couleur/texture : pour la mode et le mobilier, ajoutez un histogramme de couleur bon marché ou apprenez des poids qui valorisent la similarité de chroma. Ça compte.
- Dérive de l’index : IVF‑PQ requiert une reconstruction périodique à mesure que de nouveaux assets déplacent la distribution. Définissez une cadence (p. ex., mensuelle) et liez‑la aux métriques de rappel.
- Mélanger les tenants : nous avons vu des équipes « optimiser » en co‑indexant des tenants pour le débit, puis passer un trimestre à défaire des fuites de données. À éviter.
- Améliorations invérifiables : livrer des changements de modèles sans le harness 200 requêtes garantit des surprises. Mesurez, ou n’expédiez pas.
Acheter vs construire : un cadre de décision
Il n’y a aucune honte à utiliser une base vectorielle managée. La question est où se situe votre risque.
- Choisissez le managé si :
- Vous êtes sous 10 M d’assets, le multi‑région est obligatoire, et vous ne pouvez pas staffer une expertise ANN IRL.
- Votre posture achats préfère payer une prime pour des SLA opérationnels.
- Vous pouvez vivre avec une tarification opaque et un verrouillage fournisseur pendant 12–24 mois.
- Choisissez l’auto‑hébergé si :
- Vous faites déjà tourner Kubernetes et pouvez tolérer FAISS/Milvus.
- Votre dataset dépasse 10–50 M et l’efficacité RAM compte.
- Vous avez besoin d’un couplage serré avec vos GPUs de re‑ranking et de filtres custom à la requête.
Un hybride est courant : auto‑hébergez HNSW pour les index tenants « chauds », et déportez les index « froids » ou expérimentaux vers un service managé.
À quelle vitesse vous pouvez y arriver (un plan réaliste sur 4 semaines)
Si vous avez des données étiquetées et une base devops, vous pouvez mettre en place un système crédible en un mois.
- Semaine 1 : ingestion + déduplication pHash + embedding global v1 (CLIP/SigLIP). Prototype FAISS HNSW. Filtres durs opérationnels.
- Semaine 2 : génération de légendes + embedding de légendes. Recherche à double index + fusion de scores. Harness basique recall@10 avec 200 paires.
- Semaine 3 : service de re‑ranker sur GPU, p95 sous 250 ms. Chemin canari pour les embeddings v2.
- Semaine 4 : durcissement : chemin de takedown, sharding par tenant, rapports d’éval nocturnes sur Slack, tableaux de bord de métriques. Démarrez le backfill des assets historiques.
C’est le type de pod que nous faisons tourner avec des équipes US depuis Brazil : 6–8 heures de chevauchement, des livrables clairs, et pas de « research theater ». La stack est volontairement banale.
Réglages avancés quand vous en avez besoin
- Distillation et quantification : si la mémoire est serrée, entraînez un embedding élève (p. ex., 384‑d) depuis votre modèle enseignant sur votre domaine, puis quantifiez en FP16/INT8. Vous conservez souvent 90–95 % du rappel avec la moitié des octets.
- Expansion au moment de la requête : ajoutez des synonymes ou de courtes descriptions (p. ex., pour la mode : couleur, matière). Améliorez le rappel sans taper plus fort dans l’index.
- Fusion hybride BM25 + vecteur : pour les domaines riches en OCR, BM25 sur le texte extrait plus les scores vecteur surpasse chacun pris isolément.
- Re‑ranking temporel : si la fraîcheur compte, faites décroitre les scores avec l’âge après la fusion. Naissez pas une image virale dominer des semaines.
Que mettre sur le tableau de bord
Votre revue hebdo devrait afficher :
- Recall@10 et NDCG@10 par intention (visuel, texte, OCR) et par tenant.
- Latence p95 de bout en bout ; p95 ANN ; p95 re‑ranking.
- Cardinalité des index, mémoire par index, et date de dernière reconstruction.
- Top 10 des requêtes qui ont régressé vs la semaine dernière, avec vignettes.
- SLA de takedown : temps entre la demande et la purge dans l’index.
Si vous ne le suivez pas, vous redécouvrirez les mêmes problèmes chaque trimestre.
L’essentiel
Un RAG d’images qui marche n’est pas une percée de recherche. C’est une suite de choix d’ingénierie sobres et un peu de discipline. Utilisez deux vecteurs par asset, choisissez l’ANN adapté à votre échelle, réservez un budget pour le re‑ranking, et mesurez le rappel chaque nuit. Faites cela, et vos utilisateurs cesseront de voir des chiens quand ils cherchent des mugs.
Points clés
- Le choix du modèle est secondaire ; l’indexation et la discipline d’évaluation déterminent la qualité en production.
- Utilisez une indexation multi‑vecteurs (image globale + légende) pour augmenter le rappel sans héroïsme.
- Choisissez HNSW sous ~10 M d’assets ; planifiez IVF‑PQ et des reconstructions au‑delà.
- Re‑classez les 100–200 premiers avec un modèle cross‑modal plus fort ; allouez 40–120 ms sur un petit GPU.
- Versionnez chaque embedding ; écriture en double et canari avant de basculer le trafic.
- Mettez en place un harness recall@10/NDCG de 200–1 000 paires et exécutez‑le chaque nuit ; laissez Slack vous signaler les régressions.
- Shardez par tenant et faites des takedowns une capacité de première classe pour éviter les cauchemars de conformité.