brunch

You can make anything
by writing

C.S.Lewis

by 디큐 Dec 03. 2021

한달 10만원으로 추천시스템 구축하기

AWS PERSONALIZE 도입 후기 with python SDK

목차


0. 추천시스템, 어디 싸고 쉽게 만들 방법 없나요?
1. AWS Personalize, 쓰지 않을 이유가 하나도 없습니다
2. 여러분은 데이터만 준비해주세요 (데이터 전처리)
3. 매일 추천 결과가 업데이트 되도록 자동화하기
4. 가성비 좋게 서빙하는 방법, Batch inference
5. Personalize의 한계와 Future work



0. 추천시스템, 어디 싸고 쉽게 만들 방법 없나요? 


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

    저희 팀의 경우에도, CTO인 제 자신이 데이터 사이언티스트로서 경력도 있고, 제품에 추천시스템을 적용해본 경험이 있음에도 불구하고 제품에 많은 시간을 투자해야 하는 상황에서는 싸고 쉬운 솔루션이 어디 없을까 찾아보게 되었습니다. 그렇게 찾아낸 AWS Personalize, 쓰지 않을 이유가 없습니다.


1. AWS Personalize, 쓰지 않을 이유가 하나도 없습니다


    AWS에는 정말 다양한 솔루션들이 존재합니다. 그 중에서도 Personalize는 추천 서비스를 도입하려고 하는 제품 팀에서는 꼭 한 번 짚고 넘어가야할 서비스입니다. 아직 AWS Personalize 에 대해 모르는 제품 팀이 많겠지만, 이 글을 읽고 한 번 적용해볼 가치가 있다고 판단되면 따라해보시기 바랍니다.

    AWS Personalize 의 요금 소개 페이지에 가보면, 첫 2달은 무료 혜택이 있기 때문에 금액적인 부담 없이 테스트 해보기에도 좋을거라고 생각합니다. https://aws.amazon.com/ko/personalize/pricing/


    처음 가격 정책을 보고 계산을 해보니, 생각보다 너무 큰 금액이 나와서 이건 우리 팀이 쓸 수는 없겠구나... 생각하다가 여러가지 방법을 시도해보니 금액을 최적화 할 수 있을 거라는 판단이 들었고, 실제로 도입을 해보니 한달에 10만원이 안되는 금액으로 개인화 추천시스템을 구축할 수 있었습니다.

    금액은 저렴하다지만, 실제 구축할 때 드는 공수나, 성능에 대해서도 궁금하실텐데요. 제 사례를 말씀 드리니 대략적인 감만 잡아보시면 좋을 것 같습니다. 저 혼자서 솔루션을 도입하고, 파이프라인을 자동화하기까지 약 10일 정도의 시간이 들었고, Personalize 추천 시스템을 도입한 후에 유저가 추천 시스템 영역에서 클릭할 확률도 기존 대비 30% 이상 좋아졌습니다.

    다만 제가 작업할 때에는 aws 에서 제공하는 공식 guide notebook 밖에 없었고, 한글로 된 레퍼런스 글들이 많이 없었기 때문에 시행착오를 겪었고, 만약 이 글을 레퍼런스 삼아 작업하신다면 늦어도 5일 정도면 첫번째 개인화 추천 결과를 제품에 반영할 수 있지 않을까 예상해봅니다.


DISCLAIMER

데이터, feature 의 양에 따라 실제 과금량이 달라질 수 있으므로 주의하세요

추천된 결과에 대해 본인이 원하는 결과가 나올 것이라고 100% 보장할 수 없으므로 꼭 결과를 확인하신 후 실제 제품에 배포하세요

코드는 실제로 돌아가는 코드가 아닌, 컨셉을 이해시키기 위해 변형된 코드이므로 본인의 환경에 맞도록 코드 수정 후 사용하세요



2. 여러분은 데이터만 준비해주세요 (데이터 전처리)


    추천시스템을 구축할 때 기본적인 원리는 알고 있으면 좋지만 AWS Personalize 를 이용할 때는 필수적인 지식은 아닙니다. 다만, 재료가 되는 데이터는 꼭 필요한데, 이 장에서는 데이터를 Personalize 가 요구하는 스펙에 맞추어 작성하는 방법을 알려드리려고 합니다.

        
    먼저 필요한 데이터는 크게 3가지 종류인데, User, Item, Event 입니다. 이름에서 알 수 있듯이 User는 유저 정보들이 담긴 데이터, Item은 상품 정보들이 담긴 데이터, Event는 유저의 클릭 히스토리, like, 구매, impression 등 user 와 item 간의 interaction 이라면 어떤 데이터라도 들어갈 수 있습니다. 다만 AWS Personalize 에서 요구하는 꼭 필요한 정보들이 있어서, AWS에서 제시하는 형태로 데이터를 전처리 해서 넣어주어야 합니다. (이 단계에서 시행착오를 정말 많이 겪었습니다.)
