AWS 기술 블로그

Amazon Rekognition과 Personalize를 이용하여 감정으로 이미지 추천하기

카메라로 사람의 표정을 분석하여 현재의 감정(Emotion)을 얻을 수 있다면, 개인화된 추천 시스템에서 유용하게 활용할 수 있습니다. 여기서는 Amazon Rekognition을 이용하여 사용자의 감정을 얻고, 사용자의 감정을 잘 표현하는 이미지를 Amazon Personalize를 이용하여 추천합니다. 이를 통해 Amazon의 완전관리형 서비스인 Rekognition과 Personalize를 효과적으로 사용하는 방법을 이해할 수 있습니다. 또한 감정을 표현하는 이미지는 Amazon SageMaker JumpStart의 Stable Diffusion 모델을 이용해 생성합니다. Stable Diffusion 모델은 텍스트로 이미지를 생성하는 파운데이션 모델 (Foundation Model)로서, SageMaker JumpStart를 이용하면 감정을 표현할 수 있는 이미지들을 쉽게 만들어 낼 수 있습니다.

개인화 추천의 경우에 사용자의 이전 상호작용(interaction)을 이용하여 추천을 수행하게 되는데, 이전 상호작용이 없거나 충분하지 않은 경우에는 감정(emotion)에 맞는 적절한 추천을 할 수 없습니다. 따라서, 여기에서는 이전 상호작용이 부족한 경우에는 감정 정보를 바탕으로 미리 학습한 데이터를 이용하여 “감정 추천”을 수행하고, 일정량의 데이터가 확보되었을 때에 “개인화 추천”을 수행하는 방법을 이용합니다. 아래는 사용자의 감정에 따라 이미지를 추천하는 시스템 아키텍처를 보여주고 있습니다.

감정으로 이미지 추천하는 아키텍처

아래의 아키텍처(Archtiecture)에서는 사용자(user)에게 감정 기반의 이미지를 추천(Image Recommendation)하고 시스템 관리자(administrator)가 이미지를 생성(Image Creation)하는 구조로 구성됩니다.

이미지를 생성할 때에 SageMaker JumpStart에서 제공하는 Stable Diffusion 모델의 Endpoint를 이용하는데 GPU를 가진 EC2를 이용하므로 다수의 이미지를 일정 시간동안 빠르게 생성한 다음에 중지시키는 것이 비용 효율적입니다. 이를 위해서 Event driven 방식으로 FIFO 형태의 Amazon SQS를 사용하여 이미지를 생성하며, 생성된 이미지는 상용 폴더가 아닌 이미지풀(image pool)에 우선 저장합니다. 시스템 관리자는 이미지풀에 저장된 이미지가 적절하게 감정을 표현했는지 확인하고, 상용 폴더로 복사합니다. 상용 폴더에 다수의 이미지가 한꺼번에 들어올 수 있으므로 FIFO 형태의 SQS와 Amazon Lambda를 통해 순차적으로 아이템 데이터셋에 저장합니다. 이후 사용자가 카메라를 이용해 화면을 캡처하면, Amazon CloudFront / API Gateway를 통해 이미지가 인입되고 Lambda를 통해 Rekognition에서 이미지를 분석합니다. 이후 사용자가 감정에 따른 이미지 추천을 요청하면 Lambda를 통해 Personalize에 추천 추론(inference)을 요청합니다. 추천 추론의 결과는 이미지 리스트로서 웹페이지에 전달되어 보여집니다. 사용자는 자신의 감정에 맞는 이미지를 선택하여 선호(like)를 표시할 수 있으며, 이때 생성된 상호작용(interaction) 이벤트를 상호작용 데이터셋으로 저장하여 추천 추론(inferece)을 향상시키는 데에 활용합니다.

