小西秀和です。
Amazon BedrockのAIモデルとして利用可能になったAnthropic Claude 3ファミリーでは画像認識機能が導入されました。そして、最新モデルのAnthropic Claude 3.5 Sonnetにも更に強化された画像認識機能が備わっています。
これらのAnthropic Claudeモデルの画像認識機能、特にOCR(光学文字認識)の性能については、いくつかの簡単な試行と比較を実施してみたことがあります。詳細は以下の記事でご覧いただけます。
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Haiku
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Sonnet
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Opus
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3.5 Sonnet
Using Amazon Textract for OCR(Optical Character Recognition)
Evaluating OCR Accuracy of Claude on Amazon Bedrock and Amazon Textract Using Similarity Metrics
本記事では、Anthropic Claude 3.5 Sonnetの画像認識機能をさらに活用し、Stability AI Stable Diffusion XL(SDXL)で生成した画像を検証・再生成するAmazon Bedrockの使用例をご紹介します。
この試みは、生成画像の要件充足を自動的に判定することで、人間による目視確認の作業量削減も目指しています。
※本記事および当執筆者のその他の記事で掲載されているソースコードは自主研究活動の一貫として作成したものであり、動作を保証するものではありません。使用する場合は自己責任でお願い致します。また、予告なく修正することもありますのでご了承ください。
※本記事執筆にあたっては個人でユーザー登録したAWSアカウント上でAWSサービスを使用しています。
※本記事執筆にあたって使用したAmazon Bedrockの各Modelは2024-07-20(JST)に実行し、その時点における次のEnd user license agreement (EULA)に基づいています。
Anthropic Claude 3.5 Sonnet(anthropic.claude-3-5-sonnet-20240620-v1:0): Anthropic on Bedrock - Commercial Terms
of Service(Effective: January 2, 2024)
Stability AI Stable Diffusion XL(stability.stable-diffusion-xl-v1): STABILITY AMAZON BEDROCK END USER LICENSE AGREEMENT(Last Updated: April 29, 2024)
構成図と処理フロー
今回のテーマを実現する構成図は次のようになります。
この処理フローについて詳細を説明します。
1. プロンプトやパラメータを含むイベントを入力します。
2-1. 入力した画像作成を指示するプロンプトでAmazon BedrockでSDXLモデルを実行します。
2-2. 生成された画像をAmazon S3に保存します。
2-3. Amazon S3に保存された画像に対してAmazon BedrockでClaude 3.5 Sonnetモデルを実行し、画像作成を指示したプロンプトの要件にふさわしいかを検証します。
* 画像作成を指示したプロンプトの要件にふさわしいと判断されなければ、2-1.
から2-3.
の処理を指定した同一プロンプト実行回数だけ繰り返します。
* 画像作成を指示したプロンプトの要件にふさわしいと判断されれば、その画像を出力結果とします。
3. 修正プロンプト実行回数を超えておらず、画像作成を指示したプロンプトの要件にふさわしいと判断されない回数が同一プロンプト実行回数を超えた場合、Amazon BedrockでClaude 3.5 Sonnetモデルを実行し、画像作成を指示するプロンプトを要件が満たされる可能性が高いものに修正します。この新しい画像作成を指示するプロンプトで2-1.
から処理をやり直します。
* 修正プロンプト実行回数を超えた場合、エラーとして処理を終了します。
この処理フローでポイントとなるのはClaude 3.5 Sonnetモデルによる画像作成を指示するプロンプトの修正です。
画像作成を指示するプロンプトがAIにとって理解しやすいものであれば、要件を満たす画像は何回か実行すれば出力される可能性が高いでしょう。
ただ、画像作成を指示するプロンプトがAIにとって理解しにくいものであれば、要件を満たす画像の出力がされない事も考えられます。
そのため、指定した同一プロンプト実行回数を超えた場合には、Amazon BedrockでClaude 3.5 Sonnetモデルを実行し、画像作成を指示するプロンプトを最適化したものに修正する処理を入れました。
実装例
入力するイベントのフォーマット
{ "prompt": "[画像生成のための初期プロンプト]", "max_retry_attempts": [各プロンプトで画像生成を試行する最大回数], "max_prompt_revisions": [プロンプトを修正する最大回数], "output_s3_bucket_name": "[生成された画像を保存するS3バケットの名前]", "output_s3_key_prefix": "[生成された画像のS3キーのプレフィックス]", "claude_validate_temperature": [画像検証時のClaudeモデルのtemperatureパラメータ(0.0〜1.0)], "claude_validate_top_p": [画像検証時のClaudeモデルのtop-pパラメータ(0.0〜1.0)], "claude_validate_top_k": [画像検証時のClaudeモデルのtop-kパラメータ], "claude_validate_max_tokens": [画像検証時のClaudeモデルが生成する最大トークン数], "claude_revise_temperature": [プロンプト修正時のClaudeモデルのtemperatureパラメータ(0.0〜1.0)], "claude_revise_top_p": [プロンプト修正時のClaudeモデルのtop-pパラメータ(0.0〜1.0)], "claude_revise_top_k": [プロンプト修正時のClaudeモデルのtop-kパラメータ], "claude_revise_max_tokens": [プロンプト修正時のClaudeモデルが生成する最大トークン数], "sdxl_cfg_scale": [Stable Diffusion XLモデルのCFGスケール], "sdxl_steps": [Stable Diffusion XLモデルの推論ステップ数], "sdxl_width": [Stable Diffusion XLモデルで生成する画像の幅(ピクセル単位)], "sdxl_height": [Stable Diffusion XLモデルで生成する画像の高さ(ピクセル単位)], "sdxl_seed": [Stable Diffusion XLモデルで使用する乱数シード値(再現性のために使用、指定しない場合はランダム)] }
入力するイベントの例
{ "prompt": "A serene landscape with mountains and a lake", "max_retry_attempts": 5, "max_prompt_revisions": 3, "output_s3_bucket_name": "your-output-bucket-name", "output_s3_key_prefix": "generated-images", "claude_validate_temperature": 1.0, "claude_validate_top_p": 0.999, "claude_validate_top_k": 250, "claude_validate_max_tokens": 4096, "claude_revise_temperature": 1.0, "claude_revise_top_p": 0.999, "claude_revise_top_k": 250, "claude_revise_max_tokens": 4096, "sdxl_cfg_scale": 30, "sdxl_steps": 150, "sdxl_width": 1024, "sdxl_height": 1024, "sdxl_seed": 0 }
ソースコード
今回、実装したソースコードは次のようになります。
# #Event Sample # { # "prompt": "A serene landscape with mountains and a lake", # "max_retry_attempts": 5, # "max_prompt_revisions": 3, # "output_s3_bucket_name": "your-output-bucket-name", # "output_s3_key_prefix": "generated-images", # "claude_validate_temperature": 1.0, # "claude_validate_top_p": 0.999, # "claude_validate_top_k": 250, # "claude_validate_max_tokens": 4096, # "claude_revise_temperature": 1.0, # "claude_revise_top_p": 0.999, # "claude_revise_top_k": 250, # "claude_revise_max_tokens": 4096, # "sdxl_cfg_scale": 30, # "sdxl_steps": 150, # "sdxl_width": 1024, # "sdxl_height": 1024, # "sdxl_seed": 0 # } import boto3 import json import base64 import os import sys from io import BytesIO import datetime import random region = os.environ.get('AWS_REGION') bedrock_runtime_client = boto3.client('bedrock-runtime', region_name=region) s3_client = boto3.client('s3', region_name=region) def claude3_5_invoke_model(input_prompt, image_media_type=None, image_data_base64=None, model_params={}): messages = [ { "role": "user", "content": [ { "type": "text", "text": input_prompt } ] } ] if image_media_type and image_data_base64: messages[0]["content"].insert(0, { "type": "image", "source": { "type": "base64", "media_type": image_media_type, "data": image_data_base64 } }) body = { "anthropic_version": "bedrock-2023-05-31", "max_tokens": model_params.get('max_tokens', 4096), "messages": messages, "temperature": model_params.get('temperature', 1.0), "top_p": model_params.get('top_p', 0.999), "top_k": model_params.get('top_k', 250), "stop_sequences": ["\n\nHuman:"] } response = bedrock_runtime_client.invoke_model( modelId='anthropic.claude-3-5-sonnet-20240620-v1:0', contentType='application/json', accept='application/json', body=json.dumps(body) ) response_body = json.loads(response.get('body').read()) response_text = response_body["content"][0]["text"] return response_text def sdxl_invoke_model(prompt, model_params={}): seed = model_params.get('seed', 0) if seed == 0: seed = random.randint(0, 4294967295) body = { "text_prompts": [{"text": prompt}], "cfg_scale": model_params.get('cfg_scale', 10), "steps": model_params.get('steps', 50), "width": model_params.get('width', 1024), "height": model_params.get('height', 1024), "seed": seed } print(f"SDXL model parameters: {body}") response = bedrock_runtime_client.invoke_model( body=json.dumps(body), modelId="stability.stable-diffusion-xl-v1", contentType="application/json", accept="application/json" ) response_body = json.loads(response['body'].read()) image_data = base64.b64decode(response_body['artifacts'][0]['base64']) print(f"Image generated successfully with seed: {seed}") return image_data def save_image_to_s3(image_data, bucket, key): s3_client.put_object( Bucket=bucket, Key=key, Body=image_data ) print(f"Image saved to S3: s3://{bucket}/{key}") def validate_image(image_data, prompt, claude_validate_params): image_base64 = base64.b64encode(image_data).decode('utf-8') input_prompt = f"""Does this image match the following prompt? Prompt: {prompt}. Please answer in the following JSON format: {{"result":"<YES or NO>", "reason":"<Reason for your decision>"}} Ensure your response can be parsed as valid JSON. Do not include any explanations, comments, or additional text outside of the JSON structure.""" validation_result = claude3_5_invoke_model(input_prompt, "image/png", image_base64, claude_validate_params) try: print(f"validation Result: {validation_result}") parsed_result = json.loads(validation_result) is_valid = parsed_result['result'].upper() == 'YES' print(f"Image validation result: {is_valid}") print(f"Validation reason: {parsed_result['reason']}") return is_valid except json.JSONDecodeError: print(f"Error parsing validation result: {validation_result}") return False def revise_prompt(original_prompt, claude_revise_params): input_prompt = f"""Revise the following image generation prompt to optimize it for Stable Diffusion, incorporating best practices: {original_prompt} Please consider the following guidelines in your revision: 1. Be specific and descriptive, using vivid adjectives and clear nouns. 2. Include details about composition, lighting, style, and mood. 3. Mention specific artists or art styles if relevant. 4. Use keywords like "highly detailed", "4k", "8k", or "photorealistic" if appropriate. 5. Separate different concepts with commas. 6. Place more important elements at the beginning of the prompt. 7. Use weights (e.g., (keyword:1.2)) for emphasizing certain elements if necessary. 8. If the original prompt is not in English, translate it to English. Your goal is to create a clear, detailed prompt that will result in a high-quality image generation with Stable Diffusion. Please provide your response in the following JSON format: {{"revised_prompt":"<Revised Prompt>"}} Ensure your response can be parsed as valid JSON. Do not include any explanations, comments, or additional text outside of the JSON structure.""" revised_prompt_json = claude3_5_invoke_model(input_prompt, model_params=claude_revise_params) print(f"Original prompt: {original_prompt}") print(f"Revised prompt JSON: {revised_prompt_json.strip()}") try: parsed_result = json.loads(revised_prompt_json) revised_prompt = parsed_result['revised_prompt'] print(f"Parsed revised prompt: {revised_prompt}") return revised_prompt except json.JSONDecodeError: print(f"Error parsing revised prompt result: {revised_prompt_json}") return original_prompt def lambda_handler(event, context): try: initial_prompt = event['prompt'] prompt = initial_prompt max_retry_attempts = max(0, event.get('max_retry_attempts', 5) - 1) max_prompt_revisions = max(0, event.get('max_prompt_revisions', 3) - 1) output_s3_bucket_name = event['output_s3_bucket_name'] output_s3_key_prefix = event.get('output_s3_key_prefix', 'generated-images') print(f"Initial prompt: {initial_prompt}") print(f"Max retry attempts: {max_retry_attempts}") print(f"Max prompt revisions: {max_prompt_revisions}") # Model parameters claude_validate_params = { 'temperature': event.get('claude_validate_temperature', 1.0), 'top_p': event.get('claude_validate_top_p', 0.999), 'top_k': event.get('claude_validate_top_k', 250), 'max_tokens': event.get('claude_validate_max_tokens', 4096) } claude_revise_params = { 'temperature': event.get('claude_revise_temperature', 1.0), 'top_p': event.get('claude_revise_top_p', 0.999), 'top_k': event.get('claude_revise_top_k', 250), 'max_tokens': event.get('claude_revise_max_tokens', 4096) } sdxl_params = { 'cfg_scale': event.get('sdxl_cfg_scale', 7), 'steps': event.get('sdxl_steps', 50), "width": event.get('sdxl_width', 1024), "height": event.get('sdxl_height', 1024), "seed": event.get('sdxl_seed', 0) } print(f"Claude validate params: {claude_validate_params}") print(f"Claude revise params: {claude_revise_params}") print(f"SDXL params: {sdxl_params}") # Generate start timestamp and S3 key start_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S") for revision in range(max_prompt_revisions + 1): print(f"Starting revision {revision}") for attempt in range(max_retry_attempts + 1): print(f"Attempt {attempt} for generating image") # Generate image with SDXL image_data = sdxl_invoke_model(prompt, sdxl_params) image_key = f"{output_s3_key_prefix}-{start_timestamp}-{revision:03d}-{attempt:03d}.png" # Save image to S3 save_image_to_s3(image_data, output_s3_bucket_name, image_key) # Validate image with Claude is_valid = validate_image(image_data, initial_prompt, claude_validate_params) if is_valid: print("Valid image generated successfully") return { 'statusCode': 200, 'body': json.dumps({ 'status': 'SUCCESS', 'message': 'Image generated successfully', 'output_s3_bucket_url': f'https://s3.console.aws.amazon.com/s3/buckets/{output_s3_bucket_name}', 'output_s3_object_url': f'https://s3.console.aws.amazon.com/s3/object/{output_s3_bucket_name}?region={region}&prefix={image_key}' }) } # If max retry attempts reached and not the last revision, revise prompt if revision < max_prompt_revisions: print("Revising prompt") prompt = revise_prompt(initial_prompt, claude_revise_params) print("Failed to generate a valid image after all attempts and revisions") return { 'statusCode': 400, 'body': json.dumps({ 'status': 'FAIL', 'error': 'Failed to generate a valid image after all attempts and revisions' }) } except Exception as ex: print(f'Exception: {ex}') tb = sys.exc_info()[2] err_message = f'Exception: {str(ex.with_traceback(tb))}' print(err_message) return { 'statusCode': 500, 'body': json.dumps({ 'status': 'FAIL', 'error': err_message }) }
このソースコードで工夫した点には以下のものが挙げられます。
- 画像生成と検証のサイクルを自動化し、要件を満たすまで繰り返す仕組みを実装した
- Claude 3.5 Sonnetを使用して生成された画像の検証と、プロンプトの修正を行った
- Stable Diffusion XLを使用して高品質な画像生成を行った
- プロンプトの修正指示には、具体的な画像生成のベストプラクティスを含めた
- 画像生成パラメータ(cfg_scale, steps, width, height, seed)をカスタマイズ可能にした
- Claude 3.5 Sonnetの呼び出しパラメータ(temperature, top_p, top_k, max_tokens)も調整可能にした
- 生成された画像を自動的にS3バケットに保存し、結果のURLを返した
- エラーハンドリングとログ出力を適切に実装し、トラブルシューティングを容易にした
- JSONフォーマットを使用して、Claudeとの対話を構造化し、結果の解析を容易にした
- 最大リトライ回数と最大プロンプト修正回数を設定可能にし、無限ループを防いだ
実行内容と結果
実行の一例:入力パラメータ
{ "prompt": "自然の中から見た夜景で、空にはオーロラと月と流星群があり、地上には海が広がって流氷が流れ、地平線から太陽が出ている無人の写真。", "max_retry_attempts": 5, "max_prompt_revisions": 5, "output_s3_bucket_name": "ho2k.com", "output_s3_key_prefix": "generated-images", "claude_validate_temperature": 1, "claude_validate_top_p": 0.999, "claude_validate_top_k": 250, "claude_validate_max_tokens": 4096, "claude_revise_temperature": 1, "claude_revise_top_p": 0.999, "claude_revise_top_k": 250, "claude_revise_max_tokens": 4096, "sdxl_cfg_scale": 30, "sdxl_steps": 150, "sdxl_width": 1024, "sdxl_height": 1024, "sdxl_seed": 0 }
今回の実行例の入力パラメータには以下の工夫をしています。
max_retry_attempts
を5に設定して、画像生成の成功率を高めました。max_prompt_revisions
が5に設定されており、必要に応じてプロンプトを改善する機会を増やしました。- 画像検証と修正のためのClaudeモデルのパラメータ(temperature, top_p, top_k, max_tokens)を細かく設定しました。
sdxl_cfg_scale
を30に設定し、プロンプトへの忠実度を高めました。sdxl_steps
を150に設定し、画像生成の品質を向上させました。- 画像生成に使用する
seed
がランダムに設定されるようにし、毎回異なる画像が生成されるようにしました。
実行の一例:結果
生成された画像
今回の試行において最終的にプロンプトの要件を満たして検証をパスした画像は以下です。
この画像は実際に「自然の中から見た夜景で、空にはオーロラと月と流星群があり、地上には海が広がって流氷が流れ、地平線から太陽が出ている無人の写真。」という要件をほぼ満たしています(流星群と流氷の具現化は弱いですが、月と地平線の太陽という矛盾した情景はしっかりと表現されています)。
また、最終的に検証をパスした画像は、最終的に検証をパスするよりも前に生成された他の画像(後述の「生成された画像一覧」参照)と比較しても、指示された要件をより多く満たしていることも確認できました。
今回、試した実行で生成された画像の一覧は以下です。
この「生成された画像一覧」の各行の画像は、それぞれ修正された異なるプロンプトから生成されたものです。
最初に入力された日本語の文章によるプロンプトでは、要件とかけ離れた画像が出力されているのに対し、1回目のプロンプトの修正以降は要件に近い画像が出力されています。
修正されたプロンプトの変化
上記で掲載した「生成された画像一覧」の各行の画像は、それぞれ修正された異なるプロンプトから生成されています。
具体的には、「生成された画像一覧」の最初の行の画像は、以下の「修正0回目」のプロンプトから生成されたものであり、「生成された画像一覧」の最後の行の画像は、以下の「修正3回目」のプロンプトから生成されたものです。
プロンプトの修正回数ごとに、修正された画像生成プロンプトの内容を見てみましょう。
修正0回目
自然の中から見た夜景で、空にはオーロラと月と流星群があり、地上には海が広がって流氷が流れ、地平線から太陽が出ている無人の写真。
修正1回目
Breathtaking night landscape, vibrant aurora borealis (aurora:1.2) dancing across the starry sky, crescent moon illuminating the scene, meteor shower streaking through the atmosphere, vast arctic ocean with floating ice floes, midnight sun peeking over the horizon, ethereal glow, long exposure photography, ultra high resolution 8k, photorealistic, highly detailed, dramatic lighting, panoramic composition, inspired by Aurora Borealis paintings of Frederic Edwin Church
修正2回目
Breathtaking Arctic night landscape, vibrant green aurora borealis dancing across the starry sky, full moon illuminating the scene, meteor shower streaking through the atmosphere, vast icy ocean with floating ice floes, midnight sun peeking over the horizon, ultra-high resolution 8K, photorealistic, highly detailed, dramatic lighting, panoramic composition, inspired by Albert Bierstadt and Frederic Edwin Church, (aurora borealis:1.3), (meteor shower:1.2), serene and majestic atmosphere
修正3回目
Breathtaking nightscape from nature's perspective, vibrant aurora borealis (aurora:1.2) dancing across the starry sky, crescent moon illuminating the scene, meteor shower streaking through the atmosphere, vast ocean with floating ice floes, sun peeking over the horizon creating a golden glow, no human presence, photorealistic, highly detailed, 8k resolution, dramatic lighting, wide-angle composition, inspired by Thomas Kinkade and Aurora HDR style, serene and awe-inspiring mood
特に上記で掲載した「生成された画像一覧」と関連付けて見ると、最初に入力された日本語の文章によるプロンプトは画像生成に最適化されていないため、要件とかけ離れた画像が出力されていることがわかります。
一方で、修正1回目以降はClaude 3.5 Sonnetによって画像生成に最適化されたプロンプトに修正され、要件に近い画像が出力されるようになっています。
このように、プロンプトの修正ごと、生成の実行ごとに画像が変化し、プロンプトの要件を満たす画像が最終的に検証をパスしました。
<参考資料>
AWS Documentation(Amazon Bedrock)
Tech Blog with related articles referenced
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Haiku
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Sonnet
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3 Opus
Using Amazon Bedrock for titling, commenting, and OCR (Optical Character Recognition) with Claude 3.5 Sonnet
Using Amazon Textract for OCR(Optical Character Recognition)
Evaluating OCR Accuracy of Claude on Amazon Bedrock and Amazon Textract Using Similarity Metrics
まとめ
今回はこのAnthropic Claude 3.5 Sonnetによる画像認識機能を使用して、Stability AI Stable Diffusion XL(SDXL)で生成した画像を検証し、要件を満たすまで再生成するAmazon Bedrockの使用例を紹介しました。
今回の試みの結果からClaude 3.5 Sonnetの画像認識機能がOCRだけではなく、画像に何が描かれているかや何を表現しているかも認識することがわかり、画像が要件を満たしているかの検証にも使用できることがわかりました。
また、Claude 3.5 Sonnetでプロンプトの内容そのものを修正して最適化できることも確認できました。
そして何よりも、これらの機能によって生成画像の要件充足を自動的に判定することで、人間による目視確認の作業量を大幅に削減できました。
このClaude 3.5 Sonnetの画像認識機能や高いテキスト編集機能は、今回の例のような他の画像生成AIモデルの制御をはじめとして、今まで自動化が難しかった処理に応用できる可能性があります。
これからもAnthropic ClaudeモデルをはじめとしたAmazon BedrockがサポートするAIモデルのアップデート、実装方法、他のサービスとの組み合わせなどを継続的にウォッチしていきたいと思います。
- [English Edition] Using Amazon Bedrock to repeatedly generate images with Stable Diffusion XL via Claude 3.5 Sonnet until requirements are met