Google Colabで超手軽にベクトル検索を試してみる ― nlp4j-local-search 0.8.0.0
ベクトル検索をちょっと試してみたい。
でも、そのために検索サーバーやベクトルデータベースを構築するのは面倒です。
そこで今回は、Google Colabを開いて、上からPythonコードを1セルずつ実行するだけでベクトル検索を試してみます。
使用するのは nlp4j-local-search 0.8.0.0 です。
- 検索サーバー不要
- Elasticsearch / OpenSearch / Solr不要
- ベクトルDB不要
- Docker不要
- Embeddingを自分で実装する必要なし
- Apache Lucene 10.xによるローカル検索
- インメモリで作って、そのまま捨てることも可能
Google ColabならローカルPCのPython環境を変更する必要もありません。
1. Google Colabを開く
Google Colabで新しいノートブックを作成します。
この記事のコードを、上から順番にセルへ貼り付けて実行していきます。
2. nlp4j-local-searchをインストール
最初のセルです。
!pip install nlp4j-local-search==0.8.0.0
実行します。
次に、Embedding機能をインストールします。
!pip install nlp4j-local-search-embedding
これで準備完了です。
Embeddingにはデフォルトで、
intfloat/multilingual-e5-large
を使用します。
1024次元のEmbeddingモデルです。
初回実行時にはモデルがHugging Faceからダウンロードされます。約2.2GBあるため、モデルを最初に読み込むときは少し時間がかかります。
3. SearchEngineをimportする
次のセルです。
from nlp4j_local_search import SearchEngine
これだけです。
4. 検索する文書を用意する
今回は分かりやすいように、日本の都市についての短い文書を用意します。
documents = [
{"id": "1", "body": "京都は日本の古都です"},
{"id": "2", "body": "東京は日本最大の都市です"},
{"id": "3", "body": "奈良には歴史的な寺院があります"},
{"id": "4", "body": "大阪は食文化で有名な都市です"},
{"id": "5", "body": "北海道は広大な自然が広がっています"},
]
内容を確認してみます。
documents
普通のPythonの list と dict です。
この時点ではベクトルを作っていません。
5. ベクトル検索エンジンを作る
ここがポイントです。
engine = SearchEngine("ja", embedding=True)
embedding=True を指定しています。
これによってEmbeddingモデルがロードされ、テキストを自動的にベクトル化できるようになります。
初回はモデルのダウンロードが発生するため、ここで少し時間がかかる場合があります。
6. まず1文だけ追加してみる
Google Colabなので、細かく実行してみましょう。
engine.add("1", "京都は日本の古都です")
これだけで、テキストからEmbeddingが生成され、検索対象として登録されます。
自分で、
vector = model.encode(...)
のような処理を書く必要はありません。
もちろん、
[0.0123, -0.0345, 0.0678, ...]
のような1024次元のベクトルを自分で管理する必要もありません。
7. 残りの文書も追加する
1件ずつ実行しても構いません。
engine.add("2", "東京は日本最大の都市です")
engine.add("3", "奈良には歴史的な寺院があります")
engine.add("4", "大阪は食文化で有名な都市です")
engine.add("5", "北海道は広大な自然が広がっています")
登録を確定します。
engine.commit()
件数を確認してみましょう。
engine.count()
結果は、
5
となります。
これでベクトル検索の準備ができました。
8. ベクトル検索してみる
では検索してみます。
results = engine.search_semantic("日本の古い都", limit=3)
結果を表示します。
for r in results:
print(r.id, r.body, r.score)
「日本の古い都」という検索に対して、
京都は日本の古都です
のような意味的に近い文書が上位に出てきます。
重要なのは、単純な文字列一致ではないことです。
検索クエリは、
日本の古い都
登録した文書は、
京都は日本の古都です
です。
完全に同じ文字列を指定しなくても、意味的な近さを使って検索できます。
9. 別の言葉でも検索してみる
Google Colabでは、ここから検索文字列だけを変えていろいろ試せます。
例えば、
results = engine.search_semantic("大きな街", limit=3)
for r in results:
print(r.id, r.body, r.score)
次は、
results = engine.search_semantic("お寺や神社", limit=3)
for r in results:
print(r.id, r.body, r.score)
さらに、
results = engine.search_semantic("自然を楽しめる場所", limit=3)
for r in results:
print(r.id, r.body, r.score)
このあたりを自由に変更してみると、キーワード検索との違いが分かりやすいと思います。
10. 普通のキーワード検索もできる
同じ検索エンジンで、通常のキーワード検索もできます。
results = engine.search("京都", limit=5)
for r in results:
print(r.id, r.body, r.score)
つまり、
engine.search("京都")
はキーワード検索。
engine.search_semantic("日本の古い都")
はセマンティック検索です。
検索方法によってAPIを分けています。
11. フィルターを付けてベクトル検索する
ベクトル検索とLuceneの検索条件を組み合わせることもできます。
例えば、検索対象をID 1と2だけに限定してみます。
results = engine.search_semantic(
"都市",
limit=5,
filter_query="id:1 OR id:2",
)
表示してみます。
for r in results:
print(r.id, r.body, r.score)
これによって、
id:1 OR id:2
に一致する文書だけを対象にして、その中から「都市」に意味的に近い文書を検索できます。
実際のデータでは、
メーカー
製品
カテゴリー
日付
国
文書種別
などで対象を絞ってからベクトル検索する、といった使い方が考えられます。
12. 実はApache Lucene 10.xが動いている
ここまでのPythonコードを見ると、
engine.add(...)
engine.search(...)
engine.search_semantic(...)
程度しか書いていません。
しかし、その裏側ではApache Lucene 10.xが検索エンジンとして動いています。
概念的には次のような構成です。
Google Colab
|
Python
|
nlp4j-local-search
|
+---- Embedding
| |
| +---- multilingual-e5-large
|
+---- Apache Lucene 10.x
|
+---- Keyword Search
|
+---- Vector Search
LuceneはPythonとは別の検索サーバーとして動いているわけではありません。
そのため、ElasticsearchやOpenSearchなどのサーバーを別途構築して接続する必要がありません。
13. RAGの検索部分を手軽に試せる
ベクトル検索は、LLMでよく使われる RAG(Retrieval-Augmented Generation) の基本的な構成要素の一つです。
非常に単純化すると、
質問
↓
Embedding
↓
ベクトル検索
↓
関連文書
↓
LLM
↓
回答
という流れになります。
RAGを勉強しようとしても、LLMに到達する前に「ベクトル検索環境をどう作るか」でかなりの準備が必要になることがあります。
nlp4j-local-search では、その検索部分をGoogle Colab上ですぐ試すことができます。
14. 「作って、使って、捨てる」こともできる
nlp4j-local-search はインメモリでも利用できます。
これはGoogle Colabとも相性のよい特徴です。
今回も、
engine = SearchEngine("ja", embedding=True)
として、その場で検索エンジンを作りました。
つまり、
データを用意
↓
検索インデックスを作る
↓
ベクトル検索する
↓
使い終わったら捨てる
という使い方ができます。
検索サーバーを構築して、起動して、管理して、不要になったデータを削除して……という運用を必ずしも必要としません。
例えばAIアプリケーションが処理対象のデータを受け取ったときだけ、一時的な検索インデックスを作ることもできます。
Create → Search → Discard
という使い方です。
15. 最後に終了する
試し終わったら、
engine.close()
で終了できます。
Google Colabのランタイム自体を終了してしまっても、今回作ったインメモリの検索インデックスはそのまま破棄されます。
最小コードにするとこれだけ
ここまで理解できたら、まとめて書くこともできます。
from nlp4j_local_search import SearchEngine
with SearchEngine("ja", embedding=True) as engine:
engine.add("1", "京都は日本の古都です")
engine.add("2", "東京は日本最大の都市です")
engine.add("3", "奈良には歴史的な寺院があります")
engine.commit()
for r in engine.search_semantic("日本の古い都", limit=3):
print(r.body)
ベクトル検索のコードなのに、コード上にはベクトルが一度も登場しません。
これが今回やりたかったことです。
まとめ
Google Colabを使えば、
!pip install nlp4j-local-search==0.8.0.0
!pip install nlp4j-local-search-embedding
から始めて、
engine = SearchEngine("ja", embedding=True)
engine.add("1", "京都は日本の古都です")
engine.commit()
engine.search_semantic("日本の古い都")
と、本当に1ステップずつベクトル検索の動作を試すことができます。
ベクトルDBの構築も、検索サーバーの管理も、Embedding処理の実装も必要ありません。
Google Colabのランタイムを終了すれば、作ったインメモリの検索環境もそのまま捨てられます。
「ベクトル検索って何だろう?」
「RAGの検索部分だけ、まず動かしてみたい」
「Luceneのベクトル検索をPythonから試してみたい」
という場合の、小さな実験環境として使えると思います。
作る → 検索する → 捨てる。
まずはGoogle Colabで、気軽にベクトル検索を試してみてください。
Links
PyPI:
GitHub: