AWS 기술 블로그

Amazon Bedrock Titan 이미지 생성기로 Amazon Rekognition 데이터 세트 만들기

Amazon의 완전관리형 이미지/비디오 검색 및 분석 서비스인 Amazon Rekognition의 경우, 데이터 과학자와 같은 전문 인력은 필요 없을 정도로 쉽게 딥러닝 모델을 학습, 배포할 수 있는 서비스를 제공하고 있습니다. 하지만, 커스텀 라벨링을 통한 비즈니스에 고유한 객체 인식 서비스를 개발하기 위한 학습/테스트용 데이터셋 중 특히 불량/이상 데이터의 경우, 사례 발생 건수가 부족하여 실 데이터 확보가 어려울 수 있습니다. 특정 이미지 데이터를 확보하기 어려워 데이터셋의 불균형이 발생하면 객체 인식 서비스 품질은 저하되고, 서비스 개발 과정에 현실적으로 가장 해결하기 어려운 문제가 되는 경우가 발생하곤 합니다.

본 게시글은 Amazon Bedrock에 기 훈련된(pre-built)된 이미지 생성 모델인 Titan Image Generator 모델을 원본 이미지 데이터 증강 (Data Augmentation)에 활용하여 Amazon Rekognition 객체 인식을 위한 모델 학습 및 테스트 시 확보가 어려운 도메인 특화 합성 데이터(synthetic data)를 생성합니다. 또한, Amazon Rekognition 자동 라벨링 기능의 학습 및 테스트 데이터셋으로 활용하여 쉽고, 빠르게 특정 도메인에 특화된 이미지 추론 모델을 확보하고 효과적으로 이미지 기반의 객체 인식 서비스의 활용 가능성을 탐색하는 과정을 설명합니다.

아키텍처 개요

Amazon Bedrock에 기 훈련된 Amazon Titan 모델은 대규모 데이터 세트를 대상으로 사전 훈련하여 다양한 사용 사례를 지원하는 동시에 AI의 책임 있는 사용을 지원하도록 구축된 강력한 범용 모델입니다. 특히 Titan Image Generator 모델은 고품질의 다양한 데이터를 대상으로 훈련되었으며, 속성이 포괄적이고 왜곡이 제한된, 사실적인 이미지와 같은 보다 정확한 결과를 생성합니다. 해당 모델을 사용하면 텍스트 설명을 기반으로 여러 이미지 옵션으로 이미지 생성을 쉽게 반복할 수 있으며 여러 객체가 포함된 복잡한 프롬프트를 바탕으로 관련 이미지를 생성할 수 있습니다. 아래 다이어그램은 전체적인 아키텍처를 보여주고 있습니다.

ML 엔지니어는 Amazon S3 원본 이미지 기반으로 SageMaker 노트북을 통해 Bedrock의 Amazon Titan Image Generator모델이 학습 및 테스트용 이미지를 생성하게 합니다. 이때 ML engineer는 이미지생성에 필요한 파라미터를 조절하여 보다 다양한 이미지가 생성되도록 설정 할 수 있습니다. 생성된 합성이미지(synthetic Image)는 다시 S3의 특정 bucket에 저장하여 Aamzon Rekognition이 Custom Label 기능의 학습 및 데이터셋으로 import 하게 합니다. 기 보유한 데이터와 추가적으로 생성된 데이터를 바탕으로 Amazon Rekognition Custom Label 모델을 학습하고, 학습된 모델을 시작하여 client가 객체 인식 서비스를 호출 하도록 합니다.

단계 요약

  • 단계1 : Amazon Bedrock Titan Image Generator 모델로 학습 / 테스트용 이미지 증강하기
  • 단계2 : S3 bucket으로 학습/테스트용 이미지 업로드
  • 단계3 : Amazon Rekognition 데이터 집합 생성하기
  • 단계4 : 객체 인식을 위한 모델 훈련 및 평가 수행하기

사전 준비 사항

실습을 진행하기 위해 아래와 같은 사항을 준비 해야 합니다.

  • AWS 계정
  • Amazon SageMaker Studio
    • Execution Role에는 추가적으로 Amazon S3, Amazon Bedrock 등 필요한 역할 부여가 필요합니다.
  • Amazon Bedrock
    • 실습을 위해서는 Amazon Bedrock Model access에 접속, “Manage Model Access”를 선택하여 Titan Image Generator 모델 사용을 요청해야 합니다.

  • Amazon Rekognition
  • S3 bucket