전체적인 신호 흐름도(signal flow)는 아래를 참조합니다.

  1. 시스템 관리자(administrator)는 감정(emotion)에 맞게 생성된 이미지를 S3 bucket에 복사합니다.
  2. 이미지가 S3 Bucket에 복사되면서 발생한 S3 put event를 이용하여 AWS Lambda (s3-event)를 통해 FIFO 형태의 SQS에 저장합니다. 이후 Lambda(putItem)를 이용하여 순차적으로 꺼내서 Personalize에 putItems로 전달하여 아이템 데이터셋을 생성합니다.
  3. 사용자가 카메라 앞에 있을 때에 Personalize의 detectFaces를 이용해서 감정(emotion)을 분석합니다.
  4. Personalize의 searchFaces을 이용하여 사용자 아이디(userId)를 확인합니다. 기존에 등록된 얼굴 정보가 없는 경우에는 Rekognition의 Collections에 신규로 등록합니다.
  5. 사용자 아이디(userId)가 신규인지 이미 생성되었는지를 DynamoDB 조회를 통해 확인하여, 신규인 경우에는 Personalize의 putUsers를 이용하여 Personalize의 사용자 데이터셋에 추가합니다.
  6. 사용자는 Personalize의 getRecommendations을 이용하여 “감정 추천” 및 “개인화 추천”을 이용합니다.
  7. 사용자가 감정 이미지를 선택하는 경우에 상호작용(interaction)을 Personalize의 putEvents을 이용하여 저장하여, 상호작용 데이터셋을 생성합니다.

상세 시스템 구현하기

감정(emotion) 확인

Rekognition의 얼굴 감지 및 분석(Facial analysis)을 이용하면, Facial analysis 예제와 같이 성별(Gender), 나이 범위(AgeRange), 감정(Emotion)등에 대한 정보를 얻을 수 있습니다. 특히 감정(Emotion)은 HAPPY, SURPRISED, CALM, ANGRY, FEAR, CONFUSED, DISGUSTED, SAD와 같이 8가지 형태의 결과를 얻을 수 있습니다.

lambda-emotion은 Rekognition에 얼굴분석을 요청하여 감정(emotion) 정보를 수집합니다. Rekognition은 bucket에 저장된 이미지를 이용하므로 먼저 S3에 저장을 수행합니다.

const destparams = {
    Bucket: bucketName,
    Key: fileName,
    Body: body,
    ContentType: contentType
};

await s3.putObject(destparams).promise();

Rekognition의 detectFaces을 이용하여 성별(gender) 및 감정(emotion)을 얻어옵니다.

const rekognition = new aws.Rekognition();
const rekognitionParams = {
    Image: {
        S3Object: {
            Bucket: bucketName,
            Name: fileName
        },
    },
    Attributes: ['ALL']
};

const data = await rekognition.detectFaces(rekognitionParams).promise();

if (data['FaceDetails'][0]) {
    const gender = profile['Gender']['Value'].toLowerCase();
    const emotions = profile['Emotions'][0]['Type'];
}

사용자가 감정 분석을 요청할 때는 gallery.js의 “getEmotion()”와 같이 REST API를 이용합니다.

const uri = "emotion";
const xhr = new XMLHttpRequest();

xhr.open("POST", uri, true);
xhr.onreadystatechange = () => {
    if (xhr.readyState === 4 && xhr.status === 200) {
        let response = JSON.parse(xhr.responseText);
        
        gender = response.gender;
        emotionValue = response.emotions.toLowerCase();
    }
};

canvas.toBlob(function (blob) {
    xhr.send(blob);
});

사용자(userId) 확인

Face collections을 이용하여 이미지에 있는 사용자를 인지하고자 합니다. CDK Stack에서는 아래와 같이 이미지에서 추출된 얼굴에 대한 collections을 생성합니다.

const collectionId = 'image-recommender-collectionId';
const cfnCollection = new rekognition.CfnCollection(this, 'MyCfnCollection', {
    collectionId: collectionId,
});

SearchFacesByImage를 이용하여 기존에 collection에 유사 이미지가 있는지 확인할 수 있습니다. lambda-emotion에서는 아래와 같이 searchFacesByImage을 이용하여 매칭되는 얼굴 이미지를 찾습니다.

const searchFacesByImageParams = {
    CollectionId: collectionId,
    FaceMatchThreshold: 80, 
    Image: {
        S3Object: {
            Bucket: bucketName,
            Name: fileName
        },
    },
};
const data = await rekognition.searchFacesByImage(searchFacesByImageParams).promise();
faceId = data['FaceMatches'][0]['Face'].FaceId;

