Amazon Web Services ブログ

Amazon SageMaker と Amazon OpenSearch Service を使って CLIP モデルによるテキストと画像の統合検索システムを実装する

テキスト検索とセマンティック検索エンジンの台頭により、eコマースや小売業は消費者にとってより簡単に検索できるようになりました。検索する際にテキストと画像の両方をクエリに含むことができる検索エンジンは、検索ソリューションの柔軟性を非常に高めることができます。たとえば、ラップトップに何百もの家族の写真が入ったフォルダがあり、あなたとあなたの親友が古い家のプールの前にいたときに撮った写真をすぐに見つけたいという場合を仮定します。この場合、「プールの前に二人が立っている」のような会話形式の文章をクエリとして入力することで、テキストと画像の統合検索エンジンからお目当ての画像を検索することができます。画像タイトルに適切なキーワードを入力しなくても、クエリを実行できます。
現在、Amazon OpenSearch Service は k-NN インデックスのコサイン類似度メトリクスをサポートしています。コサイン類似度は、2 つのベクトル間の角度のコサインを測定します。コサイン角度が小さいほど、ベクトル間の類似性が高くなります。コサイン類似度を使用すると、2 つのベクトル間の向きを測定できるため、特定のセマンティック検索アプリケーションに適しています。

Contrastive Language-Image Pre-Training (CLIP) は、さまざまな画像とテキストのペアでトレーニングされたニューラルネットワークです。CLIP ニューラルネットワークは、画像とテキストの両方を同じ潜在空間に投影できるため、コサイン類似度などの類似度尺度を使用して画像とテキストを比較することができます。CLIP を使用して製品の画像や説明をEmbeddingエンコードし、OpenSearch Service k-NN インデックスに保存することができます。そうすれば、顧客はインデックスをクエリして、関心のある商品を検索できます。

Amazon SageMaker を用いることで、上記の CLIP をエンコーディングさせるために利用することができます。Amazon SageMaker Serverless Inference は、機械学習 (ML) モデルのデプロイとスケーリングを容易にする専用の推論サービスです。SageMaker を使用すると、開発とテスト用にサーバーレスをデプロイし、本番環境ではリアルタイム推論に移行することも可能です。Amazon SageMaker Serveless Inference では、アイドル時にインフラストラクチャを 0 にスケールダウンすることでコストを節約できます。これは、開発サイクル間のアイドル時間が長い POC を構築する場合に最適です。この他、Amazon SageMaker Batch transform を使用して、大規模なデータセットから推論を取得することもできます。
この記事では、SageMakerとOpenSearch ServiceでCLIPを使用して検索アプリケーションを構築する方法を示します。コードはオープンソースで、こちらのGitHubでホストされています。

ソリューション概要

Amazon OpenSearch Service は、テキストマッチングとEmbedding k-NN 検索を提供します。このソリューションでは Embedding k-NN 検索を利用します。画像とテキストの両方をクエリとして使用して、インベントリからアイテムを検索できます。この統合画像およびテキスト検索アプリケーションの実装は、次の 2 つのフェーズで構成されます。

  • k-NN 参照インデックス — このフェーズでは、一連のコーパスドキュメントまたは製品画像をCLIPモデルに渡して、それらを Embedding にエンコードします。テキストと画像の Embedding は、それぞれコーパスまたは画像の数値表現を表します。これらの Embedding を OpenSearch Service の k-NN インデックスに保存します。k-NNを支える概念は、Embedding 空間に類似したデータポイントが近接して存在するというものです。たとえば、「赤い花」、「バラ」といったテキストと赤いバラの画像は似ているため、これらのテキストと画像の Embedding は Embedding 空間内で互いに近接しています。
  • k-NN インデックスクエリ — これはアプリケーションの推論フェーズです。このフェーズでは、ディープラーニングモデル (CLIP) を介してテキスト検索クエリまたは画像検索クエリを送信し、Embedding にエンコードします。次に、それらの Embedding を使用して、OpenSearch Service に保存されている参照 k-NN インデックスをクエリします。k-NN インデックスは、emnbedding 空間から同様の Embedding を返します。たとえば、「赤い花」のテキストを渡すと、赤いバラの画像の Embedding が類似のアイテムとして返されます。

