Einführung
Große Sprachmodelle (LLMs) haben sich für zahlreiche Aufgaben als nützlich erwiesen. Sie können Text zusammenfassen, Text generieren, Text klassifizieren oder Text übersetzen. LLMs können auch konvertieren unstrukturierte Daten in strukturierte Daten.
Dies kann enorme Vorteile haben. Nützliche Fakten, die zuvor in einem großen Stück Text verborgen waren, können nun aufgedeckt werden, so dass Sie Eigenschaften eines Datensatzes nutzen können, auf die zuvor nicht direkt zugegriffen werden konnte. LLMs sind nützlich, um solche Extraktionstools schnell zu erstellen und die Entwicklungszeit erheblich zu verkürzen. Noch wichtiger ist jedoch, dass mit der Leistungsfähigkeit der LLMs die zu extrahierenden Eigenschaften komplexer sein können, als dies mit herkömmlichen Methoden möglich war.
In diesem Blogbeitragwerden wir drei Strategien für die Verwendung von LLMs zur Extraktion strukturierter Daten aus einem Textstück untersuchen: 1) Bereitstellung eines JSON-Beispiels, 2) Definition eines Pydantic-Schemas (siehe auch diesen Blogbeitrag) und schließlich, 3) die Verwendung der OpenAI API für Funktionsaufrufe. Doch bevor wir uns mit diesen Strategien beschäftigen, sollten wir uns zunächst ansehen, wie ein naiver Ansatz aussehen würde.
Machen Sie mit!
Ein erster naiver Ansatz
Lassen Sie uns mit einem naiven Ansatz beginnen. Wir haben einen Text, aus dem wir einige strukturierte Daten extrahieren möchten. Lassen Sie uns ein Beispiel nehmen. Wir betrachten eine Hausanzeige für ein Miethaus in Amsterdam. Die Anzeige sieht folgendermaßen aus (es handelt sich nicht um eine echte Anzeige):
Title: Charming Amsterdam House with Balcony, Pet-Friendly for Couples
Description:
Welcome to this charming house in the heart of beautiful Amsterdam! This cozy and well-maintained property offers a delightful living space, complete with a private balcony, making it the perfect home for a couple seeking comfort and convenience.
Key Features:
- Balcony: Step out onto the balcony and enjoy your morning coffee or unwind with a glass of wine in the evening. The balcony offers a peaceful retreat where you can soak up the vibrant atmosphere of Amsterdam.
- Pet-Friendly: We understand that pets are an important part of your family. This house gladly welcomes your furry companions, allowing you to create lasting memories with your beloved pets.
- Ideal for Couples: With its intimate atmosphere and well-designed layout, this house is an ideal choice for couples. Whether you're enjoying a romantic dinner in the dining area or relaxing in the cozy living room, this house provides a comfortable and intimate space for you and your partner.
- Central Location: Situated in the heart of Amsterdam, this house offers easy access to the city's most popular attractions, vibrant nightlife, and cultural hotspots. Explore the charming canals, visit world-renowned museums, or indulge in the diverse culinary scene – all just a stone's throw away from your doorstep.
- Well-Maintained: This house has been meticulously cared for, ensuring a comfortable living experience. The property features modern amenities, including a fully equipped kitchen, a spacious bedroom, and a clean and functional bathroom.
Additional Information:
- The house is available for long-term rental.
- Furnishings and appliances are included.
- Utilities (electricity, water, internet) are not included in the rental price.
- Public transportation options and amenities are easily accessible from the house.
Don't miss out on this fantastic opportunity to make this charming Amsterdam house your home. Contact us today to schedule a viewing or to request further information.
Es gibt in der Tat eine Menge potenziell nützlicher Informationen! In der Anzeige werden "Hauptmerkmale" aufgeführt, darunter ein Balkon, Haustierfreundlichkeit, Ideal für Paare usw. Wenn Sie ein Haustier haben, wird es Sie interessieren, ob es in Ihrer Mietwohnung erlaubt ist oder nicht. Ähnlich verhält es sich mit dem Balkon: Vielleicht haben Sie viele Wohnungsanzeigen durchgesehen, um Ihre Traumwohnung mit Balkon zu finden! Das ist Zeit, die Sie mit dem Filtern der Anzeigen verbracht haben und die auch hätte automatisiert werden können. Auf ähnliche Weise kann es ein Dutzend anderer Eigenschaften geben, die für jeden, der ein neues Zuhause sucht, sehr nützlich sein können. Lassen Sie uns versuchen, diese Eigenschaften mit Hilfe eines LLM herauszufiltern!