유사한 얼굴 이미지가 등록되어 있지 않다면, IndexFaces을 이용하여 이미지에서 추출된 얼굴로 userId를 생성합니다. lambda-emotion에서는 아래와 같이 매칭되는 얼굴 이미지가 없는 경우에 indexFaces를 이용하여 얼굴(Face)를 등록하고 faceId를 추출하여 userId로 사용합니다.

const indexFacesParams = {
    CollectionId: collectionId,
    Image: {
        S3Object: {
            Bucket: bucketName,
            Name: fileName
        },
    },
};
const data = await rekognition.indexFaces(indexFacesParams).promise();
console.log('data: '+JSON.stringify(data));

faceId = data['FaceRecords'][0]['Face'].FaceId;

Personalize의 User 정보 수집

얼굴로 인식한 사용자 정보를 Personalize에 사용자(User)로 등록합니다. Personalize의 사용자(User)에 대한 메타 정보는 USER_ID, GENDER, EMOTION 로 구성됩니다.

{
    "type": "record",
    "name": "Users",
    "namespace": "com.amazonaws.personalize.schema",
    "fields": [
        {
            "name": "USER_ID",
            "type": "string"
        },
        {
            "name": "GENDER",
            "type": "string",
            "categorical": true
        },
        {
            "name": "EMOTION",
            "type": "string",
            "categorical": true
        }
    ],
    "version": "1.0"
}

lambda-createUser에서는 DynamoDB에 기존에 이미 등록된 사용자가 있는지 확인하여 없는 경우에 Personalize에 사용자(User)로 등록합니다. 등록할 때에는 putUsers를 사용합니다.

let queryParams = {
    TableName: userTableName,
    KeyConditionExpression: "USER_ID = :userId",
    ExpressionAttributeValues: {
        ":userId": userId
    }
};

let dynamoQuery = await dynamo.query(queryParams).promise();

if (!dynamoQuery.Count) {
    // Personalize
    var params = {
        datasetArn: datasetArn,
        users: [{
            userId: userId,
            properties: {
                "GENDER": gender,
                "EMOTION": emotion
            }
        }]
    };
    await personalizeevents.putUsers(params).promise();
}

CDK Stack에서는 아래와 같이 사용자 스키마로 사용자 데이터셋을 정의합니다.

const userSchema = new personalize.CfnSchema(this, 'UserSchema', {
  name: 'image-recommender-user-schema',
  schema: userSchemaJson,
});

const userDataset = new personalize.CfnDataset(this, 'UserDataset', {
  datasetGroupArn: datasetGroup.attrDatasetGroupArn,
  datasetType: 'Users',
  name: 'image-recommender-user-dataset',
  schemaArn: userSchema.attrSchemaArn,
});

Personalize의 Item 정보 수집

이미지들이 S3에 저장될 때 발생하는 put event를 이용하여 Item 정보를 수집합니다. 이미지에 대한 하나의 아이템에 대한 메타 정보는 ITEM_ID, TIMESTAMP, EMOTION로 아래와 같이 구성합니다.

{
    "type": "record",
    "name": "Items",
    "namespace": "com.amazonaws.personalize.schema",
    "fields": [
        {
            "name": "ITEM_ID",
            "type": "string"
        },
        {
            "name": "TIMESTAMP",
            "type": "long"
        },
        {
            "name": "EMOTION",
            "type": "string",
            "categorical": true
        }
    ],
    "version": "1.0"
}

하나의 이미지가 S3 bucket에 저장될 때 “emotions/happy/img_20230423-101932_1h.jpeg”와 같이 저장되면 오브젝트의 경로를 이용하여 이미지가 분류되었던 감정(emotion)을 추출할 수 있습니다. S3의 “emotions” 폴더에 대한 이벤트 정보를 수집할 수 있도록 CDK Stack에서 “event source”로 아래와 같이 설정합니다.

const s3PutEventSource = new lambdaEventSources.S3EventSource(s3Bucket, {
    events: [
        s3.EventType.OBJECT_CREATED_PUT,
        s3.EventType.OBJECT_REMOVED_DELETE
    ],
    filters: [
        { prefix: 'emotions/' },
    ]
});
lambdaS3event.addEventSource(s3PutEventSource);

lambda-s3-event와 같이 파일 이름에서 감정을 분류합니다. 이미지 정보는 SQS에 저장되어 이후 lambda-putItem에서 이용됩니다.