以下の画像はソリューションのアーキテクチャ図です。


以下がワークフローとなります。

  1. 事前学習済みの CLIP モデルからバッチ推論とリアルタイム推論用の SageMaker モデルをデプロイします。
  2. SageMaker batch transform ジョブを使用して製品画像の Embedding を生成します。
  3. SageMaker Serverless Inference を使用して、クエリ画像とテキストを Embedding へリアルタイムにエンコードします。
  4. Amazon Simple Storage Service (Amazon S3) を使用して、未加工のテキスト (製品説明) と画像 (製品画像)、および SageMaker batch transform ジョブによって生成された画像の Embedding を保存します。
  5. OpenSearch Service を検索エンジンとして使用して、Embeddingを保存し、類似のEmbeddingを検索します。
  6. クエリ関数を使用してクエリのエンコーディングを調整し、k-NN 検索を実行します。

このソリューションを開発するための統合開発環境 (IDE) として、Amazon SageMaker Studio Notebooks を使用しています(アーキテクチャ図には示されていません) 。

ソリューションのセットアップ

ソリューションをセットアップするには、次の手順を実行します。

  1. SageMaker ドメインとユーザープロファイルを作成します。手順については、「クイックセットアップを使用して Amazon SageMaker ドメインにオンボーディングする」を参照してください。
  2. OpenSearch Service ドメインを作成します。手順については、「Amazon OpenSearch サービスドメインの作成と管理」を参照してください。

上記の手順以外にも、GitHub の記載内容に従い AWS CloudFormation テンプレートを使用してドメインを作成することも可能です。現状このテンプレートではインターネット経由で接続する形になっていますが、VPC のインターフェイスエンドポイントを使用して、Amazon 仮想プライベートクラウド (Amazon VPC) から SageMaker Studio を Amazon S3 に接続することが可能です。VPC エンドポイント (インターフェイスエンドポイント) を使用することで、VPC と SageMaker Studio 間の通信は AWS ネットワーク内で完全かつ安全に行われます。SageMaker Studio Notebookは、プライベート VPC 経由で OpenSearch Serviceに接続できるため、通信のセキュリティが確保されます。OpenSearch Service ドメインは、保存中のデータを暗号化します。これは、データへの不正アクセスを防ぐのに役立つセキュリティ機能です。ノード間暗号化は、OpenSearch Service のデフォルト機能に加えてセキュリティをさらに強化します。Amazon S3 は、別の暗号化オプションを指定しない限り、新しいオブジェクトごとにサーバー側の暗号化 (SSE-S3) を自動的に適用します。
OpenSearch Service ドメインでは、アイデンティティベースのポリシーをアタッチして、サービスにアクセスできるユーザー、実行できるアクション、および該当する場合はそれらのアクションを実行できるリソースを定義できます。

イメージとテキストのペアを Embedding にエンコードする

このセクションでは、画像とテキストを埋め込みにエンコードする方法について説明します。ここでは、データの準備、SageMaker でのモデルの作成、およびモデルを使用した Batch Transformの実行が含まれます。

(訳者追記:本記事にかかれているコードの内容はすべてこちらの GitHub リポジトリの blog_clip.ipynb に記載されています。SageMaker Studio にGit リポジトリをクローンする方法についてはこちらのドキュメントを参照してください。 )

データの概要と準備方法

Python 3 (データサイエンス) カーネルを搭載した SageMaker Studio ノートブックを使用してサンプルコードを実行できます。
この記事では、Amazon Berkeley Object Dataset を使用します。このデータセットは、多言語メタデータと 398,212 枚のユニークなカタログ画像を含む 147,702 個の製品リストのコレクションです。商品の画像と商品名は US 英語でのみ使用しています。デモ用には、約 1,600 個の製品を使用しています。このデータセットの詳細については、README を参照してください。データセットは外部公開されているパブリックな S3 バケットでホストされています。Amazon商品の商品説明とメタデータを含む16個のファイルのフォーマットは listings/metadata/listings_<i>.json.gz となっています。

このデモでは、最初のメタデータファイルを使用します。Pandas を使用してメタデータを読み込み、データフレームから US 英語のタイトルを含む製品を選択します。Pandasは、Pythonプログラミング言語上に構築されたオープンソースのデータ分析および操作ツールです。main_image_id という属性を使用してイメージを識別します。次のコードを参照してください。


