🙌

TiDB Serverless Vector Search を試す

2024/05/12に公開

昨今生成AIの目覚ましい発達により進化を続けている大規模言語モデルが様々なアプリケーションに組み込まれ始めています。これに伴い、従来のDBでは取り扱いを苦手としていた非構造化データと効率よく取り扱い、従来のアプリケーションデータとシームレスに管理できる手法が求められています。

非構造化データの取り扱いにはベクトル化という手法が用いられます。これはデータの意味とコンテキストを理解するという原則に基づいて動作します。テキスト、画像、音声などの複雑なデータを数値ベクトル埋め込みに変換します。これにより、データベースは従来の文字列やバイナリーblobの一致ではなく、意味の理解に基づいて検索を実行できるようになります。

つまり、1)様々なデータをベクトルというデータに変換する、2)ベクトルデータをデータベースに保存し検索可能とする、という2つのテクノロジーにより構成されます。

TiDB Serverless Vector Search は上の2)を実現します。ベクトル検索を実現するサービスは生成AIの発達に伴い世の中に多く生まれています。その多くはベクトル検索を実現させる専用DBとして存在しておりアプリケーションデータ用DBとは別に存在しています。一方TiDB Serverless Vector SearchはMySQL互換データベースでありながらその中にビルトインでベクトル検索の機能が備わっているため、アプリケーションデータとシームレスな連携が行えることが大きな特徴です。現在プライベートベータ中ですので興味がある方は申し込んでみて下さい
https://tidb.cloud/ai

コサイン類似度 について

試してみる前に、コサイン類似度についての解説です。
前述の通りベクトル検索は以下の2ステップで稼働します。
1)様々なデータをベクトルというデータに変換する、2)ベクトルデータをデータベースに保存し検索可能とする
まずは様々なデータ(文字列、音声、画像等)を外部ツールを使ってベクトルデータ化します。Langchianなどが有名ですがいろいろなツールが存在しています。このデータをベクトルデータベースに保存し検索を行います。その際コサイン類似度というものが一般的には使われます。

https://atmarkit.itmedia.co.jp/ait/articles/2112/08/news020.html
が非常にわかりやすいので図を拝借します。
この図では2次元ですが、実際はより複雑な多階層次元によりデータが構成されます。

Pythonのnumpyライブラリを使ったコサイン類似度の算出は以下のBlogがわかりやすくまとまっています。
https://qiita.com/hi-ku/items/8140a06c381479c16304

import numpy as np

# データ作成
a = [1, 1, 0]
b = [1, 0, 1]

# コサイン類似度計算
cos = np.dot(a, b)/(np.sqrt(np.dot(a, a))*np.sqrt(np.dot(b, b)))

print(cos)

かなり良い記事ですのでもう少しコサイン類似度について知りたい方は是非読んでみて下さい。

やってみる

ではやってみます。現在Vector Searchはプライベートベータですが、権利を持っているユーザーはフランクフルトリージョンで起動させることが出来ます。

以下のトグルをオンにしてください。

起動が出来たら以下の記事を参考にmysqlコマンドで接続してみます。
https://zenn.dev/kameping/articles/2248cb2833785e
mysqlの環境準備はこの記事では扱いませんがAmazon Linux 2023 であれば以下で準備できます。

sudo wget https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
sudo dnf install mysql80-community-release-el9-1.noarch.rpm -y
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
sudo dnf install mysql-community-client -y
接続文字列
mysql --comments -u '<ID>' -h gateway01.eu-central-1.prod.aws.tidbcloud.com -P 4000 -D 'test' --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/cert.pem -p'<password>'
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3267121
Server version: 5.7.28-TiDB-Serverless TiDB Server (Apache License 2.0) Community Edition, MySQL 5.7 compatible

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

では早速クエリを飛ばしてみます。
https://www.pingcap.com/blog/integrating-vector-search-into-tidb-for-ai-applications/
にシンプルなサンプルがあるのでそれを実行していきます。
この記事ではデータのベクトル化は扱いませんので、すでにベクトル化されたデータがある前提です。ベクトル化の手順などはまた別の記事でご紹介する予定です。
まずテーブルの作成です。

CREATE TABLE vector_table(embedding VECTOR);
Query OK, 0 rows affected (0.36 sec)
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| vector_table   |
+----------------+
1 row in set (0.24 sec)

次にベクトル化されたデータを投入します。

INSERT INTO vector_table VALUES ('[5.3, 6.2, 4.7, 9.4, 3.2]'),('[7.4, 8.3, 3.6, 9.5, 1.5]'),('[1.6, 5.3, 3.9, 4.9, 3.4]'),('[4.6, 6.2, 2.9, 5.5, 2.4]'),('[8.2, 2.7, 5.9, 4.5, 1.1]');
Query OK, 5 rows affected (0.26 sec)
Records: 5  Duplicates: 0  Warnings: 0

ちなみに通常のMYSQLテーブルのようにSELECTも実行できます。

