Developer/Data Science

한달 10만원으로 추천시스템 구축하기 [AWS Personalize 도입 후기 / python SDK]

DQ-admin 2021. 4. 15. 14:41
728x90
본문과는 상관없는 사진1

목차

0. 소규모 스타트업에서 추천시스템을 만드는 어려움
1. AWS Personalize 외않써?
2. 데이터 전처리 방법
3. AWS Personalize 자동화 (Python SDK 이용)
4. 가성비 좋게 서빙하는 방법 Batch inference
5. 한계와 Future work


0. 소규모 스타트업에서 추천시스템을 만드는 어려움

개발자 3명 이하의 초기 단계의 스타트업은 항상 리소스가 부족하다. 서비스 출시를 위해 앱, 서버, 인프라 개발만 해도 부족한데, 요즘 서비스에는 필수로 들어간다는 추천시스템까지 구축할 수 있는 스타트업은 많지 않다. 역량있는 데이터 사이언티스트를 초기 멤버로 데려올 수 있는 팀도 ( 제품의 핵심이 데이터 사이언스인 경우는 제외 ) 거의 없기 때문에 초기 스타트업에서 추천 시스템을 만들어서 제품에 녹여내는 것은 거의 불가능하다.

1. AWS Personalize 외않써?

AWS에는 정말 다양한 솔루션들이 존재한다. 오늘 소개할 내용은 Personalize, 아마존의 개인화 추천 서비스이다.
AWS Personalize 에 대해 모르고 있는 경우도 많겠지만, 알고 있더라도 금액을 계산해보고 너무 비싸다 생각해서 도입할 생각을 접은 스타트업도 많을거라 생각이 든다. 필자는 오늘 소개할 방법을 통해 한달에 10만원이 안되는 금액으로 개인화 추천시스템을 제품에 도입시킬 수 있었다. 처음 솔루션을 도입하고, 파이프라인을 자동화하기까지 약 10일 정도의 시간이 들었고, 구체적인 수치를 밝힐 수는 없지만 도입 후 유저 리텐션도 50% 이상 좋아졌다. (기존 로직은 클릭수, like수 기반)
이 글을 읽는 분들이 여기에 있는 내용 기반으로 작업을 하게 된다면 시행착오가 적을 것이기 때문에 아마 5일 정도면 첫번째 개인화 추천 결과를 제품에 런칭할 수 있지 않을까 생각해본다.

⚠️ 주의사항

  • 데이터, feature 의 양에 따라 실제 과금량이 달라질 수 있으므로 주의
  • 추천된 결과에 대해 본인이 원하는 결과가 나올 것이라고 100% 보장할 수 없으므로 꼭 확인 후 제품에 배포
  • 코드는 실제로 돌아가는 코드가 아닌, 컨셉을 이해시키기 위해 변형된 코드이므로 꼭 본인의 환경에 맞게 수정 후 사용

2. 데이터 전처리 방법

먼저 데이터를 AWS Personalize 에서 원하는 형태로 전처리 해줘야한다.
필요한 데이터는 크게 3가지인데, User, Item, Events 이다.
User와 Item에 대해서는 설명할 필요가 없을 것 같고, Events는 유저의 클릭 히스토리, like, 구매, impression 등 user 와 item 간의 interaction 이라면 어떤 데이터라도 들어갈 수 있다.
다만 AWS Personalize 를 학습시키기 위해 꼭 필요한 정보들이 있어서, Personalize 가 원하는 형태로 데이터를 전처리 해서 넣어주어야 한다. (이 단계에서 정말 시간을 많이 썼다.)
Data Preparation 단계에 대한 공식 문서는 아래 링크를 참고하면 된다.
docs.aws.amazon.com/personalize/latest/dg/data-prep.html

Preparing and importing data - Amazon Personalize

Preparing and importing data Amazon Personalize uses data that you provide to train a model. When you import data, you can choose to import records in bulk or incrementally or both. With incremental imports, you can add individual historical records or dat

docs.aws.amazon.com

데이터셋을 import 하기 전에 필요한 작업이 각 데이터셋의 schema를 정의해주는 것이다.
user_schema.json 부터 살펴보자.

{ "type": "record", "name": "Users", "namespace": "your.namespace.schema", "fields": [ { "name": "user_id", "type": "string" }, { "name": "os_device", "type": "string" }, { "name": "age", "type": "int" } ], "version": "1.0" }

user_id, os_device, age 가 정의되어있고, 실제 데이터셋을 import하는 ETL 코드에서는 이 파일에 적힌 내용에 맞추어 데이터를 전처리해서 import 시킬 것이다.
각 제품마다 수집하는 정보나 정의가 다르기 때문에 모든 케이스를 커버할 수는 없지만 이 schema 정의 부분에서 삽질을 정말 많이 했기 때문에 실제 동작하는 json 파일을 공유하는 것이다. 특히, user_id 의 경우, 내부적으로 bigint 를 쓰고있는데, 동작하지않아서, 강제로 형변환을 해줘야 했다. 그리고 "name": "Users", 부분도 틀리면 동작하지 않는 것을 확인했다.

다음으로 item_schema.json 이다.

{ "type": "record", "name": "Items", "namespace": "your.namespace.schema", "fields": [ { "name": "item_id", "type": "string" }, { "name": "category_id", "type": "string" }, { "name": "price", "type": "float" } ], "version": "1.0" }

상술한 바와 같이 이 예시들은 교육 목적으로 편집을 했기 때문에, 실제로는 더 많은 feature 가 들어간다는 점을 참고하길 바란다.
item의 경우에도 user와 크게 다른 점은 없고, 필요한 feature 들이 있다면 type을 맞추어 전처리 과정에 추가해주면 된다.

마지막, event_schema.json 이다. (name을 Interactions 로 쓰는 것에 유의하라!)

{ "type": "record", "name": "Interactions", "namespace": "your.namespace.schema", "fields": [ { "name": "user_id", "type": "string" }, { "name": "item_id", "type": "string" }, { "name": "point", "type": "float" } ] }

당연하게도, user_id 와 item_id 는 필수적으로 필요하다. 그리고 그 외의 feature 들을 추가할 수 있다. interaction 파트에서도 다양한 피쳐가 추가될 수 있고, feature 의 양,질에 따라 personalize의 성능은 크게 바뀔 수 있으니 제품이 수집하고 있는 데이터를 여러 방면으로 시도하라.
데이터 사이언스를 경험해본 분들이라면 알겠지만 모든 feature를 다 input으로 넣는다고 해서 최고의 결과를 보장하지 않는다.
여러가지 시도(feature engineering)를 통해서 최적의 결과를 얻는다면 정말 가성비 좋은 추천시스템이 구축될 수 있을거라 생각한다.

이렇게 모든 schema 를 정의해줬다면 이를 바탕으로 AWS Personalize Dataset 를 생성할 차례이다.
python SDK, boto3 를 사용했고, 로컬 컴퓨터가 아닌 서버에서 cron job 형태로 돌릴 예정이기 때문에 S3를 저장소로 이용했다.
먼저, 데이터를 전처리한 후 S3에 저장하는 코드이다.

def etl_user() -> None: df = pd.read_sql_table(대충 데이터베이스 연결하는 내용) df_selected = df.reset_index()[['user_id', 'age', 'os_device']] # 당연하게도, 이 부분에서 user_schema.json 에 정의한 내용대로 # dataframe의 column 이름, type을 맞춰줘야한다. # S3에 csv 형태로 저장한다. df.to_csv(USER_S3_PATH) def etl_item(): df = pd.read_sql_table(대충 데이터베이스 연결하는 내용) # 마찬가지, 데이터 전처리 내용이 들어간다. df.to_csv(ITEM_S3_PATH) return item_df['item_id'].tolist() def etl_event(common_list) -> None: df = pd.read_sql_table(대충 데이터베이스 연결하는 내용) # 마찬가지.. pip_df.to_csv(EVENT_S3_PATH) 


