Benutzer-Werkzeuge

Webseiten-Werkzeuge


ki:oracle_vector_search_23ai

Oracle Datenbank 23ai - Vector Search - Einen Vector speichern, vergleichen und indizieren

Mit Oracle 23ai stellt Oracle den Datentyp Vector zur Verfügung.

Der Datentyp speichert Vectoren.

Beim Anlegen kann die Anzahl (Dimenstion) der Vektorwolke, der Datentyp der einzelnen Vektoren und ob es Lücken gibt (DENSE - keine Lücken, SPARSE mit nicht besetzten Vektoren) angeben werde.

VECTOR(number_of_dimensions, dimension_element_format, DENSE)

Als Vector Darstellungen können folgende Datentypen gewählt werden:

  • int8 for 8-bit signed integers
  • binary for 8-bit unsigned integers
  • float32 for 32-bit floating-point numbers
  • float64 for 64-bit floating point numbers

Vectors (DENSE oder SPARSE ) werden intern als Securefile BLOBs gespeichert und die standard embedding model vectoren sind zwischen 1.5KB and 12KB groß.

Um den notwendige Speicherplatz abzuschätzen können folgende Formeln hilfreich sein:

  • DENSE vectors:
    • number of vectors * number of dimensions * size of your vector dimension type
    • (for example, a FLOAT32 is equivalent to BINARY_FLOAT and is 4 bytes in size)
  • SPARSE vectors:
    • number of vectors * ( ( average number of sparse dimensions * 4 bytes) + ( number of sparse dimensions * size of your vector dimension type) )

Mehr dazu unter ⇒ https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/create-tables-using-vector-data-type.html

Wie aber damit umgehen und für was können wir das einsetzen?


Die wichtigsten Elemente der Oracle AI Vector Search Architektur

Die vier wichtigen Elemente:

  • Der Datentyp Vector
    • Speichert das eine Vector als Blob Objekt in einen Secure file
    • Leider keine Introspektion Eigenschaft, kein Type wie in anderen DB-Systemen (d.h. Datentyp und Länge lasen sich nicht durch .Methoden auslesen etc.)
  • Methoden, um einen Such Vector mit einem Daten Vector zu vergleichen
    • Mathematische Methoden deren Grundlagen uns im Studium schon verzweifeln ließen d.h. echte nachvollziehbare Mathematik
  • Ein Index nicht relationaler Art auf den Vector Daten
    • HNWV - Hierarchical Navigable Small World () vector Index
    • IVF - (Inverted File) Flat Index - Vektorraum in Cluster Index
  • Einer Technik, um aus Input Daten eine Vector zu erzeugen
    • Eine Methode die Daten in eine Vector quantisiert
      • Also auch eigene Methode sein, die Daten vergleichbar darstellen sind möglich

Theorie

Ein Satz von Vectoren beschriebt die Eigenschaften eines Daten Elementes, Datensatzes, Words oder eine Satzes oder eine Bildes in einer Art Punkt Wolke in n Dimensionen im Raum.

Ich stelle mir so einen Vektor Satz eher wie eine 3-Dimensionale Punktwolke vor, die Oberfläche der Figur sind die Punkte , die Gerade zum Mittelpunkt der Punktwolke ist der jeweilige Vektor.

Also so eine Art Kartoffel mit den verschiedensten Formen.

In den meisten Beispiel reden wir noch 2-3 Dimensionen, In der Praxis haben Vektoren oft mehrere hundert oder sogar tausende Dimensionen (z. B. 2024 Dimensionen). Jede Dimension repräsentiert ein spezifisches Merkmal des Datenelements, das durch KI-Modelle (z. B. Sentence Transformer oder CNNs) gelernt wurde.

Die eigentliche AI findet beim Erstellen des Vektors statt, beim späteren Suchen kann dann mit diversen Vergleichs-Algorithmen in diesen „Wolken“ gesucht werden.

Dazu wird ein Modell für eine Künstliche Intelligenz (KI) (z. B. ein Transformer-Modell wie BERT oder ein Convolutional Neural Network (CNN) für Bilder) zuvor trainiert, um die semantischen oder visuellen Merkmale von Daten (z. B. Texten, Bildern) zu erfassen.