meta = pd.read_json("s3://amazon-berkeley-objects/listings/metadata/listings_0.json.gz", lines=True)
def func_(x):
    us_texts = [item["value"] for item in x if item["language_tag"] == "en_US"]
    return us_texts[0] if us_texts else None
 
meta = meta.assign(item_name_in_en_us=meta.item_name.apply(func_))
meta = meta[~meta.item_name_in_en_us.isna()][["item_id", "item_name_in_en_us", "main_image_id"]]
print(f"#products with US English title: {len(meta)}")
meta.head()

データフレームには 1,639 個の製品があります。次に、アイテム名と画像を結合させます。images/metadata/images.csv.gz には画像のメタデータが含まれています。このファイルは gzip で圧縮された CSV ファイルで、image_id, height, width, path 列があります。メタデータファイルを読み込んで、それをアイテムメタデータと結合してみましょう。次のコードを参照してください。


image_meta = pd.read_csv("s3://amazon-berkeley-objects/images/metadata/images.csv.gz")
dataset = meta.merge(image_meta, left_on="main_image_id", right_on="image_id") 
dataset.head()


SageMaker Studio Notebook の Python 3 カーネルに組み込まれている PIL library を使用して、データセットのサンプル画像を表示してみましょう。

from sagemaker.s3 import S3Downloader as s3down
from pathlib import Path
from PIL import Image
 
def get_image_from_item_id(item_id = "B0896LJNLH", return_image=True):
    s3_data_root = "s3://amazon-berkeley-objects/images/small/"
 
    item_idx = dataset.query(f"item_id == '{item_id}'").index[0]
    s3_path = dataset.iloc[item_idx].path
    local_data_root = f'./data/images'
    local_file_name = Path(s3_path).name
 
    s3down.download(f'{s3_data_root}{s3_path}', local_data_root)
 
    local_image_path = f"{local_data_root}/{local_file_name}"
    if return_image:
        img = Image.open(local_image_path)
        return img, dataset.iloc[item_idx].item_name_in_en_us
    else:
        return local_image_path, dataset.iloc[item_idx].item_name_in_en_us
image, item_name = get_image_from_item_id()
print(item_name)
image

モデルの準備

次に、事前学習済みの CLIP モデルから SageMaker で推論用にモデルをデプロイします。最初のステップは、事前学習済みのモデルの重みファイルをダウンロードして model.tar.gz ファイルに入れ、S3 バケットにアップロードすることです。事前学習済みモデルのパスは CLIP リポジトリにあります。このデモでは、事前学習済みの ResNet-50 (RN50) モデルを使用します。次のコードを参照してください。

%%writefile build_model_tar.sh 
#!/bin/bash 
MODEL_NAME=RN50.pt 
MODEL_NAME_URL=https://openaipublic.azureedge.net/clip/models/afeb0e10f9e5a86da6080e35cf09123aca3b358a0c3e3b6c78a7b63bc04b6762/RN50.pt 

BUILD_ROOT=/tmp/model_path 
S3_PATH=s3:////model.tar.gz 

rm -rf $BUILD_ROOT 
mkdir $BUILD_ROOT 
cd $BUILD_ROOT && curl -o $BUILD_ROOT/$MODEL_NAME $MODEL_NAME_URL 
cd $BUILD_ROOT && tar -czvf model.tar.gz . 
aws s3 cp $BUILD_ROOT/model.tar.gz $S3_PATH 
!bash build_model_tar.sh

その後、推論の entry point として指定する CLIP モデルのスクリプトを提供する必要があります。 CLIP は PyTorch を使って実装されているため、SageMaker PyTorch Framework を使用します。 PyTorch はオープンソースの ML フレームワークで、研究プロトタイピングから本番デプロイまでのパスを加速させます。 SageMaker で PyTorch モデルをデプロイする方法については、Deploy PyTorch Models を参照してください。 推論コードは、MODEL_NAME ENCODE_TYPE の2つの環境変数を受け入れます。 これにより、異なる CLIP モデルを簡単に切り替えることができます。 ENCODE_TYPE を使用して、イメージまたはテキストのどちらをエンコードするかを指定します。ここでは、デフォルトの PyTorch 推論ハンドラをオーバーライドするために、model_fninput_fnpredict_fnoutput_fn 関数を実装します。 次のコードを参照してください:

!mkdir -p code
%%writefile code/clip_inference.py
 
import io
import torch
import clip
from PIL import Image
import json
import logging
import sys
import os
 
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision.transforms import ToTensor
 
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler(sys.stdout))
 
MODEL_NAME = os.environ.get("MODEL_NAME", "RN50.pt")
# ENCODE_TYPE could be IMAGE or TEXT
ENCODE_TYPE = os.environ.get("ENCODE_TYPE", "TEXT")
 
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
# defining model and loading weights to it.
def model_fn(model_dir):
    model, preprocess = clip.load(os.path.join(model_dir, MODEL_NAME), device=device)
    return {"model_obj": model, "preprocess_fn": preprocess}
 
def load_from_bytearray(request_body):
    
    return image
 
# data loading
def input_fn(request_body, request_content_type):
    assert request_content_type in (
        "application/json",
        "application/x-image",
    ), f"{request_content_type} is an unknown type."
    if request_content_type == "application/json":
        data = json.loads(request_body)["inputs"]
    elif request_content_type == "application/x-image":
        image_as_bytes = io.BytesIO(request_body)
        data = Image.open(image_as_bytes)
    return data
 
# inference
def predict_fn(input_object, model):
    model_obj = model["model_obj"]
    # for image preprocessing
    preprocess_fn = model["preprocess_fn"]
    assert ENCODE_TYPE in ("TEXT", "IMAGE"), f"{ENCODE_TYPE} is an unknown encode type."
 
    # preprocessing
    if ENCODE_TYPE == "TEXT":
        input_ = clip.tokenize(input_object).to(device)
    elif ENCODE_TYPE == "IMAGE":
        input_ = preprocess_fn(input_object).unsqueeze(0).to(device)
 
    # inference
    with torch.no_grad():
        if ENCODE_TYPE == "TEXT":
            prediction = model_obj.encode_text(input_)
        elif ENCODE_TYPE == "IMAGE":
            prediction = model_obj.encode_image(input_)
    return prediction
  
# Serialize the prediction result into the desired response content type
def output_fn(predictions, content_type):
    assert content_type == "application/json"
    res = predictions.cpu().numpy().tolist()
return json.dumps(res)

このソリューションでは、モデルの推論時に追加の Python パッケージが必要です。そのため、SageMaker がモデルをホスティングする際に追加のパッケージをインストールできるように、requirements.txt ファイルを提供することができます。

%%writefile code/requirements.txt 
ftfy 
regex 
tqdm 
git+https://github.com/openai/CLIP.git

PyTorchModel クラスを使用すると、モデル成果物のAmazon S3 の場所と推論エントリポイントの詳細を含むオブジェクトを作成できます。このオブジェクトを使用して、Batch Transform ジョブを作成したり、エンドポイントにデプロイしてオンライン推論を行うことができます。以下のコードを参照してください:

from sagemaker.pytorch import PyTorchModel
from sagemaker import get_execution_role, Session
 
role = get_execution_role()
shared_params = dict(
    entry_point="clip_inference.py",
    source_dir="code",
    role=role,
    model_data="s3://<your-bucket>/<your-prefix-for-model>/model.tar.gz",
    framework_version="1.9.0",
    py_version="py38",
)
 
clip_image_model = PyTorchModel(
    env={'MODEL_NAME': 'RN50.pt', "ENCODE_TYPE": "IMAGE"},
    name="clip-image-model",
    **shared_params
)
 
clip_text_model = PyTorchModel(
    env={'MODEL_NAME': 'RN50.pt', "ENCODE_TYPE": "TEXT"},
    name="clip-text-model",
    **shared_params
)

アイテム画像を Embeding にエンコードするための Batch Transform を実行する

次に、CLIP モデルを使用して、アイテム画像を Embedding にエンコードし、SageMaker Batch Transformを使用してバッチ推論を実行します。 ジョブを作成する前に、以下のコードスニペットを使用して、Amazon Berkeley Objects Dataset のパブリック S3 バケットからアイテム画像を自分のバケットにコピーします。この操作は10分未満で終了します。

