Trouver les trois plus proches parmi cinq mille
Une base de vecteurs garde des morceaux de texte avec leurs vecteurs et retrouve ceux qui sont les plus proches d’une question. Cela compte, car la recherche n’est utile que si elle est rapide et rend les meilleurs résultats, pas tous.
Une université a cinq mille morceaux de guides et de règlements. Un étudiant tape une question. Le programme doit la comparer à chaque morceau et rendre les trois plus proches.
À la fin de cette leçon
Remarque
Je peux ranger des vecteurs dans une base et retrouver les plus proches.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Base de vecteurs | une liste de morceaux et de leurs vecteurs | |
| Requête | la question, transformée en vecteur | |
| Score | la proximité d’un morceau avec la requête | 0.82 |
| Top k | les k morceaux aux meilleurs scores | k = 3 |
| Index | une structure qui évite de lire presque toute la liste |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une base de vecteurs est une liste notée
La base la plus simple est une liste. Chaque élément contient un id et un vecteur. Pour chercher, on transforme la question en vecteur, on note chaque élément avec le cosinus, on trie les scores et on garde les k premiers.
C’est tout ce que veut dire « recherche du plus proche voisin ». Le meilleur résultat est l’élément au score le plus haut.
Exemple travaillé : requête [1, 0] sur trois éléments
| Élément | Vecteur | Cosinus | Rang |
|---|---|---|---|
| a | [1, 0] | 1,00 | 1 |
| b | [1, 1] | 0,71 | 2 |
| c | [0, 1] | 0,00 | 3 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un vecteur de comptes trouve les mêmes mots
Ce labo n’a pas de modèle de plongements : le script suivant fabrique donc ses vecteurs. Il compte combien de fois chaque mot d’une liste fixe apparaît dans un morceau. Il retire aussi un « s » final, pour que « livres » et « livre » se retrouvent.
C’est un substitut. Un vrai plongement rapproche aussi « emprunter » de « prêter ». Un vecteur de comptes en est incapable, car il ne voit que les mêmes mots.
Écrire le script de la base, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const vocabulaire = [\"ouvre\", \"ferme\", \"samedi\", \"dimanche\", \"emprunter\", \"livre\", \"retard\", \"amende\", \"salle\", \"médiathèque\", \"vendredi\", \"semaine\"];' 'const morceaux = [' ' { id: \"horaires-semaine\", texte: \"La médiathèque ouvre à 9 h et ferme à 18 h du lundi au vendredi.\" },' ' { id: \"horaires-weekend\", texte: \"Le samedi, la médiathèque ouvre à 10 h et ferme à 14 h. Elle est fermée le dimanche.\" },' ' { id: \"emprunt\", texte: \"Vous pouvez emprunter 8 livres pour 3 semaines. Vous pouvez prolonger un livre deux fois en ligne.\" },' ' { id: \"retards\", texte: \"Un livre rendu en retard coûte 20 centimes par jour. L’amende s’arrête à 10 euros.\" },' ' { id: \"salles\", texte: \"Une salle d’étude accueille 6 personnes. Réservez une salle à l’accueil ou en ligne pour 2 heures.\" },' '];' > base.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de la base, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function racine(mot) { return mot.length > 3 && mot.endsWith(\"s\") ? mot.slice(0, -1) : mot; }' 'function vecteurDe(texte) {' ' const mots = texte.toLowerCase().replace(/[^a-z0-9àâçéèêëîïôûù ]/g, \" \").split(\" \").filter(function (m) { return m.length > 0; }).map(racine);' ' return vocabulaire.map(function (terme) {' ' return mots.filter(function (mot) { return mot === terme; }).length;' ' });' '}' 'function cosinus(a, b) {' ' let dot = 0, sa = 0, sb = 0;' ' for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; sa += a[i] * a[i]; sb += b[i] * b[i]; }' ' return sa === 0 || sb === 0 ? 0 : dot / Math.sqrt(sa * sb);' '}' >> base.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de la base, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = morceaux.map(function (morceau) {' ' return { id: morceau.id, vecteur: vecteurDe(morceau.texte) };' '});' 'function chercher(question, k) {' ' const requete = vecteurDe(question);' ' return base' ' .map(function (element) { return { id: element.id, score: cosinus(requete, element.vecteur) }; })' ' .sort(function (a, b) { return b.score - a.score; })' ' .slice(0, k);' '}' 'const question = \"à quelle heure ouvre la médiathèque le samedi\";' 'console.log(\"Question :\", question);' 'chercher(question, 3).forEach(function (resultat) {' ' console.log(resultat.score.toFixed(2), resultat.id);' '});' >> base.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer le script et lire les scores
docker exec -w /home/node/project agent node base.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce qu’il affiche
| Ligne | Ce qu’elle montre |
|---|---|
| Question : à quelle heure ouvre la médiathèque le samedi | La requête |
| 0.77 horaires-weekend | Le meilleur morceau : il nomme le samedi |
| 0.58 horaires-semaine | Le suivant : il a ouvre et médiathèque |
| 0.00 emprunt | Le troisième, sans aucun mot en commun |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Pourquoi un index existe
Noter chaque élément va bien pour cinq morceaux, ou cinq mille. C’est lent pour cinquante millions, car chaque recherche refait tout ce travail.
Un index regroupe les vecteurs proches : une recherche peut alors ignorer presque toute la liste. La plupart des index sont approximatifs : bien plus rapides, ils peuvent parfois rater le vrai plus proche.
Une erreur fréquente
Attention
Beaucoup de gens croient que le premier résultat contient toujours la réponse. C’est seulement le morceau le plus proche. Si rien dans la base n’est proche, le premier résultat est le moins mauvais. Regardez le score avant de lui faire confiance.