experiment: validate beta selection across three seeds

This commit is contained in:
willtanoe committed 2026-09-25 13:14:28 +00:00
1 parent 50f2da7216
commit 6b97a0d38b
14 files changed
+1963 -150

No files matched your search

+230 -143
View File
@@ -3,11 +3,25 @@
This is the canonical continuation point for the next agent session. It is written to be
readable without prior chat history, terminal scrollback, or git history.
Last updated: 2026-09-25 (Session 5, one paired full beta condition + validation-only
selection). Session 5 type: **protocol-validation experiment**. Exactly one
`(dataset, alpha, seed)` condition was run across all five beta candidates; beta was
selected on validation only and the selected beta evaluated once on test. **No full grid
was launched.**
Last updated: 2026-09-25 (Session 6 + post-experiment audit). Session 6 type:
**protocol-scaling / stability experiment**. The single Session-5 condition was extended
from seed 0 to seeds `{0,1,2}`; beta was selected by the **pooled-across-seed validation**
rule and the selected beta evaluated on test. **No full grid was launched; no
alpha/dataset expansion was run.** A strict independent audit
(`docs/research/SESSION6_AUDIT.md`) returned **PASS WITH MINOR CORRECTIONS**; `beta_star =
0.75` was independently reproduced from validation artifacts.
> **Audit summary (Session 6).** Verdict: **PASS WITH MINOR CORRECTIONS**. Independently
> recomputed `beta_star = 0.75` from `val_per_client.csv`/`val_selection.json`; all 15
> seed×beta seed-level scores and the pooled mean/sample-std match the report exactly.
> Validation-only selection confirmed in code (`analyze_multiseed_beta.py` reads only
> `val_selection.json`; test read in a later block); `test_used_for_selection=false`
> upheld. Rare-class flags match the training-derived definition for all 30 cells;
> manifests reproduce from raw data with disjoint splits. beta=0 vs `original_protean` =
> semantically equivalent + floating-point divergence (class B). Minor corrections:
> persisted `artifacts/analysis/xiiotid__alpha0.5__multiseed__runtime.csv` (runtime
> provenance) and clarified the client improve/degrade counts (unchanged/tie clients).
> **Session 6 is safe to checkpoint.**
---
@@ -31,188 +45,253 @@ bitwise identical to the baseline (see §7).
## 2. Authoritative source and immutability
- `planningawal/PROTEAN_Revisi_Lurus.md` — authoritative research specification.
- `planningawal/` is immutable. Re-verified unchanged this session: `git status --
planningawal` and `git diff -- planningawal` are both empty.
- `planningawal/` is immutable. Re-verified unchanged this session: `git status --short
planningawal/` and `git diff -- planningawal/` are both empty.
- Raw datasets re-verified unchanged (SHA-256):
`~/datasets/X-IIoTID/X-IIoTID-dataset.csv`
`7b9290057ee42e784da3c0d84b781815502c9205c74175c96374e71a5ffd98a0`;
`~/datasets/5G-NIDD/5G-NIDD.csv`
`fa36f80859585f474504ca69eb951a079b35145537976c86b00aae3aab46ee59`.
- Frozen baseline (`original_protean`) source unchanged; no `equal_available` mode added;
beta candidate set unchanged (`{0, .25, .5, .75, 1}`).
## 3. Session 5 objective and outcome
## 3. Session 6 objective and outcome
Objective: run ONE paired full condition (X-IIoTID, alpha=0.5, seed=0) across
`beta ∈ {0, 0.25, 0.5, 0.75, 1}`, reusing the frozen manifest and training settings;
select beta on validation only; evaluate the selected beta on test; add the direct
prototype-effect and client-level analyses; update docs. **All acceptance items were met.**
No commit or push was made.
Objective: extend the validated paired protocol from one seed to seeds `{0,1,2}` for
X-IIoTID alpha=0.50, reuse the seed-0 Session-5 results, run the two new seeds across the
five beta candidates plus `original_protean`, select beta with **pooled validation only**,
evaluate the selected beta on test, and produce cross-seed direct-effect / client-level /
stability analyses + docs. **All acceptance items were met.** No commit or push was made.
Outcome: **PRIMARY pooled-validation selected beta = 0.75** (validation only). Per-seed
diagnostic selections varied (0.50 / 1.00 / 0.75), and between-seed variance is comparable
to the between-beta spread, so the selection is arithmetically clear but statistically
weak. The protocol ran cleanly end to end.
## 4. Condition and pairing
| Field | Value |
|---|---|
| dataset / alpha / seed | `xiiotid` / 0.50 / 0 |
| manifest | `artifacts/partitions/xiiotid__alpha0.5__seed0.json` (reused) |
| manifest content hash | `e81a6538ecefcda202aa7602310ff8d8c9529031ab9dcbec4ebbabc0e63b636c` |
| partition hash | `2465485539a3e923548dcc0c73d8acc5076d456536b78682d512897ea586e779` |
| split hash / sizes | `d0acc0c14e3ecbeba02dfa9e6227c983da12dec2d6111df0b714002e6e03c29d` / 569,909 train, 81,414 val, 162,831 test |
| preprocessing hash | `8580cee41c698ce43cf3269e4a2b3a01c92d6df22820f4004c0267478bd2579a` |
| dataset / alpha / seeds | `xiiotid` / 0.50 / `{0,1,2}` |
| runs | 15 `support_weighted` + 3 `original_protean` (seed 0 reused; 12 run this session) |
| training | 10 clients, 10 rounds, 3 local epochs, batch 256, lr 1e-3, mu 0.1, lambda 1.0, Adam |
| code revision | HEAD `ad9aa53ba76f60671ace991fcee85f003781f196`; only `scripts/run_baseline.py` modified (hash-object `2b469e35ecbbc6ea3dbd4b0632419993e4d5130b`) |
| code revision | unchanged from HEAD `50f2da72166e4f609077d65b4916f745da5e8e3b`; only read-only analysis code added |
Pairing proof: the round-0 prototype snapshot is **byte-identical** (SHA-256 prefix
`e27f41b3a62eb83c`) across all five beta runs and the frozen `original_protean` run;
`train_support_Nij.csv`, `characterization.csv`, `rare_unseen.csv` are identical across
runs. All runs are in `results/xiiotid__alpha0.5__seed0__support_weighted__beta{0,0.25,0.5,0.75,1}/`.
Manifests (reused, never regenerated; one content hash per seed across all six runs):
## 5. Validation results and beta selection
| seed | manifest | content hash | partition hash | split hash | rng seed |
|---|---|---|---|---|---|
| 0 | `artifacts/partitions/xiiotid__alpha0.5__seed0.json` | `e81a6538…` | `2465485539…` | `d0acc0c1…` | 2009062324 |
| 1 | `artifacts/partitions/xiiotid__alpha0.5__seed1.json` | `54ea1df1…` | `4061c5a1…` | `880a9f82…` | 621679668 |
| 2 | `artifacts/partitions/xiiotid__alpha0.5__seed2.json` | `be881ac6…` | `1118e7fa…` | `8e0db0be…` | 1438483982 |
Primary selection metric: **validation per-client-mean rare-class macro-F1** (per-client
rare F1 on each client's validation eval partition, averaged over clients with rare
classes). Global union-rare validation metrics also shown.
Pairing proof: round-0 snapshot byte-identical across the five betas **and**
`original_protean` within each seed — seed 0 `e27f41b3a62eb83c…`, seed 1
`b7f61c995e97474c…`, seed 2 `5b749f4185e11fc7…`. `train_support_Nij.csv`,
`characterization.csv`, `rare_unseen.csv` identical across each seed's runs.
`manifest_reload_ok = true` everywhere.
| beta | val rareMacroF1 (pc mean) | val rareMacroF1 (global) | val rareRecall | val macroF1 | val acc | val wF1 |
|---|---|---|---|---|---|---|
| 0.00 | 0.557340 | 0.611641 | 0.884184 | 0.708929 | 0.891038 | 0.917741 |
| 0.25 | 0.571787 | 0.629475 | 0.888488 | 0.723431 | 0.902953 | 0.925933 |
| **0.50** | **0.585762** | **0.641709** | 0.887173 | **0.731166** | 0.897831 | **0.926005** |
| 0.75 | 0.578003 | 0.637107 | 0.873910 | 0.726603 | 0.887894 | 0.921696 |
| 1.00 | 0.551443 | 0.615307 | 0.847043 | 0.715951 | 0.883263 | 0.921182 |
## 5. Validation results (USED FOR SELECTION) and pooled selection
**Selected beta = 0.50** (best among tested candidates under this validation condition;
no tie; both validation readings agree). Test was **not** used for selection.
Primary metric: validation per-client-mean rare-class macro-F1. All 10 clients eligible
in every seed. Full per-seed table:
`artifacts/analysis/xiiotid__alpha0.5__multiseed__validation_per_seed.csv`.
## 6. Final test evaluation of the selected beta
Per-seed validation per-client-mean rare-class macro-F1:
| metric | original_protean | support_weighted beta=0.50 |
|---|---|---|
| rare-class macro-F1 | 0.609620 | 0.640829 |
| rare-class recall | 0.871487 | 0.871583 |
| overall macro-F1 | 0.707701 | 0.730743 |
| accuracy | 0.890432 | 0.898158 |
| weighted-F1 | 0.917585 | 0.926333 |
| beta | seed 0 | seed 1 | seed 2 |
|---|---|---|---|
| 0.00 | 0.557340 | 0.628624 | 0.447118 |
| 0.25 | 0.571787 | 0.636205 | 0.458310 |
| 0.50 | 0.585762 | 0.645189 | 0.471015 |
| 0.75 | 0.578003 | 0.655160 | 0.504937 |
| 1.00 | 0.551443 | 0.683117 | 0.485877 |
All five candidates' test metrics (transparency only) are in
`docs/research/PAIRED_BETA_VALIDATION.md` §B.2. No superiority is claimed: single
dataset / alpha / seed.
Pooled across seeds (`...__validation_pooled.csv`):
## 7. `beta = 0` equivalence result (important)
| beta | pooled mean | std (sample) | min | max | selected |
|---|---|---|---|---|---|
| 0.00 | 0.544361 | 0.091447 | 0.447118 | 0.628624 | |
| 0.25 | 0.555434 | 0.090068 | 0.458310 | 0.636205 | |
| 0.50 | 0.567322 | 0.088539 | 0.471015 | 0.645189 | |
| **0.75** | **0.579367** | **0.075121** | 0.504937 | 0.655160 | **yes** |
| 1.00 | 0.573479 | 0.100450 | 0.485877 | 0.683117 | |
`support_weighted beta=0` vs frozen `original_protean`:
- **PRIMARY selected beta (`beta_star`) = 0.75**, max pooled validation per-client-mean
rare-class macro-F1; **no tie-break used**; `test_used_for_selection = false` for all 15
runs. The secondary pooled recall is highest at beta=0.25 (recall is only a tie-break).
- **SECONDARY per-seed diagnostics only:** seed 0 → 0.50, seed 1 → 1.00, seed 2 → 0.75.
These are stability diagnostics, not the condition's selection rule.
- Same frozen manifest and split: **yes**.
- Same initialisation/round-0 setup: **yes** (round-0 snapshot byte-identical).
- Training behaviour: **mathematically identical, not bitwise**. `original_protean`
computes `Σ(C_ij/count)`; `support_weighted beta=0` computes `Σ((1/count)·C_ij)`.
Per-element difference ≤ `2.2e-16`–`4.4e-16` (~1 ULP, relative ~1e-16).
- Validation/test metrics: **not identical** (baseline slightly lower; e.g. test
accuracy 0.890432 vs 0.891974). The ~1 ULP aggregation difference feeds back into
round-1 prototype-alignment training and the trajectories diverge slowly from round 1.
- Deterministic artifacts identical: round-0 snapshot, `train_support_Nij.csv`,
`characterization.csv`, `rare_unseen.csv`; `history.json`/per-client/per-class differ.
## 6. Final test evaluation of the pooled-selected beta
This is floating-point arithmetic ordering, not a semantic change; no code was altered to
force bitwise equality. **Conditional on the shared `S_j` reproduction assumption** and
does not prove `beta = 0` reproduces original PROTEAN.
`beta_star = 0.75` was fixed from pooled validation before test was read.
## 8. Direct prototype-effect findings (fixed round-0 snapshot, no retraining)
| metric (test) | seed 0 | seed 1 | seed 2 | mean ± std |
|---|---|---|---|---|
| rare-class macro-F1 | 0.636189 | 0.671937 | 0.688381 | 0.665503 ± 0.026685 |
| rare-class recall | 0.857543 | 0.851883 | 0.878321 | 0.862583 ± 0.013921 |
| macro-F1 | 0.726298 | 0.729015 | 0.738194 | 0.731169 ± 0.006234 |
| accuracy | 0.888670 | 0.925493 | 0.905012 | 0.906392 ± 0.018450 |
| weighted-F1 | 0.922278 | 0.944734 | 0.933884 | 0.933632 ± 0.011230 |
At `beta = 1.0`, largest `||Cbar_j(1)-Cbar_j(0)||`:
`Weaponization` 3.559 (rel 0.577; most support-imbalanced, cv 2.10, max/min 1,800),
`C&C` 2.156, `Lateral_movement` 2.069, `Exploitation` 1.934, `Tampering` 1.737,
`Exfiltration` 1.547; smallest: `Normal` 0.990 (cv 0.76), `RDOS` 1.286,
`Reconnaissance` 1.184. Every class has `|S_j| ≥ 8` and unequal support, so no class has
zero direct effect. Outputs:
`artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0__prototype_snapshot__prototype_effect.csv`
and `...__prototype_weights.csv`. Displacement is a direct-aggregation measure only, not
a detection result.
`original_protean` for the same seeds/condition:
## 9. Client-level findings (test, vs beta=0)
| metric (test) | seed 0 | seed 1 | seed 2 | mean ± std |
|---|---|---|---|---|
| rare-class macro-F1 | 0.609620 | 0.655665 | 0.662275 | 0.642520 ± 0.028683 |
| rare-class recall | 0.871487 | 0.889365 | 0.870018 | 0.876957 ± 0.010771 |
| macro-F1 | 0.707701 | 0.709176 | 0.710017 | 0.708965 ± 0.001172 |
| accuracy | 0.890432 | 0.900885 | 0.907045 | 0.899454 ± 0.008398 |
| weighted-F1 | 0.917585 | 0.929132 | 0.927481 | 0.924733 ± 0.006245 |
Full paired table: `artifacts/analysis/xiiotid__alpha0.5__seed0__paired_client_beta_metrics.csv`.
At `beta=0.50` six of ten clients improve; only client 3 is materially negative (−0.068).
At `beta=0.75/1.00` several clients lose, with client 3 worst (−0.257/−0.342). The
`min positive N_ij` vs ΔF1 relationship is **not monotonic** in this single condition:
client 3 (`min N_ij=140`) is the worst loser while client 5 (`min N_ij=2`) is the biggest
gainer. Descriptive only; no causal claim.
Test columns are **descriptive only** and played no part in selection. No superiority or
generalisation is claimed: single dataset, single alpha, three project-defined seeds, and
between-seed spread ~ between-method difference.
## 7. `beta = 0` equivalence result (seeds 1 and 2)
For seeds 1 and 2, `support_weighted beta=0` vs frozen `original_protean`:
- Same manifest/split/hashes: **yes**.
- Round-0 snapshot byte-identical: **yes** (seed 1 `b7f61c99…`, seed 2 `5b749f41…`).
- `train_support_Nij.csv`, `characterization.csv`, `rare_unseen.csv`: **identical**.
- Aggregation: **mathematically equivalent, not bitwise** (`Σ(C/count)` vs
`Σ((1/count)·C)`, ~1 ULP ordering difference that feeds back through round≥1).
- Test/history/per-client/per-class: **differ slightly**.
- Classification: **mathematically equivalent + floating-point numerical divergence;
no semantic discrepancy** in either seed. Same result as Session 5 seed 0.
Conditional on the shared `S_j` reproduction assumption; not proof `beta=0` reproduces
original PROTEAN.
## 8. Direct prototype-effect findings (fixed round-0 snapshots, no retraining)
`||Cbar_j(1) - Cbar_j(0)||`, cross-seed mean (std):
- Largest means: `Lateral_movement` 2.724 (1.035), `RDOS` 2.631 (1.165),
`Weaponization` 2.561 (1.228), `Tampering` 2.354 (1.165), `Exploitation` 2.344 (0.412).
- Smallest: `Reconnaissance` 1.002 (0.261), `Normal` 1.557 (0.548), `C&C` 1.694 (0.505).
- Every class moves for beta>0 (all classes have unequal support); the **rank order is
seed-sensitive** (seed 0 top = Weaponization, seed 1 top = RDOS, seed 2 top =
Lateral_movement). Displacement is a direct-aggregation measure only, not a detection
result. Outputs: `...__prototype_effect_pooled.csv` and the per-seed
`...__prototype_snapshot__prototype_effect.csv` / `..._weights.csv`.
## 9. Client-level findings (validation, vs beta = 0)
Full detail: `...__multiseed__client_validation_detail.csv`; summary:
`...__client_validation_summary.csv`.
- At `beta = 0.75`, improve/degrade/**unchanged** counts are 7/3/0 (seed 0), 8/1/1
(seed 1, tie = client 7), 8/1/1 (seed 2, tie = client 4); mean Δ is positive in all
three (+0.021 / +0.027 / +0.058).
- At `beta = 1.00`, seed 0 flips to 4 improve / 6 degrade / 0 unchanged, while seed 1
improves 9/10 with 1 unchanged (client 7) — the high-beta response is seed-dependent.
- `min positive attack N_ij` vs ΔF1 is **non-monotonic**: N=1 clients range −0.021…+0.041
at beta=0.75; the largest gain (+0.177) is an N=10 client; low support neither
systematically benefits nor degrades. Client ids are different realisations across
seeds and are not treated as one population. Descriptive only.
## 10. Runtime / resources
CPU only (no GPU). Per run: 41–44 min wall-clock, ~733–740 % CPU, peak RSS ~6.21 GiB,
no swap, exit 0. Total ~3.5 h for the five paired candidates. Preprocessing/load
negligible.
CPU only, no GPU, no swap, exit 0 on all 18 runs (15 new-session + reused). Per-run
wall-clock 40:23–42:21; CPU 733–745 %; peak RSS 6.21 GiB (seed 0), 7.48 GiB (seed 1),
6.45 GiB (seed 2). New compute this session: 12 runs, ≈ 8 h 11 m wall-clock, run
sequentially (8 cores; concurrent runs would not reduce total CPU work).
Seed-0 numbers were reused and not re-run. Machine-readable per-run values:
`artifacts/analysis/xiiotid__alpha0.5__multiseed__runtime.csv` (added by the audit;
seed 0 sourced from Session-5 docs, seeds 1–2 from `/usr/bin/time -v` logs).
## 11. Methodological decisions / assumptions
1. **`S_j` participant/denominator rule** (`S_j = {i | N_ij > 0 and valid C_ij}`, equal
weights in the baseline) is a **REPRODUCTION ASSUMPTION**, not verified original
behaviour; material in 53/162 class-condition cells (32.7 %), 15/18 conditions
(`docs/baseline/BASELINE_VALIDATION.md` §D). Carry this caveat in all reporting.
weights in the baseline) is a **REPRODUCTION ASSUMPTION**; material in 53/162
class-condition cells (32.7 %), 15/18 conditions (`BASELINE_VALIDATION.md` §D).
Carry this caveat in all reporting.
2. `N_ij` is class-specific sample support, not prototype quality.
3. Beta is one value for all classes/clients; candidates frozen at `{0,.25,.5,.75,1}`.
4. `lambda=1.0`, Adam, lr 1e-3, batch 256 remain PROJECT DECISIONS.
5. **New this session (protocol clarification, documented in
`EXPERIMENT_PROTOCOL.md` §8):** candidate validation metric = final-model validation
metrics; primary selection = per-client-mean rare-class macro-F1 using a deterministic
Dirichlet validation eval partition (mirrors the existing test per-client procedure);
tie-break = higher val rare macro-F1 → higher val rare recall → smaller beta.
6. No `equal_available` mode was added.
4. **PRIMARY selection = pooled validation across clients and seeds**; per-seed selection
is secondary stability diagnostics; test used only after the pooled beta is fixed.
Documented in `EXPERIMENT_PROTOCOL.md` §8 (updated this session).
5. The union of rare classes (and global rare support) differs by seed because partitions
differ; only the per-client-mean metric is directly comparable across seeds.
6. `lambda=1.0`, Adam, lr 1e-3, batch 256 remain PROJECT DECISIONS. No `equal_available`
mode was added.
## 12. Unresolved questions / blockers
1. `S_j` reproduction ambiguity — unresolved; no official PROTEAN code exists.
2. `beta = 0` is not bitwise identical to `original_protean` (float ordering). Whether the
thesis should report this explicitly, or whether an equivalence check should use a
tolerance, is a documentation decision for later.
3. Single condition only; no multi-seed/multi-alpha evidence yet.
2. **Pooled beta selection is statistically weak here:** pooled means span only
0.544→0.579 while within-beta across-seed std is 0.075–0.100; three seeds give a
standard error of the same order as the between-beta spread. The beta effect is small
relative to seed variance.
3. `beta = 0` is not bitwise identical to `original_protean`; documentation decision on how
to report this remains open.
4. Session-2 manifests still store `preprocessing_hash = null` / `config_hash = null`.
5. Only alpha=0.50, X-IIoTID, three seeds measured.
**Audit status (see `docs/research/SESSION6_AUDIT.md`).** No unresolved issue that could
change `beta_star` or the scientific interpretation. One inherent audit limitation: the
trained model is not persisted, so per-client prediction metrics cannot be recomputed
without retraining (forbidden); they were verified via defining source code, exact
agreement of their mean with `val_selection.json`, and exact agreement of the global
union-rare metric recomputed from `val_confusion_matrix.json`. Pooled std uses the
**sample** convention (ddof=1), labelled "(sample)" in the report.
## 13. Git status
No commit or push was made. `git status --short`:
No commit or push was made. `git status --short` (results/ is ignored):
```
M scripts/run_baseline.py
?? artifacts/analysis/xiiotid__alpha0.5__seed0__paired_client_beta_metrics.csv
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0__prototype_snapshot.json
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0__prototype_snapshot__prototype_effect.csv
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0__prototype_snapshot__prototype_weights.csv
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0.25__prototype_snapshot.json
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0.5__prototype_snapshot.json
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0.75__prototype_snapshot.json
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta1__prototype_snapshot.json
M docs/handoff/SESSION_HANDOFF.md
M docs/research/EXPERIMENT_PROTOCOL.md
?? docs/research/MULTISEED_BETA_VALIDATION.md
?? docs/research/SESSION6_AUDIT.md
?? scripts/analyze_multiseed_beta.py
?? artifacts/analysis/xiiotid__alpha0.5__multiseed__*.csv / __selection_summary.json
?? artifacts/analysis/xiiotid__alpha0.5__seed{1,2}__*__prototype_snapshot.json
?? artifacts/analysis/xiiotid__alpha0.5__seed{1,2}__support_weighted__beta0__prototype_snapshot__{prototype_effect,prototype_weights}.csv
?? artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta{0.25,0.5,0.75,1}__prototype_snapshot.json (pre-existing from Session 5)
```
`planningawal/` and both raw datasets are unchanged; `results/` and
`artifacts/processed/` are ignored; `artifacts/analysis/` is tracked.
`planningawal/` and both raw datasets are unchanged; `results/` is gitignored.
`HEAD = 50f2da72166e4f609077d65b4916f745da5e8e3b`. No commit or push was made.
## 14. Files created / modified this session
Created:
- `docs/research/PAIRED_BETA_VALIDATION.md`
- `artifacts/analysis/xiiotid__alpha0.5__seed0__paired_client_beta_metrics.csv`
- five `artifacts/analysis/...__support_weighted__beta*__prototype_snapshot.json`
- two `artifacts/analysis/...__prototype_snapshot__prototype_effect.csv` / `__prototype_weights.csv`
- `docs/research/MULTISEED_BETA_VALIDATION.md`
- `docs/research/SESSION6_AUDIT.md` (post-experiment audit)
- `scripts/analyze_multiseed_beta.py`
- `artifacts/analysis/xiiotid__alpha0.5__multiseed__*` (validation per-seed/pooled,
client summary/detail/deltas, prototype-effect pooled, test descriptive, selection
summary, and `...__runtime.csv` added by the audit)
- seed-1/seed-2 `results/...` run dirs (gitignored) and their snapshots / effect CSVs
Modified:
- `scripts/run_baseline.py` (added validation-only evaluation artifacts; test/training paths untouched)
- `docs/research/EXPERIMENT_PROTOCOL.md` (§8 Session-5 clarification)
- `docs/research/EXPERIMENT_PROTOCOL.md` (§8 selection hierarchy clarified)
- `docs/handoff/SESSION_HANDOFF.md` (this file)
Reused (not regenerated): all seed-0 Session-5 runs and artifacts.
## 15. Exact recommended next phase
**Recommendation: B — checkpoint Session 5, then proceed to a controlled
multi-seed/multi-alpha experiment.** The protocol ran cleanly (validation/test separation
held, artifacts reproducible, pairing proven by byte-identical round-0 snapshots, no test
leakage into selection), so the next step is to scale to seeds `{0,1,2}` at a small alpha
set, still paired. Do **not** run the entire `dataset × alpha × seed × beta` grid at once;
grow it in controlled, inspectable increments.
**Session 6 is safe to checkpoint** (audit PASS WITH MINOR CORRECTIONS; no issue affects
`beta_star`). The protocol is **mechanically** ready to scale, but Session 6 shows the beta
effect at alpha=0.50 is small relative to seed-to-seed variance. Recommended, in order:
Before scaling: decide how to report the float-level `beta = 0` vs `original_protean`
non-identity (§7), and whether to re-run `original_protean` per condition with the new
validation artifacts (the existing baseline run predates the validation-artifact change).
1. **Do not treat beta=0.75 as a settled optimum.** Report it as "best among tested
candidates under pooled validation for X-IIoTID alpha=0.50".
2. Before expanding alpha, decide the interpretation question: is the goal to characterise
small beta effects against seed noise (requires more seeds), or to run alpha=0.25/0.75
as a paired characterisation (accepting the same weak-effect caveat)?
3. If expanding, run alpha=0.25 then alpha=0.75 for seeds `{0,1,2}`, the five betas each,
reusing frozen manifests, sequentially (~8 h per alpha), with the same pooled
validation-only selection. Extend `scripts/analyze_multiseed_beta.py` with `--alpha`.
4. Do **not** launch 5G-NIDD or the full grid until the alpha results are inspected.
## 16. Important commands
@@ -220,14 +299,18 @@ validation artifacts (the existing baseline run predates the validation-artifact
# Tests (75 expected)
.venv/bin/python -m pytest
# One full paired beta run (CPU, ~42 min, ~6.2 GiB)
.venv/bin/python scripts/run_baseline.py --dataset xiiotid --alpha 0.5 --seed 0 \
# One full paired beta run (CPU, ~41 min, ~6.5-7.5 GiB)
.venv/bin/python scripts/run_baseline.py --dataset xiiotid --alpha 0.5 --seed 1 \
--clients 10 --rounds 10 --local-epochs 3 \
--aggregation-mode support_weighted --beta 0.5
# Pooled multi-seed validation analysis (read-only, no retraining)
.venv/bin/python scripts/analyze_multiseed_beta.py --dataset xiiotid --alpha 0.5 \
--seeds 0 1 2
# Direct prototype-effect analysis (no retraining)
.venv/bin/python scripts/analyze_prototype_effect.py \
--snapshot artifacts/analysis/xiiotid__alpha0.5__seed0__support_weighted__beta0__prototype_snapshot.json \
--snapshot artifacts/analysis/xiiotid__alpha0.5__seed1__support_weighted__beta0__prototype_snapshot.json \
--class-names "C&C,Exfiltration,Exploitation,Lateral_movement,Normal,RDOS,Reconnaissance,Tampering,Weaponization" \
--output-dir artifacts/analysis
```
@@ -235,13 +318,13 @@ validation artifacts (the existing baseline run predates the validation-artifact
## 17. Recommended next-session prompt
```
You are continuing the perdalam-protean research repository (Session 6).
You are continuing the perdalam-protean research repository (Session 7).
FIRST, read completely:
1. docs/handoff/SESSION_HANDOFF.md
2. docs/research/PAIRED_BETA_VALIDATION.md
3. docs/research/EXPERIMENT_PROTOCOL.md
4. docs/research/BETA_AGGREGATION_IMPLEMENTATION.md
2. docs/research/MULTISEED_BETA_VALIDATION.md
3. docs/research/SESSION6_AUDIT.md
4. docs/research/EXPERIMENT_PROTOCOL.md
5. docs/baseline/BASELINE_VALIDATION.md
Critical rules:
@@ -253,18 +336,22 @@ Critical rules:
- Do NOT add an executable equal_available mode.
- Do NOT launch the full alpha x seed x dataset x beta grid.
- Beta selection uses validation metrics only; test never influences selection.
- PRIMARY selection is pooled across clients and seeds; per-seed selection is
diagnostic only.
Context: Session 5 ran one paired full condition (xiiotid, alpha=0.5, seed=0) across
beta in {0,0.25,0.5,0.75,1}. Selected beta = 0.50 on validation per-client-mean
rare-class macro-F1; test rare-class macro-F1 0.640829 vs original_protean 0.609620.
beta=0 is mathematically but not bitwise equal to original_protean (~1 ULP aggregation
ordering). scripts/run_baseline.py now writes validation-only artifacts.
Context: Session 6 completed X-IIoTID alpha=0.50 for seeds {0,1,2} across beta in
{0,0.25,0.5,0.75,1}. Seed 0 was reused from Session 5. Pooled validation selected
beta=0.75 (val per-client-mean rare macro-F1 0.579367); per-seed diagnostics were
0.50/1.00/0.75. Pooled-selected test rare macro-F1 0.665503 +/- 0.026685 vs
original_protean 0.642520 +/- 0.028683. beta=0 is mathematically but not bitwise equal
to original_protean (float ordering), no semantic discrepancy. Between-seed variance is
comparable to the between-beta spread, so the selection is weak. A strict independent
audit (docs/research/SESSION6_AUDIT.md) returned PASS WITH MINOR CORRECTIONS and
independently reproduced beta_star=0.75 from validation artifacts alone.
Task: scale the paired protocol to a controlled multi-seed/multi-alpha set (e.g. seeds
{0,1,2} at alpha 0.50, reusing frozen manifests; grow only as compute allows), keeping
validation-only beta selection and reporting all candidates. Decide/document how to
report the beta=0 vs original_protean float non-identity. Update docs and
docs/handoff/SESSION_HANDOFF.md. Measure runtime before scaling further.
Use .venv/bin/python for all Python commands. Do not commit or push unless asked.
Task: decide and document the alpha-expansion plan (recommended: alpha=0.25 then 0.75,
seeds {0,1,2}, five betas each, sequential, reusing frozen manifests, pooled
validation-only selection) OR argue for more seeds at alpha=0.50 first. Do not launch
5G-NIDD or the full grid. Update docs and docs/handoff/SESSION_HANDOFF.md. Use
.venv/bin/python. Do not commit or push unless asked.
```
+30 -7
View File
@@ -121,19 +121,40 @@ Persisted metadata (minimum one row per `(dataset, alpha, seed, client, class)`)
## 8. Validation-based beta selection
Selection hierarchy (strictly ordered). Test data is never used in PRIMARY or
SECONDARY.
**PRIMARY — pooled validation across clients and random seeds for one non-IID
condition.** For each beta, average the per-client rare-class macro-F1 over eligible
clients within each seed, then average those seed-level scores over the condition's
seeds. Select `beta_star = argmax` of this pooled score using validation only. This
is the beta reported for the condition. Tie-break: higher pooled per-client-mean
rare-class macro-F1 -> higher pooled validation rare-class recall -> lower
across-seed std -> smaller beta.
**SECONDARY — per-seed beta selection, for stability analysis only.** For each seed
independently, choose the validation-best beta with the same metric. Per-seed choices
are diagnostics of seed sensitivity and are **not** the condition's selection rule.
**TEST — used only after `beta_star` is fixed from pooled validation.** Evaluate the
fixed `beta_star` on test, and report all candidates' test metrics for transparency.
No test metric may influence `beta_star`.
Procedure, strictly ordered:
1. **Candidate evaluation (validation only).** For each beta candidate, train with the paired
manifest and compute validation metrics. Primary criterion: rare-class macro-F1, computed
per client that has rare classes, then averaged over those clients.
2. **Beta selection.** Choose the beta with the best mean validation rare-class macro-F1 (ties
broken by smaller beta). The result is "best among tested candidates under this
procedure", never a global optimum.
manifest and compute validation metrics. Primary metric: per-client rare-class macro-F1,
computed per client that has rare classes, then averaged over those clients.
2. **PRIMARY beta selection.** Choose the beta with the best pooled-across-seed mean
validation per-client rare-class macro-F1 (tie-break per the hierarchy above). The result
is "best among tested candidates under this procedure", never a global optimum.
3. **Final test evaluation.** Evaluate the selected beta on the test split. Report overall
macro-F1, rare-class recall, rare-class macro-F1, and per-client results, plus the test
results of all candidates for transparency.
Test data is never used in steps 1–2.
Session-5 (single-seed) used the one-seed special case of step 2. Session 6 applies the
full pooled rule across seeds `{0,1,2}` for X-IIoTID alpha=0.50; see
`docs/research/MULTISEED_BETA_VALIDATION.md`.
**Session-5 clarification (implementation detail, not a design change).** Running
the protocol surfaced two concrete points that the original text left implicit.
@@ -151,7 +172,9 @@ They do not alter the frozen design; they pin how step 1 is computed.
evaluation, applied to the validation split (`split_name="validation"`); this
is a PROJECT DECISION, paired across all beta candidates and seeds.
2. **Tie-break order.** Higher validation rare-class macro-F1, then higher
validation rare-class recall, then smaller beta.
validation rare-class recall, then lower across-seed std (pooled PRIMARY only),
then smaller beta. The pooled PRIMARY tie-break is authoritative; the per-seed
SECONDARY uses the same order without the std step.
`scripts/run_baseline.py` writes `val_global_metrics.json`, `val_per_class.csv`,
`val_per_client.csv` and `val_selection.json` to make this explicit; the test
+399
View File
@@ -0,0 +1,399 @@
# Multi-Seed Beta Validation (Session 6)
Status: **three-seed paired protocol completed for one non-IID condition** with
pooled-across-seed validation-only beta selection, followed by a single final test
evaluation of the pooled-selected beta. This is a **stability / protocol-scaling
experiment**, not a sweep and not evidence of a global optimum.
Companion documents:
- `docs/research/EXPERIMENT_PROTOCOL.md` — frozen protocol and selection hierarchy.
- `docs/research/PAIRED_BETA_VALIDATION.md` — Session-5 single-seed condition.
- `docs/research/BETA_AGGREGATION_IMPLEMENTATION.md` — beta mode implementation.
- `docs/baseline/BASELINE_VALIDATION.md` — frozen baseline evidence.
- `docs/handoff/SESSION_HANDOFF.md` — continuation point.
---
## A. Scope
| Field | Value |
|---|---|
| dataset | `xiiotid` (X-IIoTID) |
| alpha | 0.50 |
| seeds | 0, 1, 2 |
| aggregation | `original_protean` (per seed) + `support_weighted` for `beta ∈ {0, 0.25, 0.5, 0.75, 1}` |
| runs | 15 `support_weighted` + 3 `original_protean` |
| reused | seed 0 (all five betas + `original_protean`) from Session 5, not re-run |
| new this session | seeds 1 and 2 (five betas each + `original_protean` each) |
No other alpha, dataset, seed, mode or beta was run. `equal_available` was not added.
---
## B. Frozen protocol
Training config is identical to Session 5 and to the frozen protocol
(`EXPERIMENT_PROTOCOL.md` §14): 10 clients, 10 rounds, 3 local epochs, batch 256,
Adam `lr=1e-3`, `mu=0.1`, `lambda=1.0`; model architecture, preprocessing,
train/validation/test split recipe, partition recipe, loss terms and inference
unchanged. No training/aggregation source was changed this session; the only code
changes are the read-only analysis script `scripts/analyze_multiseed_beta.py` (and
the Session-5 `scripts/run_baseline.py` validation-artifact emission already at HEAD).
### B.1 Manifest identity (reused, never regenerated)
| seed | manifest path | content hash | partition hash | split hash | rng seed | train/val/test sizes |
|---|---|---|---|---|---|---|
| 0 | `artifacts/partitions/xiiotid__alpha0.5__seed0.json` | `e81a6538…` | `2465485539…` | `d0acc0c1…` | 2009062324 | 569,909 / 81,414 / 162,831 |
| 1 | `artifacts/partitions/xiiotid__alpha0.5__seed1.json` | `54ea1df1…` | `4061c5a1…` | `880a9f82…` | 621679668 | 569,909 / 81,414 / 162,831 |
| 2 | `artifacts/partitions/xiiotid__alpha0.5__seed2.json` | `be881ac6…` | `1118e7fa…` | `8e0db0be…` | 1438483982 | 569,909 / 81,414 / 162,831 |
`manifest_reload_ok = true` and the per-seed manifest content hash is identical
across all six runs (five betas + `original_protean`) of that seed, so pairing holds.
### B.2 Build-level pairing proof (round-0 snapshot byte-identity)
The round-0 prototype snapshot is byte-identical across the five beta runs **and**
`original_protean` within each seed:
| seed | SHA-256 of round-0 snapshot (all six runs) |
|---|---|
| 0 | `e27f41b3a62eb83c…` |
| 1 | `b7f61c995e97474c…` |
| 2 | `5b749f4185e11fc7…` |
`train_support_Nij.csv`, `characterization.csv` and `rare_unseen.csv` are also
byte-identical across the runs of each seed. Beta only changes server aggregation.
### B.3 Rare/unseen derivation is per-seed
Rare classes are derived from the manifest training split (`EXPERIMENT_PROTOCOL.md`
§7). The Dirichlet partitions differ by seed, so the union of rare classes differs:
| seed | per-client rare count | union of rare classes |
|---|---|---|
| 0 | 2 per client (10/10 clients) | C&C, Exfiltration, Exploitation, Lateral_movement, Tampering, Weaponization |
| 1 | 2 per client (10/10 clients) | + RDOS |
| 2 | 2 per client (10/10 clients) | + RDOS |
Integrity checks (all pass): rare classes always have `N_ij > 0`; unseen attack
classes always have `N_ij = 0`; the benign class (`Normal`) is never rare or unseen;
rare and unseen are never merged; every client has exactly two rare classes. The
different union rare set is why global test rare support is 25,454 (seed 0) versus
53,706 (seeds 1, 2); the primary per-client-mean metric uses each client's own rare
classes and so is comparable across seeds.
### B.4 Selection rule
- **PRIMARY:** pooled-across-seed validation per-client-mean rare-class macro-F1
(mean of each seed's per-client-mean score over seeds `{0,1,2}`), selected with
validation only.
- **SECONDARY (diagnostic):** per-seed validation selection, for stability analysis.
- **TEST:** computed for all candidates by the frozen pipeline but used only after
the pooled beta is fixed. No test metric influenced selection.
---
## C. Per-seed candidate validation results
`val rareMacroF1 (pc mean)` is the primary metric: each client scored on its
deterministic Dirichlet **validation** eval partition with that client's rare class
ids, then averaged over the 10 eligible clients. All numbers are validation only.
### Seed 0 (reused from Session 5)
| beta | eligible | val rareMacroF1 (pc mean) | val rareMacroF1 (global) | val rareRecall (global) | val macroF1 | val acc |
|---|---|---|---|---|---|---|
| 0.00 | 10 | 0.557340 | 0.611641 | 0.884184 | 0.708929 | 0.891038 |
| 0.25 | 10 | 0.571787 | 0.629475 | 0.888488 | 0.723431 | 0.902953 |
| **0.50** | 10 | **0.585762** | **0.641709** | 0.887173 | **0.731166** | 0.897831 |
| 0.75 | 10 | 0.578003 | 0.637107 | 0.873910 | 0.726603 | 0.887894 |
| 1.00 | 10 | 0.551443 | 0.615307 | 0.847043 | 0.715951 | 0.883263 |
### Seed 1 (new)
| beta | eligible | val rareMacroF1 (pc mean) | val rareMacroF1 (global) | val rareRecall (global) | val macroF1 | val acc |
|---|---|---|---|---|---|---|
| 0.00 | 10 | 0.628624 | 0.656935 | 0.890520 | 0.709324 | 0.899612 |
| 0.25 | 10 | 0.636205 | 0.666240 | 0.884109 | 0.722367 | 0.919203 |
| 0.50 | 10 | 0.645189 | 0.667383 | 0.863833 | 0.724212 | 0.922593 |
| 0.75 | 10 | 0.655160 | 0.670472 | 0.855590 | 0.727134 | 0.923760 |
| **1.00** | 10 | **0.683117** | **0.685707** | 0.856876 | **0.739006** | **0.924497** |
### Seed 2 (new)
| beta | eligible | val rareMacroF1 (pc mean) | val rareMacroF1 (global) | val rareRecall (global) | val macroF1 | val acc |
|---|---|---|---|---|---|---|
| 0.00 | 10 | 0.447118 | 0.658720 | 0.880662 | 0.708464 | 0.907891 |
| 0.25 | 10 | 0.458310 | 0.669242 | 0.885401 | 0.716162 | 0.907018 |
| 0.50 | 10 | 0.471015 | 0.677892 | 0.887958 | 0.730956 | 0.920259 |
| **0.75** | 10 | **0.504937** | **0.686124** | 0.887725 | **0.735912** | 0.902351 |
| 1.00 | 10 | 0.485877 | 0.676314 | 0.877857 | 0.726922 | 0.883534 |
Machine-readable: `artifacts/analysis/xiiotid__alpha0.5__multiseed__validation_per_seed.csv`.
---
## D. Per-seed diagnostic beta selections (SECONDARY, stability only)
| seed | selected beta | val rareMacroF1 (pc mean) | tie-break used | test_used_for_selection |
|---|---|---|---|---|
| 0 | 0.50 | 0.585762 | no | false |
| 1 | 1.00 | 0.683117 | no | false |
| 2 | 0.75 | 0.504937 | no | false |
**These per-seed choices are diagnostic only.** They are not the primary selection
rule for this condition; they measure seed sensitivity. The preferred beta is
seed-dependent: 0.50, 1.00, 0.75. `val_selection.json` records
`"test_used_for_selection": false` and `"selection_split": "validation"` for all 15
`support_weighted` runs.
---
## E. PRIMARY pooled-across-seed validation selection
For each beta, `score_seed(beta)` is the seed-level validation per-client-mean
rare-class macro-F1; `score_pooled(beta)` is its mean over seeds `{0,1,2}`.
| beta | mean (pooled) | std (sample, ddof=1) | std (pop) | min | max | pooled val rareRecall (pc mean) | selected |
|---|---|---|---|---|---|---|---|
| 0.00 | 0.544361 | 0.091447 | 0.074666 | 0.447118 | 0.628624 | 0.839088 | |
| 0.25 | 0.555434 | 0.090068 | 0.073540 | 0.458310 | 0.636205 | **0.841278** | |
| 0.50 | 0.567322 | 0.088539 | 0.072292 | 0.471015 | 0.645189 | 0.838872 | |
| **0.75** | **0.579367** | **0.075121** | 0.061336 | 0.504937 | 0.655160 | 0.829088 | **yes** |
| 1.00 | 0.573479 | 0.100450 | 0.082017 | 0.485877 | 0.683117 | 0.820763 | |
- **PRIMARY selected beta (`beta_star`) = 0.75.**
- **Criterion:** maximum pooled validation per-client-mean rare-class macro-F1.
- **Tie-break:** not used (no exact tie). The secondary pooled recall is *highest* at
`beta = 0.25`, but recall is only a tie-break and the primary metric governs.
- **`test_used_for_selection = false`.** Test metrics were not inspected until
`beta = 0.75` was fixed.
- **Interpretation:** `beta = 0.75` is the *best among the tested candidates under
pooled validation across the three project-defined seeds for X-IIoTID alpha=0.50.*
It is **not** a global optimum.
Machine-readable: `artifacts/analysis/xiiotid__alpha0.5__multiseed__validation_pooled.csv`
and `...__selection_summary.json`.
---
## F. Final test evaluation after the pooled selection
`beta_star = 0.75` was fixed from pooled validation; only then were test metrics
read. Per-seed and mean ± std (sample) across the three seeds:
| metric | seed 0 | seed 1 | seed 2 | mean ± std |
|---|---|---|---|---|
| test rare-class macro-F1 | 0.636189 | 0.671937 | 0.688381 | 0.665503 ± 0.026685 |
| test rare-class recall | 0.857543 | 0.851883 | 0.878321 | 0.862583 ± 0.013921 |
| test macro-F1 | 0.726298 | 0.729015 | 0.738194 | 0.731169 ± 0.006234 |
| test accuracy | 0.888670 | 0.925493 | 0.905012 | 0.906392 ± 0.018450 |
| test weighted-F1 | 0.922278 | 0.944734 | 0.933884 | 0.933632 ± 0.011230 |
`original_protean` reference for the same three seeds and condition:
| metric | seed 0 | seed 1 | seed 2 | mean ± std |
|---|---|---|---|---|
| test rare-class macro-F1 | 0.609620 | 0.655665 | 0.662275 | 0.642520 ± 0.028683 |
| test rare-class recall | 0.871487 | 0.889365 | 0.870018 | 0.876957 ± 0.010771 |
| test macro-F1 | 0.707701 | 0.709176 | 0.710017 | 0.708965 ± 0.001172 |
| test accuracy | 0.890432 | 0.900885 | 0.907045 | 0.899454 ± 0.008398 |
| test weighted-F1 | 0.917585 | 0.929132 | 0.927481 | 0.924733 ± 0.006245 |
Descriptive difference (`beta_star` − `original_protean`) for test rare-class
macro-F1 is +0.026569 (seed 0), +0.016272 (seed 1), +0.026106 (seed 2); mean
+0.022982. **No superiority or generalisation is claimed:** this is a single
dataset, a single alpha, three project-defined seeds, and the between-seed spread
is comparable to the between-method difference.
Test columns are descriptive only. They are also recorded in
`artifacts/analysis/xiiotid__alpha0.5__multiseed__test_descriptive.csv`, clearly
separated from the validation tables.
---
## G. Direct prototype-effect analysis (fixed round-0 snapshots, no retraining)
Computed with the production aggregation from each seed's fixed `{C_ij, N_ij}`
round-0 snapshot. `||Cbar_j(beta) - Cbar_j(0)||` at `beta = 1.0`:
| class | mean disp | std across seeds | min | max | mean rel disp | typical `|S_j|` |
|---|---|---|---|---|---|---|
| Lateral_movement | 2.724 | 1.035 | 2.069 | 3.917 | 0.403 | 10 |
| RDOS | 2.631 | 1.165 | 1.286 | 3.322 | 0.352 | 10 |
| Weaponization | 2.561 | 1.228 | 1.189 | 3.559 | 0.433 | 10 |
| Tampering | 2.354 | 1.165 | 1.627 | 3.698 | 0.483 | 9–10 |
| Exploitation | 2.344 | 0.412 | 1.934 | 2.759 | 0.423 | 8–10 |
| Exfiltration | 2.020 | 0.502 | 1.547 | 2.547 | 0.297 | 9–10 |
| C&C | 1.694 | 0.505 | 1.155 | 2.156 | 0.319 | 8–10 |
| Normal | 1.557 | 0.548 | 0.990 | 2.084 | 0.292 | 10 |
| Reconnaissance | 1.002 | 0.261 | 0.703 | 1.184 | 0.169 | 10 |
Findings (descriptive):
- **Every class moves** for `beta > 0` in every seed, because every class has an
unequal support distribution across contributors (no class has equal `N_ij`).
- The **rank order of displacement is seed-sensitive**: seed 0's largest is
`Weaponization`, seed 1's is `RDOS`, seed 2's is `Lateral_movement`. `Exploitation`
and `Reconnaissance` are consistently smaller.
- **High-support-imbalance classes tend to move more** (e.g. `Weaponization`,
`RDOS`, `Tampering`, `Lateral_movement` have large max/min support ratios), but the
relationship is not exact and is seed-dependent.
- **Displacement is a direct-aggregation measure only** and is not interpreted as
improved or degraded detection. The end-to-end effect (§F) is reported separately.
Outputs: `artifacts/analysis/xiiotid__alpha0.5__seed{0,1,2}__support_weighted__beta0__prototype_snapshot__prototype_effect.csv`
and `...__prototype_weights.csv`; cross-seed aggregate
`artifacts/analysis/xiiotid__alpha0.5__multiseed__prototype_effect_pooled.csv`.
---
## H. Client-level effect (validation, relative to beta = 0)
Full per-client table: `artifacts/analysis/xiiotid__alpha0.5__multiseed__client_validation_detail.csv`.
Summary: `...__client_validation_summary.csv`.
| seed | beta | improve | degrade | unchanged | mean Δ | min Δ | max Δ |
|---|---|---|---|---|---|---|---|
| 0 | 0.25 | 9 | 1 | 0 | +0.0144 | −0.0048 | +0.0593 |
| 0 | 0.50 | 8 | 2 | 0 | +0.0284 | −0.0286 | +0.0926 |
| 0 | 0.75 | 7 | 3 | 0 | +0.0207 | −0.0358 | +0.0926 |
| 0 | 1.00 | 4 | 6 | 0 | −0.0059 | −0.0760 | +0.0414 |
| 1 | 0.25 | 6 | 2 | 2 | +0.0076 | −0.0045 | +0.0240 |
| 1 | 0.50 | 7 | 2 | 1 | +0.0166 | −0.0054 | +0.0424 |
| 1 | 0.75 | 8 | 1 | 1 | +0.0265 | −0.0030 | +0.0872 |
| 1 | 1.00 | 9 | 0 | 1 | +0.0545 | 0.0000 | +0.1927 |
| 2 | 0.25 | 7 | 1 | 2 | +0.0112 | −0.0007 | +0.0295 |
| 2 | 0.50 | 8 | 0 | 2 | +0.0239 | 0.0000 | +0.0697 |
| 2 | 0.75 | 8 | 1 | 1 | +0.0578 | −0.0019 | +0.1771 |
| 2 | 1.00 | 6 | 3 | 1 | +0.0388 | −0.4028 | +0.2676 |
Descriptive observations:
- At `beta = 0.75` the majority of eligible clients improve in every seed
(7/10, 8/10, 8/10), and the mean client delta is positive in all three seeds.
- The improvement fraction is **seed-dependent**: at `beta = 1.00` seed 0 degrades
(4 improve / 6 degrade) while seed 1 improves for 9/10 clients.
- **`min positive attack N_ij` vs ΔF1 is non-monotonic.** At `beta = 0.75`, clients
with `N_ij = 1` show deltas of −0.021, +0.029, +0.041; clients with `N_ij ≥ 130`
show +0.004 to +0.096; the largest gain (+0.177) is a client with `N_ij = 10`. Low
support neither systematically benefits nor systematically degrades.
- One extreme loss appears at `beta = 1.00`, seed 2 (client with `N_ij = 1`,
Δ = −0.403), showing that high beta can be unstable on specific clients.
**Client ids are different realisations across seeds** and are not treated as the
same statistical population. This analysis is descriptive and non-causal.
---
## I. Seed stability
- **Did the seed-specific preferred beta vary?** Yes: 0.50 (seed 0), 1.00 (seed 1),
0.75 (seed 2).
- **Was the pooled selection clear?** Arithmetically yes — `beta = 0.75` has the
highest pooled mean and the lowest across-seed std, with no tie. Statistically it
is **weak**: pooled means span only 0.544→0.579 (range ≈ 0.035), while the
within-beta across-seed std is 0.075–0.100. Between-seed variance dominates the
between-beta differences, so the pooled ordering is not strongly separated.
- **Are effect directions stable?** Partly. The pooled validation rare-class
macro-F1 rises from `beta = 0` to `0.75`, and the mean per-client delta is positive
for `0.25–0.75` in all seeds. But the *shape* and the best beta differ by seed
(seed 0 peaks at 0.50 and declines by 1.00; seed 1 increases monotonically; seed 2
peaks at 0.75). Pooled validation rare-class recall actually decreases with beta.
- **Is variance large relative to mean differences?** Yes. With only three seeds the
standard error of the pooled mean is ≈ `std/√3` ≈ 0.043–0.058, of the same order as
the total spread between beta candidates. The apparent "preference" for 0.75 is
therefore not robustly distinguishable from noise in this condition.
---
## J. Runtime and resources
Measured with `/usr/bin/time -v`, CPU only, no GPU, no swap, exit status 0 for every
run.
| seed | run | wall-clock | CPU % | peak RSS |
|---|---|---|---|---|
| 0 | `original_protean` (reused) | 42:06.78 | 740 % | 6.21 GiB |
| 0 | beta 0.00 | 41:17 | 740 % | 6.21 GiB |
| 0 | beta 0.25 | 42:11 | 738 % | 6.21 GiB |
| 0 | beta 0.50 | 42:57 | 737 % | 6.21 GiB |
| 0 | beta 0.75 | 43:45 | 733 % | 6.21 GiB |
| 0 | beta 1.00 | 42:08 | 737 % | 6.21 GiB |
| 1 | `original_protean` | 41:21.16 | 738 % | 7.48 GiB |
| 1 | beta 0.00 | 40:38.89 | 744 % | 7.48 GiB |
| 1 | beta 0.25 | 40:32.74 | 744 % | 7.48 GiB |
| 1 | beta 0.50 | 40:43.99 | 743 % | 7.48 GiB |
| 1 | beta 0.75 | 40:41.36 | 745 % | 7.48 GiB |
| 1 | beta 1.00 | 41:08.86 | 744 % | 7.48 GiB |
| 2 | `original_protean` | 41:09.70 | 743 % | 6.45 GiB |
| 2 | beta 0.00 | 40:40.14 | 742 % | 6.45 GiB |
| 2 | beta 0.25 | 41:25.22 | 740 % | 6.45 GiB |
| 2 | beta 0.50 | 40:23.42 | 742 % | 6.45 GiB |
| 2 | beta 0.75 | 41:05.82 | 740 % | 6.45 GiB |
| 2 | beta 1.00 | 42:20.66 | 741 % | 6.45 GiB |
- Seed-0 runs were reused from Session 5 and were not re-run.
- New compute this session: 12 runs, ≈ 8 h 11 m wall-clock (seed-1 chain ≈ 4 h 04 m,
seed-2 chain ≈ 4 h 07 m), single-run sequential.
- RSS rises to ≈ 7.48 GiB for seed 1; this is dataset/partition dependent, not
beta dependent, and well within the 29 GiB host.
- Preprocessing/load remains negligible next to training. No warnings, no client
failures, 9/9 global prototypes in every round, no retries.
---
## K. Limitations
- **Only X-IIoTID**, **only alpha = 0.50**, **only three project-defined seeds**
(`{0,1,2}`). No cross-alpha and no cross-dataset conclusion.
- The `S_j` participant/denominator rule
(`S_j = {i | N_ij > 0 and valid C_ij}`, equal weights in the baseline) remains a
**REPRODUCTION ASSUMPTION**, material in 53/162 class-condition cells (32.7 %).
The `beta = 0` comparison against `original_protean` is conditional on both modes
sharing this rule and does **not** prove `beta = 0` reproduces original PROTEAN.
- Train/validation/test adaptation (70/10/20 stratified) differs from the paper's
literal 80/20; the validation eval partition is a project decision.
- **No global optimum claim.** `beta = 0.75` is only the best among the five tested
candidates under pooled validation for this one condition, and the between-seed
variance is comparable to the between-beta spread.
- The selected-beta test numbers are single-condition and must not be read as
generalisation; no test metric was used for selection.
- The union of rare classes (and hence global rare support) differs by seed because
partitions differ; only the per-client-mean metric is directly comparable across
seeds.
- No 5G-NIDD beta sweep was run.
---
## L. `beta = 0` equivalence vs `original_protean` (seeds 1 and 2)
| check | seed 1 | seed 2 |
|---|---|---|
| same manifest / split / hashes | yes | yes |
| round-0 snapshot byte-identical | yes (`b7f61c99…`) | yes (`5b749f41…`) |
| `train_support_Nij.csv`, `characterization.csv`, `rare_unseen.csv` identical | yes | yes |
| aggregation arithmetic | mathematically equivalent (`vec/count` vs `vec·(1/count)`) | same |
| test / history / per-client / per-class | differ slightly (float ordering fed back through round≥1) | same |
Classified as **mathematically equivalent + floating-point numerical divergence**;
**no semantic discrepancy** was found in either seed. Mechanism is identical to
Session 5: round 0 is byte-identical, the ~1 ULP ordering difference enters the
round-1 prototype-alignment training and trajectories diverge slowly.
---
## M. Analysis tooling
Read-only, no retraining:
- `scripts/analyze_multiseed_beta.py` — pooled validation tables, per-seed
diagnostic selection, client validation summary/detail, cross-seed prototype-effect
aggregation, test descriptive table, and `...__selection_summary.json`.
- `scripts/analyze_prototype_effect.py` — per-seed direct prototype-effect CSVs from
the fixed round-0 snapshot.
Outputs under `artifacts/analysis/xiiotid__alpha0.5__multiseed__*` and the per-seed
`...__prototype_snapshot__prototype_effect.csv` / `...__prototype_weights.csv`.
+319
View File
@@ -0,0 +1,319 @@
# Session 6 Post-Experiment Audit
Status: **PASS WITH MINOR CORRECTIONS.** Independent recomputation from machine-readable
artifacts confirms the Session 6 claims. No discovered issue changes `beta_star` or the
scientific interpretation. One provenance gap was closed (runtime CSV) and one handoff
wording nuance was clarified. **No training was run, no aggregation semantics changed, no
test-based selection occurred.**
Audit performed: 2026-09-25. Audited HEAD: `50f2da72166e4f609077d65b4916f745da5e8e3b`.
Companion documents: `docs/research/MULTISEED_BETA_VALIDATION.md`,
`docs/research/EXPERIMENT_PROTOCOL.md`, `docs/research/PAIRED_BETA_VALIDATION.md`,
`docs/baseline/BASELINE_VALIDATION.md`, `docs/handoff/SESSION_HANDOFF.md`.
---
## A. Audit scope
Independently verify the Session 6 claim for:
- dataset `xiiotid`, alpha `0.50`, seeds `{0,1,2}`, beta `{0, 0.25, 0.5, 0.75, 1.0}`;
- PRIMARY pooled-validation selection `beta_star = 0.75`, using validation per-client
rare-class macro-F1 averaged within seed then across seeds.
The audit recomputed metrics from run artifacts and re-derived the selection; it did not
repeat the report. No new condition was run.
---
## B. Run inventory
18 full-data runs were enumerated from `results/*/metadata.json` (15 `support_weighted` +
3 `original_protean`). Every expected run is present, none duplicated, none partial.
| seed | runs present | metadata fields | manifest reload | smoke | missing artifacts |
|---|---|---|---|---|---|
| 0 | original + beta {0,.25,.5,.75,1} | all correct | true | false | none |
| 1 | original + beta {0,.25,.5,.75,1} | all correct | true | false | none |
| 2 | original + beta {0,.25,.5,.75,1} | all correct | true | false | none |
Verified per run: dataset=xiiotid, alpha=0.5, seed, aggregation mode, beta, clients=10,
rounds=10, local_epochs=3, batch=256, lr=1e-3, mu=0.1, lambda=1.0, `manifest_reload_ok=true`,
`smoke=false`. No run exists for any alpha ≠ 0.50 and no `5gnidd...support_weighted` run
exists.
Informational: the seed-0 `original_protean` `metadata.json` lacks the `beta` key because
it predates the Session-5 validation-artifact change; the other 17 runs carry `beta`
(`null` for `original_protean`). No scientific impact.
---
## C. Pairing / manifest integrity
Within each seed, all six runs share one manifest content hash and the deterministic
artifacts are byte-identical:
| seed | manifest content hash | round-0 snapshot SHA-256 (all 6 runs) | support/characterization/rare_unseen identical |
|---|---|---|---|
| 0 | `e81a6538…` | `e27f41b3a62eb83c…` | yes |
| 1 | `54ea1df1…` | `b7f61c995e97474c…` | yes |
| 2 | `be881ac6…` | `5b749f4185e11fc7…` | yes |
`train_support_Nij.csv` equals the manifest support matrix for each seed. `history.json`
and `test_global_metrics.json` differ across runs of a seed, exactly as expected because
beta/original changes training. Pairing is proven by hashes, not filenames.
Reproducing each manifest from the raw dataset (`prepare_dataset` → `reproduce_train_assignments`):
all three return `reproduce_ok=True`, `support_match=True`, sizes 569,909 / 81,414 / 162,831,
10 clients; train/validation/test are **pairwise disjoint** and their union is the whole
cleaned dataset (814,154 rows).
---
## D. Validation metric recomputation
Recomputed from raw artifacts (no report numbers copied):
- seed-level primary score = mean over eligible clients of `val_per_client.csv`
`rare_class_macro_f1`; **equals `val_selection.json` per-client-mean exactly (diff 0.0)**;
- global validation accuracy / macro-F1 / weighted-F1 / rare macro-F1 / rare recall
recomputed **independently from `val_confusion_matrix.json`**; **equal to
`val_global_metrics.json` exactly (diff 0.0)**.
| seed | beta | eligible | recomputed seed mean | report claim |
|---|---|---|---|---|
| 0 | 0.00 / 0.25 / 0.50 / 0.75 / 1.00 | 10 each | 0.557340 / 0.571787 / 0.585762 / 0.578003 / 0.551443 | same |
| 1 | 0.00 / 0.25 / 0.50 / 0.75 / 1.00 | 10 each | 0.628624 / 0.636205 / 0.645189 / 0.655160 / 0.683117 | same |
| 2 | 0.00 / 0.25 / 0.50 / 0.75 / 1.00 | 10 each | 0.447118 / 0.458310 / 0.471015 / 0.504937 / 0.485877 | same |
All recomputed values agree with the report to ≤ 4e-7 (six-decimal formatting precision).
Limitation (unavoidable, documented): the *per-client predictions* cannot be recomputed
because the trained model is not persisted and retraining is forbidden by the audit. The
per-client metric is therefore verified via (a) its defining source code
(`evaluation/metrics.py::rare_class_metrics` over each client's training-derived rare ids),
(b) exact equality of its mean with `val_selection.json`, and (c) exact equality of the
global union-rare metric recomputed from the confusion matrix. This does not affect
`beta_star`, which depends only on the seed-level means.
---
## E. Rare-class integrity
Per-client rare classes recomputed from training `N_ij` (two smallest positive-support
attack classes, tie by class id) and compared to `rare_unseen.csv` flags:
- **0 mismatches** across all 30 client-seed cells.
- Benign class (`Normal`) never flagged rare/unseen; rare and unseen never overlap.
- Unseen = attack class with `N_ij = 0`; union of rare classes derived from training equals
both `val_global_metrics.evaluated_rare_classes` and
`test_global_metrics.evaluated_rare_classes` for every seed — i.e. validation/test labels
did **not** influence rare-class identity.
- Eligible clients = **10 in every seed** (no client excluded); every client has exactly 2
rare classes; no client has only 1 attack class.
- Union rare classes: seed 0 = `{C&C, Exfiltration, Exploitation, Lateral_movement,
Tampering, Weaponization}`; seeds 1 and 2 additionally include `RDOS`. Hence global test
rare support 25,454 (seed 0) vs 53,706 (seeds 1,2).
---
## F. Pooled-selection recomputation
Computed from the independently recomputed seed-level scores (not from the report).
| beta | pooled mean | sample std (ddof=1) | pop std (ddof=0) | min | max |
|---|---|---|---|---|---|
| 0.00 | 0.544361 | 0.091447 | 0.074666 | 0.447118 | 0.628624 |
| 0.25 | 0.555434 | 0.090068 | 0.073540 | 0.458310 | 0.636205 |
| 0.50 | 0.567322 | 0.088539 | 0.072292 | 0.471015 | 0.645189 |
| **0.75** | **0.579367** | **0.075121** | 0.061336 | 0.504937 | 0.655160 |
| 1.00 | 0.573479 | 0.100450 | 0.082017 | 0.485877 | 0.683117 |
- Recompute **matches the report** (mean and sample std) to ≤ 4e-7.
- `argmax` of the pooled validation score = **0.75** (no tie).
- Because every seed has the same eligible count (10), the required
"average within seed, then across seeds" equals a flat average over all clients; the
flat-pool sensitivity check is numerically identical here. The required hierarchy was
still the implemented procedure.
- **Std convention:** the report uses **sample** std (ddof=1), explicitly labelled
`std (sample)` in the report table. Other std uses in the repository
(`characterization.py`, `preprocessing.py`) are population std (ddof=0) for support
statistics. No prior multi-seed std convention existed, and the label is present, so no
convention was silently changed. Recommendation: keep the "(sample)" label wherever the
pooled std is quoted.
---
## G. Validation/test leakage audit
- `run_baseline.py`: the `val_selection` payload (lines 297–313) is built **only** from
`val_rare`, `val_metrics`, `val_per_client_rows`. Test variables appear in a separate
block and feed only test artifacts. `test_used_for_selection: false` and
`selection_split: "validation"` are recorded for all 15 `support_weighted` runs.
- `analyze_multiseed_beta.py`: the PRIMARY selection block (lines 176–208) consumes only
`per_seed_rows`, which are built from `val_selection.json`. `test_rows` are read in a
later block (line 348+) and are never referenced by the selection logic or tie-break.
The pooled argmax is reproduced by rerunning this read-only script.
- No code path sorts/filters candidates by a test field.
Conclusion: selection is **programmatic and validation-only**; the boolean flag is a
literal but is corroborated by the independent recomputation of `beta_star` from validation
artifacts alone. `test_used_for_selection = false` is upheld.
---
## H. Test metric recomputation
`beta_star = 0.75` test metrics recomputed independently from `test_confusion_matrix.json`
(max diff vs the JSON = 0.0), then averaged across seeds with sample std:
| metric | recomputed mean ± std | report claim |
|---|---|---|
| rare-class macro-F1 | 0.665503 ± 0.026685 | 0.665503 ± 0.026685 |
| rare-class recall | 0.862583 ± 0.013921 | 0.862583 ± 0.013921 |
| macro-F1 | 0.731169 ± 0.006234 | 0.731169 ± 0.006234 |
| accuracy | 0.906392 ± 0.018450 | 0.906392 ± 0.018450 |
| weighted-F1 | 0.933632 ± 0.011230 | 0.933632 ± 0.011230 |
`original_protean` recomputed identically: rare macro-F1 0.642520 ± 0.028683; rare recall
0.876957 ± 0.010771; macro-F1 0.708965 ± 0.001172; accuracy 0.899454 ± 0.008398;
weighted-F1 0.924733 ± 0.006245 — all match the report. No superiority claim is made.
---
## I. beta = 0 equivalence audit
For seeds 0, 1 and 2, `original_protean` vs `support_weighted beta=0`:
| property | seed 0 | seed 1 | seed 2 |
|---|---|---|---|
| same manifest/split | yes | yes | yes |
| round-0 snapshot byte-identical | yes | yes | yes |
| support/characterization/rare_unseen identical | yes | yes | yes |
| independent aggregation diff (`ΣC/count` vs `Σ(1/count)C`) | ≤2.2e-16 | ≤2.2e-16 | ≤4.4e-16 |
| test/history/per-client | differ slightly | differ slightly | differ slightly |
| classification | **B** | **B** | **B** |
History divergence begins after round 0 (first-round rare macro-F1 abs diff 0.0 at round 0,
growing to ~2e-4–3.6e-3 by round 9, max 6e-3–1.8e-2): floating-point ordering feedback, not
a semantic change. Class **B** (semantically equivalent + floating-point divergence).
Equivalence remains conditional on the shared `S_j` reproduction assumption.
---
## J. Direct-effect audit
Recomputed independently from the fixed round-0 snapshots (own `tensordot` implementation):
- per `(class, beta)` displacement matches `...__prototype_effect.csv` **exactly**
(all classes, all betas, all seeds); rank order per seed matches;
- normalised weights sum to 1 for every beta; `support_min/max` match the CSV;
- the same snapshot is reused across beta candidates (byte-identical hashes, §C).
Cross-seed mean `||Cbar(1) − Cbar(0)||`: Lateral_movement 2.724 (std 1.035), RDOS 2.631
(1.165), Weaponization 2.561 (1.228), Tampering 2.354 (1.165), Exploitation 2.344 (0.412),
Exfiltration 2.020 (0.502), C&C 1.694 (0.505), Normal 1.557 (0.548), Reconnaissance 1.002
(0.261). All match the report. Rank order is seed-sensitive (seed 0 top = Weaponization,
seed 1 = RDOS, seed 2 = Lateral_movement). Displacement is a direct-aggregation measure
only; no detection causality is inferred.
---
## K. Client-level audit
Recomputed from `val_per_client.csv` deltas vs `beta = 0`:
| seed | beta | improve | degrade | unchanged (ties) | total | report table |
|---|---|---|---|---|---|---|
| 0 | 0.75 | 7 | 3 | 0 | 10 | 7/3/0 |
| 1 | 0.75 | 8 | 1 | 1 (client 7) | 10 | 8/1/1 |
| 2 | 0.75 | 8 | 1 | 1 (client 4) | 10 | 8/1/1 |
| 0 | 1.00 | 4 | 6 | 0 | 10 | 4/6/0 |
| 1 | 1.00 | 9 | 0 | 1 (client 7) | 10 | 9/0/1 |
The report's §H table already carries an explicit `unchanged` column that accounts for the
ties; only the handoff §9 shorthand ("8/1") omitted them (clarified, §N). Mean Δ / min / max
recomputed match report §H for all six shown cells. `client_validation_detail.csv` deltas
match recomputation (0 mismatches). `min positive N_ij` vs ΔF1 is confirmed **non-monotonic**
(e.g. seed 0 N=1 → −0.021, N=2 → +0.093, N=10 → −0.036; seed 2 N=7 → +0.163, N=10 → +0.177,
N=135 → +0.096). Client ids are treated as per-seed realisations, not one population.
---
## L. Artifact provenance
Every reported quantity traces to a machine-readable artifact or a deterministic
recomputation.
| Reported quantity | Source artifact(s) | Derivation | Verified |
|---|---|---|---|
| run existence / config | `results/<run>/metadata.json` | direct read | yes |
| manifest content/partition/split hashes | `artifacts/partitions/xiiotid__alpha0.5__seed{0,1,2}.json` | module hash + `reproduce_train_assignments` | yes |
| pairing proof | `artifacts/analysis/...__prototype_snapshot.json` | sha256 across 6 runs/seed | yes |
| seed-level validation score | `results/.../val_per_client.csv` + `val_selection.json` | mean of per-client rare macro-F1 | yes |
| validation global metrics | `results/.../val_confusion_matrix.json` | independent numpy recompute | yes |
| pooled mean/std, `beta_star` | recomputed seed scores | mean/`stdev` over seeds, argmax | yes |
| test metrics (beta 0.75, original) | `results/.../test_confusion_matrix.json` | independent recompute + mean/std | yes |
| beta=0 equivalence | original vs beta0 artifacts/snapshots | sha256 + independent aggregation | yes |
| direct-effect displacement | snapshots + `...__prototype_effect.csv` | independent `tensordot` recompute | yes |
| client improve/degrade | `results/.../val_per_client.csv` | deltas vs beta0 | yes |
| rare-class identity | `artifacts/analysis/...__rare_unseen.csv` + manifest | recompute definition | yes |
| runtime / RSS | `artifacts/analysis/xiiotid__alpha0.5__multiseed__runtime.csv` (added by this audit; seed 0 from Session-5 docs, seeds 1–2 from `/usr/bin/time -v` logs) | parse logs | yes |
---
## M. Source / data integrity
- HEAD `50f2da72166e4f609077d65b4916f745da5e8e3b`; working-tree source blobs for
`prototype_aggregation.py` (`c298967a…`) and `scripts/run_baseline.py` (`2b469e35…`)
equal their HEAD blobs. Only docs/analysis files are modified/untracked.
- `planningawal/` unchanged (`git status`/`git diff` empty).
- Raw dataset SHA-256 unchanged: X-IIoTID `7b929005…`, 5G-NIDD `fa36f808…`.
- `artifacts/partitions/` unmodified (tracked, no diff); manifests reproduced from raw data.
- `equal_available` mode: absent. Beta candidates: `(0.0, 0.25, 0.50, 0.75, 1.00)` unchanged.
- No alpha ≠ 0.50 result directory; no 5G-NIDD `support_weighted` run.
---
## N. Deviations / corrections found
1. **Provenance gap (corrected).** Runtime/RSS numbers existed only in Markdown and
ephemeral `/tmp` logs. Added machine-readable
`artifacts/analysis/xiiotid__alpha0.5__multiseed__runtime.csv` (seed 0 from Session-5
docs; seeds 1–2 parsed from `/usr/bin/time -v` logs). No scientific value changed.
2. **Handoff wording (clarified).** Handoff §9 wrote "8/1 (seed 1), 8/1 (seed 2)" omitting
the one unchanged client each; corrected to "7/3/0, 8/1/1, 8/1/1" to match the report
§H table, which already showed the `unchanged` column.
3. **Std convention (documented, not changed).** Pooled std is sample (ddof=1), labelled
"(sample)" in the report; other repo std uses are population. No prior multi-seed
convention existed. Kept sample, labelled.
4. **Metadata schema (informational).** Seed-0 `original_protean` lacks the `beta` key
(older code); no impact.
5. **Audit-side only:** two first-pass audit scripts had class-list/order bugs
(client-0 class union; support-ordered vs id-ordered comparison); corrected within the
audit. These were audit-script defects, not repository defects.
No arithmetic error, mislabeled metric, or incorrect derived CSV was found in the Session 6
report or analysis artifacts.
---
## O. Final audit verdict
**PASS WITH MINOR CORRECTIONS.**
- Run inventory, pairing, manifest/split/data integrity: **PASS**.
- Validation metric recomputation (seed-level and global): **PASS** (exact).
- Rare-class integrity: **PASS** (0 mismatches; training-derived; no val/test influence).
- Pooled selection: **PASS**; independent `beta_star = 0.75` using validation only.
- Validation/test leakage: **PASS**; selection reads validation only and is reproducible.
- Test summaries (selected beta and `original_protean`): **PASS**(exact).
- beta=0 equivalence: **PASS**, class **B** (semantically equivalent + float divergence).
- Direct-effect and client-level analyses: **PASS** (independent recompute exact).
- Source/data integrity, pytest (75 passed, 0 failed, 0 skipped, 0 warnings): **PASS**.
The corrections were provenance/wording only and do not affect `beta_star` or the
scientific interpretation. Session 6 is **safe to checkpoint** with the documented
limitations (single dataset, single alpha, three project-defined seeds, weak effect
relative to seed variance, `S_j` reproduction assumption, no global-optimum claim).