|
1 | 1 | # xplainfi 1.1.0.9000 (development version) |
2 | 2 |
|
3 | | -## Performance and new features |
4 | | - |
5 | | -- New `samples_per_row` argument on `FeatureSampler$sample()` and `$sample_newdata()` (default `1L`). |
6 | | - - When `> 1`, samplers return `samples_per_row * length(row_ids)` rows in draw-major order |
7 | | - (one block of all input rows per draw). |
8 | | - - Default behaviour is unchanged. |
9 | | -- `PerturbationImportance` (`PFI`/`CFI`/`RFI`) now passes `samples_per_row = n_repeats` to the |
10 | | - sampler on unique test row IDs, instead of replicating row IDs externally. |
11 | | - - For `ConditionalARFSampler` this means `arf::forge(n_synth = n_repeats)` on `n_test` |
12 | | - unique evidence rows rather than `n_synth = 1` on `n_repeats * n_test` replicated rows. |
13 | | - - Dramatically faster at scale and dodges an int32 overflow inside `arf::forge` for large |
14 | | - `n_test * n_repeats`. |
15 | | - - All other in-package samplers implement `samples_per_row` natively as well. |
16 | | -- `PerturbationImportance` (PFI/CFI/RFI) now registers a `doParallel` backend inside each |
17 | | - mirai daemon when the sampler is configured with `parallel = TRUE`. |
18 | | - - This lets `ConditionalARFSampler` use parallel `arf::forge()` from within mirai workers, |
19 | | - which don't inherit the caller's foreach state. |
20 | | - - Worker count per daemon is controlled by the new `arf_workers` option (default `2L`); |
21 | | - see `?xplain_opt`. |
22 | | - - Requires the `doParallel` package (now in Suggests). |
23 | | -- `WVIM` and `LOCO` gain a `batch_size` constructor argument (default `NULL`). |
24 | | - - Controls how many refits (`design_points`) are dispatched per internal `mlr3::benchmark()` |
25 | | - call by the `mlr3fselect::fs("design_points")` fselector. |
26 | | - - `NULL` keeps the prior behaviour (bbotk default of one design point per call, i.e. |
27 | | - sequential single-refit evaluation). |
28 | | - - Set a positive integer (e.g. the number of `future`/`mirai` workers) to batch refits so |
29 | | - mlr3's parallelization can spread them across workers; previously refits were dispatched |
30 | | - one at a time, so `future`/`mirai` backends sat idle during LOCO/WVIM computation. |
31 | | - - Post-benchmark aggregation keys on the per-design-point resample-result `uhash`, so |
32 | | - scores/obs-losses are attributed correctly for any `batch_size`. |
33 | | -- `ConditionalSAGE` now passes `samples_per_row = n_samples` to the sampler on unique |
34 | | - test rows, instead of replicating test rows externally before calling the sampler. |
35 | | - - For `ConditionalARFSampler` this triggers the same `arf::forge(n_synth = n_samples)` |
36 | | - speedup as `PerturbationImportance`, on `n_test` unique evidence rows rather than |
37 | | - `n_samples * n_test` replicated rows. |
38 | | - - Marginal SAGE (`MarginalSAGE`) does not use a `FeatureSampler` and is unaffected. |
| 3 | +## Behavior changes |
| 4 | + |
| 5 | +- `WVIM` and `LOCO` now default to `n_repeats = 1`, and the argument may be removed for these refit-based methods in the future (#84). |
| 6 | + - For refit-based methods, computational budget is better spent on more resampling iterations (which stabilize the FI estimate) than on repeated refits within a single resampling iteration. |
| 7 | + |
| 8 | +## New features |
| 9 | + |
| 10 | +- `FeatureSampler$sample()` and `$sample_newdata()` gain a `samples_per_row` argument (default `1L`). |
| 11 | + - With `samples_per_row > 1` the sampler returns `samples_per_row * length(row_ids)` rows in draw-major order (one block of all input rows per draw), default behaviour is unchanged. |
| 12 | + - All in-package samplers implement `samples_per_row` natively. |
| 13 | +- `WVIM` and `LOCO` gain a `batch_size` constructor argument (default `NULL`) to parallelize refits. |
| 14 | + - It controls how many refits (`design_points`) are dispatched per internal `mlr3::benchmark()` call via the `mlr3fselect::fs("design_points")` fselector. |
| 15 | + - `NULL` keeps the prior sequential single-refit behaviour (bbotk default of one design point per call). Set a positive integer (e.g. the number of `future`/`mirai` workers) so mlr3 can spread refits across workers. Previously they were dispatched one at a time and backends sat idle during LOCO/WVIM computation. |
| 16 | + - Post-benchmark aggregation keys on the per-design-point resample-result `uhash`, so scores/obs-losses are attributed correctly for any `batch_size`. |
39 | 17 |
|
40 | | -## Other user-facing changes |
| 18 | +## Performance |
41 | 19 |
|
42 | | -- `WVIM` and `LOCO` now default to `n_repeats = 1`, and the argument may be removed for these refit methods in the future. |
43 | | - - Reasoning: For refit-based methods, it is much more useful to increase the the number of resampling iterations to increase stability of the resulting FI estimate rather than trying to stabilize stochastic learners by repeatedly refitting within the same resampling iteration. If computation budget is spent on refits, it should be spent there. |
| 20 | +- `PerturbationImportance` (`PFI`/`CFI`/`RFI`) and `ConditionalSAGE` now pass `samples_per_row = n_repeats` / `n_samples` to the sampler on unique test rows instead of externally replicating row IDs. |
| 21 | + - For `ConditionalARFSampler` this calls `arf::forge(n_synth = ...)` on `n_test` unique evidence rows rather than `n_synth = 1` on replicated rows: much faster at scale, and it dodges an int32 overflow inside `arf::forge` for large `n_test * n_repeats`. |
| 22 | + - Marginal SAGE (`MarginalSAGE`) does not use a `FeatureSampler` and is unaffected. |
| 23 | +- `PerturbationImportance` now forges sequentially inside each mirai daemon, since the outer mirai layer already parallelizes across features; a `ConditionalARFSampler` with `parallel = TRUE` no longer nests `arf::forge()` parallelism inside workers (which would oversubscribe cores). The sampler's `parallel` flag still drives fit-time parallelism (`adversarial_rf`/`forde`) as before. |
44 | 24 |
|
45 | 25 | ## Bug fixes |
46 | 26 |
|
47 | | -- `relation = "ratio"` importances now return `NA` (with a warning) instead of |
48 | | - `Inf`/`NaN` for features whose baseline score is `0`. |
49 | | -- Fix `$obs_loss()` being erroneously called without `measure` in `PerturbationImportance`, |
50 | | - resulting in an error when `measures` was not the task-default. |
51 | | -- `ConditionalARFSampler$sample()` now errors when `parallel = TRUE` but no parallel backend |
52 | | - is registered, e.g. after deserializing a sampler in a new session. |
53 | | -- Fix `ConditionalARFSampler` returning under-sampled (and, with `samples_per_row > 1`, |
54 | | - partially `NA`-filled) outputs when `conditioning_set` is empty. |
55 | | - - `arf::forge(evidence = NULL, n_synth = k)` returns only `k` unconditional draws, |
56 | | - not `nrow(data) * k`; the previous code path silently recycled a single sample |
57 | | - across all output rows when `samples_per_row = 1L`. |
58 | | - - The marginal case now requests `nrow(data) * samples_per_row` independent |
59 | | - unconditional draws so every (instance, draw) pair gets its own sample. |
60 | | - - In practice this only affects `ConditionalSAGE` (which calls the sampler with an |
61 | | - empty conditioning set on every empty coalition); CFI/RFI/PFI condition on the |
62 | | - complement of the perturbed feature and never trigger the empty-conditioning path. |
63 | | - - As a side effect, `ConditionalSAGE` importance estimates for noise features are |
64 | | - now substantially less inflated under the same `n_permutations` / `n_samples` |
65 | | - budget. |
| 27 | +- `relation = "ratio"` importances now return `NA` (with a warning) instead of `Inf`/`NaN` for features whose baseline score is `0`. |
| 28 | +- `PerturbationImportance` no longer calls `$obs_loss()` without `measure`, which errored when `measures` was not the task default. |
| 29 | +- `ConditionalARFSampler$sample()` now errors when `parallel = TRUE` but no parallel backend is registered, e.g. after deserializing a sampler in a new session. |
| 30 | +- `ConditionalARFSampler` no longer returns under-sampled (and, with `samples_per_row > 1`, partially `NA`-filled) output when `conditioning_set` is empty. |
| 31 | + - `arf::forge(evidence = NULL, n_synth = k)` returns only `k` unconditional draws, not `nrow(data) * k`; the previous code path silently recycled a single sample across all output rows when `samples_per_row = 1L`. The marginal case now requests `nrow(data) * samples_per_row` independent draws so every (instance, draw) pair gets its own sample. |
| 32 | + - In practice this only affected `ConditionalSAGE` (empty conditioning set on every empty coalition); CFI/RFI/PFI condition on the complement of the perturbed feature and never trigger the empty-conditioning path. As a side effect, `ConditionalSAGE` importance estimates for noise features are now substantially less inflated under the same `n_permutations` / `n_samples` budget. |
66 | 33 |
|
67 | 34 | ## Internal changes |
68 | 35 |
|
69 | | -- The `KnockoffSampler` `n_repeats > iters` cap-and-warn behaviour moved from |
70 | | - `PerturbationImportance$initialize()` into `KnockoffSampler$sample()` itself; |
71 | | - behaviour is unchanged. |
| 36 | +- The `KnockoffSampler` `n_repeats > iters` cap-and-warn behaviour moved from `PerturbationImportance$initialize()` into `KnockoffSampler$sample()` itself; behaviour is unchanged. |
72 | 37 |
|
73 | 38 | # xplainfi 1.1.0 |
74 | 39 |
|
|
0 commit comments