paint-brush
KI für Wissensmanagement: Iteration auf RAG mit der QE-RAG-Architekturvon@shanglun
4,226 Lesungen
4,226 Lesungen

KI für Wissensmanagement: Iteration auf RAG mit der QE-RAG-Architektur

von Shanglun Wang27m2023/09/12
Read on Terminal Reader
Read this story w/o Javascript

Zu lang; Lesen

Retrieval-Augmented Generation (RAG) ist eine beliebte Architektur für die Entwicklung leistungsstarker LLM-Apps. Die aktuelle Architektur weist jedoch einige echte Einschränkungen auf. Wir gehen durch die Erstellung einer RAG-Anwendung und schauen uns dann an, wie wir sie mithilfe einer neuen Architektur namens QE-RAG verbessern können
featured image - KI für Wissensmanagement: Iteration auf RAG mit der QE-RAG-Architektur
Shanglun Wang HackerNoon profile picture


Als die LLM-Revolution Gestalt annimmt, ist der Hype der kommerziellen Entwicklung gewichen. Während die anfängliche Welle der Aufregung nachlässt, wird generative KI nicht mehr als allwissende Blackbox betrachtet, sondern eher als ein wesentliches, wenn auch äußerst leistungsfähiges Werkzeug im Arsenal eines Ingenieurs. Infolgedessen verfügen Unternehmer und Technologen nun über einen immer ausgereifteren Satz an Werkzeugen und Techniken, mit denen sie LLM-Anwendungen entwickeln können.


Einer der interessantesten Anwendungsfälle für LLMs liegt im Bereich des Wissensmanagements. Spezialisierte LLMs, die entweder auf der GPT-Technologie von OpenAI oder auf Open-Source-Modellen wie LLaMa 2 und Flan-T5 basieren, werden auf clevere Weise zur Verwaltung großer Datenmengen eingesetzt. Wo früher Organisationen mit großen Textdatensätzen auf Textsuchtechniken wie Fuzzy-Matching oder Volltextindizierung angewiesen waren, haben sie jetzt Zugriff auf ein leistungsstarkes System, das die Informationen nicht nur findet, sondern sie auch zeiteffizient und leserfreundlich zusammenfasst Mode.


In diesem Anwendungsfall hat sich die Retrieval-Augmented Generation Architecture (RAG) als herausragende Architektur mit enormer Flexibilität und Leistung herausgestellt. Mit dieser Architektur können Organisationen einen Arbeitsbestand schnell indizieren, semantische Abfragen darauf durchführen und auf der Grundlage des Korpus informative und überzeugende Antworten auf benutzerdefinierte Abfragen generieren. Mehrere Unternehmen und Dienste sind entstanden, um Implementierungen der RAG-Architektur zu unterstützen, was deren Durchhaltevermögen unterstreicht.


So effektiv RAG auch sein kann, diese Architektur weist auch einige echte Einschränkungen auf. In diesem Artikel werden wir die RAG-Architektur untersuchen, ihre Einschränkungen identifizieren und eine verbesserte Architektur vorschlagen, um diese Einschränkungen zu lösen.


Wie bei allen anderen Artikeln möchte ich mit anderen Technologen und KI-Enthusiasten in Kontakt treten. Wenn Sie darüber nachdenken, wie diese Architektur verbessert werden kann, oder Ideen zu KI haben, die Sie diskutieren möchten, zögern Sie bitte nicht, uns zu kontaktieren ! Sie finden mich auf Github oder LinkedIn, die Links finden Sie in meinem Profil sowie am Ende dieses Artikels.


Inhaltsübersicht

  • Retrieval Augmented Generation (RAG)-Architektur
  • Einschränkungen der RAG-Architektur
  • Vorschlag von QE-RAG oder Question-Enhanced RAG
  • Abschluss


Retrieval Augmented Generation (RAG)-Architektur

Mit Namen wie RAG, Flan und LLaMa wird die KI-Community in naher Zukunft wahrscheinlich keine Auszeichnungen für futuristische und stilvolle Namen gewinnen. Allerdings verdient die RAG-Architektur auf jeden Fall eine Auszeichnung für die Kombination zweier äußerst leistungsfähiger Techniken, die durch die Entwicklung von LLMs zur Verfügung gestellt wurden – kontextbezogene Dokumenteinbettung und Prompt Engineering.


Im einfachsten Fall handelt es sich bei der RAG-Architektur um ein System, das die eingebettete Vektorsuche verwendet, um die für eine Frage relevantesten Teile des Korpus zu finden, die Teile in eine Eingabeaufforderung einzufügen und dann mithilfe der Eingabeaufforderungstechnik sicherzustellen, dass die Die Antwort basiert auf den in der Eingabeaufforderung angegebenen Auszügen. Wenn das alles etwas verwirrend klingt, lesen Sie bitte weiter, denn ich werde jede Komponente der Reihe nach erklären. Ich werde auch Beispielcode beifügen, damit Sie mitmachen können.


Das Einbettungsmodell

