引言
在之前的文章中,我们介绍了利用Anthropic Claude 3.5 Sonnet的图像理解与分析功能,通过Stability AI Stable Diffusion XL (SDXL)生成的图像在Amazon Bedrock上进行验证和再生成的使用案例。
使用Claude 3.5 Sonnet和Stable Diffusion XL:如何通过Amazon Bedrock不断优化图像生成直到满足需求
在本文中,我们将介绍如何利用Anthropic Claude 3.5 Sonnet的图像理解与分析功能,在Amazon Titan Image Generator G1上生成的图像进行验证和再生成的Amazon Bedrock使用案例。本次尝试与前述文章相同,通过自动判定生成图像是否满足要求,以减少人类进行目视确认的工作量。
※本文及本作者的其他文章中展示的源码均为自主研究活动的一部分。使用时请自行承担风险。同时,代码可能会在没有通知的情况下进行修改,请谅解。 ※本文写作过程中使用的AWS服务是在个人注册的AWS账户上执行的。 ※本文写作过程中使用的Amazon Bedrock各模型均于2024年7月23日执行,并基于当时的最终用户许可协议 (EULA)。 Anthropic Claude 3.5 Sonnet(anthropic.claude-3-5-sonnet-20240620-v1:0): Anthropic on Bedrock - 商业服务条款(生效日期:2024年1月2日) Amazon Titan Image Generator G1(amazon.titan-image-generator-v1): 最终用户许可协议 (EULA)(AWS客户协议和服务条款)
Using Claude 3.5 Sonnet Vision Capabilities on Amazon Bedrock to Verify, Regenerate, and Automate Image Generation with Amazon Titan Image Generator G1
详细说明这个处理流程
1.输入包含提示或参数的事件。
2-1. 使用输入的提示在Amazon Bedrock上运行Titan Image Generator G1模型生成图像。
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重新开始处理。
- 如果超出修正提示执行次数,则作为错误处理结束。
实现示例
输入事件的格式
{
"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模型生成的最大令牌数],
"titan_img_cfg_scale": [Titan Image Generator G1模型的CFG比例],
"titan_img_width": [Titan Image Generator G1模型生成图像的宽度(以像素为单位)],
"titan_img_height": [Titan Image Generator G1模型生成图像的高度(以像素为单位)],
"titan_img_number_of_images": [Titan Image Generator G1模型生成的图像数量],
"titan_img_seed": [Titan Image Generator G1模型使用的随机种子值(用于复现,未指定则为随机)]
}
输入事件的示例
{
"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-taitan",
"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,
"titan_img_cfg_scale": 10.0,
"titan_img_width": 1024,
"titan_img_height": 1024,
"titan_img_number_of_images": 1,
"titan_img_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-taitan",
# "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,
# "titan_img_cfg_scale": 10.0,
# "titan_img_width": 1024,
# "titan_img_height": 1024,
# "titan_img_number_of_images": 1,
# "titan_img_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 titan_img_invoke_model(prompt, model_params={}):
seed = model_params.get('seed', 0)
if seed == 0:
seed = random.randint(0, 2147483646)
optimized_prompt = truncate_to_512(prompt)
body = {
"taskType": "TEXT_IMAGE",
"textToImageParams": {
"text": optimized_prompt
},
"imageGenerationConfig": {
"numberOfImages": model_params.get('img_number_of_images', 1),
"height": model_params.get('height', 1024),
"width": model_params.get('width', 1024),
"cfgScale": model_params.get('cfg_scale', 8),
"seed": seed
}
}
print(f"Titan Image Generator G1 model parameters: {body}")
response = bedrock_runtime_client.invoke_model(
body=json.dumps(body),
modelId="amazon.titan-image-generator-v1",
contentType="application/json",
accept="application/json"
)
response_body = json.loads(response['body'].read())
image_data = base64.b64decode(response_body.get("images")[0].encode('ascii'))
finish_reason = response_body.get("error")
if finish_reason is not None:
print(f"Image generation error. Error is {finish_reason}")
else:
print(f"Image generated successfully with seed: {seed}")
return image_data
def truncate_to_512(text):
if len(text) <= 512:
return text
truncated = text[:512]
last_period = truncated.rfind('.')
last_comma = truncated.rfind(',')
last_break = max(last_period, last_comma)
if last_break > 256: # Only if the last sentence or phrase is not too long
return truncated[:last_break + 1]
else:
return truncated
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 Titan Image Generator G1, incorporating best practices:
{original_prompt}
Please consider the following guidelines in your revision:
1. Start the prompt with "An image of..." and be specific and descriptive, using vivid adjectives and clear nouns.
2. Include detailed descriptions about composition, lighting, style, mood, color, and medium if relevant.
3. Mention specific artists or art styles if relevant, though this is not emphasized in Titan's guidelines.
4. Use descriptive keywords like "highly detailed" if appropriate. While "4k", "8k", or "photorealistic" can be used, they are not specifically emphasized for Titan.
5. Separate different concepts with commas, using them to structure the prompt logically.
6. Place more important elements, especially the main subject, at the beginning of the prompt.
7. Consider using negative prompts to specify what should NOT be included in the image.
8. If the original prompt is not in English, translate it to English.
9. Use double quotes instead of single quotes for any quoted text within the prompt.
10. Provide context or background details to help improve the realism and coherence of the generated image.
11. Ensure the final prompt is no longer than 500 characters. Prioritize the most important elements if you need to shorten the prompt.
Your goal is to create a clear, detailed prompt that will result in a high-quality image generation with Titan Image Generator G1, while staying within the 500-character limit.
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)
}
titan_img_params = {
'cfg_scale': event.get('titan_img_cfg_scale', 8),
"width": event.get('titan_img_width', 1024),
"height": event.get('titan_img_height', 1024),
'img_number_of_images': event.get('titan_img_number_of_images', 1),
"seed": event.get('titan_img_seed', 0)
}
print(f"Claude validate params: {claude_validate_params}")
print(f"Claude revise params: {claude_revise_params}")
print(f"Titan Image Generator G1 params: {titan_img_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 Titan Image Generator G1
image_data = titan_img_invoke_model(prompt, titan_img_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 进行生成图像的验证和提示的修正。
- 使用 Titan Image Generator G1 生成高质量的图像。
- 在提示修正指示中融入了 Amazon Titan Image Generator Prompt Engineering Best Practices 中的推荐做法。
- 使图像生成参数(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-taitan",
"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,
"titan_img_cfg_scale": 10.0,
"titan_img_width": 1024,
"titan_img_height": 1024,
"titan_img_number_of_images": 1,
"titan_img_seed": 0
}
在此次执行示例的输入参数中,我们做了以下改进:
- 将
max_retry_attempts
设置为 5,以提高图像生成的成功率。 - 将
max_prompt_revisions
设置为 5,以增加在必要时改进提示的机会。 - 精细设置了用于图像验证和修正的 Claude 模型参数(temperature、top_p、top_k、max_tokens)。
- 将
titan_img_cfg_scale
设置为 10,以提高对提示的忠实度。 - 将用于图像生成的
seed
设置为随机值,以确保每次生成不同的图像。
执行示例:结果
生成的图像
在此次试验中,最终满足提示要求并通过验证的图像如下所示。该图像几乎完全符合“从自然中看到的夜景,空中有极光、月亮和流星群,地面上是广阔的海洋和流冰,地平线上升起着太阳的无人照片。”这一要求(尽管地平线上的太阳这一构图的体现较弱,但月亮和地平线上的太阳这一矛盾的景象以及流星群和流冰得到了很好的表现)。
此外,与最终通过验证的图像相比,其他在最终验证之前生成的图像(参见后述的“生成的图像列表”)也可以确认,最终通过验证的图像更好地满足了指示的要求。
此次试验中生成的图像列表如下所示。这个“生成的图像列表”中的每一行图像都是从不同的修正提示中生成的。最初输入的日语句子的提示生成的图像与要求相差较大,而在第一次提示修正后,生成的图像则满足了要求。
生成的图像列表
修正后的提示变化
在上述“生成的图像列表”中的每一行图像都是从不同的修正提示中生成的。具体来说,“生成的图像列表”中第一行的图像是从以下的“修正0次”提示中生成的,而最后一行的图像是从以下的“修正1次”提示中生成的。
让我们逐一查看每次提示修正后的图像生成提示内容。
第0回修正
从自然中看到的夜景,空中有极光、月亮和流星群,地面上是广阔的海洋和流冰,地平线上升起着太阳的无人照片。
第1回修正
An image of a breathtaking nighttime landscape viewed from nature, featuring a vibrant aurora borealis dancing across the sky, accompanied by a luminous full moon and a dazzling meteor shower. In the foreground, a vast, dark sea stretches to the horizon, dotted with floating ice floes. The sun is just beginning to rise at the horizon, casting a warm glow on the icy waters. The scene is devoid of human presence, highly detailed, with a serene and mystical atmosphere.
特别是与上述“生成的图像列表”相关联来看,最初输入的日语句子的提示未经过图像生成优化,因此输出的图像与要求相差较大。另一方面,在第一次修正中,通过 Claude 3.5 Sonnet 将提示优化为适合图像生成的形式,随后执行的图像几乎完全符合要求。
如上所述,随着提示的每次修正和每次生成执行,图像会发生变化,最终满足提示要求的图像将通过验证。
<参考资料>
AWS Documentation(Amazon Bedrock)
Amazon Titan Image Generator Prompt Engineering Best Practices
总结
此次介绍了利用 Anthropic Claude 3.5 Sonnet 的图像理解与分析功能,结合 Amazon Titan Image Generator G1 生成图像并进行验证和再生成的 Amazon Bedrock 使用示例。
通过这一尝试,我们确认了 Claude 3.5 Sonnet 的图像识别功能不仅可以进行 OCR,还能识别图像的内容和表达,并用于验证要求的满足情况。此外,我们还发现 Claude 3.5 Sonnet 可以用于 Titan Image Generator G1 的提示优化,尤其是在将日语提示翻译成英语并修正为适合图像生成的格式方面表现出色。
最重要的是,通过自动化图像生成和验证的循环,我们大幅减少了人工目视检查的工作量。
值得注意的是,与之前使用 Stable Diffusion XL 的示例以及此次使用 Amazon Titan Image Generator G1 的示例类似,我们需要根据每种图像生成 AI 的最佳实践来调整提示修正指示。这种方法可以实现有效的提示优化和图像生成自动化,并有望应用于其他各种图像生成 AI。
因此,Claude 3.5 Sonnet 为图像生成 AI(如 Titan Image Generator G1 和 Stable Diffusion XL)的控制以及以前难以自动化的处理带来了新的可能性。未来,我们将继续关注 Amazon Bedrock 提供的 AI 模型的进化及其新实施方法,并探索进一步应用范围的扩展。
标签:prompt,claude,max,image,生成,Amazon,图像,top From: https://blog.csdn.net/rralucard123/article/details/140729328