스타트업에서 나홀로 식단 추천 시스템 만들기(1)

조홍철·2023년 7월 27일
3
post-thumbnail

처음 추천 시스템을 만들기로 할 때는 추천 시스템에 대해서 전혀 무지했다. 새롭게 시작하는 프러덕트에서 핵심 기능으로 입점된 냉동/냉장/신선 식품으로 구성된 1주일 식단을 생성/추천을 기획하게 되었고 아무 것도 모를 때 용감하다고 ‘제가 한번 해보겠습니다!’를 외쳤다. 현재 진행형인 식단 추천 시스템에 대해 자세한 구현 방식 보다는 지난 시간 동안 어떤 문제들을 어떤 질문들과 솔루션으로 해결하고 고도화 했는지 공유하고자 한다.

추천 해야하는 컨텐츠의 속성이 뭘까?


식단을 구성하기 위해서 식단에 대해서 전문 영양사님과 긴밀하게 소통하며 여러 가지를 정의하고자 했다. 그 중 추천해줘야 하는 컨텐츠 자체에 집중해보면 다음과 같은 특징이 있다.

1주일 식단을 구성하는 것은 아침/점심/저녁/간식에 해당 하는 끼니들이고, 끼니를 구성하는 것은 최대 3개의 음식이다. 우리가 가지고 있는 아이템은 ‘식품’이다. 이를 여러 개 조합하여 ‘끼니’를 만들고, 이 끼니도 여러 방법으로 조합하고 나서야 최종 형태인 식단이 된다.

추천을 처음 생각했을 때 넷플릭스, 유튜브, 기타 커머스들이 떠올랐는데, 영상이나 상품 자체가 큐레이션 형태로 추천 되는 것이 아닌 아이템을 수십 개 조합하여 하나의 추천 컨텐츠(식단)를 만들어야 하는게 달랐다. 또한 후에 고도화를 진행하면서 각각의 식품이 끼니를 구성할 때 ‘자연스러워야하고’, 1주일 식단안에는 일종의 맥락(context)이 존재한다는 것을 인지하였다. 또한 건강 식단이므로, 영양소 범위/알러지 등 특정 조건은 아주 엄격하게 지켜야한다는 점이 있었다.

우선은 최종 제공 형태를 만들어 DB에 저장하자


첫 시도에서 다양한 시행 착오가 있었다. 음식을 특징을 고려하여 총 3개의 category로 나눠 모든 음식을 조합하여 조건 검사를 통해 필터링한 ‘안전한 끼니’를 만들어 랜덤하게 조합하여 식단을 만드는 방식은 모든 음식을 조합하는 것이 너무 오랜 시간이 걸렸다. 따라서 카테고리 별로 음식을 랜덤하게 추출하고 조합된 끼니가 조건을 만족하는지 검사하여, 원하는 숫자만큼의 끼니가 만들어질 때 까지 반복하는 것을 선택하였다. 이를 효율적으로 하기 위해서 유전 알고리즘을 참고하여 낮은 점수로 조건을 만족한 끼니에 대해서 별도의 처리를 하여 진행하였다. 빠르게는 3초 정도의 식단이 나오기도 했지만, 아무리 반복해도 식단이 나올지 불명확한 경우도 다수 있었다.

처음부터 완벽한 시스템을 만들기보다 “일단은 빠르게 출시해보고 반응을 보자”가 목적이었기 때문에 알고리즘을 자동으로 돌게하는 스크립트를 작성하여 배포 전에 미리 DB에 최종 형태의 식단을 저장하여 제공하기로 하였다. 이렇게 진행하여 API요청 시에 많은 컴퓨터 리소스가 들지 않고 식단을 제공할 수 있었고, 충분히 반응을 볼 수 있었다.

테스트를 통해 결제로 이어진 경우도 있었지만, 전체의 1%~2%이내로 결제 전환이 일어날 뿐이었다. 하지만 테스트를 통해서 다양한 피드백을 수집할 수 있었다.

  • 자신의 선호/비선호를 반영할 수는 없는지
  • 끼니의 구성이 부자연스럽다
    • 유저 테스트 당시 부자연스러움을 느끼는 부분은 개인마다 달랐다
    • 유저들이 부자연스러움을 느끼는 포인트는 식단/끼니 내의 맥락(ex. 아침에 이런 음식이 추천이 되는 것이 이상하다, 특정 끼니를 구성하는 음식들이 어울리지 않는 것 같다)와 같은 것이었다.
  • 식단 내에 반복이 존재한다 / 다양성이 부족하다

이를 통해 추천에 중요한 요소가 개인의 선호 반영은 물론이고, 컨텐츠 자체의 맥락, 다양성도 중요하다는 것을 알게되었다.

유저의 선호를 알 수 있는 장치를 심자


유저의 선호를 알기 위해서 가능한 장치를 생각해보게 되었다. 기존 유저 별 권장 섭취 칼로리 계산을 위한 정보(신장, 운동량 등등)만 수집하던 온보딩(Onboarding)단계에서 유저의 선호와 비선호를 묻는 단계를 다양한 방식으로 추가하여 실험하였다. 또한 음식을 주문하고 먹어본 뒤에 해당 음식이 좋았는지 안좋았는지 피드백을 줄 수 있는 thumbs up/down 및 bookmark과 같은 장치들을 추가하였다. 지금 생각해보면 이런 explicit feedback을 받은 것이 유저 수가 적었던 점과 빠르게 유저의 선호를 반영 해야하는 상황에 좋은 판단이었던 것 같다.

User Feedback과 Numpy array를 활용하여 Rating System 만들기


본격적인 작업전에 ‘부자연스러움’을 해소하기 위해서 많은 유저 테스트를 거쳐, 자연스러운 ‘끼니’를 만들 수 있는 규칙을 정하게 되었다. 이를 기반으로 식품을 더 고도화된 category로 나누었다.(일반적인 식품 카테고리와 별개로 자연스러움을 위해 정의한 카테고리였다) 해당 카테고리를 기반으로 다양한 조건을 검사하는 수 십만 개의 끼니를 만들 수 있는 시스템을 먼저 만들었다. 물론 DB 저장 공간의 이유와 너무 많은 데이터를 로드할 수 없기 때문에 최종적으로는 균현을 고려하여 3만개의 끼니만 DB에 저장하였다 (이 작업을 배포때마다 해줘야 했다 - 식품이 추가될 때마다 새로운 조합의 끼니를 만들어주기 위해서)

Django를 사용하고 있었기 때문에 처음에는 만들어지 끼니에 대해서 ORM을 통해 요청 시에 유저 데이터를 활용하여 filter하는 방식을 선택했으나, 코드가 너무 복잡해져 새로운 조건을 추가하기가 어려웠고(유동적이지 못했고) 필터의 순서도 결과에 영향을 미치게 된다. 또한 속도도 느리고 추천이 불가능한 경우도 다수 발생하는 문제점은 그대로 남아있었다. 좋은 솔루션이 아니라고 생각하여 어떻게 다음 3가지를 해결할 수 있을지 고민하다가 Numpy array를 활용한 Rating system을 만들게 되었다

  • 컴퓨터 리소스를 크게 들이지 않고 수 만개의 끼니들에 유저 선호 및 다양성을 반영한 점수를 매길 수 있을까?
  • 추후에 또 다른 피드백이 들어왔을 때 유연하게 대응할 수 있는 방법이 무엇일까?
  • 식단을 만들 수 없어 추천에 실패하는 경우를 최소화 할 수 있을까?
    • 유저의 알러지가 보유한 음식에 대부분 포함되어 추천할 수 없는 경우를 제외하고, 필터 방식으로 진행하지 않으면서 유저의 선호를 제일 가깝게 맞출 수 있는 방법이 무엇일까?

먼저 numpy array는 python에는 존재하지 않는 array를 사용할 수 있게 해준다. python은 모든 것이 객체인 만큼 각각의 객체가 메모리 공간에 따로 존재하고 해당 주소값을 통해 관리하는 list만 존재하였다. 하지만 numpy는 C의 array와 같이 실제 메모리 공간에 연속되게 저장해주기 때문에 병렬처리가 가능하였다. 이 장점을 이용하여 컴퓨터 리소스를 크게 들이지 않고 여러 조건을 검사할 수 있다. 독립적인 조건을 검사하는 여러 개의 층(layer)를 만들고 각 아이템(끼니)에 매겨진 점수를 모두 합하면 아이템 별 최종 점수가 나오기 때문에 순위를 매길 수 있게 된다. 또한 새로운 조건이 추가되면 그 떄마다 layer를 추가하면 되는 유동적인 구조를 만들 수 있었다.

조건 알러지를 제외하고 모든 것을 점수화하였다(알러지는 어떤 경우에도 추천되면 안된다). 유저의 explicit feedback을 기반으로한 조건도 있지만, implicit feedback을 기반으로한 조건도 추가되었다. 조건 간의 우선순위가 있기 때문에, layer에 따른 가중치를 정의하였고, 최종적으로는 가중치 행렬과 점수 행렬의 곱을 통해서 최종 점수를 계산하는 시스템을 만들게 되었다

해당 방식은 좋은 반응이 있었다. 코호트 당 결제 전환이 5%~10%까지 올랐다. API 요청시에 rating system자체는 1초가 걸리지 않았고, 뽑힌 식단을 후가공하여 DB에 최종적으로 저장까지 하는 전체 과정은 2초~3초가 정도였다.

하지만 조건이 늘어남에 따라 layer 가중치를 다루는 것이 쉽지 않았다. 기존에는 기존 유저 데이터를 기반으로 표본 집단을 추출하여 건전성 검사 코드를 만들어 결과를 추출한 뒤 layer 가중치를 조절하는 방식으로 진행하였다. 하지만 조건이 많아짐에 따라 가중치 값을 관리하는 것이 어려워졌다. 또한 정해준 규칙(Rule)로 의해 만들어진 끼니들로 인해 다양성이 부족하다는 user feedback이 있었다. 마지막으로 유저들은 비선호에 대해서는 확실한 필터링을 원했는데, 끼니 단위로 점수를 매기다 보니 끼니 중 점수가 월등히 높은 음식이 포함되어있을 때 비선호하는 상품이 같이 추천되어 좋지 않다는 feedback도 수집할 수 있었다.

profile
데이터와 파이썬을 좋아합니다 :) contact : chal405@naver.com

2개의 댓글

comment-user-thumbnail
2023년 7월 27일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

답글 달기
comment-user-thumbnail
2023년 7월 28일

혼자서 식단 추천 시스템을 만드시다니 정말 대단하십니다 🥹

답글 달기