Zuallererst erfordert ein effektives RAG-System ein leistungsstarkes Einbettungsmodell. Das Einbettungsmodell wandelt ein natürliches Textdokument in eine Reihe von Zahlen oder einen „Vektor“ um, der grob den semantischen Inhalt des Dokuments darstellt. Vorausgesetzt, das Einbettungsmodell ist gut, können Sie die semantischen Werte zweier verschiedener Dokumente vergleichen und mithilfe der Vektorarithmetik feststellen, ob die beiden Dokumente semantisch ähnlich sind.


Um dies in Aktion zu sehen, fügen Sie den folgenden Code in eine Python-Datei ein und führen Sie ihn aus:


 import openai from openai.embeddings_utils import cosine_similarity openai.api_key = [YOUR KEY] EMBEDDING_MODEL = "text-embedding-ada-002" def get_cos_sim(input_1, input_2): embeds = openai.Embedding.create(model=EMBEDDING_MODEL, input=[input_1, input_2]) return cosine_similarity(embeds['data'][0]['embedding'], embeds['data'][1]['embedding']) print(get_cos_sim('Driving a car', 'William Shakespeare')) print(get_cos_sim('Driving a car', 'Riding a horse'))



Der obige Code generiert die Einbettungen für die Phrasen „Fahren eines Autos“, „William Shakespeare“ und „Reiten auf einem Pferd“, bevor er sie mithilfe des Kosinus-Ähnlichkeitsalgorithmus miteinander vergleicht. Wir würden erwarten, dass die Kosinusähnlichkeit höher ist, wenn die Phrasen semantisch ähnlich sind, also sollten „Fahren eines Autos“ und „Reiten auf einem Pferd“ viel näher beieinander liegen, wohingegen „Fahren eines Autos“ und „William Shakespeare“ unterschiedlich sein sollten.


Sie sollten sehen, dass laut dem Einbettungsmodell von OpenAI, ada-002, der Ausdruck „ein Auto fahren“ zu 88 % dem Ausdruck „auf einem Pferd reiten“ und zu 76 % dem Ausdruck „William Shakespeare“ ähnelt. Das bedeutet, dass das Einbettungsmodell die von uns erwartete Leistung erbringt. Diese Bestimmung der semantischen Ähnlichkeit ist die Grundlage des RAG-Systems.


Die Idee der Kosinusähnlichkeit ist bemerkenswert robust, wenn man sie auf Vergleiche viel größerer Dokumente ausweitet. Nehmen Sie zum Beispiel den kraftvollen Monolog aus Shakespeares Macbeth: „ Morgen und morgen und morgen “:


 monologue = '''Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing.''' print(get_cos_sim(monologue, 'Riding a car')) print(get_cos_sim(monologue, 'The contemplation of mortality'))


Sie sollten sehen, dass der Monolog nur zu 75 % der Idee „Autofahren“ und zu 82 % der Idee „Die Betrachtung der Sterblichkeit“ ähnelt.


Aber wir müssen nicht nur Monologe mit Ideen vergleichen, wir können die Monologe auch mit Fragen vergleichen. Zum Beispiel:


 get_cos_sim('''Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing.''', 'Which Shakespearean monologue contemplates mortality?') get_cos_sim('''Full of vexation come I, with complaint Against my child, my daughter Hermia. Stand forth, Demetrius. My noble lord, This man hath my consent to marry her. Stand forth, Lysander. And my gracious Duke, This man hath bewitch'd the bosom of my child. Thou, thou, Lysander, thou hast given her rhymes, And interchanged love-tokens with my child: Thou hast by moonlight at her window sung With feigning voice verses of feigning love, And stol'n the impression of her fantasy With bracelets of thy hair, rings, gauds, conceits, Knacks, trifles, nosegays, sweetmeats (messengers Of strong prevailment in unharden'd youth): With cunning hast thou filch'd my daughter's heart, Turn'd her obedience, which is due to me, To stubborn harshness. And, my gracious Duke, Be it so she will not here, before your Grace, Consent to marry with Demetrius, I beg the ancient privilege of Athens: As she is mine, I may dispose of her; Which shall be either to this gentleman, Or to her death, according to our law Immediately provided in that case.''', 'Which Shakespearean monologue contemplates mortality?')



Sie sollten sehen, dass die Einbettung zeigt, dass der Macbeth-Monolog kontextuell viel näher an der Frage „Welcher Shakespeare-Monolog denkt über die Sterblichkeit nach?“ liegt. als der Egeus-Monolog, der zwar den Tod erwähnt, sich aber nicht direkt mit dem Konzept der Sterblichkeit auseinandersetzt.


Die Vektorsuche

Nachdem wir nun die Einbettung haben, wie verwenden wir sie in unserem RAG-System? Angenommen, wir wollten unserem RAG-System das Wissen aller Shakespeare-Monologe geben, damit es Fragen über Shakespeare beantworten kann. In diesem Fall würden wir alle Monologe von Shakespeare herunterladen und die Einbettungen dafür generieren. Wenn Sie mitmachen, können Sie die Einbettung wie folgt generieren:



 embedding = openai.Embedding.create(model=EMBEDDING_MODEL, input=[monologue])['data'][0]['embedding']


Sobald wir die Einbettungen haben, möchten wir sie so speichern, dass wir sie abfragen und mit einer neuen Einbettung vergleichen können. Normalerweise würden wir sie in einer sogenannten Vektordatenbank ablegen, einem speziellen Datenspeicher, der schnelle Vergleiche zweier Vektoren ermöglicht. Sofern Ihr Korpus jedoch nicht besonders groß ist, sind Brute-Force-Vergleiche für die meisten experimentellen Anwendungsfälle außerhalb der Produktion, bei denen die Leistung nicht entscheidend ist, überraschend akzeptabel.


