chore: checkpoint PROTEAN research planning and architecture

This commit is contained in:
willtanoe committed 2026-09-24 17:08:56 +00:00
commit 2cc6a9859e
22 files changed
+2138

No files matched your search

+192
View File
@@ -0,0 +1,192 @@
# Dataset Inspection
Status: completed inspection (read-only)
Inspected: 2026-09-24
Method: full load with pandas 3.0.6 / Python 3.12 in an isolated temporary virtualenv.
The raw files are external source data. They were only read; paths are configured in
`configs/datasets/datasets.yaml`. No files were moved, renamed, overwritten, or copied
into the repository.
## Integrity (recorded before and used for final verification)
| Dataset | Path | Size (bytes) | SHA-256 |
|---|---|---|---|
| X-IIoTID | `~/datasets/X-IIoTID/X-IIoTID-dataset.csv` | 355,308,902 (338.8 MiB) | `7b9290057ee42e784da3c0d84b781815502c9205c74175c96374e71a5ffd98a0` |
| 5G-NIDD | `~/datasets/5G-NIDD/5G-NIDD.csv` | 275,265,610 (262.5 MiB) | `fa36f80859585f474504ca69eb951a079b35145537976c86b00aae3aab46ee59` |
Source filesystem modification times: X-IIoTID `2021-10-07`, 5G-NIDD `2023-02-18`
(i.e. unchanged by this session).
---
## 1. X-IIoTID
- Shape: **820,834 rows × 68 columns**.
- In-memory (deep) usage when loaded: ~671 MB.
- Encoding: UTF-8.
- dtypes: 52 string columns, 9 int64, 7 bool.
- Many numeric-looking columns are stored as strings because they contain the missing
placeholder `'-'` (e.g. `Duration`, `Scr_bytes`, `Scr_pkts`, `Avg_*`, `Std_*`).
### Label columns
`class1` (fine-grained, 19 values):
| class1 | count |
|---|---|
| Normal | 421,417 |
| RDOS | 141,261 |
| Scanning_vulnerability | 52,852 |
| Generic_scanning | 50,277 |
| BruteForce | 47,241 |
| MQTT_cloud_broker_subscription | 23,524 |
| Discovering_resources | 23,148 |
| Exfiltration | 22,134 |
| insider_malcious | 17,447 |
| Modbus_register_reading | 5,953 |
| False_data_injection | 5,094 |
| C&C | 2,863 |
| Dictionary | 2,572 |
| TCP Relay | 2,119 |
| fuzzing | 1,313 |
| Reverse_shell | 1,016 |
| crypto-ransomware | 458 |
| MitM | 117 |
| Fake_notification | 28 |
`class2` (general classes, 10 values) — the label used by PROTEAN:
| class2 | count |
|---|---|
| Normal | 421,417 |
| RDOS | 141,261 |
| Reconnaissance | 127,590 |
| Weaponization | 67,260 |
| Lateral _movement | 31,596 |
| Exfiltration | 22,134 |
| Tampering | 5,122 |
| C&C | 2,863 |
| Exploitation | 1,133 |
| crypto-ransomware | 458 |
`class3` (binary, 2 values): `Normal` 421,417; `Attack` 399,417.
- Benign label: **`Normal`**.
- Attack labels (general, after removing `crypto-ransomware` as the paper does):
`RDOS`, `Reconnaissance`, `Weaponization`, `Lateral _movement`, `Exfiltration`,
`Tampering`, `C&C`, `Exploitation` → **8 attack classes + Normal = 9 classes**, matching
the PROTEAN `K` for this dataset.
### Missing data / anomalies
- True empty cells: `Date` 331 (0.04%), `Timestamp` 297 (0.04%). All other columns have
zero empty cells.
- Documented missing placeholder `'-'` appears in 42 columns. Highest counts:
`Scr_bytes`, `Des_bytes`, `total_bytes`, `paket_rate`, `byte_rate`, `Scr_bytes_ratio`,
`Des_bytes_ratio` (≈223,871 each), `Scr_pkts`/`Des_pkts`/`Scr_ip_bytes`/`Des_ip_bytes`/
`total_packet`/`*_ratio` (≈152,743), `Duration` (75,761), and the `Avg_*`/`Std_*` system
columns (≈479–526).
- Exact duplicate rows: **6,222**.
- Label anomaly: the value is `Lateral _movement` (extra space before the underscore),
whereas the paper writes "Lateral Movement". Label normalisation is required.
- Fine-grained typos exist (`insider_malcious`). Not used by the general-class study but
relevant if `class1` is ever used.
### Non-feature / identifier fields
`Date`, `Timestamp` (temporal), `Scr_IP`, `Scr_port`, `Des_IP`, `Des_port` (network
endpoints). These must not be model features without an explicit documented decision.
`Protocol`/`Service` are categorical but plausibly valid flow features.
---
## 2. 5G-NIDD
- Shape: **1,215,890 rows × 52 columns**.
- In-memory (deep) usage when loaded: ~551 MB.
- Encoding: UTF-8.
- dtypes: 32 float64, 12 int64, 8 string columns.
- Column 0 has an empty header (an unnamed row index).
### Label columns
`Label` (binary): `Malicious` 738,153; `Benign` 477,737.
`Attack Type` (9 values) — the multi-class label used by PROTEAN:
| Attack Type | count |
|---|---|
| Benign | 477,737 |
| UDPFlood | 457,340 |
| HTTPFlood | 140,812 |
| SlowrateDoS | 73,124 |
| TCPConnectScan | 20,052 |
| SYNScan | 20,043 |
| UDPScan | 15,906 |
| SYNFlood | 9,721 |
| ICMPFlood | 1,155 |
`Attack Tool` (6 values): `Benign` 477,737; `Hping3` 468,216; `Goldeneye` 140,812;
`Torshammer` 59,870; `Nmap` 56,001; `Slowloris` 13,254.
- Benign label: **`Benign`**.
- Attack classes: 8 (all `*Flood`/`*Scan`/`*DoS`), giving **9 classes including Benign**,
matching PROTEAN's `K` for this dataset.
### Missing data / anomalies
Large, structurally correlated missingness (likely attack-type dependent):
| Column | missing | % |
|---|---|---|
| dVid | 1,213,881 | 99.83% |
| sVid | 1,101,319 | 90.58% |
| DstWin | 1,038,812 | 85.44% |
| DstTCPBase | 985,843 | 81.08% |
| SrcWin | 973,470 | 80.06% |
| dTos, dHops, dDSb, dTtl | 943,067 | 77.56% |
| DstGap, SrcGap, SrcTCPBase | 937,219 | 77.08% |
| sHops, sTtl, sTos, sDSb | 214 | 0.02% |
- Exact duplicate rows: **1**.
- No `'-'` placeholder tokens; missingness is real NaN.
### Non-feature / identifier fields
Empty-header index column (`""`), `Seq` (sequence number), `Offset` (flow ordering).
`Label`, `Attack Type`, `Attack Tool` are targets and must not be features.
`Proto`, `sDSb`, `dDSb`, `Cause`, `State` are categorical features.
---
## 3. Mapping to PROTEAN assumptions
| PROTEAN assumption | X-IIoTID | 5G-NIDD | Match |
|---|---|---|---|
| 9 attack classes (incl. benign) | Normal + 8 attacks after dropping `crypto-ransomware` | Benign + 8 attacks | Yes |
| Multi-class label | `class2` | `Attack Type` | Yes |
| Benign present | `Normal` | `Benign` | Yes |
| Dirichlet over classes | feasible | feasible | Yes |
| 80/20 train/test | feasible | feasible | Yes |
| Time-agnostic Dirichlet | `Date`/`Timestamp` exist but unused | `Seq`/`Offset` exist but unused | Paper is time-agnostic; no temporal split |
### Mismatches / items to freeze
1. `Lateral _movement` typo must be normalised before class-index mapping.
2. PROTEAN's exact feature preprocessing is unspecified (encoding, scaling, missing
handling). This must be frozen here and reported as a reproduction deviation.
3. X-IIoTID numeric columns carry `'-'`; coercion strategy must be explicit and identical
across aggregation variants.
4. Exact duplicate rows exist (X-IIoTID 6,222; 5G-NIDD 1). Dropping them is a decision that
must be frozen and applied identically to all variants.
5. 5G-NIDD's high structural missingness means imputation/dropping choices strongly affect
features; these choices must be recorded.
6. Identifier/temporal fields should be excluded from features by default.
## 4. Reproduction of this inspection
The inspection script is not stored in the repository (to keep the bootstrap minimal), but
was a straight `pandas.read_csv(..., low_memory=False)` plus `value_counts`, `isna`, and
`duplicated` per dataset. Re-running it must reproduce the counts above for the recorded
hashes. A future `scripts/inspect_datasets.py` may formalise it (see implementation plan).
+177
View File
@@ -0,0 +1,177 @@
# Experiment Protocol
Status: frozen design (to be executed in later sessions)
Machine-readable counterpart: `configs/experiments/experiment_protocol.yaml`
Values marked **PROVISIONAL** require confirmation before any scientific sweep. Values
marked **FROZEN** must remain identical across every aggregation variant.
---
## 1. Datasets
FROZEN:
| Key | Dataset | Label column | Benign | Dropped | Classes `K` |
|---|---|---|---|---|---|
| `xiiotid` | X-IIoTID | `class2` | `Normal` | `crypto-ransomware` | 9 |
| `5gnidd` | 5G-NIDD | `Attack Type` | `Benign` | — | 9 |
Details and mismatches: `docs/research/DATASET_INSPECTION.md`.
`crypto-ransomware` is removed exactly as the paper does (very few valid instances).
## 2. Non-IID partition (Dirichlet)
FROZEN: symmetric Dirichlet over classes, `num_clients = 10`, `alpha ∈ {0.75, 0.50, 0.25}`.
- Partitions are generated from the **training split only**.
- Class proportions are drawn per client from `Dirichlet(alpha)` and used to allocate
training samples per class to clients.
- The exact Dirichlet implementation (per-class client allocation vs per-client class
sampling) must be fixed and documented, because it changes realised `N_ij`. `PROVISIONAL`.
- Success criterion for reproducing the paper's setting: at low alpha, several clients miss
several classes, consistent with the paper's reported counts (e.g. X-IIoTID `alpha=0.25`:
8/10 clients miss 1–5 classes).
## 3. Splits
- Test: 20% of each dataset (paper uses 80/20). FROZEN.
- Validation: a separate split used only for beta selection. The paper has **no** validation
set, so this is an explicit project addition. `PROVISIONAL` fraction and procedure.
- Requirement: train/validation/test come from a single deterministic split with a recorded
seed, and the same split is reused for every beta variant.
## 4. Seeds
- The paper averages over three runs with unnamed seeds. `PROVISIONAL` set: `{0, 1, 2}`.
- Once frozen, the seed set is identical across all aggregation variants.
- Each seed deterministically controls: split, Dirichlet partition, model initialisation,
batch order/shuffling.
- A beta run MUST NOT derive a new partition; it loads the manifest for
`(dataset, alpha, seed)`.
## 5. Beta candidates and modes
FROZEN: `beta ∈ {0.00, 0.25, 0.50, 0.75, 1.00}`, one beta for all classes and clients.
Modes:
- `original_protean` — verified baseline semantics. Not yet encodable (see baseline
investigation). This is the baseline to reproduce.
- `support_weighted` — `w_ij = N_ij^beta`, normalised over `S_j` (proposed mechanism).
- `equal_available` — equal weights over `S_j`; a methodological control introduced only if
the baseline participant rule differs from `S_j`. Not to be presented as a novelty.
`S_j = { i | N_ij > 0 and a valid local prototype C_ij exists }`.
## 6. Partition persistence
Each `(dataset, alpha, seed)` produces one persisted manifest containing at least:
- dataset name, alpha, seed, num_clients, K;
- per-client sample indices or a reproducible reference plus a content hash;
- per-client `N_ij` matrix `[client][class]`;
- class label mapping;
- split indices for train/validation/test;
- preprocessing/config hash and code version.
Manifests live under `artifacts/partitions/`. Reloading a manifest must reproduce the same
partition (tested). Training reads manifests; it does not regenerate them.
## 7. Rare and unseen classes
Derived from the **training** split of the manifest, before beta selection.
- Rare classes on client `i`: the two **attack** classes with the smallest positive support
`N_ij > 0`. If exactly one attack class is available, use one. If none, the client is
excluded from the rare-class aggregate.
- Unseen classes on client `i`: attack classes with `N_ij = 0`. Evaluated separately from
rare classes; never merged.
- The benign class is not a rare class.
Persisted metadata (minimum one row per `(dataset, alpha, seed, client, class)`):
`dataset, alpha, seed, client_id, class, N_ij, rare_flag, unseen_flag`.
## 8. Validation-based beta selection
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 a pre-declared rule, e.g. smaller beta). 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. The selection code must not have access to test
outputs; test evaluation is a separate step keyed on the selected beta.
## 9. Required metrics
Per dataset/alpha/seed/beta and per client, record:
- rare-class macro-F1 (primary selection metric);
- rare-class recall;
- overall macro-F1;
- accuracy (for comparison with the paper);
- per-class precision/recall/F1 and confusion counts;
- per-client metrics.
Also record the validation/test rare-class support counts, so rare-class metrics are never
read without their sample sizes. Results are reported as mean and variation across seeds.
## 10. Non-IID characterization
For every manifest, record at least: `N_ij`, class total, number of clients containing class
`j`, `min N_ij`, `max N_ij`, `mean N_ij`, `std N_ij`, coefficient of variation, and
`max/min` ratio where defined. Alpha alone is insufficient; different seeds realise
different distributions.
## 11. Direct prototype analysis
Separate the direct mathematical aggregation effect from the end-to-end training effect.
- During a run, persist at least one round's fixed `C_ij` and `N_ij` snapshot.
- Reusing those snapshots, compute `Cbar_j(beta)` for several beta without retraining.
- Report `|| Cbar_j(beta) - Cbar_j(0) ||`; optionally the normalised aggregation weights,
distances to contributing prototypes, pairwise global-prototype displacement, and support
imbalance.
- This analysis complements, and never replaces, end-to-end evaluation.
## 12. Paired comparison guarantee
For each `(dataset, alpha, seed)`, every beta variant and every mode reuses:
- the same partition manifest (content-hash verified);
- the same train/validation/test split;
- the same class mapping;
- the same model initialisation policy;
- the same hyperparameters;
- the same client membership.
A beta run that regenerates a fresh Dirichlet partition is a protocol violation.
## 13. Frozen vs provisional summary
| Item | Value | Status |
|---|---|---|
| Datasets | X-IIoTID, 5G-NIDD | FROZEN |
| Clients | 10 | FROZEN |
| Alpha | 0.75, 0.50, 0.25 | FROZEN |
| Beta candidates | 0, 0.25, 0.50, 0.75, 1.00 | FROZEN |
| Test fraction | 0.20 | FROZEN |
| Rounds / local epochs | 10 / 3 | FROZEN |
| `mu` | 0.1 | FROZEN |
| `lambda` | unknown | PROVISIONAL |
| Batch size / learning rate | unknown | PROVISIONAL |
| Validation fraction / procedure | unknown | PROVISIONAL |
| Seeds | 0, 1, 2 | PROVISIONAL |
| Dirichlet allocation algorithm | unknown | PROVISIONAL |
| Preprocessing recipe | unknown | PROVISIONAL |
| Missing-class baseline rule | unknown | PROVISIONAL / blocked on baseline |
The provisional items must be resolved and recorded here before the first sweep. Until then
only smoke tests are permitted.
+217
View File
@@ -0,0 +1,217 @@
# Implementation Plan
Status: design document (no full training implemented in this session)
Scope: architecture for the server-side prototype-aggregation study on top of a reproduced
PROTEAN baseline.
Governing constraint: only server-side prototype aggregation changes. Architecture, model
aggregation, loss terms, and inference follow the verified baseline
(`docs/baseline/PROTEAN_BASELINE_INVESTIGATION.md`).
---
## 1. Repository layout
```
perdalam-protean/
├── planningawal/ # IMMUTABLE source material (read-only)
│ └── PROTEAN_Revisi_Lurus.md
├── docs/
│ ├── research/ # spec interpretation, datasets, plan, protocol
│ ├── baseline/ # PROTEAN baseline investigation
│ └── handoff/ # session handoff (canonical continuation point)
├── configs/
│ ├── datasets/datasets.yaml
│ ├── experiments/experiment_protocol.yaml
│ └── models/protean_cnn.yaml
├── src/protean_research/
│ ├── data/ # (planned) loading, cleaning, partitions, manifests
│ ├── federated/ # (planned) client update, round orchestration
│ ├── aggregation/ # (exists) pure prototype aggregation + modes
│ ├── models/ # (planned) CNN embedding/classifier
│ ├── evaluation/ # (planned) metrics, rare/unseen, per-client
│ └── utils/ # (exists) config/path resolution; (planned) seeding
├── scripts/ # (planned) thin CLI entry points
├── tests/ # (exists: none yet; planned aggregation tests)
├── artifacts/ # generated: partitions, processed, checkpoints, analysis
└── results/ # experiment result tables/plots
```
Existing code at the start of this session: `configs/*`, `src/protean_research/__init__.py`,
`src/protean_research/aggregation/*`, `src/protean_research/utils/config.py`. Everything
marked "(planned)" is future work.
## 2. Configuration
Centralised YAML, loaded through `src/protean_research/utils/config.py`.
- `configs/datasets/datasets.yaml` — raw paths (external, `~`-expanded), env overrides
(`PROTEAN_XIIOTID_RAW_PATH`, `PROTEAN_5GNIDD_RAW_PATH`), label columns, benign label,
drop labels, missing tokens, non-feature columns, and `generated_root`.
- `configs/experiments/experiment_protocol.yaml` — datasets, alphas, betas, modes, training
and evaluation settings, provisional seeds, artifact directories.
- `configs/models/protean_cnn.yaml` — frozen architecture and loss/inference contract.
Rules:
- No raw dataset path is hardcoded in source. Resolution precedence: env override, then
`raw_path`, then `~` expansion. Generated artifacts always resolve under `generated_root`.
- Raw data and generated data are strictly separated.
- `lambda` is `null` and seeds are `provisional`; both must be frozen before experiments.
## 3. Data flow
```
raw CSV (external)
-> load + clean (label normalise, '-'/NaN handling, drop duplicates policy)
-> encode + scale features (frozen recipe)
-> train/validation/test split (frozen)
-> Dirichlet partition of TRAIN over 10 clients --(persist manifest)-->
per-client class support N_ij
-> per (dataset, alpha, seed): reuse the SAME manifest for every beta variant
-> federated training (original / equal_available / support_weighted)
-> validation metrics -> beta selection
-> test metrics for selected beta (+ all candidates reported)
```
Partition manifests are the unit of pairing. A beta run must load a manifest, never
regenerate a partition.
## 4. Modules
### `data/` (planned)
- `loading.py`: read CSV with configured dtypes/encoding; apply `missing_tokens`; drop
non-feature columns; normalise labels (including the `Lateral _movement` typo).
- `preprocessing.py`: deterministic encoding/scaling; persisted so test data uses train
statistics.
- `splits.py`: train/validation/test split with a fixed seed policy.
- `partition.py`: Dirichlet non-IID partition of the training split over `num_clients`.
- `manifest.py`: save/load partition manifests; verify content hash on reload.
- `characterization.py`: compute `N_ij`, class totals, contributor counts, and inequality
statistics (min/max/mean/std/CV/max-min ratio).
- `rare_unseen.py`: derive rare (two smallest positive attack support) and unseen
(`N_ij = 0` attack classes) metadata per `(dataset, alpha, seed, client, class)`.
### `models/` (planned)
- `protean_cnn.py`: two Conv1D layers (64, 128 filters, ReLU, max-pool, dropout 0.2/0.5),
flatten, dense 128 (embedding `phi`), classification head (log-softmax).
- `prototypes.py`: compute `C_ij` from embeddings; require `N_ij > 0`.
### `federated/` (planned)
- `client.py`: local update minimising Eq. (3)/(5.6): cross-entropy + prototype alignment +
model alignment. Returns local model and observed-class prototypes.
- `server.py`: model aggregation + prototype aggregation dispatch by mode; builds `S_j`.
- `round.py`: one communication round; records per-round prototype snapshots for direct
analysis.
### `aggregation/` (exists)
Pure, dependency-free functions implementing only the unambiguous modes:
- `beta_weights(supports, beta)` → `N_ij ** beta` (unnormalised).
- `aggregation_weights(prototypes, mode, beta, supports)` → normalised weights summing to 1.
- `aggregate_prototypes(...)` → `Cbar_j` for one class.
- `AggregationMode`: `original_protean` (raises `NotImplementedError` until verified),
`support_weighted`, `equal_available`.
Planned addition: a server-facing helper that accepts `{client_id: (C_ij, N_ij)}` for class
`j`, constructs `S_j` (drop clients with `N_ij = 0` or missing prototype), and delegates to
`aggregate_prototypes`. This keeps all weighting logic in one tested place.
### `evaluation/` (planned)
- `metrics.py`: accuracy, macro-F1, per-class precision/recall/F1.
- `rare_unseen.py`: rare-class macro-F1 and recall per client; unseen-class reporting.
- `per_client.py`: per-client result aggregation.
### `utils/` (partly exists)
- `config.py` (exists): YAML loading and path resolution.
- `seeding.py` (planned): deterministic seeding for Python/NumPy/PyTorch.
- `integrity.py` (planned): hash helpers for manifests and source verification.
### `analysis/` (planned)
- `direct_prototype.py`: given a saved round snapshot of fixed `C_ij, N_ij`, compute
`Cbar_j(beta)` for several beta without retraining and report
`||Cbar_j(beta) - Cbar_j(0)||`, weights, distances, displacement, imbalance.
## 5. Aggregation API concept
```
aggregate_prototypes(prototypes, mode, beta=None, supports=None) -> list[float]
```
Contract:
- `prototypes[i]` is `C_ij`; `supports[i]` is `N_ij > 0`; order is client order.
- `support_weighted`: weights `N_ij ** beta`, normalised over `S_j`.
- `equal_available`: equal weights over `S_j` (control; not for `beta > 0` claims).
- `original_protean`: only after the baseline missing-class rule is verified and encoded.
- Validates finiteness, strictly positive supports, equal dimensions; normalised weights sum
to 1; never emits NaN/Inf for valid inputs.
## 6. Experiment orchestration
- A run is keyed by `(dataset, alpha, seed)`.
- For each key: generate or load one partition manifest; derive rare/unseen metadata once.
- For each beta in the candidate set: train using the same manifest and the same model
initialisation policy; evaluate on validation; store per-round metrics.
- Select the best beta by validation rare-class macro-F1.
- Evaluate the selected beta (and report all candidates) on the test split.
- Every run records the manifest hash, config hash, and code version.
- Orchestration must make test leakage structurally hard: selection code only receives
validation outputs; test outputs are produced by a separate step keyed on the selected
beta.
## 7. Testing strategy
Required tests (from the specification), to be implemented as the code is built:
1. `beta = 0` equals the arithmetic mean over the same `S_j`.
2. `beta = 1` equals sample-count weighting (equals pooling of included embeddings).
3. One contributing client gives the same output for all beta.
4. Equal supports give the same output for all beta.
5. A missing class does not enter `S_j`.
6. Normalised weights sum to one.
7. Valid inputs never produce NaN/Inf.
8. Partition generation is deterministic for a fixed seed.
9. Saved partition manifests reload identically.
10. Baseline behaviour is unchanged by adding the proposed mode.
11. Validation/test separation is maintained.
This session adds the dependency-free subset (1–7) in `tests/test_prototype_aggregation.py`.
Items 8–11 require the future data/federated modules and are deferred.
## 8. Compute feasibility
`SOURCE FACT` Paper environment: Linux Ubuntu 22.04.4, 11th Gen Intel i7-11800H (16 threads),
31 GiB RAM. No GPU is reported.
Local environment (measured this session): 8 CPU threads, ~29 GiB RAM, ~113 GiB free disk.
No CUDA device was probed.
Assessment:
- **Framework**: PyTorch (paper). No GPU is strictly required by the paper's setup.
- **Memory**: full CSVs load at ~671 MB (X-IIoTID) and ~551 MB (5G-NIDD) in pandas. Training
on a partitioned subset plus a CNN with 128-dim embeddings is comfortably within 29 GiB.
- **Clients**: 10 clients, simulated sequentially (standard for cross-silo CPU runs).
- **Storage**: partitions and manifests are small; per-round prototype snapshots are
`K x d` floats per client. Checkpoints of the small CNN are small. The main storage
pressure is the processed feature cache, which should be stored once under
`artifacts/processed/`.
- **Likely bottleneck**: local training over 10 clients × 3 epochs × 10 rounds per
`(dataset, alpha, seed, beta)`. With 5 betas × 3 alphas × 3 seeds × 2 datasets this is
90 training runs; wall-clock is unknown and must not be estimated without measurement.
- **Checkpoint strategy**: save per-round model and prototype snapshots so direct prototype
analysis and interrupted runs do not require retraining.
- **One-GPU execution**: feasible in principle (CPU-only was enough for the paper); a GPU
would only speed up local training.
`UNRESOLVED` No timing benchmark was run (this session forbids launching training). Define a
future profiling smoke test: 1 dataset, 1 alpha, 1 seed, 1 beta, 1 round, 1 epoch, and record
wall-clock and peak memory before any sweep.
@@ -0,0 +1,219 @@
# Research Specification Interpretation
Status: interpretation document
Authoritative source: `planningawal/PROTEAN_Revisi_Lurus.md` (immutable, read-only)
Companion documents: `docs/baseline/PROTEAN_BASELINE_INVESTIGATION.md`,
`docs/research/EXPERIMENT_PROTOCOL.md`, `docs/research/IMPLEMENTATION_PLAN.md`
This document translates the local research specification into implementation-relevant
statements. Every statement is labelled with one of:
- `SOURCE FACT` — written explicitly in `planningawal/PROTEAN_Revisi_Lurus.md` or in the
published PROTEAN paper/thesis.
- `INFERENCE` — a defensible reading that is not written verbatim.
- `IMPLEMENTATION DECISION` — a choice made in this project that the specification leaves open.
- `UNRESOLVED` — cannot be settled without more evidence.
The terminology of the specification is preserved. This project studies **server-side
prototype aggregation** only.
---
## 1. Research focus
`SOURCE FACT` The study optimises how the server combines class prototypes in PROTEAN for
rare attack detection under non-IID federated data. The focus is the server-side prototype
aggregation step, because the global prototype is used both as a training alignment target
and as the inference classifier (nearest prototype).
`SOURCE FACT` The novelty is **not** "using N_ij" or "adding a beta". FedProto already
weights prototypes by sample counts (Tan et al., 2022). Novelty must come from mechanism
differences, behavioural findings, and empirical evidence.
`SOURCE FACT` The reference aggregation for discussion is Equation (2) of the paper:
equal contribution of local prototypes (`1/M * sum_i C_ij`), even though Figure 1 uses the
phrase "Weighted averaging".
## 2. Original PROTEAN mechanism (as stated in the spec)
- `SOURCE FACT` Local prototype (Eq. 1): for class `j` with `N_ij > 0`,
`C_ij = (1 / N_ij) * sum_{x in class j} phi(x)`.
- `SOURCE FACT` Clients send local model parameters and local class prototypes to the server.
- `SOURCE FACT` Server aggregation (Eq. 2): `omega = (1/M) sum_i omega_i`,
`Cbar_j = (1/M) sum_i C_ij`.
- `SOURCE FACT` Server sends back global model and global prototypes.
- `SOURCE FACT` Local objective (Eq. 3): classification loss + prototype alignment
(`||C_ij - Cbar_j||^2`) + model parameter alignment (`mu/2 * ||omega_i - omega||^2`).
- `SOURCE FACT` Inference: assign the label of the global prototype with the smallest L2
distance to the sample embedding.
`SOURCE FACT` The spec explicitly warns that "Weighted averaging" in Figure 1 is **not**
present in Equation (2), and adopts Equation (2) as the formal reference.
## 3. Proposed formulation (this study)
`SOURCE FACT` For `S_j = { i | N_ij > 0 and a valid local prototype C_ij exists }`:
```
Cbar_j(beta) = ( sum_{i in S_j} N_ij^beta * C_ij ) / ( sum_{i in S_j} N_ij^beta )
```
`SOURCE FACT` Candidate values: `beta in {0.0, 0.25, 0.5, 0.75, 1.0}`. One beta is used for
all classes and clients in an experiment.
`SOURCE FACT` Interpretations:
- `beta = 0` gives equal weights inside the proposed `S_j`. It is a **control**, not
automatically the original baseline.
- `beta = 1` weights by class sample count; because `C_ij` is a mean embedding, the result
equals the mean of all local embeddings of class `j` included that round.
- `0 < beta < 1` gives a moderate support influence.
`SOURCE FACT` `N_ij` is aggregation metadata only; per-sample embeddings are never sent.
`SOURCE FACT` `beta = 0` may only be called "identical to baseline PROTEAN" if the
aggregation participant set and the missing-class handling are also identical.
## 4. Missing-class ambiguity in the specification
`SOURCE FACT` Equation (2) sums over `M` participants but does not state how a class
prototype that is unavailable on a client is handled.
`SOURCE FACT` `S_j` (clients that actually contributed a valid prototype for class `j`) is
defined **for the proposed formulation**. Its conformance to the original implementation
must still be verified.
`SOURCE FACT` If the baseline's missing-class rule differs from `S_j`, an
`equal_available` control (equal weights over `S_j`) may be used to separate the effect of
the participant rule from the effect of the weights. The spec says such a control is only
introduced if needed.
`IMPLEMENTATION DECISION` `equal_available` is kept as a dormant, documented control mode.
It must not be reported as a result unless baseline investigation demonstrates its
necessity (see `docs/baseline/PROTEAN_BASELINE_INVESTIGATION.md`).
## 5. Rare class and unseen class definitions
`SOURCE FACT` Rare classes on client `i`:
- the two **attack** classes with the smallest **positive** training support (`N_ij > 0`)
on that client;
- if exactly one attack class is available, that one is used;
- if no attack class is available (`N_ij = 0` for all attack classes), the client does not
enter the rare-class aggregate.
`SOURCE FACT` Classes with `N_ij = 0` are **unseen** classes and are evaluated separately
from rare classes. Rare and unseen evaluation must not be merged.
`SOURCE FACT` Rare-class scoring is computed per client that has rare classes, then averaged
across clients and across random seeds.
`INFERENCE` The benign class (Normal / Benign) is never a "rare class" because the
definition restricts to attack classes. This should be kept explicit in code.
`UNRESOLVED` Whether "attack class" excludes the benign label by name/position, and how
the class label mapping is fixed across clients and rounds, must be frozen before
experiments. See `docs/research/EXPERIMENT_PROTOCOL.md`.
## 6. Validation, selection, test
`SOURCE FACT` Beta is selected on a **validation** set using macro-F1 of rare classes as the
primary criterion.
`SOURCE FACT` The test set is used only after beta selection is complete. The spec explicitly
forbids using test data for selection.
`SOURCE FACT` The selected value is "the best beta among the tested candidates under the
specified validation procedure", **not** a claim of global optimality.
`SOURCE FACT` After selection, overall macro-F1, rare-class recall, and per-client results
are still reported, to expose the consequences of the choice.
`UNRESOLVED` The example PROTEAN paper uses an 80/20 train/test split with **no**
validation set. The specification adds a validation split. The exact split procedure and
fraction are left to this project. An `IMPLEMENTATION DECISION` must be recorded once fixed
(see EXPERIMENT_PROTOCOL.md).
## 7. Datasets and non-IID settings
`SOURCE FACT` Datasets: X-IIoTID and 5G-NIDD.
`SOURCE FACT` Initial partition uses a Dirichlet distribution with `alpha in {0.75, 0.5, 0.25}`.
`SOURCE FACT` Training, validation, and test data are separated. Rare classes are determined
from **training** data before beta selection.
`SOURCE FACT` For each partition, record the total samples per class, the number of clients
that contain the class, and the inequality of `N_ij` across contributing clients. Alpha alone
is insufficient because different seeds give different realised distributions.
## 8. Metrics
`SOURCE FACT` Required outputs include:
- macro-F1 of rare classes (primary selection criterion);
- rare-class recall;
- overall macro-F1;
- per-client results.
`SOURCE FACT` Composition of the validation and test sets must be recorded so that rare-class
metrics are not interpreted without the number of evaluation samples.
`IMPLEMENTATION DECISION` Per-class confusion counts for each evaluated split are persisted,
so precision/recall/F1 can be recomputed without re-running training.
## 9. Direct prototype analysis
`SOURCE FACT` Separate the **direct mathematical aggregation effect** from the
**end-to-end training effect**.
`SOURCE FACT` The direct effect is inspected by computing candidate global prototypes from
the **same set of local prototypes at the same round**, across beta values, without
retraining.
`SOURCE FACT` Report at least `|| Cbar_j(beta) - Cbar_j(0) ||`, and optionally normalized
aggregation weights, distance to contributing prototypes, pairwise global-prototype
displacement, and support imbalance.
`SOURCE FACT` Direct analysis does not replace end-to-end evaluation.
## 10. Research invariants (must hold in all experiments)
1. Only server-side prototype aggregation changes. Architecture, model aggregation, loss
terms (`lambda`, `mu`), and the inference rule follow the verified baseline.
2. For a given `(dataset, alpha, seed)`, all aggregation variants reuse the exact same
partition, splits, client membership, class mapping, model initialisation policy, and
hyperparameters.
3. Beta comparisons are paired: a beta run must not regenerate a fresh Dirichlet partition.
4. Validation data selects beta; test data never does.
5. Rare classes are two smallest positive-support **attack** classes; unseen classes are
separate.
6. `beta = 0` inside `S_j` is reported as an equal-weighting control; it is only equated
with the original PROTEAN when the participant rule is proven identical.
## 11. Methodological constraints and non-goals
`SOURCE FACT` Do not introduce, without need to reproduce the frozen baseline: client
rejection, poisoning defence, adaptive losses, prototype clustering, distance-metric
experiments, or any optimisation beyond beta weighting.
`SOURCE FACT` Learning rate, communication rounds, local epochs, batch size, architecture,
and loss parameters are held equal across comparisons. Partitions and seeds are paired.
## 12. Unresolved questions carried forward
1. Original PROTEAN missing-class and denominator semantics (see baseline investigation).
2. Original global-prototype initialisation for all `K` classes at round 0.
3. Value of `lambda` (the paper states `mu = 0.1` but not `lambda`).
4. Whether model-parameter aggregation is equal (`1/M`) or FedAvg-style weighted by `N_i`.
5. Exact validation split procedure/fraction (paper has none).
6. Exact random seed set and reported units (paper averages over three runs, seeds unnamed).
7. Feature preprocessing (encoding, scaling, missing-value handling) is not specified by
the paper and must be frozen here.
8. Class-label normalisation for the `Lateral _movement` typo and for any class with zero
valid samples.
9. How the prototype alignment loss term handles `N_ij = 0` classes (sum over `j = 1..K`
with no local prototype is undefined as written).
These are tracked with evidence in `docs/baseline/PROTEAN_BASELINE_INVESTIGATION.md` and
carried into `docs/handoff/SESSION_HANDOFF.md`.