Daten und Künstliche Intelligenz

Techniken des In-Context-Learning

Techniken des In-Context-Learning

Max Schattauer
12 Jun 2023
6 min Lesezeit

Techniken und Herausforderungen des In-Context-Learnings

In einem unserer letzten Artikel
, haben wir einen Überblick über verschiedene Techniken gegeben, mit denen Unternehmen LLMs (large language models) in Business-Szenarien für sich nutzbar machen können. Die meisten dieser Szenarien erfordern, dass diese Modelle Geschäftsdaten und Geschäftsdokumente kennen. Ein vielversprechender Ansatz dafür ist das Konzept des In-Context-Learnings, bei dem dem LLM während der Abfrage die relevanten Daten übergeben werden. Da das Modell dabei nicht gefinetuned wird, fallen keine Kosten für die Anpassung der Modellgewichte an und das Modell bleibt flexibel im Hinblick auf den Kontext der Anfragen. In diesem Artikel werden wir die grundlegenden Ansätze des In-Context-Learnings und ihre Nachteile anhand von extern bereitgestellten und selbst gehosteten LLMs beispielhaft untersuchen.

Fluch und Segen der Dokumentabfrage

In-Context-Learning bedeutet, dass die Daten, zu denen das LLM befragt werden soll, in den Prompt des LLMs einbezogen werden: zum Beispiel ganze Verträge, um ein bestimmtes Detail darin zu finden, lange Umsatztabellen, um Wochen mit dem höchsten Umsatz zu finden, oder Lebensläufe von Bewerber:innen, um unter ihnen die am besten geeignete Person für eine offene Stelle zu finden - und das alles mit nur einer präzisen Formulierung der Frage in menschlicher Sprache. Dieser Ansatz mag erstaunlich einfach erscheinen: Einfach alle Dokumente, die für das LLM als relevant erachtet werden, über den Prompt eingeben, die Frage hinzufügen und und auf die Antwort warten. Aber selbst wenn am Ende das gewünschte Ergebnis herauskommt, werden Sie wahrscheinlich enttäuscht sein über die Zeit, die Sie gewartet haben, oder von der Rechnung des Modellanbieters davon abgeschreckt werden, diesen Ansatz weiter zu verfolgen. Indem Sie Ihre Dokumente an das LLM weiterleiten, erhöhen Sie die Menge der zu verarbeitenden Daten und damit die Zeit und die teuren Rechenressourcen, die zur Erfüllung der Anfrage erforderlich sind. Kurz gesagt: Wir brauchen eine bessere Strategie, anstatt alle Dokumente ohne vorherige Auswahl an das Modell weiterzugeben.

Auswahl relevanter Dokumente

Im Folgenden werden wir diese Strategien anhand eines Beispiels aus dem HR-Bereich unter Verwendung der LlamaIndex-Bibliothek untersuchen: Wir wollen das LLM fragen, wen es aus einer Menge potenzieller Kandidat:innen für eine offene Stelle für am besten geeignet hält. Wir geben ihm im Prompt eine Beschreibung der Stelle  zusammen mit den Lebensläufen der Kandidat:innenals Dokumente mit.Die Llama-Index-Bibliothek bietet uns einige sehr nützliche Werkzeuge für die Auswahl und Übergabe der geeigneten Dokumente an das LLM. Dokumentensammlungen werden in Nodes unterteilt, die wiederum in Indizes organisiert werden. Diese Indizes können unter anderem geordnete Listen oder Baumstrukturen von Dokumenten sein oder auch eine ungeordnete Menge von Dokumenten in einem Vektorraum. Diese Indizes werden Vektorindizes genannt. Die Wahl des richtigen Index für jede einzelne Art von Dokument ist entscheidend für ein konsistentes Abrufen der passenden Dokumente und die Performance der Abfrage.

Vektorindizes

Vektorindizes sind besonders interessant für Szenarien mit einer großen Menge von Dokumenten, die zu viel für eine gleichzeitige Verarbeitung durch das LLM wären. Sie werden erstellt, indem jedem Dokument eine Vektorrepräsentation, das so genannte "Embedding", zugewiesen wird. Diese "Embeddings" repräsentieren die semantische Ähnlichkeit der Dokumente zueinandern und werden mit Hilfe eines Embedding-LLMs erstellt (aus Kosten- oder Ressourcengründen normalerweise ein weniger leistungsfähiges Modell als das, das für die Abfrage benutzt wird). Aus dem Index werden nun diejenigen Dokumente abgerufen, die semantisch dem LLM-Prompt am nächsten sind, also die Dokumente mit der geringsten Entfernung zwischen deren Embeddings dem Embedding des LLM-Prompts.

Die Geschäftsdokumente werden als Embeddings in einem Dokumenten-Index dargestellt. Wenn die Dokumente abgefragt werden sollen, wird der Index nach den passenden Dokumenten durchsucht. Diese werden dann zusammen mit der Anfrage an das LLM übergeben.