Unabhängig davon, ob Sie sich für die Verwendung einer Datenbank entscheiden oder nicht, möchten Sie ein System aufbauen, das Elemente in Ihrem Korpus findet, die am besten zur Frage passen. In unserem Beispiel möchten wir den Monolog finden, der für die jeweilige Benutzerfrage am relevantesten ist. Vielleicht möchten Sie so etwas tun:



 monologues_embeddings = [ ['Tomorrow, and tomorrow, and tomorrow...', [...]], # text in the left position, embedding in the right position ['Full of vexation come I...', [...]], … # More monologues and their embeddings as you see fit. ] def lookup_most_relevant(question): embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=[question])['data'][0]['embedding'] top_monologue = sorted(monologues_embeddings, key=lambda x: cosine_similarity(embed, x[1]), reverse=True)[0] return top_monologue lookup_most_relevant(“How does Macbeth evaluate his life when he is confronted with his mortality?“)


Wenn Sie dieses Beispiel ausführen, sollten Sie sehen, dass der Macbeth-Monolog ausgewählt ist und eine Ähnlichkeit von etwa 82 % mit der Frage aufweist.


Das prompte Engineering

Der letzte Schritt im RAG-Modell ist das Prompt Engineering. In unserem Fall ist es nicht allzu schwierig. Da wir nun den Monolog zur Hand haben, können wir eine Eingabeaufforderung erstellen, indem wir einfach den Monolog in unsere Abfrage einbetten und dann die Frage stellen:


 completion = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": """ Consider the following monologue: Tomorrow, and tomorrow, and tomorrow, Creeps in this petty pace from day to day, To the last syllable of recorded time; And all our yesterdays have lighted fools The way to dusty death. Out, out, brief candle! Life's but a walking shadow, a poor player, That struts and frets his hour upon the stage, And then is heard no more. It is a tale Told by an idiot, full of sound and fury, Signifying nothing. How does Macbeth evaluate his life when he is confronted with his mortality? """}, ] )


Sie sollten eine Antwort sehen wie:


 In the well-known "Tomorrow and tomorrow and tomorrow" monologue from Shakespeare's Macbeth, the titular character plunges into existential despair when confronted with his impending death. Macbeth evaluates his life as hollow, futile, and meaningless when facing his mortality. He sees it as a "walking shadow" and himself as a "poor player," highlighting his view of life as a fleeting, contrived performance with no lasting substance or impact. The "brief candle" is a metaphor for his own mortality, suggesting that life's flame can be extinguished suddenly and unceremoniously. By stating "It is a tale told by an idiot, full of sound and fury, signifying nothing," Macbeth is expressing his belief that life, despite all its noise and action, is ultimately empty, absurd, and void of meaning. Overall, Macbeth's examination of his life is profoundly pessimistic, revealing his deep despair and cynicism.


Natürlich ist dieses spezielle Beispiel nicht die aussagekräftigste Demonstration der RAG-Architektur, da die meisten GPT-Modelle Shakespeares Monologe bereits kennen und sich mit der umfangreichen Analyse öffentlicher Shakespeare-Analysen im Internet vertraut gemacht haben. Wenn Sie GPT-4 genau diese Frage ohne eingebetteten Monolog stellen, erhalten Sie wahrscheinlich eine sehr gute Antwort, obwohl es wahrscheinlich nicht so viele Zitatverweise auf den Monolog gibt. Es sollte jedoch klar sein, dass diese Technik in einem kommerziellen Umfeld auf proprietäre oder esoterische Datensätze angewendet werden kann, auf die bestehende GPT-Implementierungen nicht zugreifen können.


Tatsächlich werden Leser, die mit meinem vorherigen Artikel „ Erstellen eines Dokumentenanalysators mit ChatGPT, Google Cloud und Python“ vertraut sind, möglicherweise erkennen, dass der letzte Teil der Technik dem Prompt Engineering, das ich in diesem Artikel durchgeführt habe, sehr ähnlich ist. Ausgehend von dieser Idee können wir uns sehr leicht ein RAG-System vorstellen, das auf Veröffentlichungen der japanischen Regierung (den Beispieldaten aus diesem Artikel) aufbaut und es Benutzern ermöglichen würde, nach japanischer Wirtschaftspolitik zu suchen und Fragen dazu zu stellen. Das System würde schnell die relevantesten Dokumente abrufen, sie zusammenfassen und eine Antwort erstellen, die auf tiefgreifendem domänenspezifischem Wissen basiert, das den GPT-Basismodellen nicht zur Verfügung steht. Diese Leistungsfähigkeit und Einfachheit sind genau der Grund, warum die RAG-Architektur bei LLM-Entwicklern großen Anklang findet.


Nachdem wir uns nun mit der RAG-Architektur befasst haben, wollen wir einige der Mängel dieser Architektur untersuchen.


Einschränkungen der RAG-Architektur

Einbettung der Debugging-Funktion

Da viele RAG-Systeme auf der Einbettung von Dokumenten und der Vektorsuche basieren, um die Frage mit den relevanten Dokumenten zu verbinden, ist das gesamte System oft so gut wie das verwendete Einbettungsmodell. Das OpenAI-Einbettungsmodell ist unglaublich flexibel und es gibt viele Techniken zur Anpassung an Einbettungen. LLaMa, Metas Open-Source-Konkurrent zu GPT, bietet fein abstimmbare Einbettungsmodelle. Das Einbettungsmodell weist jedoch einen unvermeidlichen Black-Box-Aspekt auf. Dies ist beim Vergleich kurzer Textzeichenfolgen einigermaßen beherrschbar, wird jedoch beim Vergleich kurzer Zeichenfolgen mit viel längeren Dokumenten schwierig zu validieren und zu debuggen. In unserem vorherigen Beispiel müssen wir einen kleinen Vertrauensvorschuss wagen, dass die Einbettungssuche in der Lage ist, „Sterblichkeit“ mit dem „Morgen und morgen und morgen“-Monolog zu verbinden. Dies kann bei Workloads, bei denen Transparenz und Debugbarkeit von entscheidender Bedeutung sind, recht unangenehm sein.

Kontextüberlastung

Eine weitere Einschränkung des RAG-Modells ist die relativ begrenzte Menge an Kontext, die an es übergeben werden kann. Da das Einbettungsmodell Kontext auf Dokumentebene erfordert, um gut zu funktionieren, müssen wir vorsichtig sein, wenn wir den Korpus für die Einbettung zerlegen. Der Macbeth-Monolog hat möglicherweise eine 82-prozentige Ähnlichkeit mit der Frage nach der Sterblichkeit, aber diese Zahl sinkt auf 78 %, wenn man die Frage mit der Einbettung der ersten beiden Zeilen des Monologs vergleicht, also „Morgen und morgen und.“ morgen. Kriecht in diesem kleinen Tempo von Tag zu Tag, bis zur letzten Silbe der aufgezeichneten Zeit.


Daher muss der Kontext, der an die RAG-Eingabeaufforderung übergeben wird, ziemlich groß sein. Derzeit sind die kontextreichsten GPT-Modelle noch auf 16.000 Token begrenzt, was ziemlich viel Text ist. Wenn Sie jedoch mit langen Interviewtranskripten oder kontextreichen Artikeln arbeiten, ist die Menge an Kontext, die Sie angeben können, begrenzt in der letzten Generierungsaufforderung.

Neuartige Terminologie

Die letzte Einschränkung des RAG-Modells ist seine Unfähigkeit, mit neuartiger Terminologie zu arbeiten. Menschen, die in bestimmten Bereichen arbeiten, neigen dazu, Terminologien und Sprechweisen zu entwickeln, die für diesen Bereich einzigartig sind. Wenn diese Terminologien nicht in den Trainingsdaten des Einbettungsmodells vorhanden sind, wird der Suchvorgang beeinträchtigt.


Beispielsweise weiß das Einbettungsmodell ada-002 möglicherweise nicht, dass die „Rust Programming Language“ mit „LLVM“ verwandt ist. Tatsächlich wird eine relativ geringe Kosinusähnlichkeit von 78 % zurückgegeben. Dies bedeutet, dass Dokumente, die sich mit LLVM befassen, bei einer Anfrage zu Rust möglicherweise keine große Ähnlichkeit aufweisen, obwohl die beiden Ideen im wirklichen Leben eng miteinander verbunden sind.


Normalerweise kann das Problem der neuartigen Terminologie durch schnelles Engineering gelöst werden, im Kontext einer Einbettungssuche ist dies jedoch relativ schwierig. Die Feinabstimmung eines Einbettungsmodells ist, wie bereits erwähnt, möglich, aber dem Einbettungsmodell die neuartige Terminologie in allen Kontexten beizubringen, kann fehleranfällig und zeitaufwändig sein.

Vorschlag von QE-RAG oder Question-Enhanced RAG

Angesichts dieser Einschränkungen möchte ich eine modifizierte Architektur für eine neue Klasse von RAG-Systemen vorschlagen, die viele der oben beschriebenen Einschränkungen umgeht. Die Idee basiert darauf, neben dem Korpus auch Vektorsuchen nach häufig gestellten Fragen durchzuführen und einen LLM zu verwenden, um den Korpus im Kontext der Fragen vorzuverarbeiten. Wenn sich dieser Prozess kompliziert anhört, machen Sie sich keine Sorgen. In diesem Abschnitt gehen wir die Implementierungsdetails zusammen mit Codebeispielen durch, die Sie zum Nachvollziehen verwenden können.


Zu beachten ist, dass QE-RAG zusammen mit einer Standard-RAG-Implementierung ausgeführt werden sollte, damit bei Bedarf auf eine andere Implementierung zurückgegriffen werden kann. Mit zunehmender Reife der Implementierung sollte der Fallback immer seltener erforderlich sein, aber QE-RAG soll immer noch eine Verbesserung und nicht ein Ersatz für die Vanilla-RAG-Architektur sein.


Die Architektur

Die Grundzüge der QE-RAG-Architektur sind wie folgt:

  1. Erstellen Sie eine Vektordatenbank mit Fragen, die zum Korpus gestellt werden können oder wahrscheinlich gestellt werden.
  2. Verarbeiten Sie den Korpus vor und fassen Sie ihn anhand der Fragen in der Vektordatenbank zusammen.
  3. Wenn eine Benutzeranfrage eingeht, vergleichen Sie die Benutzeranfrage mit den Fragen in der Vektordatenbank.
  4. Wenn eine Frage in der Datenbank der Benutzeranfrage sehr ähnlich ist, rufen Sie die Version des Korpus ab, die zur Beantwortung der Frage zusammengefasst wird.
  5. Verwenden Sie den zusammengefassten Korpus, um die Benutzerfrage zu beantworten.
  6. Wenn keine Frage in der Datenbank der Benutzerabfrage sehr ähnlich ist, greifen Sie auf eine Vanilla-RAG-Implementierung zurück.


Gehen wir die einzelnen Teile der Reihe nach durch.


Frageneinbettungen

Die Architektur beginnt, ähnlich wie beim Vanilla RAG, mit einer Einbettung und einer Vektordatenbank. Anstatt jedoch die Dokumente einzubetten, werden wir eine Reihe von Fragen einbetten.


Um dies zu veranschaulichen, nehmen wir an, wir versuchen, ein LLM aufzubauen, das ein Experte für Shakespeare ist. Wir möchten vielleicht, dass es Fragen beantwortet wie:


 questions = [ "How does political power shape the way characters interact in Shakespeare's plays?", "How does Shakespeare use supernatural elements in his plays?", "How does Shakespeare explore the ideas of death and mortality in his plays?", "How does Shakespeare explore the idea of free will in his plays?" ]


Wir möchten für sie eine Einbettung wie folgt erstellen und sie speichern oder später verwenden:


 questions_embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=questions)