Data Preparation 단계에 대한 공식 문서는 아래 링크를 참고하세요. (영어 주의)
https://docs.aws.amazon.com/personalize/latest/dg/data-prep.html

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

https://gist.github.com/dq-hustlecoding/af22f9016e40afebeaf83044ecc0b3d9

    위 코드를 보면, type, name, namespace 중 type, name 은 그대로 유지해주시고, namespace 부분은 여러분이 편하신대로 바꾸시면 됩니다. 여러개의 personalize 를 동시에 작동시킬 수 있으니 구분하기 위한 구분자로 보입니다. fields 에 보면 실제 들어갈 데이터들을 정의하는 곳인데요, user_id, os_device, age 가 정의되어있는 걸 볼 수 있고, 실제 데이터를 넣을 때는 이 schema 파일에 정의된 필드이름(name)과 필드타입(type)에 맞추어 전처리를 해줘야합니다. 

    각 제품마다 수집하는 정보나 정의가 다르기 때문에 모든 경우의 수를 포함할 수는 없겠지만, 이 schema 를 정의하는 부분에서 삽질을 정말 많이 했기 때문에 실제로 동작하는 것을 확인한 schema.json 파일을 공유합니다. 특히, user_id 의 경우, 저희 제품 내부적으로는 bigint 자료형을 쓰고있는데 동작하지않아서, 강제로 string 자료형으로 형변환을 해주었습니다. 또한 "name": "Users", 부분도 틀리면 동작하지 않는 것을 확인했습니다.

다음으로 item_schema.json 을 살펴보겠습니다.

https://gist.github.com/dq-hustlecoding/a0b5202a8f8441134480f9f6156a7d7d

    상술한 바와 같이 위 코드는 교육 목적으로 편집을 했기 때문에, 실제로는 더 많은 field 가 들어간다는 점을 유의하시기 바랍니다. (예를 들어, 상품의 색깔이나, 판매자 정보 등도 field로 포함될 수 있습니다.) item의 경우에도 user와 크게 다른 점은 없고, 추천시스템에 추가하고 싶은 feature 들이 있다면 type을 맞추어 전처리 과정에 추가해주면 됩니다.

마지막으로 event_schema.json 입니다. (name을 Interactions 로 쓰는 것에 주의하세요)

https://gist.github.com/dq-hustlecoding/8892edd43b4b5e3c4ddc7747bf741655

    당연하게도, user_id 와 item_id 는 필수적으로 필요합니다. 유저와 상품 사이에 일어나는 일을 interactions 로 정의하고 있으니까요. 그리고 point 외의 feature 들도 추가할 수 있습니다. 대표적으로 type을 추가할 수도 있는데요. 유저가 상품을 구매하는 것과 상품을 클릭하는 것은 구분이 되어야겠죠? 이러한 데이터를 얼마나 잘 넣어주느냐에 따라서 personalize의 성능은 크게 바뀔 수 있으니 제품이 수집하고 있는 데이터를 여러 방면으로 나눠서 시도하는 것을 추천드립니다. 

    데이터 사이언스를 경험해본 분들이라면 아시겠지만 모든 feature와 모든 데이터를 다 넣는다고 해서 최고의 추천 결과가 나오는 것은 아닙니다. 따라서 결과를 관찰하면서 다양한 실험을하면서 최적의 결과 값을 찾아보는 것이 AWS Personalize 를 가장 잘 활용하는 길이 아닐까 싶습니다. (이를 고급스러운 말로 feature engineering 이라고 합니다.) 

    



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

https://gist.github.com/dq-hustlecoding/608c4d56309f7e0b626d8111662fb12a

    이제 boto3 를 이용해 personalize 에 새로운 dataset을 생성해보겠습니다. 여기서 주의할 내용은 코드블록 내에 주석으로 적어두었으니 참고하길 바랍니다. 코드를 보기 전에 하나 알아두어야할 것은 ARN 이라는 개념인데, 아마존에서 각 객체를 관리하는 ID 라고 이해하면 됩니다. 

각 ARN은 boto3에서 각 객체를 생성할 때 생성이 되고 (dataset group, dataset, dataset import job 등) 각 response 값 안의 arn 값을 받아서 추후에 활용할 수 있도록 변수에 저장해두는 것이 좋습니다.

아래 코드로 dataset을 생성해줍니다. (create_dataset.py)