const eventName = event.Records[i].eventName;       
console.log('eventName: ' + eventName);

const bucket = event.Records[i].s3.bucket.name;
const key = decodeURIComponent(event.Records[i].s3.object.key.replace(/\+/g, ' '));

let splitKey = key.split("/");
let emotion, fname;

emotion = splitKey[1];
fname = splitKey[2];

if (eventName == 'ObjectCreated:Put') {
    let date = new Date();
    const timestamp = Math.floor(date.getTime() / 1000.0);

    let searchKey;
    searchKey = emotion;

    const jsonData = {
        key: key,
        timestamp: timestamp,
        searchKey: searchKey
    };

    // push the event to SQS
    let params = {
        MessageDeduplicationId: key,
        MessageAttributes: {},
        MessageBody: JSON.stringify(jsonData),
        QueueUrl: sqsUrl,
        MessageGroupId: "putItem"  // use single lambda for stable diffusion 
    };

    await sqs.sendMessage(params).promise();
}

lambda-putItem은 아래와 같이 putItems을 이용하여 아이템에 대한 정보를 Personalize에 전달합니다.

let params = {
    datasetArn: datasetArn,
    items: [{
        itemId: key,
        properties: {
            "TIMESTAMP": timestamp,
            "EMOTION": searchKey,
        }
    }]
};

await personalizeevents.putItems(params).promise();

CDK stack에서는 아래와 같이 아이템 스키마로 아이템 데이터셋을 정의합니다.

const itemSchema = new personalize.CfnSchema(this, 'ItemSchema', {
  name: 'image-recommender-itemSchema',
  schema: itemSchemaJson,
});

const itemDataset = new personalize.CfnDataset(this, 'ItemDataset', {
  datasetGroupArn: datasetGroup.attrDatasetGroupArn,
  datasetType: 'Items',
  name: 'image-recommender-itemDataset',
  schemaArn: itemSchema.attrSchemaArn,
});

Personalize의 상호작용(interaction) 정보 수집

사용자의 event를 상호작용(interaction)로 등록하여 개인화 추천에 사용합니다. Personalize의 상호작용(interaction)에 대한 메타 정보는 USER_ID, ITEM_ID, TIMESTAMP, EVENT_TYPE, IMPRESSION로 구성됩니다.

{
    "type": "record",
    "name": "Interactions",
    "namespace": "com.amazonaws.personalize.schema",
    "fields": [
        {
            "name": "USER_ID",
            "type": "string"
        },
        {
            "name": "ITEM_ID",
            "type": "string"
        },
        {
            "name": "TIMESTAMP",
            "type": "long"
        },
        { 
            "name": "EVENT_TYPE",
            "type": "string"
        },
        {
            "name": "IMPRESSION",
            "type": "string"
        }
    ],
    "version": "1.0"
}

lambda-like에서는 Personalize의 상호작용(interaction) 정보를 등록합니다. 등록할 때에는 putEvents를 사용합니다.

let params = {            
    sessionId: itemId,
    trackingId: trackingId,
    userId: userId,
    eventList: [{
        eventType: "click", 
        sentAt: timestamp,
        eventId: uuidv4(),
        itemId: itemId,
        impression: impression, 
    }],
};
console.log('event params: ', JSON.stringify(params));

await personalizeevents.putEvents(params).promise();

CDK stack에서는 아래와 같이 상호작용 스키마로 상호작용 데이터셋을 정의합니다.

const interactionSchema = new personalize.CfnSchema(this, 'InteractionSchema', {
  name: 'image-recommender-interaction-schema',
  schema: interactionSchemaJson,
});

const interactionDataset = new personalize.CfnDataset(this, 'InteractionDataset', {
  datasetGroupArn: datasetGroup.attrDatasetGroupArn,
  datasetType: 'Interactions',
  name: 'image-recommender-interaction-dataset',
  schemaArn: interactionSchema.attrSchemaArn,
});

추천 리스트 읽어오기

“감정 추천”은 감정에 따라 추천을 수행할 수 있도록 사전에 정의한 상호작용 데이터를 기반으로 추천을 수행하고, “개인화 추천”은 “감정 추천”을 통해 사용자의 상호작용 데이터가 충분히 확보되면 이를 기반으로 추천을 수행합니다.