Vorverarbeitung und Zusammenfassung

Nachdem wir nun die Fragen haben, möchten wir das Korpus herunterladen und zusammenfassen. Für dieses Beispiel laden wir die HTML-Versionen von Macbeth und Hamlet herunter:


 import openai import os import requests from bs4 import BeautifulSoup plays = { 'shakespeare_macbeth': 'https://www.gutenberg.org/cache/epub/1533/pg1533-images.html', 'shakespeare_hamlet': 'https://www.gutenberg.org/cache/epub/1524/pg1524-images.html', } if not os.path.exists('training_plays'): os.mkdir('training_plays') for name, url in plays.items(): print(name) file_path = os.path.join('training_plays', '%s.txt' % name) if not os.path.exists(file_path): res = requests.get(url) with open(file_path, 'w') as fp_write: fp_write.write(res.text)


Dann verarbeiten wir die Stücke zu Szenen, wobei wir uns an den HTML-Tags orientieren:


 with open(os.path.join('training_plays', 'shakespeare_hamlet.txt')) as fp_file: soup = BeautifulSoup(''.join(fp_file.readlines())) headers = soup.find_all('div', {'class': 'chapter'})[1:] scenes = [] for header in headers: cur_act = None cur_scene = None lines = [] for i in header.find_all('h2')[0].parent.find_all(): if i.name == 'h2': print(i.text) cur_act = i.text elif i.name == 'h3': print('\t', i.text.replace('\n', ' ')) if cur_scene is not None: scenes.append({ 'act': cur_act, 'scene': cur_scene, 'lines': lines }) lines = [] cur_scene = i.text elif (i.text != '' and not i.text.strip('\n').startswith('ACT') and not i.text.strip('\n').startswith('SCENE') ): lines.append(i.text)