Die Aufgabe teilt sich also in zwei hauptsächliche Aufgaben, ein Daten Element in einen passenden Vektor wandeln und speichern; und dann über eine Suche in dem Vektor Raum die wahrscheinlichsten Ähnlichkeiten zu identifizieren.

Da heißt das Feature Vector Search umfasst die folgenden wichtigen Architektur Elemente, die zu verstehen sind:

Vector erzeugen
  • Ein Daten Element in einen Vector wandeln
    • Statisch über eine eigene Logik z.b. in PL/SQL (siehe Beispiel im Text weiter unten)
    • Bei Texten ein Sentence Transformer Model - ein neuronale Netzwerk, das Texte (z. B. Sätze oder Absätze) in dichte Vektoren (Embeddings) umwandeln, um die semantischen Bedeutungen zu erfassen
    • Bei Visuelen Daten, kann ein Residual Network (ResNet) Model verwendet werden
    • Bei Audio data kann eine Darstellung der „spectrogram representation of the audio data“ verwendet werden um Visuelle Modell zu verwenden
Vector speichern
  • Die Speicherung dieser Vektoren in der relationalen Welt einer Oracle Datenbank
    • Vector Datentyp mit dem erzeugten Vector füllen
    • Indizierung der Vektoren um diese später effizient durchsuchen zu können
      • Zum Beispiel als HNSW Index Typ - Hierarchical Navigable Small World () vector indexes 
Vector vergleichen
  • Die Vergleichsalgorithmen um eine gewichtet Ähnlichkeit zwischen den Vektoren zu erkennen
  • Zur Bestimmung des Ähnlichkeitsmaßes können verschiedene Vergleichs Vergleichsalgorithmen in der Oracle DB verwendet werden :
    • L1_DISTANCE - MANHATTAN - Misst die Summe der absoluten Differenzen zwischen den Vektorkomponenten
    • L2_DISTANCE - EUCLIDEAN - Misst den direkten Abstand zwischen zwei Vektoren im Raum
    • COSINE_DISTANCE - COSINE - Misst die Ähnlichkeit der Richtungen der Vektoren, nicht deren absoluten Wert
      • Ideal für Text-Embeddings und semantische Suche
    • INNER_PRODUCT - DOT - Misst das Produkt der Vektorkomponenten
    • HAMMING_DISTANCE - HAMMING - Misst die Anzahl der unterschiedlichen Stellen zwischen zwei binären Vektoren
      • Ideal für binäre oder kategorische Daten
    • JACCARD_DISTANCE - JACCARD - Misst die Ähnlichkeit zwischen zwei Mengen (z. B. bei binären oder spärlichen Vektoren)
      • Gut für binäre oder kategorische Daten.

Details auch unter https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/vector_distance.html

Daten finden

D.h. wir suchen im Prinzip auf in den Vektor Räumen immer nach Ähnlichkeiten!

Und sortieren das Ergebnis nach dem entsprechenden Ähnlichkeitsmaßes, das für unsere jeweilige Aufgabe die beste Wahrscheinlichkeit darstellt auch wirklich etwas sinnvolles zu finden.


Eine Entwicklungsumgebung vorbereiten

Als Vorbereitung wurde eine 23ai Container Umgebung (siehe ⇒ Oracle Datenbank 23ai Free Edition über ein Container Image unter Linux 9 als Testumgebung für AI Vector Search verwenden ) bereitgestellt und über den Python Client OML4Py (siehe ⇒ Oracle Datenbank 23ai - Modelle für Vector Search über Python OML4Py bereitstellen und mit Hybrid Vector Search testen ) ein Model wie ALL_MINILM_L6_V2 geladen.


Eigene Vektoren erstellen

Da die Vektorisierung und das Vergleichen von Vektoren zwei komplett unterschiedliche Dinge sind, kann auch ein eigener Vektor Algorithmus erstellt werden.

So soll in diesem kleinen Beispiel nach Auto Farben gesucht werden, Kunden sucht über einen Hex Color Picker eine Farbe, wie f20707 und in der Suche werden alle Fahrzeuge mit einer ähnlichen Farbe angezeigt in dem wir einen Farb Vector definieren aus [ Farbanteil R [0 .. bis 256] , Farbanteil G [0 .. bis 256], Farbanteil R [B .. bis 256], Hersteller [0 - x ], Fahrzeug Typ [ 1 … 10 ] ], Hersteller und Fahrzeug Typ werden nach Ähnlichkeit in der Wertemege dafür sortiert, wie BMW 1, VW 2, Merzedes 3, Citrön 5 , Ferrari 80, Bently 999 usw, Typen wie Stufenheck =1, Kombi 2, Transporter 10, Pickup 20, LKW 99, Kettenbagger 999, um hier wieder Ähnlichkeiten zu identifizieren.