lambda-gallery에서는 사용자의 id를 가지고 해당 사용자에 대한 추천 이미지 리스트를 가져올 수 있습니다. 이때 추천정보를 가져올 수 있도록 PersonalizeRuntime을 이용합니다.

const aws = require('aws-sdk');
const personalizeRuntime = new aws.PersonalizeRuntime();

lambda-gallery에서는 Personalize로 추론(inference)을 요청합니다. 이때 getRecommendations로 userId에 대한 추천 리스트를 가져옵니다.

let userId = body['id'];
    
let recommendationParams = {
    campaignArn: campaignArn,
    userId: userId
};

let recommendation; 
try {
    recommendation = await personalizeRuntime.getRecommendations(recommendationParams).promise();
    console.log ('recommendation: ', JSON.stringify(recommendation));
} catch (error) {
    console.log(error);
    return;
}  

추천정보는 itemList로 내려오므로 “recommendation[‘itemList’]”로 리스트 정보를 확인할 수 있습니다.

let result = [];
for (let i in recommendation['itemList']) {
    let itemStr = recommendation['itemList'][i].itemId;
    console.log("itemStr: ", itemStr);

    const url = itemStr;
    console.log('url: ', url);

    const imgProfile = {
        url: url,
    };

    result.push(imgProfile);
}

let response = {
    statusCode: 200,
    body: JSON.stringify(result)
};

return response;

gallery에서는 “감정 추천”과 “개인화 추천”을 제공합니다. “감정 추천”의 경우에는 성별과 감정의 조합으로 추천을 수행하고 “개인화 추천”은 사용자 아이디와 감정의 조합으로 추천을 수행합니다. Rekognition에서 감정은 8가지로 분류되므로 상호작용으로 모아지는 추천 정보는 사용자당 최대 8개까지 저장될 수 있습니다.

if(type == 'emotionbase') {
    drawGallery(emotionValue, gender, `${gender}/${emotionValue}`);
}
else {  // userbase
    drawGallery(emotionValue, gender, `${userId}/${emotionValue}`);
}  

직접 실습 해보기

사전 준비 사항

이 솔루션을 사용하기 위해서는 사전에 아래와 같은 준비가 되어야 합니다.

SageMaker JumpStart로 Stable Diffusion 설치

여기에서는 Stable Diffusion 이미지 생성을 위한 인프라를 설치합니다. 편의상 인프라 설치와 관련된 설명은 서울 리전(Region)을 기준으로 구성합니다.

  1. SageMaker Studio Console에서 [Open Studio]를 선택합니다. Studio를 처음 사용한다면 Launch Amazon SageMaker Studio에 따라 Studio를 생성합니다.
  2. SageMaker Jumpstart에서 “Stable Diffusion 2 FP16″을 검색한 후에 “Open notebook”을 선택합니다.
  3. 노트북의 마지막으로 이동하여 아래와 같이 “model_predictor.delete_model()”와 “model_predictor.delete_endpoint()”을 주석처리 합니다.
  4. 상단 메뉴의 [Run] – [Run All Cells]를 선택하여 Stable Diffusion Endopoint를 생성합니다. 전체실행은 약 15분이 소요됩니다.
  5. Endpoint의 이름을 확인합니다.

노트북이 실행이 완료되면, SageMaker Endpoint로 접속하여 생성된 SageMaker Endpoint의 이름을 확인합니다. 여기서는 “jumpstart-example-model-txt2img-stabili-2023-04-22-16-31-10-149″라는 Endpoint가 생성되었습니다. Stable Diffusion 모델의 상세한 설치 방안은 관련 블로그를 참조하십시오.

Cloud9로 인프라 설치

Cloud9 Console에 접속하여 [Create environment] 이름으로 “Image Recommender”를 입력하고, EC2 instance는 편의상 “m5.large”를 선택합니다. 나머지는 기본값을 유지하고, 하단으로 스크롤하여 [Create]를 선택합니다.

[Environment]에서 “Image Recommender”를 [Open]한 후에 아래와 같이 터미널을 실행합니다.

CDK로 인프라 설치하기

소스를 다운로드 합니다.