이제 boto3 를 이용해 personalize 에 새로운 dataset을 생성해보자.
주의할 내용은 코드블록 내에 주석으로 적어두었으니 참고하길 바란다.
하나 알아두어야할 것은 ARN 이라는 개념인데, 아마존에서 각 객체를 관리하는 ID 라고 이해하면 쉽다.
dataset group, dataset, dataset import job 등 객체를 생성할 때마다 response 값을 받아서
추후에 활용할 수 있도록 메모리에 저장을 해둔다.

 def create_dataset() -> None: personalize = boto3.client('personalize', region_name='your region') # dataset group 의 이름이 중복되면 안되므로 적당히 time 함수를 써서 중복을 방지해준다. response = personalize.create_dataset_group(name=f'dsg-{int(time.time())}') dsg_arn = response['datasetGroupArn'] description = personalize.describe_dataset_group(datasetGroupArn=dsg_arn)['datasetGroup'] print('1. Name: ' + description['name']) print('1. ARN: ' + description['datasetGroupArn']) print('1. Status: ' + description['status']) # dataset 생성과 코드실행간의 타이밍 오류를 방지하기 위함. time.sleep(5) # schema로 정의한 파일들은 이 코드와 동일한 폴더에 위치해야한다. # 21 ~ 42 line 을 통해 personalize 내부에 schema 를 생성해준다. with open('event_schema.json') as f: createSchemaResponse = personalize.create_schema( name=f'event_schema_{int(time.time())}', schema=f.read() ) event_schema_arn = createSchemaResponse['schemaArn'] print("1-1. EVENT SCHEMA :: ", event_schema_arn) with open('user_schema.json') as f: createSchemaResponse = personalize.create_schema( name=f'user_schema_{int(time.time())}', schema=f.read() ) user_schema_arn = createSchemaResponse['schemaArn'] print("1-1. USER SCHEMA :: ", user_schema_arn) with open('item_schema.json') as f: createSchemaResponse = personalize.create_schema( name=f'item_schema_{int(time.time())}', schema=f.read() ) item_schema_arn = createSchemaResponse['schemaArn'] print("1-1. item SCHEMA :: ", item_schema_arn) # Dataset을 생성하는 코드이다. # Dataset이 생성되는 것에 시간이 오래걸리기 때문에 # 이런식으로 30초에 한번씩 Dataset 생성을 체크하며 # 모든 Dataset이 생성되기 전까지는 다음 코드로의 진행을 block 한다. DSG_STATUS = '' while DSG_STATUS != 'ACTIVE': print("wait DSG to created... ::", DSG_STATUS) time.sleep(30) DSG_STATUS = personalize.describe_dataset_group( datasetGroupArn=dsg_arn )['datasetGroup']['status'] response = personalize.create_dataset( name=f'event-{int(time.time())}', schemaArn=event_schema_arn, datasetGroupArn=dsg_arn, datasetType='interactions') print('2. Event Dataset Arn: ' + response['datasetArn']) event_ds_arn = response['datasetArn'] response = personalize.create_dataset( name=f'user-{int(time.time())}', schemaArn=user_schema_arn, datasetGroupArn=dsg_arn, datasetType='users') print('2. User Dataset Arn: ' + response['datasetArn']) user_ds_arn = response['datasetArn'] response = personalize.create_dataset( name=f'item-{int(time.time())}', schemaArn=item_schema_arn, datasetGroupArn=dsg_arn, datasetType='items') print('2. Item Dataset Arn: ' + response['datasetArn']) item_ds_arn = response['datasetArn'] 


dataset 을 생성했다면, dataset import job 이라는 것을 해줘야하는데, personalize 에서 학습할 수 있도록 데이터를 import 해주는 작업이다. 이 또한 코드로 자동화할 수 있다. (위의 create_dataset 함수에 연결된 내용이다)
이 코드에서 중요한건 roleArn 부분인데, data import job 을 실행하기 위한 권한이 적절히 부여되어있는 role을 사용해야한다. 이 부분에서도 삽질을 많이했는데, 필자가 아마존 IAM 시스템을 잘 아는 편은 아니라서 공식 문서 링크로 구체적인 설명을 대체한다.
docs.aws.amazon.com/personalize/latest/dg/aws-personalize-set-up-permissions.html#set-up-create-role-with-permissions

Setting up permissions - Amazon Personalize

Setting up permissions To use Amazon Personalize, you have to set up permissions that allow IAM users to access the Amazon Personalize console and API operations. You also have to set up permissions that allow Amazon Personalize to perform tasks on your be

docs.aws.amazon.com

참고로 roleArn 에 들어가는 arn의 형태는 다음과 같다.
arn:aws:iam::123123123123:role/AmazonPersonalize-ExecutionRole

 response = personalize.create_dataset_import_job( jobName=f'event-import-{int(time.time())}', datasetArn=event_ds_arn, dataSource={'dataLocation': EVENT_S3_PATH}, roleArn='your role arn') event_dsij_arn = response['datasetImportJobArn'] print('3. Event Dataset Import Job arn: ' + event_dsij_arn) response = personalize.create_dataset_import_job( jobName=f'user-import-{int(time.time())}', datasetArn=user_ds_arn, dataSource={'dataLocation': USER_S3_PATH}, roleArn='your role arn') user_dsij_arn = response['datasetImportJobArn'] print('3. User Dataset Import Job arn: ' + user_dsij_arn) response = personalize.create_dataset_import_job( jobName=f'item-import-{int(time.time())}', datasetArn=item_ds_arn, dataSource={'dataLocation': ITEM_S3_PATH}, roleArn='your role arn') item_dsij_arn = response['datasetImportJobArn'] print('3. Item Dataset Import Job arn: ' + item_dsij_arn)

