You are here

Spielen mit MariaDB Vector für erste KI-Tests

Künstliche Intelligenz (KI) und Vektor-Datenbanken sind heute in aller Munde. Da MariaDB demnächst auch mit Vektor-Datenbank-Funktionalität auf den Markt kommt, habe ich es als Datenbank-Berater für an der Zeit befunden mich etwas mit dem Thema zu beschäftigen, damit ich wenigstens einen Hauch Ahnung davon habe um was es geht...

Da ich nicht so der Theoretiker bin sondern eher gerne etwas praktisches mache, habe ich einen kleinen "KI" Prototypen gebaut, den jeder auf seinem Laptop (ohne GPU) sehr schnell und einfach nachbauen kann...

Ich habe mir auch erlaubt die Graphen aus dem Vortrag der MariaDB Foundation zu klauen (siehe Quellen am Ende).

Herunterladen der MariaDB Datenbank mit Vektor Funktionalität

Noch gib es keine MariaDB Pakete mit Vektor-Funktionalität, aber der Quellcode ist bereits verfügbar. Also baut man sich die Binaries halt schnell selber. Dies hat auf meiner alten Kiste eine knappe Stunde gedauert. Wenn die Binaries gebaut sind, kann man sich einen Tarball draus machen:

# tar xf mariadb-11.6.0_vector.tar.gz
# cd mariadb-11.6.0_vector/
# cmake .
# make
# make package

Anschliessend muss die MariaDB Datenbank nur noch gestartet werden.

Das Modell

Um das Konzept des Tokenisierens zu zeigen habe ich mir vorgenommen ein KI für URLs zu bauen und um das Konzept der verschiedenen Modelle und deren Verbesserungspotenzial zu zeigen habe ich eine ganz dummes Modell in PHP gebaut, welches ganz einfach und simple eine URL zerlegt.

Die Frage, die mit diesem Modell beantwortet werden können sollte lautet: "Gib mir ähnliche URLs zur folgenden URL."

Die dazu gehörende Tabelle sieht wie folgt aus:

DROP TABLE IF EXISTS `urls`;
-- TRUNCATE TABLE is NOT sufficient!!!
CREATE TABLE `urls` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
, `url` varchar(1024) DEFAULT NULL
, `title` varchar(2000) DEFAULT NULL
, `embedding` blob NOT NULL
, VECTOR KEY `embedding` (`embedding`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
;

Das Modell fromdual_llm_v1 kann man hier herunter laden.

Wie das Ganze in etwa funktioniert kann man an diesem Schaubild der MariaDB Foundation entnehmen:

Trainieren der KI

Dann wird die Datenbank trainiert: Die URL wird als gegeben angenommen und der Title kann z.B. über einen HTML Scraper ausgelesen werden. Hier 8 Trainings-Datensätze:


Anschliessend werden mittels unseres Models die Vektoren generiert:

(./fromdual_llm_v1.php https://mariadb.org/download/?t=mariadb&p=mariadb&r=11.6.0+Vector&os=source
./fromdual_llm_v1.php https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/
./fromdual_llm_v1.php https://mariadb.org/wp-content/uploads/2024/02/MariaDB-Vector.pdf
./fromdual_llm_v1.php https://mariadb.com/resources/blog/mariadb-vector-preview-is-out/
./fromdual_llm_v1.php https://mariadb.org/projects/mariadb-vector/
./fromdual_llm_v1.php https://metacpan.org/pod/Perl::Tokenizer
./fromdual_llm_v1.php https://www.qwak.com/post/utilizing-llms-with-embedding-stores
./fromdual_llm_v1.php https://github.com/qwak-ai/qwak-examples/tree/main/qa_bot_falcon_chroma) | grep '^\['
[0.2, 0.0107421875, 0, 0, 0, 0.0006103515625, 0.00054931640625, 0]
[0.2, 0.0107421875, 0, 0, 0, 0.00262451171875, 0, 0]
[0.2, 0.0107421875, 0, 0, 0, 0.0028076171875, 0, 0]
[0.2, 0.0107421875, 0, 0, 0, 0.0028076171875, 0, 0]
[0.2, 0.0107421875, 0, 0, 0, 0.00152587890625, 0, 0]
[0.2, 0.01171875, 0, 0, 0, 0.001220703125, 0, 0]
[0.2, 0.01171875, 0, 0, 0, 0.0025634765625, 0, 0]
[0.2, 0.009765625, 0, 0, 0, 0.00323486328125, 0, 0]

Mit diesen Vektoren wird jetzt die Datenbank gefüttert (trainiert):

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://mariadb.org/download/?t=mariadb&p=mariadb&r=11.6.0+Vector&os=source'
, 'Download MariaDB Server'
, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.00262451171875, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/'
, 'Creating the MariaDB Binary Tarball'
, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.0006103515625, 0.00054931640625, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://mariadb.org/wp-content/uploads/2024/02/MariaDB-Vector.pdf'
, 'MariaDB Vector'
, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.0028076171875, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://mariadb.com/resources/blog/mariadb-vector-preview-is-out/'
, 'MariaDB Vector preview is out'
, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.0028076171875, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://mariadb.org/projects/mariadb-vector/'
, 'MariaDB Vector'
, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.00152587890625, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://metacpan.org/pod/Perl::Tokenizer'
, 'Perl::Tokenizer - A tiny Perl code tokenizer'
, VEC_FromText('[0.2, 0.01171875, 0, 0, 0, 0.001220703125, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://www.qwak.com/post/utilizing-llms-with-embedding-stores'
, 'Integrating Vector Databases with LLMs: A Hands-On Guide'
, VEC_FromText('[0.2, 0.01171875, 0, 0, 0, 0.0025634765625, 0, 0]')
);

INSERT INTO `urls` (id, url, title, embedding) VALUES (
  NULL
, 'https://github.com/qwak-ai/qwak-examples/tree/main/qa_bot_falcon_chroma'
, 'LLM Model Enhanced with Vector DB'
, VEC_FromText('[0.2, 0.009765625, 0, 0, 0, 0.00323486328125, 0, 0]')
);

Hier mal zuerst ein Überblick, was jetzt in der Datenbank drin steht:

SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
;
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+
| id | url                                                                         | title                                                    | VEC_ToText(embedding)                                                     |
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+
|  1 | https://mariadb.org/download/?t=mariadb&p=mariadb&r=11.6.0+Vector&os=source | Download MariaDB Server                                  | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002625,0.000000,0.000000] |
|  2 | https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/              | Creating the MariaDB Binary Tarball                      | [0.200000,0.010742,0.000000,0.000000,0.000000,0.000610,0.000549,0.000000] |
|  3 | https://mariadb.org/wp-content/uploads/2024/02/MariaDB-Vector.pdf           | MariaDB Vector                                           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002808,0.000000,0.000000] |
|  4 | https://mariadb.com/resources/blog/mariadb-vector-preview-is-out/           | MariaDB Vector preview is out                            | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002808,0.000000,0.000000] |
|  5 | https://mariadb.org/projects/mariadb-vector/                                | MariaDB Vector                                           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.001526,0.000000,0.000000] |
|  6 | https://metacpan.org/pod/Perl::Tokenizer                                    | Perl::Tokenizer - A tiny Perl code tokenizer             | [0.200000,0.011719,0.000000,0.000000,0.000000,0.001221,0.000000,0.000000] |
|  7 | https://www.qwak.com/post/utilizing-llms-with-embedding-stores              | Integrating Vector Databases with LLMs: A Hands-On Guide | [0.200000,0.011719,0.000000,0.000000,0.000000,0.002563,0.000000,0.000000] |
|  8 | https://github.com/qwak-ai/qwak-examples/tree/main/qa_bot_falcon_chroma     | LLM Model Enhanced with Vector DB                        | [0.200000,0.009766,0.000000,0.000000,0.000000,0.003235,0.000000,0.000000] |
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+

Suche in der MariaDB Vektor Datenbank

Jetzt kommt der spannend Teil der ganzen Geschichte: Finden wir auch was in unserer MariaDB Vektor Datenbank mit URLs?

Wie das schematisch vor sich geht kann wieder dem Schaubild der MariaDB Foundation entnommen werden:

Als erster Versuch ein perfekter Treffer:

./fromdual_llm_v1.php https://mariadb.org/projects/mariadb-vector/
[0.2, 0.0107421875, 0, 0, 0, 0.00152587890625, 0, 0]

SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
 ORDER BY VEC_DISTANCE(embedding, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.00152587890625, 0, 0]'))
LIMIT 3
;
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
| id | url                                                            | title                                         | VEC_ToText(embedding)                                                     |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
|  5 | https://mariadb.org/projects/mariadb-vector/                   | MariaDB Vector                                | [0.200000,0.010742,0.000000,0.000000,0.000000,0.001526,0.000000,0.000000] |
|  6 | https://metacpan.org/pod/Perl::Tokenizer                       | Perl::Tokenizer - A tiny Perl code tokenizer. | [0.200000,0.011719,0.000000,0.000000,0.000000,0.001221,0.000000,0.000000] |
|  2 | https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/ | Creating the MariaDB Binary Tarball           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.000610,0.000549,0.000000] |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+

Die erste Zeile stimmt zu 100% überein. Dann werden die Resultate relativ schnell viel schlechter...

Zweiter Versuch eine ähnliche URL:

./fromdual_llm_v1.php https://mariadb.com/kb/en/e4201/
[0.2, 0.0107421875, 0, 0, 0, 0.00079345703125, 0, 0]

SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
 ORDER BY VEC_DISTANCE(embedding, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.00079345703125, 0, 0]'))
LIMIT 3
;
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
| id | url                                                            | title                                         | VEC_ToText(embedding)                                                     |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
|  2 | https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/ | Creating the MariaDB Binary Tarball           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.000610,0.000549,0.000000] |
|  5 | https://mariadb.org/projects/mariadb-vector/                   | MariaDB Vector                                | [0.200000,0.010742,0.000000,0.000000,0.000000,0.001526,0.000000,0.000000] |
|  6 | https://metacpan.org/pod/Perl::Tokenizer                       | Perl::Tokenizer - A tiny Perl code tokenizer. | [0.200000,0.011719,0.000000,0.000000,0.000000,0.001221,0.000000,0.000000] |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+

