A Creative Intelligence prototype for mobile advertisers โ built for the Smadex Creative Intelligence Challenge.
The Creative Copilot helps advertising teams answer four hard operational questions:
- Which creatives are working โ and why?
- Which are wearing out โ and when exactly did fatigue kick in?
- What should I test next โ given what I am already running?
- What would a brand-new creative score โ before I spend a dollar producing it?
It answers all four through a single self-contained HTML dashboard backed by a scikit-learn + XGBoost ML pipeline.
| Tab | What it does |
|---|---|
| ๐ Performance Explorer | Browse, filter and rank all 1 080 creatives by any KPI. Paginated grid view or bar chart. |
| ๐ Fatigue Detection | Time-series CTR chart for any creative. Normalized view (% of launch baseline) with per-status benchmark lines. |
| ๐ฏ Recommendations | KPI-aware action cards: Scale / Monitor / Pause, ranked by performance score. |
| ๐ก ML Insights | Random Forest feature importance (global + per KPI goal). Cross-validation Rยฒ and MAE. PCA variance breakdown. |
| ๐ต Clusters | K-Means cluster profiles (k=8). Cluster composition, dominant traits, average performance. |
| ๐งฎ Portfolio Optimizer | Gram-Schmidt orthogonal portfolio builder with a strategy slider (pure performance โ pure diversity) and KPI targeting. |
| ๐ฎ Predict Creative | Estimate the performance of a hypothetical new creative before producing it. |
| ๐ Help | Full metric glossary, tab guide, and an embedded Ask Claude AI chatbot. |
CSV data (6 files)
โ
โผ
ml_pipeline_sklearn.py โ Python, run once locally
โโ pandas: data loading & joins
โโ PIL: image feature extraction (18 visual features per asset)
โโ sklearn: PCA ยท KMeans(k=8) ยท RandomForestRegressor
โโ xgboost: XGBRegressor (KPI-specific models)
โโ shap: TreeExplainer (per-creative feature attribution)
โโ 5-fold cross-validation (Rยฒ ยฑ std, MAE)
โ
โผ
app_data.js โ embedded APP_DATA (creatives + timeseries)
ml_results.json โ feature importance ยท clusters ยท SHAP ยท model params
โ
โผ
build_dashboard.py โ patches both files into the HTML template
โ
โผ
creative_copilot.html โ single self-contained file, open in any browser
โโ Chart.js 4: all charts
โโ Anthropic API: live AI explanations (claude-haiku-4-5)
โโ Gram-Schmidt orthogonalisation in JS (Portfolio Optimizer)
- Python 3.9+
- pip
pip install -r requirements.txtThe requirements.txt includes:
pandas>=1.5
scikit-learn>=1.3
xgboost>=2.0
shap>=0.44
Pillow>=9.0
numpy>=1.24
Note:
xgboostandshapare optional. The pipeline falls back tosklearn.ensemble.RandomForestRegressorand simplified feature attribution if either is unavailable.
python ml_pipeline_sklearn.pyThis takes roughly 2โ5 minutes (image feature extraction is the bottleneck). It produces:
app_data.jsโ all creative and timeseries data (~3 MB)ml_results.jsonโ ML outputs: feature importance, clusters, SHAP values, cross-validation metrics, model parameters (~500 KB)
python build_dashboard.pyPatches app_data.js and ml_results.json into the HTML template and writes creative_copilot.html (~3 MB).
# macOS
open creative_copilot.html
# Linux
xdg-open creative_copilot.html
# Windows
start creative_copilot.htmlNo server required โ it runs entirely in the browser.
Browse all 1 080 creatives with live filtering by advertiser, KPI goal, status, format, vertical, and theme. Switch between a paginated card grid (with thumbnail, status badge, KPI value, and an AI Explain button) and a bar chart of the top 20 by any metric.
Controls:
- Sort by: Performance Score, CTR, CVR, ROAS, IPM, Spend
- Filters: Status ยท KPI Goal ยท Format ยท Vertical ยท Theme
- View: Grid (24 per page) or Chart (top 20)
Pick any creative and see its daily CTR over time. Toggle between:
- Absolute CTR โ raw click-through rate per day
- Normalized (% of launch) โ CTR expressed as a fraction of the first-7-day average (= 100%). Makes fatigue curves comparable across creatives with different baseline CTRs.
Three benchmark lines show where each status group typically lands:
- Top Performer: retains ~33% of launch CTR
- Stable: retains ~23%
- Fatigued: retains ~16%
Important: CTR naturally decreases for all digital ads (banner blindness, audience saturation). The distinction between "good" and "fatigued" is not whether CTR drops, but how fast and how far.
For each creative, the engine assigns one of three actions:
| Action | Criteria |
|---|---|
| Scale | top_performer status |
| Monitor | stable status |
| Pause | fatigued or underperformer status |
The KPI metric shown in each card matches the campaign's kpi_goal: CPA โ CVR, ROAS โ ROAS, IPM โ IPM, CTR โ CTR.
Three panels:
- Feature Importance โ select a model (global or KPI-specific) to see which creative attributes the model found most predictive. Image features (extracted from PNG thumbnails via PIL) are prefixed with
img_. - Cross-Validation โ 5-fold CV results: Rยฒ ยฑ std and Mean Absolute Error for an honest out-of-sample accuracy estimate.
- PCA Variance โ cumulative explained variance across 20 principal components.
K-Means (k=8) groups the 1 080 creatives into 8 behavioural archetypes based on performance metrics, creative attributes, and image features. Each cluster card shows dominant format/theme/tone/KPI, average performance score, and status distribution.
Step 1 โ Build your portfolio. Search and add the creatives you are already running.
Step 2 โ Choose your strategy. A slider controls the blend:
ฮฑ = 0โ pure performance: recommend the best-performing remaining creativesฮฑ = 1โ pure orthogonality: recommend creatives that explore the most unexplored creative territory relative to your current portfolio- Any point in between blends both signals proportionally
KPI targeting โ check one or more KPI types to give a 2ร score boost to candidates whose campaign matches your goals.
How orthogonality works: Each creative is represented by its 10-dimensional PCA feature vector. The optimizer computes a Gram-Schmidt orthonormal basis for the portfolio's subspace, then scores each candidate by the fraction of its vector that lies outside that subspace. Score = 100% means completely unexplored creative territory.
Enter the attributes of a hypothetical creative (format, vertical, theme, tone, OS, KPI, duration, text density, and binary flags) and get an instant performance estimate.
- Cluster matching โ finds the K-Means cluster whose profile best matches your inputs.
- Feature adjustments โ applies boosts/penalties from the RF/XGBoost feature importance for your specific KPI.
- Prediction band โ maps the score to Likely Top Performer / Stable / Below Average / High-Risk with a recommended action.
- Benchmarks โ shows the top 3 existing creatives in the matched cluster+KPI combination.
After running ml_pipeline_sklearn.py locally (which embeds PCA components and scaler parameters), the predictor upgrades to full PCA-based OLS inference.
Static glossary of all metrics, a tab-by-tab usage guide, and an embedded Ask Claude AI chatbot. The chatbot can answer questions like "What does a perf_score of 0.3 mean?", "Why does CTR always drop over time?", or "How do I interpret the orthogonality score?"
Each creative card has an ๐ค Explain button:
- On first use, you will be asked for your Anthropic API key (stored in memory only, never persisted or sent anywhere except the Anthropic API).
- The dashboard structures a JSON payload: performance metrics, status, cluster profile, top KPI feature drivers, and auto-detected positive/negative signals.
claude-haiku-4-5translates the payload into a plain-English 3โ4 sentence explanation in marketing language.
The model is strictly constrained to explain using only the provided data, preventing hallucination.
| Metric | Definition | Typical range |
|---|---|---|
| CTR | Clicks รท Impressions. Fraction of users who tap the ad. | 0.8โ2.5% |
| CVR | Conversions รท Clicks. Fraction of clickers who complete the goal action. | 10โ40% |
| ROAS | Revenue รท Spend. Dollars of attributed revenue per dollar spent. | >1.0 = profitable |
| IPM | Conversions per 1 000 impressions. Direct install-campaign efficiency. | 3โ15 |
| perf_score | Composite score (0โ1) from CTR, CVR, ROAS, and IPM normalised within the dataset. | >0.65 = top performer |
| Metric | Definition |
|---|---|
| first_7d_ctr | Average CTR across the first 7 active days โ the freshness baseline. |
| last_7d_ctr | Average CTR across the last 7 active days of data. |
| ctr_decay_pct | (last_7d_ctr โ first_7d_ctr) / first_7d_ctr. Negative = CTR fell. โ0.67 = 67% drop. |
| cvr_decay_pct | Same as above for CVR. |
| fatigue_day | Day number (since launch) when performance decay became material. |
| CTR retention | last_7d_ctr / first_7d_ctr. Top performers retain ~33%; fatigued creatives only ~16%. |
| Attribute | Definition | Range |
|---|---|---|
| text_density | Fraction of visual area occupied by text. | 0 (minimal) โ 1 (text-heavy) |
| readability_score | Ease of reading the copy. | 0 โ 1 (higher = easier) |
| brand_visibility_score | Prominence of logo/brand mark. | 0 โ 1 |
| clutter_score | How visually busy/cluttered the layout is. | 0 (clean) โ 1 (cluttered) |
| novelty_score | Estimated originality relative to other creatives. | 0 โ 1 |
| motion_score | Intensity of movement/animation. | 0 (static) โ 1 (high motion) |
| duration_sec | Video or interactive ad length in seconds; 0 for static formats. | 0 โ 120 |
| faces_count | Number of people/faces visible. | Integer โฅ 0 |
| has_gameplay | 1 if the creative shows gameplay footage. | 0 / 1 |
| has_ugc_style | 1 if the creative mimics a UGC / creator-content layout. | 0 / 1 |
| has_price | 1 if a monetary price or offer is visible. | 0 / 1 |
| has_discount_badge | 1 if a sale or bonus badge is shown. | 0 / 1 |
| Status | Meaning | Recommended action |
|---|---|---|
| top_performer | High performance, CTR holds well over time. | Scale budget; create A/B variants. |
| stable | Solid but not exceptional; no strong fatigue. | Monitor; consider iterating. |
| fatigued | Clear performance decay past fatigue_day. |
Pause or refresh; do not increase spend. |
| underperformer | Consistently weak primary KPI. | Reallocate budget; retire concept. |
| KPI | Campaign focus | Primary metric |
|---|---|---|
| CPA | Cost-per-acquisition โ minimise cost per conversion | CVR |
| ROAS | Return on ad spend โ maximise revenue per dollar | ROAS |
| IPM | Installs per mille โ maximise install volume | IPM |
| CTR | Click-through rate โ maximise engagement | CTR |
| Term | Definition |
|---|---|
| Feature Importance | How much each input variable contributed to the model's predictive accuracy. Ranges 0โ1; all features sum to 1. |
| SHAP value | Shapley Additive exPlanation. For a specific creative, the exact contribution of each feature to the difference between its predicted score and the global average. Positive = pushed score up; negative = pulled it down. |
| PCA | Principal Component Analysis. Reduces the 36-feature matrix to 20 orthogonal components capturing 93% of variance. |
| K-Means cluster | One of 8 behavioural archetypes. Creatives in the same cluster share similar format, theme, tone, and performance profile. |
| Rยฒ | Coefficient of determination. Fraction of variance explained by the model. 1.0 = perfect; 0 = no better than the mean. Values above 0.6 are good for this data. |
| MAE | Mean Absolute Error. Average absolute difference between predicted and actual values. |
| Orthogonality score | Fraction of a creative's 10-PC feature vector that lies outside the current portfolio's PCA subspace. 100% = completely new creative territory. |
18 image features extracted per PNG asset using PIL:
- Per-channel (R, G, B) mean, std, skewness (9)
- Brightness and contrast (2)
- Saturation mean and std (2)
- Edge density x/y/average (3)
- Top/mid/bottom brightness zones (3, vertical composition proxy)
- HaslerโSรผsstrunk colorfulness index (1)
12 tabular features: CTR, CVR, ROAS, IPM, decay metrics, duration, text density, readability, brand visibility, clutter, novelty, motion scores.
5 label-encoded categoricals: format, vertical, theme, tone, OS.
4 binary flags: has_gameplay, has_ugc_style, has_price, has_discount_badge.
All features are median-imputed (SimpleImputer) and standardised (StandardScaler) before PCA/clustering/modelling.
| Purpose | Model | n |
|---|---|---|
| Global perf_score | RandomForestRegressor (200 trees, max_features='sqrt') | 1 080 |
| Global CTR / CVR / ROAS | RandomForestRegressor | 1 080 |
| CPA-campaign CVR | XGBRegressor (200 rounds, lr=0.05, depth=5) | ~270 |
| ROAS-campaign ROAS | XGBRegressor | ~312 |
| IPM-campaign IPM | XGBRegressor | ~252 |
| CTR-campaign CTR | XGBRegressor | ~246 |
All models evaluated with 5-fold cross-validation (KFold(n_splits=5, shuffle=True, random_state=42)). Rยฒ and MAE reported in the ML Insights tab.
shap.TreeExplainer computes exact Shapley values for every creative. When not installed, a simplified approximation (feature_importance ร standardised_feature_value) is used. Both feed the same structured JSON payload to Claude when the Explain button is clicked.
Smadex_Creative_Intelligence_Dataset_FULL/
โ
โโโ creative_copilot.html โ Main deliverable โ open in any browser
โโโ ml_pipeline_sklearn.py โ Regenerates ML outputs (run locally)
โโโ build_dashboard.py โ Patches ML outputs into the HTML
โโโ requirements.txt โ pip dependencies
โ
โโโ README.md โ This file
โโโ challenge_instructions.md โ Original challenge brief
โโโ data_dictionary.csv โ Column definitions for all CSVs
โ
โโโ advertisers.csv
โโโ campaigns.csv
โโโ creatives.csv
โโโ creative_summary.csv โ Main fact table used by the dashboard
โโโ creative_daily_country_os_stats.csv
โโโ campaign_summary.csv
โ
โโโ assets/ โ Synthetic PNG creative thumbnails
โโโ 500000.png
โโโ ...
Self-contained HTML โ zero server setup for demo. All data embedded as JS variables; 3 MB is comfortably within what modern browsers handle instantly.
PIL over CLIP โ CLIP requires PyTorch (~2 GB), impractical for a hackathon. The 18 PIL features capture the most interpretable visual signals and are fully explainable to a non-technical marketer.
RF for global + XGBoost for KPI subsets โ RF gives stable global feature importance on the full dataset; XGBoost with SHAP gives exact per-creative attribution on the smaller KPI-specific subsets where gradient boosting's bias-variance tradeoff is more favourable.
Gram-Schmidt for portfolio diversity โ the exact linear-algebra formulation for "what fraction of this vector lies outside the current subspace". Directly maps to "creative territory not yet covered" without heuristic approximation.
CTR normalisation in fatigue chart โ raw CTR values are not comparable across creatives with different baseline CTRs. Expressing daily CTR as a percentage of the first-7-day average puts all creatives on the same scale so decay curves are visually comparable.
- The dataset is fully synthetic. Patterns may not generalise to real ad inventory.
- Image features are heuristic (PIL-based), not semantic (no object detection or brand recognition).
- The prediction engine uses a cluster-match heuristic until
ml_pipeline_sklearn.pyis run locally to embed full PCA/scaler parameters. - The AI explain and chatbot features require a valid Anthropic API key.
- SHAP values are computed on training data; use CV Rยฒ for honest accuracy estimates.
Built for the Smadex Creative Intelligence Challenge. Dataset provided by Smadex โ fully synthetic, no real user data. Dashboard: Chart.js ยท AI: Anthropic Claude.