curl https://raw.githubusercontent.com/aws-samples/generative-ai-demo-using-amazon-sagemaker-jumpstart-kr/main/blogs/image-recommender-based-on-emotion/image-recommender-based-on-emotion.zip -o image-recommender-based-on-emotion.zip && unzip image-recommender-based-on-emotion.zip

관련된 라이브러리를 설치합니다.

cd image-recommender-based-on-emotion/cdk-image-recommender/ && npm install

Account ID를 확인합니다.

aws sts get-caller-identity --query Account --output text

아래와 같이 bootstrap을 수행합니다. 여기서 “account-id”는 상기 명령어로 확인한 12자리의 Account ID입니다. bootstrap 1회만 수행하면 되므로, 기존에 cdk를 사용하고 있었다면 bootstrap은 건너뛰어도 됩니다.

cdk bootstrap aws://account-id/ap-northeast-2

Cloud9의 왼쪽 메뉴에서 “image-recommender-based-on-emotion/cdk-image-recommender/lib/cdk-image-recommender-stack.ts”을 오픈후에 Endpoint의 이름을 아래와 같이 업데이트 합니다.

이제 다시 터미널로 이동하여 cdk로 인프라를 설치합니다.

cdk deploy

실행이 완료 되면 아래와 같이 CDK의 Output을 확인할 수 있습니다.

여기서, “CopyHtml”을 터미널에서 실행하여 html로 만든 툴들을 복사합니다.

aws s3 cp ../html/ s3://[bucket]/ --recursive

이미지 생성

실습에 필요한 이미지를 “Image Generator”로 직접 생성하는 방법과 제공된 Sample 이미지들을 활용하는 방법에 대해 아래와 같이 설명합니다.

Image Generator로 이미지 생성 및 선택하기

CDK Output의 “ImageGenerator”에 있는 URL로 접속합니다. 아래와 같이 [RepeatCount]을 30으로 설정하고, [Emotion]로 “행복(HAPPY)”을 선택한 후에 [Generate] 버튼을 선택합니다. 이미지 생성 상태는 [Updata] 버튼을 통해 확인할 수 있습니다. 약 210초 후에 [Update] 버튼을 눌러서 이미지 상태를 확인합니다.

생성된 이미지가 적절하지 않다고 판단되면, 오른쪽의 “dislike”를 선택한 후에 [Remove]로 삭제합니다. Preview 접속은 CDK Output의 “Preview”에 있는 URL을 이용합니다.

동일한 작업을 “놀람(SURPRISED)”등 나머지 7개 감정에 대해 수행합니다. 이미지 생성이 완료가 되면, 이미지를 Personalize에 반영하기 위해 imgPool에 있는 이미지를 Cloud9으로 다운로드 합니다. 이때 [Bucket]은 S3 버킷 이름으로서 “CDK로 인프라 생성하기”의 CDK Output을 참조하여 입력합니다.

cd ~/ && aws s3 cp s3://[Bucket]/imgPool/ ./imgPool/ --recursive

“emotions” 폴더로 생성한 이미지들을 복사합니다.

aws s3 cp ./imgPool/ s3://[Bucket]/emotions/ --recursive

Sample 이미지를 이용하기

이제까지 “Image Generator”를 이용하여 직접 이미지를 생성하였습니다. 추천에 집중해서 실습을 하고자 할 경우에는 Sample 이미지를 활용할 수 있습니다. Copy 명령어는 CDK Output의 “CopySample”을 참조하여 아래처럼 입력합니다.

cd .. && unzip samples.zip && aws s3 cp ./samples/emotions/ s3://[Bucket]/emotions/ --recursive

Personalize 학습

Personalize에서 User와 Interaction 데이터를 수집하기 위해서는 Event Tracker를 생성하여야 합니다. Data Group Console로 진입하여, 아래와 같이 “image-recommender-dataset”을 선택합니다. 이후 [Create event tracker]를 선택합니다.

[Configure tracker]에서 아래와 같이 [Tracker name]로 “image-recommender-event-tracker”로 입력 후에 [Next]를 선택하고, [Finish]를 선택합니다.

왼쪽 메뉴의 [Event trackers]를 선택한 후에 “image-recommender-event-tracker”를 선택하면, 아래와 같이 Tracking ID를 확인할 수 있습니다. 아래와 같이 여기에서는 Tracking ID가 “326c8489-2683-420c-b7eb-4ac44bde346d”임을 알 수 있습니다.