from multiprocessing.pool import ThreadPool 
import boto3 
from tqdm import tqdm 
from urllib.parse import urlparse 
s3_sample_image_root = "s3://<your-bucket>/<your-prefix-for-sample-images>" 
s3_data_root = "s3://amazon-berkeley-objects/images/small/" 
client = boto3.client('s3')

def upload_(args): 
    client.copy_object(CopySource=args["source"], Bucket=args["target_bucket"], Key=args["target_key"]) 
  
arugments = []
for idx, record in dataset.iterrows(): 
    argument = {} 
    argument["source"] = (s3_data_root + record.path)[5:] 
    argument["target_bucket"] = urlparse(s3_sample_image_root).netloc 
    argument["target_key"] = urlparse(s3_sample_image_root).path[1:] + record.path 
    arugments.append(argument)
    
with ThreadPool(4) as p: 
    r = list(tqdm(p.imap(upload_, arugments), total=len(dataset)))

次に、バッチ処理でアイテム画像の推論を行います。SageMakerの Batch Transform ジョブは、Amazon S3 の入力用ディレクトリに格納されているすべての画像をエンコードするためにCLIPモデルを使用し、出力された Embedding を出力S3フォルダにアップロードします。 このジョブには約10分かかります。

batch_input = s3_sample_image_root + "/"
output_path = f"s3://<your-bucket>/inference/output"
 
clip_image_transformer = clip_image_model.transformer(
    instance_count=1,
    instance_type="ml.c5.xlarge",
    strategy="SingleRecord",
    output_path=output_path,
)
 
clip_image_transformer.transform(
    batch_input, 
    data_type="S3Prefix",
    content_type="application/x-image", 
    wait=True,
)

Amazon S3 から Embeddings を変数に読み込み、後ほどOpenSearch Service にデータを格納できるようにします。

embedding_root_path = "./data/embedding"
s3down.download(output_path, embedding_root_path)
 
embeddings = []
for idx, record in dataset.iterrows():
    embedding_file = f"{embedding_root_path}/{record.path}.out"
    embeddings.append(json.load(open(embedding_file))[0])

機械学習を活用した統合検索エンジンを作成する

このセクションでは、Embeddings を使用した k-NN 検索を実行する検索エンジンの作成方法について説明します。これには、OpenSearch Serviceクラスターの構成、Embeddings の取り込み、フリーテキストおよび画像検索クエリの実行が含まれます。

k-NN を使って OpenSeach Service ドメインを設定する

以前に、OpenSearch クラスターを作成しました。ここでは、カタログデータと埋め込みを格納するためのインデックスを作成します。インデックス設定を次の構成で構成して、k-NN機能を有効にできます。

index_settings = {
  "settings": {
    "index.knn": True,
    "index.knn.space_type": "cosinesimil"
  },
  "mappings": {
    "properties": {
      "embeddings": {
        "type": "knn_vector",
        "dimension": 1024 #Make sure this is the size of the embeddings you generated, for RN50, it is 1024
      }
    }
  }
}

この例では、Python Elasticsearch Client を使用してOpenSearch クラスターと通信し、データをホストするインデックスを作成します。 ノートブックで %pip install elasticsearch を実行してライブラリをインストールできます。 次のコードを参照してください:

import boto3
import json
from requests_aws4auth import AWS4Auth
from elasticsearch import Elasticsearch, RequestsHttpConnection
 
def get_es_client(host = "<your-opensearch-service-domain-url>",
    port = 443,
    region = "<your-region>",
    index_name = "clip-index"):
 
    credentials = boto3.Session().get_credentials()
    awsauth = AWS4Auth(credentials.access_key,
                       credentials.secret_key,
                       region,
                       'es',
                       session_token=credentials.token)
 
    headers = {"Content-Type": "application/json"}
 
    es = Elasticsearch(hosts=[{'host': host, 'port': port}],
                       http_auth=awsauth,
                       use_ssl=True,
                       verify_certs=True,
                       connection_class=RequestsHttpConnection,
                       timeout=60 # for connection timeout errors
    )
    return es
es = get_es_client()
es.indices.create(index=index_name, body=json.dumps(index_settings))

