File size: 13,834 Bytes
a13f796 a6ef619 a13f796 a6ef619 a13f796 a6ef619 a13f796 a6ef619 a13f796 8935b3e a13f796 a6ef619 a13f796 a6ef619 a13f796 a6ef619 61e88da a13f796 8935b3e a13f796 8935b3e a13f796 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | ---
license: cc-by-nc-4.0
language: en
library_name: xgboost
tags:
- sports-analytics
- soccer
- football
- vaep
- action-valuation
- xgboost
- statsbomb
- wyscout
- idsse
- metrica
- skillcorner
- silly-kicks
datasets:
- luxury-lakehouse/spadl-vaep-action-values
- luxury-lakehouse/xg-shot-data
- luxury-lakehouse/football2vec-player-embeddings
pipeline_tag: tabular-classification
---
# VAEP Model
Two XGBClassifier models that estimate **P(scoring)** and **P(conceding)** within the next 10 actions, enabling per-action valuation of every on-ball event in a soccer match. Trained on **~5,414 matches** across five open providers — [StatsBomb Open Data](https://github.com/statsbomb/open-data), [Wyscout](https://figshare.com/collections/Soccer_match_event_dataset/4415000), IDSSE (DFL Bundesliga), Metrica Sports, and SkillCorner — via [Hugging Face Jobs](https://huggingface.co/docs/hub/jobs) (CPU). Own goals are valued (≈ −1) as of silly-kicks 4.13.
Part of the (Right! Luxury!) Lakehouse soccer analytics platform.
## Model Description
**VAEP** (Valuing Actions by Estimating Probabilities) scores each on-ball action by its impact on the probability of scoring and conceding within the next 10 actions, as described in:
> Decroos, T., Bransen, L., Van Haaren, J., & Davis, J. (2019). **Actions Speak Louder than Goals: Valuing Player Actions in Soccer.** *Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining.*
This model implements the VAEP framework using two independent XGBClassifier models:
- **P(scores)**: Probability that the team in possession scores within the next 10 actions
- **P(concedes)**: Probability that the team in possession concedes within the next 10 actions
The net VAEP value of an action is the change in scoring probability minus the change in conceding probability before and after that action:
```
VAEP(action_i) = [P_scores(S_i) - P_scores(S_{i-1})] + [P_concedes(S_{i-1}) - P_concedes(S_i)]
```
where `S_i` is the game state after action `i`.
### Architecture
Both models are XGBClassifier instances with identical hyperparameters:
| Parameter | Value |
|-----------|-------|
| `n_estimators` | 100 |
| `max_depth` | 3 |
| `learning_rate` | 0.1 |
| `objective` | `binary:logistic` |
| `eval_metric` | `logloss` |
| `random_state` | 42 |
### Feature Extraction
Features are extracted using the [silly-kicks](https://github.com/karsten-s-nielsen/silly-kicks) library with 11 feature functions applied to game states composed of the current action and the previous `NB_PREV_ACTIONS = 3` actions:
| Feature Function | Description |
|-----------------|-------------|
| `actiontype_onehot` | One-hot encoding of the 23 SPADL action types |
| `result_onehot` | One-hot encoding of action outcomes (success, fail, etc.) |
| `bodypart_onehot` | One-hot encoding of body part (foot, head, other) |
| `time` | Period and time within the period |
| `startlocation` | Start x, y coordinates (SPADL 105×68m) |
| `endlocation` | End x, y coordinates |
| `startpolar` | Start location in polar coordinates (distance + angle to goal) |
| `endpolar` | End location in polar coordinates |
| `movement` | Displacement between start and end locations |
| `team` | Whether the team changed between consecutive actions |
| `time_delta` | Time elapsed between consecutive actions |
With `NB_PREV_ACTIONS = 3`, each feature function generates columns for the current action plus the 3 preceding actions, creating a rich game state representation.
### Serialization
Both models are serialized in a single **JSON envelope** — no pickle is used (banned by project security policy):
- Each model's XGBoost booster is saved via `save_raw("json")` and base64-encoded
- The envelope includes the number of input features and `nb_prev_actions` for reproducibility
```json
{
"model_type": "vaep_xgboost_v1",
"scores_booster_b64": "...",
"concedes_booster_b64": "...",
"n_features": 264,
"nb_prev_actions": 3
}
```
This makes model weights fully inspectable, version-controllable, and safe to load without arbitrary code execution.
## Training Data
| Source | Matches | License |
|--------|---------|---------|
| [StatsBomb Open Data](https://github.com/statsbomb/open-data) | ~3,000 | CC-BY 4.0 |
| [Wyscout Public Dataset](https://figshare.com/collections/Soccer_match_event_dataset/4415000) | ~1,900 | CC-BY-NC 4.0 |
| **Total** | **~2,388** (deduplicated) | CC-BY-NC 4.0 (most restrictive applies) |
Coverage includes the Premier League, La Liga, Serie A, Bundesliga, Ligue 1, Champions League, World Cup, and more.
All event data is converted to the **SPADL** (Soccer Player Action Description Language) unified format with standardized coordinates (105×68 meters) and 23 canonical action types, enabling cross-source training without vendor-specific adapters.
Training is performed on [Hugging Face Jobs](https://huggingface.co/docs/hub/jobs) using the `cpu-basic` flavor.
### Training / Test Split
- **80/20 split** by game, stratified by `competition_id`
- Evaluation metrics computed on the held-out test set
## How to Use
### Quick Start
```bash
pip install huggingface_hub xgboost
```
```python
import json
import base64
from huggingface_hub import snapshot_download
from xgboost import XGBClassifier
# Download model
model_dir = snapshot_download("luxury-lakehouse/vaep-model")
# Load from JSON envelope
with open(f"{model_dir}/vaep_model.json") as f:
envelope = json.load(f)
# Deserialize P(scores) model
model_scores = XGBClassifier()
scores_raw = base64.b64decode(envelope["scores_booster_b64"])
model_scores.load_model(bytearray(scores_raw))
# Deserialize P(concedes) model
model_concedes = XGBClassifier()
concedes_raw = base64.b64decode(envelope["concedes_booster_b64"])
model_concedes.load_model(bytearray(concedes_raw))
# Predict probabilities (requires silly-kicks feature extraction)
# p_scores = model_scores.predict_proba(X)[:, 1]
# p_concedes = model_concedes.predict_proba(X)[:, 1]
```
### Full Pipeline (with silly-kicks)
```python
import silly_kicks.spadl as spadl
import silly_kicks.vaep.features as fs
import silly_kicks.vaep.labels as labels
NB_PREV_ACTIONS = 3
FEATURE_FNS = [
fs.actiontype_onehot, fs.result_onehot, fs.bodypart_onehot,
fs.time, fs.startlocation, fs.endlocation,
fs.startpolar, fs.endpolar, fs.movement, fs.team, fs.time_delta,
]
# actions: pandas DataFrame in SPADL format
gamestates = fs.gamestates(actions, nb_prev_actions=NB_PREV_ACTIONS)
X = pd.concat([fn(gamestates) for fn in FEATURE_FNS], axis=1)
p_scores = model_scores.predict_proba(X)[:, 1]
p_concedes = model_concedes.predict_proba(X)[:, 1]
# Compute VAEP values (change in probabilities)
vaep_offensive = p_scores[1:] - p_scores[:-1] # delta P(scores)
vaep_defensive = p_concedes[:-1] - p_concedes[1:] # delta P(concedes), note sign flip
vaep_value = vaep_offensive + vaep_defensive
```
## Intended Use
- **Player valuation**: Rank players by total VAEP contribution beyond goals and assists
- **Action analysis**: Identify the most impactful passes, carries, and defensive actions in a match
- **Tactical analysis**: Evaluate team playing styles by aggregating VAEP across action types
- **Scouting**: Compare players across leagues using a unified valuation framework
- **Research**: Reproducible VAEP implementation on open data
## Three-Axis Interpretation
VAEP's two probability deltas map naturally to three cognitive axes for interpretation, borrowing framing from García de Marina's xR framework (SOCCHUB 2026):
- **Survival** — the defensive component (`vaep_defensive`). How much did the action reduce the opponent's scoring probability? Positive = safer (opponent less likely to score). Negative = riskier (opponent more likely to score). Computed as `P(concedes)_before - P(concedes)_after` — the sign flip in the `vaep_defensive` computation ensures positive = good.
- **Progression** — the offensive component (`vaep_offensive`). How much did the action advance the team's own scoring probability? Positive = more threatening. Computed as `P(scores)_after - P(scores)_before`.
- **Decision Value** — the composite VAEP score (`vaep_value = vaep_offensive + vaep_defensive`). Was the implicit risk worth it? Positive = the action's Progression gain exceeded its Survival cost. Negative = the risk outweighed the benefit.
This is a labeling convention, not a new model. The underlying probabilities and computation are unchanged. The xR model itself (which introduces additional axes like xR-score) is not implemented here.
## EU AI Act — Intended Use and Non-Use
This model is published for **research and reproducibility** purposes on public, open-licensed match data. It is **not intended for, not validated for, and not supplied to** any use that would fall within Annex III §4 (Employment, workers management and access to self-employment) of Regulation (EU) 2024/1689 — including recruitment or selection of natural persons, decisions affecting work-related contractual relationships, promotion, termination, task allocation based on individual traits, or the monitoring and evaluation of performance and behaviour of workers for employment decisions.
Any deployer who wishes to use this model for such a purpose is responsible for performing their own conformity assessment under Article 43, for drawing up the technical documentation required by Article 11 and Annex IV, for implementing the human oversight measures required by Article 14, for declaring accuracy metrics under Article 15, and for ensuring the data governance obligations of Article 10 are met. Note specifically that the training data contains no protected attributes and therefore cannot support the group-fairness audits required by Article 10(2)(g) without ingesting additional personal data.
See the [`AI_GOVERNANCE.md`](https://github.com/karsten-s-nielsen/luxury-lakehouse/blob/main/AI_GOVERNANCE.md) gap analysis in the source repository for the project's full risk classification, re-classification triggers, and governance posture.
## Limitations
- **Open data only**: Trained on publicly available open data (StatsBomb, Wyscout, IDSSE/DFL, Metrica, SkillCorner). Commercial datasets with richer event annotations may yield different VAEP scores.
- **No tracking data**: VAEP is event-based. Off-ball positioning, pressing intensity, and space creation are not captured. See [OBSO](https://github.com/karsten-s-nielsen/luxury-lakehouse) for tracking-based approaches.
- **Competition-agnostic**: The models are trained across all competitions jointly. League-specific models may produce more calibrated probabilities for individual leagues.
- **Cross-source alignment**: The five providers use different event taxonomies. The SPADL adapter normalizes them, but subtle differences in event definitions (e.g., duel classification) remain.
- **No calibration**: Unlike the xG models, the VAEP classifiers do not include post-hoc isotonic calibration. Predicted probabilities may not be perfectly calibrated in absolute terms. For ranking players or actions, this is less consequential; for applications requiring absolute probability values, validate with a reliability diagram.
- **10-action horizon**: VAEP considers only the next 10 actions. Longer-range effects (e.g., a switch of play that leads to a goal 20 actions later) are not captured.
## Model Files
```
vaep_model.json -- scores + concedes XGBoost boosters (JSON envelope, no pickle)
metrics.json -- evaluation metrics and training configuration
```
## Citation
If you use this model, please cite the original VAEP paper:
```bibtex
@inproceedings{decroos2019actions,
title={Actions Speak Louder than Goals: Valuing Player Actions in Soccer},
author={Decroos, Tom and Bransen, Lotte and Van Haaren, Jan and Davis, Jesse},
booktitle={Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining},
pages={1851--1861},
year={2019},
publisher={ACM}
}
```
And the silly-kicks library:
```bibtex
@article{silly-kicks,
title={silly-kicks: A Python library for valuing soccer actions},
author={Decroos, Tom and Van Haaren, Jan and Davis, Jesse},
year={2020},
url={https://github.com/karsten-s-nielsen/silly-kicks}
}
```
```bibtex
@software{nielsen2026vaep,
title={VAEP Model: Multi-Provider Action Valuation on Open Soccer Data},
author={Nielsen, Karsten Skyt},
year={2026},
url={https://github.com/karsten-s-nielsen/luxury-lakehouse}
}
```
## Companion Resources
| Dataset | Description |
|---------|-------------|
| [SPADL/VAEP Action Values](https://huggingface.co/datasets/luxury-lakehouse/spadl-vaep-action-values) | Pre-computed per-action VAEP valuations (~9.5M actions) |
| [xG Shot Data](https://huggingface.co/datasets/luxury-lakehouse/xg-shot-data) | Tabular shot features for xG training |
| [Player Embeddings](https://huggingface.co/datasets/luxury-lakehouse/football2vec-player-embeddings) | Pre-computed behavioral + statistical vectors |
## Demo
Try the interactive [Soccer Analytics App](https://huggingface.co/spaces/luxury-lakehouse/soccer-analytics-app) — explore player impact rankings powered by VAEP valuations, and compare players across leagues.
> **Explore interactively:** [Soccer Analytics App](https://huggingface.co/spaces/luxury-lakehouse/soccer-analytics-app)
## More Information
- **License**: [CC-BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) (inherited from Wyscout training data)
- **Training script**: `scripts/train_vaep_model_hf.py` (PEP 723 standalone)
- **Source module**: `src/ingestion/spadl_vaep.py`
- **Platform**: [Luxury Lakehouse Soccer Analytics](https://github.com/karsten-s-nielsen/luxury-lakehouse)
|