Amazon Bedrockがリリース! Lambdaから画像を10枚生成してS3に置くまでの流れをやってみた

Bedrock+Lambda+S3で画像生成ライフが捗ります。

こんにちは、AWS事業本部の荒平(@0Air)です。

待望のAmazon Bedrockが2023年9月末にリリースされました。

色々触ってみる中で、画像生成はやっぱり複数枚をワンクリックで同時にできたらいいな〜と思ったので、Lambdaでスクリプトを動かして、生成した画像をS3に保管するまでの流れを試してみました。

構成図

作ってみた構成です。まずはシンプルに。

LambdaからBedrockにプロンプトを提供し、生成した画像をS3バケットへ保存します。

やってみた

★ この手順は、Stability AIの「Stable Diffusion XL」モデルがアカウントで有効になっていることが前提です。

1. Lambda関数の準備

まずは、動作環境の準備が必要です。以下記事の通り、Python 3.11の組み込みboto3のバージョンは、執筆時点でBedrock非対応バージョンのため、レイヤーとして追加する必要があります。
以下記事でまとめられていますので、ご参照ください。

Dockerが動作する環境であれば、以下コードでもzip化されたレイヤーを取得することができます。

docker run --entrypoint "" -v "$PWD":/var/task "public.ecr.aws/lambda/python:3.11.2023.09.27.10" /bin/sh -c "mkdir -p /tmp/python && pip3 install boto3 -t /tmp/python && cd /tmp && yum install -y zip && zip -r /var/task/boto3-mylayer.zip ."

Lambdaの実行ロールに権限を付与します。
今回は検証のため、S3FullAccessと、Bedrockのフルアクセスを付与しました。

なお、Bedrockは執筆時点でマネージドポリシーが無いようでしたので、以下を定義しています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "bedrock:*",
      "Resource": "*"
    }
  ]
}

そして、最後にタイムアウト値を任意の時間に変更します。
10枚の画像生成だと、およそ1分程度あれば完走できます。

2. S3バケットの準備

画像の出力先となる、適当なバケットを1つ用意します。
名前については何でも構いませんが、後述のコードではus-east-1を指定しているため、差し支えなければus-east-1で作成します。

3. Lambda関数の記述

以下のコードをコピーしてください。
ベースのモデル呼び出し部分などはユーザーガイドを参考にしました。

import boto3
import json
import base64
import datetime

BUCKET_NAME = 'bedrock-output-xxxxx'
s3 = boto3.client('s3', region_name="us-east-1")
bedrock = boto3.client(service_name='bedrock-runtime', region_name="us-east-1")


def lambda_handler(event, context):
    prompt_data = event.get('prompt')
    modelId = "stability.stable-diffusion-xl-v0" 
    accept = "application/json"
    contentType = "application/json"

    # 10回の繰り返しを実行
    for i in range(10):
        body = json.dumps({
            "text_prompts": [
                { 
                    "text": prompt_data 
                }
            ],
            "cfg_scale":10,
            # Seed値を繰り返し毎に変更する
            "seed":20 + i, 
            "steps":50
        })

        response = bedrock.invoke_model(
            body=body, modelId=modelId, accept=accept, contentType=contentType
        )
        response_body = json.loads(response.get("body").read())
        print(response_body['result'])

        # 取得した画像データのデコード
        encoded_png_data = response_body.get("artifacts")[0].get("base64")
        decoded_png_data = base64.b64decode(encoded_png_data)

        # ファイル名の付与
        now = datetime.datetime.now()
        formatted_date = now.strftime('%y%m%d-%H%M%S%f')[:-4]
        file_name = f"output-{formatted_date}.png"

        # S3バケットへ出力
        s3.put_object(Bucket=BUCKET_NAME, Key=file_name, Body=decoded_png_data)

BUCKET_NAME は環境によって、それぞれのバケット名に置き換えてください。
また、us-east-1を指定していますが、他のリージョンをご利用の場合はこちらも置き換えてください。

4. Lambda関数の実行

イベントにプロンプトを記述して保存し、実行します。
サンプルとして、以下のプロンプトを用意しました。

{
  "prompt": "Charming European cobblestone streets in a historic town."
}

成功すれば、「success」と画像出力枚数分、表示されるはずです。

実験では、45秒ほどで10枚の出力が完了しました。
S3バケットにも、無事画像が出力されています。

狙い通りヨーロピアンな街並みが10枚生成されました!

おわりに

待ちに待ったBedrockを触ってみました。これで画像生成ライフが捗りますね!!
for毎にSeedを変更しないと同じ画像ばかり生成されることに気付けてよかったです。

AWSの各種サービスとの連携は無限に広がりそうなので、これからの発展に期待です。
参考までに、今回の 50stepまで、512x512以下を10枚 の画像生成は $0.018 × 10枚 = $0.18 が課金されます。
使いすぎには注意が必要ですね。

このエントリが誰かの助けになれば幸いです。

それでは、AWS事業本部 コンサルティング部の荒平(@0Air)がお送りしました!

参考