Ein naiver Ansatz zur Indizierung

In unserem Anwendungsfall im HR-Bereich mit der LlamaIndex-Bibliothek könnte ein erster Versuch darin bestehen, alle Dokumente, d. h. alle Stellenangebote und alle Bewerberprofile, in einem einzigen Vektorindex zu vereinen und die Bibliothek die richtigen Dokumente für uns auswählen zu lassen. Zuerst werden die beiden Dokumentensätze separat mit dem SimpleDirectoryReader von LlamaIndex geladen:

from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader
profile_documents = SimpleDirectoryReader('data/employee_profiles').load_data()
job_offer_documents = SimpleDirectoryReader('data/job_offers').load_data()

Dann erstellen wir daraus einen Vektorindex:

combined_index = GPTVectorStoreIndex([]) 
for documents in [profile_documents, job_offer_documents]: 
    for document in documents: 
        combined_index.insert(document)

Um das LLM abzufragen, erstellen wir einfach eine QueryEngine aus dem Indexobjekt:

query_engine = combined_index.as_query_engine()
query_string = '''
Give me a score to what extent each of the candidates is suited for the 'freelance
project manager' project?
'''
query_engine.query(query_string)

Theoretisch könnte dies durchaus funktionieren, aber in unserem Beispiel stellte sich heraus, dass bei Wiederholung der Abfrage zufälligen Ergebnissen ausgegeben wurden:

First run:
	Candidate D: 8/10
	Candidate I: 6/10
	Candidate X: 5/10
	Candidate M: 7/10

Second run:
	Candidate I: 8/10
	Candidate X: 6/10
	Candidate D: 5/10
	Candidate M: 7/10

Bei näherer Betrachtung stellte sich heraus, dass insgesamt nur zwei Dokumente an das LLM weitergeleitet wurden, so dass das LLM nicht in der Lage ist, eine Aussage über alle potenziellen Bewerber zu treffen.

Szenariospezifische Suchtechniken

Wir müssen also LlamaIndex dazu bringen, in diesem speziellen Fall Dokumente so auszuwählen, wie es für das Szenario notwendig ist. Für die oben genannte Anfrage bedeutet dies, dass wir alle neben der relevanten Stellenanzeige alle Kandidatenprofile an das LLM übergeben müssen, da wir eine Aussage über alle Kandidaten treffen wollen.

Zu diesem Zweck haben wir einen benutzerdefinierten Dokumentabrufer implementiert, der von zwei Dokumentindexen abhängt: einem Listenindex aller Kandidatenprofile und einem Vektorindex aller potenziellen Stellenangebote.

from llama_index.indices.base_retriever import BaseRetriever

class ProfileQueryRetriever(BaseRetriever):
    def __init__(self, profile_index: GPTListIndex, job_offer_index: GPTVectorStoreIndex) -> None:
        super().__init__()
        self._profile_index = profile_index
        self._job_offer_index = job_offer_index

In dieser Klasse haben wir die Methode _retrieve so implementiert, dass für jede Abfrage alle Kandidatenprofile zusammen mit dem Stellenangebot, das der Abfrage semantisch am nächsten kommt, zurückgegeben werden. Mit der Methode _get_embeddings werden die Embeddings der Abfrage und der Stellenangebotsdokumente abgerufen, um die Stellenangebote semantisch mit der Abfrage zu vergleichen.

from llama_index.data_structs import Node, NodeWithScore
from llama_index.indices.query.schema import QueryBundle
from llama_index.indices.query.embedding_utils import get_top_k_embeddings

def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
				"""Collect all nodes from the profile index and
				the closest node from the job offer index."""
        # include all profile nodes
        nodes_with_scores = [
            NodeWithScore(node=node, score=1)
            for node in self._profile_index.docstore.get_nodes(
                self._profile_index.index_struct.nodes
            )
        ]

        # include the job offer node whose semantic embedding is closest
				# to the embedding of the query
        job_offer_nodes = self._job_offer_index.docstore.get_nodes(
            self._job_offer_index.index_struct.nodes_dict.values()
        )
        query_embedding, job_offer_node_embeddings = self._get_embeddings(
            query_bundle, job_offer_nodes
        )
        top_similarities, top_idxs = get_top_k_embeddings(
            query_embedding,
            job_offer_node_embeddings,
            similarity_top_k=1,
            embedding_ids=list(range(len(job_offer_nodes))),
        )
        nodes_with_scores += [
            NodeWithScore(node=node, score=score)
            for node, score in zip(
                [job_offer_nodes[idx] for idx in top_idxs], top_similarities
            )
        ]
        return nodes_with_scores

def _get_embeddings(
        self, query_bundle: QueryBundle, nodes: List[Node]
    ) -> Tuple[List[float], List[List[float]]]:
        """Get embeddings of the query and a list of nodes."""
        if query_bundle.embedding is None:
            query_bundle.embedding = self._job_offer_index._service_context.embed_model.get_agg_embedding_from_queries(
                query_bundle.embedding_strs
            )

        node_embeddings: List[List[float]] = []
        for node in nodes:
            if node.embedding is None:
                node.embedding = self._job_offer_index.service_context.embed_model.get_text_embedding(
                    node.get_text()
                )
            node_embeddings.append(node.embedding)

        return query_bundle.embedding, node_embeddings

