象使いのための pgvector 入門 (2)

これは **[PostgreSQL Advent Calendar 2023](https://qiita.com/advent-calendar/2023/postgresql)** シリーズ 2 の 4 日目の記事です。

同・アドベントカレンダー 2 日目の記事の続きです。

- **[象使いのための pgvector 入門 (1)](https://qiita.com/hmatsu47/items/b393cecef8ed9df57c35)**

https://qiita.com/hmatsu47/items/b393cecef8ed9df57c35

[pgvector](https://github.com/pgvector/pgvector) の用途・Docker での起動・テーブル作成・検索・インデックス作成については↑をご覧ください。

## 検索（探索）負荷を下げるには？

↑の記事で触れたとおり、ベクトル検索は **「精密な検索をしようとするとテーブル内の全行に対して距離を計算し比較する必要がある」** ので、通常のデータ行以上に検索（探索）負荷に配慮する必要があります。

検索（探索）負荷を下げ性能を上げる方法はいくつかあります。

### フィルタリング

例えば FAQ を RAG で構築する場合に質問カテゴリを指定して検索させるようなケースでは、

```sql:
SELECT * FROM items WHERE category_id = 123 ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
```

のような形で事前に絞り込んでおくとベクトル検索の対象行を減らすことができます。

あわせて、[部分インデックス](https://www.postgresql.jp/docs/15/indexes-partial.html)や[パーティショニング](https://www.postgresql.jp/docs/15/ddl-partitioning.html)を併用する例が [README の **Filtering**](https://github.com/pgvector/pgvector#filtering) に示されています。

### ハイブリッド検索

全文検索など、ベクトル以外の検索によってあらかじめ絞り込んだ行をベクトル検索する方法をハイブリッド検索と呼びます。

[README の **Hybrid Search**](https://github.com/pgvector/pgvector#hybrid-search) で示された SQL 文の他に、例えば **[PGroonga](https://pgroonga.github.io/ja/)** のような全文検索エンジンを使うことも考えられます。

https://pgroonga.github.io/ja/

絞り込みにキーワードを使うため、ベクトル検索より厳密な検索が行われる一方で、関係のありそうな文書を取りこぼす可能性は高くなります。

### バッファの暖機（プレウォーム）

データやインデックスがメモリ上のバッファ（共有バッファまたは OS のキャッシュ）に載った状態にしておくと、検索時の I/O 負荷を下げ、検索が速くなります。

例えば、拡張機能 [pg_prewarm](https://www.postgresql.jp/docs/15/pgprewarm.html) を、

```sql:
CREATE EXTENSION pg_prewarm;
```

で有効化しておき、

```sql:
SELECT pg_prewarm(’【テーブル名】’, 【オプション】);
```

でベクトルデータが入ったテーブルを暖機します。

https://bftnagoya.hateblo.jp/entry/2022/03/11/144935

:::note
オプションには`buffer`（共有バッファ）または`read`（OS キャッシュ）を指定します。
:::

### 検索精度と性能のバランスを取る

[README の **FAQ 内**](https://github.com/pgvector/pgvector#can-i-store-vectors-with-more-precision)や、先の記事でリンクした[石井さんのスライド資料](https://www.slideshare.net/nttdata-tech/postgresql-pgvector-chatgpt-pgcon23j-nttdata) 37 ページに記されているとおり、

- ベクトル型のデフォルトでは 32 ビット浮動小数点数をベクトル要素として使用
- さらに精度を上げることも可能（`double precision`または`numeric`）
- ただしインデックスを使う場合はデフォルトの 32 ビット浮動小数点数で検索

です。また、インデックスについては、

- IVFFlag・HNSW とも、インデックス作成時・検索時それぞれの指定パラメータによって検索精度と性能のバランス調整が可能

です。

つまり **検索精度と性能はトレードオフの関係** になります。

必要な検索精度を確保しつつ、なるべくデータサイズを抑えて性能も確保します。

:::note
データサイズを抑えるためには、ベクトルの次元数を削減（圧縮）する方法もあります。
:::

## 結局、象使いはどうすれば良い？

ここからは **完全に私見** です。

現状、RAG を構成する際の Vector Store には、

- インメモリ型
- 細かな設定が不要で手軽に使えるもの
- 容量の大きさよりは（数千行程度のデータ量で）高速に使えるもの

が選択される傾向にあるように思います。

「RDBMS 上の蓄積データをそのままベクトル検索に使いたい」または「RDBMS 上のデータを連携する形でベクトル検索したい」という、より複雑（または大容量が必要）なニーズが出てきたときにはじめて PostgreSQL + [pgvector](https://github.com/pgvector/pgvector) が選択肢として浮上してきそうです。

一方で、[pgvector](https://github.com/pgvector/pgvector) 自体もまだ「バージョン 0.5.1」が示すとおり発展途上です。

なので現状は、以下のようなスタンスで良い気がします。

- ベクトル検索（や Vector Store）については、基本的な事項をおさえておく
- それ以外の、RDBMS 全般を利用・管理する知識やスキルを磨いておく

変化の激しい世界、すぐ別の技術に取って代わられる可能性もあります。

それを踏まえて、当面は

- いざ「RDBMS でベクトル検索したい」というニーズが出てきたときに、本当に RDBMS でベクトル検索するのが良いか他の技術を併用するのが良いか、見極められるようにしておく

のが良いのではないでしょうか。

---

**2024/5/19 追記：**
[続編](https://qiita.com/hmatsu47/items/8cdafc093fcda793e863)を公開しました。

https://qiita.com/hmatsu47/items/8cdafc093fcda793e863