ベクトルデータベース「Weaviate」を試す 2:複数のセットアップ方式
前回の記事
Weaviateは、オープンソースのベクトルデータベースであり、複数のセットアップ方式が用意されているのでこれを少し見ていく。
Weaviateのセットアップ方式は以下の2点に関連する。
- デプロイメントの選択
- ベクトル化モジュールの選択
デプロイメントの選択については、Weaviateは、Quickstartでも触れた「Weaviate Cloud Services (WCS)」以外に、以下のようなデプロイ方式にも対応している。
-
Embedded Weaviate
- 組み込み型。ローカルでバイナリを動かす。
- ただしExperimental。
-
Docker-Compose
- Dockerイメージ/docker-composeを使ってDockerコンテナを動かす
-
Self-managed Kubernetes
- Kubernetesのクラスタ上にデプロイする
-
Hybrid SaaS
- ここはドキュメントにも記載がないのでよくわからないが、以下と推測している。
- おそらくAWS/GCP/Azureの各MarketPlaceから利用する形態
- もしくは自分のVPC内でフルマネージドのWeaviateを動かす形態
- ここはドキュメントにも記載がないのでよくわからないが、以下と推測している。
ベクトル化モジュールの選択については、Weaviateに登録するベクトルデータをどこでベクトル化するか?ということになる。
- 自分でベクトル化したベクトルをWeaviateに登録する
- Weaviateのベクトル化モジュールを使う
また、ベクトル化については、
- パブリックなAPIベースのものを使うか?
- ローカルでホスティングが必要なものを使うか?
によっても変わってくる。
この2つの選択はそれぞれ、選択できるデプロイメントの種類・選択できるモジュールの種類に影響する、つまり、これらを自分のユースケースを踏まえたうえでチョイスする必要があるということになる。
例えばWeaviateのVectorizerやRerankerには、transformersベースのものやHuggingFaceのモデルを使うモジュールがある。これらを選択したい場合は当然ながらその実行環境が必要になり、以下は選択できない模様。
- WCS(そういう実行環境までは提供していないと思われる)
- Embedded Weaviate(理屈からするとできそうだけど、できないらしいので、Embeddedのバイナリが対応してないとかなのかもしれない)
以下にモジュールとデプロイの組み合わせでの可否がまとまっている(全部ではなさそうだけども)
WCS以外のデプロイ方式について、すこし見てみる。
Embedded Weaviate
ローカルでWeaviateのプロセスが立ち上がるモード。connect_to_embedded
を使ってクライアントを初期化する。初回実行時にバイナリが見つからなければ指定されたバージョンのバイナリがダウンロードされ、自動的に起動する様になっている様子。
Colaboratoryで実行してみた。
import weaviate
import weaviate.classes as wvc
import os
from google.colab import userdata
client = weaviate.connect_to_embedded(
version="1.24.1",
headers={
"X-OpenAI-Api-Key": userdata.get('OPENAI_API_KEY')
},
)
Binary /root/.cache/weaviate-embedded did not exist. Downloading binary from https://github.com/weaviate/weaviate/releases/download/v1.24.1/weaviate-v1.24.1-Linux-amd64.tar.gz
Started /root/.cache/weaviate-embedded: process ID 9647
!ps auxw | grep weaviate
root 9647 0.2 0.4 1335460 57640 ? Sl 07:19 0:00 /root/.cache/weaviate-embedded/weaviate-v1.24.1-2a293e7ef4467d602
root 10511 0.0 0.0 7376 3428 ? S 07:23 0:00 /bin/bash -c ps auxw | grep weaviate
root 10513 0.0 0.0 6484 2336 ? S 07:23 0:00 grep weaviate
プロセスが立ち上がっているのがわかる。
デフォルトでは以下のモジュールが有効になっている様子。
-
generative-openai
- OpenAI Completion APIを使った生成モジュール
-
text2vec-openai
- OpenAI Embedding APIを使ったベクトル化モジュール
-
qna-openai
- OpenAI Embedding/Completion APIを使った回答生成モジュール
- 多分、
text2vec-openai
/generative-openai
を組み合わせてRAGを行うのと同じようなものだと思う
-
ref2vec-centroid
-
text2vec-cohere
- Cohere Embed APIを使ったベクトル化モジュール
-
text2vec-huggingface
- HuggingFaceで提供されているEmbeddingモデルを使ったベクトル化モジュール(だと思う)
これでQuickstartと同じことができるかを試してみた。
faq = client.collections.create(
name="FAQ",
vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(),
generative_config=wvc.config.Configure.Generative.openai(),
)
データセット
!wget https://d.line-scdn.net/stf/linecorp/ja/csr/dataset_.zip
!unzip dataset_.zip
import pandas as pd
df = pd.read_excel("dataset_.xlsx")
df.drop(columns=["ID","サンプルID", "カテゴリ2","出典","<参考>UMカテゴリタグ","<参考>UMサービスメニュー\n(標準的な行政サービス名称)"], inplace=True)
df.rename(columns={
'サンプル 問い合わせ文': 'question',
'サンプル 応答文': 'answer',
'カテゴリ1': 'category',
}, inplace=True)
faq_objs = df.to_dict(orient='records')
faq = client.collections.get("FAQ")
faq.data.insert_many(faq_objs)
query = "妊娠したのですが、どういう手続が必要ですか?"
response = faq.generate.near_text(
query=query,
limit=5,
grouped_task="提供されたコンテキストを踏まえて回答してください。回答は可能な限り丁寧に詳しいものである必要があります。",
)
for r in response.objects:
print("コンテキスト: ", r.properties['answer'].replace("\n",""))
print()
print("回答: ", response.generated)
結果。WCSで実施した場合と同じように使えている。
コンテキスト: 妊娠したら妊娠届を○○課窓口(または支所・出張所窓口)に提出し、母子手帳を受け取ってください。▼詳しくはこちら(自治体HP内関連ページのURL)
コンテキスト: 産前は母子手帳以外の手続きは特にありません。産後に、出生の届出や出生通知書の提出、(自治体が行う出産助成等)の申請をお願いします。
コンテキスト: 母子手帳の申請には診断書はいりませんが、妊娠届に診断を受けた病院名・医師名を記入していただきます。
コンテキスト: 出産後に必要な手続きは出生届・出生通知票の提出、児童手当、子ども医療費助成の申請等があります。▼詳しくはこちら(自治体HP内関連ページのURL)
コンテキスト: 窓口で妊娠届をご記入いただき、母子手帳をお渡しします。住民票の世帯が別の方が代理で窓口に来られる場合は、委任状が必要になります。▼詳しくはこちら(自治体HP内関連ページのURL)
回答: 妊娠した場合、まずは妊娠届を○○課窓口(または支所・出張所窓口)に提出し、母子手帳を受け取る必要があります。母子手帳の申請には診断書は必要ありませんが、妊娠届に診断を受けた病院名・医師名を記入する必要があります。産前には母子手帳以外の特別な手続きは必要ありませんが、産後には出生の届出や出生通知書の提出、出産助成等の申請が必要です。子どもが生まれた後には、出生届・出生通知書の提出や児童手当、子ども医療費助成の申請などが必要です。窓口で妊娠届を記入し、母子手帳を受け取る際には、住民票の世帯が別の方が代理で来られる場合は委任状が必要になります。詳細な手続きや必要書類については、自治体のHP内関連ページをご確認ください。
デフォルトで有効になっていないモジュールを有効化するには、connect_to_embedded()
ではなく、WeaviateClient()
を使う。
一旦プロセスを止める。
client.close()
!ps auxw | grep weaviate
root 16433 0.0 0.0 7376 3484 ? S 07:48 0:00 /bin/bash -c ps auxw | grep weaviate
root 16435 0.0 0.0 6484 2336 ? S 07:48 0:00 grep weaviate
WeaviateClient()
でクライアントを初期化する。ここではbackup-filesystem
というローカルファイルシステムへのバックアップ用のモジュールを追加で組み込んでみる。
import weaviate
from weaviate.embedded import EmbeddedOptions
import weaviate.classes as wvc
import os
client = weaviate.WeaviateClient(
embedded_options=EmbeddedOptions(
additional_env_vars={
"ENABLE_MODULES": "backup-filesystem,text2vec-openai,text2vec-cohere,text2vec-huggingface,ref2vec-centroid,generative-openai,qna-openai",
"BACKUP_FILESYSTEM_PATH": "/content/weaviate-backup"
}
),
additional_headers={
"X-OpenAI-Api-Key": userdata.get('OPENAI_API_KEY')
},
)
WeaviateClient()
の場合は明示的に接続してやる必要がある。
client.connect()
ここでプロセスが立ち上がる。
Binary /root/.cache/weaviate-embedded did not exist. Downloading binary from https://github.com/weaviate/weaviate/releases/download/v1.23.7/weaviate-v1.23.7-Linux-amd64.tar.gz
Started /root/.cache/weaviate-embedded: process ID 19508
Embeddedの場合、初回起動時にデータ領域を自動で作成して、登録したオブジェクトなどのデータはそこに保存されている。XDG_CACHE_HOME
が設定されているかによって変わるが、デフォルトだと~/.local/share/weaviate
になる
!ls -lt ~/.local/share/weaviate
total 140
-rw------- 1 root root 131072 Mar 8 08:01 schema.db
-rw------- 1 root root 32768 Mar 8 08:01 tx.db
-rw------- 1 root root 32768 Mar 8 08:01 classifications.db
drwxr-xr-x 3 root root 4096 Mar 8 07:36 faq
-rw-r--r-- 1 root root 0 Mar 8 07:19 migration1.19.filter2search.skip.flag
-rw-r--r-- 1 root root 0 Mar 8 07:19 migration1.19.filter2search.state
-rw-r--r-- 1 root root 0 Mar 8 07:19 migration1.22.fs.hierarchy
-rw------- 1 root root 16384 Mar 8 07:19 modules.db
先程作ったFAQのデータが存在しているのがわかる。なのでこのままcollectionに対してクエリを投げれば先ほどと同じように使える。
faq = client.collections.get("FAQ")
query = "妊娠したのですが、どういう手続が必要ですか?"
response = faq.generate.near_text(
query=query,
limit=5,
grouped_task="提供されたコンテキストを踏まえて回答してください。回答は可能な限り丁寧に詳しいものである必要があります。",
)
for r in response.objects:
print("コンテキスト: ", r.properties['answer'].replace("\n",""))
print()
print("回答: ", response.generated)
コンテキスト: 妊娠したら妊娠届を○○課窓口(または支所・出張所窓口)に提出し、母子手帳を受け取ってください。▼詳しくはこちら(自治体HP内関連ページのURL)
コンテキスト: 産前は母子手帳以外の手続きは特にありません。産後に、出生の届出や出生通知書の提出、(自治体が行う出産助成等)の申請をお願いします。
コンテキスト: 母子手帳の申請には診断書はいりませんが、妊娠届に診断を受けた病院名・医師名を記入していただきます。
コンテキスト: 出産後に必要な手続きは出生届・出生通知票の提出、児童手当、子ども医療費助成の申請等があります。▼詳しくはこちら(自治体HP内関連ページのURL)
コンテキスト: 窓口で妊娠届をご記入いただき、母子手帳をお渡しします。住民票の世帯が別の方が代理で窓口に来られる場合は、委任状が必要になります。▼詳しくはこちら(自治体HP内関連ページのURL)
回答: 妊娠した場合、まずは妊娠届を○○課窓口(または支所・出張所窓口)に提出し、母子手帳を受け取る必要があります。母子手帳の申請には診断書は必要ありませんが、妊娠届には診断を受けた病院名・医師名を記入する必要があります。産前には母子手帳以外の特別な手続きは必要ありませんが、産後には出生届・出生通知書の提出や出産助成等の申請が必要です。子どもが生まれた後には、さらに出生届・出生通知書の提出や児童手当、子ども医療費助成の申請などが必要になります。窓口で妊娠届を記入し、母子手帳を受け取る際には、住民票の世帯が別の方が代理で来る場合は委任状が必要です。詳細な手続きや必要書類については、自治体のHP内関連ページをご確認ください。
で有効化したバックアップモジュールが使えるようになっているかを確認してみる。バックアップモジュールについてはこちらを参照。
result = client.backup.create(
backup_id="backup-faq-20240308",
backend="filesystem",
include_collections=["FAQ"],
wait_for_completion=True,
)
print(result)
status=<BackupStatus.SUCCESS: 'SUCCESS'> path='/content/weaviate-backup/backup-faq-20240308' collections=['FAQ']
バックアップ先のディレクトリをtreeコマンドで見てみる。(Colaboratoryの標準だとtreeコマンドはないのでapt updat && apt install tree
が必要)
!tree /content/weaviate-backup
/content/weaviate-backup
└── backup-faq-20240308
├── backup_config.json
└── Embedded_at_8079
├── backup.json
└── FAQ
└── chunk-1
3 directories, 3 files
モジュールが有効化されたのが確認できた。
Docker-Compose
WeaviateはDockerイメージでも提供されているので、DockerもしくはDocker-Composeを使って簡単にデプロイできる。
今回はローカルのUbuntu 22.04.2で試してみる。なお、
- Dockerはインストール済
- python仮想環境を作成済
- jupyter-labをインストール済
とする。
シンプルにDockerコンテナを立ち上げるだけなら設定不要で以下で立ち上がる。
$ docker run -p 8080:8080 -p 50051:50051 cr.weaviate.io/semitechnologies/weaviate:1.24.1
$ curl http://localhost:8080
{"links":{"href":"/v1","name":"api v1","documentationHref":"https://weaviate.io/developers/weaviate/current/"}}
ただ、どうせなら、もう少しいろいろ細かく設定したくなると思うので、Docker-Composeでやってみる。docker-compose.yml
を作成する必要があるが、上記のドキュメントサイト内に、質問に答えていくだけでdocker-compose.yml
を生成できるConfiguratorがある。
こんな感じで順番に回答していく。ただし、Configuratorでの設定はそれほど自由度も網羅性もあるわけではない。
- 有効にしても日本語に対応してないモデルしか選択肢に出てこない場合がある
- 特にTransformerベースのモデルをローカルで動かして使いたいような場合、出てくる選択肢は日本語非対応のものしか出てこないが、それだけしか対応していないわけではなさそう。詳細はモジュールリファレンスのドキュメントを参照。
- Configuratorで出てこないモジュールとかもある
なので、一旦回答してみて駄目なら戻ってやり直し、とか、一旦docker-compose.yml
を作成させてあとで自分でカスタマイズ、みたいな感じでやるのが良いと思う。
最後まで回答するとdocker-compose.ymlをダウンロードするコマンドが出力されるので、それを使ってダウンロードする。
今回出力したのはこういう内容だった。OpenAIのVectorizer/Generative、AWSのGenerative,CohereのRerankerを有効にしてみた。
---
version: '3.4'
services:
weaviate:
command:
- --host
- 0.0.0.0
- --port
- '8080'
- --scheme
- http
image: cr.weaviate.io/semitechnologies/weaviate:1.24.1
ports:
- 8080:8080
- 50051:50051
volumes:
- ./weaviate_data:/var/lib/weaviate
restart: on-failure:0
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
DEFAULT_VECTORIZER_MODULE: 'text2vec-openai'
ENABLE_MODULES: 'text2vec-openai,ref2vec-centroid,generative-openai,generative-aws,reranker-cohere'
CLUSTER_HOSTNAME: 'node1'
docker compose upで起動。
$ docker compose up -d
$ curl http://localhost:8080
{"links":{"href":"/v1","name":"api v1","documentationHref":"https://weaviate.io/developers/weaviate/current/"}}
ではJupyter-Labからやってみる。今回の設定では、各APIキーをクライアント側で自分で設定するようにしたので、.envで読み込むようにしたいと思う。
やる内容は前回やった内容と同じなので詳細は割愛。あと、AWSとかCOHEREは設定はしているけどあくまでも例ということで、このあとの流れでは使わない(のでもしかしたら足りない設定があるかもしれないのでご容赦を。)
OPENAI_API_KEY=XXXXXXXXXX
COHERE_API_KEY=XXXXXXXXXX
AWS_ACCESS_KEY_ID=XXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXX
!pip install -U weaviate-client pandas openpyxl python-dotenv
from dotenv import load_dotenv
load_dotenv(verbose=True)
import weaviate
import weaviate.classes as wvc
import os
client = weaviate.connect_to_local(
headers={
"X-OpenAI-Api-Key": os.environ['OPENAI_API_KEY'],
"X-Cohere-Api-Key": os.environ['COHERE_API_KEY'],
"X-AWS-Access-Key": os.environ['AWS_ACCESS_KEY_ID'],
"X-AWS-Secret-Key": os.environ['AWS_SECRET_ACCESS_KEY'],
}
)
faq = client.collections.create(
name="FAQ",
vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(),
generative_config=wvc.config.Configure.Generative.openai(),
)
!wget https://d.line-scdn.net/stf/linecorp/ja/csr/dataset_.zip
!unzip dataset_.zip
import pandas as pd
df = pd.read_excel("dataset_.xlsx")
df.drop(columns=["ID","サンプルID", "カテゴリ2","出典","<参考>UMカテゴリタグ","<参考>UMサービスメニュー\n(標準的な行政サービス名称)"], inplace=True)
df.rename(columns={
'サンプル 問い合わせ文': 'question',
'サンプル 応答文': 'answer',
'カテゴリ1': 'category',
}, inplace=True)
faq_objs = df.to_dict(orient='records')
faq = client.collections.get("FAQ")
faq.data.insert_many(faq_objs)
faq = client.collections.get("FAQ")
query = "妊娠したのですが、どういう手続が必要ですか?"
response = faq.generate.near_text(
query=query,
limit=5,
grouped_task="提供されたコンテキストを踏まえて回答してください。回答は可能な限り丁寧に詳しいものである必要があります。",
)
for r in response.objects:
print("コンテキスト: ", r.properties['answer'].replace("\n",""))
print()
print("回答: ", response.generated)
EmbeddedやDockerならば、オンプレ/セルフホストも可能。OSSなのでメンテができるならコストも抑えれる。またTransformerベースのモジュールが使えるのもメリット。
ただし、
- Web GUIはない
- 繰り返しになるけども、当然ながら自分/自社/自チームでメンテできるならば、という条件はつく。
あたりはユースケースに合わせて考える必要がある。
Kubernetes
Kubernetesクラスタ向けにHelm chartも提供されている。ここは流石に環境作って試すには手間がかかるのでパス。まあある意味dockerでやってるのと同じことだと思うし。
いろいろな形態を試していて気づいたこととして、最新バージョンへの追随というところは抑えておいたほうがいいかもしれない、特にWCSを使う場合。
- WCS
- 基本的には最新版「のみ」が提供される、バージョンを選択することはできない。
- 既存のクラスタは、クラスタ作成時点での最新バージョンで動作し、新しいバージョンが出たら自分でバージョンアップする必要がある
- 新しいバージョンが出ても自動でアップデートはされない、ただし、セキュリティアップデートの場合は自動で行われる(互換性の問題がない限り)
- かならずしも最新バージョンが適用されるとは限らない、例えば、v1.24.1からv1.24.4みたいなアップデートになる場合があるし、新しいバージョンが出てもWCSに適用されるまでには少しタイムラグがある模様。
WCSのクラスタサーババージョンについて
今回ちょっとバグ報告したりしてて最新バージョンの動きを見ていたので、この辺を自分でコントロールしたいみたいな要件がある場合は、Embeddedかdockerがいい気がするね、その分面倒も見ないといけないけども。
あとは、
- WCSで提供されているモジュール以外にどうしても使いたいモジュールがあるか?
- あればWCS以外
- 日本語のキーワード検索をgseで行いたいか?
- gseが必須ならばdockerのみ。
- トリグラムはWCS/Embeddedでも動作する
あたりの制約と各々のユースケースを合わせて決めればいいのではないかな。
次はWeaviateの特徴の1つでもあるスキーマ/collection定義について見ていく。