Zwei Suchen, zwei blinde Flecken
Die hybride Suche startet eine Stichwortsuche und eine Bedeutungssuche und verschmilzt die beiden Ranglisten. Sie ist wichtig, weil jede Suche verpasst, was die andere findet.
Jemand tippt „Benutzername vergessen“. Keine Hilfeseite enthält „Benutzername“, also findet die Stichwortsuche nichts. Eine andere Person tippt „Bestellnummer 4417“. Die Bedeutungssuche weiß nicht, was 4417 ist.
Das Ziel dieser Lektion
Notiz
Ich kann Stichwort- und Bedeutungssuche zu einer Rangliste verbinden und sagen, wann welche gewinnt.
Schlüsselwörter
| Begriff | Was er bedeutet |
|---|---|
| BM25 | ein Stichwortwert, der seltene Wörter und kurze Seiten belohnt |
| Termhäufigkeit | wie oft ein Wort in einem Dokument vorkommt |
| Inverse Dokumenthäufigkeit | ein hohes Gewicht für Wörter, die wenige Dokumente enthalten |
| Rang | der Platz eines Ergebnisses in einer Liste, ab 1 gezählt |
| Reziproke Rangverschmelzung | Listen verbinden, indem man 1 durch k plus Rang addiert |
| Lexikalische Suche | eine Suche, die die Wörter selbst vergleicht |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
BM25 bewertet die Wörter selbst
Eine Stichwortsuche zählt die Wörter der Anfrage in jedem Dokument. BM25 verbessert diese einfache Zählung auf drei Arten. Ein Wort zählt mehr, wenn nur wenige Dokumente es enthalten. Jede Wiederholung hilft weniger. Ein kurzes Dokument schlägt ein langes mit gleicher Zählung.
Das Ergebnis ist exakt: Ein fehlendes Wort bringt nichts. Das ist Stärke und blinder Fleck zugleich.
Die Bedeutungssuche vergleicht Vektoren
Beim Thema Embeddings wurde Text zu Vektoren, und es wurde deren Richtung verglichen. Hier hat jede Hilfeseite drei erfundene Zahlen: wie sehr sie von Konto, Lieferung und Geld handelt.
Die Anfrage erhält eigene Zahlen, und die nächsten Seiten gewinnen. „Benutzername vergessen“ handelt vor allem vom Konto. Kontoseiten kommen also zuerst, ohne ein gemeinsames Wort. Ein echtes System bekommt seine Vektoren aus einem Embedding-Modell mit Hunderten Zahlen.
Verschmelzen Sie Ränge, nie Werte
BM25-Werte reichen von 0 bis zehn oder mehr. Kosinus-Werte bleiben zwischen 0 und 1. Würde man sie addieren, übertönte eine Liste die andere.
Die reziproke Rangverschmelzung ignoriert Werte und nutzt Ränge. Ein Element erhält aus jeder Liste, die es enthält, 1 / (60 + Rang), und die Punkte werden addiert. Ein Element, das beide Listen mögen, schlägt eines, das nur eine mag.
Durchgerechnetes Beispiel: Bestellnummer 4417
| Seite | Rang nach Wörtern | Rang nach Bedeutung | Verschmolzene Punkte |
|---|---|---|---|
| d4 Paket verfolgen | 1 | 3 | 0,0323 |
| d3 Erstattung | keiner | 1 | 0,0164 |
| d5 Bezahlen | keiner | 2 | 0,0161 |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein häufiger Fehler
Warnung
Viele glauben, hybride Suche bedeute, die beiden Werte zu addieren. Die Skalen unterscheiden sich, also gewinnt immer dieselbe Liste. Addieren Sie stattdessen Ränge mit 1 / (k + Rang), dann haben beide Listen gleiches Gewicht.
Die hybride Suche schreiben, Teil 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const seiten = [' ' { id: \"d1\", text: \"Passwort über die Anmeldeseite zurücksetzen\", v: [9, 0, 1] },' ' { id: \"d2\", text: \"E-Mail-Adresse Ihres Kontos ändern\", v: [8, 1, 0] },' ' { id: \"d3\", text: \"Erstattung für zurückgeschickte Pakete\", v: [1, 5, 8] },' ' { id: \"d4\", text: \"Paket mit der Bestellnummer verfolgen\", v: [0, 9, 1] },' ' { id: \"d5\", text: \"Mit Karte oder Überweisung bezahlen\", v: [1, 1, 9] },' ' { id: \"d6\", text: \"Konto und Daten löschen\", v: [7, 0, 2] },' '];' 'const K1 = 1.5, B = 0.75;' 'function woerter(t) { return t.toLowerCase().split(\" \"); }' 'const mittel = seiten.reduce(function (s, d) { return s + woerter(d.text).length; }, 0) / seiten.length;' > hybrid1.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.
Die hybride Suche schreiben, Teil 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function bm25(anfrage, seite) {' ' let punkte = 0;' ' woerter(anfrage).forEach(function (w) {' ' const n = seiten.filter(function (d) { return woerter(d.text).includes(w); }).length;' ' if (n === 0) return;' ' const idf = Math.log(1 + (seiten.length - n + 0.5) / (n + 0.5));' ' const f = woerter(seite.text).filter(function (x) { return x === w; }).length;' ' const laenge = woerter(seite.text).length;' ' punkte += idf * f * (K1 + 1) / (f + K1 * (1 - B + B * laenge / mittel));' ' });' ' return punkte;' '}' >> hybrid1.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.
Die hybride Suche schreiben, Teil 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function kosinus(a, b) {' ' let prod = 0, sa = 0, sb = 0;' ' for (let i = 0; i < a.length; i++) { prod += a[i] * b[i]; sa += a[i] * a[i]; sb += b[i] * b[i]; }' ' return prod / Math.sqrt(sa * sb);' '}' 'function rangliste(werte) {' ' return seiten.map(function (d, i) { return { id: d.id, s: werte[i] }; })' ' .filter(function (x) { return x.s > 0.01; })' ' .sort(function (x, y) { return y.s - x.s; })' ' .map(function (x) { return x.id; });' '}' 'function verschmelzen(listen) {' ' const summe = {};' ' listen.forEach(function (liste) {' ' liste.forEach(function (id, stelle) { summe[id] = (summe[id] || 0) + 1 / (60 + stelle + 1); });' ' });' ' return Object.keys(summe).sort(function (x, y) { return summe[y] - summe[x]; });' '}' >> hybrid1.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.
Die hybride Suche schreiben, Teil 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const anfragen = [' ' { text: \"Benutzername vergessen\", v: [9, 0, 0] },' ' { text: \"Bestellnummer 4417\", v: [2, 3, 3] },' '];' 'anfragen.forEach(function (q) {' ' const a = rangliste(seiten.map(function (d) { return bm25(q.text, d); }));' ' const b = rangliste(seiten.map(function (d) { return kosinus(q.v, d.v); })).slice(0, 3);' ' console.log(q.text);' ' console.log(\" Wörter:\", a.join(\" \") || \"nichts\");' ' console.log(\" Bedeutung:\", b.join(\" \"));' ' console.log(\" verschmolzen:\", verschmelzen([a, b]).join(\" \"));' '});' >> hybrid1.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.
Starten und die Listen vergleichen
docker exec -w /home/node/project agent node hybrid1.jsHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Das ist ein Platzhalter
Der Run ruft kein Modell auf. Die Vektoren sind drei erfundene Zahlen, und sechs Seiten stehen für einen Index. Ein echtes System enthält Millionen Seiten und holt seine Vektoren aus einem Embedding-Modell.
Die Form bleibt gleich: zwei Ranglisten, dann eine verschmolzene Liste.
Was der Run ausgibt
| Anfrage | Wörter | Bedeutung | Verschmolzen |
|---|---|---|---|
| Benutzername vergessen | nichts | d1 d2 d6 | d1 d2 d6 |
| Bestellnummer 4417 | d4 | d3 d5 d4 | d4 d3 d5 |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.