단계 1: Amazon Bedrock Titan Image Generator 모델로 학습 / 테스트용 이미지 증강(Image Augmentation) 하기

입고되는 상품의 박스 상태를 식별하여 박스가 손상된 경우 추가 검수 서비스를 고객에게 제공하는 3자 물류 유스케이스를 가정해보겠습니다. 손상된 박스 이미지의 경우 빈번하게 발생하지 않아 데이터가 부족하고 박스 손상의 형태도 다양하기 때문에 주어진 몇개의 샘플이미지를 바탕으로 합성이미지를 증강하는 작업을 Amazon Bedrock서비스를 활용해 수행하겠습니다. 아래처럼 boto3로 bedrock client를 정의한 뒤, 원본이미지를 바탕으로 IMAGE_VARIATION 작업을 수행하는 json body를 정의합니다.

prompt = "photo-realistic"
negative_prompt = "bad quality, low resolution, cartoon"
numberOfImages = 1   # Range: 1 to 5 
quality = "standard" # Options: standard or premium
height = 512        # Supported height list in the docs 
width = 512          # Supported width list in the docs
cfgScale = 6      # Range: 1.0 (exclusive) to 10.0
seed = 2000     # Range: 0 to 214783647

body = json.dumps(
    {
        "taskType": "IMAGE_VARIATION",
        "imageVariationParams": {
             "text": prompt,  # Optional,
             "negativeText": negative_prompt,   # Optional,
             "images": [image_to_base64(original)], # One image is required
         },
        "imageGenerationConfig": {
            "numberOfImages": numberOfImages,   
            "quality": quality,  
            "height": height,
            "width": width,
            "cfgScale": cfgScale, 
            "seed": seed 
        }
    }
)

modelId = "amazon.titan-image-generator-v1"

response = bedrock_runtime.invoke_model(
    body=body, 
    modelId=modelId,
    accept="application/json", 
    contentType="application/json"
)
response_body = json.loads(response.get("body").read())

images = [Image.open(BytesIO(base64.b64decode(base64_image))) for base64_image in response_body.get("images")]

Amazon Bedrock에 전달하는 json body에 포함된 imageVariationParams, imageGenerationConfig 설정에 따라 이미지 생성 결과를 제어할 수 있습니다. prompt값과 negative_prompt를 어떻게 넣는지에 따라 결과는 크게 달라질 수 있기 때문에 유스케이스에 따라 적절한 프롬프트를 넣는 것이 매우 중요합니다. 프롬프트 엔지니어링에 대해 이해하려면 Bedrock에서 제공하는 프롬프트 엔지니어링 가이드라인 문서를 참고하시기 바랍니다. 원본 이미지를 imageVariationParams으로 전달하려면 base64형태로 인코딩을 해야합니다. imageVariationParams의 images는 변형을 생성할 base64로 인코딩된 이미지 목록입니다. 아래와 같은 코드로 로딩된 이미지를 base64 포맷으로 인코딩할 수 있으며 현재 하나의 이미지만 전달가능합니다. 이미지를 base64로 인코딩하는 방법과 base64로 인코딩된 문자열을 디코딩하여 이미지로 변환하는 방법에 대한 추가적인 예제는 코드 예시를 참조하세요.

def image_to_base64(img) -> str:
    """Convert a PIL Image or local image file path to a base64 string for Amazon Bedrock"""
    if isinstance(img, str):
        if os.path.isfile(img):
            print(f"Reading image from file: {img}")
            with open(img, "rb") as f:
                return base64.b64encode(f.read()).decode("utf-8")
        else:
            raise FileNotFoundError(f"File {img} does not exist")
    elif isinstance(img, Image.Image):
        buffer = io.BytesIO()
        img.save(buffer, format="PNG")
        return base64.b64encode(buffer.getvalue()).decode("utf-8")
    else:
        raise ValueError(f"Expected str (filename) or PIL Image. Got {type(img)}")

