k-NN
- 近傍探索する
- ドキュメントの量に比例して検索時間が伸びる
- 実運用には耐えられない
近似 k-NN
これらを組み合わせることで、データ量が億いくぐらいまで増大しても対応
- ベクトル検索を高速化
- ベクトルを圧縮
HNDW (Hierarchical Navigable Small World ) algorithm
ある初期検索値から近い一のベクトルを探していく 貪欲な探索アルゴリズムっぽい
IVF algorithm
- ベクトルを事前に複数バケットにクラスタリングする
- k-means クラスタリングとかが使われる
- 各クラスターのセントロイド(中央の値?)をみて、クラスタを決定する
ベクトルの量子化 (圧縮)
- マスターデータ的なIDを用意する
- ベクトルを小さな部分に分割し、各部分がマスターデータのどこに所属するか ID で指定
- マスターデータのサイズだけになるので、容量めっちゃ減る
HNDW より IVF の方が精度低めらしい
フィルタリング
実際はk-NN対象をメタデータ属性とかで、事前にフィルタすることが多い
VectorDB s
- ElasticSearch
- OpenSearch
- Aurora / RDS ( postgres )
OSS
-
Elastic Cloud
-
redis
-
Pinecone etc…
-
opensearch :
- HNSW / IVF :
- 圧縮PQ
- postフィルタ
- 最大次元 16000
-
pgvector : 最近は対応している pgvector v0.5.0 から
- HNSW / IVF
- 圧縮なし
- postフィルタ
- 最大次元 16000 (格納)
- 近似k-NNの場合使用できるのは 2000
ベクトルエンジンは、ユークリッド距離、コサイン類似度、ドット積などの一般的な距離メトリックをサポートし、16,000 次元に対応できるため、幅広い基盤モデルやその他の AI/ML モデルのサポートに適しています。
直積量子化 (PQ) は最近傍検索の領域では非常に良く使われる量子化手法です。ANN アルゴリズムと併用して最近傍検索に使うことができます。 OpenSearch 1.2 では、k-NN プラグインに IVF と共に Faiss の PQ 実装をサポートするようになりました。
用語調査
リランキング
-
機械学習を活用した検索後処理、すなわちリランキング(Re-ranking)です。
-
検索後処理?
-
Solr の re-ranking はクエリのトップ N 件のスコアを変更するタイプ
- 上N件だけ丁寧になるから計算速度の割に精度がでる
セマンティックサーチ
結局コンテキストをいろいろなところから集めて検索することなので、技術的用語ではないっぽい
資料調査
https://zenn.dev/kun432/articles/20230921-vector-databases-jp-part-1 キーワード検索+ベクトル検索のハイブリッドが最良の結果をもたらす ![[Pasted image 20231125223150.png]]
https://docs.google.com/spreadsheets/d/1tUxif_MQYByprhFArZ2XqssvlMVfn5mKjsPjYTOO08o/edit#gid=0
適当な VectorDB 使ってみる
chromadb https://pypi.org/project/chromadb/ The fastest way to build Python or JavaScript LLM apps with memory!
https://www.trychroma.com/ Chroma has all the tools you need to use embeddings the AI-native open-source embedding database 自然言語を計算が可能な形に変換することを Embedding と呼ぶらしい
特徴
- Redis 上に乗っかってて速い
- Word2Vec / GloVe / FastTextなど、さまざまな埋め込みフォーマットをサポート
- 最近傍探索 (Nearest neighbor search) のサポート: ChromaDBは、指定された埋め込みの最近傍を見つけるために使用
- k-NNのこと -> yes : Nearest neighbor search が最近傍探索
OpenSearch資料 https://zenn.dev/tasai/scraps/9fd55a1babc043
使ってみた
pip3 install chromadb
import chromadb
client = chromadb.Client()
collection = client.create_collection("sample_collection")
# Add docs to the collection. Can also update and delete. Row-based API coming soon!
collection.add(
documents=["This is document1", "This is document2"], # we embed for you, or
metadatas=[{"source": "notion"}, {"source": "google-docs"}], # filter on arbitrary metadata!
ids=["doc1", "doc2"], # must be unique for each doc
)
results = collection.query(
query_texts=["This is a query document"],
n_results=2,
)
Collection を作って、そこに doc を add して最後にそこに対してクエリで検索できるっぽい
https://docs.trychroma.com/getting-started
- AI App 向けのDB
- Collection : Embedding(自然言語を計算可能な形にしたデータ)、document、metadata を保存するDB
- テキストを保存すると、tokenization と embedding と indexing を自動で行う
- 自前ですでに embedding を行った結果のベクトルを格納することもできる
collection.add(
embeddings=[[1.2, 2.3, 4.5], [6.7, 8.2, 9.2]],
documents=["This is a document", "This is another document"],
metadatas=[{"source": "my_source"}, {"source": "my_source"}],
ids=["id1", "id2"]
)
n近傍の検索が可能
永続データの利用
client = chromadb.PersistentClient(path="/Users/hge/_tmp/chroma")
ls -l
total 288
drwxr-xr-x 6 shuaki staff 192 Nov 25 22:21 c1e7b412-5f05-43a7-b1c6-3f32ab46e641
-rw-r--r-- 1 shuaki staff 147456 Nov 25 22:21 chroma.sqlite3
collection に対する embedding function の指定
- collection への document の追加時の tokenization / embedding 処理を指定
- Chroma will use sentence transfomer as a default.
- 他には?
- カスタムで自作できるっぽい
距離関数の変更
- hnsw:space
- Squared L2 がデフォルト
Qdrant
finch vm start
finch pull qdrant/qdrant
finch run -p 6333:6333 qdrant/qdrant
WEB UI が立ち上がるっぽい
elapsed: 172.0s total: 51.7 M (308.1 KiB/s)
_ _
__ _ __| |_ __ __ _ _ __ | |_
/ _` |/ _` | '__/ _` | '_ \| __|
| (_| | (_| | | | (_| | | | | |_
\__, |\__,_|_| \__,_|_| |_|\__|
|_|
Access web UI at http://localhost:6333/dashboard
curl -X PUT 'http://localhost:6333/collections/test_collection' \
-H 'Content-Type: application/json' \
--data-raw '{
"vectors": {
"size": 4,
"distance": "Dot"
}
}'
curl -L -X PUT 'http://localhost:6333/collections/test_collection/points?wait=true' \
-H 'Content-Type: application/json' \
--data-raw '{
"points": [
{"id": 1, "vector": [0.05, 0.61, 0.76, 0.74], "payload": {"city": "Berlin"}},
{"id": 2, "vector": [0.19, 0.81, 0.75, 0.11], "payload": {"city": ["Berlin", "London"] }},
{"id": 3, "vector": [0.36, 0.55, 0.47, 0.94], "payload": {"city": ["Berlin", "Moscow"] }},
{"id": 4, "vector": [0.18, 0.01, 0.85, 0.80], "payload": {"city": ["London", "Moscow"] }},
{"id": 5, "vector": [0.24, 0.18, 0.22, 0.44], "payload": {"count": [0]}},
{"id": 6, "vector": [0.35, 0.08, 0.11, 0.44]}
]
}'
curl -L -X POST 'http://localhost:6333/collections/test_collection/points/search' \
-H 'Content-Type: application/json' \
--data-raw '{
"vector": [0.2,0.1,0.9,0.7],
"top": 3
}'
簡単な CRUD はできた
サンプル
SNSサンプル
コレクション作成
curl -X PUT 'http://localhost:6333/collections/sns' \
-H 'Content-Type: application/json' \
--data-raw '{
"vectors": {
"size": 4,
"distance": "Dot"
}
}'
オプション
- vectors : 必須
- size : ベクターの次元数
- distance : cos or dot
- hnsw_config
- wal_config
- optimizers_config
- shard_number
- on_disk_payload
- quantization_config
あ〜このDBもしかして、ベクトル情報は保存できるけど Embedding後しかだめっぽい?
https://zenn.dev/kun432/scraps/8eab42294bde75 OpenAI で Embedding して, 保存している。この場合 1536 次元
GiNZA の場合 300 次元
https://zenn.dev/tfutada/articles/acf8adbb2ba5be
search(query_vector=クエリーベクトル)で検索します。検索に使用するドキュメントのベクトルを引数に渡します。Qdrant自体にはベクトル化の機能は無いため、ドキュメント(原型)を渡すことはできません。
Qdrant 使う場合は Embedding エンジンを別途用意しとかないとダメっぽい
https://zenn.dev/kun432/articles/20230921-vector-databases-jp-part-2
Embedding ツール調査
- Titan Embedding とか?
- Chroma provides lightweight wrappers around popular embedding providers
- OpenAI
- Cohere
- GooglePaLM
- HugginFace
- Instructor
- 基本 LLM でベクトル生成するぽい
- TnesorFlow
- Gensim
- FastText
- BERT
- ELMo
- GinZAもそう
GiNZAの単語分散表現にchiVeを使って精度向上のお試し - Taste of Tech Topics https://acro-engineer.hatenablog.com/entry/2020/07/21/120000 単語にベクトル埋め込むのは Word2Vec などが有名 未知語対応や文脈把握は文章単位でベクトル変換が必要