research: add post-hoc statistical audit and defense preparation aids

- scripts/posthoc_statistical_audit.py + docs/research/POSTHOC_STATISTICAL_AUDIT.md:
  read-only paired audit of the completed grid (exact sign-flip / Wilcoxon,
  selection stability, per-class/per-client summaries); writes only posthoc__*.
- kisikisithesis/: robustness audit and examiner-preparation document with
  deterministic read-only checks (robustness_checks.py).
- artifacts/analysis/posthoc__*.csv: audit outputs (6 tables).
- artifacts/analysis: track the 4 remaining beta0-snapshot effect/weight CSVs
  (xiiotid a0.5 seeds 1-2), completing the tracked beta0 set.
- .gitignore: codify that non-beta0 prototype snapshots stay untracked.
This commit is contained in:
willtanoe committed 2026-10-17 04:06:04 +07:00
1 parent 34bab45865
commit 111adb272e
15 files changed
+2431

No files matched your search

@@ -0,0 +1,454 @@
# Audit Ketangguhan (Robustness) dan Persiapan Penguji Tesis PROTEAN
Dokumen ini merupakan audit berbasis bukti dan bahan persiapan sidang untuk penelitian
**agregasi prototype terbobot pada PROTEAN**. Seluruh angka diambil dari artefak eksperimen yang
telah selesai; tidak ada pelatihan ulang, tidak ada perubahan pada algoritma, model, loss,
optimizer, dataset, partisi, maupun hasil eksperimen. Skrip pendukung berada di
`kisikisithesis/robustness_checks.py` dan bersifat deterministik serta hanya-baca.
Status repositori saat audit: branch `main`, HEAD `34bab45`, tidak ada perubahan pada berkas
terlacak. Direktori `kisikisithesis/` seluruhnya baru.
---
## 1. Ringkasan Eksekutif
1. **Mekanisme yang diubah hanya agregasi prototype di sisi server.** Bobot client
`w_ij(β) = N_ij^β / Σ_{k∈S_j} N_kj^β` dengan `β ∈ {0, 0.25, 0.50, 0.75, 1.00}`; model lokal,
loss, optimizer, inferensi, dataset, dan manifest partisi tetap.
2. **Sifat yang dijamin oleh rumus** (diverifikasi sintetis, `robustness_checks.py`):
`β=0` = rata-rata aritmetik atas `S_j`; `β=1` = rata-rata terbobot dukungan; satu penyumbang
invarian terhadap `β`; dukungan seragam invarian terhadap `β`; bobot selalu berjumlah 1;
`N_ij=0` dan prototype hilang dikeluarkan dari `S_j`; `β` tak valid ditolak.
3. **Efek langsung vs efek akhir-ke-akhir berbeda.** Displacement prototype `‖C̄_j(β)−C̄_j(0)‖`
naik monoton terhadap `β` (X-IIoTID 1.305–3.422; 5G-NIDD 1.385–2.919 pada `β=1`), tetapi
displacement besar **bukan** bukti deteksi membaik.
4. **Hasil akhir-ke-akhir kecil dan bergantung kondisi.** Perbedaan rare-class macro-F1 uji:
−0,0007 / +0,0230 / +0,0014 (X-IIoTID) dan −0,0010 / −0,0086 / +0,0168 (5G-NIDD). Hanya
X-IIoTID α=0,50 yang arahnya konsisten pada ketiga seed.
5. **Daya uji statistik tidak memadai.** Dengan 3 seed, p terkecil yang mungkin untuk uji tanda
eksak dua sisi adalah **0,25** (diverifikasi); hanya X-IIoTID α=0,50 mencapainya. Maka tidak
ada klaim signifikansi.
6. **Pemilihan β rapuh secara relatif.** Margin terbaik vs terbaik kedua hanya **0,0024–0,0122**,
dan kesepakatan seleksi pooled dengan seleksi per-seed paling banyak **2 dari 3**.
7. **Asumsi reproduksi `S_j` material.** `|S_j| < M` pada **53 dari 162** sel kelas-kondisi.
8. **Reproduksi vs paper belum dapat diverifikasi** karena `journal/main.tex` (dan Table IV paper)
tidak ada di repositori ini.
Kalimat klaim yang aman: *"Di antara kandidat β yang diuji, pembobotan dukungan sampel kelas
memberi perubahan rare-class macro-F1 yang kecil dan bergantung kondisi; hanya sebagian kondisi
menunjukkan perbaikan yang konsisten antar-seed, dan tidak ada klaim signifikansi statistik."*
---
## 2. Inventaris Repositori dan Bukti
Sumber utama (semua sudah tersimpan, hanya-baca):
| Keperluan | Artefak |
|---|---|
| Protokol beku | `docs/research/EXPERIMENT_PROTOCOL.md`, `configs/experiments/experiment_protocol.yaml` |
| Implementasi agregasi | `src/protean_research/aggregation/prototype_aggregation.py` |
| Efek agregasi langsung | `src/protean_research/analysis/direct_prototype.py`, `scripts/analyze_prototype_effect.py` |
| Snapshot prototype | `artifacts/analysis/*prototype_snapshot.json` (108 berkas; 18 sel × 6 konfigurasi) |
| Partisi beku | `artifacts/partitions/*.json` (18 manifest) |
| Perbandingan berpasangan | `artifacts/analysis/deepcmp__*.csv` |
| Ringkasan enam kondisi | `artifacts/analysis/final__six_condition_master.csv` |
| Kurva seleksi | `final__pooled_validation_curves.csv`, `final__selection_strength.csv` |
| Sensitivitas β (tabel) | `final__prototype_displacement_by_beta.csv`, `..._by_class.csv` |
| Heterogenitas terealisasi | `final__realized_heterogeneity.csv` |
| Aturan `S_j` | `artifacts/analysis/sj_participant_rule_analysis.csv` |
| Ekuivalensi β=0 | `*__multiseed__beta0_equivalence.csv` (5 kondisi; X-IIoTID α=0,50 tidak ada) |
| Audit statistik | `docs/research/POSTHOC_STATISTICAL_AUDIT.md`, `artifacts/analysis/posthoc__*.csv` |
| Metrik per-run | `results/<run>/test_global_metrics.json`, `test_per_class.csv`, `per_client.csv` |
| Validasi per-seed | `artifacts/analysis/<ds>__alpha*__multiseed__validation_per_seed.csv` |
| Baseline tereproduksi | `docs/baseline/BASELINE_VALIDATION.md`, `PROTEAN_BASELINE_INVESTIGATION.md` |
**Batas bukti yang harus dicatat:**
- `journal/main.tex` / Table IV paper **tidak ada** → perbandingan metrik paper tidak dapat
diverifikasi.
- `xiiotid__alpha0.5__multiseed__beta0_equivalence.csv` **tidak ada**, dan satu run
`xiiotid__alpha0.5__seed0__original_protean` kehilangan artefak validasi.
- Hanya **3 seed** per kondisi.
- Pemilihan β memakai **rata-rata per-client (validasi)**, sedangkan headline memakai
**rare-class macro-F1 global (uji)**.
Analisis yang **dapat** dihitung dari artefak: seluruh perbandingan berpasangan, displacement
langsung, stabilitas seleksi, audit `S_j`, ringkasan per-kelas/per-client.
Analisis yang **memerlukan pelatihan baru**: menambah seed, dataset, arsitektur, atau kandidat β
kontinu.
---
## 3. Penjelasan Matematis β dan Bobot Client (terverifikasi)
Diberikan himpunan peserta `S_j = { i : N_ij > 0 dan prototype lokal C_ij valid }`. Bobot
ternormalisasi dan prototype global:
```
w_ij(β) = N_ij^β / Σ_{k∈S_j} N_kj^β
C̄_j(β) = Σ_{i∈S_j} w_ij(β) · C_ij
```
**β = 0.** `N_ij^0 = 1` sehingga `w_ij = 1/|S_j|` dan `C̄_j(0)` adalah rata-rata aritmetik atas
`S_j`. Ini adalah **kontrol setara**, bukan otomatis sama dengan baseline PROTEAN: kesetaraan hanya
berlaku bila aturan peserta/penanganan kelas-tak-tersedia juga sama.
**β = 1.** `w_ij ∝ N_ij`. Karena setiap `C_ij` adalah rata-rata embedding kelas lokal, `C̄_j(1)`
setara dengan rata-rata gabungan seluruh embedding kelas `j` yang disertakan bila seluruh asumsi
terpenuhi.
**0 < β < 1.** Bobot tumbuh sub-linear terhadap dukungan: client berdukungan besar lebih
berpengaruh, tetapi tidak sepenuhnya proporsional.
**Mengapa β besar tidak menjamin deteksi lebih baik.** Agregasi hanya menghasilkan posisi
`C̄_j(β)`; inferensi memilih prototype terdekat (jarak L2). Meningkatnya bobot client berdukungan
besar dapat menggeser `C̄_j` ke arah cluster padat tetapi **kurang mewakili** variasi serangan
langka, sehingga menurunkan recall kelas langka. Jadi peningkatan `β` mengubah geometri prototype
secara deterministik, tetapi hubungannya dengan kinerja deteksi bersifat empiris dan bergantung
data.
**N_ij = 0.** Client tidak menyumbang prototype untuk kelas tersebut dan tidak masuk `S_j`;
implementasi tidak pernah membuat vektor nol (`aggregate_class_prototypes`, baris penyaring
`support <= 0 atau proto is None`).
**Mengapa penyebut dan aturan peserta penting.** Bila Eq. (2) paper (`1/M` atas semua client)
diterapkan harfiah dengan mengisi prototype hilang sebagai nol, hasilnya berbeda dari `1/|S_j|`.
Pada **53/162** sel kelas-kondisi, `|S_j| < M`, sehingga aturan ini material dan seluruh hasil
bersifat kondisional.
**Sifat yang dijamin rumus** (diverifikasi): normalisasi (Σw=1), endpoint β, invariansi satu
penyumbang/dukungan seragam, pengecualian `N_ij=0`, penolakan β tak valid.
**Sifat yang bergantung data/implementasi**: besar displacement per kelas, client mana yang
dominan, dan apakah deteksi membaik.
---
## 4. Temuan Sensitivitas Parameter yang Ada (efek agregasi langsung)
Dihitung ulang dari snapshot round-0 yang tersimpan oleh `kisikisithesis/robustness_checks.py`
(tanpa pelatihan, snapshot tidak diubah).
**Tabel kondisi (mean displacement `‖C̄_j(1)−C̄_j(0)‖`, dari
`final__prototype_displacement_by_beta.csv`):**
| Dataset | α=0.25 | α=0.50 | α=0.75 |
|---|---|---|---|
| X-IIoTID | 3.422 (β=0.25: 1.476; β=0.50: 2.403; β=0.75: 3.003) | 2.099 | 1.305 |
| 5G-NIDD | 2.919 (1.295 / 2.055 / 2.559) | 2.340 | 1.385 |
Displacement naik **monoton** terhadap β di setiap kondisi; makin kecil α (makin timpang),
makin besar displacement.
**Contoh sensitivitas per kelas (5G-NIDD α=0.25 seed 0, snapshot β=0):**
| Kelas | \|S_j\| | N_min | N_max | disp@0.5 | disp@1.0 | bobot client terbesar @β=1 |
|---|---|---|---|---|---|---|
| HTTPFlood | 9 | 1 | 48.469 | 3.895 | 4.727 | 0.492 |
| TCPConnectScan | 9 | 2 | 10.870 | 4.040 | 6.173 | 0.774 |
| SlowrateDoS | 9 | 1 | 16.517 | 3.606 | 4.216 | 0.323 |
| SYNFlood | 7 | 144 | 4.762 | 1.151 | 2.726 | 0.700 |
| Benign | 10 | 1 | 185.504 | 2.335 | 3.229 | 0.555 |
Pada snapshot ini, mean disp@1.0 untuk kelas `|S_j|<M` (6 kelas) = **3.619** vs `|S_j|=M`
(3 kelas) = **3.537**. Pada X-IIoTID α=0.25 seed 0: **3.620** vs **2.726** (kelas dengan
peserta tidak lengkap bergeser lebih jauh). Ini **deskriptif**, bukan kausal.
**Kasus prototype berubah tanpa bukti perbaikan deteksi (penting).** Contoh `TCPConnectScan`
bergeser 6.173 pada β=1, tetapi perubahan F1 uji kelas ini kecil dan bergantung kondisi. Jadi
displacement ≠ kinerja deteksi; ini dibahas juga di §5 dan §8.
**Catatan utility langsung.** `prototype_effect_rows` dan `client_weight_rows`
(`src/protean_research/analysis/direct_prototype.py`) menghitung displacement dan bobot dari
snapshot yang sama; tidak memodifikasi snapshot dan tidak memicu pelatihan.
---
## 5. Temuan Robustness End-to-End yang Ada
Berbasis audit post-hoc (`posthoc__*.csv`); angka terverifikasi.
**Perbedaan berpasangan rare-class macro-F1 uji (proposed − original), per seed:**
| Dataset | α | β* | Δ s0 | Δ s1 | Δ s2 | mean | std berpasangan | std base | std proposed | +/−/0 | arah sama |
|---|---|---|---|---|---|---|---|---|---|---|---|
| X-IIoTID | 0.25 | 0.00 | +0.0016 | −0.0020 | −0.0016 | −0.0007 | 0.0019 | 0.0472 | 0.0476 | 1/2/0 | tidak |
| X-IIoTID | 0.50 | 0.75 | +0.0266 | +0.0163 | +0.0261 | **+0.0230** | 0.0058 | 0.0287 | 0.0267 | 3/0/0 | **ya** |
| X-IIoTID | 0.75 | 0.50 | −0.0097 | +0.0101 | +0.0036 | +0.0014 | 0.0101 | 0.0778 | 0.0855 | 2/1/0 | tidak |
| 5G-NIDD | 0.25 | 0.00 | −0.0050 | −0.0010 | +0.0029 | −0.0010 | 0.0039 | 0.0254 | 0.0242 | 1/2/0 | tidak |
| 5G-NIDD | 0.50 | 1.00 | +0.0090 | −0.0811 | +0.0464 | −0.0086 | 0.0655 | 0.0371 | 0.0285 | 2/1/0 | tidak |
| 5G-NIDD | 0.75 | 0.50 | −0.0011 | +0.0450 | +0.0067 | +0.0168 | 0.0247 | 0.0231 | 0.0145 | 2/1/0 | tidak |
- **Recall kelas langka turun pada 5/6 kondisi** (dan pada **semua 4** kondisi β>0): perolehan F1
cenderung berasal dari presisi.
- **Uji eksak (n=3):** sign-flip p = 0.75 / **0.25** / 0.75 / 0.75 / 1.00 / 0.50; Wilcoxon p =
1.00 / 0.50 / 1.00 / 1.00 / 1.00 / 1.00. p terkecil yang mungkin = **0.25**.
- **Confusion matrix** (5G-NIDD α=0.75 seed0): error dominan `Benign → UDPFlood` nyaris tetap
(56.899 → 57.054; **+155**, bukan perbaikan); error `HTTPFlood → SlowrateDoS` turun 547;
`Benign → TCPConnectScan` turun 520; `SlowrateDoS → HTTPFlood` naik 229.
- **Per kelas** (mean ΔF1 atas semua 9 observasi): Tampering +0.052, Reconnaissance +0.030,
ICMPFlood +0.027; SYNFlood −0.019, C&C −0.018. Konsistensi antar-seed tidak merata.
- **Per client:** redistribusi — sebagian client naik, sebagian turun; pada α=0.25 beta=0 semua
client "tidak berubah". Analisis dukungan rendah bersifat deskriptif (Pearson Δ vs fraksi
dukungan ≈ −0,09; n=120).
**Diagnostik seleksi:** margin 0,0024–0,0122; kesepakatan pooled vs per-seed ≤2/3; pooled
per-seed: X 0.75/0.50/0.00; 0.50/1.00/0.75; 0.50/0.50/0.00 dan 5G 0.25/0.00/0.00; 1.00/0.00/1.00;
0.50/1.00/1.00.
**Eksperimen yang dikecualikan:** kondisi X-IIoTID α=0,50 tidak memiliki artefak ekuivalensi β=0
dan kehilangan artefak validasi untuk original seed 0; analisis langsung memakai sel yang lengkap.
---
## 6. Pertanyaan Penguji dan Jawaban (≥20)
Format: **Jawaban lisan (ID)** · *Alasan teknis* · *Bukti* · *Tindak lanjut* · *Peringatan*.
**Q1. Apa yang berubah jika β = 0, 0,5, atau 1?**
Jawab: "β=0 berarti rata-rata sederhana atas client penyumbang; β=1 berarti kontribusi
proporsional terhadap jumlah sampel kelas; β=0,5 adalah kompromi sub-linear." *Alasan:* rumus di
§3. *Bukti:* `test_beta0_is_equal_arithmetic_mean_over_s_j`, `test_beta1_is_support_weighted_mean`;
`robustness_checks.py` (β=0 → [1.333,1.667]; β=1 → [1.857,2.286]; β=0,5 → [1.586,2.000]).
*Tindak lanjut:* "Apakah β=0 identik dengan PROTEAN asli?" → lihat Q13. *Peringatan:* perubahan
prototype tidak otomatis mengubah kinerja deteksi.
**Q2. Mengapa memilih himpunan kandidat β ini?**
Jawab: "Lima kandidat diskret menutupi dua ujung dan tiga titik antara agar eksperimen tetap
terkendali dan terjangkau, sesuai spesifikasi riset." *Alasan:* protokol membekukan
`{0,0.25,0.5,0.75,1.0}`. *Bukti:* `EXPERIMENT_PROTOCOL.md`, `test_beta_candidates_are_the_frozen_protocol_values`.
*Tindak lanjut:* "Mengapa tidak β kontinu?" *Peringatan:* belum ada analisis β kontinu.
**Q3. Apa yang terjadi bila partisi makin/tidak makin non-IID?**
Jawab: "Makin kecil α, dukungan kelas makin timpang dan displacement prototype makin besar;
sebaliknya pada α besar hampir seragam." *Alasan & bukti:* `final__realized_heterogeneity.csv`,
`final__prototype_displacement_by_beta.csv` (displacement β=1 turun saat α naik). *Tindak lanjut:*
"Apakah α menyebabkannya?" *Peringatan:* hanya asosiasi deskriptif; α adalah knob, bukan
heterogenitas terealisasi.
**Q4. Bagaimana bila kelas langka absen di sebagian besar client?**
Jawab: "Kelas itu hanya diagregasi dari client yang memilikinya (`S_j`); bila tidak ada penyumbang,
kelas dihilangkan dari hasil agregasi." *Bukti:* `test_class_with_only_missing_contributors_is_omitted`,
`aggregate_class_prototypes`. *Tindak lanjut:* "Bagaimana pengaruhnya pada evaluasi rare-class?"
*Peringatan:* kelangkaan ekstrem membatasi daya metrik.
**Q5. Apa yang terjadi ketika N_ij = 0?**
Jawab: "Client tidak masuk `S_j` dan tidak ada vektor nol yang dibuat." *Bukti:*
`aggregate_class_prototypes` (penyaring `support <= 0`), `test_missing_class_is_excluded_from_s_j`.
*Tindak lanjut:* Q14.
**Q6. Bagaimana bila satu client jauh lebih banyak sampelnya?**
Jawab: "Pada β tinggi bobotnya mendominasi; pada β=0 semua setara." *Bukti:* synthetic dominance
[1,1000] → bobot 0,999 bagian; snapshot TCPConnectScan bobot client terbesar ≈0,774 pada β=1.
*Tindak lanjut:* "Apakah ini selalu baik?" *Peringatan:* dominasi dapat menekan variasi kelas
langka.
**Q7. Mengapa dukungan spesifik kelas, bukan total sampel client?**
Jawab: "Bobot diterapkan per kelas, jadi relevan jumlah sampel kelas tersebut; total client tidak
mewakili dukungan kelas tertentu." *Alasan:* `N_ij` didefinisikan per kelas. *Bukti:*
`prototype_aggregation.py` menggunakan `N_ij` per kelas. *Tindak lanjut:* "Bisakah total sampel
dipakai?" → mungkin sebagai pembanding, tetapi berbeda semantik.
**Q8. Apakah prototype yang berubah pasti memperbaiki klasifikasi?**
Jawab: "Tidak." *Bukti:* displacement monoton naik terhadap β, sedangkan respons deteksi
non-monoton (`final__prototype_displacement_by_beta.csv` vs `final__pooled_validation_curves.csv`).
*Tindak lanjut:* Q9. *Peringatan:* jangan menyamakan displacement dengan kinerja.
**Q9. Mengapa macro-F1 bisa naik tetapi recall/presisi turun?**
Jawab: "F1 adalah rata-rata harmonik; pergeseran prototype dapat mengubah trade-off
presisi–recall." *Bukti:* rare-class recall turun di 5/6 kondisi; contoh Tampering (ΔF1 +0,144,
Δpresisi +0,212, Δrecall −0,009 di X-IIoTID α=0,50). *Tindak lanjut:* "Apakah itu bermanfaat?"
**Q10. Bagaimana bila β terpilih tidak terbaik di uji?**
Jawab: "Mungkin saja; seleksi memakai validasi, uji tidak boleh dipakai memilih β." *Bukti:*
`test_used_for_selection = false`; `final__selection_strength.csv`. *Tindak lanjut:* Q12.
**Q11. Mengapa hanya tiga seed, dan apa yang tidak dapat disimpulkan?**
Jawab: "Tiga seed sesuai protokol proyek; belum cukup untuk klaim signifikansi." *Bukti:* p
terkecil yang mungkin = 0,25 (`posthoc__paired_test_results.csv`). *Tindak lanjut:* Q16.
*Peringatan:* ketidaksignifikanan ≠ tidak ada efek.
**Q12. Apa implikasi margin seleksi kecil?**
Jawab: "Seleksi rapuh; β terpilih adalah 'terbaik di antara kandidat yang diuji', bukan optimal."
*Bukti:* margin 0,0024–0,0122; kesepakatan ≤2/3. *Tindak lanjut:* "Perlu seed tambahan?"
**Q13. Mengapa β=0 dianggap setara dengan agregasi bobot seragam asli, dan bagaimana diverifikasi?**
Jawab: "β=0 secara matematis = rata-rata seragam atas `S_j`; kesetaraan dengan baseline asli hanya
bila aturan peserta sama." *Bukti:* `test_beta0_matches_original_protean_on_identical_s_j`;
`*__multiseed__beta0_equivalence.csv` (kelas B; `aggregation_max_abs_diff` ≈ 2e−16; snapshot
round-0 byte-identik). *Tindak lanjut:* "Mengapa tidak bitwise identik?" *Peringatan:* bukan bukti
reproduksi PROTEAN asli; masih ada divergensi floating-point end-to-end. X-IIoTID α=0,50 tidak
punya artefak ekuivalensi ini (celah).
**Q14. Bagaimana perilaku bila |S_j| < jumlah client?**
Jawab: "Hanya client di `S_j` yang diagregasi; penyebut memakai jumlah bobot kontributor."
*Bukti:* `sj_participant_rule_analysis.csv` (53/162 sel), kode `aggregate_class_prototypes`.
*Tindak lanjut:* "Apa alternatifnya?" *Peringatan:* aturan ini asumsi reproduksi.
**Q15. Bisakah pembobotan dukungan memperburuk kinerja? Mekanisme masuk akal?**
Jawab: "Bisa, secara prinsip." *Alasan:* pada β tinggi, client berdukungan besar mendominasi;
jika variasi kelas langka hanya ada di client berdukungan kecil, prototype global bergeser menjauh
dari variasi itu → recall turun. *Bukti:* hal ini teramati pada **5G-NIDD α=0,50** (Δ = −0,0086)
dan pada α=0,25 (β=1 paling merugikan di tingkat client, dari sintesis). *Peringatan:* contoh lain
bersifat mekanisme hipotetis, bukan hasil teramati.
**Q16. Eksperimen tambahan apa yang paling langsung menguji reprodusibilitas efek?**
Jawab: "Menambah seed pada kondisi terkuat (X-IIoTID α=0,50) dan kondisi terlemah (5G-NIDD α=0,50),
lalu seleksi dan evaluasi berpasangan; idealnya juga beberapa arsitektur." *Alasan:* daya uji
bergantung jumlah seed. *Bukti:* §5. *Tindak lanjut:* Q21.
**Q17. Asumsi apa yang berbeda dari paper PROTEAN?**
Jawab: "Penanganan kelas tak tersedia (`S_j`), split 70/10/20 (paper 80/20), nilai λ tanpa rujukan,
seed tak diberi paper, dan preprocessing yang tidak dispesifikasikan." *Bukti:*
`PROTEAN_BASELINE_INVESTIGATION.md`, `EXPERIMENT_PROTOCOL.md`. *Peringatan:* perbedaan reproduksi,
bukan otomatis bug.
**Q18. Apa implikasi privasi karena server menerima N_ij?**
Jawab: "Server menerima hitungan dukungan per kelas selain prototype; ini memperluas informasi yang
diungkap dibanding hanya prototype." *Alasan & bukti:* `client.py` menghasilkan `supports`;
`server.py` meneruskan `client_supports` ke agregasi. *Peringatan:* **belum ada analisis
threat-model/privasi**, jadi tidak boleh diklaim sebagai kerentanan; ini hanya deskripsi aliran data.
**Q19. Parameter mana yang tetap, mana yang disetel, mana bagian metode usulan?**
Jawab: "Tetap: model, loss, optimizer, inferensi, dataset, partisi, arsitektur. Disetel: β.
Bagian metode usulan: bobot `N_ij^β` pada agregasi prototype server." *Bukti:* §3,
`prototype_aggregation.py`, protokol.
**Q20. Apa yang akan menggugurkan klaim empiris utama?**
Jawab: "Bila pada seed tambahan perbaikan X-IIoTID α=0,50 tidak muncul, atau bila β terpilih tidak
stabil, maka klaim 'perbaikan bergantung kondisi' harus diperlemah; klaim keunggulan universal
sudah tidak diajukan." *Bukti:* §5. *Peringatan:* tanpa seed tambahan, ini belum diuji.
**Q21. Mengapa tidak menjalankan β kontinu?**
Jawab: "Di luar lingkup dan biaya; grid diskret sudah dibekukan." *Bukti:* protokol. *Tindak
lanjut:* sebagai pekerjaan lanjutan.
**Q22. Apa arti 'dukungan sampel' dan mengapa bukan ukuran kualitas?**
Jawab: "`N_ij` adalah jumlah sampel training kelas j pada client i; bukan skor kualitas prototype."
*Bukti:* `AGENTS.md` §9/§10, `prototype_aggregation.py`.
*(22 pertanyaan; mencakup seluruh daftar yang diminta.)*
---
## 7. Rencana Demonstrasi Perubahan Parameter Terkendali
Tidak dieksekusi otomatis oleh dokumen ini; `robustness_checks.py` telah menjalankan versi
read-only untuk sebagian skenario. Semua demo memakai input sintetis kecil atau snapshot tersimpan.
| # | Skenario | Input terkendali | Hasil yang diharapkan (rumus) | Implementasi / tes yang ada | Perlu tes baru? | Penjelasan lisan |
|---|---|---|---|---|---|---|
| 1 | Endpoint β | `p=[[1,0],[0,2],[3,3]]`, `N=[5,10,20]` | β=0 → rata-rata; β=1 → terbobot | `test_beta0_...`, `test_beta1_...` | tidak | "Dua ujung rumus terbukti langsung." |
| 2 | Normalisasi bobot | support apa pun > 0 | Σw = 1 | `test_normalized_weights_sum_to_one` | tidak | "Bobot selalu terdefinisi." |
| 3 | Client dukungan nol | `N=[5,0,7]` | client 0 di luar `S_j` | `test_missing_class_is_excluded_from_s_j` | tidak | "Tanpa fabrikasi prototype." |
| 4 | Dukungan seragam | `N=[4,4,4]` | invarian β | `test_equal_support_is_invariant_to_beta` | tidak | "Tanpa ketimpangan, β tak berpengaruh." |
| 5 | Sangat timpang | `N=[1,1000]` | bobot ≈ [0,001; 0,999] | `robustness_checks.py` (dominance) | disarankan (unit test formal) | "Dominasi client besar." |
| 6 | Ekuivalen β=0 | `S_j` identik | sama dengan rata-rata seragam | `test_beta0_matches_original_protean_on_identical_s_j`; `beta0_equivalence.csv` | tidak | "Kontrol, bukan asli." |
| 7 | Stabilitas numerik / β tak valid | β ∈ {−0.1,1.1,None,"x",∞} | ValueError | `test_invalid_beta_is_rejected`, `test_output_is_finite_...` | tidak | "Validasi ketat." |
| 8 | Sensitivitas satu client dominan terhadap prototype | snapshot nyata + client dominan | pergeseran prototype | `client_weight_rows`, `prototype_effect_rows` | tidak | "Pergeseran terukur, bukan kinerja." |
---
## 8. Audit Integritas Riset dan Batasan
**Klaim yang didukung eksperimen.** Desain berpasangan tervalidasi (manifest/split/seed sama);
perbedaan rare-F1 uji kecil dan bergantung kondisi; hanya X-IIoTID α=0,50 konsisten positif;
recall kelas langka cenderung turun; seleksi β rapuh; `S_j` material (53/162).
**Klaim yang hanya deskriptif.** Asosiasi timpang–displacement; redistribusi manfaat antar-client;
dukungan rendah tidak dirugikan (asosiasi lemah, tanpa kausalitas).
**Asumsi reproduksi.** `S_j` dan penanganan kelas tak tersedia; split 70/10/20; λ=1,0; preprocessing;
seed proyek; inisialisasi prototype round-0.
**Klaim yang harus dilemahkan/dihapus.** Superioritas/signifikansi; β optimal universal; bahwa
displacement = deteksi lebih baik; keunggulan umum lintas dataset.
**Eksperimen tambahan yang memperkuat.** Penambahan seed (terutama X-IIoTID α=0,50 dan 5G α=0,50);
dataset ketiga; arsitektur kedua; analisis β kontinu; analisis threat-model privasi `N_ij`.
**Analisis yang tidak layak dijalankan.** Uji inferensial dengan 3 seed (daya nihil); bootstrap
interval dari 3 seed; menjadikan client sebagai replikasi independen (nested).
**Perhatian khusus:**
- **Tiga seed** → p minimum 0,25.
- **Celah reproduksi vs paper** → `journal/main.tex`/Table IV tidak ada; belum terverifikasi.
- **`S_j`** → 53/162 sel; asumsi, bukan statistik.
- **Metrik seleksi vs headline** → per-client mean (validasi) vs global (uji).
- **Displacement vs deteksi** → dipisahkan.
- **Privasi** → deskripsi aliran `N_ij`; belum ada threat-model.
---
## 9. Eksperimen Lanjutan Prioritas (dengan estimasi komputasi)
Estimasi berdasar runtime terukur (`artifacts/analysis/*__multiseed__runtime.csv`): 5G-NIDD
≈ 62–65 menit/run, X-IIoTID ≈ 40–45 menit/run; 108 run ≈ 94,5 jam CPU (8 core).
| Prioritas | Eksperimen | Rasional | Estimasi biaya |
|---|---|---|---|
| 1 | Tambah seed (3→10) pada X-IIoTID α=0,50 dan 5G α=0,50 (kondisi terkuat & terlemah) | menguji reprodusibilitas efek terbesar & arah negatif | 2 kondisi × 7 seed × 6 config ≈ 84 run ≈ 70–75 jam |
| 2 | Tambah seed penuh (3→10) semua 6 kondisi | daya uji dan stabilitas seleksi | ≈ 252 run ≈ 210–220 jam |
| 3 | Dataset IDS ketiga | validitas eksternal | bergantung ukuran; ≈ 40–70 jam |
| 4 | Arsitektur alternatif (mis. MLP/1D-CNN berbeda) | sensitivitas arsitektur | ≈ 90–200 jam |
| 5 | Grid β kontinu (mis. 0.0–1.0 langkah 0.1) | resolusi kandidat | 6 kondisi × 3 seed × 6 β tambahan ≈ 108 run ≈ 90 jam |
| 6 | Analisis threat-model privasi `N_ij` | klaim privasi | tanpa pelatihan (analistik) |
Tidak disarankan: uji signifikansi atau bootstrap dari 3 seed (informasi rendah, berisiko
menyesatkan).
---
## 10. Cheat Sheet Pertahanan Lisan (satu halaman)
- **Mekanisme:** hanya agregasi prototype server; `w_ij(β)=N_ij^β/ΣN_kj^β`; `β∈{0,.25,.5,.75,1}`.
- **β=0:** rata-rata seragam atas `S_j` (kontrol; setara baseline hanya bila aturan peserta sama).
- **β=1:** terbobot dukungan; **β antara:** sub-linear.
- **Hasil utama:** Δrare-F1 uji = X {−0,0007; +0,0230; +0,0014}, 5G {−0,0010; −0,0086; +0,0168}.
- **Konsisten antar-seed:** hanya X-IIoTID α=0,50.
- **Statistik:** n=3; p minimum 0,25; **tanpa klaim signifikansi**.
- **Seleksi:** margin 0,0024–0,0122; rapuh; validasi-saja (`test_used_for_selection=false`).
- **Direct vs end-to-end:** displacement naik monoton; deteksi non-monoton; dipisahkan.
- **`S_j`:** 53/162 sel; asumsi reproduksi.
- **Confusion dominan:** `Benign→UDPFlood` tetap (+155), bukan perbaikan.
- **Jangan klaim:** superioritas universal, β optimal, displacement = deteksi lebih baik,
signifikansi, kerentanan privasi tanpa threat-model.
- **Celah:** paper Table IV tidak tersedia; X-IIoTID α=0,50 tanpa ekuivalensi β=0;
artefak validasi seed0 original hilang.
---
## 11. Path Artefak dan Perintah Reproduksi
```bash
# Pemeriksaan formula + efek agregasi langsung (read-only, tanpa pelatihan)
.venv/bin/python kisikisithesis/robustness_checks.py
# Audit statistik post-hoc (read-only; hanya menulis posthoc__*.csv)
.venv/bin/python scripts/posthoc_statistical_audit.py
# Analisis efek prototype langsung (read-only) - contoh
.venv/bin/python scripts/analyze_prototype_effect.py \
--snapshot artifacts/analysis/5gnidd__alpha0.25__seed0__support_weighted__beta0__prototype_snapshot.json \
--class-names "Benign,HTTPFlood,ICMPFlood,SYNFlood,SYNScan,SlowrateDoS,TCPConnectScan,UDPFlood,UDPScan" \
--output-dir /tmp/opencode/proto_effect # output di luar repo bila ingin menghindari file baru
# Uji unit agregasi & efek (read-only)
.venv/bin/python -m pytest tests/test_support_weighted_aggregation.py tests/test_baseline_aggregation.py tests/test_prototype_effect.py
```
Artefak kunci: `artifacts/analysis/deepcmp__*.csv`, `final__*.csv`,
`posthoc__*.csv`, `sj_participant_rule_analysis.csv`, `*__multiseed__validation_per_seed.csv`,
`*__multiseed__beta0_equivalence.csv`, `artifacts/partitions/*.json`.
---
## 12. Pertanyaan Tak Terjawab dan Bukti yang Hilang
1. **Metrik paper PROTEAN (Table IV)** tidak dapat diverifikasi: `journal/main.tex` tidak ada.
2. **Ekuivalensi β=0 untuk X-IIoTID α=0,50** tidak tersedia (berkas tidak ada).
3. **Artefak validasi seed 0 original X-IIoTID α=0,50** tidak lengkap.
4. **Daya statistik** tidak memadai (3 seed); signifikansi tidak dapat diklaim.
5. **Privasi `N_ij`** belum dianalisis dengan threat-model.
6. **β kontinu** dan **generalisasi lintas dataset/arsitektur** belum diuji.
7. **Ketidakpastian seleksi β** tidak dapat dipisahkan sepenuhnya dari variasi seed dengan n=3.
+169
View File
@@ -0,0 +1,169 @@
#!/usr/bin/env python3
"""Deterministic, read-only robustness checks for the PROTEAN thesis.
Run from the repository root:
.venv/bin/python kisikisithesis/robustness_checks.py
Nothing is written and no experimental artifact is modified. The script:
* exercises the *actual* aggregation implementation on tiny synthetic inputs
(formula invariants);
* recomputes direct aggregation effects on persisted round-0 prototype
snapshots (no training, no snapshot mutation).
All values are printed deterministically so the thesis report can quote them.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from protean_research.aggregation.prototype_aggregation import ( # noqa: E402
AggregationMode,
aggregate_prototypes,
aggregate_class_prototypes,
normalized_support_weights,
validate_beta,
BETA_CANDIDATES,
)
from protean_research.analysis.direct_prototype import ( # noqa: E402
client_weight_rows,
prototype_effect_rows,
)
ANALYSIS = ROOT / "artifacts" / "analysis"
def section(title: str) -> None:
print("\n" + "=" * 74)
print(title)
print("=" * 74)
def check(label: str, ok: bool) -> None:
print(f" [{'PASS' if ok else 'FAIL'}] {label}")
def synthetic_invariants() -> None:
section("A. Synthetic formula invariants (actual implementation)")
p = [[1.0, 0.0], [0.0, 2.0], [3.0, 3.0]]
s = [5.0, 10.0, 20.0]
eq = [sum(v[d] for v in p) / len(p) for d in range(2)]
b0 = aggregate_prototypes(p, AggregationMode.SUPPORT_WEIGHTED, supports=s, beta=0.0)
print(f" beta=0 aggregate = {b0}")
print(f" arithmetic mean = {eq}")
check("beta=0 equals the arithmetic mean over S_j",
all(abs(a - b) < 1e-12 for a, b in zip(b0, eq)))
w1 = normalized_support_weights(s, 1.0)
b1 = aggregate_prototypes(p, AggregationMode.SUPPORT_WEIGHTED, supports=s, beta=1.0)
closed = [sum(w * v[d] for w, v in zip(w1, p)) for d in range(2)]
print(f" beta=1 weights = {[round(w, 6) for w in w1]}")
print(f" beta=1 aggregate = {b1}")
check("beta=1 equals the support-weighted closed form",
all(abs(a - b) < 1e-12 for a, b in zip(b1, closed)))
w05 = normalized_support_weights(s, 0.5)
b05 = aggregate_prototypes(p, AggregationMode.SUPPORT_WEIGHTED, supports=s, beta=0.5)
print(f" beta=0.5 weights = {[round(w, 6) for w in w05]}")
print(f" beta=0.5 aggregate = {b05}")
single = aggregate_prototypes([[7.0, 9.0]], AggregationMode.SUPPORT_WEIGHTED,
supports=[3.0], beta=1.0)
check("single contributor is beta-invariant", single == [7.0, 9.0])
equal = [aggregate_prototypes(p, AggregationMode.SUPPORT_WEIGHTED,
supports=[4.0, 4.0, 4.0], beta=b) for b in BETA_CANDIDATES]
check("equal support is beta-invariant", all(g == equal[0] for g in equal))
for b in BETA_CANDIDATES:
tot = sum(normalized_support_weights(s, b))
if abs(tot - 1.0) > 1e-12:
check(f"weights sum to 1 at beta={b}", False)
break
else:
check("normalized weights sum to 1 for every beta candidate", True)
# zero-support exclusion and missing-class handling through the S_j builder
contributions = {0: [([1.0], 5.0), ([2.0], 0.0), (None, 7.0)], 1: [(None, 0.0)]}
agg = aggregate_class_prototypes(contributions, AggregationMode.SUPPORT_WEIGHTED, beta=1.0)
print(f" S_j builder output = {agg}")
check("N_ij=0 and missing prototypes are dropped from S_j", agg.get(0) == [1.0])
check("a class with no valid contributor is omitted", 1 not in agg)
dom = normalized_support_weights([1.0, 1000.0], 1.0)
print(f" dominance weights [1,1000] at beta=1 = {[round(w, 6) for w in dom]}")
check("a single dominant client can dominate the aggregate at beta=1", dom[1] > 0.999)
rejected = 0
for bad in (-0.1, 1.1, None, "x", float("inf")):
try:
validate_beta(bad)
except (ValueError, TypeError):
rejected += 1
check("invalid beta values are rejected", rejected == 5)
def snapshot_sensitivity(tag: str, path: Path, names: dict) -> None:
section(f"B. Direct aggregation effect on a persisted snapshot: {tag}")
snap = json.loads(path.read_text())
print(f" snapshot: {path.relative_to(ROOT)} (round {snap['round']})")
rows = prototype_effect_rows(snap, betas=[0.0, 0.5, 1.0], class_names=names)
weights = client_weight_rows(snap, betas=[0.0, 0.25, 0.5, 0.75, 1.0], class_names=names)
by_class = {}
for r in rows:
by_class.setdefault(r["class_id"], {})[r["beta"]] = r
maxw = {}
for w in weights:
if w["beta"] == 1.0:
cur = maxw.get(w["class_id"])
if cur is None or w["weight"] > cur["weight"]:
maxw[w["class_id"]] = w
print(f" {'class':16} {'|S_j|':>5} {'sup_min':>8} {'sup_max':>9} "
f"{'disp@0.5':>9} {'disp@1.0':>9} {'maxW@1.0':>9}")
for cid in sorted(by_class):
r = by_class[cid]
n = r[0.0]["n_contributors"]
d05 = r[0.5]["displacement_from_beta0"]
d10 = r[1.0]["displacement_from_beta0"]
mw = maxw.get(cid, {}).get("weight", float("nan"))
name = (r[0.0]["class_name"] or cid)
print(f" {str(name):16} {n:>5} {r[0.0]['support_min']:>8.1f} "
f"{r[0.0]['support_max']:>9.1f} {d05:>9.4f} {d10:>9.4f} {mw:>9.4f}")
partial = [cid for cid in by_class if by_class[cid][0.0]["n_contributors"] < 10]
full = [cid for cid in by_class if by_class[cid][0.0]["n_contributors"] == 10]
if partial:
md = sum(by_class[c][1.0]["displacement_from_beta0"] for c in partial) / len(partial)
print(f" mean disp@1.0, |S_j|<M classes ({len(partial)}): {md:.4f}")
if full:
md = sum(by_class[c][1.0]["displacement_from_beta0"] for c in full) / len(full)
print(f" mean disp@1.0, |S_j|=M classes ({len(full)}): {md:.4f}")
def main() -> None:
synthetic_invariants()
snapshot_sensitivity(
"5G-NIDD, alpha=0.25, seed=0 (beta=0 snapshot)",
ANALYSIS / "5gnidd__alpha0.25__seed0__support_weighted__beta0__prototype_snapshot.json",
{i: n for i, n in enumerate(
["Benign", "HTTPFlood", "ICMPFlood", "SYNFlood", "SYNScan",
"SlowrateDoS", "TCPConnectScan", "UDPFlood", "UDPScan"])})
snapshot_sensitivity(
"X-IIoTID, alpha=0.25, seed=0 (beta=0 snapshot)",
ANALYSIS / "xiiotid__alpha0.25__seed0__support_weighted__beta0__prototype_snapshot.json",
{i: n for i, n in enumerate(
["C&C", "Exfiltration", "Exploitation", "Lateral_movement", "Normal",
"RDOS", "Reconnaissance", "Tampering", "Weaponization"])})
print("\nREAD-ONLY CHECKS COMPLETE (no files written, no snapshots modified).")
if __name__ == "__main__":
main()