OceanBaseデータベースは、ベクトル型データの格納、ベクトルインデックス、そしてembeddingベクトル検索機能を提供しています。これにより、ベクトル化したデータをOceanBaseデータベースに保存し、その後の検索処理で利用することが可能になります。
Hugging Faceは、事前に学習済みのモデルやデータセット、ツールを提供するオープンソースの機械学習プラットフォームです。開発者はこれらを利用してAIモデルを簡単に構築・デプロイできます。
前提条件
OceanBaseデータベースV4.4.0以降をデプロイし、MySQLモードのテナントを作成していること。テナントの作成後、以下の手順に従って操作します。
環境に使用可能なMySQLテナント、MySQLデータベース、およびアカウントが存在し、データベースアカウントに読み取り書き込み権限が付与されていること。
Python 3.11以降をインストールしていること。
依存関係のパッケージをインストールしていること。
python3 -m pip install pyobvector sqlalchemy datasets transformers torchベクトル検索機能を有効にするために、テナントで
ob_vector_memory_limit_percentage構成パラメータを設定していることを確認してください。V4.3.5 BP3以前のバージョンでは、値として30を設定することを推奨します。V4.3.5 BP3以降のバージョンでは、デフォルト値の0を維持することを推奨します。この構成パラメータをより正確に設定する必要がある場合は、ob_vector_memory_limit_percentage参照してこの値を計算してください。
ステップ1:データベース接続情報を取得する
OceanBaseデータベースのデプロイ担当者または管理者から、該当するデータベース接続文字列を取得します。例:
obclient -h$host -P$port -u$user_name -p$password -D$database_name
パラメータの説明:
$host:OceanBaseデータベースへの接続IPアドレス。OceanBaseデータベースプロキシ(OceanBase Database Proxy、ODP)接続方式ではODPアドレスを使用し、直接接続方式ではOBServerノードのIPアドレスを使用します。$port:OceanBaseデータベースへの接続ポート。ODP接続方式のデフォルトポートは2883で、ODPデプロイ時にカスタマイズ可能です。直接接続方式のデフォルトポートは2881で、OceanBaseデータベースのデプロイ時にカスタマイズ可能です。$database_name:アクセス対象のデータベース名。注意
テナントに接続するユーザーには、データベースに対する
CREATE、INSERT、DROP、およびSELECT権限が付与されている必要があります。ユーザー権限の詳細については、MySQLモードの権限分類を参照してください。$user_name:テナントの接続アカウント。ODP接続の一般的な形式:ユーザー名@テナント名#クラスタ名またはクラスタ名:テナント名:ユーザー名。直接接続方式の形式:ユーザー名@テナント名。$password:アカウントのパスワード。
その他の接続文字列の詳細については、OBClientを使用したOceanBaseテナントへの接続を参照してください。
ステップ2:AIアシスタントを構築する
環境変数の設定
Hugging Face APIキーを取得し、OceanBase接続情報とともに環境変数に設定します。
export OCEANBASE_DATABASE_URL=YOUR_OCEANBASE_DATABASE_URL
export OCEANBASE_DATABASE_USER=YOUR_OCEANBASE_DATABASE_USER
export OCEANBASE_DATABASE_DB_NAME=YOUR_OCEANBASE_DATABASE_DB_NAME
export OCEANBASE_DATABASE_PASSWORD=YOUR_OCEANBASE_DATABASE_PASSWORD
export HUGGING_FACE_API_KEY=YOUR_HUGGING_FACE_API_KEY
サンプルコードスニペット
データの準備
Hugging Faceはさまざまな埋め込みモデルを提供しており、ユーザーは自分のニーズに応じて対応するモデルを選択して使用できます。 ここでは、sentence-transformers/all-MiniLM-L6-v2を例に挙げ、Hugging Face埋め込みAPIを呼び出してデータを準備します。
import os,shutil,torch,requests
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
from datasets import load_dataset
from sqlalchemy import Column, Integer, String
from pyobvector import ObVecClient, VECTOR, IndexParam, l2_distance
# キャッシュディレクトリを削除
if os.path.exists("./cache"):
shutil.rmtree("./cache")
HUGGING_FACE_API_KEY = os.getenv('HUGGING_FACE_API_KEY')
DATASET = "squad" # HuggingFace Datasetsからのデータセット名
INSERT_RATIO = 0.001 # 挿入されるサンプルデータセットの割合
data = load_dataset(DATASET, split="validation", cache_dir="./cache")
# 固定サブセットを生成します。ランダムサブセットを生成するには、seedを削除してください。
data = data.train_test_split(test_size=INSERT_RATIO, seed=42)["test"]
# データセット内のデータ構造をクリーンアップします。
data = data.map(
lambda val: {"answer": val["answers"]["text"][0]},
remove_columns=["id", "answers", "context"],
)
# HuggingFace API設定
import os
from sentence_transformers import SentenceTransformer
# モデルのダウンロードにHF Mirrorを設定
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
print("モデルのダウンロード中...")
model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
print("モデルのダウンロード完了!")
def encode_text(batch):
questions = batch["question"]
# ローカルモデルを使用して推論を実行
embeddings = model.encode(questions)
# embeddingsをフォーマット
formatted_embeddings = []
for embedding in embeddings:
formatted_embedding = [round(float(val), 6) for val in embedding]
formatted_embeddings.append(formatted_embedding)
batch["embedding"] = formatted_embeddings
return batch
INFERENCE_BATCH_SIZE = 64 # モデル推論のバッチサイズ
data = data.map(encode_text, batched=True, batch_size=INFERENCE_BATCH_SIZE)
data_list = data.to_list()
ベクトルテーブル構造を定義し、ベクトルをOceanBaseに保存する
テキストを格納する title 列、question 列、answer 列、埋め込みベクトルを格納する embedding 列、およびベクトルインデックス情報を含む huggingface_oceanbase_demo_documents という名前のテーブルを作成します。そして、ベクトルデータをOceanBaseに保存します。
OCEANBASE_DATABASE_URL = os.getenv('OCEANBASE_DATABASE_URL')
OCEANBASE_DATABASE_USER = os.getenv('OCEANBASE_DATABASE_USER')
OCEANBASE_DATABASE_DB_NAME = os.getenv('OCEANBASE_DATABASE_DB_NAME')
OCEANBASE_DATABASE_PASSWORD = os.getenv('OCEANBASE_DATABASE_PASSWORD')
client = ObVecClient(uri=OCEANBASE_DATABASE_URL, user=OCEANBASE_DATABASE_USER,password=OCEANBASE_DATABASE_PASSWORD,db_name=OCEANBASE_DATABASE_DB_NAME)
table_name = "huggingface_oceanbase_demo_documents"
client.drop_table_if_exist(table_name)
cols = [
Column("id", Integer, primary_key=True, autoincrement=True),
Column("title", String(255), nullable=False),
Column("question", String(255), nullable=False),
Column("answer", String(255), nullable=False),
Column("embedding", VECTOR(384))
]
# Create vector index
vector_index_params = IndexParam(
index_name="idx_question_embedding",
field_name="embedding",
index_type="HNSW",
distance_metric="l2"
)
client.create_table_with_index_params(
table_name=table_name,
columns=cols,
vidxs=[vector_index_params]
)
print('- Inserting Data to OceanBase...')
client.insert(table_name, data=data_list)
セマンティック検索
Hugging Faceの埋め込みAPIを使用してクエリテキストの埋め込みベクトルを生成し、その後、テキストの埋め込みベクトルに基づいてベクトルテーブル内の各埋め込みベクトルとのL2距離を照会し、最も関連性の高いドキュメントを検索します。
# Step 5. Query the most relevant document based on the query.
questions = {
"question": [
"What is LGM?",
"When did Massachusetts first mandate that children be educated in schools?",
]
}
# Generate question embeddings
question_embeddings = encode_text(questions)["embedding"]
for i, question in enumerate(questions["question"]):
print(f"Question: {question}")
# Search across OceanBase
search_results = client.ann_search(
table_name,
vec_data=question_embeddings[i],
vec_column_name="embedding",
distance_func=l2_distance,
with_dist=True,
topk=3,
output_column_names=["id", "answer", "question"],
)
# Print out results
results_list = list(search_results)
for r in results_list:
print({
"answer": r[1],
"score": r[3] if len(r) > 3 else "N/A",
"original question": r[2],
"id": r[0]
})
print("\n")
期待される結果
- Inserting Data to OceanBase...
Question: What is LGM?
{'answer': 'Last Glacial Maximum', 'score': 0.29572604605808755, 'original question': 'What does LGM stands for?', 'id': 10}
{'answer': 'coordinate the response to the embargo', 'score': 1.2553772660960183, 'original question': 'Why was this short termed organization created?', 'id': 9}
{'answer': '"Reducibility Among Combinatorial Problems"', 'score': 1.2691888905109625, 'original question': 'What is the paper written by Richard Karp in 1972 that ushered in a new era of understanding between intractability and NP-complete problems?', 'id': 11}
Question: When did Massachusetts first mandate that children be educated in schools?
{'answer': '1852', 'score': 0.2408329167590669, 'original question': 'In what year did Massachusetts first require children to be educated in schools?', 'id': 1}
{'answer': 'several regional colleges and universities', 'score': 1.1474774558319025, 'original question': 'In 1890, who did the university decide to team up with?', 'id': 4}
{'answer': '1962', 'score': 1.2703532682776688, 'original question': 'When were stromules discovered?', 'id': 2}