Schema-Entwurf Hands-On
Information Retrieval (IR)-Systeme, auch bekannt als Suche, sind für verschiedene KI-Anwendungen wie Retrieval-augmented Generation (RAG), Bildsuche und Produktempfehlungen unerlässlich. Der erste Schritt bei der Entwicklung eines IR-Systems ist das Entwerfen des Datenmodells. Dazu gehört die Analyse der Geschäftsanforderungen, die Festlegung, wie Informationen organisiert werden sollen, und die Indizierung der Daten, um sie semantisch durchsuchbar zu machen.
Milvus unterstützt die Definition des Datenmodells durch ein Sammlungsschema. Eine Sammlung organisiert unstrukturierte Daten wie Text und Bilder zusammen mit ihren Vektordarstellungen, einschließlich dichter und spärlicher Vektoren in unterschiedlicher Präzision, die für die semantische Suche verwendet werden. Zusätzlich unterstützt Milvus die Speicherung und Filterung von Nicht-Vektor-Datentypen, die als "Skalar" bezeichnet werden. Zu den Skalar-Typen gehören BOOL, INT8/16/32/64, FLOAT/DOUBLE, VARCHAR, JSON und Array.
Beispiel eines Datenschemas für die Suche nach Nachrichtenartikeln
Der Entwurf eines Datenmodells für ein Suchsystem beinhaltet die Analyse der Geschäftsanforderungen und die Abstraktion der Informationen in ein schemaexprimiertes Datenmodell. Um beispielsweise einen Text zu durchsuchen, muss er "indiziert" werden, indem die wörtliche Zeichenkette durch "Einbettung" in einen Vektor umgewandelt wird, was die Vektorsuche ermöglicht. Über diese Grundvoraussetzung hinaus kann es erforderlich sein, weitere Eigenschaften wie den Zeitstempel der Veröffentlichung und den Autor zu speichern. Mit diesen Metadaten kann die semantische Suche durch Filterung verfeinert werden, so dass nur Texte gefunden werden, die nach einem bestimmten Datum oder von einem bestimmten Autor veröffentlicht wurden. Möglicherweise müssen sie auch zusammen mit dem Haupttext abgerufen werden, damit das Suchergebnis in der Anwendung angezeigt werden kann. Um diese Textteile zu organisieren, sollte jedem ein eindeutiger Bezeichner zugewiesen werden, ausgedrückt als Ganzzahl oder Zeichenkette. Diese Elemente sind für eine ausgefeilte Suchlogik unerlässlich.
Ein gut durchdachtes Schema ist wichtig, da es das Datenmodell abstrahiert und entscheidet, ob die Geschäftsziele durch die Suche erreicht werden können. Da außerdem jede in die Sammlung eingefügte Datenzeile dem Schema entsprechen muss, trägt es wesentlich zur Wahrung der Datenkonsistenz und langfristigen Qualität bei. Aus technischer Sicht führt ein gut definiertes Schema zu einer gut organisierten Speicherung von Spaltendaten und einer sauberen Indexstruktur, was die Suchleistung steigern kann.
Ein Beispiel: Nachrichtensuche
Nehmen wir an, wir wollen eine Suche für eine Nachrichten-Website entwickeln und haben einen Korpus von Nachrichten mit Text, Miniaturbildern und anderen Metadaten. Zunächst müssen wir analysieren, wie wir die Daten nutzen wollen, um die Geschäftsanforderungen der Suche zu unterstützen. Stellen Sie sich vor, die Anforderung besteht darin, die Nachrichten auf der Grundlage des Miniaturbildes und der Zusammenfassung des Inhalts abzurufen und die Metadaten wie Autoreninfo und Veröffentlichungszeitpunkt als Kriterien zum Filtern des Suchergebnisses zu verwenden. Diese Anforderungen lassen sich weiter aufschlüsseln.
Um Bilder über Text zu suchen, können wir Bilder über ein multimodales Einbettungsmodell in Vektoren einbetten, die Text- und Bilddaten im selben latenten Raum abbilden können.
Der zusammenfassende Text eines Artikels wird über ein Text-Einbettungsmodell in Vektoren eingebettet.
Um nach dem Veröffentlichungszeitpunkt zu filtern, werden die Daten als skalares Feld gespeichert, und für eine effiziente Filterung ist ein Index für das skalare Feld erforderlich. Andere, komplexere Datenstrukturen wie JSON können in einem Skalarfeld gespeichert werden, und eine gefilterte Suche wird nach deren Inhalt durchgeführt (die Indizierung von JSON ist ein zukünftiges Feature).
Um die Bytes der Bildminiaturen abzurufen und auf der Suchergebnisseite darzustellen, wird auch die Bildurl gespeichert. Ähnliches gilt für den Zusammenfassungstext und den Titel. (Alternativ könnten wir die Rohdaten der Text- und Bilddateien als skalare Felder speichern, falls erforderlich).
Zur Verbesserung der Suchergebnisse für den Zusammenfassungstext entwickeln wir einen hybriden Suchansatz. Für einen Suchpfad verwenden wir ein reguläres Einbettungsmodell, um einen dichten Vektor aus dem Text zu generieren, wie z. B. das Modell von OpenAI
text-embedding-3-large
oder das Open-Source-Modellbge-large-en-v1.5
. Diese Modelle sind gut geeignet, um die Gesamtsemantik des Textes darzustellen. Der andere Weg ist die Verwendung von spärlichen Einbettungsmodellen wie BM25 oder SPLADE, um einen spärlichen Vektor zu generieren, der der Volltextsuche ähnelt und gut geeignet ist, die Details und einzelnen Konzepte im Text zu erfassen. Milvus unterstützt dank seiner Multi-Vektor-Funktion die Verwendung beider in derselben Datensammlung. Die Suche in mehreren Vektoren kann in einer einzigenhybrid_search()
Operation durchgeführt werden.Schließlich benötigen wir auch ein ID-Feld zur Identifizierung jeder einzelnen Nachrichtenseite, die in der Milvus-Terminologie formell als "Entität" bezeichnet wird. Dieses Feld wird als Primärschlüssel (oder kurz "pk") verwendet.
Feldname | article_id (Primärschlüssel) | Titel | Autor_Info | veröffentlichen_ts | bild_url | bild_vektor | Zusammenfassung | zusammenfassung_dichter_vektor | zusammenfassung_sparse_vektor |
---|---|---|---|---|---|---|---|---|---|
Typ | INT64 | VARCHAR | JSON | INT32 | VARCHAR | FLOAT_VECTOR | VARCHAR | FLOAT_VECTOR | SPARSE_FLOAT_VECTOR |
Benötigter Index | N | N | N (Unterstützung in Kürze) | Y | N | Y | N | Y | Y |
So implementieren Sie das Beispielschema
Schema erstellen
Zunächst erstellen wir eine Milvus-Client-Instanz, die zur Verbindung mit dem Milvus-Server und zur Verwaltung von Sammlungen und Daten verwendet werden kann.
Um ein Schema zu erstellen, verwenden wir create_schema()
um ein Schema-Objekt zu erstellen und add_field()
um dem Schema Felder hinzuzufügen.
from pymilvus import MilvusClient, DataType
collection_name = "my_collection"
# client = MilvusClient(uri="http://localhost:19530")
client = MilvusClient(uri="./milvus_demo.db")
schema = MilvusClient.create_schema(
auto_id=False,
)
schema.add_field(field_name="article_id", datatype=DataType.INT64, is_primary=True, description="article id")
schema.add_field(field_name="title", datatype=DataType.VARCHAR, max_length=200, description="article title")
schema.add_field(field_name="author_info", datatype=DataType.JSON, description="author information")
schema.add_field(field_name="publish_ts", datatype=DataType.INT32, description="publish timestamp")
schema.add_field(field_name="image_url", datatype=DataType.VARCHAR, max_length=500, description="image URL")
schema.add_field(field_name="image_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="image vector")
schema.add_field(field_name="summary", datatype=DataType.VARCHAR, max_length=1000, description="article summary")
schema.add_field(field_name="summary_dense_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="summary dense vector")
schema.add_field(field_name="summary_sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR, description="summary sparse vector")
Vielleicht fällt Ihnen das Argument uri
in MilvusClient
auf, das für die Verbindung mit dem Milvus-Server verwendet wird. Sie können die Argumente wie folgt setzen.
Wenn Sie nur eine lokale Vektordatenbank für kleine Datenmengen oder Prototypen benötigen, ist die Angabe der Uri als lokale Datei, z. B.
./milvus.db
, die bequemste Methode, da Milvus Lite automatisch alle Daten in dieser Datei speichert.Wenn Sie große Datenmengen haben, z. B. mehr als eine Million Vektoren, können Sie einen leistungsfähigeren Milvus-Server auf Docker oder Kubernetes einrichten. Bei dieser Einrichtung verwenden Sie bitte die Serveradresse und den Port als Uri, z. B.
http://localhost:19530
. Wenn Sie die Authentifizierungsfunktion auf Milvus aktivieren, verwenden Sie "<Ihr_Benutzername>:<Ihr_Passwort>" als Token, andernfalls setzen Sie das Token nicht.Wenn Sie Zilliz Cloud, den vollständig verwalteten Cloud-Dienst für Milvus, verwenden, passen Sie die
uri
undtoken
an, die dem öffentlichen Endpunkt und dem API-Schlüssel in Zilliz Cloud entsprechen.
Was auto_id
in MilvusClient.create_schema
betrifft, so ist AutoID ein Attribut des Primärfeldes, das bestimmt, ob die automatische Erhöhung für das Primärfeld aktiviert werden soll. Da wir das Feldarticle_id
als Primärschlüssel festlegen und die Artikel-ID manuell hinzufügen wollen, setzen wir auto_id
auf False, um diese Funktion zu deaktivieren.
Nachdem wir alle Felder zum Schemaobjekt hinzugefügt haben, stimmt unser Schemaobjekt mit den Einträgen in der obigen Tabelle überein.
Definieren des Index
Nach der Definition des Schemas mit verschiedenen Feldern, einschließlich Metadaten und Vektorfeldern für Bild- und Zusammenfassungsdaten, besteht der nächste Schritt in der Vorbereitung der Indexparameter. Die Indexierung ist entscheidend für die Optimierung der Suche und des Abrufs von Vektoren und gewährleistet eine effiziente Abfrageleistung. Im folgenden Abschnitt werden wir die Indexparameter für die angegebenen Vektor- und Skalarfelder in der Sammlung definieren.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="image_vector",
index_type="AUTOINDEX",
metric_type="IP",
)
index_params.add_index(
field_name="summary_dense_vector",
index_type="AUTOINDEX",
metric_type="IP",
)
index_params.add_index(
field_name="summary_sparse_vector",
index_type="SPARSE_INVERTED_INDEX",
metric_type="IP",
)
index_params.add_index(
field_name="publish_ts",
index_type="INVERTED",
)
Sobald die Indexparameter eingerichtet und angewendet sind, ist Milvus für die Bearbeitung komplexer Abfragen auf Vektor- und Skalardaten optimiert. Diese Indexierung verbessert die Leistung und Genauigkeit von Ähnlichkeitssuchen innerhalb der Sammlung und ermöglicht ein effizientes Abrufen von Artikeln auf der Grundlage von Bildvektoren und zusammenfassenden Vektoren. Durch die Nutzung des AUTOINDEX
für dichte Vektoren, der SPARSE_INVERTED_INDEX
für spärliche Vektoren und die INVERTED_INDEX
für Skalare kann Milvus schnell die relevantesten Ergebnisse identifizieren und zurückgeben, was die allgemeine Benutzererfahrung und die Effektivität des Datenabrufs erheblich verbessert.
Es gibt viele Arten von Indizes und Metriken. Weitere Informationen dazu finden Sie unter Milvus Index-Typ und Milvus Metrik-Typ.
Sammlung erstellen
Wenn das Schema und die Indizes definiert sind, erstellen wir eine "Sammlung" mit diesen Parametern. Eine Sammlung ist für Milvus wie eine Tabelle in einer relationalen DB.
client.create_collection(
collection_name=collection_name,
schema=schema,
index_params=index_params,
)
Wir können überprüfen, ob die Sammlung erfolgreich erstellt wurde, indem wir die Sammlung beschreiben.
collection_desc = client.describe_collection(
collection_name=collection_name
)
print(collection_desc)
Andere Überlegungen
Index laden
Wenn Sie eine Sammlung in Milvus erstellen, können Sie wählen, ob Sie den Index sofort laden wollen oder ob Sie dies bis nach der Massenaufnahme einiger Daten aufschieben wollen. Normalerweise müssen Sie sich nicht explizit dafür entscheiden, da die obigen Beispiele zeigen, dass der Index automatisch für alle eingelesenen Daten direkt nach der Erstellung der Sammlung erstellt wird. Dies ermöglicht eine sofortige Durchsuchbarkeit der aufgenommenen Daten. Wenn Sie jedoch nach der Sammlungserstellung eine große Menge an Daten einfügen und erst zu einem bestimmten Zeitpunkt nach diesen Daten suchen müssen, können Sie die Indexerstellung aufschieben, indem Sie index_params bei der Sammlungserstellung weglassen und den Index durch expliziten Aufruf von load erstellen, nachdem Sie alle Daten eingelesen haben. Diese Methode ist effizienter für den Aufbau des Index für eine große Sammlung, aber es können keine Suchvorgänge durchgeführt werden, bis load() aufgerufen wird.
Wie definiert man ein Datenmodell für mehrere Mandanten?
Das Konzept mehrerer Mandanten wird häufig in Szenarien verwendet, in denen eine einzige Softwareanwendung oder ein einziger Dienst mehrere unabhängige Benutzer oder Organisationen bedienen muss, die jeweils über eine eigene isolierte Umgebung verfügen. Dies ist häufig bei Cloud Computing, SaaS-Anwendungen (Software as a Service) und Datenbanksystemen der Fall. Ein Cloud-Speicherdienst kann beispielsweise die Mandantenfähigkeit nutzen, um verschiedenen Unternehmen die Möglichkeit zu geben, ihre Daten getrennt zu speichern und zu verwalten, während sie dieselbe zugrunde liegende Infrastruktur nutzen. Dieser Ansatz maximiert die Ressourcennutzung und Effizienz und gewährleistet gleichzeitig die Datensicherheit und den Datenschutz für jeden Mieter.
Die einfachste Möglichkeit zur Unterscheidung von Mandanten besteht darin, ihre Daten und Ressourcen voneinander zu isolieren. Jeder Tenant hat entweder exklusiven Zugriff auf bestimmte Ressourcen oder teilt Ressourcen mit anderen, um Milvus-Entitäten wie Datenbanken, Sammlungen und Partitionen zu verwalten. Es gibt spezifische Methoden, die auf diese Entitäten abgestimmt sind, um Milvus Multi-Tenancy zu implementieren. Weitere Informationen finden Sie auf der Milvus-Multi-Tenancy-Seite.