Auch können wir nun bei diese einfachen Vektor Mengen das auch mal mit dem Taschenrechner nachrechnen, um die Vektor Distanzen besser zu verstehen.

Ein Beispiel - Suche nach Automobilen:

DROP TABLE car_search;
 
CREATE TABLE car_search ( id NUMBER(9), color varchar2(6), manufacturerid NUMBER(4), car_type  NUMBER(2), search_vector VECTOR(5, FLOAT32  , DENSE) );
 
-- ein schwarzes Auto
-- 1 BMW 
-- 1 Stufenheck
INSERT INTO car_search VALUES ( 1, '000000',1,1,'[0,0,0,1,1]');
 
-- ein weißes Auto
-- 2 VW 
-- 2 Kombi
INSERT INTO car_search VALUES ( 2, 'ffffff',2,2,'[256,256,256,2,2]');
 
-----
commit;
 
-----

Abfragen:

COLUMN v_L1_DISTANCE       format 999G990D0999 heading "Vector Distance|L1 or MANHATTAN"
COLUMN v_L2_DISTANCE       format 999G990D0999 heading "Vector Distance|L2 or EUCLIDEAN"
COLUMN v_COSINE_DISTANCE   format 999G990D0999 heading "Vector Distance|COSINE"
COLUMN v_INNER_PRODUCT     format 999G990D0999 heading "Vector Distance|INNER_PRODUCT"
COLUMN v_HAMMING_DISTANCE  format 999G990D0999 heading "Vector Distance|HAMMING"
COLUMN v_JACCARD_DISTANCE  format 999G990D0999 heading "Vector Distance|JACCARD"
 
VARIABLE query_vector CLOB
 
-- ein schwarzes Auto
-- 1 BMW 
-- 1 Stufenheck
 
BEGIN
    :query_vector := '[0,0,0,1,1]';  
END;
/
 
 
SELECT ID
    , manufacturerid
    , car_type
    , vector_distance(search_vector, :query_vector, MANHATTAN)       AS  v_L1_DISTANCE
    , vector_distance(search_vector, :query_vector, EUCLIDEAN )      AS  v_L2_DISTANCE
    , vector_distance(search_vector, :query_vector, COSINE )         AS  v_COSINE_DISTANCE
    , vector_distance(search_vector, :query_vector, DOT)             AS  v_INNER_PRODUCT
    , vector_distance(search_vector, :query_vector, HAMMING )        AS  v_HAMMING_DISTANCE
    --, vector_distance(text_vector, :query_vector, JACCARD) as  v_JACCARD_DISTANCE
  FROM car_search
                                           Vector Distance Vector Distance Vector Distance Vector Distance Vector Distance
                     ID        MANUFACTURERID   CAR_TYPE L1 OR MANHATTAN L2 OR EUCLIDEAN COSINE          INNER_PRODUCT   HAMMING        
----------     -------------- ---------- --------------- --------------- --------------- --------------- ---------------
 => Suchvektor        1              1          1          0.0000          0.0000          0.0000         -2.0000          0.0000
                      2              2          2        770.0000        443.4073          0.9936         -4.0000          5.0000

Der Sichvektor des Kunden könnten also sein, Farbe f20707, Citrön, Kombi in der Auswahl >= das ergibt dann folgenden Suchvektor ⇒ [ 242, 7, 7 , 5, 2]

BEGIN
    :query_vector := '[ 242, 7, 7 , 5, 2]';  
END;
/
-- liefert mit obiger Abfrage die folgenden Ergebnisse: 
 
                                     Vector Distance Vector Distance Vector Distance Vector Distance Vector Distance
        ID MANUFACTURERID   CAR_TYPE L1 OR MANHATTAN L2 OR EUCLIDEAN COSINE          INNER_PRODUCT   HAMMING        
---------- -------------- ---------- --------------- --------------- --------------- --------------- ---------------
         1              1          1        261.0000        242.2375          0.9796         -7.0000          5.0000
         2              2          2        515.0000        352.4301          0.3898  -65,550.0000            4.0000