https://gist.github.com/dq-hustlecoding/6132f159a47db4c36f978d2ba886c80a

    dataset 을 생성했다면, dataset import job 을 실행시켜줘야 합니다. 이는 personalize 에서 학습할 수 있도록 데이터를 import 해주는 작업입니다. 이 또한 코드로 자동화할 수 있습니다. (위의 create_dataset 함수와 연결된 부분입니다.) 
    이 코드에서 중요한건 roleArn 부분인데, data import job 을 실행하기 위한 권한이 적절히 부여되어있는 role을 사용해야합니다. 이 부분에서도 작동하지 않아서 시간을 많이 뺐겼는데, 구체적인 작동 원리가 궁금하신 분들은 아래 공식 문서 링크를 참고하시기 바랍니다.
https://docs.aws.amazon.com/personalize/latest/dg/aws-personalize-set-up-permissions.html#set-up-create-role-with-permissions

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

create_dataset.py (이어서)

https://gist.github.com/dq-hustlecoding/570ef229369cb0cc8578f846b3e07b51



3. 매일 추천 결과가 업데이트 되도록 자동화하기


    이제 dataset 까지 준비가 끝났고, 드디어 Personalize 를 실행시켜 추천시스템을 학습시킬 차례입니다.
이 부분도 사실 AWS Personalize 의 핵심 기능인 만큼 customizing 할 수 있는 부분도 많고, personalize 자체적으로 제공하는 기능이 많기 때문에 모든 기능을 이 글에서 커버하기는 어렵고, 제가 실제로 운영했던 방식을 공유해보려고 합니다.

    편의를 위해 기존에 운영하는 personalize 가 없다고 가정하겠습니다. 아래 코드에서는 datasetgroup 을 호출할때, 무조건 0번째 원소를 불러오기 때문입니다. 만약 운영하는 personalize의 갯수가 2개 이상이 될 경우에는 몇번째 dataset group이 원하는 그룹인지 코드에서 확실히 지정해주어야 합니다.

    간단하지만 빠르게 이 코드에서 수정할 수 있는 부분은 performAutoML, performHPO 이라는 파라미터인데, 개인적인 경험으로는 두 옵션 모두 True로 하는 것을 추천하고 싶습니다. performAutoML 은 최적의 모델을 찾아주는 옵션, perfomHPO는 하이퍼파라미터 최적화인데, 두 옵션을 켜두었을때, 결과적으로는 더 좋은 결과를 보여주었기 때문인데, 단점은 training에 드는 시간과 비용이 조금 더 증가할 수 있습니다. 데이터에 따라서 이 값 조정이 유의미한 결과값의 차이를 가져올 수 있으므로 실험을 통해서 최적의 상태를 찾아내시기 바랍니다.


create_solution.py

https://gist.github.com/dq-hustlecoding/4bdcdc72ac4320717de8cdf8b19dd69a


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



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


    AWS Personalize 의 과금 체계를 이해하기 위해 공식 요금 안내 문서의 예시를 보겠습니다.

https://aws.amazon.com/ko/personalize/pricing/?nc2=type_a

소매업체는 배치 추천을 사용해 사용자를 위한 추천을 생성하여 이메일을 개인화하는 데 사용합니다. 모델 훈련에 10GB의 데이터가 사용되었고, 훈련에 50 교육 시간이 소비되었으며, 배치 추론 작업을 통해 1백만 명의 사용자를 위해 사용자당 10개의 추천 항목을 생성했다고 가정해보겠습니다.
이 경우 개인화에 대한 요금은 다음과 같습니다.
데이터 처리 및 저장 = 10GB x 0.05 USD/GB = 0.5 USD
교육 비용 = 50 교육 시간 x 교육 시간당 0.24 USD = 12 USD
추론 비용 = 1백만 사용자* x 0.067 USD/추천 1,000개 = 0.67 USD
*배치 요금에서는 ‘USER-PERSONALIZATION’ 레시피 유형을 기반으로 ‘Solution’을 사용하여 각 사용자에 대해 생성된 추천은 사용자당 요청된 결과(항목) 수와 관계없이 1개의 추천으로 계산됩니다. 마찬가지로 사용자당 순위가 재지정된 항목 수와 관계없이 PERSONALIZED-RANKING을 위해 처리된 사용자 수에 대한 비용과 요청된 항목당 유사 항목 수와 관계없이 ‘RELATED_ITEMS’ 레시피를 사용하여 처리된 항목 수에 대한 비용만 지불하면 됩니다.

    Personalize 를 사용함에 있어서 교육 비용 부분은 줄일 수 없는 부분입니다. 상술한 performAutoML 이나 performHPO 옵션을 False로 두면 조금 줄일 수는 있지만, 기본적으로는 데이터 양과 학습 방법, 학습 시간 등에 비례하는 금액이므로, 저 부분은 크게 줄일 수 없고, "추론" 부분이 요금 최적화에 있어서 집중해야할 부분입니다. 

    추론에는 두가지 방법을 제공하는데, batch inference 방식과 campaign 방식입니다. 간단히 설명하면 batch inference는 추천 결과를 1회성으로 받는 방식. campaign 는 API 형태로 실시간 추천 결과를 받는 방식입니다.