Abbildung: Überblick über den Prozess. Ein Text wird an den LLM übergeben, der eine strukturierte Version der Informationen ausgibt. Hinweis: Die Hausanzeige auf der linken Seite ist nicht echt.
Versuchen wir es mit einer Eingabeaufforderung, um zu sehen, ob wir einige Informationen extrahieren können.
A house listing will follow, as it appeared on a housing website. Please extract the following information from the listing:
- Is pet friendly (yes/no/unknown, quote: "...")
- Has a garden (yes/no/unknown, quote: "...")
- Has a parking place (yes/no/unknown, quote: "...")
- Is suitable for a couple (yes/no/unknown, quote: "...")
- Has a balcony (yes/no/unknown, quote: "...")
## House listing
{house}
... wobei wir {house} durch die obige Hausanzeige ersetzen. Schauen wir uns an, was der GPT-3.5-Turbo von OpenAI ausgibt:
From the house listing, the extracted information is as follows:
- Is pet friendly: Yes (quote: "Pet-Friendly: We understand that pets are an important part of your family. This house gladly welcomes your furry companions, allowing you to create lasting memories with your beloved pets.")
- Has a garden: Unknown (no mention of a garden in the listing)
- Has a parking place: Unknown (no mention of a parking place in the listing)
- Is suitable for a couple: Yes (quote: "Ideal for Couples: With its intimate atmosphere and well-designed layout, this house is an ideal choice for couples. Whether you're enjoying a romantic dinner in the dining area or relaxing in the cozy living room, this house provides a comfortable and intimate space for you and your partner.")
- Has a balcony: Yes (quote: "Balcony: Step out onto the balcony and enjoy your morning coffee or unwind with a glass of wine in the evening. The balcony offers a peaceful retreat where you can soak up the vibrant atmosphere of Amsterdam.")
Nicht schlecht! Es scheint die meisten Eigenschaften richtig zu haben, auch die, die es nicht bestimmen konnte(Unbekannt). In den Fällen, in denen es die Informationen extrahieren konnte, hat es auch ein Zitat geliefert.
Allerdings liegen uns die Informationen noch immer nicht in einem strukturierten Format vor. Das ist schade, denn genau das brauchen wir, um damit zu arbeiten. Lassen Sie uns versuchen, die Informationen in einem strukturierten Format abzurufen.
Strukturiert vorgehen
Unser Ziel ist es also, die Informationen in einer strukturierten Weise zu erhalten. Struktur kann viele Formen annehmen. Es gibt z.B. viele strukturierte Dateiformate. Denken Sie an JSON, XML, CSV, usw. Wir werden JSON verwenden, weil es ein sehr verbreitetes Format ist und sich in Python leicht verarbeiten lässt.
Wir werden verschiedene Ansätze für das Prompting des LLM untersuchen. Sie können dies Prompt Engineering nennen, wenn Sie wollen. Das Ziel ist es, herauszufinden, welche Ansätze mit einem bestimmten LLM-Modell gut funktionieren und welche nicht. Lassen Sie uns beginnen.
1. JSON-Beispiel
Wir werden den LLM bitten, seine Ausgabe in JSON bereitzustellen. Daher sollten wir den LLM über das Format informieren, in dem er das JSON zurückgeben soll. Zu diesem Zweck können wir dem LLM einfach ein Beispiel für die gewünschte JSON-Ausgabe geben. Dies wird unsere Aufforderung sein:
A house listing will follow, as it appeared on a housing website. Please extract the following information from the listing in a JSON response:
```
{
"is_pet_friendly": {
"value": ..., # yes/no/unknown
"quote": ..., # exact quote from listing
},
"has_garden": {
"value": ..., # yes/no/unknown
"quote": ..., # exact quote from listing
},
"has_parking_place": {
"value": ..., # yes/no/unknown
"quote": ..., # exact quote from listing
},
"is_suitable_for_couple": {
"value": ..., # yes/no/unknown
"quote": ..., # exact quote from listing
},
"has_balcony": {
"value": ..., # yes/no/unknown
"quote": ..., # exact quote from listing
},
}
```
... auf die GPT-3.5 turbo die folgende Antwort hat:
{
"is_pet_friendly": {
"value": "no",
"quote": "Pets not allowed"
},
"has_garden": {
"value": "unknown",
"quote": "-"
},
"has_parking_place": {
"value": "yes",
"quote": "Parking possibility: Private parking place outside"
},
"is_suitable_for_couple": {
"value": "unknown",
"quote": "-"
},
"has_balcony": {
"value": "yes",
"quote": "Balcony (facing South-West)"
}
}
Hinweis: Diese Antwort wurde mit einem anderen Eingabebeispiel als im vorherigen Abschnitt erzeugt.
Das ist toll! Der LLM hat gerade ein gültiges JSON zurückgegeben. Er lieferte das von uns gewünschte Format und listet die Eigenschaften als Objekte auf, die ein json.loads in Python parsen:
obj = json.loads(
response_text
)
... wobei response_text der Text ist, den der LLM zurückgegeben hat. Das ist großartig. Wir können die Daten nun strukturiert in Python verwenden.
Allerdings garantieren LLMs keine bestimmte Leistung, auch wenn Sie darum bitten. Es kann z.B. vorkommen, dass die Hausliste so kurz ist, dass der LLM halluzinieren wird. Wenn die Eingabedaten zu kurz sind, weicht der LLM vom gewünschten Format ab.
Betrachten Sie diese Ausgabe, die der LLM erzeugt hat:
"House listing"
{
"title": "Beautiful Modern House",
"description": "Spacious and modern house with stunning views. Perfect for a couple or small family.",
"location": "123 Main Street, Anytown, USA",
"price": "$500,000",
"is_pet_friendly": {
"value": "yes",
"quote": "Pets allowed with additional deposit."
},
"has_garden": {
"value": "yes",
"quote": "Large backyard garden with fruit trees."
},
"has_parking_place": {
"value": "yes",
"quote": "Two-car garage and additional street parking available."
},
"is_suitable_for_couple": {
"value": "yes",
"quote": "Perfect for a couple or small family."
},
"has_balcony": {
"value": "yes",
"quote": "Spacious balcony with beautiful views."
}
}
Diese wurde mit der gleichen Eingabeaufforderung wie zuvor erstellt. Jetzt gibt der LLM jedoch alle möglichen Eigenschaften zurück, nach denen nicht gefragt wurde, wie price, location, description und title. Noch schlimmer ist, dass die Zeichenkette im oberen Bereich "House listing" die Ausgabe zu ungültigem JSON macht. Das Parsen dieser Zeichenfolge ergibt:
JSONDecodeError Traceback (most recent call last)
……
----> 1 obj = json.loads(
2 response["choices"][0]["message"]["content"]
3 )
……
JSONDecodeError: Extra data: line 2 column 1 (char 16)
... das ist ein Fehler.
Um dies zu verhindern, kann die Eingabe im Voraus überprüft werden, um festzustellen, ob genügend Daten vorhanden sind, um damit zu arbeiten. Wenn wir dennoch auf diese Weise reparieren wollen, gibt es zwei Strategien:
- Führen Sie die Eingabeaufforderung erneut aus. Da wir die
temperaturehöher als 0.0 gesetzt haben, kann der LLM eine weitere Antwort erzeugen. Vielleicht erzeugt er dieses Mal ein gültiges JSON. - Zeigen Sie dem LLM seinen eigenen Fehler an. Noch besser ist es, wenn wir eine weitere Eingabeaufforderung ausführen, aber den Fehler selbst einschließen. Wir werden den LLM bitten, den Fehler zu berücksichtigen und beim nächsten Mal ein gültiges JSON zu liefern.
Mit diesen Methoden können wir versuchen, den zuvor erzeugten Fehler zu reparieren. Aber gibt es bessere Möglichkeiten, um den LLM diese Struktur aus den Daten abrufen zu lassen? Lassen Sie uns einen anderen Ansatz versuchen.
2. Pydantisches Schema
Bisher haben wir dem LLM eine Beispiel-JSON-Ausgabe gegeben, mit der er arbeiten konnte. Aber können wir eine genauere Definition unserer gewünschten Ausgabe angeben? Wir können sie verwenden pydantic dafür. Pydantic ist eine Datenvalidierungsbibliothek für Python, die über nützliche Funktionen zur Serialisierung und Deserialisierung von JSON verfügt. Wir können eine Klasse definieren, die die zu extrahierenden Eigenschaften beschreibt:
from typing import Optional
from pydantic import Field, BaseModel
class HouseFeature(BaseModel):
was_extracted: bool = Field(..., description="Whether the feature was extracted")
quote: Optional[str] = Field(
default=None, description="Exact quote from the listing"
)
value: Optional[bool] = Field(
default=None, description="Whether the house has the feature"
)
class HouseFeatures(BaseModel):
"""Correctly extracted house listing features"""
is_pet_friendly: HouseFeature
has_garden: HouseFeature
has_parking_place: HouseFeature
is_suitable_for_couple: HouseFeature
has_balcony: HouseFeature
Diese beiden Klassen beschreiben also die Informationen, die wir extrahieren möchten. Jetzt können wir die gesamte Funktionalität von Pydantic nutzen, um die Eingabeaufforderung zu formulieren und die Antwort anschließend zu analysieren. Mit Pydantic können wir ein ausgeklügeltes Schema der Modelle ausdrucken, indem wir HouseFeatures.model_json_schema() verwenden, etwa so:
output_template = HouseFeatures.model_json_schema()
output_template_str = json.dumps(output_template)
... wobei output_template_str jetzt eine JSON-Zeichenkette mit allen Details ist:

Das sind sehr detaillierte Informationen! Die genauen gewünschten Typen der einzelnen Attribute sind definiert, z.B. boolean, string oder null. Wir können dies nun verwenden, um eine neue Eingabeaufforderung zu formulieren:
A house listing will follow. Your task is to extract details from the house listing. Fill in the following template:
# template
{output_template_str}
# house listing
{str(house)}
... hier ersetzen wir das Pydantic-Schema, das wir gerade generiert haben, durch output_template_str. Schauen wir mal, was der LLM zurückgibt.
{
"is_pet_friendly": {
"was_extracted": false
},
"has_garden": {
"was_extracted": false
},
"has_parking_place": {
"was_extracted": true,
"value": true,
"quote": "Parking in front of the building is available through a permit system"
},
"is_suitable_for_couple": {
"was_extracted": true,
"value": true,
"quote": "preference for single household"
},
"has_balcony": {
"was_extracted": true,
"value": true,
"quote": "east-facing balcony (morning sun) spans the entire width of the apartment and overlooks the beautiful courtyard"
}
}
Perfekt! Das sieht wie ein gültiges JSON aus. Jetzt kommt der spaßige Teil. Um das JSON zurück in eine Instanz des Pydantic-Modells zu deserialisieren, können wir einfach Folgendes tun:
house_features = HouseFeatures.model_validate_json(
response_text
)
Und schon sind wir in der Python-Welt. Außerdem kann Pydantic in Fällen, in denen das Parsing fehlschlägt, beschreibende Fehlermeldungen ausgeben. Wir können dann die im vorherigen Abschnitt beschriebenen Strategien anwenden, um das Parsing zu korrigieren.
Dies ist bereits eine leistungsstarke Technik. Aber wir sind natürlich sehr neugierig darauf, was noch alles möglich ist. OpenAI hat etwas für uns auf Lager.
3. OpenAI Funktionsaufruf
Im Juni 2023 kündigte OpenAI die Unterstützung für Funktionsaufrufe an. Dies ermöglicht Ihnen, Funktionen zu definieren, die der LLM als Teil seiner Antwort aufrufen kann. GPT-3.5 und GPT-4 sind so eingestellt, dass sie in einem bestimmten Format antworten, wenn wir Funktionen bereitstellen. Der eigentliche Aufruf des Funktionscodes liegt immer noch bei Ihnen, aber der LLM kann vorschlagen, welche Funktion mit welchen Argumenten aufgerufen werden soll. Denken Sie an Anwendungsfälle wie das Abrufen des aktuellen Wetters, das Ausführen einer SQL-Abfrage oder das Senden einer Nachricht an einen Empfänger. Aber wir können dasselbe für unseren Anwendungsfall ausnutzen: Was wäre, wenn wir eine Funktion definieren, die als Argumente die Eigenschaften hat, die wir extrahieren möchten? Lassen Sie es uns gemeinsam herausfinden.
Zunächst definieren wir eine Funktion im erforderlichen OpenAI-Format:
functions = [
{
"name": "extract_house_features",
"type": "function",
"description": "Extract features and properties from a house listing text.",
"parameters": {
"type": "object",
"properties": {
"is_pet_friendly": {
"type": "boolean",
"description": "Whether the house allows pets"
},
"has_garden": {
"type": "boolean",
"description": "Whether the house has a garden"
},
"has_parking_place": {
"type": "boolean",
"description": "Whether the house has a parking place"
},
"is_suitable_for_couple": {
"type": "boolean",
"description": "Whether the house is suitable for couples"
},
"has_balcony": {
"type": "boolean",
"description": "Whether the house has a balcony"
},
},
"required": [
"is_pet_friendly",
"has_garden",
"has_parking_place",
"is_suitable_for_couple",
"has_balcony"
]
},
}
]
Das beschreibt die Funktion ausführlich, mit allen Argumenten, ihren Typen und ob sie optional oder erforderlich sind. Wir können dieses functions Objekt nun an die OpenAI ChatCompletion API übergeben:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
deployment_id="gpt-35-turbo-us",
messages=messages,
functions=functions,
function_call={
"name": "extract_house_features”
},
)
Erwähnenswert ist hier der Parameter function_call. Dadurch wird der Aufruf dieser Funktion bei jedem Aufruf erzwungen, was die gewünschte Funktionalität für unseren Anwendungsfall ist.
Außerdem werden wir den LLM in einen anderen Kontext stellen:
Assistant is a large language model designed to extract structured data from text.
Auch System-Eingabeaufforderung genannt. Siehe diesen Beitrag. Die System-Eingabeaufforderung hilft dabei, den LLM gezielter auf sein Ziel auszurichten. Wir brauchen nur eine Eingabeaufforderung:
{house}
Das wurde gerade super kurz! Alle Anweisungen sind bereits im Kontext und in der Funktionsbeschreibung gekapselt. Schauen wir mal, was der LLM zurückgibt:
{
"name": "extract_house_features",
"arguments": "{n "is_pet_friendly": false,n "has_garden": false,n "has_parking_place": true,n "is_suitable_for_couple": true,n "has_balcony": truen}"
}
Das ist ein JSON, das in einem bestimmten Format formatiert ist. Das Format ist so beschaffen, dass es direkt zur Ausführung der Funktion verwendet werden kann, die wir zuvor definiert haben. Insbesondere soll die Funktion mit bestimmten Werten ausgeführt werden, die der LLM ebenfalls für uns definiert hat: das sind die Daten, die wir extrahieren möchten. Beachten Sie, dass wir der Einfachheit halber die Eigenschaft quote nicht in die Funktionsdefinition aufgenommen haben.
Jetzt können wir dieses JSON parsen und die Funktion tatsächlich aufrufen:
function_call_args = json.loads(
function_call["arguments"]
)
extract_house_features(
**function_call_args
)
... was unsere Daten in die strukturierte Python-Welt bringt. Das ist großartig! Es gibt sogar noch eine andere Möglichkeit, es noch besser zu machen. Es gibt eine Bibliothek, die den erstgenannten Prozess ein wenig vereinfacht.
Bibliothek des Kursleiters
Der
GitHub Ausbilder
Bibliothek bietet einige Abkürzungen für das, was wir oben gemacht haben. Anstatt unsere Funktionsdefinition wie oben akribisch in JSON zu definieren, können wir jetzt ein Pydantic-Modell verwenden, um zu definieren, welche Informationen wir benötigen. Aus diesem Pydantic-Modell können wir dann die Funktionsdefinition generieren. Das funktioniert folgendermaßen:
from instructor import OpenAISchema
class HouseFeatures(OpenAISchema):
"""Correctly extracted house listing features"""
is_pet_friendly: bool
has_garden: bool
has_parking_place: bool
is_suitable_for_couple: bool
has_balcony: bool
Wenn wir nun HouseFeatures.openai_schema aufrufen, wird das Schema gedruckt, das eine Funktion mit den Eigenschaften als Argumente enthält. Wir können den LLM-Aufruf nun wie folgt durchführen:
prompt = str(house)
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
deployment_id="gpt-35-turbo-us",
functions=[HouseFeatures.openai_schema],
function_call={"name": HouseFeatures.openai_schema["name"]},
messages=[
{"role": "system", "content": "Assistant is a large language model designed to extract structured data from text."},
{"role": "user", "content": prompt}
],
)
house_features = HouseFeatures.from_response(response)
... wobei house_features nun unsere strukturierten Daten in einem Pydantic-Modell enthält! Mit der Bibliothek instructor wurde der Prozess noch reibungsloser ✨.
Fazit
Unstrukturierte Daten, wie z.B. Text, können potenziell sehr wertvolle Informationen enthalten. LLMs können uns helfen, diese Informationen zu extrahieren. Es gibt drei Hauptansätze, um dies zu tun:
- JSON-Beispiel. Geben Sie dem LLM ein Beispiel für die gewünschte JSON-Ausgabe und weisen Sie ihn an, eine Ausgabe im gleichen Format zu erzeugen. Das ist einfach, aber es kann dazu führen, dass Ausgaben im falschen Format erzeugt werden. Die Leistung variiert je nach verwendetem LLM.
- Pydantisches Schema. Mit
pydantickönnen wir ein Schema für die gewünschte Ausgabe definieren. Dieses Schema können wir dann verwenden, um eine Eingabeaufforderung zu formulieren. Der LLM wird dann versuchen, ein JSON im gleichen Format zu erzeugen. Eine leistungsstarke Technik, aber keine Garantie für eine bessere Leistung. Auch hier variiert die Leistung je nach verwendetem LLM. - OpenAI Funktionsaufrufe. Mit der API für Funktionsaufrufe von OpenAI können wir den LLM anweisen, eine Funktion mit den gewünschten Eigenschaften als Argumente aufzurufen. Die Modelle von OpenAI sind so abgestimmt, dass sie eine speziell formatierte Ausgabe erzeugen, die leicht geparst werden kann. Dies kann sehr viel robuster sein als die erste Variante, ist aber nur mit den Modellen von OpenAI verwendbar.
Probieren Sie verschiedene Modelle aus, um zu sehen, welches für Ihren Anwendungsfall am besten geeignet ist. Modelle von verschiedenen Anbietern oder sogar Open-Source behandeln jede Methode mit unterschiedlicher Leistung. Ein Experiment ist gerechtfertigt, um herauszufinden, welches für Sie am besten funktioniert.
Schön, dass Sie mitlesen & viel Erfolg mit Ihrem eigenen Anwendungsfall!
Code mit Beispielen verfügbar unter: Datensatzanreicherung mit llms
Dieser Inhalt wurde auch in einem PyData-Vortrag vorgestellt! Sehen Sie sich die Aufzeichnung hier an: Jeroen Overschie - Anreicherung von Datensätzen mit LLMs ✨ Youtube
Verfasst von

Jeroen Overschie
Machine Learning Engineer
Jeroen is a Machine Learning Engineer at Xebia.
Unsere Ideen
Weitere Blogs
Contact