Was muss jetzt dem Kunden angezeigt werden? Was ist am ähnlichsten?

Die Treffermenge wird dann nach dem entsprechenden Distance Algorithmus sortiert, damit das wahrscheinlichste Ergebnis in der Treffermenge ganz oben steht.

Im nächsten Testschritt lässt sich die Tabellen nun einfach über DBMS_RANDOM mit größeren Datenmengen füllen und auch ein passender Index auf der Vector Spalte anlegen.

CREATE SEQUENCE CAR_SEARCH_SEQ; 
 
CREATE OR REPLACE PROCEDURE fillColorTab (p_num_records NUMBER)
 
IS
 v_color varchar2(256);
 v_r     pls_integer;
 v_g     pls_integer;
 v_b     pls_integer;
 v_vector varchar2(256);
 
 v_cnt pls_integer;
 
BEGIN
 
  v_cnt:=0;
 
  while v_cnt < p_num_records
  loop
 
        v_r:=FLOOR(DBMS_RANDOM.VALUE(0, 256));
        v_g:=FLOOR(DBMS_RANDOM.VALUE(0, 256));
        v_b:=FLOOR(DBMS_RANDOM.VALUE(0, 256));
 
        v_color:= lpad(TRIM((LOWER(to_char(v_r, 'XX'))) ),2,'0')|| lpad(TRIM(LOWER(to_char(v_g, 'XX'))),2,'0')||lpad(TRIM(LOWER(to_char(v_b, 'XX'))),2,'0');
        dbms_output.put_line(v_color);
 
        v_vector:='['||v_r||','||v_g||','||v_b||','||1||','||1||']';
 
 
         INSERT INTO CAR_SEARCH(ID, COLOR, MANUFACTURERID, CAR_TYPE, SEARCH_VECTOR)
         VALUES (
                   CAR_SEARCH_SEQ.nextval -- ID
                 , v_color                -- COLOR
                 , 1                      -- MANUFACTURERID
                 , 1                      -- CAR_TYPE
                 , TO_VECTOR ( v_vector )              --SEARCH_VECTOR
         );
 
      IF MOD(v_cnt,1000) = 0  THEN
        commit;
      END IF;  
 
      v_cnt:=v_cnt+1;
 
    END loop;
 
    commit;
 
END fillColorTab;
/
 
SET serveroutput ON
TRUNCATE TABLE CAR_SEARCH;
EXEC fillColorTab(100);

Auf dieser Datenmenge können wir nun testen mit welchen Ähnlichkeitsmaß wir die sinnvollste Sortierung erreichen.

Optimiert lässt sich das ganze durch die Umstellung von auf HSV/HSB (Farbton, Sättigung, Wert/Helligkeit) statt RGB-Modell mit den drei unabhängige Kanäle (Rot, Grün, Blau), diese boten sich für diese Demo an.

Mehr dazu unter Oracle Datenbank 23ai - Mit Vectoren in der Oracle Datenbank Daten vergleichen - Demo Applikation für die Suche nach Farben


Die wichtigsten Ähnlichkeitsmaße

COSINE_DISTANCE

Viel wird mit dem COSINE_DISTANCE - (Name in der Syntax von Oracle COSINE) gearbeitet

Misst die Ähnlichkeit der Richtungen der Vektoren, nicht deren absoluten Wert.

D.h. folgende Vektoren haben die gleiche Cosinus Distanz:

 '[ 0, 0, 0 ]'
 '[ 125, 125, 125 ]'
 '[ 256, 256, 256 ]'
 
=> 0

In unseren oberen Beispiel dazu wird dann schnell klar, das dieser Ähnlichkeitsmaße für folgende Aufgabe dann ungeeignet ist:

In einer Suche sollen ähnliche Farben über Ihren RGB Wert identifiziert werden:

  • der Vektor besteht aus [ Farbanteil R [0 .. bis 256] , Farbanteil G [0 .. bis 256], Farbanteil R [B .. bis 256]

⇒ Schwarz / Grau / Weiß ist dann vom Ähnlichkeitsmaße die gleiche Farbe

Bei Semantischen Analyse sieht das dann gleich ganz anders aus da hier die Richtung des Vektors in der Punktwolken Menge eines Satzes den entscheidenden Hinweis geben kann.

L2_DISTANCE - EUCLIDEAN

L2_DISTANCE - (Name in der Syntax von Oracle vector_distance EUCLIDEAN, als einzelne Methode L2_DISTANCE ) - Misst den direkten Abstand zwischen zwei Vektoren im Raum.

select   vector_distance('[ 0, 0, 0 ]','[ 125, 125, 125 ]', EUCLIDEAN) from dual;
216.50635094610965
 
 
select   vector_distance('[ 125, 125, 125 ]','[ 256, 256, 256 ]', EUCLIDEAN) from dual;
226.8986557915229
 
-- die Short Methode heißt dann wieder L2_DISTANCE!
 
select L2_DISTANCE('[ 0, 0, 0 ]','[ 256, 256, 256 ]')  from dual;
443.40500673763256

Rechnen wir das mal nach:

(a,b) =sqrt{     
    sum{i=1}{n}{ (A_i - B_i)2 }

   }

A=[125, 125, 125],B=[256, 256, 256] ⇒ EUCLIDEAN(A,B)≈226.90

A_1-B_1 = 125-256 = -131(-131)2 = 17161

A_2-B_2 = 125-256 = -131(-131)2 = 17161

A_3-B_3 = 125-256 = -131(-131)2 = 17161

17161+17161+17161=51483

sqrt{51483} = 226.8986557915

⇒ Schwarz / Grau / Weiß ist dann vom Ähnlichkeitsmaße dann mit einem gewissen Abstand, werde das bald möglichst als APEX App bauen um das „graphisch“ anzuzeigen ob das so am Ende gehen könnte, hier geht es ja erstmal um das Prinzip.

Euclidean Norm eines Vector

Die euklidische Norm (auch bekannt als L²-Norm oder Euclidean Norm) eines Vektors ist ein Maß für die „Länge“ oder den „Abstand“ des Vektors im euklidischen Raum. (also von 0 aus)

Sie wird berechnet als die Quadratwurzel der Summe der quadrierten Komponenten des Vektors.

SELECT VECTOR_NORM(VECTOR('[2, 2]')) AS euclidean_norm ;
2.8284271247461903
 
SELECT  vector_distance(VECTOR('[2, 2]'), VECTOR('[0, 0]'), EUCLIDEAN );
2.8284271247461903

VECTOR_NORM(VECTOR('[2, 2]')) entspricht als der Distance vom 0 Punkt = vector_distance(VECTOR('[2, 2]'), VECTOR('[0, 0]'), EUCLIDEAN )

SELECT  vector_distance(VECTOR('[2, 2]'), VECTOR('[2, 2]'), EUCLIDEAN );
0.0
-- Abstand der beiden Vectoren im Raum
SELECT  vector_distance(VECTOR('[1, 1]'), VECTOR('[2, 2]'), EUCLIDEAN ) ;
1.4142135623730951

https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/vector_norm.html

Das kann zum Beispiel dazu benutzt werden um in den Daten die Vectoren mit starken Abweichungen zu identifizieren.


Ein Daten Element in einen Vector wandeln

Im nächsten Schritt wird ein Daten Element über ein KI Modell in einen Vector umgewandelt, dazu steht die Funktion „vector_embedding“ zu Verfügung.

SELECT vector_embedding(ALL_MINILM_L6_V2  USING 'Das ist ein Text' AS DATA) FROM dual; 
[-4.86620981E-003,1.44163042E-001,1.31779145E-002
,6.53347224E-002,1.372308E-002,8.78371745E-002,1.14743046E-001,7.9288438E-002,7.86188394E-002,-6.70713037E-002,-2.15163687E-003,-2.01251972E-002,3.27725452E-003,-3.89427617E-002,-3.76474112E-002 ......
,-1.39121301E-002,2.33094934E-002,8.33002329E-002,-7.44610419E-003,-7.68417343E-002,-7.87851494E-003,5.6516204E-002,2.01204326E-002,9.20519829E-002,-3.29588093E-002
]

Besser Lesbar mit „JSON_ARRAY returning clob „ausgeben:

SELECT 
  JSON_ARRAY( vector_embedding(ALL_MINILM_L6_V2  USING 'Das ist ein Text' AS DATA) returning CLOB) AS DatenFormat
FROM dual;
 
[[-0.00486621,0.14416304,0.013177915,0.06533472,0.01372308
,0.087837175,0.11474305,0.07928844,0.07861884,-0.0670713,-0.0021516369,-0.020125197,0.0032772545,-0.03894276
,-0.03764741,0.007716139,-0.011220228,0.026907498,0.023640944
,-0.030299427,-0.020495197920648,0.002982179,-0.018670402,-0.00887637,....]]

Das Ergebnis kann wiederum als Datentyp Vector gespeichert werden.

VECTOR_DIMENSION_COUNT

Wie groß ist der Vector geworden?

 
SELECT 
  VECTOR_DIMENSION_COUNT( vector_embedding(ALL_MINILM_L6_V2  USING 'Das ist ein Text' AS DATA) ) AS Anzahl_Dimensionen 
 
FROM dual;
 
ANZAHL_DIMENSIONEN
------------------
               384

VECTOR_DIMENSION_FORMAT

Welches Datenformat hat der Vector

 
SELECT 
  VECTOR_DIMENSION_FORMAT( vector_embedding(ALL_MINILM_L6_V2  USING 'Das ist ein Text' AS DATA) ) AS DatenFormat
FROM dual;
 
DATENFORMAT                                       
---------------
FLOAT32

KI generierten Vector in der Datenbank speichern

Beispiel Tabelle mit einem Vector auf Basis eine Analyse mit einem KI Modell auf die Daten anlegen:

DROP TABLE vtexte;
 
CREATE TABLE vtexte (id NUMBER(8) , text varchar2(4000) , text_vector VECTOR );+
 
INSERT INTO vtexte VALUES (1,'Ein Hund steht neben der Hütte' , vector_embedding(ALL_MINILM_L6_V2  USING 'Ein Hund steht neben der Hütte' AS DATA) );
INSERT INTO vtexte VALUES (2,'Die liebsten Haustiere sind Katze und Hund',vector_embedding(ALL_MINILM_L6_V2  USING 'Die liebsten Haustiere sind Katze und Hund' AS DATA));
INSERT INTO vtexte VALUES (3,'Die Katze auf dem Dach',vector_embedding(ALL_MINILM_L6_V2  USING 'Die Katze auf dem Dach' AS DATA));
 
commit;

Ließe sich mit einem Trigger oder ähnlichen auch lösen .-).