Hier würde ich unter den ersten 3 Treffern nur mariadb URLs erwarten. Dies ist aber nicht der Fall. Hier hat unser Modell also noch Verbesserungspotential!

Und noch eine andere ähnliche URL:

./fromdual_llm_v1.php https://mariadb.com/kb/en/vec_totext/
[0.2, 0.0107421875, 0, 0, 0, 0.0010986328125, 0, 0]

SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
 ORDER BY VEC_DISTANCE(embedding, VEC_FromText('[0.2, 0.0107421875, 0, 0, 0, 0.0010986328125, 0, 0]'))
LIMIT 3
;
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
| id | url                                                            | title                                         | VEC_ToText(embedding)                                                     |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+
|  5 | https://mariadb.org/projects/mariadb-vector/                   | MariaDB Vector                                | [0.200000,0.010742,0.000000,0.000000,0.000000,0.001526,0.000000,0.000000] |
|  2 | https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/ | Creating the MariaDB Binary Tarball           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.000610,0.000549,0.000000] |
|  6 | https://metacpan.org/pod/Perl::Tokenizer                       | Perl::Tokenizer - A tiny Perl code tokenizer. | [0.200000,0.011719,0.000000,0.000000,0.000000,0.001221,0.000000,0.000000] |
+----+----------------------------------------------------------------+-----------------------------------------------+---------------------------------------------------------------------------+

Selbes Problem hier. Der Hostname wird zu wenig gewichtet. Wahrscheinlich kann/muss man hier mit der Streuung spielen welche mariadb.org und mariadb.com erzeugen.

Und zu guter letzt eine URL die gar nicht im Datensatz vorkommt:

./fromdual_llm_v1.php https://www.mongodb.com/blog/post/vector-search-llm-essentials-what-when-why
[0.2, 0.0146484375, 0, 0, 0, 0.00323486328125, 0, 0]
SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
 ORDER BY VEC_DISTANCE(embedding, VEC_FromText('[0.2, 0.0146484375, 0, 0, 0, 0.00323486328125, 0, 0]'))
LIMIT 5
;
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+
| id | url                                                                         | title                                                    | VEC_ToText(embedding)                                                     |
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+
|  7 | https://www.qwak.com/post/utilizing-llms-with-embedding-stores              | Integrating Vector Databases with LLMs: A Hands-On Guide | [0.200000,0.011719,0.000000,0.000000,0.000000,0.002563,0.000000,0.000000] |
|  6 | https://metacpan.org/pod/Perl::Tokenizer                                    | Perl::Tokenizer - A tiny Perl code tokenizer.            | [0.200000,0.011719,0.000000,0.000000,0.000000,0.001221,0.000000,0.000000] |
|  3 | https://mariadb.org/wp-content/uploads/2024/02/MariaDB-Vector.pdf           | MariaDB Vector                                           | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002808,0.000000,0.000000] |
|  4 | https://mariadb.com/resources/blog/mariadb-vector-preview-is-out/           | MariaDB Vector preview is out                            | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002808,0.000000,0.000000] |
|  1 | https://mariadb.org/download/?t=mariadb&p=mariadb&r=11.6.0+Vector&os=source | Download MariaDB Server                                  | [0.200000,0.010742,0.000000,0.000000,0.000000,0.002625,0.000000,0.000000] |
+----+-----------------------------------------------------------------------------+----------------------------------------------------------+---------------------------------------------------------------------------+

