Chanhee's Dev Blog

마케팅 Life Time Value (LTV) 예측 프로젝트

진행 기간: 2021.01 ~ 2024.03

프로젝트 소개

효율적인 마케팅 예산 집행을 위해 캠페인 별 평생 가치(LTV)를 예측하는 프로젝트.

문제 상황

프로젝트 목표

프로젝트 변천사

LTV 프로젝트는 제가 이어받기 전인 2017년부터 연구가 시작돼서 장장 7년이 넘게 지속된 프로젝트인만큼, 프로젝트 나름의 역사를 가지고 있습니다. 제가 프로젝트에 참여하기 시작한 2021년부터, 프로젝트가 마무리 된 2024년까지의 변천사를 큼직한 변화를 기준으로 ‘스테이지’로 구분해 설명해보겠습니다.

stage 기간 한 줄 요약 모델링 서빙 방식 데이터/훈련/서빙 파이프라인
1 2021 기존 서비스 유지보수 Bayesian (MCMC) dashboard / jupyter notebook 서빙: airflow
2 2022 모델 성능 개선 MCMC + linear regression dashboard / jupyter notebook 서빙: airflow
3 2023 상반기 UX + 속도 개선 위와 동일 AWS Sagemaker + Dash web 데이터: AWS lambda, 서빙: AWS Sagemaker + lambda
4 2023 하반기 모델링 방법론 변경 Deep learning (MLP, TiDE) dashboard 데이터: databricks, 훈련: mlflow, 서빙: databricks
5 2024 초기 수준의 MLOps 구축 classical ML mlflow model registry 데이터: databricks, 훈련: mlflow, 서빙: mlflow

Stage 1: LTV 초기 모델

LTV 예측 프로젝트는 제가 입사한 2021년 이전부터 오랫동안 연구가 이어진 오래된 프로젝트로, 이미 LTV 예측값을 통해 마케터가 캠페인의 성과를 판단하는 프로세스가 어느 정도 정립된 상황이었습니다. 마케터는 두 가지 방식으로 LTV 예측값을 제공받았는데, 하나는 대시보드, 하나는 jupyter notebook 형태의 코드였습니다.

LTV 예측 모델은 BTYD (Buy Till You Die) 확률 모델 기반으로 MCMC 시뮬레이션을 수행하는 방식이었고, 한 코호트의 7일 간 평균 데이터를 바탕으로, 365일 간의 평균 LTV를 예측하는 형태였습니다.

프로젝트에 대한 자세한 내용은 여기 프로젝트 문서에, LTV 모델링 및 MCMC를 활용한 예측 방식에 대해서는 별도의 모델링 설명 문서에 정리했습니다.

Stage 2: LTV 모델 성능 개선

Stage 1에서의 LTV 모델은 특정 한 게임의 유저 패턴을 바탕으로 얻은 인사이트가 반영됐다보니, 다른 게임에 적용했을 때 오차가 있을 것으로 예상했습니다. 하지만, 예측하려는 타겟 기간 자체가 365일로 길었기 때문에 새로운 게임에 대한 LTV 모델 개선을 빠르게 진행하는 것은 어려웠습니다. 일종의 콜드 스타트 문제라고도 볼 수 있겠습니다.

아무 증거없이 모델 개선을 실행하는 것보다는, 새로운 게임에 적용한지 365일 이후부터 LTV의 실제값과 예측값의 오차를 추적해보기로 결정했습니다. Stage 2는 바로 이 시점부터 시작된 프로젝트였습니다.

모델링 문서에서 언급했듯이, LTV 모델의 큰 구성인 ARPU(Average Revenue Per User) 예측과 Life Time 예측 중 ARPU 예측에 휴리스틱 기반의 가정들이 많았습니다. Life Time에도 많은 가정이 들어가지만 일반적인 모바일 게임 리텐션 패턴에서 크게 벗어나지 않는다고 보았습니다. 때문에 실제 오차를 확인해본 뒤, 예상대로 ARPU 쪽의 예측의 오차 비중이 클 경우 전체적인 뼈대를 유지하되 ARPU 예측에 추가적인 fitting 모델이 들어가는 편이 효율적일 것이라 판단했습니다.

실제 오차를 확인해본 결과, 예상대로 ARPU 예측의 오차가 비중이 높았습니다. 데이터 분석 결과 7일 간의 ARPU 데이터와 365일의 ARPU에서 강한 선형성을 볼 수 있었기 떄문에 이를 단순한 linear regression으로 fitting 했습니다.