campaign 방식으로 쓸 경우, 추천 후 유저의 feedback 등도 streaming API를 통해 제공받을 수 있고, 실시간으로 추천이 가능하기 때문에 기능적인 측면에서 더 좋은게 맞지만, 제 목적은 가성비 좋은 서빙이었기 때문에 batch inference 를 사용했습니다. 제 경우 하루에 1번 batch inference 가 실행되도록 자동화를 시켰습니다.

       


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

Personalize 에서는 batch inference job 을 만들때, filter arn 을 넣음으로써 필터를 적용시킬 수 있습니다. Filter 를 적용시켜 Batch Inference 를 저장하는 방법은 아래 코드를 참고하세요.


batch_inference.py

https://gist.github.com/dq-hustlecoding/af8dead300d7560e732bb9e18bf647f9

    사실 batch inference 단계에서 result 를 받으면 우리의 첫번째 목적이었던 AWS Personalize를 통해 추천의 결과물을 얻는 것까지는 완료된 것입니다. 하지만 여기까지만 작업하면 두번째 실행부터는 실행도 안될 뿐더러, 한달 후에 AWS 과금 폭탄을 맞게 될 수도 있습니다. 그렇기 때문에 추천의 결과를 제품에 서빙하는 것 이후에 사용한 AWS Personalize 인스턴스를 모두 제거하는 코드까지 공유하려고 합니다. 다만 이 부분도 사용하는 필터, schema 등에 따라서 차이가 있을 수 있으니, 공식 문서를 충분히 숙지하신 후에 자동화 하시는 것을 추천 드립니다.

추천 결과를 받은 직후에 Personalize 관련 instance 를 모두 삭제하는것, 이 것이 가성비의 핵심이다.

load_and_clean_up.py

https://gist.github.com/dq-hustlecoding/7cf7344017b91f9264196b805063d2ae




5. 한계와 Future work


    완벽한 글과 코드가 아니기 때문에 한계점이 있고 저도 구현하며 아쉬웠던 점이 있는 만큼 한계와 Future work 를 정리하며 이 글을 마치고자 합니다. 궁금한 점이나 더 나은 개선 방안은 댓글이나 이메일 통해 연락 주시면 감사드리겠습니다. 


    먼저 한계점입니다. 이 글에 나와있는 방법으로 구현하게 되면 실시간으로 추천해줄 수 없다보니 실시간성이 중요한 서비스에는 근본적으로 적용하기가 어렵습니다. 

    또한 각자의 데이터와 환경에 따라서 추천의 성능이 크게 다를 수 있기 때문에 주의를 기울여야 합니다. AWS의 훌륭한 데이터 사이언티스트들이 이미 최적의 성능을 내도록 개발했지만, 아마존의 데이터가 학습에 다수 사용되었을 것으로 예상 되기 때문에 특정 데이터 구성에서는 좋은 성능을 내지 못할 수도 있습니다. 


    그럼에도 불구하고 저는 Personalize를 사용하는 방법이 유저 한명 한명의 데이터에 따라서 개인화 추천 결과를 만들어 낼 수 있는 가장 빠른 방법이라고 생각합니다. 이 개인화 추천 결과를 이용해서 shuffling 이나 다른 로직을 이용해서 충분히 좋은 추천 경험을 유저에게 선사할 수 있는 방법도 있구요. 

    실제 비즈니스 환경에서 이렇게 빠르게 추천시스템을 개발하고 운영하다보니 가장 좋았던 점은 다른 중요한 부분들에 집중할 수 있었다는 점입니다. 개인화 추천의 점수를 100점 만점으로 한다면, 짧은 시간 내에 70점 짜리 개인화 추천 기능을 만들어두고, 다른 부분의 개선에 집중할 수 있다는 측면에서 저희 팀과 같은 소규모 팀에서 시도해보기 좋은 서비스라고 생각합니다.

    이 글에 적힌 시행착오의 흔적들로 인해 여러분이 직접 구현하실 때 조금이나마 시간적인 이득을 보셨다면 저는 필자로서 행복할 것 같습니다. 

그럼 더 좋은 콘텐츠로 찾아뵙겠습니다. :)



P.S:

AWSKRUG 데이터 사이언스 소모임에서 본 글 내용으로 발표하였습니다. 

글 내용을 이해하시는데 도움이 되시면 좋겠습니다.

https://youtu.be/bOjE3IWYSS8

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari