Slide 1

Slide 1 text

Rust製ベクトル検索エンジン Qdrantの紹介 Junichi Sato

Slide 2

Slide 2 text

発表の概要 ● ベクトル検索エンジンとは ○ ベクトル検索技術 ● Qdrantについて ○ 機能の紹介 ● Qdrantコードを読んでみて ○ Rust初心者の視点で良いと思ったところ

Slide 3

Slide 3 text

自己紹介 ● 佐藤純一 ○ note: https://note.com/sat0b3ee ● 類似画像検索に興味あり ○ 先週、類似画像検索の本が 技術の泉シリーズで販売されました ● Rustは入門したばかり ※ Qdrantの直接の関係者ではないです

Slide 4

Slide 4 text

ベクトル検索エンジンとは何か ● 近似最近傍探索(ANN)の技術を利用して、 大規模なベクトルデータの中から近似ベクトルを探す ● 活用例 ○ 類似画像検索 ○ セマンティックテキスト検索 ● 代表的なベクトル検索エンジン(サービス) ○ Milvus (Go) ○ Vald (Go) ○ Elasticsearch KNN (Java) ○ Qdrant (Rust) 参考) https://github.com/currentsapi/awesome-vector-search

Slide 5

Slide 5 text

類似画像検索での活用例 ANN インデックス 数百万 ~ 数十億スケール 画像 Deep Learning 画像のベクトル化 [0.0232, -0.832, …, ]

Slide 6

Slide 6 text

ベクトル検索エンジンの課題 ● 一般的なベクトル検索では「絞り込み」が利用できないことが多い ○ 全文検索エンジンではよく使う機能 (Apache SolrやElasticsearchなど) ■ 機能要件的によくある ○ ECサイトでの商品の類似画像検索の例: 「ストアAに絞り込んで類似画像を検索したい」 ● 対処法 ○ 検索を多めに引いて、後段でフィルタする ■ ただし、ごっそり後段のフィルタで消えてしまう可能性がある ○ フィルタ条件ごとにインデックスを分割する ■ ただし、構築が大変、柔軟性がない

Slide 7

Slide 7 text

Qdrantについて ● Rust製のベクトル検索エンジン ○ フィルタ条件による絞り込み検索が可能 ○ 高い信頼性と高いパフォーマンス ● GitHubリポジトリ ○ https://github.com/qdrant/qdrant ● 公式のデモ ○ https://demo.qdrant.tech/#/ ○ セマンティック検索のデモ(クエリの意味を把握して結果を返す) 余談)現在、Rustの開発者を募集しているらしいです。(LinkedIn) https://github.com/qdrant/qdrant より

Slide 8

Slide 8 text

Qdrantの詳細 ● APIを持つサービスとして起動 ○ REST API or gRPC API ● フィルタ検索 ○ HNSWを元にした条件付き検索 ■ 文字列、数値、Geo、... ○ should / must / must_not ● 分散システム(シャーディングなど)はTodo ○ https://qdrant.tech/documentation/distributed_deployment/

Slide 9

Slide 9 text

フィルタ付き検索 POST /collections/{collection_name}/points/search { “filter”: { “must”: [ { “key”: “city”, “match”: { “keyword”: “London” } } ] }, “params”: { “hnsw_ef”: 128 }, “vector”: [0.2, 0.1, 0.9, 0.7], “top”: 3 } ● HNSWアルゴリズムをベース としてフィルター可能に変更 例 ● キーcityがLondon のエンティティのみを検索

Slide 10

Slide 10 text

Qdrantのコード (Rust) を読んでみて 良いなと思ったところ 1. 列挙型とパターンマッチ 2. 排他制御

Slide 11

Slide 11 text

列挙型とパターンマッチ ● RustではEnumでさまざまな型のデータを持てる ● 例: インデックスへのUpsertとDelete ○ それぞれ異なる型のデータを統一的に処理 pub enum PointOperations { /// Insert or update points UpsertPoints(PointInsertOperations), /// Delete point if exists DeletePoints { ids: Vec }, } pub(crate) fn process_point_operation(...) -> CollectionResult { match point_operation { PointOperations::DeletePoints { ids, .. } => delete_points(&segments.read(), op_num, &ids), PointOperations::UpsertPoints(operation) => { let (ids, vectors, payloads) = match operation { ... コードの引用: https://github.com/qdrant/qdrant/blob/bf3d8c25753188b4ca5e69a13c7f26e3c383f05b/lib/collection/src/collection_manager/segments_updater.rs#L234-L265

Slide 12

Slide 12 text

排他制御 ● マルチスレッドでインデックスにアクセスする場合は排他制御が必要 ○ 読み込みは同時アクセス OK、書き込みは一度に一つだけ ● Arc> ○ 所有権システムにより、 データ競合を起こせない 、明示的にアンロック不要 ■ (デッドロックは起きうるので注意は必要 ) ● 該当部分抜粋 ○ read()でリードロック ○ write()でライトロック コードの引用: https://github.com/qdrant/qdrant/blob/0f91c9a5e29ef9065c79a20e0ace25be898beff8/lib/collection/src/collection_manager/holders/segment_holder.rs // read let read_segment = segment_arc.read(); f(point, &read_segment); // write let mut write_segment = segment_arc.write(); f(point_id, *idx, &mut write_segment)?

Slide 13

Slide 13 text

おわりに ● Qdrantはきれいに書かれていて色々勉強になる