Man hätte auch einen einfachen Keyword-Matching-Ansatz wählen können, der für das Suchen des betreffenden Stellenangebots ebenfalls ausreichen könnte.

Eine Abfrage kann nun mit dem benutzerdefinierten Dokumentabrufer auf die folgende Weise durchgeführt werden:

retriever = ProfileQueryRetriever(profile_index, job_offer_index)
retriever_engine = RetrieverQueryEngine(retriever=retriever)
query='''
Give me a score to what extent each of the candidates is suited for the 
'freelance project manager' project?
'''
response = retriever_engine.query(query)

Nun werden alle Kandidatenprofile und das betreffende Stellenangebot einheitlich an das LLM weitergeleitet. Nun gibt es für jeden Kandidaten einheitliche Scorings ab. Auch detailliertere Abfragen zu spezifischen Fähigkeiten von Kandidaten sind nun möglich.

Ausblick auf selbst gehostete Modelle

Wenn LlamaIndex wie oben beschrieben verwendet wird, nutzt die Bibliothek die LLMs von OpenAI, um die Embeddings zu erzeugen und die Anfragen zu beantworten. In Geschäftsszenarien mit sensiblen Daten ist dies oft unerwünscht, da die Daten dem Datenschutz oder Geheimhaltungsrichtlinien unterliegen. Mit dem Aufkommen immer leistungsfähigerer Open-Source-Modelle unter business-geeigneten Lizenzen wie gpt4all oder MPT 7B ist eine LLM-Abfrage mit In-Context-Datenübergabe in einer kontrollierten und sicheren Umgebung, wie z. B. in einem privaten Cloud-Netzwerk oder im unternehmenseigenen Datencenter, nur noch einen Katzensprung entfernt.

Als Proof-of-Concept haben wir unseren HR-Anwendungsfall vom Standard-LlamaIndex-Setup mit OpenAI auf ein GPU-ausgerüstetes Cloud-Notebook übertragen, in dem wir das MPT 7B-Modell geladen haben. Mit diesem Setup waren wir ebenso in der Lage, unsere Dokumente abzufragen. Da der Speicherbedarf des Modells stark mit der Datenmenge steigt, stieß die Cloud-Umgebung unter Einbeziehung von mehreren Dokumenten in die Abfragen schnell an ihre Grenzen. Außerdem ist für ein Abfragen in Echtzeit, also ohne langes Warten auf Antworten, die Verwendung eines sehr leistungsstarken Grafikprozessors erforderlich.

Während dies zeigt, dass die Ausführung von datengesteuerten LLM-Anwendungen auf eigener Hardware oder in einem eigenen Cloud-Netzwerk möglich ist, wird deutlich, dass ein gut durchdachter Ansatz für die Auswahl von Dokumenten erforderlich ist, da Rechenressourcen in der Größenordnung, wie sie den heutigen LLMs benötigt werden, knapp und teuer sind.

Ausblick

In diesem Artikel haben wir untersucht, wie LLMs durch In-Context-Learning bereit für den Einsatz in Unternehmen gemacht werden können. Bei dieser Technik werden dem LLM über seinen Prompt Dokumente je nach Szenario übergeben: Abhängig vom Anwendungsfall und der Semantik der Anfrage werden Eingabedaten aus einem Dokumentenindex ausgewählt. Es ist wichtig, die Menge an Daten, die zur Beantwortung der Anfrage benötigt werden, sorgfältig gegen die Menge an Rechenressourcen abzuwägen, die zur Abfrage des LLMs mit diesen Daten erforderlich sind.

In einem HR-Anwendungsfall haben wir gezeigt, wie man einen benutzerdefinierten LlamaIndex-Dokumentenabrufer verwendet, um eine solche fallbasierte Dokumentauswahl zu implementieren. Wir haben außerdem selbst gehostete Modelle untersucht und festgestellt, dass sie eine immer besser verfügbare und realistische Alternative zu externen LLM-Dienstleistern darstellen, bei deren Verwendungen vertrauliche Geschäftsdaten das Unternehmen verlassen müssen.

Die sich in jüngster Zeit stark beschleunigende Entwicklung von LLMs und das Aufkommen von Bibliotheken zur Dokumentenintegration deuten stark darauf hin, dass Unternehmen bald die Möglichkeit haben werden, LLM-basierte Datenabfragen in eine Vielzahl von Geschäftsprozessen zu integrieren.

Andere Insights

How do we solve problems?

Please consider helping us improve our website. By clicking “Accept All Cookies”, you agree to the storing of cookies on your device to enhance site navigation and analyse site usage. View our Privacy Settings for more information.