Dokumente in Abschnitte teilen (Chunking)
Kurz gesagt: Chunking teilt ein Dokument nach Wörtern oder nach Sätzen in Abschnitte, mit einer Überlappung, damit jeder Abschnitt seinen eigenen Vektor bekommt und die Antwort nicht in der Mitte zerschnitten wird.
Problem
Die ganze Seite in einem Vektor
Der Shop hat Rückgaberegeln. Es ist eine Seite mit sechs Sätzen:
// die Rückgaberegeln: sechs Sätzestring[] policy =[ "die ware kannst du 14 tage nach der lieferung zurückgeben", "die ware muss dabei in der originalverpackung sein", "ein kurier holt sie kostenlos ab", "das geld ist in fünf tagen wieder auf deiner karte", "ware mit eigenem aufdruck ist von der rückgabe ausgeschlossen", "bei schäden schreibe uns innerhalb von zwei tagen",];
// die Frage des Kundenvar question = "wann bekomme ich mein geld zurück";Der Kunde fragt „wann bekomme ich mein geld zurück“. Die Antwort ist der vierte Satz. Wenn die ganze Seite als ein Text im Index liegt, ist sie ein Vektor:
Console.WriteLine($"Wörter: {words.Length}");var doc = string.Join(" ", policy);Console.WriteLine($"ganzes Dokument: {Cosine(v, space.OfText(doc)):F3}");Console.WriteLine($"der Antwortsatz: {Cosine(v, space.OfText(policy[3])):F3}");Wörter: 51ganzes Dokument: 0.744der Antwortsatz: 0.762Die ganze Seite hat 0,744, der Antwortsatz allein 0,762. Der Vektor der Seite ist eine Mischung aus sechs Themen: Frist, Verpackung, Kurier, Geld, Ausnahmen, Schäden. Je länger die Seite, desto mehr verwischt sie. In einer Wissensbasis mit vielen Dokumenten verliert so eine Seite gegen kurze Texte, die die Frage gar nicht beantworten.
Und wenn sie doch gefunden wird, gehen wegen 10 Wörtern alle 51 in den Prompt. Echte Richtlinien haben Tausende Wörter, und drei solche Seiten füllen das Kontextfenster und die Rechnung.
Die Idee
Abschnitte
Das Dokument wird in Abschnitte (Chunks) geschnitten, bevor es in den Index kommt. Jeder Abschnitt ist ein eigener Eintrag: sein eigener Text, sein eigener Vektor und von welchem Dokument er stammt. Die Suche aus der vorigen Lektion findet den Abschnitt, nicht die ganze Seite.
Der einfachste Schnitt geht nach Anzahl der Wörter: die ersten 10 Wörter, die nächsten 10 und so weiter bis zum Ende. Für die Vektoren in der Lektion nehmen wir dasselbe Modell wie in der Lektion über Vektoren, mit zwei Unterschieden: Es lernt auch aus dem Text der Richtlinie (sonst sind ihre Wörter unbekannt), und es hat 16 Achsen statt 8. Alle Abschnitte handeln von Rückgaben, und mit 8 Achsen unterscheiden sie sich nur wenig.
var space = new Space([.. text, .. policy], stop, 16);var words = string.Join(" ", policy).Split(' ');var v = space.OfText(question);Sieh es dir an
Abschnitte von 10 Wörtern
Die Funktion schneidet die Wörter in Abschnitte. Vorerst ist die Überlappung 0: Jeder Abschnitt beginnt dort, wo der vorige endet.
// Abschnitte von size Wörtern; die Schrittweite ist size - overlapstatic List<string> Chunk( string[] words, int size, int overlap){ var pieces = new List<string>(); int step = size - overlap; for (int from = 0; from < words.Length; from += step) { int end = Math.Min(from + size, words.Length); pieces.Add(string.Join(" ", words[from..end])); if (end == words.Length) break; } return pieces;}Eine andere Funktion gibt jeden Abschnitt mit seiner Ähnlichkeit zur Frage aus und zeigt, welcher am nächsten ist:
// jeder Abschnitt mit seiner Ähnlichkeit; der nächste am Endestatic void Show(Space space, int[] v, List<string> pieces){ int best = 0; double top = -1; for (int i = 0; i < pieces.Count; i++) { double c = Cosine(v, space.OfText(pieces[i])); Console.WriteLine($"{i + 1} {c:F3} {pieces[i]}"); if (c > top) { top = c; best = i; } } Console.WriteLine($"→ {best + 1}");}Show(space, v, Chunk(words, 10, 0));1 0.589 die ware kannst du 14 tage nach der lieferung zurückgeben2 0.512 die ware muss dabei in der originalverpackung sein ein kurier3 0.793 holt sie kostenlos ab das geld ist in fünf tagen4 0.645 wieder auf deiner karte ware mit eigenem aufdruck ist von5 0.676 der rückgabe ausgeschlossen bei schäden schreibe uns innerhalb von zwei6 0.434 tagen→ 3Am nächsten ist Abschnitt 3 (0,793). Er endet mit „das geld ist in fünf tagen“, und „wieder auf deiner karte“ steht in Abschnitt 4. Das Modell liest „das Geld ist in fünf Tagen“ und weiß nicht, wohin es zurückkommt. Der Schnitt fiel mitten in die Antwort.
Die Idee
Überlappung
Der Schnitt ist blind: Er weiß nicht, wo der Satz endet. Die Lösung ist eine Überlappung: Jeder Abschnitt beginnt etwas vor dem Ende des vorigen. Bei Abschnitten von 10 Wörtern und einer Überlappung von 4 beginnt jeder Abschnitt 6 Wörter nach dem vorigen, und seine letzten 4 Wörter sind die ersten 4 des nächsten.
So wiederholen sich die Ränder, und ein Satz, der in einem Abschnitt zerschnitten ist, ist im Nachbarabschnitt ganz. Der Preis sind mehr Abschnitte und wiederholte Wörter.
Sieh es dir an
Mit einer Überlappung von 4
Show(space, v, Chunk(words, 10, 4));1 0.589 die ware kannst du 14 tage nach der lieferung zurückgeben2 0.586 nach der lieferung zurückgeben die ware muss dabei in der3 0.403 muss dabei in der originalverpackung sein ein kurier holt sie4 0.507 ein kurier holt sie kostenlos ab das geld ist in5 0.762 das geld ist in fünf tagen wieder auf deiner karte6 0.645 wieder auf deiner karte ware mit eigenem aufdruck ist von7 0.626 eigenem aufdruck ist von der rückgabe ausgeschlossen bei schäden schreibe8 0.702 ausgeschlossen bei schäden schreibe uns innerhalb von zwei tagen→ 5Jetzt ist Abschnitt 5 am nächsten (0,762), und er enthält den ganzen Satz: „das geld ist in fünf tagen wieder auf deiner karte“. Es sind 8 Abschnitte statt 6.
// Abschnitte von size Wörtern; die Schrittweite ist size - overlapstatic List<string> Chunk( string[] words, int size, int overlap){ var pieces = new List<string>(); int step = size - overlap; for (int from = 0; from < words.Length; from += step) { int end = Math.Min(from + size, words.Length); pieces.Add(string.Join(" ", words[from..end])); if (end == words.Length) break; } return pieces;}
var v = space.OfText(question);Show(space, v, Chunk(words, 10, 0));// Abschnitte von size Wörtern; die Schrittweite ist size - overlapstatic List<string> Chunk( string[] words, int size, int overlap){ var pieces = new List<string>(); int step = size - overlap; for (int from = 0; from < words.Length; from += step) { int end = Math.Min(from + size, words.Length); pieces.Add(string.Join(" ", words[from..end])); if (end == words.Length) break; } return pieces;}
var v = space.OfText(question);Show(space, v, Chunk(words, 10, 0));// Abschnitte von size Wörtern; die Schrittweite ist size - overlapstatic List<string> Chunk( string[] words, int size, int overlap){ var pieces = new List<string>(); int step = size - overlap; for (int from = 0; from < words.Length; from += step) { int end = Math.Min(from + size, words.Length); pieces.Add(string.Join(" ", words[from..end])); if (end == words.Length) break; } return pieces;}
var v = space.OfText(question);Show(space, v, Chunk(words, 10, 4));Schritt für Schritt, in Worten
- Die Rückgaberegeln: 51 Wörter. Orange ist der Satz mit der Antwort: „das geld ist in fünf tagen wieder auf deiner karte“.
- Abschnitte von 10 Wörtern ohne Überlappung: 6 Abschnitte, einer nach dem anderen.
- Die Ähnlichkeit jedes Abschnitts zur Frage. Am nächsten ist Abschnitt 3 (0,793).
- Aber Abschnitt 3 endet mit „in fünf tagen“: „wieder auf deiner karte“ steht im nächsten Abschnitt. Die Antwort ist in der Mitte zerschnitten.
- Überlappung 4: Jeder Abschnitt beginnt 6 Wörter nach dem vorherigen, die Ränder wiederholen sich. 8 Abschnitte.
- Am nächsten ist Abschnitt 5 (0,762), und er enthält den ganzen Satz.
- Ohne Überlappung ist die Antwort auf zwei Abschnitte verteilt; mit Überlappung enthält ein Abschnitt sie ganz. Der Preis: 8 Abschnitte statt 6 und wiederholte Wörter.
Probier es aus
Sag die Ausgabe voraus
Ein Satz von 8 Wörtern in Abschnitten von 4 mit einer Überlappung von 1. Wie viele Abschnitte kommen heraus?
var ws = "die lieferung in eine filiale kostet 3 euro".Split(' ');Console.WriteLine(Chunk(ws, 4, 1).Count);Die Schrittweite ist 4 − 1 = 3. Die Abschnitte beginnen bei den Wörtern 0, 3 und 6. Der dritte besteht nur aus zwei Wörtern, „3 euro“, und reicht bis zum Ende, also hält die Schleife an. Ohne Überlappung wären es zwei Abschnitte.
Probier es aus
Ergänze
Wie viele Wörter später beginnt jeder nächste Abschnitt? Was gehört an die Stelle von ___?
// Abschnitte von size Wörtern; die Schrittweite ist size - overlap
static List<string> Chunk(
string[] words, int size, int overlap)
{
var pieces = new List<string>();
int step = size - ;
for (int from = 0; from < words.Length; from += step)
{
int end = Math.Min(from + size, words.Length);
pieces.Add(string.Join(" ", words[from..end]));
if (end == words.Length)
break;
}
return pieces;
}overlap. Die Schrittweite ist die Größe minus die Überlappung: Bei 10 und 4 beginnt der nächste Abschnitt 6 Wörter später, und 4 Wörter wiederholen sich.
Probier es aus
Finde den Fehler
Ein Kollege schneidet mit einer Überlappung von 4, aber ein Teil der Richtlinie steckt in keinem Abschnitt. In welcher Zeile steckt der Fehler?
Wähle die Zeile mit dem Fehler.
Die Schrittweite ist size + overlap: Die Abschnitte beginnen bei 0, 14, 28 und 42, und je 4 Wörter dazwischen landen nirgends. Diese Wörter werden nie gefunden. Es muss size - overlap heißen.
Die Idee
Nach Sätzen
Eine Anzahl von Wörtern ist grob. Dokumente haben natürliche Grenzen: Sätze, Absätze, Überschriften. Wenn ein Abschnitt ein ganzer Satz ist, wird die Antwort nicht zerschnitten:
Show(space, v, [.. policy]);1 0.589 die ware kannst du 14 tage nach der lieferung zurückgeben2 0.582 die ware muss dabei in der originalverpackung sein3 0.163 ein kurier holt sie kostenlos ab4 0.762 das geld ist in fünf tagen wieder auf deiner karte5 0.586 ware mit eigenem aufdruck ist von der rückgabe ausgeschlossen6 0.620 bei schäden schreibe uns innerhalb von zwei tagen→ 4Der Antwortsatz gewinnt (0,762). Aber sieh dir Satz 3 an: „ein kurier holt sie kostenlos ab“, 0,163. „sie“ ist die Ware aus dem Satz davor. Für sich allein sagt der Abschnitt weder, was der Kurier abholt, noch, dass es um Rückgaben geht. Fragt der Kunde „kommt ein kurier für die rückgabe“, hat dieser Abschnitt einen Teil seiner Bedeutung verloren.
Deshalb halten echte Systeme die Verbindung: Sie setzen den Titel des Dokuments und des Abschnitts vor jeden Abschnitt, fügen einen Satz von jedem Nachbarn hinzu oder schneiden nach Absätzen. Und sie behalten für jeden Abschnitt, woher er stammt: Dokument, Abschnitt, Stelle. So belegt die Antwort ihre Quelle.
Die Idee
Wann es wehtut
- Zu kleine Abschnitte verlieren die Bedeutung: „innerhalb von 14 tagen“ ohne das Wort „rückgabe“ sagt nicht, wofür die 14 Tage gelten.
- Zu große Abschnitte verwischen wie die ganze Seite und füllen den Prompt.
- Die Überlappung bringt Wiederholungen. Die Abschnitte 5 und 6 haben dieselben Wörter. Wenn beide unter den ersten k sind, landet wiederholter Text im Prompt. Benachbarte Abschnitte eines Dokuments werden zusammengeführt, bevor sie zum Modell gehen.
- Tabellen, Listen und Code schneidet man nicht nach Anzahl der Wörter. Eine zerschnittene Zeile einer Preistabelle ergibt keinen Sinn: Solche Dokumente schneidet man nach ihrer Struktur.
- Änderst du den Schnitt, änderst du den Index. Alle Abschnitte werden neu geschnitten und neu berechnet.
Es gibt keine Größe, die für alle Dokumente richtig ist. Größe und Überlappung wählt man mit einem Testset von Fragen, wie k in der vorigen Lektion: siehe Bewertung.
Genauer, für Neugierige
Die Größe in echten Systemen. Abschnitte misst man in Tokens, nicht in Wörtern: oft 200 bis 800 Tokens mit einer Überlappung von 10 bis 20 % (Beispielzahlen). Wie viele Tokens ein Text hat, steht in der Lektion über Tokens. Hier sind die Abschnitte 10 Wörter lang und die Sätze fast genauso lang, deshalb ist die Überlappung groß.
Wann die Überlappung die Antwort ganz hält. Ein Satz von L Wörtern ist in irgendeinem Abschnitt ganz, wenn L ≤ overlap + 1. Bei einer Überlappung von 4 sind das Sätze bis 5 Wörter. Ein längerer Satz ist nur dann ganz, wenn ein Schnitt genau vor ihm fällt: Die Übung zeigt, dass das davon abhängt, wo die Schnitte fallen.
Schneiden nach Bedeutung. Manche Systeme schneiden dort, wo sich die Vektoren benachbarter Sätze voneinander entfernen: Ein Themenwechsel ist eine Grenze. Das kostet pro Satz einen Aufruf des Modells für Vektoren.
Übung
Eine Prüfung für den Schnitt
Löse es in deinem Editor.
Schreibe eine Prüfung: Finde für Abschnitte von 10 Wörtern und eine Überlappung von 0 bis 4 den Abschnitt, der der Frage am nächsten ist, und gib aus, wie viele Abschnitte es sind, die Ähnlichkeit und ob der Abschnitt den ganzen Antwortsatz enthält.
Überlappung 0: 6 Abschnitte, 0.793, Antwort ganz: neinÜberlappung 1: 6 Abschnitte, 0.701, Antwort ganz: neinÜberlappung 2: 7 Abschnitte, 0.762, Antwort ganz: jaÜberlappung 3: 7 Abschnitte, 0.849, Antwort ganz: neinÜberlappung 4: 8 Abschnitte, 0.762, Antwort ganz: jaDie Überlappungen 2 und 4 halten die Antwort ganz, 0, 1 und 3 nicht. Bei Überlappung 3 ist die Ähnlichkeit am höchsten (0,849), aber der Abschnitt endet mit „fünf tagen wieder“, und „auf deiner karte“ ist abgeschnitten. Eine höhere Ähnlichkeit bedeutet keine ganze Antwort. Ob die Antwort ganz ist, hängt davon ab, wo die Schnitte fallen, deshalb läuft so eine Prüfung bei jeder Änderung des Schnitts.
Weitere Aufgaben
- Sätze bis 16 Wörter. Füge ganze Sätze zu einem Abschnitt zusammen, bis er über 16 Wörter geht. Welcher Abschnitt ist am nächsten?
- Ohne Wiederholungen. Wenn sich zwei der ersten k Abschnitte überlappen, führe sie zu einem zusammen.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Embeddings: Bedeutung als Vektoren · Das Kontextfenster · Kosten und Latenz: Budget, Cache, ein kleineres Modell · Semantische Suche: die k nächsten
- Geht weiter in: RAG: Antworten aus den Dokumenten · RAG bewerten: Wird der Abschnitt gefunden, und ist die Antwort belegt · Der Support-Assistent
- Aufgaben zum Thema: Semantische Suche und RAG