Ähnlichkeit auf den Vector Daten abfragen

Mit den verschienden Vektor Distance Verfahren können wir nun die Ähnlichkeit des gesuchten Begriffes mit dem vorhanden Daten ermitteln.

Dazu verwenden wir die Funktion vector_distance (siehe ⇒ https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/vector_distance.html ) um den Abstand nach den jeweilen Algorithmus zu ermitteln.

Abfrage:

COLUMN v_L1_DISTANCE       format 999G990D0999 heading "Vector Distance|L1 or MANHATTAN"
COLUMN v_L2_DISTANCE       format 999G990D0999 heading "Vector Distance|L2 or EUCLIDEAN"
COLUMN v_COSINE_DISTANCE   format 999G990D0999 heading "Vector Distance|COSINE"
COLUMN v_INNER_PRODUCT     format 999G990D0999 heading "Vector Distance|INNER_PRODUCT"
COLUMN v_HAMMING_DISTANCE  format 999G990D0999 heading "Vector Distance|HAMMING"
COLUMN v_JACCARD_DISTANCE  format 999G990D0999 heading "Vector Distance|JACCARD"
 
COLUMN ID      format 999 heading "ID"
COLUMN text    format a126 heading "Text"
 
VARIABLE query_string VARCHAR2(1000)
VARIABLE query_vector CLOB
BEGIN
    :query_string := 'Die Katze steht auf der Hütte';
    SELECT vector_embedding(ALL_MINILM_L6_V2 USING :query_string AS DATA) INTO :query_vector;
END;
/
 
WITH results AS (
SELECT ID
    , TEXT
    , vector_distance(text_vector, :query_vector, MANHATTAN)       AS  v_L1_DISTANCE
    , vector_distance(text_vector, :query_vector, EUCLIDEAN )      AS  v_L2_DISTANCE
    , vector_distance(text_vector, :query_vector, COSINE )         AS  v_COSINE_DISTANCE
    , vector_distance(text_vector, :query_vector, DOT)             AS  v_INNER_PRODUCT
    , vector_distance(text_vector, :query_vector, HAMMING )        AS  v_HAMMING_DISTANCE
    --, vector_distance(text_vector, :query_vector, JACCARD) as  v_JACCARD_DISTANCE
  FROM vtexte 
)
SELECT * 
 FROM results
