Recherche par association : un modèle mental basé sur la mémoire pour la recherche vectorielle
Apprenez comment les embeddings, la similarité sémantique, la recherche d’adjacent le plus proche approximative et les filtres de métadonnées fonctionnent ensemble, en s’appuyant sur la mémoire humaine comme guide.
Des années peuvent s’écouler sans qu’on pense ne serait-ce qu’une fois à son professeur d’enfance. Puis, une odeur de poussière de craie, l’arôme d’une cantine scolaire ou une chanson qui passait pendant le trajet en bus pour rentrer à la maison font immédiatement et vivement réapparaître cette personne. Rien n’a été recherché par son nom ; quelque chose de similaire est apparu, et le souvenir est remonté à la surface tout seul.
C’est ce que les systèmes de recherche sémantique, la génération améliorée par la récupération d’informations (RAG) et les systèmes de recommandation basés sur l’IA tentent de reproduire, de manière imparfaite mais sérieuse. Le composant qui rend cela possible est la base de données vectorielle. Cet article utilise le modèle mental du rappel par association pour expliquer ce qu sont les vecteurs, comment on mesure la similarité, comment fonctionne la recherche du voisin le plus proche, et comment ces éléments forment un pipeline de récupération. Il souligne également où cette analogie cesse d’être valable, car ce sont précisément les endroits où les systèmes en production ont tendance à faire des erreurs.
Stockage par signification plutôt que par nom
La mémoire humaine ne dispose d’aucun index alphabétique. Il n’existe pas de dossier mental intitulé « Aliments » contenant un sous-dossier « Italien » avec un fichier nommé « Pizza ». C’est cette hiérarchie stricte qui permet à une base de données conventionnelle d’organiser les données : chaque enregistrement se trouve à une adresse connue et est retrouvé grâce à une clé précise.
La mémoire, elle, est organisée par signification et par association : en fonction du contexte, des émotions, de la manière dont une chose est liée aux autres. L’idée de pizza est associée aux vendredis soir, à un voyage à Naples, aux plats réconfortants, aux tranches de fromage étirées, à l’arôme de l’origan, et peut-être aussi à un colocataire d’université qui a gâché chaque lot de pâte faite maison. Penser à la pizza ne déclenche pas l’ouverture d’un seul fichier ; cela active plutôt tout un réseau de souvenirs liés entre eux, en commençant par ceux qui sont le plus fortement associés.
Ce que code un vecteur
Pour comprendre cette base de données, il faut d’abord comprendre ce qu’elle contient. Un vecteur n’est ici rien de plus qu’une liste ordonnée de nombres représentant une signification.
Une machine n’a pas de notion concrète de ce qu’est la pizza. Ce qu’un modèle d’embedding peut apprendre, en traitant d’énormes quantités de texte, c’est l’association que maintient un mot. « Pizza » apparaît à proximité de « fromage », « italien », « pâte », « four » et « tranche ». Ces associations diffèrent de celles liées à « sushi », mais coïncident fortement avec celles associées à « pain plat » ou « calzone ».
Le modèle condense ces motifs en une liste de nombres de longueur fixe, généralement allant de quelques centaines à plusieurs milliers de valeurs ; 384 et 1 536 constituent des tailles typiques. Aucun nombre en particulier ne porte d’étiquette lisible, mais ensemble ils positionnent précisément l’élément dans un espace à haute dimension. La propriété importante est la suivante : les entrées ayant des significations similaires se voient attribuer des vecteurs situés proches les uns des autres. « Pizza » se trouve près de « pain plat » et très éloigné du « rapport de résultats trimestriels ».
En d’autres termes, le sens est transformé en distance. Tout le reste dans cet article découle de cette seule transformation.
La similarité en tant que spectre, et non comme correspondance
Une requête traditionnelle est binaire : une ligne satisfait soit à la condition, soit non. En filtrant par « chien », on obtient des lignes contenant exclusivement le mot « chien », sans aucun résultat pour « chiot », « golden retriever » ou « fidèle compagnon à quatre pattes ».
La similarité sémantique remplace cette réponse oui/non par une note indiquant à quel point deux significations sont proches. Sur une échelle illustrative, « chiot » pourrait obtenir une note de 0,94 par rapport à « chien », « loup » environ 0,71, et « facture » autour de 0,08. La métrique utilisée est généralement la similarité cosinus ou une distance apparentée telle que le produit scalaire ou la distance euclidienne, et le choix approprié dépend de la manière dont le modèle d’incorporation a été entraîné.
Cela reflète l’effet de la poussière de craie : ce n’est pas une correspondance exacte, mais un indice qui active des souvenirs proches, lesquels à leur tour activent leurs voisins. Dans une base de données vectorielle, les mécanismes sont numériques. La requête est convertie en un vecteur, et la base de données renvoie les éléments stockés dont les vecteurs sont les plus proches du sien. Proche signifie similaire en sens.
C’est pourquoi une recherche pour « comment corriger une requête de base de données lente » peut faire apparaître un document intitulé « optimisation de la performance des requêtes à grande échelle », même si les deux phrases ne partagent presque aucun mot. Leurs vecteurs sont proches car ils expriment la même intention.
Une mise en garde concernant les chiffres
Les scores de similarité sont relatifs, et non absolus. Un score de 0,8 obtenu avec un modèle d’incorporation ne peut pas être comparé à un score de 0,8 obtenu avec un autre modèle ; même au sein d’un même modèle, la fourchette typique dépend du domaine. Considérez ces scores comme un moyen de classer les candidats, et si vous appliquez une limite, calibrez-la à l’aide de vos propres données plutôt que de choisir un nombre rond.
Correction importante : le SQL classique basé sur des mots-clés ne peut pas exprimer de similarité sémantique, mais cela ne signifie pas pour autant que les bases de données SQL sont excluses. Des extensions comme pgvector, abordées ci-dessous, ajoutent des colonnes vectorielles et des opérateurs de similarité à Postgres, permettant ainsi cette fonctionnalité au sein d’une base de données relationnelle.
Recherche du voisin le plus proche à grande échelle
Comment la base de données localise-t-elle réellement les vecteurs les plus proches ? L’opération principale s’appelle recherche du voisin le plus proche, et c’est justement pour la rendre rapide que les bases de données vectorielles existent.
Imaginez chaque élément stocké comme une étoile dans une galaxie, disposée en fonction de son sens, avec des éléments similaires regroupés dans la même région. Une requête ajoute une nouvelle étoile à cette galaxie et demande quels sont les cinq étoiles existantes les plus proches.
L’approche naïve mesure la distance entre la requête et chaque vecteur stocké, classe les résultats puis renvoie ceux qui se situent en tête. Pour des milliers de vecteurs, c’est rapide et tout à fait suffisant. Mais pour des millions ou des milliards de vecteurs, comparer avec tous devient très rapidement coûteux.
Les systèmes de production s’appuient donc sur des algorithmes d’approximation du voisin le plus proche. Au lieu de visiter chaque étoile, ils utilisent un index qui réduit la recherche aux régions prometteuses, acceptant une légère perte en précision en échange d’une grande amélioration de la vitesse. L’algorithme le plus utilisé aujourd’hui est HNSW, pour Hierarchical Navigable Small World. Vous n’avez pas besoin de connaître son fonctionnement interne pour utiliser une base de données vectorielle, mais vous devez savoir qu’il est la raison pour laquelle une recherche parmi plus d’un milliard d’embeddings peut être effectuée en quelques millisecondes.
Le terme « approximatif » mérite attention. Un index ANN peut parfois manquer le véritable voisin le plus proche, et le taux auquel il trouve les vrais meilleurs résultats, appelé rappel, dépend de paramètres d’index qui compromettent mémoire et latence au profit de la précision. Si la qualité des recherches semble inexplicablement irrégulière à grande échelle, il vaut la peine de vérifier les paramètres d’index ; notre guide sur l’ajustement des index HNSW pour les systèmes RAG en production aborde en détail ces paramètres.
Dans le stockage des embeddings
En son essence, une base de données vectorielle est un système de stockage optimisé pour une seule tâche : conserver des vecteurs et effectuer très rapidement des recherches de voisin le plus proche parmi eux.
Imaginez une bibliothèque qui ignore le système décimal de Dewey et classe les livres en fonction de leur atmosphère. Les ouvrages sur le chagrin se trouvent à côté des livres sur la perte, ceux-ci à leur tour à côté de ceux traitant de la solitude, puis de l’isolement, suivis par des mémoires d’expéditions en solitaire. Personne n’a attribué manuellement ces catégories ; cet agencement est apparu parce que les lecteurs attirés par un type de contenu ont tendance à vouloir aussi ceux des autres catégories.
Pinecone, Weaviate, Chroma et Qdrant sont des exemples de telles bibliothèques. On y charge des embeddings de documents, des descriptions de produits, des images converties en vecteurs ou des schémas de comportement utilisateur, et elles maintiennent l’index afin que les recherches restent rapides même à mesure que la collection s’agrandit.
Chaque enregistrement stocké comprend généralement trois éléments :
- Un ID qui identifie de manière unique l’élément.
- Le vecteur qui représente son sens.
- Des métadonnées optionnelles telles que le titre, la date, la catégorie ou l’URL de source, qui peuvent servir à filtrer les résultats.
Les métadonnées sont plus précieuses qu’il n’y paraît au premier abord. Une demande réaliste pourrait être la suivante : trouver les cinq documents les plus similaires à la requête, mais uniquement ceux des 30 derniers jours et provenant exclusivement de la base de connaissances de l’équipe d’ingénierie. Il s’agit là d’une recherche vectorielle combinée à un filtre de métadonnées, et la plupart des systèmes en production reposent sur cette combinaison. La manière dont le filtre est appliqué compte également : filtrer après la recherche de similarité peut vous donner moins de résultats que demandé, il est donc nécessaire de vérifier si votre base de données effectue des filtrages pendant la recherche elle-même.
Le processus de récupération du début à la fin
Que ce soit au sein d’un système RAG, d’une fonction de recherche sémantique ou de toute application utilisant des connaissances stockées, la récupération suit toujours les mêmes quatre étapes.
- Intégrer le contenu. Chaque document, article, description de produit ou enregistrement que vous souhaitez rendre recherchable passe par un modèle d’incorporation, et le vecteur résultant est stocké aux côtés du contenu original. Les documents longs sont généralement divisés en parties au préalable, car un seul vecteur pour tout un manuel mélange trop d’idées entre elles.
- Incorporer la requête avec le même modèle. Cela n’est pas optionnel. Les différents modèles d’incorporation produisent des vecteurs dans des espaces non liés, de sorte que comparer une requête issue d’un modèle avec des documents provenant d’un autre donne des distances sans signification. Changer de modèle signifie réincorporer l’ensemble de la collection.
- Rechercher. Le vecteur de la requête est envoyé dans la base de données, la recherche du voisin le plus proche identifie les vecteurs stockés les plus proches, et les meilleurs résultats sont retournés, généralement accompagnés de scores de similarité.
Du point de vue de l’utilisateur, une réponse pertinente apparaît en quelques instants. En réalité, le sens a été transformé en nombres, ces nombres ont été comparés à des millions d’éléments stockés, les correspondances les plus proches ont été retournées, et une réponse a été élaborée à partir de contenu authentique.
Pourquoi la recherche vectorielle est partout aujourd’hui
Il y a peu encore, les bases de données vectorielles n’étaient qu’un outil de niche utilisé uniquement pour mettre en place des recherches sémantiques ou des systèmes de recommandation spécialisés. Aujourd’hui, elles font partie intégrante de nombreuses applications d’intelligence artificielle. Le RAG a besoin d’un endroit pour stocker et interroger les embeddings de documents. Les agents interrogent des bases de connaissances en se basant sur le sens. Les systèmes de recommandation à grande échelle fonctionnent grâce à la similarité vectorielle. La recherche multimodale, telle que la découverte d’images à partir d’une description textuelle ou de produits à partir d’une photo téléchargée, s’appuie également sur les vecteurs.
Si vous développez des applications utilisant des grands modèles de langage en production, il est fort probable que vous dépendiez déjà de la recherche vectorielle ou que vous y arriviez bientôt. Ce qui est encourageant, c’est que l’idée de base nous est déjà familière : depuis toujours, notre mémoire récupère les associations les plus proches de tout ce qui atteint nos sens. Une base de données vectorielle fait la même chose avec des nombres, en quelques millisecondes, sur des millions d’éléments.
Où l’analogie avec la mémoire échoue
La comparaison avec le cerveau est utile pour l’intuition, mais quelques différences sont importantes en pratique :
- La mémoire s’adapte continuellement ; un modèle d’embedding est figé. Si le vocabulaire de votre domaine change, les vecteurs ne s’actualisent pas automatiquement.
- La mémoire intègre facilement le contexte ; un vecteur ne capture que ce qui y a été introduit. Un texte source mal segmenté ou bruité génère des voisins de mauvaise qualité.
- La similarité n’équivaut pas à la pertinence. Deux passages peuvent être proches en sens tout en ne répondant qu’à un seul la question, c’est pourquoi de nombreux systèmes ajoutent une recherche par mots-clés ou une étape de réclassement supplémentaire.
Se mettre à l’œuvre
Vous pouvez expérimenter sans avoir à mettre en place aucune infrastructure :
- ChromaDB s’exécute localement au sein de Python, sans compte, clé API ni déploiement nécessaire, et il ne faut que quelques lignes de code pour l’installer et l’initialiser. Il convient bien à l’apprentissage et aux petits projets.
- Qdrant propose un niveau gratuit en cloud ainsi qu’un client Python pratique, idéal lorsque l’on souhaite quelque chose proche de l’environnement de production sans devoir gérer soi-même des serveurs. Vérifiez les limites actuelles avant de compter sur ces fonctionnalités.
- pgvector est une extension de Postgres. Si vous utilisez déjà Postgres, elle ajoute la recherche vectorielle sans introduire de base de données distincte, ce qui simplifie l’ensemble du système.
Quelle que soit votre choix, le flux de travail reste identique : choisir un modèle d’incorporation, incorporer votre contenu, stocker les vecteurs, incorporer les requêtes reçues et effectuer une recherche. Les concepts restent les mêmes ; seule la bibliothèque client change.
Le même modèle des deux côtés du score
La similarité cosinus n’a de sens que si la requête et le passage viennent du même modèle.
def cosine(a: list[float], b: list[float]) -> float:
dot = sum(x * y for x, y in zip(a, b))
na = sum(x * x for x in a) ** 0.5
nb = sum(y * y for y in b) ** 0.5
if na == 0 or nb == 0:
return 0.0
return dot / (na * nb)
# query and passage must come from the same embedding model
score = cosine(embed(query), embed(passage))
Le filtre de métadonnées précède la recherche des voisins et décide qui entre dans l’ensemble des candidats.
hits = collection.query(
query_embeddings=[embed(query)],
n_results=8,
where={"tenant_id": tenant_id},
)
Points clés
- Une embedding transforme le sens en position, de sorte que les éléments similaires se retrouvent proches les uns des autres.
- Les scores de similarité classent les candidats ; calibrez tout seuil en fonction de vos propres données.
- Les index ANN tels que HNSW sacrifient un peu de rappel pour des gains de vitesse importants à grande échelle.
- Les filtres de métadonnées transforment la similarité brute en réponses respectant les règles de temps, de source et d’accès.
- Les requêtes et les documents doivent partager un même modèle d’embedding, et la qualité de la récupération dépend autant du découpage en blocs et de la qualité des données que de la base de données elle-même.