Linear regression 적용 이후에도 지속적으로 LTV 예측 오차를 추적했고, 만족할만한 오차 개선을 확인할 수 있었습니다.

Stage 3: LTV 웹앱 개발

제가 생각한 데이터 사이언티스트의 역할은 그저 예측값을 정확하게 제공하는 것에 그치지 않았습니다. 결국은 의사결정 과정에서 얼마나 실질적으로 도움이 되는지, 더 효율적인 방법으로 도움을 줄 수 있는지 고민하는 것이 더 근본적인 역할이라고 생각했습니다. Stage 1, 2를 거치면서 모델의 예측력 측면에서는 개선이 있었으나, 사용성 측면, 그리고 유지보수 측면에서는 아직 많은 불편함이 있었습니다. 마케터와의 인터뷰를 통해 사용성 측면의 불편함을 파악하고, 제가 개발 과정에서 느낀 유지보수 측면에서의 개선점을 정리하여 LTV 웹앱 개발 프로젝트를 진행했습니다.

사용성 측면에서 대부분의 문제는, 마케터가 익숙하지 않은 서버에 접속해서 jupyter notebook을 업로드하고 실행해야 하는 환경에서 비롯됐습니다. LTV 예측이 실행되는 동안 웹브라우저 세션을 종료하지 않고 유지해야 했는데, 그동안 렌더링이나 데이터로 인한 메모리 점유 때문에 브라우저 리소스를 많이 소모해서 다른 업무에 지장이 가기도 했습니다. 그래서 시간이 오래 걸릴만한 LTV 예측 작업을 별도의 백엔드 서버에서 비동기로 처리하고, 그 결과만 웹을 통해 확인할 수 있도록 웹 애플리케이션을 개발하는 방향으로 기획했습니다. 그러지 않아도 당시 빅데이터 및 BI가 데이터 업계의 큰 화두로 떠오르면서 streamlit, dash 등 웹 기술을 잘 모르는 데이터 사이언티스트도 쉽게 웹앱을 만들 수 있는 라이브러리가 유행하던 시기였습니다. 팀 내의 데이터 분석가 및 데이터 사이언티스트들이 이미 plotly라는 라이브러리를 활용하여 인터랙티브한 시각화를 많이 제공하고 있었기 때문에, 사용자의 경험을 비슷하게 유지하기 위해 dash를 선택해 프론트엔드 쪽을 구현했습니다.

유지보수 및 성능 측면에서도 몇 가지 문제가 있었습니다. 우선 매번 LTV 예측 작업을 실행시킬 때마다 파라미터만 바뀌고 같은 로직의 데이터 전처리 과정이 반복됐는데 이 전처리 과정만 해도 전체 작업 시간의 절반 정도를 차지하고 있었습니다. 그래서 연산량이 큰 쿼리 작업들은 별도의 DW(Data Warehouse)에 매일 단위로 적재하고, LTV 예측 시에는 최소한의 전처리만 하도록 파이프라인을 구성했습니다. 또한 LTV 예측값이 매 실행 때마다 휘발되어 과거 분석 결과를 다시 재현하기 어려운 문제가 있었습니다. 이를 해결하기 위해 AWS lambda 및 Sagemaker Pipeline 기술 스택을 채용해, dash 프론트에서 발생한 요청이 생기면 lambda를 통해 Sagemaker pipeline job을 트리거하고, 그 결과를 S3에 적재한 뒤 다시 lambda를 통해 slack 알림을 보내는 플로우를 구현했습니다.

이를 통해 LTV 예측 실행이 마케터의 본업을 방해하지 않고 더 효율적으로 활용할 수 있었다는 피드백을 받을 수 있었습니다. 데이터 사이언스 팀 입장에서도 job 단위의 버전 관리 및 MCMC를 통해 예측한 결과를 저장해두고 계속 재활용할 수가 있었고 전체 작업 시간도 크게 단축시킬 수 있었습니다.

Stage 4: 딥러닝 기반의 LTV 모델링