3. AWS Personalize 학습, 모델 준비 (Python SDK 이용)

이제 dataset 까지 준비가 끝났고, Personalize 를 학습시킬 차례이다.
이 부분도 customizing 할 수 있는 부분이 많고, personalize 자체적으로 제공하는 기능이 많기 때문에
모든 케이스를 커버하기는 어렵고, 현재 필자가 운영하고 있는 방식을 공유해보려고 한다.
그리고 기존에 personalize를 운영하고 있지 않다고 가정한다. (갯수가 1개라는 뜻)
이 코드에서는 datasetgroup 을 호출할때, 무조건 0번째 원소를 불러오기 때문이다.
갯수가 2개 이상이 될 경우에는 몇번째 dataset group이 원하는 그룹인지 확실히 알고 코드를 실행해야할 것이다.
이 코드에서 수정할 수 있는 부분은
performAutoML, performHPO 인데, 개인적인 경험으로는 두 옵션 모두 True로 하는 것을 추천한다.
AutoML 은 최적의 모델을 찾아주는 옵션, HPO는 하이퍼파라미터 최적화인데, 두 옵션을 켜두었을때, 결과적으로는 더 좋은 결과를 보여주었기 때문인데, 단점은 training에 드는 시간과 비용이 조금 더 증가할 수 있다.

def create_solution() -> None: DSG_ARN = personalize.list_dataset_groups()['datasetGroups'][0]['datasetGroupArn'] dataset_list = personalize.list_datasets( datasetGroupArn=DSG_ARN )['datasets'] # solution 을 만들기 위해서는 dataset import job 이 끝나야 하므로, # block 하는 while loop 를 만들어준다. DSIJ_STATUS = ['', '', ''] while DSIJ_STATUS != ['ACTIVE'] * 3: print("WAITING... ::", DSIJ_STATUS) time.sleep(10) for i, dataset in enumerate(dataset_list): DSIJ_STATUS[i] = personalize.list_dataset_import_jobs( datasetArn=dataset['datasetArn'] )['datasetImportJobs'][0]['status'] solution_obj = personalize.create_solution( name=f"solution-{int(time.time())}", datasetGroupArn=DSG_ARN, performAutoML=True, performHPO=True ) solution_arn = solution_obj['solutionArn'] print("1. SOLUTION :: ", solution_arn) time.sleep(10) sv_obj = personalize.create_solution_version( solutionArn=solution_arn, trainingMode='FULL' ) sv_arn = sv_obj['solutionVersionArn'] print("2. Solution is Training :: ", sv_arn) 

solution, solution version 까지 준비가 되면 Personalize 를 통해 개인화 추천 결과를 받을 준비는 모두 끝난것이다.

4. 가성비 좋게 서빙하는 방법 Batch inference

AWS Personalize 의 과금은 크게 두가지 부분에서 이루어지는데,
첫번째는 모델을 학습하는 부분에서 GPU도 사용이 되고, 메모리에 모델을 올려둬야하기 때문에 Solution, Solution version 이 존재하기만 해도 시간에 따라 과금이 이루어진다.
두번째는 결과를 inference 하는 부분인데, personalize는 이 결과를 받는데에 2가지 옵션을 제공한다.
batch inference 와 campaign 인데 여러가지 차이가 있긴 하지만 쉽게 생각하면
batch inference는 1회성으로 결과를 받는것.
campaign 는 API 형태로 실시간 결과를 받는 것이다.
campaign 으로 쓸 경우, 추천 후 유저의 feedback 등도 usage를 통해 제공받을 수 있고, 실시간으로 추천이 가능하기 때문에 기능적인 측면에서 더 좋은게 맞지만, 우리의 목적은 가성비 좋은 서빙이기 때문에 batch inference 를 사용해야한다.
결과를 업데이트하는 주기에 따라서 금액도 달라지게 되고, 필자는 하루에 1번 결과를 얻는 형태로 운영하고 있다.

