Elasticsearch bietet umfassende Unterstützung für das Speichern und Abrufen von Vektoren und ist daher eine ideale Datenbank für die Arbeit mit Einbettungen.
Feldtypen
Im Kapitel „Volltextsuche“ dieses Tutorials haben Sie gelernt, wie man einen Index mit mehreren Feldern erstellt. Damals wurde erwähnt, dass Elasticsearch größtenteils automatisch den besten Datentyp für jedes Feld anhand der Daten selbst bestimmen kann. Obwohl Elasticsearch 8.11 einige Vektortypen automatisch zuordnen kann, werden Sie in diesem Kapitel diesen Typ explizit definieren, um mehr über Typzuordnungen in Elasticsearch zu erfahren.
Abrufen von Typzuordnungen
Die jedem Feld in einem Index zugeordneten Datentypen werden in einem Prozess namens Mapping bestimmt, der dynamisch oder explizit erfolgen kann. Die im Abschnitt „Volltextsuche“ dieses Tutorials erstellten Zuordnungen wurden alle dynamisch von Elasticsearch generiert .
Der Elasticsearch-Client bietet eine get_mapping -Methode an, die die für einen gegebenen Index geltenden Typzuordnungen zurückgibt. Wenn Sie diese Zuordnungen selbst erkunden möchten, starten Sie eine Python-Shell und geben Sie folgenden Code ein:
Die Antwort der Methode get_mapping() ist ein Wörterbuch mit Informationen zu jedem Feld im Index. Zur Vereinfachung finden Sie unten eine übersichtliche Struktur dieser Informationen für den my_documents -Index, der im Abschnitt „Volltextsuche“ des Tutorials erstellt wurde:
Daraus lässt sich erkennen, dass die Felder created_on und updated_at automatisch mit date typisiert wurden, während alle anderen Felder den Typ text erhielten. Bei der Bestimmung eines Datentyps prüft Elasticsearch zunächst den Datentyp, um Feldern numerische, boolesche und Objekttypen zuzuordnen. Wenn es sich bei den Felddaten um eine Zeichenkette handelt, wird außerdem geprüft, ob die Daten einem Datumsmuster entsprechen. Die Erkennung von Zeichenketten anhand von Mustern kann bei Bedarf auch für Zahlen aktiviert werden.
Die Textfelder haben eine fields -Definition mit einem keyword -Eintrag. Dies wird als Unterfeld bezeichnet, ein alternativer oder sekundärer Datentyp, der bei Bedarf verwendet werden kann. In Elasticsearch erhalten dynamisch typisierte text Felder ein keyword Unterfeld. Sie haben bereits das Unterfeld category.keyword verwendet, um eine exakte Suche nach einer bestimmten Kategorie durchzuführen. Um zu vermeiden, dass das Unterfeld hinzugefügt wird, kann eine explizite Zuordnung von text oder keyword angegeben werden, und dies ist dann der Haupt- und einzige Typ.
Hinzufügen eines Vektorfelds zum Index
Fügen wir dem Index ein neues Feld hinzu, in dem für jedes Dokument eine Einbettung gespeichert wird.
Die Struktur einer expliziten Zuordnung entspricht dem Schlüssel mappings der Antwort, die von der Methode get_mapping() des Elasticsearch-Clients zurückgegeben wird. Es müssen nur die Felder angegeben werden, die explizit typisiert werden müssen, da alle Felder, die nicht in der Zuordnung enthalten sind, wie bisher dynamisch typisiert werden.
Im Folgenden sehen Sie eine neue Version der create_index() -Methode der Search -Klasse, die ein explizit typisiertes Feld mit dem Namen embedding hinzufügt. Ersetzen Sie diese Methode in search.py:
Wie Sie sehen können, hat das Feld embedding den Typ dense_vector, was der geeignete Typ zum Speichern von Einbettungen ist. Später lernen Sie einen weiteren Vektortyp kennen, den sparse_vector, der in anderen Arten von semantischen Suchanwendungen nützlich ist.
Der Typ dense_vector akzeptiert einige Parameter, die alle optional sind.
dims: die Größe der Vektoren, die gespeichert werden. Seit Version 8.11 werden die Abmessungen beim Einfügen des ersten Dokuments automatisch zugewiesen.index: muss aufTruegesetzt werden, um anzugeben, dass die Vektoren für die Suche indiziert werden sollen. Dies ist die Standardeinstellung.similarityDie Distanzfunktion, die beim Vergleich von Vektoren verwendet werden soll. Die beiden häufigsten sinddot_productundcosine. Das Skalarprodukt ist effizienter, erfordert aber, dass die Vektoren normalisiert werden. Der Standardwert istcosine.
Hinzufügen von Einbettungen zu Dokumenten
Im vorherigen Abschnitt haben Sie gelernt, wie man mithilfe des SentenceTransformers-Frameworks und des all-MiniLM-L6-v2 -Modells Einbettungen generiert. Nun ist es an der Zeit, das Modell in die Anwendung zu integrieren.
Zunächst kann das Modell im Konstruktor der Klasse Search instanziiert werden:
Wie Sie sich vielleicht aus dem Abschnitt über die Volltextsuche in diesem Tutorial erinnern, verfügt die Klasse Search über die Methoden insert_document() und insert_documents() , um einzelne bzw. mehrere Dokumente in den Index einzufügen. Diese beiden Methoden müssen nun die entsprechenden Einbettungen generieren, die zu jedem Dokument gehören.
Der nächste Codeblock zeigt neue Versionen dieser beiden Methoden sowie eine neue get_embedding() Hilfsmethode, die ein Embedding zurückgibt.
Die geänderten Methoden fügen das neue Feld embedding zum einzufügenden Dokument hinzu. Die Einbettung wird aus dem summary -Feld jedes Dokuments generiert. Im Allgemeinen werden Einbettungen aus Sätzen oder kurzen Absätzen generiert, daher ist in diesem Fall die Zusammenfassung ein ideales Anwendungsfeld. Weitere Möglichkeiten wären das Feld name gewesen, das den Titel des Dokuments enthält, oder vielleicht die ersten paar Sätze aus dem Feld body des Dokuments.
Nach diesen Änderungen kann der Index neu aufgebaut werden, sodass er für jedes Dokument eine Einbettung speichert. Um den Index neu zu erstellen, verwenden Sie diesen Befehl:
Falls Sie eine Erinnerung benötigen: Der Befehl flask reindex ist in der Funktion reindex() in app.py implementiert. Es ruft die Methode reindex() der Klasse Search auf, welche wiederum create_index() aufruft und dann alle Daten aus der Datei data.json an insert_documents() übergibt.
Vorher
Einbettungen generierenNächste
k-Nächste-Nachbarn-Suche