Closed14

S3 Vectorsを利用してマルチモーダル検索をするための調査

kobayashi-m42kobayashi-m42

S3 Vectorsの気になる点

公式ドキュメント

https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vectors.html

kobayashi-m42kobayashi-m42

マルチモーダル検索

埋め込みモデル

参考資料

https://dev.classmethod.jp/articles/amazon-s3-vectors-multimodal-search/

kobayashi-m42kobayashi-m42

マルチモーダル検索をやってみる

  • リージョンはバージニア北部を利用
  • モデルはAmazon Titan Multimodal Embeddings G1

概要

  • Amazon S3 Vectorsの作成
  • ベクトル埋め込みをベクトルインデックスに挿入
  • テキスト検索
  • 類似画像の検索
kobayashi-m42kobayashi-m42

S3 Vectorsの作成

ベクトルバケットをコンソールから作成

インデックスの作成

ディメンションは、Amazon Titan Multimodal Embeddings G1が以下となっているのでため、1024を指定(256 や 384 次元に落とすことも可能)

Output vector size – 1,024 (default), 384, 256
https://docs.aws.amazon.com/bedrock/latest/userguide/titan-multiemb-models.html

距離メトリックはコサインを指定
追加設定のメタデータはなし

kobayashi-m42kobayashi-m42

ベクトル埋め込みをベクトルインデックスに挿入

import base64
import glob
import json

import boto3 
from dotenv import load_dotenv

load_dotenv()

bedrock= boto3.client("bedrock-runtime", region_name="us-east-1")
s3vectors = boto3.client("s3vectors", region_name="us-east-1")

file_paths = glob.glob("image/*")

embeddings=[]
for path in file_paths:
    image_bytes = open(path, "rb").read()

    body = json.dumps({
        "inputImage": base64.b64encode(image_bytes).decode("utf-8"),
        })    
    response = bedrock.invoke_model(
        modelId='amazon.titan-embed-image-v1',
        body=body
    )   
    response_body = json.loads(response['body'].read())
    embeddings.append(        {
            "key": path,
            "data": {"float32": response_body["embedding"]},
        })

res = s3vectors.put_vectors(
    vectorBucketName="vector-20251003", indexName="vector-20251003-index", vectors=embeddings
)

用意した画像

5つの猫の画像を利用する




kobayashi-m42kobayashi-m42

テキスト検索

日本語で「白い猫」「黒い猫」と入力された場合を想定し、white catblack catで検索。

import base64
import json

import boto3 

bedrock= boto3.client("bedrock-runtime", region_name="us-east-1")
s3vectors = boto3.client("s3vectors", region_name="us-east-1")

input_text = "white cat"

body = json.dumps({
    "inputText": input_text,
    })

response = bedrock.invoke_model(
    modelId="amazon.titan-embed-image-v1",
    body=body
)

response_body = json.loads(response["body"].read())
embedding = response_body["embedding"]

query = s3vectors.query_vectors(
    vectorBucketName="vector-20251003",
    indexName="vector-20251003-index",
    queryVector={"float32":embedding},
    topK=3, 
    returnDistance=True,
    returnMetadata=True
)

results = query["vectors"]
print(results)

結果

検索ワードに近い画像が取得できている

  • white catの結果の画像

  • black catの結果の画像

kobayashi-m42kobayashi-m42

類似画像の検索

import base64
import json
import boto3 

bedrock= boto3.client("bedrock-runtime", region_name="us-east-1")
s3vectors = boto3.client("s3vectors", region_name="us-east-1")

image_bytes = open("image/5b73a923-a894-4c55-abe9-c2a2279d89e4.webp", "rb").read()
body = json.dumps({
    "inputImage": base64.b64encode(image_bytes).decode("utf-8"),
    })

response = bedrock.invoke_model(
    modelId="amazon.titan-embed-image-v1",
    body=body
)

response_body = json.loads(response["body"].read())
embedding = response_body["embedding"]

query = s3vectors.query_vectors(
    vectorBucketName="vector-20251003",
    indexName="vector-20251003-index",
    queryVector={"float32":embedding},
    topK=3, 
    returnDistance=True,
    returnMetadata=True
)

results = query["vectors"]
print(results)

検索結果

[
{'key': 'image/5b73a923-a894-4c55-abe9-c2a2279d89e4.webp', 'metadata': {}, 'distance': 0.0004391670227050781},
{'key': 'image/0ab0c123-0562-4ee9-9f89-d7c544a14fd2.webp', 'metadata': {}, 'distance': 0.2183462381362915}
]
  • 検索用の画像

  • 検索結果
    検索に利用した画像と同じもの、同じポーズをしている猫の画像が検索された

kobayashi-m42kobayashi-m42

日本語による検索

Cohere Embed v4が使用可能になった(2025年10月2日)
https://aws.amazon.com/jp/about-aws/whats-new/2025/10/coheres-embed-v4-multimodal-embeddings-bedrock/
https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-embed.html

やってみる

モデルアクセスのリクエスト
ベクトルインデックスの作成
ベクトル埋め込みをベクトルインデックスに挿入
日本語によるテキスト検索
類似画像の検索

kobayashi-m42kobayashi-m42

モデルアクセスをリクエスト

AWSコンソール Amazon Bedrock > モデルアクセスから「Embed v4」のアクセスをリクエスト
数秒程度でアクセス可能になった

kobayashi-m42kobayashi-m42

