Long Tail Event에서 예측시 정확도 높이기 (Doordash Case)

Long Tail Event

  • Long Tail Event는 Right Skewed Distribution 에서 발생하는 특징 중 하나로, Outlier와도 비슷해보이지만, Outlier대비 상당히 길게 꼬리형태로 길게 이어지는 Data Point들이 보이고, 이들의 빈도가 상대적으로 높은 편이다. 이러한 형태는 이커머스나 배달앱 등 여러 곳에서 자주 보이는 형태이다. 전체 회원을 RFM 관점으로 나눠서 봐도 Long Tail Event를 찾아 볼 수 있다.
  • Long Tail을 예측하는 것은 비즈니스 관점에서 매우 중요하다. 빈도가 많다고 하지만, 그래도 상대적으로는 드물게 발생하고 발생시점이 불규칙적인데 비해서, 비즈니스 관점에서 매출 등에서 꽤 중요한 위치를 차지하기 때문에 적절히 예측하고 관리할 수 있다면 비즈니스의 지속가능성에서 꽤 중요하다.
  • Normal Distribution 대비 대칭 구조가 아니기도 하고, 데이터 양도 적다 보니 상대적으로 Ground Truth를 찾기 어렵렵다보니 Long Tail Event은 상대적으로 예측이 어렵다.
image

예측시 정확도 높이기

  • Doordash는 이런 Long Tail Event가 포함된 Data를 예측하기 위해서 다음과 같은 접근을 취하였다. 첫 두개의 액션은 Feature Engineering 관점에서 도메인의 지식을 빌어서, 세 번째는 Training 관점에서 Modeling 지식을 바탕으로 접근한 것으로 보인다.
  • Historical Feature
  • 도메인 지식을 바탕으로 적절한 Feature를 선정하되 Target Value에 기반한 bucketing / encoding을 통해서  Feature값을 Scaling 해주고 Feature의 Range을 Normalization해주었다. 이렇게 해서 Normal Distribution 형태로 Transformation을 시도하였다.
  • Real-time Feature
  • 다양한 상황을 모델에 모두 넣고 훈련하는 것은 쉽지않다. 대신 Inference 시점에 다양한 실시간 정보를 받아 처리함으로써 Model이 그 상황에 맞춰 적절한 예측값을 내보내도록 할 수는 있다.
  • Custom Loss Function
  • MSE(Mean Squared Error) 등의 Quadratic Loss Function을 이용해서 Long Tail Event에 보다 민감하게 반응하도록 유도를 할 수 있다. 하지만 Long Tail Event은 앞서 언급한 바와 같이 Symmetric 하지 않기 때문에 Asymmetric하게 값이 변하도록 바꿔줄 수 있다.  아래 식은 Doordash에서 제시한 Custom Loss Function이다. $${1 \over n} \sum\limits^n_{i=1} |\alpha - \mathbb{1}_{g(x_i) - \hat{g}(x_i)<0}|  {(g(x_i) - \hat{g}(x_i))^2}, \space for \space \alpha \in (0,1)$$
  • ${(g(x_i) - \hat{g}(x_i))}^2$ 이 이제 예측치와 실제 값의  Residual에 대한 부분을 계산하는 L2 Loss Function이라면 앞 부분이 Asymmetric하게 Long Tail Event에 대해서 대응할 수 있는 부분이다. 실제 값이 예측치보다 작으면 항등함수가 1을 뱉을 것이고, 아니면 0을 뱉을 것인데 여기에 $\alpha$를 이용해서 조절해준다.
  • 이 때 MSE가 아닌 MAE를 쓸 수도 있겠지만, 앞서 언급한 것처럼  Long Tail Event에 대해서 더욱더  Sensitive하게 반응을 하도록 하기 위해서 MSE를 쓴 것을 확인할 수 있다. 아래 코드를 봐도 MSE가 더욱 민감하다. Doordash가 Base Loss Function으로 MSE를 쓴 이유이다. 여기에 $\alpha$를 이용해서 늦은 배달과 빠른 배달의 패널티 차이를 조절하고 있다.
from sklearn.metrics import mean_squared_error, mean_absolute_error
actual = [100,120,80,110]
predicted = [90,120,50,140]
mse = mean_squared_error(actual, predicted)
mae = mean_absolute_error(actual, predicted)
print(f"mse: {mse}")
print(f"mae: {mae}")
mse: 475.0
mae: 17.5

References