프로덕션 레벨의 추천시스템에서는 filter가 필수적으로 필요한데, 예를 들어 너무 기간이 오래된 상품을 추천하고 싶지 않을 경우 조건을 걸어 결과에서 제외시킬 필요가 있다.
Personalize 에서는 batch inference job 을 만들때, filter arn 을 넣음으로써 필터를 적용시킬 수 있다.

def user_input() -> None: df = pd.read_sql_table(데이터 베이스) # 여기에서도 위의 etl_user 함수에서 해준 것처럼 동일한 type으로 전처리 해줘야한다. # 그 결과를 json 형태로 만들어서 S3에 업로드 해주어야 한다. out = user_df.to_json(orient='records')[1:-1].replace('},{', '}\n{') s3 = boto3.resource('s3') s3.Object('your destination', 'path/to/batch_input.json').put(Body=out) def batch_recommendation() -> str: DSG_ARN = personalize.list_dataset_groups()['datasetGroups'][0]['datasetGroupArn'] SOLUTION_ARN = personalize.list_solutions(datasetGroupArn=DSG_ARN)['solutions'][0]['solutionArn'] SV_ARN = personalize.list_solution_versions( solutionArn=SOLUTION_ARN )['solutionVersions'][0]['solutionVersionArn'] filter_list = personalize.list_filters( datasetGroupArn=DSG_ARN )['Filters'] if len(filter_list) == 0: filter_obj = personalize.create_filter( name=f"filter-{int(time.time())}", datasetGroupArn=DSG_ARN, filterExpression=f'EXCLUDE ItemID WHERE 조건1 AND 조건 2' ) filter_arn = filter_obj['filterArn'] else: filter_arn = filter_list[0]['filterArn'] print("1. FILTER :: ", filter_arn) # filter 와 solution version 가 생성될 때까지 대기하는 while loop. FILTER_STATUS = '' SV_STATUS = '' while SV_STATUS != 'ACTIVE' or FILTER_STATUS != 'ACTIVE': print("waiting SV.... ", SV_STATUS, "\nfilter.... ::", FILTER_STATUS) time.sleep(30) SV_STATUS = personalize.describe_solution_version( solutionVersionArn=SV_ARN )['solutionVersion']['status'] FILTER_STATUS = personalize.describe_filter( filterArn=filter_arn )['filter']['status'] response = personalize.create_batch_inference_job( solutionVersionArn=SV_ARN, jobName=f"recommendation-batch-{int(time.time())}", roleArn='your role arn', jobInput= {"s3DataSource": {"path": "s3://path/to/batch_input.json"}}, jobOutput= {"s3DataDestination": {"path": "s3://path/to/batch_result/"}}, numResults=500, #(maximum is 500) filterArn=filter_arn ) BATCH_ARN = response['batchInferenceJobArn'] print("Batch job created ", BATCH_ARN) return BATCH_ARN 

사실 batch inference 단계에서 result 를 받으면 AWS Personalize를 통해 추천의 결과물을 얻는 것까지는 완료된 것이다. 하지만 이렇게까지 하고 마무리하면 두번째부터 실행도 안될 뿐더러, 한달후에 AWS 과금 폭탄을 맞게 될 것이다. 그렇기 때문에 추천의 결과를 제품에 서빙하는 것 이후에 Personalize 인스턴스를 모두 제거하는 코드까지 공유하려고 한다.
추천 결과를 받은 직후에 Personalize 관련 instance 를 모두 삭제하는것, 이 것이 가성비의 핵심이다.
이 부분까지 코드로 자동화를 해둬야 서버에서 cron job으로 매일 돌아갈 수 있도록 세팅할 수 있다.

