|
| 1 | +# How to choose a featurization with a VAMP-2 score |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Decide *which* features to build your MSM on - and how many TICA dimensions and what TICA lag to keep - objectively rather than by intuition. The cross-validated VAMP-2 score ({py:func}`~htmd.projections.vamp.vampScore`) rates how much of the slow dynamics a featurization resolves; you score your candidates and keep the best one. This is the variational feature-selection idea of McGibbon and Pande. |
| 6 | + |
| 7 | +## Minimal example |
| 8 | + |
| 9 | +Starting from your `sims` (a {py:func}`~htmd.simlist.simlist`), score two candidate featurizations of the same trajectories and compare. Higher is better: |
| 10 | + |
| 11 | +```python |
| 12 | +from htmd.ui import Metric, MetricSelfDistance, MetricDihedral, vampScore |
| 13 | + |
| 14 | +# Candidate A: Ca-Ca contact map |
| 15 | +metrA = Metric(sims) |
| 16 | +metrA.set(MetricSelfDistance("protein and name CA", metric="contacts", periodic="chains")) |
| 17 | +dataA = metrA.project() |
| 18 | +dataA.fstep = 0.1 |
| 19 | + |
| 20 | +# Candidate B: backbone dihedrals |
| 21 | +metrB = Metric(sims) |
| 22 | +metrB.set(MetricDihedral()) |
| 23 | +dataB = metrB.project() |
| 24 | +dataB.fstep = 0.1 |
| 25 | + |
| 26 | +for label, data in [("contacts", dataA), ("dihedrals", dataB)]: |
| 27 | + mean, std = vampScore(data, lag=2, dim=4, units="ns") |
| 28 | + print(f"{label}: VAMP-2 = {mean:.2f} +/- {std:.2f}") |
| 29 | +``` |
| 30 | + |
| 31 | +Keep the featurization with the highest cross-validated score. |
| 32 | + |
| 33 | +## How to read the score |
| 34 | + |
| 35 | +- **Higher is better** - a higher VAMP-2 score means the features resolve more of the slow dynamics. |
| 36 | +- **About 1.0 is the floor** - a featurization that captures no slow process (pure noise) scores around 1.0, the trivial stationary process. |
| 37 | +- **Compare like with like** - scores are only comparable when computed at the **same `lag` and `dim`**. Changing either changes the absolute score. |
| 38 | +- **It is cross-validated** - the score is averaged over `nfolds` folds, so a featurization that looks good only by overfitting the training data is penalised on the held-out folds. Use the mean and watch the spread (`std`). |
| 39 | + |
| 40 | +## Common variations |
| 41 | + |
| 42 | +Choose how many dimensions to keep. The score keeps rising as you add dimensions (it sums the squared singular values), so look for **diminishing returns** rather than a maximum: |
| 43 | + |
| 44 | +```python |
| 45 | +for d in (2, 3, 4, 6, 8): |
| 46 | + mean, std = vampScore(data, lag=2, dim=d, units="ns") |
| 47 | + print(f"dim={d}: VAMP-2 = {mean:.2f} +/- {std:.2f}") |
| 48 | +``` |
| 49 | + |
| 50 | +Check robustness to the TICA lag: |
| 51 | + |
| 52 | +```python |
| 53 | +for lag in (0.5, 1, 2, 5): |
| 54 | + mean, std = vampScore(data, lag=lag, dim=4, units="ns") |
| 55 | + print(f"lag={lag} ns: VAMP-2 = {mean:.2f} +/- {std:.2f}") |
| 56 | +``` |
| 57 | + |
| 58 | +Get the per-fold scores instead of the mean and standard deviation (for example to plot your own error bars): |
| 59 | + |
| 60 | +```python |
| 61 | +scores = vampScore(data, lag=2, dim=4, units="ns", return_scores=True) |
| 62 | +``` |
| 63 | + |
| 64 | +If you have only a few long trajectories, cross-validating over whole trajectories is not possible; split each trajectory into blocks of `blocksize` frames instead: |
| 65 | + |
| 66 | +```python |
| 67 | +mean, std = vampScore(data, lag=2, dim=4, units="ns", blocksize=1000) |
| 68 | +``` |
| 69 | + |
| 70 | +## Gotchas |
| 71 | + |
| 72 | +- **Only compare at matched `lag` and `dim`.** A featurization scored at `dim=6` will out-score one at `dim=3` purely because of the extra dimensions, not because it is better. |
| 73 | +- **Do not simply maximise over `dim`.** Because VAMP-2 sums squared singular values, it rises monotonically with `dim`. Read the *shape* of the curve (where the gains flatten), not the peak. |
| 74 | +- **A tie is a real and useful answer.** Featurizations that both capture the slow process well score about the same - the score is telling you the choice does not matter for the kinetics, so decide on other grounds such as clustering robustness or cost. For example, on the villin headpiece a Cα contact map and raw Cα distances score about equally; the contact map is preferred there for its clustering robustness, not because it resolves more dynamics. |
| 75 | +- **Needs at least 2 trajectories** for the default whole-trajectory cross-validation. With a single long trajectory, pass `blocksize` so it can be split into independent blocks. |
| 76 | +- **Scoring is not validation.** A high VAMP-2 score says the features are promising; it does not replace building the MSM and checking the implied timescales and the Chapman-Kolmogorov test. |
| 77 | + |
| 78 | +## See also |
| 79 | + |
| 80 | +- {py:func}`htmd.projections.vamp.vampScore` - the API reference. |
| 81 | +- {doc}`How to read an implied-timescales plot <msm-interpret-its-plot>` - the validation step after picking features. |
| 82 | +- {doc}`Protein folding MSM <../tutorials/analysis/villin-folding>` and {doc}`Ligand binding MSM <../tutorials/analysis/trypsin-benzamidine-binding>` - the full pipelines these features feed into. |
0 commit comments