Und hier ist der Teil, der QE-RAG einzigartig macht: Anstatt Einbettungen für bestimmte Szenen zu erstellen, erstellen wir Zusammenfassungen für diese, die auf jede der Fragen ausgerichtet sind:


 def summarize_for_question(text, question, location): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that provides helpful summaries."}, {"role": "user", "content": """Is the following excerpt from %s relevant to the following question? %s === %s === If so, summarize the sections that are relevant. Include references to specific passages that would be useful. If not, simply say: \"nothing is relevant\" without additional explanation""" % ( location, question, text )}, ] ) return completion


Diese Funktion fordert ChatGPT auf, zwei Dinge zu tun: 1) festzustellen, ob die Passage tatsächlich für die Beantwortung der vorliegenden Frage nützlich ist, und 2) die Teile der Szene zusammenzufassen, die für die Beantwortung der Frage nützlich sind.


Wenn Sie diese Funktion mit einigen Schlüsselszenen aus Macbeth oder Hamlet ausprobieren, werden Sie feststellen, dass GPT3.5 recht gut erkennen kann, ob eine Szene für die Frage relevant ist, und die Zusammenfassung wird um einiges kürzer sein als die Szene selbst. Dies erleichtert die spätere Einbettung im unmittelbaren Engineering-Schritt erheblich.


Jetzt können wir dies für alle Szenen tun.


 for scene in scenes: scene_text = ''.join(scene['lines']) question_summaries = {} for question in questions: completion = summarize_for_question(''.join(scene['lines']), question, "Shakespeare's Hamlet") question_summaries[question] = completion.choices[0].message['content'] scene['question_summaries'] = question_summaries