画像の Embedding データを OpenSearch Service へ投入する

ここで、データセットをループ処理して、クラスターにアイテムデータを取り込みます。 この練習のためのデータ取り込みは60秒以内に完了するはずです。 また、データがインデックスに正常に取り込まれたことを確認するためのシンプルなクエリを実行します。 次のコードを参照してください:

# ingest_data_into_es
for idx, record in tqdm(dataset.iterrows(), total=len(dataset)):
    body = record[['item_name_in_en_us']].to_dict()
    body['embeddings'] = embeddings[idx]
    es.index(index=index_name, id=record.item_id, doc_type='_doc', body=body)
 
# Check that data is indeed in ES
res = es.search(
    index=index_name, body={
        "query": {
                "match_all": {}
    }},
    size=2)
assert len(res["hits"]["hits"]) > 0

リアルタイムでクエリを実行する

これで在庫の画像アイテムの Embeddings が含まれたOpenSearch Service Index を用意できたので、次はクエリの Embeddings を生成する方法を見ていきましょう。テキストと画像の Embeddings をそれぞれ処理するために、2 つのSageMaker 推論エンドポイントを作成する必要があります。

また、エンドポイントを使用して画像とテキストをエンコードする 2 つの関数を作成します。encode_text 関数の場合、this is をアイテム名の前に追加して、アイテム名をアイテムの説明文として翻訳します。TransformerResNet モデルをサポートするために、memory_size_in_mb は 6 GB に設定されています。以下のコードを参照してください:

text_predictor = clip_text_model.deploy(
    instance_type='ml.c5.xlarge',
    initial_instance_count=1,
    serverless_inference_config=ServerlessInferenceConfig(memory_size_in_mb=6144),
    serializer=JSONSerializer(),
    deserializer=JSONDeserializer(),
    wait=True
)
 
image_predictor = clip_image_model.deploy(
    instance_type='ml.c5.xlarge',
    initial_instance_count=1,
    serverless_inference_config=ServerlessInferenceConfig(memory_size_in_mb=6144),
    serializer=IdentitySerializer(content_type="application/x-image"),
    deserializer=JSONDeserializer(),
    wait=True
)
 
def encode_image(file_name="./data/images/0e9420c6.jpg"):    
    with open(file_name, "rb") as f:
        payload = f.read()
        payload = bytearray(payload)
    res = image_predictor.predict(payload)
    return res[0]
 
def encode_name(item_name):
    res = text_predictor.predict({"inputs": [f"this is a {item_name}"]})
    return res[0]
 

まず使用する画像をプロットします。

item_image_path, item_name = get_image_from_item_id(item_id = "B0896LJNLH", return_image=False) 
feature_vector = encode_image(file_name=item_image_path)
print(feature_vector.shape) 
Image.open(item_image_path)


簡単なクエリの結果を見てみましょう。OpenSearch Serviceから結果を取得した後、dataset からアイテム名と画像のリストを取得します。

def search_products(embedding, k = 3):
    body = {
        "size": k,
        "_source": {
            "exclude": ["embeddings"],
        },
        "query": {
            "knn": {
                "embeddings": {
                    "vector": embedding,
                    "k": k,
                }
            }
        },
    }        
    res = es.search(index=index_name, body=body)
    images = []
    for hit in res["hits"]["hits"]:
        id_ = hit["_id"]
        image, item_name = get_image_from_item_id(id_)
        image.name_and_score = f'{hit["_score"]}:{item_name}'
        images.append(image)
    return images
 
def display_images(
    images: [PilImage], 
    columns=2, width=20, height=8, max_images=15, 
    label_wrap_length=50, label_font_size=8):
 
    if not images:
        print("No images to display.")
        return 
 
    if len(images) > max_images:
        print(f"Showing {max_images} images of {len(images)}:")
        images=images[0:max_images]
 
    height = max(height, int(len(images)/columns) * height)
    plt.figure(figsize=(width, height))
    for i, image in enumerate(images):
 
        plt.subplot(int(len(images) / columns + 1), columns, i + 1)
        plt.imshow(image)
 
        if hasattr(image, 'name_and_score'):
            plt.title(image.name_and_score, fontsize=label_font_size); 
            
images = search_products(feature_vector)