Tracking ID 정보를 업데이트하여야 하므로, 다시 Cloud9의 왼쪽 메뉴에서 “image-recommender-based-on-emotion/cdk-image-recommender/lib/cdk-image-recommender-stack.ts”을 오픈후에 아래와 같이 trackingId를 업데이트 합니다.

이후 터미널에서 아래와 같이 CDK folder로 이동하여 재배포를 합니다.

cd cdk-image-recommender/ && cdk deploy

Personalize는 최소 25명 이상의 user와 최소 1000개 이상의 interaction 데이터가 있어야 합니다. 따라서 gallery에서 이미지를 보여주기 위해서는 먼저 최소한의 Traning Dateset을 준비하여야 합니다.

Enabler 이용한 데이터셋 생성

Enabler를 이용하여 데이터를 수집할 수 있습니다. Enabler는 DynamoDB에서 item 정보를 가져와서 감정에 따라 보여주고, 사용자의 선호를 like API를 이용해 수집합니다. Enabler의 접속은 CDK Output의 주소 “Enabler”를 이용합니다. Enabler를 이용하여 25명에 대한 1000개의 interaction 데이터셋을 수집하는 것은 많은 시간이 소요되므로 아래와 같이 Dataset Generator을 사용하여 편리하게 생성할 수 있습니다.

Dataset Generator를 이용한 데이터셋 생성

Dataset Generator를 이용해 Personalize에 dataset을 push 합니다. Dataset Generator의 접속 위치는 CDK Output의 “DatasetGenerator”을 이용하여 아래와 같이 접속 후에 [Generate]를 선택합니다.

Dataset Generator는 datasetGenerator.js와 같이 userId를 ${gender}/${emotions[i]}와 같이 설정하여 성별(gender)과 감정(emotion)에 따라 lambda-generate-dataset을 호출합니다. lambda-generate-dataset은 DynamoDB에 있는 item 데이터를 이용하여 interaction 데이터셋을 생성합니다.

생성한 Dataset이 Personalize에 반영되었는지 약 30분후에 Dataset groups Console로 접속하여 “image-recommender-dataset”로 접속하여 왼쪽 메뉴의 [Datasets] – [Data analysis]을 선택한 후에 [Run analysis]을 선택하여 분석합니다. 분석 결과(Insights)에서 User의 숫자가 25이하, interaction이 1000이하로 알림이 발생하면, Personalize에 입력한 데이터가 아직 분석중임으로 수분 정도 대기한 후에 재시도 합니다.

Solution Console로 접속하여, 이미 생성한 dataset인 “image-recommender-dataset”을 선택하여 진입한 후에 [Create solution]을 선택합니다. 이후 아래와 같이 [Solution name]로 “image-recommender-solution”을 입력하고 [Solution type]로 “Item recommendation”을 선택한 다음에 [Recipe]로 “aws-user-personalization”을 선택합니다. Solution 생성에는 약 20분정도가 소요됩니다.

왼쪽 메뉴에서 아래와 같이 [Campaigns]를 선택한 후에 [Create campaign]을 선택합니다.

아래와 같이 [Campaign name]로 “image-recommender-campaign”로 입력한 후에 [Solution]로 “image-recommender-solution”을 선택합니다. 이후 [Create campaign]을 선택하면 약 10분 정도후에 Campaign이 생성합니다.

실행하기

웹 브라우저를 이용하여 CDK Output의 “Gallery”의 URL로 접속하면 아래와 같은 이미지 추천을 이용할 수 있습니다. [Video] 버튼을 눌러서 Video 권한을 허용하면 Video 화면을 볼 수 있습니다. “감정 추천”을 선택하면 “gender/emotion”로 Personalize의 추천 추론을 수행하며, 해당되는 감정 이미지를 하단에서 3개씩 보여줍니다. [Next]를 통해 다음 순위의 추천 이미지를 3개씩 볼 수 있습니다.