imageGenerationConfig파라미터에 포함된 cfgScale 값은 생성된 이미지가 프롬프트를 얼마나 강하게 준수해야 하는지 지정합니다. 큰 값은 이미지 생성 시 제시된 프롬프트에 보다 적합한 이미지가 생성되며, 낮은 값을 사용하면 더 많은 무작위성을 적용할 수 있습니다. seed값은 생성 결과를 제어하고 재현하는 데 사용합니다. Amazon Bedrock의 invoke_model 메서드를 반복 호출해서 대량의 이미지를 증강하는 경우, 상기 코드를 함수로 정의하고, 반복 호출 시 마다 seed값을 변경해야 보다 다양한 결과 이미지를 생성할 수 있으니 유의하시기 바랍니다.

단계 2: S3 bucket으로 학습/테스트용 이미지 업로드

Amazon Rekognition의 학습/테스트 이미지로 활용하기 위해 Amazon Bedrock에서 생성된 이미지를 s3에 업로드합니다. boto3로 s3 client를 정의한 뒤, 아래와 같은 코드를 수행하여 invoke_model메서드의 통해 생성된 이미지 목록을 특정 s3 bucket에 업로드합니다. 아래 예제에서는 파일 객체를 로컬폴더에 저장하지 않고, 메모리에 저장한 뒤 곧바로 전송하기 때문에 s3.upload_fileobj 메서드를 사용하였습니다. IMAGE_KEY는 s3에 저장할 버킷명과 폴더, 이미지명을 나타냅니다. 이미지 저장 시 이미지의 특성에 맞게 폴더명을 지정(예시 : normal / damaged 등) 하시면 “폴더 이름을 기준으로 이미지에 이미지 수준 레이블 자동 할당” 기능을 이용해 보다 손쉽게 이미지 레이블을 지정할 있으니 참고하시기 바랍니다.

response = bedrock_runtime.invoke_model(
    body=body, 
    modelId=modelId,
    accept="application/json", 
    contentType="application/json"
)
response_body = json.loads(response.get("body").read())
images = [Image.open(BytesIO(base64.b64decode(base64_image))) for base64_image in response_body.get("images")]

for index, img in enumerate(images):
    IMAGE_KEY = prefix + "random_generated_" + datetime.today().strftime("%Y-%m-%d-%H:%M:%S") + "-" + str(index) + ".png"
    in_mem_file = io.BytesIO()
    img.save(in_mem_file, format=img.format)
    in_mem_file.seek(0)
    s3.upload_fileobj(
        Fileobj=in_mem_file,
        Bucket=BUCKET,
        Key=IMAGE_KEY,
        ExtraArgs={"ContentType":"image/png"},
        Callback=None,
        Config=None)        

단계 3: Amazon Rekognition 데이터 집합 생성하기

Amazon Rekognition에서 사용자 지정 레이블을 기반으로 모델을 학습하고 서비스하기 위해서는 데이터 집합 생성, 모드 훈련, 모델 평가 순으로 프로젝트 진행이 필요합니다. 먼저 데이터 집합을 생성하는 과정을 살펴보겠습니다. Rekognition 콘솔의 좌측 패널에서 “사용자 지정 레이블 사용 ”을 클릭하고 다음화면에서 “프로젝트” 섹션으로 이동하여 “프로젝트 생성”을 시작합니다. 프로젝트가 생성되고 나면 “데이터 세트 생성”을 클릭하여 아래와 같이 Amazon Bedrock에서 활용 가능한 Titan Image Generator 모델을 통해 증강된 이미지들을 저장한 s3 bucket URI를 지정합니다. 데이터 세트 생성 시 지원되는 이미지 형식(JPG, PNG)과 데이터 집합당 최대 이미지 개수(250,000) 최소/최대 이미지 크기(64 x 64 / 4096 x 4096) 등 제약사항에 유의하시기 바랍니다.

“폴더 이름을 기준으로 이미지에 이미지 수준 레이블 자동 할당”을 체크하면 하위 폴더 별로 이미지에 대한 Label을 자동으로 지정할 수 있습니다. 이미지를 업로드 한뒤에도 “이미지 레이블 지정” 기능을 활용하여 각각 이미지를 특정 레이블로 할당할 수 있지만, 본 기능을 사용하면 대량의 데이터에 대한 라벨 지정을 보다 손쉽게 할 수 있습니다.

