Retrieval Augmented Generation
Große Sprachmodelle (Large Language Models, kurz LLMs), wie das GPT-Modell, finden in immer mehr Anwendungsfällen und Firmen ihren Platz. Wie die meisten bekannten Machine-Learning-Modelle, ob zur Bilderkennung oder Vektorisierung von Texten, sind auch diese auf sehr großen und diversen Datensätzen trainiert. Diese Größe ermöglicht es unter anderem, Text in der bekannten Form zu generieren und die Illusion von logischem Denken zu erschaffen. Es ist aber immer wichtig, sich vor Augen zu führen, dass es sich hierbei um kein menschliches Denken und Wissen handelt und jegliches Generieren von Aussagen nicht direkt auf vorhandenem Faktenwissen beruht, sondern auf dem stochastischen Vorhersagen des nächsten Tokens anhand des Kontexts.
Um die Präzision bei Generierung von Fakten in der eigenen Domäne zu erhöhen, ist es möglich, auf ein bekanntes Mittel zurückzugreifen, welches bereits auch bei der Bilderkennung Anwendung gefunden hat: das Fine-Tuning. LLMs bieten für diesen Anwendungsfall noch eine weitere Möglichkeit, die deutlich weniger technisches Wissen und Arbeit benötigt. Denn noch ausschlaggebender für den Inhalt des Outputs eines LLMs als der dahinter liegende Trainingssatz ist der Kontext, der mit der Eingabe an das LLM gegeben wird. Da LLMs diesen verarbeiten und daraufhin das nächste Token raten, ist es für ein präzises Ergebnis in den meisten Fällen zielführender, den Kontext möglichst informativ zu gestalten, anstatt sich auf den Trainingskorpus des Modells zu verlassen. Hier kommt dann das Retrieval Augmented Generation, kurz RAG[1], ins Spiel.
Die Basis eines RAG-Systems
Stellt der Nutzer eine Anfrage an ein LLM, welches in ein RAG-System eingebunden ist, dann durchläuft das System, wie dem Namen zu entnehmen ist, drei Schritte.
- Retrieval – Im Retrieval-Schritt werden Informationen aus einer externen Quelle abgerufen. Das System nimmt die Nutzeranfrage an, um aus der Quelle die geeigneten Daten zu beschaffen.
- Augmentation – Beim Augmentation-Schritt wird der Kontext des LLM um die abgerufenen Informationen erweitert. Dies passiert durch eine zusätzliche System- Message, in der die Daten geschrieben werden, mit dem Vermerk an das LLM, diese Informationen bei der Beantwortung der Frage zu nutzen.
- Generation – Der Generation-Schritt unterscheidet sich nicht sonderlich von einer normalen Textgenerierung durch das LLM, nur dass hier im Kontext mehr Informationen berücksichtigt werden und so das Resultat der Nutzeranfrage präziser beantwortet werden kann.
Der erste Schritt im RAG-System, das Retrieval, ist hierbei der entscheidende. Die anderen beiden Schritte sind meist sehr einfach zu implementieren und werden daher im Folgenden auch nicht tiefer betrachtet. Für das Retrieval im ersten Schritt gibt es aber mehrere entscheidende Faktoren, allen voran die Wahl der externen Quelle und deren Einstellung, die das Ergebnis beeinflussen. Im Grunde spricht nichts dagegen, hier eine bereits vorhandene relationale Datenbank zu nutzen, die mit einer SQL Query die nötigen Daten bereitstellt. Auch ist es möglich, Informationen aus dem Internet zu sammeln und diese in den Kontext zu geben. In den meisten Fällen nutzen RAG-Systeme aber Vektordatenbanken im Hintergrund. Im direkten Vergleich generieren Modelle, die ein Fine-Tuning in der Domäne erhalten haben, schneller passende Antworten als Modelle, die über ein RAG-System erst passende Dokumente suchen müssen, dafür ist der Wissensstand aber eingefroren. Wenn Dokumente sich ändern oder neue hinzukommen, ist es sehr aufwändig, durch erneutes Fine-Tuning den Wissensstand zu aktualisieren. Dieser Aufwand ist es auch, der es meistens teurer macht, Modelle anzupassen, anstatt RAG zu nutzen. Es werden große Daten und damit eine funktionierende Data-Engineering-Pipeline benötigt sowie das nötige Fachwissen und Hardware (bzw. Cloud-Instanzen), um diese Modelle auf ihren Anwendungsfall anzupassen. Daher ist heutzutage für die meisten Nutzer RAG die effizientere Alternative.
Vektordatenbanken und ihre Rolle im RAG-System
Zur Veranschaulichung des gesamten RAG-Prozesses betrachten wir folgendes Beispiel: Ein neuer Mitarbeiter fängt in der Musterfirma an. Die Firma hält ihr gesamtes internes Wissen über Prozesse, Veranstaltungen und weitere für den Job relevante Informationen in einem internen Wiki vor. Der neue Mitarbeiter ist kurz nach Beginn der Stelle krank geworden und versucht nun herauszufinden, wie der Prozess der Übermittlung einer Krankschreibung bei Musterfirma funktioniert. Eine Suche nach Stichwörtern kann oft zu unbefriedigenden Ergebnissen führen und so hat die Firma den Prozess vereinfacht, indem sie ein LLM nutzt, welches Zugriff auf die Artikel im Wiki hat. Die Gesamtmenge der Wiki-Artikel von Musterfirma ist jedoch zu groß, um sie als Kontext dem LLM zur Verfügung zu stellen. Es müssen also vorher die passenden Artikel gefiltert werden, damit diese dann als Referenz zur richtigen Zeit übergeben werden können.
Um eine ideale Referenz für die Erweiterung des Kontexts zu finden, laufen wir in ein altbekanntes Problem des Natural Language Processing (NLP), denn wir müssen zuerst die Intention der Nutzeranfrage verstehen. Nutzt man eine relationale Datenbank, so würde man versuchen, die Schlüsselwörter der Anfrage zu extrahieren, um dann mit diesen und passenden Synonymen alle Artikel zu finden, die genau auf diese Wörter passen. Da Sprache aber nicht eindeutig ist, kann dieses Verfahren unter Umständen in Schwierigkeiten kommen.
Das heutzutage beste Verfahren, um die Intention von natürlicher Sprache für den Computer greifbar zu machen, ist, die Nutzeranfrage in einen mathematischen Vektor zu übersetzen. Wie so etwas funktionieren kann, veranschaulichen wir sehr vereinfacht mit einem alten Machine-Learning-Verfahren, mit dem Wörter vektorisiert werden können: Der Word2Vec-(W2V-)Architektur.
Die Idee der Word Embeddings [2] geht ursprünglich auf die Distributionshypothese zurück, die besagt, dass Wörter mit ähnlichem Kontext dazu neigen, ähnliche Bedeutungen zu haben. Schaut man sich also einen Satz an wie: "Der Sprinter läuft eine Bestzeit." und nutzt das Wort läuft als zentrales Wort, so würden die Wörter Der, Sprinter, Eine und Bestzeit den Kontext für dieses Wort bilden. Innerhalb dieses Kontextes könnte auch das Wort rennt stehen und die Bedeutung des Satzes würde gleich bleiben. Somit ist anzunehmen, dass läuft und rennt eine ähnliche Bedeutung haben. Mit diesem Grundgedanken kann man nun ein Machine-Learning-Modell trainieren, welches versucht, für einen beliebigen Kontext das zentrale Wort zu raten.
Die Grafik zeigt, wie der linke Vektor, dessen Dimensionsgröße der Anzahl der verschiedenen Wörter im Trainingssatz entspricht, genutzt wird, um den Vektor rechts (mit den gleichen Dimensionen) zu raten Der linke Vektor repräsentiert den Kontext, indem ein Wert entsprechend der Anzahl des Kontextworts an der zuständigen Dimension eingetragen ist. Der rechte Vektor hat nur den Wert 1 an der Dimension, die das zentrale Wort repräsentiert. Die Distanz zwischen dem Ideal rechts und dem in Wirklichkeit geratenen Wert in der Mitte wird genutzt, um das Modell zu trainieren. Der Hidden Layer und seine Gewichte werden nach dem Training genutzt, um die Vektorrepräsentationen der einzelnen Wörter zu bestimmen. Daraus ergibt sich dann auch, dass ähnliche Wörter wie laufen und rennen auch ähnliche Vektoren haben, da deren Kontext beim Trainieren des Modells ähnlich war und somit das Ergebnis beim Raten auch ähnlich war.
Heute werden Texte mit Transformern, einem komplexeren und präziseren Verfahren, vektorisiert. Dies zeigt sich vor allem bei der Einordnung von Sätzen und Textabschnitten. Für ein generelles Verständnis, wie aus Wörtern und daraus folgenden Texten Vektoren werden können, sollte die knappe Erklärung zu W2V aber reichen.
Beispiele in der Praxis
Wie kann nun so eine Pipeline im Detail aufgebaut werden? Im Grunde sind drei bis vier verschiedene Elemente zu identifizieren. Wir betrachten im Folgenden alle einmal und schauen, welche realen Anwendungen und Tools hier für die Umsetzung zur Verfügung stehen.
Als ersten Schritt in der Pipeline nutzt man ein Transformer-Modell, um seinen Textdatensatz in Vektoren zu übersetzen. Dazu werden die einzelnen Dokumente in Abschnitte (Chunks) unterteilt und diese Abschnitt für Abschnitt in Vektoren übersetzt. Diese werden dann zusammen mit den Metadaten (u. a. Referenz zum Ursprungsdokument) in einer Vektordatenbank gespeichert. Stellt der neue Mitarbeiter eine Anfrage an das LLM, um Informationen zu den internen Prozessen der Musterfirma zu erhalten, so wird diese Anfrage vom selben (zwingend notwendig) Transformer-Modell in einen Vektor übersetzt und kann nun durch eine mathematische Distanz (aufgrund der hohen Dimensionalität meist die Cosinus-Distanz) mit den Einträgen in der Vektordatenbank verglichen werden. Die Texte der Abschnitte mit der kleinsten Distanz können jetzt geladen und in den Kontext des LLMs geschrieben werden. Dieses hat nun alle Informationen, um die Anfrage des Nutzers zu beantworten.
Vektordatenbanken
Bei Vektordatenbanken gibt es eine Vielzahl verschiedener Implementierungen, auf die man zurückgreifen kann. An erster Stelle wollen wir die Open-Source-Lösungen von Weaviate, Qdrant und ChromaDBerwähnen, mit denen wir in der Praxis gute Erfahrungen gemacht haben. Alle drei bieten eine kostenlose Self-hosted-Möglichkeit an und bis auf ChromaDB gibt es hier auch Cloud-Lösungen. Pinecone ist eine proprietäre Lösung, die vor allem durch Performance und Skalierbarkeit punkten kann. Auch bieten mittlerweile Elastic Search sowie Postgres die Möglichkeit, Vektoren zu speichern und abzurufen. Zwar sind diese nicht immer äquivalent in ihrer Performance wie dedizierte Vektordatenbanken, jedoch hat sich vor allem die Postgres Lösung aus eigener Erfahrung gut bewährt. Des Weiteren ist hier der Vorteil, dass diese Datenbank eventuell bereits im Einsatz ist und nicht eine neue Technologie ins Haus geholt werden muss. Nutzt man bereits einen Cloud-Anbieter, dann lohnt es sich auch dort nachzuschauen, welche Lösungen angeboten werden, da diese sich oft leicht in bestehende Pipelines integrieren lassen.
Embedding-Modelle
Aus eigener Erfahrung können wir vor allem die Modelle von OpenAI (embeddings-3) und nomic-embed-text-1.5 empfehlen. Das erste Modell überzeugt vor allem durch seine Präzision, besonders in der "large" Variante, ist jedoch eine proprietäre Lösung. Beim zweiten Modell handelt es sich um eine Open-Source-Alternative, die in diversen Metriken mit dem "3-small"-Modell von OpenAI mithalten kann.
Large-Language-Modelle
Auch bei den LLMs ist es sinnvoll, zwischen proprietären und Open-Source-Lösungen zu unterscheiden. Die zwei herausragenden Modelle im proprietären Bereich sind GPT-4o von OpenAI, welches mit der Azure-Cloud oder direkt über OpenAI genutzt werden kann, sowie Claude 3.5-Sonnett von Anthropic, welches unter anderem über AWS und Google-Cloud nutzbar ist. Im Open-Source-Bereich und somit auch lokal anwendbar sind die zwei besten Modelle aktuell LLama-3.2 sowie Mistral-NeMo.
Serving/Deployment
Es gibt verschiedene Tools, die die Kommunikation zwischen Code und LLM erleichtern. Nutzt man zum Beispiel Ollama oder LMStudio lokal, so kann man die meisten LLMs herunterladen und die Serving-Tools kümmern sich um die Anbindung. Dies ist notwendig, wenn man nicht einen der großen Cloudbetreiber nutzt, die diese Serving-Funktionalität intern bereitstellen. Ein weiterer Vorteil ist, dass diese Serving-Modelle wie ein OpenAI-API-kompatibler Server agieren, das heißt, dass jeglicher Code, der mit den LLMs interagiert, unabhängig vom Modell, gleich geschrieben (nach OpenAI-Standard) werden kann. Die zwei zuvor genannten Tools sind vor allem beim lokalen Hosting auf der eigenen Maschine interessant. Bei Deployment nach Produktion empfehlen wir jedoch vLLM oder NVIDIA NIM, da diese deutlich besser skalieren und somit Anfragen von mehreren Nutzern gleichzeitig ermöglichen.
Optimierung der Pipeline
Bei der Optimierung der Pipeline werden wir im Folgenden drei Metriken betrachten:
- Genauigkeit: Wie hoch ist die Wahrscheinlichkeit, dass beim Retrieval-Schritt die passenden Dokumente gefunden werden?
- Geschwindigkeit: Wie lange dauert es, die Dokumente zur Nutzeranfrage zu finden?
- Speicher: Wie viel Speicher (Festplatte und RAM) braucht das System? Diese Metrik wirkt sich vor allem auf die Kosten aus.
Nun schauen wir uns ein paar Methoden zur Optimierung und deren Auswirkungen auf die oben genannten Metriken an.
Embedding Size
Je nach Wahl des Embedding-Modells können Vektoren mit verschiedenen Dimensionsgrößen erstellt werden. Als Faustregel gilt, dass Vektoren mit größerer Dimensionszahl mehr Aussagekraft und dadurch auch beim Retrieval eine höhere Genauigkeit erzielen als kleinere Vektoren. Gleichzeitig benötigen größere Vektoren natürlich mehr Speicher und die notwendige Zeit für das Retrieval erhöht sich, da die Berechnung der Distanz komplexer wird. Vektoren mit unter 256 Dimensionen sind im Regelfall nicht zu empfehlen. Außerdem verringert sich der Beitrag zur Genauigkeit asymptotisch mit der Anzahl an Dimensionen. Dementsprechend ist der Vorteil eines Vektors mit 3072 Dimensionen gegenüber einem Vektor mit 1024 Dimensionen kleiner als der Vorteil von 1024 zu 256 Dimensionen.
Wurde das Embedding-Modell mit dem Verfahren des Matryoshka Representation Learning trainiert, wie zum Beispiel die beiden oben vorgestellten Modelle, so kann die Zahl der Dimensionen dynamisch für den Use Case verkleinert werden, um so Geschwindigkeitsvorteile zu erhalten.
Vector Quantization
Vector Quantization beschreibt die Methode, die Genauigkeit der Präzision der einzelnen Dimensionen eines Vektors zu verringern. Es werden hier also die Bits, die die einzelne Dimension des Vektors nutzt, verringert. Hierzu existieren diverse Methoden und die meisten lassen sich auf Vektoren unabhängig vom Embedding-Modell anwenden. Wählt man Quantization-Methoden wie Binary oder Scalar Quantization, kann man ohne großen Verlust der Genauigkeit die Geschwindigkeit des Retrieval-Schritts um ein Vielfaches verbessern und gleichzeitig auch den Speicherbedarf stark reduzieren. Eine noch höhere Kompression erhält man mit Methoden wie Product Quantization (Vektoren verbrauchen bis zu 64mal weniger Speicher). Dies passiert aber auf Kosten der anderen beiden Metriken, Genauigkeit und Geschwindigkeit.
Speicherwahl
Die Wahl der Speicherart, ob Festplattenspeicher oder RAM, ist eine direkte Wahl, welche Metrik, also Speicher (und damit Kosten) oder Geschwindigkeit, optimiert werden soll. Systeme mit größerem RAM Speicher sind teurer, bieten aber den Vorteil einer deutlichen Geschwindigkeitszunahme beim Retrieval. Kombiniert mit den beiden zuvor genannten Methoden kann auch die Genauigkeit reduziert werden, um den Speicherbedarf und die Kosten zu verkleinern.
Indexierung
Ähnlich wie bei relationalen Daten ist es auch bei Vektordaten sinnvoll, ab einer gewissen Menge diese mit einem Index zu versehen. Dieser erhöht zwar die Speichernutzung, verringert dafür aber die Geschwindigkeit für das Retrieval.
Chunksize
Zuerst einmal lässt sich sagen, je kleiner die Chunksize (Abschnittsgröße), desto mehr Vektoren entstehen. Dies führt zu einer erhöhten Speichergröße in der Datenbank sowie einer längeren Laufzeit beim Retrieval. Jedoch ist die Möglichkeit bei der Erhöhung der Genauigkeit enorm. Gleichzeitig leidet dann der kontextuelle Bezug der einzelnen Textpassagen zueinander unter einer kleineren Chunksize. Je nach Textgrundlage ist hier ein großes Potenzial vorhanden, jedoch haben wir die Erfahrung gemacht, dass sich schwer eine allgemeine Aussage zur optimalen Chunking Größe treffen lässt. Der Effekt hängt hierbei zu stark vom Datensatz ab. Texte, die viel Inhalt in kurzen Abschnitten vermitteln, benötigen eher eine kleine Chunksize, während Texte, die eher längere Abschnitte besitzen, auch mit einer längeren Chunksize zurechtkommen. Wir empfehlen daher, mit einer Chunksize von 512-1024 Tokens anzufangen, eventuell sogar 256 Tokens. Die genaue Länge wird dann besser über eine Evaluation der RAG-Pipeline bestimmt.
Evaluation des RAG-Systems
Um ein RAG-System zu evaluieren, wird ein Datensatz aus vorbereiteten Fragen und Antworten benötigt. Die Fragen beziehen sich auf den Datensatz, während die Antworten zwei Kerninformationen enthalten: den Textabschnitt, in dem das RAG-System die Antwort finden kann, sowie eine ausformulierte Beispielantwort. Dieser Datensatz kann von einem Domänenexperten zusammengestellt werden oder aber, um die Größe des Datensatzes zu erhöhen, auch ein LLM beauftragt werden, einen solchen Datensatz anhand der Textabschnitte zu generieren (synthetischer Frage-Antwort-Katalog). Dieser sollte dann vom Domänenexperten auf Richtigkeit und Relevanz geprüft werden. Mit dem Katalog kann danach die Pipeline abgefragt werden und mit einer automatisierten Überprüfung der Ergebnisse werden zwei Präzisions-Scores erstellt. Zuerst kann festgestellt werden, bei wie vielen der Fragen der passende Textabschnitt gefunden wurde, als zweites, bei wie vielen der Fragen mit einem passenden Textabschnitt eine zufriedenstellende Antwort generiert wurde. Letzteres kann auch automatisiert durch ein LLM geprüft werden, indem es die Beispielantwort mit der generierten Antwort vergleicht. Die oben genannten Optimierungen der Pipeline beziehen sich auf eine Verbesserung des ersten Scores, also das Finden der passenden Textabschnitte. Der zweite Fall bedarf einer Überprüfung des Prompts, mit dem das LLM eingestellt wurde. Eine detaillierte Erläuterung der Evaluation würde hier den Rahmen sprengen und sollte bei Bedarf weiter recherchiert werden.
Fazit
In diesem Artikel haben wir uns das Verfahren des Retrieval Augmented Generation angeschaut, welches es ermöglicht, LLMs mit externem Wissen auszustatten. Hierfür wird zwischen der Nutzeranfrage und der Antwort des LLMs eine Wissensdatenbank eingefügt. Sollte das LLM die Antwort auf die Frage nicht durch sein Weltwissen beantworten können, kann dieses die Wissensdatenbank nutzen, um deren Inhalt bei der Generierung der Antwort zu berücksichtigen. In den meisten Fällen handelt es sich hierbei um Vektordatenbanken, die sich als besonders geeignet erwiesen haben, um Textwissen maschinell mit der Nutzeranfrage abzugleichen und somit passende Wissensbausteine zu finden. Wir haben uns mit realen Systemen auseinandergesetzt, die heutzutage genutzt werden, um eine derartige Pipeline aufzubauen. Als Bausteine kamen dafür Vektordatenbanken, Embedding-Modelle, Large-Language-Modelle und Serving/Deployment-Systeme in Betrachtung.
Abschließend haben wir uns mit der Optimierung einer derartigen Pipeline beschäftigt. Die meisten Methoden weisen Vor- und Nachteile auf und sollten somit genauer evaluiert werden. Hierzu haben wir uns abschließend kurz mit der Evaluation von RAG-Systemen beschäftigt. Diese Informationen können dann genutzt werden, um verschiedene Optimierungseinstellungen zu vergleichen, um so eine ideale RAG-Pipeline zu erstellen, die es LLMs ermöglicht, eigenes Domänenwissen einzuarbeiten.
- Retrieval-Augmented Generation for Knowledge-Intensive NLP Task, NeurIPS Proceedings,
- Efficient Estimation of Word Representations in Vector Space