# DB에 추천 결과를 ETL def etl_recommendation(batch_arn: str) -> None: BATCH_STATUS = '' while BATCH_STATUS != 'ACTIVE': print('WAITING BATCH TO DONE... ::', BATCH_STATUS) time.sleep(10) BATCH_STATUS = personalize.describe_batch_inference_job( batchInferenceJobArn=batch_arn )['batchInferenceJob']['status'] s3 = boto3.resource('s3') output = s3.Object('bucket name', 'path/to/batch_result/batch_input.json.out') tmp = output.get()['Body'].read().decode('utf-8').split('\n') result = pd.DataFrame(tmp)[:-1] # 제품에서 필요한 형태로 재가공해서 데이터베이스에 결과를 serving 할 수 있다. result.to_sql('result', 데이터베이스 내용) # AWS 위에 Personalize 관련 인스턴스를 모두 지워준다. def clean_up() -> None: DSG_ARN = personalize.list_dataset_groups()['datasetGroups'][0]['datasetGroupArn'] FILTER_LIST = personalize.list_filters( datasetGroupArn=DSG_ARN )['Filters'] SOLUTION_LIST = personalize.list_solutions( datasetGroupArn=DSG_ARN )['solutions'] DS_LIST = personalize.list_datasets( datasetGroupArn=DSG_ARN )['datasets'] for idx, filter in enumerate(FILTER_LIST): response = personalize.delete_filter( filterArn=filter['filterArn'] ) print("number ", idx, " FILTER CLEAN UP") for idx, solution in enumerate(SOLUTION_LIST): response = personalize.delete_solution( solutionArn=solution['solutionArn'] ) print("number ", idx, " SOLUTION CLEAN UP") FILTER_LENGTH = 1 while FILTER_LENGTH > 0: print("WAIT FILTER TO DELETE... :: ", FILTER_LENGTH) time.sleep(10) FILTER_LENGTH = len(personalize.list_filters( datasetGroupArn=DSG_ARN )['Filters']) for idx, dataset in enumerate(DS_LIST): response = personalize.delete_dataset( datasetArn=dataset['datasetArn'] ) print("number ", idx, " DATASET CLEAN UP") DS_LENGTH = 1 while DS_LENGTH > 0: print("WAIT DS TO DELETE... :: ", DS_LENGTH) time.sleep(30) DS_LENGTH = len(personalize.list_datasets( datasetGroupArn=DSG_ARN )['datasets']) response = personalize.delete_dataset_group( datasetGroupArn=DSG_ARN ) print("DSG CLEAN UP") 

5. 한계와 Future work

실시간으로 추천해줄 수 없다보니 실시간성이 중요한 서비스에는 적용할 수 없다.
하지만 일단 개인화 추천결과가 나올 수 있기 때문에 이를 기반으로 shuffle 이나, 기존 로직을 적당히 섞는 등의 기교를 이용하면 충분히 좋은 사용자 경험을 유도할 수 있을 것이다. 좋은 백엔드 개발자라면 이 내용만 가지고도 production level 에서 잘 동작하는 추천시스템을 구현할 수 있을거라는 것이 필자의 생각이다.
다만, 서두에 밝힌 것처럼 코드를 어느정도 이해한 후에 본인의 환경에 맞도록 세팅을 해야 동작할 것이다. 신경써야할 것들이 이상한 부분에서 튀어나오기 때문에, (예를들어 roleArn, boto3 configure 등.. ) 이 글에서 다루지 않은 부분들도 처리해야할 것들이 꽤 있을 것이다. 다만, 필자가 고생했던 부분이 어딘지 명시했고, 코드가 어떤식으로 작성되어야하는지 틀을 제시했기 때문에 이 글을 통해 최소 3~5일의 시간적인 이득을 볼 수 있을거라 예상한다.
personalize 가 출시된 후 이런 식으로 운영을 해보니 가장 좋았던 점은 다른 부분에 집중할 수 있다는 것이다. 개인화 추천이라는 것이 절대 쉬운 feature가 아닌데, 2주일정도 투자해서 운영 자동화까지 할 수 있었고, 그로 인해 (당분간은) 개인화 추천보다는 다른 중요한 feature들에 집중할 시간을 벌 수 있었다. 이 글을 읽는 분들도 시도해서 좋은 결과가 있으면 이 글을 쓴 보람이 있을 것 같다.
어느정도 앱의 주요 feature 들이 안정화돼서, 필자는 앞으로 Graph Neural Network 기반의 추천시스템을 서빙해보려고 한다. 하지만 파볼수록 production level 에서 추천 시스템을 서빙하는 것이 얼마나 어려운 일인지 뼈아프게 깨닫는 중이다.
하지만 시도하지 않으면 0이다, 시도하면 1이될 가능성이 생긴다. 해보자.


AWSKRUG 데이터 사이언스 소모임에서 본 글 내용으로 발표하였습니다. 개인적으로는 영상이 많은걸 생략해서 글을 보는게 좋은거 같습니다.

https://youtu.be/bOjE3IWYSS8

반응형