435 Views
January 20, 25
スライド概要
BuriKaigi2025 2025/2/1
(ただいま作成中なのでダウンロード不可)
Qiita や Zenn でいろいろ書いてます。 https://qiita.com/hmatsu47 https://zenn.dev/hmatsu47 MySQL 8.0 の薄い本 : https://github.com/hmatsu47/mysql80_no_usui_hon Aurora MySQL v1 → v3 移行計画 : https://zenn.dev/hmatsu47/books/aurora-mysql3-plan-book https://speakerdeck.com/hmatsu47
ベクトルストア入門 BuriKaigi2025 2025/2/1 まつひさ(hmatsu47)
自己紹介 松久裕保(@hmatsu47) ● https://qiita.com/hmatsu47 ● Web インフラのお守り係をしています ● 普段は JAWS-UG 名古屋(・浜松)や PostgreSQL アンカンファレンスで DB ネタを中心に話しています ● 実務では MySQL 系の DBを中心に扱っています 2/22(土)に PHP カンファレンス名古屋 2025(名古屋駅前・ ウインクあいち)で MySQL 8.4 以降の話をします 2
本日のお題 ● ベクトル検索およびベクトルストアの話をします ○ ベクトル検索:「近いもの」「似ているもの」を探す ○ ベクトルストア:ベクトル(と関連情報)を保存・検索する仕組み 3
おことわり ● 正確さよりも感覚的なわかりやすさを優先します ○ 「数学も機械学習もなんもわからん」人(私)が同じような人に 向けて話します ● ベクトルストアとしては PostgreSQL+pgvector を中心 に扱います ○ 考え方は他のベクトルストアと共通しています 4
ベクトルとベクトル検索 5
ところで…ベクトルって? ● いくつかの意味がある:例えば ○ ①大きさと向きを持った量 ○ ②数字の組(縦や横に並べたもの) ○ … 6
ところで…ベクトルって? ● いくつかの意味がある:例えば ○ ①大きさと向きを持った量 ○ ②数字の組(縦や横に並べたもの) ○ … この話の中で主に扱うのはこれ 縦向き・横向きがありますが、ここでは主に (1, 2, 3, …) のように横に並べて表記します (ベクトルストアで扱う上でも横向きがイメージしやすい) 7
突然ですが ● A さんに近いのは誰でしょう?(注:名前以外で・以降同じ) 名前 身長 A さん 170.0 cm B さん 174.5 cm C さん 167.2 cm D さん 186.7 cm ?? 8
突然ですが ● A さんに近いのは誰でしょう? 名前 身長 身長差 A さん 170.0 cm B さん 174.5 cm 4.5 cm C さん 167.2 cm 2.8 cm 差を計算すればわかりやすい D さん 186.7 cm 16.7 cm - 9
体重を加えて… ● A さんに近いのは誰でしょう? 名前 身長 体重 A さん 170.0 cm 64.0 kg B さん 174.5 cm 82.7 kg C さん 167.2 cm 46.8 kg D さん 186.7 cm 76.2 kg ?? 10
体重を加えて… ● A さんに近いのは誰でしょう? 名前 身長 体重 BMI A さん 170.0 cm 64.0 kg 22.15 B さん 174.5 cm 82.7 kg 27.16 C さん 167.2 cm 46.8 kg D さん 186.7 cm 76.2 kg 16.74 比べたいのは 肥満度? 体型? 21.86 体の大きさ? 11
こんなとき ● 近さや類似度をはかるのに便利なのがベクトル 名前 (身長, 体重) 2次元のベクトル A さん (170.0, 64.0) B さん (174.5, 82.7) C さん (167.2, 46.8) D さん (186.7, 76.2) 12
近さ・類似度をはかる方法はいくつかある ● マンハッタン距離(L1 ノルム) 縦軸と横軸の和 【注】グラフの表現の都合上、縦軸 で第1次元、横軸で第 2次元を表現し ています(以降同じ) 13
近さ・類似度をはかる方法はいくつかある ● ユークリッド距離(L2 ノルム) 直線距離 14
近さ・類似度をはかる方法はいくつかある ● コサイン類似度 方向性 (原点から見て) cosθ:-1(全く似ていない) 〜 0(無関係) 〜 1(同じ) ● 身長・体重の例では (大雑把に) マンハッタン距離・ユークリッド距離は「体の大きさ」 θ コサイン類似度は「体型」 の近さをはかるのに使えそう? 15
近さ・類似度をはかる方法はいくつかある ● コサイン類似度 ほかにも「内積」などがある が、ここでは省略 方向性 (原点から見て) cosθ:-1(全く似ていない) 〜 0(無関係) 〜 1(同じ) ● 身長・体重の例では (大雑把に) マンハッタン距離・ユークリッド距離は「体の大きさ」 θ コサイン類似度は「体型」 の近さをはかるのに使えそう? 16
ここで注意! ● 次元ごとに単位や値が取る範囲が違う値を持つベクトル では、各次元の「1」が表す意味に差が生じてしまう ○ 例えば身長の単位が「cm」ではなく「mm」だったら計算結果は さらに変わってしまう(身長の影響度>体重の影響度) ○ そういうときは各次元の値を正規化または標準化して使う ■ ここでは正規化・標準化の詳細については省略 ■ 正規化・標準化(前処理)の方法によっても計算結果が変わるので注意 17
簡単のために ● 今回は正規化・標準化せずに比較してみる 名前 (身長, 体重) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0) 0 0 1.0000 B さん (174.5, 82.7) 23.2 19.2 0.9966 C さん (167.2, 46.8) 20.0 17.4 0.9962 D さん (186.7, 76.2) 28.9 20.7 0.9996 18
簡単のために 距離は「同じものが 0」になる 値が小さいほど似ている ● 今回は正規化・標準化せずに比較してみる 名前 (身長, 体重) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0) 0 0 1.0000 B さん (174.5, 82.7) 23.2 19.2 0.9966 C さん (167.2, 46.8) 20.0 17.4 0.9962 D さん (186.7, 76.2) 28.9 20.7 0.9996 19
簡単のために どちらの距離も 「Cさんが一番近い」と判定 ● 今回は正規化・標準化せずに比較してみる 名前 (身長, 体重) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0) 0 0 1.0000 B さん (174.5, 82.7) 23.2 19.2 0.9966 C さん (167.2, 46.8) 20.0 17.4 0.9962 D さん (186.7, 76.2) 28.9 20.7 0.9996 20
簡単のために 類似度は「同じものが 1」に 値が大きいほど似ている ● 今回は正規化・標準化せずに比較してみる 名前 (身長, 体重) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0) 0 0 1.0000 B さん (174.5, 82.7) 23.2 19.2 0.9966 C さん (167.2, 46.8) 20.0 17.4 0.9962 D さん (186.7, 76.2) 28.9 20.7 0.9996 21
簡単のために 「Dさんが最も似ている」 ● 今回は正規化・標準化せずに比較してみる 名前 (身長, 体重) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0) 0 0 1.0000 B さん (174.5, 82.7) 23.2 19.2 0.9966 C さん (167.2, 46.8) 20.0 17.4 0.9962 D さん (186.7, 76.2) 28.9 20.7 0.9996 22
要素(次元)が増えても大丈夫 ● 「腹囲」が増えた 名前 (身長, 体重, 腹囲) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0, 77.0) 0 0 1.0000 B さん (174.5, 82.7, 86.6) 32.8 21.5 0.9969 C さん (167.2, 46.8, 71.8) 25.2 18.2 0.9967 D さん (186.7, 76.2, 67.1) 38.8 22.9 0.9965 23
Bさんのお腹は 意外と太って見えない? 要素(次元)が増えても大丈夫 ● 「腹囲」が増えた 名前 (身長, 体重, 腹囲) マンハッタン距離 ユークリッド距離 コサイン類似度 A さん (170.0, 64.0, 77.0) 0 0 1.0000 B さん (174.5, 82.7, 86.6) 32.8 21.5 0.9969 C さん (167.2, 46.8, 71.8) 25.2 18.2 0.9967 D さん (186.7, 76.2, 67.1) 38.8 22.9 0.9965 24
もう一つ注意 ● 次元数が異なるベクトルの比較はできない ○ 例:MLB 2023 日本人選手 名前 大谷翔平 オオタニサン 鈴木誠也 セイヤサン 吉田正尚 マッチョマン 打率 本塁打 打点 盗塁 勝利 .304 44 95 20 .285 20 74 6 .289 15 72 8 10 敗戦 5 セーブ 防御率 0 3.14 25
打者成績だけで 比較する必要がある もう一つ注意 ● 次元数が異なるベクトルの比較はできない ○ 例:MLB 2023 日本人選手 名前 大谷翔平 オオタニサン 鈴木誠也 セイヤサン 吉田正尚 マッチョマン 打率 本塁打 打点 盗塁 勝利 .304 44 95 20 .285 20 74 6 .289 15 72 8 10 敗戦 5 セーブ 防御率 0 3.14 26
ここまでのまとめ ● 近さや類似度をはかるのに便利なのがベクトル ● ベクトルが近い・似ているものを探すのがベクトル検索 ○ 近さ・類似度をはかる方法はいくつかある ■ どれを選ぶかは対象が持つ性質や探す切り口による ● 比較可能なベクトルを使う必要がある ○ 単位や範囲が違う場合は前処理が必要 ○ 次元数は合わせる 27
ベクトル検索の使い道 28
ベクトル検索の使い道の例 [1] ● レコメンド(推薦)システム ○ 例:オンラインショップで閲覧中の商品に似た商品を「お勧め」 欄に表示 ■ あらかじめ商品の属性(分類や性質など)をベクトル化 ■ ベクトルが近いほうから順に、いくつかの商品をピックアップ ○ 後述のキーワード検索に近い手法を取ることも ■ 商品の入れ替わりが速い・一点ものの販売→属性を個別に登録するのは煩雑 ■ 商品名や説明文を単語に分解してベクトル化 29
ベクトル検索の使い道の例 [2] ● キーワード検索 ○ キーワードの一致度高=検索結果上位に表示 ○ あらかじめ文章を単語別に分解してベクトル化 ○ 例文: ■ ①ブリ会議に来た ■ ②ブリ会議でブリを食べる ■ ③ハマチを食べたい これを形態素解析などの方法で単語に分解して… 30
ベクトル検索の使い道の例 [2] ● キーワード検索 ○ 例えば、単語ごとの出現回数をカウントしてベクトル化する 例文 No. ブリ 会議 に 来 た で を 食べ る ハマチ たい ① 1 1 1 1 1 0 0 0 0 0 0 ② 2 1 0 0 0 1 1 1 1 0 0 ③ 0 0 0 0 0 0 1 1 0 1 1 31
ベクトル検索の使い道の例 [2] ● キーワード検索 ○ 「ブリ会議」でベクトル検索:近い順に②→①がヒット 例文 No. ○ ブリ 会議 に 来 た で を 食べ る ハマチ たい ① 1 1 1 1 1 0 0 0 0 0 0 ② 2 1 0 0 0 1 1 1 1 0 0 ③ 0 0 0 0 0 0 1 1 0 1 1 32
「0」の次元が多い →疎ベクトル ベクトル検索の使い道の例 [2] ● キーワード検索 ○ 「ブリ会議」でベクトル検索:近い順に②→①がヒット 例文 No. ブリ 会議 に 来 た で を 食べ る ハマチ たい ① 1 1 1 1 1 0 0 0 0 0 0 ② 2 1 0 0 0 1 1 1 1 0 0 ③ 0 0 0 0 0 0 1 1 0 1 1 33
ベクトル検索の使い道の例 [2] ● キーワード検索 ○ 実際には単純に単語の出現回数をカウントするのではなく、 文章中に占める(その)単語の割合 対象となる全文章の中での単語のレア度 などを加味してベクトル化する → TF-IDF・(Okapi)BM25 など 34
ベクトル検索の使い道の例 [3] ● セマンティック検索 ○ キーワードの一致度ではなく意味の近さで文章を検索 ○ 文章のベクトル化には主に LLM の埋め込みモデルを使う ■ 768 次元(Cohere Embed v3 など)〜 3072 次元(OpenAI text-embeddings-3-large など)の多次元ベクトル化 ○ RAG(検索拡張生成)の仕組みの中で使われている ■ LLM が持たない外部知識を追加情報として渡す際に、外部知識をベクトルス トアから検索して取り出す 35
ここまでのまとめ ● ベクトル検索にはいくつかの使い道がある ● 使い道によってベクトルの使い方・生成方法が異なる ○ キーワード検索では文章を単語に分解して疎ベクトル化する ○ セマンティック検索では主に LLM の埋め込みモデルを使ってベ クトル化する ■ こちらで扱うのは密ベクトル(「0」の次元がほとんど含まれない) 36
ベクトルストアの機能 37
ベクトルストアの機能 [1] ● ベクトルを含むデータ(行・レコード)の保管・更新 ○ PostgreSQL+pgvector(RDB のベクトルストア)ではテーブル内の データとして保管(ベクトルはベクトル型カラムへ) ○ Pinecone のような非 RDB のベクトルストアではインデックス内 のデータとして保管 ■ ベクトル以外の属性(ベクトルのフィルタ・絞り込みで使う)はメタデータ としてベクトルデータとともに保管 38
ベクトルストアの機能 [1] ● PostgreSQL+pgvector の例(以降同じ) ○ 先の A さん〜 D さんのデータを登録・保管 // pgvector有効化 postgres=# CREATE EXTENSION vector; CREATE EXTENSION // テーブル作成 postgres=# CREATE TABLE persons (id bigserial PRIMARY KEY, name varchar(50) NOT NULL, physique vector(3)); CREATE TABLE // データ登録 postgres=# INSERT INTO persons (name, physique) VALUES ('Aさん', '[170.0,64.0,77.0]') ,('Bさ ん', '[174.5,82.7,86.6]'), ('Cさん', '[167.2,46.8,71.8]'), ('Dさん', '[186.7,76.2,67.1]'); INSERT 0 4 39
ベクトルストアの機能 [1] ● PostgreSQL+pgvector の例 ○ 登録データを確認 // 登録データ確認 postgres=# SELECT * FROM persons; id | name | physique ----+-------+------------------1 | Aさん | [170,64,77] 2 | Bさん | [174.5,82.7,86.6] 3 | Cさん | [167.2,46.8,71.8] 4 | Dさん | [186.7,76.2,67.1] (4 rows) 40
ベクトルストアの機能 [2] ● ベクトルを含むデータ(行・レコード)の検索 ○ 例:A さんとユークリッド距離が近い(小さい)順に表示 // ユークリッド距離が近い順に表示 postgres=# SELECT name FROM persons WHERE id != 1 ORDER BY physique <-> (SELECT physique FROM persons WHERE id = 1); name ------Cさん Bさん Dさん (3 rows) 41
ベクトルストアの機能 [2] ● ベクトルを含むデータ(行・レコード)の検索 ○ 例:A さんとユークリッド距離が近い(小さい)順に表示 // ユークリッド距離が近い順に表示 postgres=# SELECT name FROM persons WHERE id != 1 ORDER BY physique <-> (SELECT physique FROM persons WHERE id = 1); name ------<+> マンハッタン距離 Cさん <-> ユークリッド距離 Bさん <=> コサイン距離 Dさん (1-コサイン類似度) (3 rows) 42
ベクトルストアの機能 [3] ● ベクトル間の距離・類似度の計算 ○ 例:A さんとのコサイン距離(1-コサイン類似度)を表示 // コサイン距離を表示(コサイン距離の昇順) コサイン類似度とは大小が逆になる(値の範囲: 0〜2) postgres=# SELECT name, cosine_distance((SELECT physique FROM persons WHERE id = 1), physique) AS dist FROM persons WHERE id != 1 ORDER BY dist; name | dist -------+----------------------Bさん | 0.0030710384673471314 Cさん | 0.0032670664285965323 Dさん | 0.0035040553323729684 (3 rows) 43
ベクトルストアの機能 [3] ● ベクトル間の距離・類似度の計算 ○ 例:A さんとのコサイン距離(1-コサイン類似度)を表示 // コサイン距離を表示(コサイン距離の昇順) postgres=# SELECT name, cosine_distance((SELECT physique FROM persons WHERE id = 1), physique) AS dist FROM persons WHERE id != 1 ORDER BY dist; name | dist -------+----------------------Bさん | 0.0030710384673471314 l1_distance(vector, vector) マンハッタン距離 Cさん | 0.0032670664285965323 l2_distance(vector, vector) ユークリッド距離 Dさん | 0.0035040553323729684 cosine_distance(vector, vector) コサイン距離 (3 rows) (1-コサイン類似度) 44
ベクトルストアの機能 [4] ● 近似最近傍探索(ANN)用インデックス ○ ベクトル検索では、検索対象となるデータ全てのベクトルとの間 で距離や類似度の計算が必要になる ■ 単純なデータスキャンより計算負荷が高い ○ 「近いベクトル」を効率的に探すためのインデックスを作成して 計算量とデータスキャン量を減らすことができる ■ ただし「近似」であり完全に正確な結果を返せない可能性もある 45
ベクトルストアの機能 [4] ● 例:HNSW(Hierarchical Navigable Small World)Index ○ 階層的なグラフ構造を持つインデックス ○ 多くのベクトルストアでサポートされている 46
ベクトルストアの機能 [4] ● HNSW Index での検索例 ID x y A 18 15 B 4 14 C 8 17 D 5 5 E 9 11 F 14 8 G 19 4 H 13 13 この2次元ベクトルの 中から (8, 8) に最も近いものを検索 するときの流れ 47
ベクトルストアの機能 [4] ● HNSW Index での検索例 ID x y A 18 15 B 4 14 C 8 17 D 5 5 E 9 11 F 14 8 G 19 4 H 13 13 第3層のグラフの始点 Aから探索開始 (8, 8) に近いのは? →B 48
ベクトルストアの機能 [4] ● HNSW Index での検索例 ID x y A 18 15 B 4 14 C 8 17 D 5 5 E 9 11 F 14 8 G 19 4 H 13 13 第2層のグラフは Bを 始点に探索開始 B→D→Fと進む HはFより遠い →Fが最も近い 49
ベクトルストアの機能 [4] ● HNSW Index での検索例 ID x y A 18 15 B 4 14 C 8 17 D 5 5 E 9 11 F 14 8 G 19 4 H 13 13 第1層のグラフは Fを 始点に探索開始 F→Eと進む グラフで繋がる別の点 はEより遠い →Eが最も近い 50
ベクトルストアの機能 [4] ● PostgreSQL+pgvector で HNSW Index 作成 ○ 例:ユークリッド距離用の HNSW Index を作成 // ユークリッド距離用の HNSW INDEXを作成 postgres=# CREATE INDEX ON persons USING hnsw (physique vector_l2_ops); CREATE INDEX vector_l1_ops マンハッタン距離用 vector_l2_ops ユークリッド距離用 vector_cosine_ops コサイン距離用 51
ベクトルストアの機能 [5] ● ベクトルの圧縮・量子化 ○ 一般的に、ベクトル 1 次元は 32 ビット ■ 次元数が多いとデータサイズが膨大に ○ メモリ容量を超えるベクトルデータを検索すると極端に遅くなる ■ HNSW Index はメモリ容量を超えるサイズになると作成も極端に遅くなる ○ 多次元で大量のベクトルデータは圧縮・量子化して容量を削減 ■ 例:32 ビットを 1 ビットに→バイナリ量子化 52