LDG-11: Time Series MIA - #368
Open
fazelehh wants to merge 34 commits into
Open
Conversation
- Add DTW and MSM signal classes for time series distance metrics
- Add msm.py utility for Move-Split-Merge distance calculation
- Add sample_shadow_indices() to AbstractInputHandler for custom sampling
- Add configurable sampling_method ('balanced'/'random') to shadow models
- Fix dataset params propagation using population.return_params()
- Add optimizer param filtering to prevent invalid argument errors
… into pr-c-core-module-changes
- Removed setting params to empty dict in get_dataloader() - Made better logic in get_dataset() such that if params is None we try to collect params from population. If we cant params is set to an empty dict to fit UserDataset().
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
…g, also added model_config = ConfigDict(extra='forbid') so exceptions are thrown if unkown params are initalized.
…fig rename The image_handler fixture constructs every attack in the audit config, and LiRA crashed on init: its config default 'ModelRescaledLogits' is the old signal class name, but lira.py now resolves signals from leakpro.signals.functional, which exposes 'rescaled_logits'. This blocked every MIA test. Default updated, legacy class names aliased so existing user configs keep working, and unknown names now fail with the list of available signals. Test fixtures had also gone stale: constants.py still set z_data_sample_fraction (renamed to attack_data_fraction), tests passed the 'attack' routing key straight into RMIA's now extra='forbid' config (stripped the same way AttackScheduler does), and test_lira asserted on target_logits where the attack sets target_model_logits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dual likelihood RMIA assumed classification: it indexed the softmax output with the label (softmax_logits(...)[arange(n), labels]), which crashes for forecasting models where labels are continuous horizons, and is conceptually undefined there. The RMIA paper scopes itself to classifiers and offers no guidance for continuous outputs. Pr(x|theta) is now task-dependent, resolved from the target's training criterion: classification keeps the temperature softmax at the true class (bit-identical to before); regression losses (MSE/L1/SmoothL1/Huber) use a Gaussian residual likelihood exp(-MSE(y, y_hat)/(2 sigma^2)). The normalizing constant is dropped deliberately: with sigma^2 shared across the target and all reference models it cancels in RMIA's ratios, and the surrogate stays in (0,1] so the offline marginal approximation 0.5*((1+a)*p_out + (1-a)) remains well-defined. sigma acts as the regression analogue of the softmax temperature: configurable via the new 'sigma' field, defaulting to a one-shot estimate from shadow-model residuals on the z-population that is reused for the audit points so the scale is consistent across all ratios. Calibration (FPR = 1-beta at gamma=1) is proven only for the classification likelihood; empirical validation for the Gaussian variant is tracked in LDG-22. End-to-end forecasting coverage needs a time-series test fixture (GitHub #365); the new tests cover the signal function, dispatch, and sigma resolution at the unit level. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e dead 'signal' config field The Gaussian residual likelihood added for forecasting/regression models treated every regression criterion as MSE: a model trained with L1 or Huber loss was scored under a mismatched noise model. Each criterion implies its own noise model, and the signal probability now uses the matching likelihood: MSE -> Gaussian exp(-MSE/(2*scale)), L1 -> Laplace exp(-MAE/scale), SmoothL1/Huber -> Huber density (Gaussian core, Laplace tails; SmoothL1(beta) maps to Huber delta=beta since the 1/beta factor is absorbed by the scale). Scale estimation is family-specific: mean squared residual (Gaussian MLE), mean absolute residual (Laplace MLE), mean Huber energy (heuristic, no closed-form MLE). A user-provided sigma keeps its 'residual std' meaning and is mapped per family (sigma^2, sigma/sqrt(2), quadratic-core variance). Also removes the AttackConfig 'signal' field: it was never read by the attack, so configs like the time-series example's 'signal: mse' validated and silently did nothing - a user believed they chose a signal while RMIA dispatched on the criterion regardless. With extra='forbid' it now fails loudly; the example audit.yaml is updated. Kernelized support for the time-series signal family (DTW, MSM, trend, ...) where likelihood semantics no longer hold is deliberately out of scope and tracked as a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ame config to `signals` Score cached logits with functional signals instead of re-querying every shadow model once per signal (the old class-based path re-ran each model N_signals times). prepare_attack now loads each model's logits once and applies all signals to the cached array. Fix the offline path to match arXiv:2509.04169 Eq. (2): the one-sided multivariate Gaussian tail under the OUT model (summed per-signal norm.logcdf), with each signal oriented so higher = more member-like via SIGNAL_MEMBERSHIP_DIRECTION. Previously the offline branch reused the online log-likelihood-ratio form with in_prs = 0, which is not the paper's offline score. Add a functional `loss` signal. Rename the attack config field `signal_names` -> `signals` (user-facing) and rename the internal resolved-callables list to `signal_fns` to avoid the collision. Add test coverage: test_multi_signal_lira.py (online/offline, single/multi-signal, no per-signal model re-query, undeclared-direction failure) and tests/signals/ test_functional.py. Update the time_series example audit.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI ran ruff over the whole file once this branch touched it, surfacing pre-existing violations in the time-series signal functions. Fix them: - Reformat the module docstring (D205/D208/D209) and fix typos. - Add missing public-function docstrings (D103) for logits/loss/mse/mae/ smape/rescaled_smape/seasonality/trend/ts2vec/dtw/msm. - Correct the `true_labels` arg name in rescaled_logits' docstring (D417). - Rename uppercase locals Y/Z/P to lowercase (N803/N806). - Strip trailing whitespace and add the final newline (W291/W292). - Sort imports (I001). No behavior change. Verified clean with ruff 0.11.2 (CI's pinned version) and 44 related tests still pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This branch changed ShadowModelConfig.init_params from `default=None` (main)
to `default_factory=dict`, which collapsed the distinction between a missing
init_params (inherit from the target model) and an explicitly empty one (stay
empty). ModelHandler relies on `None` meaning "missing" to inherit the target's
init_params; with `{}` it always took the empty branch.
Revert the default to None, matching main. Fixes
test_shadow_model_init_params_distinguish_missing_partial_and_empty.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The configurable-sampling work diverged create_shadow_models from main in two ways that broke CI for num_models=1 (the E2E smoke value): - It added an axis=0 assertion (each point used in exactly num_models//2 models) that main never had. Balanced in/out assignment can only satisfy this for even num_models; for odd counts (incl. 1) the construction tacks a half-load onto the last model, so it crashed. Restore main's check: only the per-model size invariant (axis=1), raised as a ValueError rather than a bare assert (which -O strips and which gives no message). - It dropped `batch_size=self.batch_size` from the shadow training dataloader, so shadow models trained at the default batch size instead of the configured one. Restore the argument to match main. Fixes test_all_attacks_end_to_end[loss_traj/seqmia] and test_shadow_model_creation_uses_shadow_batch_size. Whether balanced sampling should support odd num_models is left open (see follow-up task). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds time series MIA support: two new distance signals (DTW and MSM), configurable shadow model sampling, handler fixes, and a reworked multi-signal LiRA attack (functional signals + corrected offline scoring).
New signals
DTW): Dynamic Time Warping distance between model output and target series. Requires optionalsktimedependency — raises a clearImportErrorif not installed.MSM): Move-Split-Merge distance, implemented from scratch inleakpro/signals/utils/msm.pywith no external dependencies.SIGNAL_REGISTRYand usable viasignalsin attack configs (e.g.multi_signal_lira).MS-LiRA: functional signals + corrected offline scoring
multi_signal_liranow scores cached logits with functional signals instead of re-querying every shadow model once per signal. The old class-based path re-ran each modeln_signalstimes (issue Getting model logits is very slow #390);prepare_attacknow loads each model's logits exactly once (num_shadow_models + 1loads total) and applies every signal to the cached array.norm.logcdf), with each signal oriented so higher = more member-like viaSIGNAL_MEMBERSHIP_DIRECTION. Previously the offline branch reused the online log-likelihood-ratio form within_prs = 0, which is not the paper's offline score. For a single signal it now reduces exactly to offline LiRA (Carlini Eq. 4)._online_score(summed per-signal LLR, Eq. 1); both score paths are static helpers and unit-tested.losssignal.Config rename
multi_signal_liraconfig fieldsignal_namesis renamed tosignals(user-facing). The internal resolved-callables list issignal_fns. Class-style names (ModelRescaledLogits) and functional names (rescaled_logits) are both accepted for back-compat.Configurable shadow model sampling
sampling_methodfield toShadowModelConfig(default:"balanced").create_shadow_models()now acceptssampling_method="balanced"(existing behavior) or"random"(delegates to handler for custom sampling logic).sample_shadow_indices()toAbstractInputHandleras an overrideable method — time series handlers (e.g.IndividualizedInputHandler) can override it to sample by individual rather than by record.Handler fixes
MIAHandler.get_dataset(): defaultparamsnow usespopulation.return_params()instead of{}, preserving dataset-specific settings (e.g. scaler, lookback) when recreating datasets for shadow model training.MIAHandler.get_optimizer(): usesinspect.signature()to filter out invalid optimizer kwargs before instantiation, preventing crashes when time series configs pass extra parameters.Bug fix
DotMapcompatibility bug inshadow_model_handler.pywheresampling_methodwould be read as an emptyDotMap()instead of defaulting to"balanced"when the key was absent from the config.Dependencies
sktimetomiaoptional deps anddevgroup inpyproject.toml.Tests
leakpro/tests/mia_attacks/utils/test_time_series_signals.py(18 tests): MSM cost/distance units, end-to-end MSM signal, DTWImportErrorwithout sktime, registry presence, shadow sampling,get_optimizer()kwarg filtering.leakpro/tests/mia_attacks/attacks/test_multi_signal_lira.py(new): online/offline, single- and multi-signal runs, signals resolve to functional functions, no per-signal model re-query (load_logits called exactlynum_shadow_models + 1times), undeclared-direction failure, offline/online scoring formulas.leakpro/tests/signals/test_functional.py(new): functional signal unit tests.Test plan
examples/mia/time_series_mia) withmulti_signal_liraover the full signal setbalancedsampling