Stockage des vecteurs
- Leçon 15
- 55 min
- Module 5 : Fichiers, vecteurs et cycle de vie
Leçon 15 sur 16 · Bases de données et stockage pour les applications d'IAStockage des vecteurs
1. Choisir le stockage selon la charge de travail
2. Concevoir un schéma relationnel
3. Accéder aux données depuis une API
4. Améliorer la performance des requêtes
5. Fichiers, vecteurs et cycle de vie
Ce que vous saurez faire
Gardez les vecteurs des billets de soutien dans PostgreSQL pour qu’une recherche ne mélange jamais deux modèles. Trouvez vite des billets similaires avec un index approximatif. Obtenez quand même assez de résultats quand la recherche filtre par équipe.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Ce qu’est un vecteur de billet et comment
<=>mesure la distance entre deux vecteurs, dans De SQLite à PostgreSQL. Qu’un vecteur est une copie créée à partir du texte du billet, dans Propriété et source de vérité. - Les plans de requête et les index, dans Lire un plan de requête et Indexer de façon réfléchie.
- Facultatif : le cours Les plongements pour la recherche explique les plongements dans Les plongements pour la recherche, et le rappel dans Mesurer la récupération. Cette leçon explique les éléments qu’elle utilise.
Ce qu’il vous faut sur votre ordinateur
- Le projet après Modèles de stockage objet.
python -m pytestaffiche95 passed, 1 deselected(95 tests réussis, 1 test exclu). - Facultatif : le modèle de plongement du premier module (
requirements-embed.txt, environ 1 Go avec le modèle), pour une deuxième version de plongement. Sans lui, vous pouvez faire toutes les autres étapes; la leçon montre sa sortie.
Cette leçon ajoute deux migrations, 010_embedding_versions.sql et 011_ticket_embeddings_hnsw.sql, ainsi qu’un fichier de tests. Le point de terminaison des billets similaires que vous avez copié dans la dernière leçon commence à fonctionner.
Deux cartes d’une même ville
Section intitulée « Deux cartes d’une même ville »Vous avez deux cartes de la même ville. Sur la première, la gare se trouve dans la case C4. Sur la deuxième, dessinée par une autre entreprise, C4 est un lac. Les deux cartes sont correctes. Mais une case d’une carte ne veut rien dire sur l’autre, et rien sur le papier ne vous avertit.
Un vecteur est une position sur une carte du sens. Chaque modèle de plongement (embedding) dessine sa propre carte.
Le problème : deux modèles, aucune erreur
Section intitulée « Le problème : deux modèles, aucune erreur »Dans Propriété et source de vérité, les vecteurs des billets provenaient de intfloat/multilingual-e5-small. Priya veut essayer un deuxième modèle, sentence-transformers/all-MiniLM-L6-v2. Il produit lui aussi 384 nombres par billet. La base de données accepte donc ses vecteurs dans la même colonne vector(384).
Le cours a créé des vecteurs MiniLM pour les 200 billets, à côté des vecteurs e5. Il a ensuite cherché les billets les plus proches de T-30002, « Charged twice for order LK-182074 » (double facturation pour la commande LK-182074). D’abord avec des vecteurs e5 des deux côtés, puis avec le vecteur e5 de T-30002 comparé aux vecteurs MiniLM :
ticket_id | subject | distance-----------+-----------------------------------+---------- T-30050 | Charged twice for order LK-710804 | 0.025 T-30168 | Charged twice for order LK-919420 | 0.028 T-30064 | Charged twice for order LK-167846 | 0.034(3 rows)
ticket_id | subject | distance-----------+-----------------------------+---------- T-30011 | Gift card balance | 0.874 T-30105 | Discount code did not apply | 0.895 T-30153 | Discount code did not apply | 0.895(3 rows)La deuxième requête s’est exécutée sans erreur et a renvoyé des résultats absurdes, sans aucun avertissement. Sur le grand jeu de données, une requête e5 comparée aux vecteurs MiniLM a trouvé 0,4 % des bons voisins. La base de données ne peut pas voir le problème : deux listes de 384 nombres se ressemblent. Même MiniLM comparé à MiniLM ne partage que 60 % des voisins trouvés par e5. Chaque modèle a sa propre idée de ce qui est « similaire ».
L’idée : chaque vecteur indique sa version
Section intitulée « L’idée : chaque vecteur indique sa version »Une version de plongement nomme une recette : un modèle, une révision, un préfixe. La migration 010 en fait une règle de la base de données :
- Une table
embedding_versionsliste chaque recette et marque exactement une version comme courante. Un index partiel couvre seulement les lignes qui correspondent à sa conditionWHERE. Ici, un index partiel unique sur les lignesWHERE is_currentautorise une seule valeurtrue. - Chaque ligne de vecteur reçoit
embedding_version, une clé étrangère vers cette table. La clé deticket_embeddingsdevient(ticket_id, embedding_version). Un billet peut donc avoir un vecteur par version. - Chaque ligne de vecteur reçoit aussi
source_sha256: la somme de contrôle SHA-256 du texte du billet utilisé pour la créer. Si quelqu’un modifie le billet plus tard, la somme de contrôle ne correspond plus. Le vecteur est alors périmé : une copie d’un ancien texte.
La recherche suit ensuite une règle : utiliser la version courante des deux côtés. Voici T-30002 avec les deux versions :
Description textuelle du diagramme
Le billet T-30002 est l’original, dans la table tickets. La somme de contrôle SHA-256 de son objet et de son corps commence par bf5d3388. La table ticket_embeddings a comme clé ticket_id plus embedding_version, et contient deux lignes pour T-30002. La première est la version e5-small-v1, avec la somme de contrôle bf5d3388 et les nombres 0,0501, 0,0100, -0,0195 et 381 autres. La deuxième est la version minilm-v1, avec la même somme de contrôle et 384 autres nombres qui signifient autre chose. L’index HNSW partiel contient seulement les lignes e5-small-v1. La table embedding_versions marque e5-small-v1 comme version courante et minilm-v1 comme version non courante. Exactement une version est courante, et chaque recherche l’utilise des deux côtés. Les mélanger ne produit aucune erreur et trouve 0,4 % des bons voisins.
Sam demande : « Pourquoi garder les vecteurs MiniLM? » Pour comparer les deux modèles sur de vraies recherches avant de changer. Quand Priya décide, un seul UPDATE fait de MiniLM la version courante. Les anciens vecteurs peuvent attendre que plus personne n’en ait besoin. Le balayage de la prochaine leçon les compte comme « vecteurs d’autres versions ».
Recherche exacte ou approximative
Section intitulée « Recherche exacte ou approximative »Le service de soutien a maintenant 10 000 vecteurs de billets dans le grand jeu de données. Les agents veulent des billets similaires pendant qu’ils tapent.
Imaginez que vous cherchez les dix cafés les plus proches de votre hôtel. La méthode exacte : mesurer la distance jusqu’à chaque café de la ville, puis trier. Vous êtes certain de la réponse, mais c’est long. La méthode rapide : demander à quelques personnes du quartier qui connaissent beaucoup de monde. Elles vous envoient chez leurs voisins, qui vous dirigent vers d’autres. Vous arrivez vite, mais vous pourriez manquer un café dans une rue tranquille.
PostgreSQL avec l’extension pgvector peut faire les deux. Sans index, ORDER BY embedding <=> query LIMIT 10 est une recherche exacte : elle calcule la distance jusqu’à chaque vecteur. Avec un index HNSW (Hierarchical Navigable Small World), c’est une recherche approximative. L’index est un graphe de voisins organisé en couches. Une recherche entre par la couche supérieure, se déplace vers des vecteurs toujours plus proches et garde les meilleurs candidats. Le paramètre hnsw.ef_search (40 par défaut) définit la longueur de cette liste. Une liste plus longue trouve davantage de vrais voisins, mais prend plus de temps.
Quelle est la qualité d’une recherche « approximative »? Le rappel@10 est la proportion des dix vrais voisins les plus proches que la recherche approximative a renvoyés. Voici un exemple inventé. La recherche exacte renvoie A, B, C, D, E, F, G, H, I et J. L’index renvoie A, B, C, D, E, F, G, K, L et M. Sept sur dix sont corrects, donc le rappel@10 est de 0,7.
Les billets du cours ajoutent une particularité. Beaucoup de billets inventés partagent un texte, par exemple de nombreux billets « Charged twice » (double facturation). Il y a 10 000 vecteurs, mais seulement 5 484 textes différents. Si K a exactement la même distance que H, alors K est une réponse tout aussi bonne. Le cours rapporte donc aussi le rappel en comptant les égalités. Une réponse compte si elle est aussi proche que le dixième résultat exact. Le rappel strict avec le réglage par défaut était de 0,70; en comptant les égalités, il était de 0,96.
La migration 011 construit l’index. Sur le grand jeu de données, cela a pris 0,36 seconde et 12 Mo, à côté d’une table de 35 Mo. Voici 200 recherches sur 10 000 vecteurs, chacune exécutée avec les deux méthodes :
- Index HNSW
- recherche exacte
Description textuelle du graphique
Le graphique montre le rappel@10, en comptant les égalités, selon le temps médian par recherche, pour 200 recherches sur 10 000 vecteurs. L’index HNSW avec ef_search 10 a pris 0,15 ms et trouvé 0,82 des voisins. Sur ses 200 recherches, 69 ont renvoyé moins de 10 billets. Avec ef_search 40, la valeur par défaut, il a pris 0,22 ms et trouvé 96 % des voisins. Avec ef_search 100, il a pris 0,33 ms et trouvé 97,5 % des voisins. La recherche exacte a pris 6,6 ms et les a tous trouvés, par définition.
La valeur par défaut, ef_search 40, offre ici le meilleur compromis : 0,22 ms au lieu de 6,6 ms, et 96 % des voisins. Passer à 100 prend 50 % plus de temps et ajoute 1,5 point de rappel. Descendre à 10 économise peu et fait perdre beaucoup : 69 des 200 recherches ont renvoyé moins de 10 billets.
La recherche de Larkfield, avec un filtre
Section intitulée « La recherche de Larkfield, avec un filtre »Le point de terminaison d’Amira, GET /v1/tickets/{id}/similar, a des filtres : team (équipe) et status (statut). Un agent de l’équipe de connexion demande seulement des billets de connexion. Le grand jeu de données contient 1 046 vecteurs de connexion sur 10 000. Le cours a mesuré les mêmes 200 recherches avec le filtre team = 'login', et le résultat a surpris. 176 des 200 recherches ont renvoyé moins de 10 billets. Le rappel, en comptant les égalités, est tombé à 0,14.
La raison est l’ordre des opérations. L’index ne connaît pas les équipes. Il fournit ses 40 meilleurs candidats, et PostgreSQL applique le filtre seulement après. Vous pouvez le voir dans psql sur le grand jeu de données, avec T-399991, un billet de facturation concernant une double facturation :
Description textuelle du diagramme
La requête demande 10 billets de connexion similaires à T-399991, un billet de facturation. Sans balayage itératif, l’index HNSW fournit ses 40 vecteurs les plus proches. Il y avait 39 candidats, tous des billets de facturation. Le filtre team = login n’en garde aucun, donc la recherche renvoie 0 résultat. Sur 200 recherches avec ce filtre, 176 ont renvoyé trop peu de résultats. Avec le balayage itératif réglé sur relaxed_order, PostgreSQL lit de nouveau l’index, avec plus de candidats à chaque tour. Il continue jusqu’à ce que 10 lignes passent le filtre. La recherche renvoie 10 résultats. Sur 200 recherches, 5 ont renvoyé trop peu de résultats. Cela prend 9,1 ms par recherche, près des 12,5 ms de la recherche exacte.
SET hnsw.iterative_scan = off; ... found: 0SET hnsw.iterative_scan = relaxed_order; ... found: 10pgvector 0.8 a ajouté la solution, le balayage itératif. Avec relaxed_order (ordre assoupli), l’index continue de chercher jusqu’à ce qu’assez de lignes passent le filtre. Sur les 200 recherches, 5 ont renvoyé trop peu de résultats, et le rappel est monté à 0,957. Cela a un coût : 9,1 ms par recherche, près de la recherche exacte avec le même filtre (12,5 ms). Un filtre rare force l’index à lire une grande partie du graphe. repository.similar_tickets règle relaxed_order pour chaque recherche et trie de nouveau les k lignes. En effet, l’ordre « assoupli » peut être légèrement incorrect.
Description textuelle du graphique
Sur 200 recherches avec le filtre team = login, 176 ont renvoyé moins de 10 billets avec l’index HNSW seul. Avec le balayage itératif, 5 l’ont fait. La recherche exacte renvoie 10 billets dès que 10 billets de connexion existent. Aucune n’a donc renvoyé trop peu de résultats.
Pourquoi la version est écrite dans le SQL
Section intitulée « Pourquoi la version est écrite dans le SQL »L’index de la migration 011 est lui aussi un index partiel : WHERE embedding_version = 'e5-small-v1'. PostgreSQL peut l’utiliser seulement s’il voit, pendant la planification, que la requête demande cette version. psycopg prépare une instruction qui s’exécute souvent. Après 5 exécutions, PostgreSQL peut passer à un plan générique, créé sans les valeurs des paramètres. Le cours a forcé ce plan sur le grand jeu de données pour montrer la différence :
SET plan_cache_mode = force_custom_plan; -> Index Scan using ticket_embeddings_e5_small_v1_hnsw on ticket_embeddings eSET plan_cache_mode = force_generic_plan; -> Seq Scan on ticket_embeddings e Filter: (embedding_version = $1)Avec $1, PostgreSQL ne peut pas prouver que l’index partiel couvre les lignes. Le plan générique balaie donc tous les vecteurs et les trie. Ainsi, similar_tickets écrit la version dans le texte SQL avec psycopg.sql.Literal. C’est sûr ici, pour deux raisons : psycopg met la valeur entre guillemets, et elle vient de la table embedding_versions, jamais de la requête. Chaque valeur envoyée par un utilisateur, comme team, reste un paramètre, comme l’expliquait Requêtes sûres.
pgvector ou une base de données vectorielle spécialisée
Section intitulée « pgvector ou une base de données vectorielle spécialisée »Tomás reprend son idée du premier module : « Déplaçons les vecteurs vers une base de données vectorielle. Elle a aussi des métadonnées, donc on peut y mettre le texte et le statut des billets. On n’aurait plus besoin de PostgreSQL pour la recherche. »
Mei répond avec les mesures de cette leçon. Une base de données vectorielle spécialisée est conçue pour une seule tâche, et elle peut être le bon outil. Utilisez-en une quand vous avez des dizaines de millions de vecteurs. Ou quand la charge de recherche doit croître indépendamment de la base de données principale. Mais c’est une deuxième copie, et chaque copie a un coût :
- Les filtres ont besoin des métadonnées au même endroit. Le filtre d’équipe ci-dessus fonctionne parce que les vecteurs et
tickets.teamsont dans une même base, dans une même requête. Dans un système séparé, vous copiez l’équipe, le statut et le client dans chaque vecteur. Vous devez les garder synchronisés à chaque changement. - La suppression doit l’atteindre. Quand un client est effacé, les vecteurs de l’autre système doivent aussi disparaître, avec leur texte copié. La prochaine leçon montre à quel point supprimer « partout » est déjà difficile avec deux systèmes de stockage.
- Ce n’est jamais l’original. Le texte reste dans PostgreSQL, comme le premier module l’a décidé. Tout système de stockage de vecteurs peut alors être reconstruit.
Pour 10 000 vecteurs, pgvector a répondu en 0,22 ms avec un rappel de 96 %. Il l’a fait dans la base de données qui contient déjà les filtres. Le service de soutien garde pgvector. Mei note la taille à laquelle l’équipe réexaminera ce choix.
Exercice guidé : les versions, l’index et le point de terminaison
Section intitulée « Exercice guidé : les versions, l’index et le point de terminaison »-
Copiez les deux migrations et le fichier de tests depuis l’instantané. Sur macOS ou Linux :
Fenêtre de terminal S=../nextia-learning-labs/C20/snapshots/end-of-m05cp $S/migrations/010_embedding_versions.sql $S/migrations/011_ticket_embeddings_hnsw.sql migrations/cp $S/tests/test_vectors.py tests/Sur Windows :
Copy-Item "$S\migrations\010_*.sql", "$S\migrations\011_*.sql" migrations\etCopy-Item "$S\tests\test_vectors.py" tests\, avec$Scomme dans la leçon précédente. -
Lisez
011_ticket_embeddings_hnsw.sql. Sa première ligne est-- migrate: no-transaction(migration sans transaction). La commandeCREATE INDEX CONCURRENTLYne peut pas s’exécuter dans une transaction, comme dans Migrations. Appliquez les deux migrations :Fenêtre de terminal python -m ticket_api.migrateapplied 010_embedding_versionsapplied 011_ticket_embeddings_hnswDatabase is up to date (11 migrations). -
Exécutez les tests :
python -m pytest. La dernière ligne est101 passed, 1 deselected(101 tests réussis, 1 non sélectionné). Un nouveau test prouve que les recherches approximative et exacte donnent les mêmes résultats sur les petites données. Un autre prouve qu’un vecteur d’une autre version n’est jamais comparé. -
Dans
psql, examinez la table avec\d ticket_embeddings. La clé est maintenant(ticket_id, embedding_version), et la liste des index se termine par :"ticket_embeddings_e5_small_v1_hnsw" hnsw (embedding vector_cosine_ops) WHERE embedding_version = 'e5-small-v1'::text -
Démarrez l’API avec
fastapi dev, puis demandez les cinq billets les plus semblables à T-30002 :Fenêtre de terminal curl -s 'http://127.0.0.1:8000/v1/tickets/T-30002/similar?k=5'{"embedding_version":"e5-small-v1","items":[{"ticket_id":"T-30050","subject":"Charged twice for order LK-710804","team":"billing","status":"pending","created_at":"2026-09-28T19:48:43Z","distance":0.025122697629444524},{"ticket_id":"T-30168","subject":"Charged twice for order LK-919420","team":"billing","status":"pending","created_at":"2026-10-05T15:19:12Z","distance":0.02812230084139522},{"ticket_id":"T-30064","subject":"Charged twice for order LK-167846","team":"billing","status":"closed","created_at":"2026-09-29T15:45:40Z","distance":0.033563429076056694},{"ticket_id":"T-30076","subject":"Charged twice for order LK-140806","team":"billing","status":"open","created_at":"2026-09-30T09:50:54Z","distance":0.03663163940528669},{"ticket_id":"T-30095","subject":"Charged twice for order LK-539653","team":"billing","status":"resolved","created_at":"2026-10-01T11:09:23Z","distance":0.04021954056983024}]}Les cinq résultats sont d’autres billets « Charged twice » (facturé deux fois), du plus proche au plus éloigné. Sur Windows, tapez
curl.exeau lieu decurl. -
Ajoutez un filtre qui ne correspond pas à la double facturation :
curl -s 'http://127.0.0.1:8000/v1/tickets/T-30002/similar?k=3&team=login'. Vous obtenez quand même trois billets : T-30192, T-30065 et T-30025. Ils portent tous le texte « Locked out of my account » (accès à mon compte bloqué), à des distances de 0,109 à 0,120. Ils sont éloignés, mais ce sont les billets de connexion les plus proches. -
Facultatif, avec le modèle. Si vous avez installé
requirements-embed.txtdans le premier module, créez des vecteurs avec MiniLM comme deuxième version :Fenêtre de terminal python -m scripts.embed --version minilm-v1Embedded 200 tickets with sentence-transformers/all-MiniLM-L6-v2 in 3.2 s.Stored 200 vectors of version minilm-v1.Dans
psql, exécutezSELECT version, model, is_current FROM embedding_versions;etSELECT embedding_version, count(*) FROM ticket_embeddings GROUP BY 1;. Vous voyez deux versions de 200 vecteurs chacune, et seulee5-small-v1est courante. Le point de terminaison répond toujours avec"embedding_version":"e5-small-v1". Gardez ces vecteurs pour la prochaine leçon.
Erreur courante
Une recherche filtrée renvoie moins de résultats que demandé
Symptôme : similar?k=10&team=login renvoie 3 billets, ou aucun, même si l’équipe en a des centaines. Il n’y a aucune erreur.
Cause : l’index approximatif fournit un nombre fixe de candidats (hnsw.ef_search, 40 par défaut). Le filtre s’applique ensuite. Si les candidats les plus proches appartiennent à d’autres équipes, le filtre les élimine tous. Sur les grandes données, 176 des 200 recherches de billets de connexion ont ainsi renvoyé trop peu de résultats.
Diagnostic : exécutez la requête dans psql avec EXPLAIN. Le plan affiche Index Scan using ticket_embeddings_e5_small_v1_hnsw (parcours avec cet index), avec Filter: (team = 'login'::text) (filtre sur l’équipe) sur la jointure. Comptez les résultats avec SET hnsw.iterative_scan = off;, puis avec relaxed_order (ordre assoupli). Pour T-399991 sur les grandes données : 0, puis 10.
Solution : activez le parcours itératif pour la recherche, comme le fait similar_tickets avec SET LOCAL hnsw.iterative_scan = relaxed_order. Il faut pgvector 0.8 ou une version plus récente. Pour un filtre qui sélectionne toujours très peu de résultats, la recherche exacte peut être tout aussi rapide. Un index partiel par équipe est l’autre option, si les équipes sont peu nombreuses et fixes.
Variante autonome : observer le comportement sur les grandes données
Section intitulée « Variante autonome : observer le comportement sur les grandes données »Cette étape prend environ une minute et remplace temporairement vos petites données. Chargez les grandes données avec python -m scripts.load --size large --reset. Dans psql, comptez les billets de connexion que l’index trouve pour T-399991, d’abord avec le parcours itératif désactivé :
SET hnsw.iterative_scan = off;SELECT count(*) AS found FROM ( SELECT e.ticket_id FROM ticket_embeddings e JOIN tickets t USING (ticket_id) WHERE e.embedding_version = 'e5-small-v1' AND t.team = 'login' ORDER BY e.embedding <=> (SELECT embedding FROM ticket_embeddings WHERE ticket_id = 'T-399991') LIMIT 10) AS s;Prédisez ensuite le nombre avec SET hnsw.iterative_scan = relaxed_order;, puis exécutez la requête. Enfin, prédisez l’effet de SET hnsw.ef_search = 400; avec le parcours désactivé, puis essayez. Rechargez les petites données avec python -m scripts.load --reset quand vous avez terminé. Le chargement supprime aussi les vecteurs MiniLM de l’étape 7. Si vous les avez créés, refaites l’étape 7 : la prochaine leçon les utilise.
Afficher la réponse
Avec le parcours désactivé, found (nombre trouvé) vaut 0; avec relaxed_order, il vaut 10. Avec le parcours désactivé et ef_search = 400, l’exécution du cours a aussi trouvé 10. Une liste dix fois plus longue a atteint des candidats assez éloignés pour inclure des billets de connexion. Mais la session construit alors cette liste plus longue pour chaque recherche, filtrée ou non. Un filtre plus rare nécessiterait une liste encore plus longue. Le parcours itératif fait ce travail supplémentaire seulement quand le filtre l’exige. C’est pourquoi le projet modifie le parcours, pas ef_search.
Quand utiliser un index approximatif, et ce qu’il coûte
Section intitulée « Quand utiliser un index approximatif, et ce qu’il coûte »Utilisez la recherche exacte tant qu’elle est assez rapide. Avec 200 vecteurs, son coût est imperceptible, et elle donne toujours les bons résultats. Ajoutez un index HNSW quand la recherche exacte devient trop lente pour la personne qui attend. Pour Larkfield, cela arrive à 10 000 vecteurs : 6,6 ms, plusieurs fois par page.
L’index a des coûts que le graphique ne montre pas :
- Rappel inférieur à 1. Certains vrais voisins sont manqués. Mesurez le rappel sur vos propres données, en tenant compte des égalités, avant de vous fier à un réglage.
- Temps de construction et mémoire. 0,36 s et 12 Mo pour 10 000 vecteurs. Les deux augmentent avec les données, et chaque insertion met le graphe à jour.
- Les filtres demandent de l’attention. Activez le parcours itératif et testez les filtres rares.
- Un index par version. Un index partiel couvre une version de plongement. Une nouvelle version courante a besoin de son propre index avant la transition, sinon ses recherches sont exactes.
Vérification des connaissances
Section intitulée « Vérification des connaissances »Vérifiez votre compréhension
Un collègue stocke les vecteurs MiniLM dans la même table sans colonne de version. Les recherches fonctionnent toujours. Quel problème survient, et quelles deux parties de la migration 010 l'empêchent?
Afficher la réponse
Une recherche peut comparer un vecteur de requête e5 avec des vecteurs MiniLM. Les deux ont 384 nombres, donc PostgreSQL ne signale aucune erreur. Mais le résultat n’a aucun sens : 0,4 % des bons voisins sur les grandes données. La migration 010 donne à chaque vecteur une embedding_version (version de plongement), une clé étrangère vers embedding_versions. Elle rend aussi exactement une version courante, avec un index partiel unique sur is_current. La recherche utilise alors la version courante des deux côtés.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Expliquez le rappel@10 avec les chiffres du cours, et pourquoi le cours le présente aussi en tenant compte des égalités.
Afficher la réponse
Le rappel@10 est la proportion des dix voisins exacts les plus proches que la recherche approximative a renvoyés. Avec l’index HNSW et ef_search à 40, le rappel strict était de 0,70. Plusieurs billets inventés ont le même texte : 5 484 textes pour 10 000 vecteurs. Plusieurs billets sont donc exactement aussi proches que le dixième résultat exact, et chacun constitue une réponse tout aussi bonne. En comptant ces égalités, le rappel était de 0,96, à 0,22 ms par recherche plutôt que 6,6 ms.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Prédisez : la version dans similar_tickets devient un paramètre normal (%s). Les tests réussissent. Que se passe-t-il sur les grandes données après quelques appels au point de terminaison, et pourquoi?
Afficher la réponse
Après 5 exécutions de l’instruction préparée, PostgreSQL peut choisir un plan générique, construit sans la valeur de la version. Il ne peut alors pas prouver que l’index partiel (WHERE embedding_version = 'e5-small-v1') couvre les lignes. Il parcourt et trie donc tous les vecteurs, selon le plan que le cours a imposé avec force_generic_plan. Les réponses restent correctes, mais chaque recherche prend environ autant de temps qu’une recherche exacte. Les petites données masquent ce problème, car un parcours de 200 lignes est rapide de toute façon.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Les vecteurs de deux modèles se ressemblent et se mélangent sans erreur : 0,4 % des bons voisins. Chaque vecteur a maintenant une version de plongement, et exactement une version est courante.
source_sha256enregistre le texte qui a servi à créer chaque vecteur. Un billet modifié apparaît donc comme un vecteur périmé.- La recherche exacte lit chaque vecteur (6,6 ms pour 10 000). L’index HNSW est approximatif : 0,22 ms et un rappel de 0,96 en comptant les égalités, avec ef_search à 40 par défaut.
- Un filtre s’applique après l’index : 176 des 200 recherches de billets de connexion ont renvoyé trop peu de résultats. Le parcours itératif de pgvector a corrigé ce problème (5 recherches incomplètes), à 9,1 ms par recherche.
- L’index partiel exige la version comme valeur littérale dans le SQL; un paramètre peut produire un plan générique qui parcourt chaque vecteur.
- Votre projet contient les migrations 010 et 011 et un point de terminaison
/similarfonctionnel.python -m pytestaffiche101 passed, 1 deselected.
Leçon suivante : Restauration et conservation