ORDER BY 1;

Ergebnis:

                                                                                                                                    Vector Distance Vector Distance Vector Distance Vector Distance Vector Distance
  ID Text                                                                                                                           L1 OR MANHATTAN L2 OR EUCLIDEAN COSINE          INNER_PRODUCT   HAMMING        
---- ------------------------------------------------------------------------------------------------------------------------------ --------------- --------------- --------------- --------------- ---------------
   1 Ein Hund steht neben der Hütte                                                                                                         10.8571          0.7016          0.2461         -0.7539        381.0000
   2 Die liebsten Haustiere sind Katze und Hund                                                                                             11.8764          0.7725          0.2984         -0.7016        381.0000
   3 Die Katze auf dem Dach                                                                                                                 10.4859          0.6971          0.2430         -0.7570        381.0000

Auch hier gilt wie bei unsern ersten Beispiel, die Kunst im Einsatz der Vector Search ist es auf den Daten den passenden Ähnlichkeitsmaßstab zu finden um die Daten am sinnvollsten zu sortieren!


Index auf den Vector Daten anlegen

Bisher müssen wir mit einem Full Table Scan alle Vectoren mit dem Suchvektor verleichen um eine Sortierung nach Ähnlichkeit zu erreichen.

D.h. von der Performance haben wir ein O(n) Probem, der Rechenaufwand steigt linear mit der Datenmenge und wird schnell sehr unpraktisch.

Um das zu vermeiden verwenden wir Indexe auch auf den Vector Datentyp.

Zwei Typen werden unterstützt:

  • IVF (Inverted File) Flat index - partitioned-based index - classifed as Neighbor Partition Vector Index
  • HNSW (Hierarchical Navigable Small Worlds) index -graph-based index - In-Memory Neighbor Graph Vector Index

siehe auch ⇒ https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/create-vector-index.html

IVF (Inverted File) Flat index

IVF (Inverted File) Flat index - partitioned-based index - classifed as Neighbor Partition Vector Index

Ein IVF (Inverted File) Flat Index ist ein Indexierungsverfahren, bei dem der Vektorraum in Cluster (Gruppen ähnlicher Vektoren) unterteilt wird, um die Suche nach den nächsten Nachbarn zu beschleunigen, indem nur die relevanten Cluster durchsucht werden, anstatt den gesamten Vektorraum.

HNSW (Hierarchical Navigable Small Worlds) index

HNSW (Hierarchical Navigable Small Worlds) index -graph-based index - In-Memory Neighbor Graph Vector Index

Effizienter Algorithmus für die annähernde k-Nächste-Nachbarn-Suche (k-NN) in hochdimensionalen Vektorräumen, der durch die Kombination von Hierarchien und Graphen eine schnelle und skalierbare Suche ermöglicht

Die annähernde k-Nächste-Nachbarn-Suche (k-NN) ist ein Algorithmus, der die k ähnlichsten Datenpunkte zu einem gegebenen Query-Punkt in einem Datensatz findet, wobei zugunsten von Geschwindigkeit und Skalierbarkeit eine geringe Genauigkeit in Kauf genommen wird.

Gute Erklärung findet sich auch hier ⇒ Vector Database Search - Hierarchical Navigable Small Worlds (HNSW) Explained ⇒ https://www.youtube.com/watch?v=77QH0Y2PYKg

Index anlegen
DROP INDEX idx_vtexte_hnsw_idx ;
 
CREATE VECTOR INDEX idx_vtexte_hnsw_idx 
    ON vtexte (text_vector)
ORGANIZATION INMEMORY NEIGHBOR GRAPH
DISTANCE COSINE
WITH TARGET ACCURACY 90 PARAMETERS (TYPE HNSW, neighbors 40, efconstruction 500);

Index verwenden

Index wird verwendet wenn:

  • Keyword APPROX notwendig
  • Gleicher Algorithmus für den Index und für die vector_distance Methode

Abfrage:

EXPLAIN PLAN FOR
 