Bei Produktions-Workloads würden wir die Zusammenfassungen in eine Datenbank stellen, aber in unserem Fall schreiben wir sie einfach als JSON-Datei auf die Festplatte.


Zweistufige Vektorsuche

Nehmen wir nun an, wir erhalten eine Benutzerfrage wie die folgende:


 user_question = "How do Shakespearean characters deal with the concept of death?"


Wie in Vanilla RAG möchten wir eine Einbettung für die Frage erstellen:


 uq_embed = openai.Embedding.create(model=EMBEDDING_MODEL, input=[user_question])['data'][0]['embedding']


In einem Vanilla-RAG würden wir die Einbettung von Benutzerfragen mit den Einbettungen für die Szenen in Shakespeare vergleichen, aber in QE-RAG vergleichen wir die Einbettungen mit den Fragen:


 print([cosine_similarity(uq_embed, q) for q in question_embed])


Wir sehen, dass die Vektorsuche Frage 3 (richtig) als relevanteste Frage identifiziert hat. Jetzt rufen wir die zusammenfassenden Daten für Frage 3 ab:


 relevant_texts = [] for scene in hamlet + macbeth: # hamlet and macbeth are the scene lists from the above code if "NOTHING IS RELEVANT" not in scene['question_summaries'][questions[2]].upper() and \ "NOTHING IN THIS EXCERPT" not in scene['question_summaries'][questions[2]].upper() and \ 'NOTHING FROM THIS EXCERPT' not in scene['question_summaries'][questions[2]].upper() and \ "NOT DIRECTLY ADDRESSED" not in scene['question_summaries'][questions[2]].upper(): relevant_texts.append(scene['question_summaries'][questions[2]])


Bitte beachten Sie, dass Sie möglicherweise mehrere unterschiedliche Zeichenfolgen erhalten, die darauf hinweisen, dass eine Szene für die vorliegende Frage nicht relevant ist, da die GPT-Zusammenfassung nicht deterministisch ist. Der Schlüssel besteht darin, nur die relevanten Auszüge in die Liste der relevanten Zusammenfassungen zu verschieben.


In diesem Stadium können wir eine Vektorsuche der zweiten Ebene durchführen, um nur die relevantesten Zusammenfassungen in unsere Eingabeaufforderung aufzunehmen. Angesichts der Größe unseres Korpus können wir jedoch einfach die gesamte relevant_texts Liste in unserer Eingabeaufforderung verwenden.


Schnelles Engineering

