Die fünf nächsten von fünftausend finden
Ein Vektorspeicher hält Textstücke mit ihren Vektoren und findet die, die einer Frage am nächsten sind. Das ist wichtig, weil der Abruf nur nützt, wenn er schnell ist und die wenigen besten liefert, nicht alle.
Eine Hochschule hat fünftausend Stücke aus Leitfäden und Ordnungen. Eine Studentin tippt eine Frage. Das Programm muss sie neben jedes Stück legen und die drei nächsten holen.
Am Ende dieser Lektion
Notiz
Ich kann Vektoren speichern und die nächsten finden.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Vektorspeicher | eine Liste von Textstücken und ihren Vektoren | |
| Anfrage | die Frage, in einen Vektor verwandelt | |
| Wert | wie nah ein Textstück an der Anfrage liegt | 0.82 |
| Top k | die k Textstücke mit den besten Werten | k = 3 |
| Index | eine Struktur, die fast die ganze Liste überspringt |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein Vektorspeicher ist eine bewertete Liste
Der einfachste Speicher ist eine Liste. Jeder Eintrag hält eine id und einen Vektor. Zum Suchen verwandeln Sie die Frage in einen Vektor, bewerten jeden Eintrag mit dem Kosinus, sortieren die Werte und behalten die ersten k.
Das ist schon die ganze „Suche nach dem nächsten Nachbarn“. Das beste Ergebnis ist der Eintrag mit dem höchsten Wert.
Beispiel: Anfrage [1, 0] gegen drei Einträge
| Eintrag | Vektor | Kosinus | Rang |
|---|---|---|---|
| a | [1, 0] | 1,00 | 1 |
| b | [1, 1] | 0,71 | 2 |
| c | [0, 1] | 0,00 | 3 |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein Zählvektor findet nur gleiche Wörter
Dieses Labor hat kein Einbettungsmodell, also baut das nächste Skript seine Vektoren selbst. Es zählt, wie oft jedes Wort einer festen Liste in einem Stück vorkommt. Gezählt werden nur gleiche Wörter in gleicher Form: „Buch“ und „Bücher“ sind zwei Wörter.
Das ist ein Ersatz. Eine echte Einbettung rückt auch „ausleihen“ und „verleihen“ zusammen. Ein Zählvektor kann das nicht, weil er nur gleiche Wörter sieht.
Das Speicher-Skript schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const woerter = [\"öffnet\", \"schließt\", \"samstag\", \"sonntag\", \"ausleihen\", \"buch\", \"bücher\", \"verspätetes\", \"gebühr\", \"raum\", \"bibliothek\", \"uhr\"];' 'const stuecke = [' ' { id: \"zeiten-woche\", text: \"Von Montag bis Freitag öffnet die Bibliothek um 9 Uhr und schließt um 18 Uhr.\" },' ' { id: \"zeiten-wochenende\", text: \"Am Samstag öffnet die Bibliothek um 10 Uhr und schließt um 14 Uhr. Am Sonntag ist sie geschlossen.\" },' ' { id: \"ausleihe\", text: \"Sie können 8 Bücher für 3 Wochen ausleihen. Jedes Buch lässt sich zweimal online verlängern.\" },' ' { id: \"gebuehren\", text: \"Ein verspätetes Buch kostet 20 Cent pro Tag. Die Gebühr steigt nie über 10 Euro.\" },' ' { id: \"raeume\", text: \"Ein Raum zum Lernen bietet Platz für 6 Personen. Reservieren Sie den Raum am Schalter oder online für 2 Stunden.\" },' '];' > speicher.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Speicher-Skript schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function vektorVon(text) {' ' const gefunden = text.toLowerCase().replace(/[^a-z0-9äöüß ]/g, \" \").split(\" \").filter(function (w) { return w.length > 0; });' ' return woerter.map(function (begriff) {' ' return gefunden.filter(function (wort) { return wort === begriff; }).length;' ' });' '}' 'function kosinus(a, b) {' ' let punkt = 0, sa = 0, sb = 0;' ' for (let i = 0; i < a.length; i++) { punkt += a[i] * b[i]; sa += a[i] * a[i]; sb += b[i] * b[i]; }' ' return sa === 0 || sb === 0 ? 0 : punkt / Math.sqrt(sa * sb);' '}' >> speicher.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das Speicher-Skript schreiben, Teil 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const speicher = stuecke.map(function (stueck) {' ' return { id: stueck.id, vektor: vektorVon(stueck.text) };' '});' 'function suchen(frage, k) {' ' const anfrage = vektorVon(frage);' ' return speicher' ' .map(function (eintrag) { return { id: eintrag.id, wert: kosinus(anfrage, eintrag.vektor) }; })' ' .sort(function (a, b) { return b.wert - a.wert; })' ' .slice(0, k);' '}' 'const frage = \"wann öffnet die bibliothek am samstag\";' 'console.log(\"Frage:\", frage);' 'suchen(frage, 3).forEach(function (treffer) {' ' console.log(treffer.wert.toFixed(2), treffer.id);' '});' >> speicher.js"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ausführen und die Werte lesen
docker exec -w /home/node/project agent node speicher.jsHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Was es ausgibt
| Zeile | Was sie zeigt |
|---|---|
| Frage: wann öffnet die bibliothek am samstag | Die Anfrage |
| 0.58 zeiten-wochenende | Das beste Stück: Es nennt den Samstag |
| 0.44 zeiten-woche | Das nächste: Es hat öffnet und bibliothek |
| 0.00 ausleihe | Das dritte, ohne gemeinsames Wort |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Warum es einen Index gibt
Jeden Eintrag zu bewerten, geht bei fünf Stücken gut, auch bei fünftausend. Bei fünfzig Millionen ist es langsam, weil jede Suche all diese Arbeit wiederholt.
Ein Index gruppiert nahe Vektoren, sodass eine Suche fast die ganze Liste überspringt. Die meisten Indizes sind ungefähr: viel schneller, aber sie können hin und wieder den wahren nächsten Nachbarn verfehlen.
Ein häufiger Fehler
Warnung
Viele glauben, das oberste Ergebnis enthalte immer die Antwort. Es ist nur das nächste Textstück. Liegt nichts im Speicher nah, ist das oberste Ergebnis das am wenigsten schlechte. Schauen Sie auf den Wert, bevor Sie ihm trauen.