“개인화 추천”을 선택하면 “userId/emotion”로 추천 추론의 결과를 얻습니다. 처음에는 사용자에 대한 상호작용(interaction) 데이터가 없으므로 최신 아이템 순서대로 보여줍니다. 하지만, 하단의 Like에 해당하는 버튼을 클릭하여 상호작용 결과를 Personalize에 전달하면, 이후에는 개인화된 추천을 이용할 수 있습니다. Personalize는 상호작용에 대한 업데이트를 2시간 간격으로 자동으로 반영하므로 Like 버튼을 10회이상 선택하고 일정시간이 지나면 개인화된 추천동작을 확인 할 수 있습니다.

리소스 정리하기

더이상 인프라를 사용하지 않는 경우에 아래처럼 모든 리소스를 삭제할 수 있습니다. 먼저 Personalize Console에 접속하여, “image-recommender-dataset”을 선택하여 들어간 후에 좌측의 [Custom resouces]에서 Campaign과 Solution을 차례로 삭제합니다. 이후 [Event trackers]를 선택하여 “image-recommender-event-tracker”을 삭제합니다.

Cloud9의 터미널에 접속하여 아래와 같이 설치한 인프라들을 삭제합니다.

cdk destroy

또한, 본 실습에서는 GPU를 사용하는 SageMaker Endpoint를 사용하므로 실습이 끝나고 반드시 삭제하여야 합니다. SageMaker ModelsSageMaker EndpointsSageMaker Endpoint configuration에서 생성했던model, endpoint, endpoint configuration을 모두 삭제합니다.

Cloud9 console에서 배포에 사용되었던 Cloud9을 삭제합니다.

결론

AWS의 완전 관리형 AI 서비스인 Amazon Rekognition을 이용하여 카메라로 부터 얻어진 얼굴 이미지로부터 감정을 분석하고 얼굴 아이디를 생성하였습니다. 또한, Amazon Personalize를 이용하여 상호작용(interaction) 데이터가 없는 경우에는 “감정 추천”을 적용하고, 데이터가 쌓이면 “개인화 추천”을 제공하는 방법을 제안하였습니다. 감정을 표현하기 위해 생성한 이미지들은 SageMaker JumpStart의 생성 AI 모델인 Stable Diffusion을 이용하였으며, 비용 효율적으로 다수의 이미지를 빠르게 생성할 수 있는 Event Driven 아키텍처를 구현하였습니다. 감정을 활용한 개인화 추천은 개별 상황에 최적화된 추천을 수행할 수 있어서 다양한 용도로 활용될 수 있을 것으로 기대됩니다.

실습 코드 및 도움이 되는 참조 블로그

아래의 링크에서 실습 소스 파일 및 기계 학습(ML)과 관련된 자료를 확인하실 수 있습니다.

Kyoung-Su Park

Kyoung-Su Park

박경수 솔루션즈 아키텍트는 다양한 워크로드에 대한 개발 경험을 바탕으로 고객이 최적의 솔루션을 선택하여 비즈니스 성과를 달성할 수 있도록 고객과 함께 효율적인 아키텍처를 구성하는 역할을 수행하고 있습니다. 현재 AWS의 Machine Learning, IoT, Analytics TFC에서 활동하고 있습니다.

Youngjoon Choi

Youngjoon Choi

최영준 Principal AI/ML Expert SA는 제조, 하이테크, 금융 등의 다양한 산업에서 엔터프라이즈 IT를 경험하면서 개발자로, 아키텍트로, 데이터 과학자로 다양한 활동을 하였습니다. 기계학습과 딥러닝 연구를 진행하였고, 구체적으로 Hyperparameter optimization과Domain adaptation등을 주제로 알고리즘 연구와 논문 발표를 진행하였습니다. AWS에서는 AI/ML를 전문 분야로 다양한 산업군에서 분산학습/초거대 모델 개발과 ML 파이프라인 구축 등에 대해 AWS 서비스를 이용한 기술 검증 지원, 아키텍처 제안 및 검토 등을 수행하고 있으며, AI/ML 생태계가 더욱 확장할 수 있도록 다양한 기여를 하고자 합니다.

Kichul Kim

Kichul Kim

김기철 솔루션즈 아키텍트는 고객의 요구사항을 도출하고 다양한 개발 경험과 모범사례를 바탕으로 효율적인 아키텍처를 제안하는 역할을 수행하고 있습니다. 현재는 생성형 AI를 이용한 자동화 시스템 구축을 위한 기술적인 도움을 드리고자 노력하고 있습니다.