Da wir nun alle Auszüge haben, können wir die Eingabeaufforderung wie folgt erstellen:

 completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that answers questions based on excerpts."}, {"role": "user", "content": """Consider the following passages: %s ==== Answer this question: %s Be detailed and complete in your analysis. Make references to specific passages where helpful. Answer only based on passages provided. """ % ( '\n===\n'.join(relevant_texts), user_question )}, ] )


Lassen Sie den API-Aufruf laufen, und Sie sollten etwa Folgendes sehen:


 Based on the passages provided, Shakespearean characters deal with the concept of death in various ways. In Hamlet, for example, the characters contemplate the nature of death and its implications. Hamlet himself reflects on the brevity of life and the corrupting influence of vices. He questions the purpose of existence and contemplates suicide as a release from the burdens of life. In the passage, Hamlet says, "To die, to sleep— / To sleep, perchance to dream. Ay, there's the rub, / For in that sleep of death what dreams may come" (Lines 65-67). This shows his contemplation of the afterlife and the uncertainty surrounding it. In Macbeth, characters are confronted with the consequences of their actions and the fragility of life. Lady Macbeth, for instance, experiences guilt and remorse for her involvement in the murder of King Duncan. She is haunted by visions of blood on her hands and expresses the desire to escape the consequences of her actions. In the passage, Lady Macbeth says, "Out, damned spot! Out, I say!" (Line 30). This demonstrates her struggle to cope with the guilt and the psychological toll it takes on her. Furthermore, both Macbeth and Lady Macbeth reflect on the inevitability and the consequences of their actions. In the passage, Macbeth laments the decay of life and the loss of purpose and meaning. He reflects on the transitory nature of existence and the insignificance of worldly achievements. Macbeth states, "Life's but a walking shadow, a poor player / That struts and frets his hour upon the stage" (Lines 217-218). This shows his acceptance of mortality and his realization that material possessions and power do not bring true fulfillment. In summary, Shakespearean characters deal with the concept of death in various ways. They contemplate the nature of life and the afterlife, struggle with guilt and remorse, and question the meaning and purpose of their existence. Shakespeare's exploration of death and mortality through his characters provides insight into the complexities of human nature and the universal human experience of confronting one's mortality.


Das ist eine sehr gute Zusammenfassung, die auf vielen verschiedenen Teilen von Hamlet und Macbeth basiert.

Zum Vergleich habe ich Einbettungen für ganze Szenen erstellt und mit derselben Eingabeaufforderung eine Vanilla-RAG-Suche durchgeführt, um die N-relevantesten Szenen zu finden, bis ich 10.000 Token erreicht habe


 relevant_texts = [] for i in range(10): if len(encoding.encode('\n===\n'.join(relevant_texts))) > 10000: break relevant_texts.append(''.join(macbeth[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(macbeth_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) relevant_texts.append(''.join(hamlet[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(hamlet_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines']))


Wenn Sie dies in die Eingabeaufforderung eingeben, sollten Sie etwa Folgendes sehen:


 Based on the provided passages, Shakespearean characters deal with the concept of death in various ways. In Hamlet, the characters reflect on death and mortality extensively. Hamlet himself contemplates suicide, expressing his desire to end his suffering and questioning the purpose of life. He says, "To be, or not to be: that is the question" (Hamlet, Act III, Scene I, Lines 56). This soliloquy reveals his struggle with the concept of death and his contemplation of the consequences of choosing either life or death. Additionally, in the same play, the appearance of the Ghost prompts discussions about death. The characters question the meaning and significance of encountering the dead, and Hamlet grapples with the idea of mortality in relation to his father's death. He reflects, "Oh God! a beast that wants discourse of reason / Would have mourn'd longer" (Hamlet, Act I, Scene II, Lines 152-153). This passage highlights Hamlet's grief and his pondering of the brevity of life and the transitory nature of existence. In Macbeth, the characters also confront death and its consequences. Macbeth, after killing King Duncan, reflects on the significance of the act and its implications. He says, "Will all great Neptune's ocean wash this blood Clean from my hand? No, this my hand will rather The multitudinous seas incarnadine" (Macbeth, Act II, Scene II, Lines 77-79). This passage showcases Macbeth's awareness of the guilt associated with murder and his realization that the blood on his hands cannot be cleansed. Lady Macbeth, on the other hand, shows a different perspective on death. She suppresses her guilt and tries to detach herself from the consequences of their actions. She states, "A little water clears us of this deed" (Macbeth, Act II, Scene II, Line 69). This passage reveals her denial and determination to move forward without remorse. In conclusion, Shakespearean characters deal with the concept of death in various ways. Some contemplate the meaning of life and question the purpose of existence, while others grapple with the guilt and consequences that come with taking a life. Shakespeare explores the complexities of death and mortality through his characters' introspection and their different responses to the inevitability of death.


Dies ist eine sehr überzeugende Analyse, geht jedoch nicht auf viele der wichtigsten Passagen aus Hamlet und Macbeth ein. Wie Sie sehen, hat QE-RAG den entscheidenden Vorteil, dass es einen relevanteren Kontext einbetten kann als ein Standard-RAG-System.


Das obige Beispiel zeigt jedoch nicht einen weiteren Vorteil des QE-RAG, nämlich die Möglichkeit, dem Entwickler eine bessere Kontrolle über den Einbettungsprozess zu geben. Um zu sehen, wie QE-RAG dies erreicht, schauen wir uns eine Erweiterung dieses Problems an – den Umgang mit neuer Terminologie.


Erweiterung von QE-RAG um neue Terminologie

QE-RAG glänzt wirklich, wenn Sie eine neue Terminologie einführen. Angenommen, Sie führen ein neues Konzept ein, wie das japanische Wort „zetsubou“, ein Begriff, der zwischen Verzweiflung und Hoffnungslosigkeit angesiedelt ist und insbesondere die Hingabe an die eigenen Umstände zum Ausdruck bringt. Dabei handelt es sich nicht um eine so unmittelbare Katastrophe wie das englische Konzept der Verzweiflung, sondern es geht vielmehr um die Duldung unangenehmer Dinge, die geschehen.


Angenommen, wir möchten eine Frage beantworten wie:


user_question = "How do Shakespearean characters cope with Zetsubou?"


Mit Vanilla RAG würden wir eine Einbettungssuche durchführen und dann im letzten Schritt der Eingabeaufforderungsentwicklung eine Erklärung hinzufügen:


 relevant_texts = [] for i in range(10): if len(encoding.encode('\n===\n'.join(relevant_texts))) > 10000: break relevant_texts.append(''.join(macbeth[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(macbeth_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) relevant_texts.append(''.join(hamlet[sorted( [(idx, cosine_similarity(uq_embed, q)) for idx, q in enumerate(hamlet_embed)], key=lambda x: x[1], reverse=True )[i][0]]['lines'])) completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that answers questions based on excerpts."}, {"role": "user", "content": """Zetsubou is the concept of hopelessness and despair, combined with a surrender to whim of one's circumstances. Consider the following passages: %s ==== Answer this question: %s Be detailed and complete in your analysis. Make references to specific passages where helpful. Answer only based on passages provided. """ % ( '\n===\n'.join(relevant_texts), user_question )}, ] )


Das Ergebnis ist eine sehr gut geschriebene und überzeugende, aber etwas überzogene Antwort, die sich auf einige Szenen aus Hamlet konzentriert. Macbeth wird in dieser Antwort überhaupt nicht erwähnt, da keine der Szenen die Einbettungssuche bestanden hat. Wenn man sich die Einbettungen ansieht, wird sehr deutlich, dass die semantische Bedeutung von „zetsubou“ nicht richtig erfasst wurde und daher keine relevanten Texte daraus abgerufen werden konnten.