ベクトルインデックスの作成

ディメンションはデフォルトの1536を設定

kobayashi-m42kobayashi-m42

ベクトル埋め込みをベクトルインデックスに挿入

def get_base64_image_uri(image_file_path: str, image_mime_type: str):
    with open(image_file_path, "rb") as f:
        image_bytes = f.read()

    base64_image = base64.b64encode(image_bytes).decode("utf-8")
    return f"data:{image_mime_type};base64,{base64_image}"

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")

model_id = "cohere.embed-v4:0"

input_type = "search_document"
embedding_types = ["float"]
accept = '*/*'
content_type = 'application/json'

embeddings = []

file_paths = glob.glob("../image/*")
for path in file_paths:
    image_base64_uri = get_base64_image_uri(path, image_mime_type)

    body = json.dumps({
        "input_type": input_type,
        "images": [image_base64_uri],
        "embedding_types": embedding_types
    })

    response = bedrock.invoke_model(
        modelId=model_id,
        body=body,
        accept=accept,
        contentType=content_type
    )
    response_body = json.loads(response['body'].read())
    embedding_vector = response_body["embeddings"]["float"][0]

    embeddings.append({
        "key": path,
        "data": {"float32": embedding_vector},
    })

s3vectors = boto3.client("s3vectors", region_name="us-east-1")

res = s3vectors.put_vectors(
    vectorBucketName="vector-20251003", 
    indexName="vector-20251004-cohere-v4-index", 
    vectors=embeddings
)

AWS CLIで登録されていることを確認

aws s3vectors list-vectors \
  --vector-bucket-name "vector-20251003" \
  --index-name "vector-20251004-cohere-v4-index"
kobayashi-m42kobayashi-m42

日本語によるテキスト検索

日本語で「白い猫」「黒い猫」「寝ている猫」を検索

結果

  • 「白い猫」の結果
    [
    {'key': '../image/7717955d-f83e-4466-b9f5-201c232ffd14.webp', 'metadata': {}, 'distance': 0.6253760457038879},
    {'key': '../image/b301e8c3-71fb-4db4-ade3-34693311980a.webp', 'metadata': {}, 'distance': 0.6433943510055542},
    {'key': '../image/fb7f3a0e-fccd-45ee-a3f0-17c7138a6a8a.webp', 'metadata': {}, 'distance': 0.6550061702728271}
    ]
    

  • 「黒い猫」の結果
[
{'key': '../image/5b73a923-a894-4c55-abe9-c2a2279d89e4.webp', 'metadata': {}, 'distance': 0.6397557258605957},
{'key': '../image/7717955d-f83e-4466-b9f5-201c232ffd14.webp', 'metadata': {}, 'distance': 0.6870872974395752},
{'key': '../image/0ab0c123-0562-4ee9-9f89-d7c544a14fd2.webp', 'metadata': {}, 'distance': 0.7297742366790771}
]



  • 「寝ている猫」の結果
[
{'key': '../image/7717955d-f83e-4466-b9f5-201c232ffd14.webp', 'metadata': {}, 'distance': 0.5258817672729492},
{'key': '../image/b301e8c3-71fb-4db4-ade3-34693311980a.webp', 'metadata': {}, 'distance': 0.6387431621551514},
{'key': '../image/0ab0c123-0562-4ee9-9f89-d7c544a14fd2.webp', 'metadata': {}, 'distance': 0.6802964210510254}
]



kobayashi-m42kobayashi-m42

類似画像の検索

def get_base64_image_uri(image_file_path: str, image_mime_type: str):
    with open(image_file_path, "rb") as f:
        image_bytes = f.read()

    base64_image = base64.b64encode(image_bytes).decode("utf-8")
    return f"data:{image_mime_type};base64,{base64_image}"

bedrock= boto3.client("bedrock-runtime", region_name="us-east-1")

model_id = "cohere.embed-v4:0"

input_type = "search_document"
embedding_types = ["float"]
accept = '*/*'
content_type = 'application/json'
image_mime_type = "image/webp"

image_base64_uri = get_base64_image_uri("../image/5b73a923-a894-4c55-abe9-c2a2279d89e4.webp", image_mime_type)

body = json.dumps({
        "input_type": input_type,
        "images": [image_base64_uri],
        "embedding_types": embedding_types
    })

response = bedrock.invoke_model(
        modelId=model_id,
        body=body,
        accept=accept,
        contentType=content_type
)

response_body = json.loads(response["body"].read())
embedding = response_body["embeddings"]["float"][0]

s3vectors = boto3.client("s3vectors", region_name="us-east-1")

query = s3vectors.query_vectors(
    vectorBucketName="vector-20251003", 
    indexName="vector-20251004-cohere-v4-index", 
    queryVector={"float32":embedding},
    topK=3, 
    returnDistance=True,
    returnMetadata=True
)

results = query["vectors"]
print(results)

[
{'key': '../image/5b73a923-a894-4c55-abe9-c2a2279d89e4.webp', 'metadata': {}, 'distance': 0.0004458427429199219},
{'key': '../image/0ab0c123-0562-4ee9-9f89-d7c544a14fd2.webp', 'metadata': {}, 'distance': 0.4271997809410095},
{'key': '../image/7717955d-f83e-4466-b9f5-201c232ffd14.webp', 'metadata': {}, 'distance': 0.45190203189849854}
]



このスクラップは4日前にクローズされました