Verwendung einer Vektordatenbank zur Suche in Reden des Weißen Hauses
Dieser Artikel wurde ursprünglich auf The New Stack veröffentlicht.
Die Wahlkampfsaison für den US-Präsidentschaftswahlkampf steht bevor. Es ist ein guter Zeitpunkt, auf einige der Reden zurückzublicken, die die Biden-Regierung während seiner ersten beiden Jahre im Amt gehalten hat. Wäre es nicht großartig, einige Redetranskripte durchsuchen zu können, um mehr über die bisherige Botschaft des Weißen Hauses zu bestimmten Themen zu erfahren?
Nehmen wir an, wir möchten den Inhalt einer Rede durchsuchen. Wie würden wir das machen? Wir könnten semantische Suche verwenden. Semantische Suche ist derzeit eines der heißesten Themen in der künstlichen Intelligenz (KI). Sie ist wichtiger geworden, da wir den Popularitätsanstieg von Anwendungen zur Verarbeitung natürlicher Sprache (NLP) wie ChatGPT erlebt haben. Anstatt GPT wiederholt anzupingen, was sowohl wirtschaftlich als auch ökologisch teuer ist, können wir eine Vektordatenbank verwenden, um die Ergebnisse zwischenzuspeichern (wie etwa mit GPTCache).
In diesem Tutorial werden wir lokal eine Vektordatenbank hochfahren, damit wir Bidens Reden von 2021 bis 2022 nach Inhalt durchsuchen können. Der Datensatz, den wir verwenden, ist der Datensatz „The White House (Speeches and Remarks) 12/10/2022“, den wir auf Kaggle gefunden und für dieses Beispiel über Google Drive zum Download bereitgestellt haben. Ein begleitendes Notebook zu diesem Tutorial ist auf GitHub verfügbar.
Bevor wir in den Code eintauchen, stellen Sie bitte sicher, dass Sie die Voraussetzungen herunterladen. Wir benötigen vier Bibliotheken: PyMilvus, Milvus, Sentence-Transformers und gdown. Sie können die notwendigen Bibliotheken von PyPi beziehen, indem Sie Folgendes ausführen: pip3 install pymilvus==2.2.5 sentence-transformers gdown milvus.
Vorbereiten des White-House-Rededatensatzes
Wie bei fast jedem KI/ML-Projekt, das auf realen Datensätzen basiert, müssen wir zuerst die Daten vorbereiten. Wir verwenden gdown, um den Datensatz herunterzuladen, und zipfile, um ihn in einen lokalen Ordner zu extrahieren. Nachdem der unten stehende Code ausgeführt wurde, erwarten wir, eine Datei mit dem Titel „The white house speeches.csv“ in einem Ordner mit dem Titel „white_house_2021_2022“ zu sehen.
import gdown
url = 'https://drive.google.com/uc?id=10_sVL0UmEog7mczLedK5s1pnlDOz3Ukf'
output = './white_house_2021_2022.zip'
gdown.download(url, output)
import zipfile
with zipfile.ZipFile("./white_house_2021_2022.zip","r") as zip_ref:
zip_ref.extractall("./white_house_2021_2022")
Wir verwenden pandas, um die CSV-Daten zu laden und zu überprüfen.
import pandas as pd
df = pd.read_csv("./white_house_2021_2022/The white house speeches.csv")
df.head()
Wenn wir uns den head der Daten ansehen, was fällt Ihnen auf? Das Erste, was mir auffällt, ist, dass die Daten vier Spalten haben: eine Titel-, Datumszeit-, Orts- und Redespalte. Das Zweite ist, dass es Nullwerte gibt. Nullwerte sind nicht immer ein Problem, aber für unsere Daten sind sie es.
Bereinigen des Datensatzes
Reden ohne jegliche Substanz (Nullwerte in der Spalte „Speech“) sind für uns völlig nutzlos. Lassen Sie uns unsere Nullwerte entfernen und die Daten erneut untersuchen.
df = df.dropna()
df
Jetzt sehen wir, dass es tatsächlich ein zweites Problem gibt, das beim bloßen Blick auf den head der Daten nicht sofort offensichtlich war. Wenn Sie sich den letzten Eintrag ansehen, werden Sie feststellen, dass dieser Eintrag nur eine Uhrzeit ist. „12:18 P.M. EST“ ist kaum eine Rede. Es ergibt keinen Sinn, diesen Eintrag zu speichern. Wir können aus dem Speichern eines Vektor-Embeddings keinen Wert ableiten.
Entfernen wir alle Reden, die kürzer als eine bestimmte Länge sind. Für dieses Beispiel habe ich 50 gewählt, aber du kannst wählen, was für dich sinnvoll ist. Ich habe 50 gewählt, nachdem ich viele verschiedene Zahlen ausprobiert habe. Wenn du nach Redetranskripten zwischen 20 und 50 Zeichen suchst, wirst du sehen, dass viele davon Orte oder Uhrzeiten sind, mit ein paar zufälligen Sätzen dazwischen.
cleaned_df = df.loc[(df["Speech"].str.len() > 50)]cleaned_df
Nachdem die kurzen, substanzlosen Reden erledigt sind, schauen wir uns erneut unsere Daten an und sehen noch ein weiteres Problem. Viele der Reden enthalten \r\n-Werte – Zeilenumbrüche und Wagenrückläufe. Diese Zeichen werden zur Formatierung verwendet, enthalten aber keinen semantischen Wert. Der nächste Schritt in unserem Datenbereinigungsprozess besteht darin, diese zu entfernen.
cleaned_df["Speech"] = cleaned_df["Speech"].str.replace("\r\n", "")
cleaned_df
Das sieht schon viel besser aus. Der letzte Schritt besteht darin, die Spalte „Date_time“ in ein besseres Format umzuwandeln, damit sie in unserer Vektordatenbank gespeichert und mit anderen Datums- und Zeitangaben verglichen werden kann. Wir verwenden die datetime-Bibliothek, um dieses Datumszeitformat einfach in ein universelles YYYY-MM-DD-Format umzuwandeln.
import datetime
# Convert the 'date' column to datetime objects
cleaned_df["Date_time"] = pd.to_datetime(cleaned_df["Date_time"], format="%B %d, %Y")
cleaned_df
Einrichten einer Vektordatenbank für semantische Suche
Unsere Daten sind jetzt bereinigt und einsatzbereit. Der nächste Schritt besteht darin, eine Vektordatenbank zu starten, um die Reden tatsächlich nach ihrem Inhalt zu durchsuchen. Für dieses Beispiel verwenden wir Milvus Lite, eine Lite-Version von Milvus, die du ohne Docker, Kubernetes oder den Umgang mit irgendeiner YAML-Datei zum Laufen bringen kannst.
Als Erstes definieren wir einige unserer Konstanten. Wir benötigen einen Collection-Namen (für die Vektordatenbank), die Anzahl der Dimensionen in unserem eingebetteten Vektor, eine Batch-Größe und eine Zahl, die definiert, wie viele Ergebnisse wir bei der Suche zurückhaben möchten. Dieses Beispiel verwendet den MiniLM L6 v2 Sentence Transformer, der 384-dimensionale Embedding-Vektoren erzeugt.
COLLECTION_NAME = "white_house_2021_2022"
DIMENSION = 384
BATCH_SIZE = 128
TOPK = 3
Wir verwenden den default_server von Milvus. Dann verwenden wir das PyMilvus SDK, um eine Verbindung zu unserem lokalen Milvus-Server herzustellen. Wenn es in unserer Vektordatenbank eine Collection gibt, die denselben Namen hat wie der Collection-Name, den wir zuvor definiert haben, löschen wir diese Collection, um sicherzustellen, dass wir mit einer leeren Ausgangsbasis starten.
from milvus import default_server
from pymilvus import connections, utility
default_server.start()
connections.connect(host="127.0.0.1", port=default_server.listen_port)
if utility.has_collection(COLLECTION_NAME):
utility.drop_collection(COLLECTION_NAME)
Wie bei den meisten anderen Datenbanken benötigen wir ein Schema, um Daten in die Milvus-Vektordatenbank zu laden. Zunächst definieren wir die Datenfelder, die jedes Objekt haben soll. Gut, dass wir uns die Daten vorher angesehen haben. Wir verwenden fünf Datenfelder: die vier Spalten, die wir zuvor hatten, und eine ID-Spalte. Nur dass wir dieses Mal das Vektor-Embedding der Rede anstelle des eigentlichen Textes verwenden.
from pymilvus import FieldSchema, CollectionSchema, DataType, Collection
# object should be inserted in the format of (title, date, location, speech embedding)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=500),
FieldSchema(name="date", dtype=DataType.VARCHAR, max_length=100),
FieldSchema(name="location", dtype=DataType.VARCHAR, max_length=200),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=DIMENSION)
]
schema = CollectionSchema(fields=fields)
collection = Collection(name=COLLECTION_NAME, schema=schema)
Das Letzte, was wir definieren müssen, bevor wir bereit sind, Daten in die Vektordatenbank zu laden, ist der Index. Es gibt viele Vektorindizes und Muster, aber für dieses Beispiel verwenden wir den IVF_FLAT-Index mit 128 Clustern. Größere Anwendungen verwenden normalerweise mehr als 128 Cluster, aber wir haben ohnehin nur etwas mehr als 600 Einträge. Für unsere Distanz messen wir mit der L2-Norm. Sobald wir unsere Indexparameter definiert haben, erstellen wir den Index in unserer Collection und laden sie zur Verwendung.
index_params = {
"index_type": "IVF_FLAT",
"metric_type": "L2",
"params": {"nlist": 128},
}
collection.create_index(field_name="embedding", index_params=index_params)
collection.load()
Vektoreinbettungen aus Reden erhalten
Vieles von dem, was wir bisher durchgegangen sind, gilt bei der Arbeit mit fast jeder Datenbank. Wir haben einige Daten bereinigt, eine Datenbankinstanz gestartet und ein Schema für unsere Datenbank definiert. Abgesehen von der Definition eines Index ist eine weitere Sache, die wir insbesondere für Vektordatenbanken tun müssen, das Abrufen der Einbettungen.
Zuerst holen wir uns das oben erwähnte Sentence-Transformer-Modell MiniLM L6 v2. Dann erstellen wir eine Funktion, die eine Transformation an den Daten durchführt und sie in die Collection einfügt. Diese Funktion nimmt einen Datenbatch, ruft die Einbettungen für die Redetranskripte ab, erstellt ein Objekt zum Einfügen und fügt es in die Collection ein.
Zum Kontext: Diese Funktion führt eine Batch-Aktualisierung durch. In diesem Beispiel fügen wir 128 Einträge auf einmal als Batch ein. Die einzige Datentransformation, die wir bei unserem Insert durchführen, ist die Umwandlung des Redetextes in eine Einbettung.
from sentence_transformers import SentenceTransformer
transformer = SentenceTransformer('all-MiniLM-L6-v2')
# expects a list of (title, date, location, speech)
def embed_insert(data: list):
embeddings = transformer.encode(data[3])
ins = [
data[0],
data[1],
data[2],
[x for x in embeddings]
]
collection.insert(ins)
Ihre Vektordatenbank befüllen
Mit einer Funktion, die Batch-Einbettungen erstellt und Einfügungen abschließt, sind wir bereit, die Datenbank zu befüllen. Für dieses Beispiel durchlaufen wir jede Zeile in unserem Dataframe und hängen sie an eine Liste von Listen an, die wir für das Batchen unserer Daten verwenden. Sobald wir unsere Batchgröße erreichen, rufen wir die Funktion embed_insert auf und setzen unseren Batch zurück.
Wenn nach Abschluss der Schleife noch Daten im Datenbatch übrig sind, betten wir die verbleibenden Daten ein und fügen sie ein. Schließlich rufen wir, um das Befüllen unserer Vektordatenbank abzuschließen, flush auf, um sicherzustellen, dass die Datenbank aktualisiert und indexiert wird.
data_batch = [[], [], [], []]
for index, row in cleaned_df.iterrows():
data_batch[0].append(row["Title"])
data_batch[1].append(str(row["Date_time"]))
data_batch[2].append(row["Location"])
data_batch[3].append(row["Speech"])
if len(data_batch[0]) % BATCH_SIZE == 0:
embed_insert(data_batch)
data_batch = [[], [], [], []]
# Embed and insert the remainder
if len(data_batch[0]) != 0:
embed_insert(data_batch)
# Call a flush to index any unsealed segments.
collection.flush()
Semantische Suche in Reden des Weißen Hauses auf Grundlage von Beschreibungen
Nehmen wir an, ich bin daran interessiert, eine Rede zu finden, in der der Präsident über die Auswirkungen erneuerbarer Energien im National Renewable Energy Lab (NREL) sprach, sowie eine Rede, in der der Vizepräsident und der Premierminister von Kanada sprechen. Ich kann die Titel der ähnlichsten Reden finden, die von Mitgliedern des Weißen Hauses in den Jahren 2021–2022 gehalten wurden, indem ich die Vektordatenbank verwende, die wir gerade erstellt haben.
Wir können unsere Vektordatenbank nach Reden durchsuchen, die unseren Beschreibungen am ähnlichsten sind. Dann müssen wir lediglich die Beschreibung mit demselben Modell, das wir verwendet haben, um die Einbettungen der Reden zu erhalten, in eine Vektoreinbettung umwandeln und anschließend die Vektordatenbank durchsuchen.
Nachdem wir die Beschreibungen in ein Vektor-Embedding umgewandelt haben, verwenden wir die Funktion search auf unserer Collection. Wir übergeben die Embeddings als Suchdaten, übergeben das Feld, nach dem wir suchen, fügen einige Parameter dafür hinzu, wie gesucht werden soll, ein Limit für die Anzahl der Ergebnisse und das Feld, das wir zurückgeben möchten. In diesem Beispiel sind die Suchparameter, die wir übergeben müssen, der Metriktyp, der derselbe Typ sein muss, den wir beim Erstellen des Index verwendet haben (L2-Norm), und die Anzahl der Cluster, die wir durchsuchen möchten (indem nprobe auf 10 gesetzt wird).
import time
search_terms = ["The President speaks about the impact of renewable energy at the National Renewable Energy Lab.", "The Vice President and the Prime Minister of Canada both speak."]
# Search the database based on input text
def embed_search(data):
embeds = transformer.encode(data)
return [x for x in embeds]
search_data = embed_search(search_terms)
start = time.time()
res = collection.search(
data=search_data, # Embeded search value
anns_field="embedding", # Search across embeddings
param={"metric_type": "L2",
"params": {"nprobe": 10}},
limit = TOPK, # Limit to top_k results per search
output_fields=["title"] # Include title field in result
)
end = time.time()
for hits_i, hits in enumerate(res):
print("Title:", search_terms[hits_i])
print("Search Time:", end-start)
print("Results:")
for hit in hits:
print( hit.entity.get("title"), "----", hit.distance)
print()
Wenn wir in diesem Beispiel nach den Sätzen suchen, erwarten wir eine Ausgabe wie im Bild unten. Dies war eine erfolgreiche Suche, weil die Titel dem entsprechen, was wir erwarten. Die erste Beschreibung gibt den Titel einer Rede von Präsident Biden im NREL zurück, und die zweite Beschreibung gibt einen Titel zurück, der eine Rede von Vizepräsidentin Harris und Premierminister Trudeau widerspiegelt.
Zusammenfassung
In diesem Tutorial haben wir gelernt, wie man eine Vektordatenbank verwendet, um Reden der Biden-Regierung vor den Zwischenwahlen 2022 semantisch zu durchsuchen. Die semantische Suche ermöglicht es uns, einen kurzen Text zu nehmen und nach semantisch ähnlichem Text zu suchen, nicht nur nach syntaktisch ähnlichem Text. Dadurch können wir nach einer allgemeinen Beschreibung einer Rede suchen, anstatt nach einer Rede auf Basis bestimmter Sätze oder Zitate zu suchen. Für die meisten von uns macht das das Finden von Reden, an denen wir interessiert wären, deutlich einfacher.
Weiterlesen

Zilliz Cloud Now Available in AWS Europe (Ireland)
Zilliz Cloud launches in AWS eu-west-1 (Ireland) — bringing low-latency vector search, EU data residency, and full GDPR-ready infrastructure to European AI teams. Now live across 30 regions on five cloud providers.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.

Zilliz Cloud BYOC Upgrades: Bring Enterprise-Grade Security, Networking Isolation, and More
Discover how Zilliz Cloud BYOC brings enterprise-grade security, networking isolation, and infrastructure automation to vector database deployments in AWS