In QE-RAG können wir die Definition für den neuen Begriff in der Zusammenfassungsphase einfügen und so die Qualität des für das System zugänglichen Textes erheblich verbessern:


 def summarize_for_question(text, question, location, context=''): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo-16k", messages=[ {"role": "system", "content": "You are a literature assistant that provides helpful summaries."}, {"role": "user", "content": """%s Is the following excerpt from %s relevant to the following question? %s === %s === If so, summarize the sections that are relevant. Include references to specific passages that would be useful. If not, simply say: \"nothing is relevant\" without additional explanation""" % ( context, location, question, text )}, ] ) return completion questions = [ "How do characters deal with Zetsubou in Shakespearean plays?" ] summarize_for_question(''.join(scene['lines']), questions[0], "Shakespeare's Macbeth", "Zetsubou is the concept of hopelessness and despair, combined with a surrender to whim of one's circumstances." )


Verwenden Sie diese Zusammenfassungsaufforderung für die Passagen, und Sie werden Zusammenfassungen sehen, die äußerst genau und kontextbezogen sind. Diese Zusammenfassungen können dann im nachfolgenden QE-RAG verwendet werden.


Darüber hinaus wird die Frageneinbettung äußerst ähnlich sein, denn auch wenn ada-002 die genaue Bedeutung von „zetsubou“ nicht kennt, führt das Vorhandensein dieses Wortes in beiden Fragen dazu, dass die Frageneinbettungen äußerst ähnlich sind.


Wenn wir also QE-RAG zur Beantwortung der ursprünglichen Frage zu Zetsubou verwenden, erhalten wir die folgende Antwort:


 Based on the passages provided, Shakespearean characters cope with Zetsubou, or hopelessness and despair, in different ways. In "Macbeth," Lady Macbeth copes with Zetsubou through guilt and anguish. She is consumed by the guilt of her and her husband's actions, and this guilt manifests in her sleepwalking and attempts to wash her hands of imagined blood stains. This can be seen in her utterances of "Out, damned spot!" and her preoccupation with the blood on her hands. Lady Macbeth's coping mechanism is to face the consequences of her actions head-on, even if it means descending into madness. The Doctor and Gentlewoman observe her behavior and acknowledge that her condition requires divine intervention. In "Hamlet," the excerpt does not provide specific passages or examples of characters coping with Zetsubou. However, it does address Hamlet's inner turmoil and struggle to cope with his circumstances. Hamlet's state of mind, confusion, and madness are a reflection of despair. The characters in this scene are attempting to understand Hamlet's condition and find ways to deal with it, but the excerpt does not provide details on how they cope with Zetsubou. In "Macbeth," Macbeth himself copes with Zetsubou by surrendering to his dire circumstances. He becomes desensitized to fear and horror, indicating a kind of resignation to his grim circumstances. He acknowledges that there is no escape from his fate and chooses to face it head-on, even though he knows it will lead to his downfall. Macbeth's reaction to the news of his wife's death and his subsequent soliloquy reflect a deep despair and hopelessness. He sees life as meaningless, a "tale Told by an idiot, full of sound and fury, Signifying nothing." Macbeth's surrender to his circumstances and his belief in the futility of life demonstrate his coping mechanism for Zetsubou. In "Macbeth," Lady Macduff copes with Zetsubou by expressing her feelings of despair and hopelessness. She feels betrayed and abandoned by her husband, who has fled and left their family behind. She questions his actions and believes that they show a lack of love and wisdom. Lady Macduff experiences a sense of abandonment, which aligns with the concept of Zetsubou. She tries to make her son understand the severity of their circumstances, but he questions the definition of a traitor. Lady Macduff ultimately meets a tragic end, indicating that her coping mechanism for Zetsubou is not successful. In summary, Shakespearean characters cope with Zetsubou in different ways. Lady Macbeth copes through guilt and faces the consequences of her actions, even if it means descending into madness. Macbeth copes through surrendering to his dire circumstances and embracing his fate. Lady Macduff copes through expressing her feelings of despair and hopelessness but ultimately meets a tragic end. These characters' coping mechanisms reflect different responses to Zetsubou in Shakespearean plays


Das Ergebnis ist mit diesem Konzept immer noch etwas schwierig, aber es sollte offensichtlich sein, dass QE-RAG viel besser in der Lage ist, sich mit dem Kern der Frage auseinanderzusetzen und aus einer Vielzahl von Quellen zu schöpfen.

Und damit haben wir QE-RAG implementiert und seinen Anwendungsfall demonstriert!


Abschluss

Im heutigen Artikel haben wir die immer beliebter werdende RAG-Architektur und ihre Einschränkungen untersucht. Anschließend haben wir die RAG-Architektur um eine neue Architektur namens QE-RAG erweitert, die darauf abzielt, die Fähigkeiten großer Sprachmodelle besser zu nutzen. Neben verbesserter Genauigkeit und kontextbezogenem Zugriff ermöglicht QE-RAG dem gesamten System, durch die Interaktion mit Benutzern zu wachsen und sich besser mit den Arten der gestellten Fragen vertraut zu machen, sodass Unternehmen einzigartiges geistiges Eigentum auf Basis von Open Source entwickeln können oder kommerziell erhältliche LLMs .

Als experimentelle Idee ist QE-RAG natürlich nicht perfekt. Wenn Sie Ideen haben, wie diese Architektur verbessert werden kann, oder einfach nur eine Diskussion über LLM-Technologien führen möchten, zögern Sie bitte nicht, mir über meinen Github oder LinkedIn eine Nachricht zu schreiben.