LTV 예측 서비스 자체는 마케팅 팀에서 꾸준히 활용하고 있었지만, 이를 2-3인 규모의 작은 데이터 사이언스 팀에서, 타 머신러닝 프로젝트까지 진행하면서 유지보수하는 데에 부담이 계속 쌓이고 있었습니다. 아무래도 MCMC 자체가 일반적인 머신러닝, 딥러닝에 비해 널리 쓰이는 기법은 아니다보니 방법론에 대한 학습 및 비즈니스 맥락을 타 팀원에게 전달하기가 어려웠습니다. 또한 특정 확률 분포를 고정해둔 모델이기 때문에 다른 문제에 적용할 수 있는 범용성이 떨어지고, 다양한 feature를 사용하기도 어렵고, output의 형태도 쉽게 변경하기 어려운 등 지속적으로 변경되는 요구사항에 비해 유연성이 부족하다는 문제도 있었습니다. 그래서 4년 넘게 잘 서비스 해오던 예측이지만 더 이상의 개선이 어렵다고 판단해 과감하게 근본적인 모델링 방식을 변경하게 되었습니다.

우선 태스크 정의를 변경했습니다. 기존에는 코호트 별 7일 간의 매출 평균과 리텐션 값들을 토대로, 365일 평균 LTV라는 스칼라 값을 뽑는 태스크였습니다. 하지만 Stage 2 과정에서도 느꼈듯이, 새로운 게임에 적용해서 빠르게 개선을 해보려면 더 빠른 피드백을 받을 수 있는 문제 세팅이 중요했습니다. 그래서 새로운 태스크는 일종의 seq-to-seq regression 태스크로 정의해, 0-7일차의 데이터 배열을 받아, 이후 1년 동안(8-364일차)의 LTV 예측값 배열을 뽑아내는 것으로 변경했습니다. 이렇게 하면 365일을 기다리지 않고도 30일, 60일 등 중간 시점에서 예측값과 실제값을 비교해 모델을 검증할 수 있습니다.

모델링 후보는 문헌 조사 후 그 중에서 가능성이 높아보이는 모델링 방식들을 선택했습니다. 단일 모델에 의존하는 것은 너무 불확실하기 때문에, 문헌 조사 과정에서 최대한 여러 방식을 탐색해보고자 했습니다. 그렇다고 모델 수가 너무 많아질 경우, 2인의 인력으로 모든 모델을 구현하고 비교분석하는 데에 시간이 너무 많이 필요하기 때문에 실제 구현을 결정한 건 2가지 모델링이었습니다. 하나는 베이스라인 모델, 하나는 메인 예측 모델의 역할로 고려해 병렬로 개발했습니다. 베이스라인으로서 시도해본 것은 단순한 MLP(Multi-Layer Perceptron) 구조였습니다. 구현이 단순하기 때문에 다양한 feature를 넣어보고 빠르게 실험해보고 싶은 의도에 잘 맞을 것이라고 판단했습니다. 메인 모델로는 문헌 조사를 통해 TiDE(Time-series Dense Encoder)를 선택했습니다. 예측하려는 데이터가 시계열이다보니 시계열에 특화된 딥러닝 모델을 위주로 찾아봤고, 365일이라는 기간이 꽤나 장기 예측이기 때문에 이에 대한 강건성이 돋보이는 모델링이라는 점이 주요 선택 요인이었습니다.

하지만, 예측 결과가 LTV의 특성을 잘 반영하지 못하는 문제가 있었습니다. LTV는 시간이 갈수록 누적되는 값이어야 하는데, 그런 constraint를 반영하기 위해서는 데이터셋의 가공이 유연하게 이뤄져야 했습니다. 당시에는 모델 개발 과정에서 데이터 전처리의 유연성을 충분히 고려하지 못하고 이전 Stage처럼 스키마를 어느 정도 고정시켜놓고 빠르게 데이터를 추출하도록 파이프라인을 구축하는 데에 집중했는데, 이런 점이 오히려 모델 개발의 발목을 잡았던 것 같습니다. 또한, 캠페인 단위로 특성을 뽑아내다보니 데이터 수 자체가 충분히 많지 않았던 점도 있었습니다. 이 때문에 예측값의 오차를 분석해봤을 때 underfitting이 되는 패턴이 많이 보였습니다.

Stage 5: LTV MLOps - 모델 라이프사이클 관리

Stage 4에서 배운 가장 큰 교훈은, 모델을 개발하는 과정에서도 모델 평가 과정을 자주, 빠르게 실행해야 한다는 것이었습니다. 그동안은 모델이 배포된 이후 실제 데이터와 비교해보는, 어떻게 보면 모델의 재훈련 관점에서만 모델 평가를 바라보고 있었습니다. 하지만 실제 데이터가 추가로 공급되기 전, 모델을 개발하는 과정에서 모델 성능을 어떻게 향상시킬 수 있을지 더 생각해봐야 했습니다. 결국 중요한 건, 매번 코드나 데이터셋이 바뀔 때마다 어떤 변경점이 모델의 성능에 영향을 주었는가 판단하는 것이었습니다. 이 판단을 위해서는 마치 제가 물리학과 대학원 때 실험했던 그때처럼, 그 기록을 잘해두고 재현성을 향상시키는 것이 필요했습니다. 특히나 Stage 4에서 모델 개발의 발목을 잡았다고 지적했던 데이터셋의 유연성 측면에서 더 그렇습니다. 데이터셋의 영향을 알아보기 위해 통계 분석 등을 진행하려면 다시 그 데이터셋을 불러올 필요가 있는데, 데이터셋을 유연하게 구성하다보면 그 방식이 복잡해져 재현이 어려워지기 때문입니다.

또한, 모델 자체도 서비스라는 관점에서 개발 단계인지 배포 가능한 수준인지 정의할 필요가 있었습니다. 생성되는 모델의 수가 굉장히 많기 때문에 그 중에서 배포 후보를 뽑고, 그 후보들 간의 장단점을 비교해서 최종 배포 모델을 선정하는 과정이 필요했습니다. 물론 이 과정이 최종적으로는 자동화되겠지만 개발 초기 단계에서는 어떤 지표가 비즈니스 성과와 연관성이 깊을지 판단하기 어렵기 때문에 사람의 판단이 필요합니다.

이를 위해 새로운 LTV 모델 개발과 더불어 초기 수준의 MLOps 시스템을 구축했습니다. 새로운 모델은 딥러닝 기반 모델 대신 scikit-learn, XGBoost 등을 활용한 고전적인 머신러닝 모델을 활용했고 MLOps 시스템은 Databricks 플랫폼과 mlflow를 통해 만들었습니다.

모델링 방식을 바꾼 것은 크게 2가지 이유입니다. 우선 데이터 부족으로 인한 underfitting 문제입니다. 이를 위해 상대적으로 데이터 효율성이 좋은 XGBoost를 선택했습니다. XGBoost는 정형 데이터에 강한 예측력을 보일 때가 많아서 일부 문제에서는 딥러닝보다 좋은 성능을 내기도 합니다. 또한 데이터 분석을 통해 몇몇 feature가 타겟 예측값과 꽤나 강한 선형성을 보이는 것을 확인해, 복잡도가 낮은 모델로도 가능하겠다는 예상이 가능했습니다. 두 번째 문제는 해석 가능성입니다. 딥러닝 기반 모델을 제공해보니, 의사결정하는 마케터 입장에서는 예측값 뿐만 아니라 그 값이 도출되는 과정을 알 수 없어 답답할 때가 많았습니다. 예를 들어 특정 상품을 산 경우에 LTV가 높았다던지 하는 인사이트가 있었다면 다음 예산을 설정할 때나 마케팅 소재를 선정할 때 도움이 됩니다. 이를 위해 해석 가능성이 좋은 모델을 고르고자 했습니다.

그리고 모델의 라이프사이클 관리를 위해 Databricks 플랫폼 상에서 mlflow를 이용해 MLOps 시스템을 만들었습니다. 우선 Mlflow tracking이라는 기능을 적극 활용해 훈련 과정에서의 하이퍼파라미터, 데이터셋 버전, 코드 커밋 버전, 테스트 metric 및 시각화 결과를 로깅하고 재확인할 수 있었습니다. 또한 모델의 개발부터 배포까지의 단계를 표현하기 위해 mlflow model registry 기능을 활용해 dev -> challenger -> champion 단계로 모델 수준을 tiering 및 태깅했습니다. 일종의 모델 배포 프로세스로, challenger까지의 선정은 지표만을 활용해 rule-based로 자동화하고, champion 선정은 주기적으로 사람이 리뷰하는 과정도 만들었습니다. 마지막으로 champion으로 선정된 모델은 mlflow serving 기능을 통해 원하는 endpoint로 서비스할 수 있었습니다.