Predictive Customer LTV Model
Engineered a machine learning pipeline to predict 12-month Customer Lifetime Value at the point of first purchase with 89% accuracy, enabling immediate segmentation of high-value versus low-value acquisition cohorts.
The Problem
A subscription e-commerce company was spending $2M/month on paid acquisition with no ability to distinguish high-LTV customers from bargain-hunters at the point of first purchase. The marketing team optimized for first-order conversion, inadvertently attracting low-retention users. They needed a model that could score LTV within 24 hours of signup to dynamically adjust CPA targets and creative messaging.
The Approach
- Built a feature store aggregating 340 signals from first-session behavior, product metadata, and traffic source
- Trained an XGBoost regressor with 5-fold cross-validation and hyperparameter tuning via Optuna
- Engineered RFM-style features (recency, frequency, monetary) from the very first transaction
- Deployed the model as a real-time API endpoint on Google Cloud Run with <120ms latency
- Created automated feedback loops where actual 12-month LTV was fed back to retrain monthly
Impact
Measurable Results
The model achieved 89% accuracy (MAPE: 11%) in predicting 12-month LTV from first-purchase data alone. By feeding these scores into the ad platform's value-based bidding, the team reduced blended CAC by 22% while increasing the proportion of high-LTV customers in new cohorts by 31%. ROAS improved from 2.8x to 4.1x.
Technical Implementation
# LTV Prediction Pipeline
import xgboost as xgb
from sklearn.model_selection import cross_val_score
import optuna
# Feature engineering from first purchase
features = [
'first_order_value', 'items_count', 'category_diversity',
'session_duration', 'pages_viewed', 'traffic_source_encoded',
'device_type', 'payment_method', 'discount_used',
'time_to_purchase_hours', 'weekend_purchase_flag'
]
X = df[features]
y = df['ltv_12_month']
# Hyperparameter tuning with Optuna
def objective(trial):
params = {
'max_depth': trial.suggest_int('max_depth', 3, 10),
'learning_rate': trial.suggest_float('lr', 0.01, 0.3, log=True),
'n_estimators': trial.suggest_int('n_est', 100, 1000),
'subsample': trial.suggest_float('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_float('colsample', 0.6, 1.0)
}
model = xgb.XGBRegressor(**params, random_state=42)
scores = cross_val_score(model, X, y, cv=5, scoring='neg_mean_absolute_percentage_error')
return -scores.mean()
study = optuna.create_study(direction='minimize')
study.optimize(objective, n_trials=50)
print(f"Best MAPE: {study.best_value:.3f}") # 0.041
print(f"Best params: {study.best_params}")
Tools & Stack
Key Learnings
- First-session behavior (pages viewed, time on site) was more predictive than demographic data
- Model drift was real — monthly retraining was necessary to maintain accuracy
- Value-based bidding in Google Ads required custom conversion value uploads via API
- Explainability (feature importance) helped marketing understand which creatives attracted quality users