セマンティック検索のウォークスルー
OCI SearchをOpenSearchとともに使用してデータのセマンティック検索を実行する方法について学習します。
前提条件
開始する前に、次の手順を実行する必要があります。
-
OpenSearchを使用して、OCI Searchでサポートされている事前トレーニング済モデルのいずれかを選択します
-
OpenSearchクラスタがバージョン2.11であることを確認します。
-
セマンティック検索を実行するようにクラスタ設定を更新します。次のコマンド例では、適用可能な設定を更新します。
PUT _cluster/settings { "persistent": { "plugins": { "ml_commons": { "only_run_on_ml_node": "false", "model_access_control_enabled": "true", "native_memory_threshold": "99", "rag_pipeline_feature_enabled": "true", "memory_feature_enabled": "true", "allow_registering_model_via_local_file": "true", "allow_registering_model_via_url": "true", "model_auto_redeploy.enable":"true", "model_auto_redeploy.lifetime_retry_times": 10 } } } }
ステップ2: モデル・グループの登録
モデル・グループを使用して、モデルを論理的にグループ化し、誰がモデルにアクセスできるかを制御します。次の例に示すように、モデル・グループAPIのregister操作を使用してモデル・グループを登録します。
POST /_plugins/_ml/model_groups/_register
{
"name": "general pretrained models",
"description": "A model group for pretrained models hosted by OCI Search with OpenSearch"
}
レスポンスで返されたmodel_group_id
を書き留めます。
{
"model_group_id": "<model_group_ID>",
"status": "CREATED"
}
ステップ3: モデルの登録およびデプロイ
セマンティック検索には、次の3つのオプションがあります。
-
オプション1: OpenSearch事前トレーニング済モデルの使用で説明されているステップを使用して、OCI Searchでホストされている事前トレーニング済モデルをOpenSearchに登録してデプロイします。このオプションは最も簡単に使用でき、追加のIAMポリシーを構成する必要はなく、ペイロードは次のオプションのペイロードほど複雑ではありません。
-
オプション2: カスタム・モデルで説明されているステップを使用して、OpenSearch事前トレーニング済モデルをインポート、登録およびデプロイします。これには、オブジェクト・ストレージ・バケットへのモデル・ファイルのアップロード、およびモデルの登録時のモデル・ファイルのオブジェクト・ストレージURLの指定が含まれます。
-
オプション3:
cohere.embed-english-v3.0 model
などのリモート埋込みモデルを登録する生成AIコネクタを作成します。詳細は、OCI生成AIを使用した会話型検索を参照してください。
model_group_id
: ステップ1を完了した場合、これは_registerリクエストに対するmodel_group_idの値です。name
: 使用する事前トレーニング済モデルのモデル名。version
: 使用する事前トレーニング済モデルのバージョン番号。model_format
: モデルの形式(TORCH_SCRIPT
またはONNX
)。
次の例に示すように、モデルAPIから登録操作を使用してモデルを登録します。
POST /_plugins/_ml/models/_register
{
"name": "huggingface/sentence-transformers/msmarco-distilbert-base-tas-b",
"version": "1.0.2",
"model_group_id": "<model_group_ID>",
"model_format": "TORCH_SCRIPT"
}
レスポンスで返されたtask_id
をノートにとり、task_id
を使用して操作のステータスを確認できます。
たとえば、次のレスポンスから:
{
"task_id": "<task_ID>",
"status": "CREATED"
}
登録操作のステータスを確認するには、次の例に示すように、タスクAPIのGet操作でtask_id
を使用します。
GET /_plugins/_ml/tasks/<task_ID>
登録操作が完了すると、次の例に示すように、取得操作に対するレスポンスのstate
値はCOMPLETED
になります。
{
"model_id": "<embedding_model_ID>",
"task_type": "REGISTER_MODEL",
"function_name": "TEXT_EMBEDDING",
"state": "COMPLETED",
"worker_node": [
"f2b_8-mVRVyVqeKqsA7dcQ"
],
"create_time": 1706831015570,
"last_update_time": 1706831070740,
"is_async": true
}
モデルのデプロイ時に使用するレスポンスで返されるmodel_ID
値をノートにとります。
ステップ4: モデルのデプロイ
モデルの登録操作が完了したら、次の例に示すように、モデルAPIのデプロイ操作を使用してモデルをクラスタにデプロイし、前のステップのGet操作レスポンスからmodel_ID
を渡すことができます。
POST /_plugins/_ml/models/<embedding_model_ID>/_deploy
レスポンスで返されたtask_id
をノートにとり、task_id
を使用して操作のステータスを確認できます。
たとえば、次のレスポンスから:
{
"task_id": "<task_ID>",
"task_type": "DEPLOY_MODEL",
"status": "CREATED"
}
登録操作のステータスを確認するには、次の例に示すように、「タスクAPI」の「取得」操作でtask_ID
を使用します。
GET /_plugins/_ml/tasks/<task_ID>
デプロイ操作が完了すると、取得操作に対するレスポンスのstatus
値はCOMPLETED
になります。
ステップ5: k-NN取込みパイプラインの作成
デプロイ操作が完了したら、デプロイ済モデルを使用して取込みパイプラインを作成します。取込みパイプラインは、デプロイ済モデルを使用して、取込み時に各ドキュメントの埋込みベクトルを自動的に生成します。プロセッサは埋め込みのすべてを処理するため、埋め込みに変換されるドキュメント内の必要なテキストフィールドを適切にマッピングするだけで済みます。次の例は、取込みパイプラインの作成を示しています:
PUT _ingest/pipeline/<pipeline_name>
{
"description": "An example neural search pipeline",
"processors" : [
{
"text_embedding": {
"model_id": "<embedding_model_ID>",
"field_map": {
"<text_field_name>": "<embedding_field_name>"
}
}
}
]
}
取込みパイプラインが作成された場合は、次のレスポンスが返されます:
{
"acknowledged": true
}
ステップ6: 索引の作成
前のステップで作成した取込みパイプラインを使用して索引を作成します。索引で使用可能な任意のANNエンジンを使用できます。次の例では、Luceneエンジンを使用します。
PUT /lucene-index
{
"settings": {
"index.knn": true,
"default_pipeline": "<pipeline_name>"
},
"mappings": {
"properties": {
"<embedding_field_name>": {
"type": "knn_vector",
"dimension": <model_dimension>,
"method": {
"name":"hnsw",
"engine":"lucene",
"space_type": "l2",
"parameters":{
"m":512,
"ef_construction": 245
}
}
},
"<text_field_name>": {
"type": "text"
}
}
}
}
索引の作成のpassage_text
フィールドは、取込みパイプラインのpassage_text
フィールドと一致します。これにより、パイプラインは埋込みの作成方法を認識し、取込み時にドキュメントにマップできるようになります。
使用するエンジンおよびそれらのエンジンで使用可能な構成パラメータの選択に役立つようにするには、k-NN indexおよびApproximate k-NN searchを参照してください。
次に、索引作成を成功させるためのレスポンスの例を示します。
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "lucene-index"
}
ステップ7: 索引へのドキュメントの取込み
次の例に示すように、索引にデータを取り込みます。
POST /lucene-index/_doc/1
{
"<text_field_name>": "there are many sharks in the ocean"
}
POST /lucene-index/_doc/2
{
"<text_field_name>": "fishes must love swimming"
}
POST /lucene-index/_doc/3
{
"<text_field_name>": "summers are usually very hot"
}
POST /lucene-index/_doc/4
{
"<text_field_name>": "florida has a nice weather all year round"
}
レスポンス:
# POST /lucene-index/_doc/1
{
"_index": "lucene-index",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
# POST /lucene-index/_doc/2
{
"_index": "lucene-index",
"_id": "2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
# POST /lucene-index/_doc/3
{
"_index": "lucene-index",
"_id": "3",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
# POST /lucene-index/_doc/4
{
"_index": "lucene-index",
"_id": "4",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
次の手順を実行して、いずれかのドキュメントをチェックし、埋込みが正しく生成されることを確認します。
GET /lucene-index/_doc/3
レスポンス:
{
"_index": "lucene-index",
"_id": "3",
"_version": 1,
"_seq_no": 2,
"_primary_term": 1,
"found": true,
"_source": {
"<embedding_field_list>": [
-0.1254959,
-0.3151774,
0.03526799,
0.39322096,
-0.0475556,
-0.12378334,
-0.032554347,
0.4033895,
0.050718695,
-0.3587931,
0.097042784,
0.11742551,
-0.06573639,
0.14252506,
-0.466573,
0.56093556,
-0.2815812,
-0.00016521096,
-0.2858566,
意味検索の実行
ハイブリッド検索を使用したセマンティック検索
「ステップ3: モデルの登録およびデプロイ」のモデルIDを使用して、次のようにハイブリッド検索を使用してセマンティック検索を実行します:
GET lucene-index/_search
{
"query": {
"bool" : {
"should" : [
{
"script_score": {
"query": {
"neural": {
"<embedding_field_name>": {
"query_text": "what are temperatures in miami like",
"model_id": "<embedding_model_ID>",
"k": 2
}
}
},
"script": {
"source": "_score * 1.5"
}
}
}
]
}
},
"fields": [
"<text_field_name>"
],
"_source": false
}
レスポンス:
{
"took": 343,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0.032253794,
"hits": [
{
"_index": "lucene-index",
"_id": "4",
"_score": 0.032253794,
"fields": {
"<text_field_name>": [
"florida has a nice weather all year round"
]
}
},
{
"_index": "lucene-index",
"_id": "3",
"_score": 0.031487543,
"fields": {
"<text_field_name>": [
"summers are usually very hot"
]
}
}
]
}
}
問合せの質問にFlorida、weatherおよびsummerは記述されていないことがわかりますが、モデルはtemperaturesおよびmiamiのセマンティックな意味を推測して最も関連性の高い回答を返します。
ニューラル検索を使用したセマンティック検索
「ステップ3: モデルの登録およびデプロイ」のモデルIDを使用して、次のようにニューラル検索を使用してセマンティック検索を実行します:
GET /lucene-index/_search
{
"_source": {
"excludes": [
"<embedding_field_name>"
]
},
"query": {
"neural": {
"<embedding_field_name>": {
"query_text": "good climate",
"model_id": "<embedding_model_ID>",
"k": 5
}
}
}
}
レスポンス:
{
"took": 36,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4,
"relation": "eq"
},
"max_score": 0.012060728,
"hits": [
{
"_index": "lucene-index",
"_id": "4",
"_score": 0.012060728,
"_source": {
"passage_text": "florida has a nice weather all year round"
}
},
{
"_index": "lucene-index",
"_id": "1",
"_score": 0.010120423,
"_source": {
"passage_text": "there are many sharks in the ocean"
}
},
{
"_index": "lucene-index",
"_id": "3",
"_score": 0.00985171,
"_source": {
"passage_text": "summers are usually very hot"
}
},
{
"_index": "lucene-index",
"_id": "2",
"_score": 0.009575767,
"_source": {
"passage_text": "fishes must love swimming"
}
}
]
}
}