Shadow models are trained with handler.get_criterion(), never checked against the target's recorded criterion
- Dominant language
- Python
- Stars
- 23
- Forks
- 28
- Avg merge
- 4d 12h
- Merged PRs (30d)
- 5
Description
## Problem
Shadow models are trained with the criterion returned by `handler.get_criterion()`, while the criterion the target was actually trained with is recorded separately in `target_model_metadata`. Nothing checks that the two agree, so a user whose `get_criterion()` does not match how they trained their target gets silently invalid results.
When no `shadow_model` block is given in `audit.yaml` (the schema default is `None`), `use_target_model_setup` is `True` and the chain is:
```
ShadowModelHandler.create_shadow_models
-> ModelHandler._get_model_criterion_optimizer() # model_handler.py:225
-> MIAHandler.get_target_replica() # mia_handler.py:318
-> handler.get_criterion() # user code
```
## Consequences of a mismatch
1. **Shadow models do not replicate the target's training.** They are trained with the handler's loss, not the target's. This invalidates the core assumption behind every shadow-model attack.
2. **Saved metadata misreports the criterion.** `shadow_model_handler.py:329` writes `criterion=self.criterion_name`, which `_load_model_setup` took from `target_setup.criterion.name` (`model_handler.py:92`). The shadow models are therefore recorded as having been trained with the *target's* criterion regardless of what was actually used, so the metadata hides the problem rather than revealing it.
3. **RMIA picks the wrong residual likelihood.** `rmia.py:121` infers the likelihood family from `handler.get_criterion()` (MSE to Gaussian, L1 to Laplace, SmoothL1/Huber to Huber). A wrong criterion means the regression target is scored under the wrong noise model.
## Concrete instance
`examples/mia/time_series_mia/data_handler.py` returned `MSELoss()` unconditionally, while `train_config.yaml` sets `loss: MAE` which `model_preparation.py` maps to `nn.L1Loss()`. The saved target metadata records `criterion: name='l1loss'`.
So that example trained its target with L1, trained its shadow models with MSE, recorded them all as `l1loss`, and audited with a Gaussian likelihood. None of it surfaced as an error. Fixed for that example in #368; the underlying gap is not addressed.
## Suggested fix
At handler or attack setup, compare the criterion the handler reports against the one recorded for the target and fail loudly on mismatch:
```python
reported = type(handler.get_criterion()).__name__.lower()
recorded = handler.target_model_metadata.criterion.name.lower()
if reported != recorded:
raise ValueError(...)
```
Worth considering as well: build shadow-model replicas from the *recorded* target criterion rather than from `handler.get_criterion()`, which removes the opportunity for the two to disagree instead of only reporting it.
## Notes
Found while reviewing PR #368. Related to the criterion-matched residual likelihood in RMIA, which is correct in itself but can only be as accurate as what `get_criterion()` reports.
Contributor guide
Assessment
This issue has not been assessed yet.