mysql> select * from vector_table;
+-----------------------+
| embedding             |
+-----------------------+
| [5.3,6.2,4.7,9.4,3.2] |
| [7.4,8.3,3.6,9.5,1.5] |
| [1.6,5.3,3.9,4.9,3.4] |
| [4.6,6.2,2.9,5.5,2.4] |
| [8.2,2.7,5.9,4.5,1.1] |
+-----------------------+
5 rows in set (0.24 sec)

では次にメインのコサイン類似度検索です。

SELECT embedding, vec_cosine_distance(embedding, '[1.2, 3.4, 7.2, 4.5, 8.2]') AS d FROM vector_table ORDER BY d;
+-----------------------+---------------------+
| embedding             | d                   |
+-----------------------+---------------------+
| [1.6,5.3,3.9,4.9,3.4] | 0.12053308403440499 |
| [5.3,6.2,4.7,9.4,3.2] | 0.23127187226668666 |
| [4.6,6.2,2.9,5.5,2.4] |  0.2698508031705604 |
| [8.2,2.7,5.9,4.5,1.1] |  0.3569485224953568 |
| [7.4,8.3,3.6,9.5,1.5] | 0.36798866834626065 |
+-----------------------+---------------------+
5 rows in set (0.24 sec)

dが類似度です。数字が0に近ければ近いほど一致しています。
この例では[1.2, 3.4, 7.2, 4.5, 8.2][1.6,5.3,3.9,4.9,3.4]が一番近いということを表しています。

ユークリッド検索

Vector Search にはコサイン類似度のほかにユークリッド距離の検索にも対応しています。
上記の画像にある通りコサイン類似度は0の座標からそれぞれの点への→(これがベクトルです)の角度により似ているかどうかを判別します。つまり、似ているものだと角度が小さくなります。
一方ユークリッド距離というのは直接それぞれの点の距離を算定し、距離が小さいものを似ていると判別します。テクノロジーにおけるブログ文化って本当に素晴らしくて検索すると大体適切な情報がありますね。先人たちに感謝です。

https://mdsc.kyushu-u.ac.jp/wp/wp-content/uploads/2023/07/a85dfe612a31b1a1e0a4b399a9b073bf.pdf

まずはやってみます。

SELECT embedding, vec_l2_distance(embedding, '[1.2, 3.4, 7.2, 4.5, 8.2]') AS d FROM vector_table ORDER BY d;

vec_cosine_distancevec_l2_distanceに変更するだけです。

+-----------------------+--------------------+
| embedding             | d                  |
+-----------------------+--------------------+
| [1.6,5.3,3.9,4.9,3.4] |  6.153047764779199 |
| [4.6,6.2,2.9,5.5,2.4] |  8.516454141830527 |
| [5.3,6.2,4.7,9.4,3.2] |  8.939239119338673 |
| [8.2,2.7,5.9,4.5,1.1] | 10.079186293441085 |
| [7.4,8.3,3.6,9.5,1.5] | 12.054045090829792 |
+-----------------------+--------------------+
5 rows in set (0.25 sec)

一番似ているものと一番似ていないものは同じですが、2番目、3番目、4番目が入れ替わっています。

どちらを使うべきか

ケースバイケースです。皆さんが保有するデータごとに双方を試してみて、どちらがよりイメージに近い結果となるかとみる必要があります。時にはA/Bテストを行う2つのユーザーインターフェースを用いて継続的な定点観測が必要となります。
単純に2次元でなぜ結果が異なるかを説明すると以下の図になります。

Aにコサイン類似度で近いのはB、ユークリッド距離で近いのがCです。
実世界の例で例えると
1日目に焼肉2日目にジャブシャブを食べている人に、3日目焼き魚を進めた方がいいでしょうか。それともステーキを進めた方がいいでしょうか。そういう使い分けになります。

もっとシンプルなデータでコサイン類似度を理解する

上で用いたSQLは5次元でした。もっと単純に理解しやすいように2次元のデータに入れ変えます。

delete from vector_table;
INSERT INTO vector_table VALUES ('[4,1]'),('[-1,-1]');

上の図のBとCを単純にデータとして投入しました。
これに対してA(1,1)で検索を行います。

コサイン類似度
mysql> SELECT embedding, vec_cosine_distance(embedding, '[1,1]') AS d FROM vector_table ORDER BY d;
mysql> SELECT embedding, vec_cosine_distance(embedding, '[1,1]') AS d FROM vector_table ORDER BY d;
+-----------+--------------------+
| embedding | d                  |
+-----------+--------------------+
| [4,1]     | 0.1425070742874559 |
| [-1,-1]   |                  2 |
+-----------+--------------------+
2 rows in set (0.26 sec)
ユークリッド距離
SELECT embedding, vec_l2_distance(embedding, '[1,1]') AS d FROM vector_table ORDER BY d;
+-----------+--------------------+
| embedding | d                  |
+-----------+--------------------+
| [-1,-1]   | 2.8284271247461903 |
| [4,1]     |                  3 |
+-----------+--------------------+
2 rows in set (0.25 sec)

結果が入れ替わることがイメージできましたでしょうか?

Discussion