2 つの画像が同じであるため、最初の項目のスコアは 1.0 です。その他の項目は、OpenSearch Service Index にあるさまざまな種類のウォーターグラスになります。

テキストを使用してインデックスをクエリすることもできます。

feature_vector = encode_name("drinkware glass") 
images = search_products(feature_vector) 
display_images(images)

これで、インデックスからウォーターグラスの写真を 3 枚取得できるようになりました。CLIP エンコーダーを使用すると、同じ潜在空間内の画像とテキストを検索できます。これのもう 1 つの例は、索引で「ピザ」という単語を検索することです。

feature_vector = encode_name("pizza")
images = search_products(feature_vector) 
display_images(images)

Clean up

従量課金制モデルの Serverless Inference は、頻度の低いトラフィックパターンや予測不可能なトラフィックパターンに対して費用対効果の高いオプションです。厳格なサービスレベル契約 (SLA) を締結している場合や、コールドスタートに耐えられない場合は、リアルタイムエンドポイントの方が適しています。マルチモデルまたはマルチコンテナのエンドポイントを使用すると、多数のモデルをデプロイするためのスケーラブルで費用対効果の高いソリューションが得られます。詳細については、Amazon SageMaker の料金ページを参照してください。
サーバーレスエンドポイントは、不要になったら削除することをお勧めします。この演習を終了したら、次の手順でリソースを削除できます (これらのリソースは、AWS マネジメントコンソール から、または AWS SDK または SageMaker SDK を使用して削除できます)。

  1. 作成したエンドポイントを削除します。
  2. (オプション) 登録されたモデルを削除します。
  3. (オプション) SageMaker 実行ロールを削除します。
  4. (オプション) S3 バケットを空にして削除します。

Summary

この記事では、SageMaker と OpenSearch Service の k-NN インデックス機能を使用して k-NN 検索アプリケーションを作成する方法を示しました。OpenAI 実装の事前トレーニング済みの CLIP モデルを使用しました。

投稿の OpenSearch サービスのインジェスト実装は、プロトタイピングにのみ使用されます。Amazon S3 から OpenSearch サービスに大規模にデータを取り込みたい場合は、適切なインスタンスタイプとインスタンス数で Amazon SageMaker 処理ジョブを起動できます。別のスケーラブルな埋め込み取り込みソリューションについては、「Novartis AG uses Amazon OpenSearch Service K-Nearest Neighbor (KNN) and Amazon SageMaker to power search and recommendation (Part 3/4)」を参照してください。

CLIPはゼロショット機能を備えているため、転移学習を使用してモデルを微調整しなくても、事前にトレーニングされたモデルを直接採用できます。これにより、CLIPモデルの適用が簡単になります。製品画像と説明文の両方がある場合は、転移学習を使用して独自のデータでモデルを微調整し、モデルのパフォーマンスをさらに向上させることができます。詳細については、「Learning Transferable Visual Models From Natural Language Supervision」と「CLIP GitHub リポジトリ」を参照してください。

著者について

Kevin Du は AWS のシニアデータラボアーキテクトで、お客様の機械学習 (ML) 製品と MLOps プラットフォームの開発を促進できるよう支援することに専念しています。スタートアップと企業の両方を対象に ML 対応製品を開発してきた 10 年以上の経験を持つ彼は、お客様が ML ソリューションの本番化を合理化できるよう支援することに重点を置いています。自由時間には、ケビンは料理やバスケットボール観戦を楽しんでいます。

Ananya Royは、オーストラリアのシドニーを拠点とするAIと機械学習を専門とするシニアデータラボアーキテクトです。彼女はさまざまな顧客と協力してアーキテクチャのガイダンスを提供し、データラボとの連携を通じて効果的なAI/MLソリューションを提供できるよう支援してきました。AWS に入社する前は、シニアデータサイエンティストとして働き、通信会社、銀行、フィンテックなどのさまざまな業界の大規模な ML モデルを扱っていました。AI/ML の経験により、複雑なビジネス上の問題に対して効果的なソリューションを提供でき、最先端のテクノロジーを活用してチームが目標を達成できるよう支援することに情熱を注いでいます。

翻訳はソリューションアーキテクトの辻 浩季が担当しました。原文はこちらです。