마지막으로 해당 S3 Bucket에 접근권한을 Amazon Rekognition이 부여받는데 필요한 아래와 같은 Bucket Access Policy가 자동 생성됩니다. 해당 정책을 복사하여 해당 Bucket의 버킷 정책에 추가하여 버킷에 저장된 객체에 대한 액세스 권한을 설정합니다. 모든 설정을 확인한 뒤 “데이터 집합 생성” 버튼을 클릭하면 Amazon Rekognition모델 학습에 필요한 데이터 집합이 생성됩니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AWSRekognitionS3AclBucketRead20191011",
            "Effect": "Allow",
            "Principal": {
                "Service": "rekognition.amazonaws.com"
            },
            "Action": [
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::<YOUR BUCKET>"
        },
        {
            "Sid": "AWSRekognitionS3GetBucket20191011",
            "Effect": "Allow",
            "Principal": {
                "Service": "rekognition.amazonaws.com"
            },
            "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectVersion",
                "s3:GetObjectTagging"
            ],
            "Resource": "arn:aws:s3:::<YOUR BUCKET>/*"
        },
        {
            "Sid": "AWSRekognitionS3ACLBucketWrite20191011",
            "Effect": "Allow",
            "Principal": {
                "Service": "rekognition.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::<YOUR BUCKET>"
        },
        {
            "Sid": "AWSRekognitionS3PutObject20191011",
            "Effect": "Allow",
            "Principal": {
                "Service": "rekognition.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<YOUR BUCKET>/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

데이터 집합 생성이 완료되면, 프로젝트 화면에서 “레이블 추가”버튼을 클릭하여 s3 bucket의 해당 이미지들이 훈련용, 테스트용 데이트 세트로 구성되어 있는 목록을 확인합니다. 레이블에 대한 수정이 필요한 경우 레이블 조정 기능을 통해 새롭게 레이블을 할당 할 수 있습니다.

단계 4: 객체 인식을 위한 모델 훈련 및 평가 수행하기

Amazon Bedrock Titan Image Generator 모델을 통해 부족한 학습/테스트 이미지를 증강하여 충분한 데이터 세트를 구성하고 이미지 라벨링이 완료되었다면, AWS콘솔, Amazon Rekognition SDK를 활용하여 모델 훈련을 수행합니다. 모델 훈련 시 태그를 추가하여 모델을 추적할 수 있으며, 자체 AWS Key Management Service 키로 이미지를 암호화할 수도 있습니다. 모델을 성공적으로 훈련하는 데 걸리는 시간만큼 요금이 부과됩니다. 일반적으로 교육을 완료하는 데 30분에서 24시간이 소요됩니다. 자세한 정보는 훈련 시간 섹션을 참조하시기 바랍니다.

모델 훈련이 완료되면, 전체 테스트 데이터 세트에 대한 테스트 결과에 대한 F1 score, precision, recall 등 모델 훈련 지표를 각 사용자 지정 레이블에 대한 지표와 함께 반환합니다. 각 지표에 대한 상세내용과 개선방법은 모델 평가를 위한 지표 페이지를 참고하시기 바랍니다.

훈련된 모델은 “모델 시작”을 통해 이미지를 입력으로 객체를 인식하는 이미지 분석을 수행할 수 있습니다. 모델 시작 시 아래와 같은 코드 샘플이 자동으로 생성 되므로 해당 코드를 바탕으로 실제 이미지 분석에 필요한 커스텀 코드를 작성할 수 있습니다.

#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#PDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-rekognition-custom-labels-developer-guide/blob/master/LICENSE-SAMPLECODE.)

import boto3
import io
from PIL import Image, ImageDraw, ExifTags, ImageColor, ImageFont

def display_image(bucket,photo,response):
    # Load image from S3 bucket
    s3_connection = boto3.resource('s3')

    s3_object = s3_connection.Object(bucket,photo)
    s3_response = s3_object.get()

    stream = io.BytesIO(s3_response['Body'].read())
    image=Image.open(stream)

    # Ready image to draw bounding boxes on it.
    imgWidth, imgHeight = image.size
    draw = ImageDraw.Draw(image)

    # calculate and display bounding boxes for each detected custom label
    print('Detected custom labels for ' + photo)
    for customLabel in response['CustomLabels']:
        print('Label ' + str(customLabel['Name']))
        print('Confidence ' + str(customLabel['Confidence']))
        if 'Geometry' in customLabel:
            box = customLabel['Geometry']['BoundingBox']
            left = imgWidth * box['Left']
            top = imgHeight * box['Top']
            width = imgWidth * box['Width']
            height = imgHeight * box['Height']

            fnt = ImageFont.truetype('/Library/Fonts/Arial.ttf', 50)
            draw.text((left,top), customLabel['Name'], fill='#00d400', font=fnt)

            print('Left: ' + '{0:.0f}'.format(left))
            print('Top: ' + '{0:.0f}'.format(top))
            print('Label Width: ' + "{0:.0f}".format(width))
            print('Label Height: ' + "{0:.0f}".format(height))

            points = (
                (left,top),
                (left + width, top),
                (left + width, top + height),
                (left , top + height),
                (left, top))
            draw.line(points, fill='#00d400', width=5)

    image.show()

def show_custom_labels(model,bucket,photo, min_confidence):
    client=boto3.client('rekognition')

    #Call DetectCustomLabels
    response = client.detect_custom_labels(Image={'S3Object': {'Bucket': bucket, 'Name': photo}},
        MinConfidence=min_confidence,
        ProjectVersionArn=model)

    # For object detection use case, uncomment below code to display image.
    # display_image(bucket,photo,response)

    return len(response['CustomLabels'])

def main():

    bucket='MY_BUCKET'
    photo='MY_IMAGE_KEY'
    model='arn:aws:rekognition:us-east-1:<YOUR ACCOUNT>:project/<YOUR PROJECT>/version/damangedBoxPjt1.2023-12-06T23.34.44/1701873284276'
    min_confidence=95

    label_count=show_custom_labels(model,bucket,photo, min_confidence)
    print("Custom labels detected: " + str(label_count))


if __name__ == "__main__":
    main()

해당 코드를 바탕으로 Titan Image Generator 모델로 생성한 이미지들에 대해 객체 인식을 수행한 결과들은 다음과 같습니다. 이처럼 Amazon Bedrock Titan Image Generator 모델과 Amazon Rekognition을 활용하면 현장의 데이터가 부족하거나 없는 상황에서도 단기간에 특정 도메인에 특화된 객체 인식 서비스에 대한 프로젝트 과정을 빠르게 수행할 수 있어, 프로젝트 전체 진행에 대한 가시성을 확보하면서 서비스의 효용성을 검증할 수 있습니다.

리소스 정리하기

실습을 완료한 후 불필요한 과금을 방지하기 위해 사용했던 리소스를 삭제합니다.

  1. Amazon Rekognition 콘솔에 접속하여 활성화된 모델을 중지하고 해당 프로젝트를 삭제합니다.
  2. Amazon SageMaker의 노트북 인스턴스, 노트북 App을 중지하고 S3 bucket을 삭제하여 불필요한 과금이 되지 않도록 주의하시기 바랍니다.

결론

Amazon Bedrock에 pre-built된 Titan Image Generator 모델을 활용하면 원본 이미지를 바탕으로 사실적인 이미지들을 다양하게 생성하여 부족한 데이터셋을 증강할 수 있고 생성된 데이터셋을 s3업로드하는 절차를 자동화하여 Amazon Rekognition의 자동 라벨링 기능의 학습 및 테스트 데이터셋으로 활용 할 수 있습니다. AWS에서 제공하는 생성형 AI기술을 활용하여, 보다 효과적으로 도메인에 특화된 이미지 기반의 객체 인식 서비스의 활용 가능성을 탐색 하시기 바랍니다.

참조 사례

https://www.aboutamazon.com/news/retail/generative-ai-trains-amazon-one-palm-scanning-technology

Hyo Choi

Hyo Choi

최효근 솔루션 아키텍트는 소프트웨어 개발자, 아키텍트, 데이터 플랫폼 제품팀 경험을 바탕으로 엔터프라이즈 기업이 AI/ML 최신 기술을 실제 현장에 도입하는데 직면하는 실질적인 문제들을 함께 고민하고 해결해왔습니다. 최근에는 디지털 기업의 다양한 워크로드에 적합한 클라우드 아키텍처 설계, AWS의 AI/ML 플랫폼과 서비스를 적용하는데 도움을 주고자 노력하고 있습니다.