Hier scheint das Resultat völlig willkürlich zu sein. Wenn man sich den Vektor der Abfrage mit den Vektoren der Resultate vergleicht macht die Reihenfolge aber schon Sinn... Werden die Dimensionen im Vektor von links nach rechts ausgewertet? Es soll ja der Abstand von zwei Punkten im 8-dimensionalen Raum bestimmt werden...

Verbesserungen im Modell

Die Resultate unserer KI sind noch nicht so sonderlich berauschend. Einerseits liegt das sicher an der sehr beschränkten Datenmenge, andererseits haben wir auch sehr wichtige Kriterien in unserem Model noch nicht abgebildet oder völlig unsinnige Kriterien verwendet.

Verbesserungsvorschläge für ein nächstes Modell: Der hostname könnte zusätzlich noch tokenisiert werden, damit könnte man erreichen dass mariadb.com und mariadb.org näher beieinander liegen.

Die Länge von hostname, path, query und fragment ist sicherlich kein sonderlich schlaues Kriterium um eine Änhlichkeit von URLs abzubilden. Hier wäre also wesentlich mehr Intelligenz im Modell vonnöten. Eine Funktion 1/CRC32(dim) liefert ev. bereits minimal bessere Resultate?

titel könnte mit einbezogen werden, oder zumindest die wichtigsten Worte (Nomen, Verben) aus dem Titel.

Der Dokumententyp (MIME type) könnte mit einbezogen werden: Ist ein PDF ähnlicher zu einem anderen PDF als zu einer CSV Datei oder einer statischen HMTL Seite oder einer dynamischen PHP Seite?

Punkte die beim Spielen aufgefallen sind

Die Anzahl der Dimensionen in einem Vektor scheinen beim ersten INSERT festgelegt zu werden. Wenn man anschliessend Daten mit einer anderen Vektorlänge eingibt erscheint folgender Fehler:

INSERT INTO products (name, description, embedding) VALUES (
  'Coffee Machine'
, 'Built to make the best coffee you can imagine'
, VEC_FromText('[0.2, 0.013671875, 0, 0, 0, 6.103515625E-5, 0, 0]')
);
ERROR 1366 (22007): Incorrect vector value: '...' for column `test`.`products`.`embedding` at row 1

Ein Änderen der Vector-Länge ist mit einem TRUNCATE TABLE Befehl zur Zeit noch NICHT möglich. Die Tabelle muss gelöscht (DROP TABLE) und wieder kreiert (CREATE TABLE) werden.

Das Suchen mit einem kürzeren Vector geht hingegen:

SELECT id, url, title, VEC_ToText(embedding)
  FROM urls
 ORDER BY VEC_DISTANCE(embedding, VEC_FromText('[0.2, 0.0107421875]'))
LIMIT 3
;
+----+-------------------------------------------------------------------------+-------------------------------------+---------------------------------------------------------------------------+
| id | url                                                                     | title                               | VEC_ToText(embedding)                                                     |
+----+-------------------------------------------------------------------------+-------------------------------------+---------------------------------------------------------------------------+
|  8 | https://github.com/qwak-ai/qwak-examples/tree/main/qa_bot_falcon_chroma | LLM Model Enhanced with Vector DB   | [0.200000,0.009766,0.000000,0.000000,0.000000,0.003235,0.000000,0.000000] |
|  2 | https://mariadb.com/kb/en/creating-the-mariadb-binary-tarball/          | Creating the MariaDB Binary Tarball | [0.200000,0.010742,0.000000,0.000000,0.000000,0.000610,0.000549,0.000000] |
|  5 | https://mariadb.org/projects/mariadb-vector/                            | MariaDB Vector                      | [0.200000,0.010742,0.000000,0.000000,0.000000,0.001526,0.000000,0.000000] |
+----+-------------------------------------------------------------------------+-------------------------------------+---------------------------------------------------------------------------+

Ob das Resultat so sinnvoll ist, kann ich (noch) nicht beurteilen.

Quellen


Comments

Wir haben unser Model verbessert (siehe v2). Jetzt sind die Resultate ein wenig besser/genauer.

Für die Benutzung des Modells siehe: ./fromdual_llm_v2.php --help

Shinguzcomment