SELECT ID
      , TEXT
      , vector_distance(text_vector, :query_vector, COSINE )         AS  v_COSINE_DISTANCE
 FROM vtexte 
ORDER BY vector_distance(text_vector, :query_vector, COSINE )       
FETCH APPROX FIRST 3 ROWS ONLY;
 
 
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
 
PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
-----------------------------------------------------------
Plan hash VALUE: 3113142583
 
------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name                | ROWS  | Bytes | Cost (%CPU)| TIME     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                     |     3 |  6063 |     2  (50)| 00:00:01 |
|*  1 |  COUNT STOPKEY                 |                     |       |       |            |          |
|   2 |   VIEW                         |                     |     3 |  6063 |     2  (50)| 00:00:01 |
|*  3 |    SORT ORDER BY STOPKEY       |                     |     3 | 18351 |     2  (50)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| VTEXTE              |     3 | 18351 |     1   (0)| 00:00:01 |
|   5 |      VECTOR INDEX HNSW SCAN    | IDX_VTEXTE_HNSW_IDX |     3 | 18351 |     1   (0)| 00:00:01 |
 
 

;


Vector Index Qualität

Findet der Index Lauf die gleichen Daten wie ein full Table Scan?

SELECT INDEX_NAME,INDEX_SUBTYPE,TABLE_NAME 
 FROM USER_INDEXES 
WHERE TABLE_NAME LIKE '%TEXTE%' AND index_type = 'VECTOR';
 
INDEX_NAME                       INDEX_SUBTYPE                 TABLE_NAME                                                                                                                      
--------------------------------------------------------------------------------
IDX_VTEXTE_HNSW_IDX	       INMEMORY_NEIGHBOR_GRAPH_HNSW	VTEXTE

Qualität bewerten:

SET serveroutput ON
 
VARIABLE query_string VARCHAR2(1000)
VARIABLE query_vector CLOB
BEGIN
    :query_string := 'Die Katze steht auf der Hütte';
    SELECT vector_embedding(ALL_MINILM_L6_V2 USING :query_string AS DATA) INTO :query_vector;
END;
/
 
 
DECLARE
    v_qvector VECTOR; 
    v_report varchar2(128);
BEGIN 
    v_qvector := to_vector(:query_vector);
    v_report := dbms_vector.index_accuracy_query(
          OWNER_NAME => 'GPI' 
        , INDEX_NAME => 'IDX_VTEXTE_HNSW_IDX'
        , qv => v_qvector
        , top_K =>10 
        , target_accuracy =>90 );
    dbms_output.put_line(v_report); 
END; 
/
 
 
Accuracy achieved (100%) IS 10% higher than the Target Accuracy requested (90%)

Weitere Anwendungsfälle

Probleme

Problem: ORA-51961: The vector memory area is out of space

Ein Vector Index verwendet einen eigenen Pool Bereich in der Datenbank den „Vector Pool“, siehe https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/size-vector-pool.html

Der benötigt für komplexere Indexe dann auch eine gewisse Größe nach dem Motto „Nicht zu groß und nicht zu klein, gerade richtig muss es sein“ …. .

Was ist eingestellt:

sqlplus / AS sysdba
 
SHOW parameter vector_memory_size 
NAME               TYPE        VALUE 
------------------ ----------- ----- 
vector_memory_size big INTEGER 0     
 
-- mit sqlplus im Container in der CDB anmelden und Parameter ändern 
 
podman EXEC -it freeDB bash
bash-4.4$ su - oracle
export ORACLE_HOME=/opt/oracle/product/23ai/dbhomeFree
export ORACLE_SID=FREE
export PATH=$ORACLE_HOME/bin:$PATH
 
sqlplus / AS sysdba
 
ALTER system SET vector_memory_size=512M scope=spfile;
 
shutdown IMMEDIATE
 
startup

Nun läßt sich der Index auch anlegen.


Quellen

Web:

Diese Website verwendet Cookies. Durch die Nutzung der Website stimmen Sie dem Speichern von Cookies auf Ihrem Computer zu. Außerdem bestätigen Sie, dass Sie unsere Datenschutzbestimmungen gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information
"Autor: Gunther Pipperr"
ki/oracle_vector_search_23ai.txt · Zuletzt geändert: 2025/04/11 09:35 von gpipperr