feat: modernize PSO and add convergence research

Migrate the package and examples to the tensor-native PyTorch implementation, add benchmark evidence, and add the guarded post-training convergence protocol with TensorBoard progress monitoring and hash-verified recovery.

Constraint: Preserve one-shot official-test sealing and auditable research artifacts

Rejected: Commit local .omc runs and downloaded datasets | multi-gigabyte runtime state is machine-local

Confidence: high

Scope-risk: broad

Not-tested: Production CUDA run on pieroot-server
This commit is contained in:
2026-09-07 22:03:25 +09:00
parent 9745bb7ad4
commit 813433000a
120 changed files with 198148 additions and 4745 deletions
-23
View File
@@ -1,23 +0,0 @@
name: Release Tag
on:
push:
tags:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Get Version
run: echo "##[set-output name=version;]$(echo '${{ github.event.head_commit.message }}' | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')"
id: extract_version_name
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.extract_version_name.outputs.version }}
release_name: Release ${{ steps.extract_version_name.outputs.version }}
body: |
Release ${{ steps.extract_version_name.outputs.version }}
+40 -23
View File
@@ -1,38 +1,55 @@
name: PyPI package name: Publish to PyPI
on: on:
push: push:
paths: tags:
- "setup.py" - "v*"
- "pso/__init__.py"
permissions: permissions:
contents: read contents: read
packages: write
jobs: jobs:
build-linux: test:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
strategy: strategy:
max-parallel: 5
matrix: matrix:
python-version: ["3.9"] python-version: ["3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python - name: Install uv and Python ${{ matrix.python-version }}
uses: actions/setup-python@v3 uses: astral-sh/setup-uv@v9
with: with:
version: "0.12.7"
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies enable-cache: true
run: | - name: Sync dependencies
python -m pip install --upgrade pip run: uv sync --locked --group dev
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Run tests
pip install setuptools wheel twine run: uv run --no-sync pytest -q
- name: Build and publish - name: Build package
env: run: uv build
TWINE_USERNAME: __token__ - name: Check package distribution
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: uv run --no-sync twine check dist/*
run: |
python setup.py bdist_wheel sdist publish:
twine upload dist/*.whl dist/*.tar.gz needs: test
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install uv and Python 3.11
uses: astral-sh/setup-uv@v9
with:
version: "0.12.7"
python-version: "3.11"
enable-cache: true
- name: Sync dependencies
run: uv sync --locked --group dev
- name: Build package
run: uv build
- name: Check package distribution
run: uv run --no-sync twine check dist/*
- name: Publish package to PyPI
uses: pypa/gh-action-pypa-publish@release/v1
with:
password: ${{ secrets.PYPI_TOKEN }}
@@ -1,25 +0,0 @@
name: Python Package using Conda
on: [push]
jobs:
build-linux:
runs-on: ubuntu-22.04
strategy:
max-parallel: 5
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v3
with:
python-version: "3.9"
- name: Add conda to system path
run: |
# $CONDA is an environment variable pointing to the root of the miniconda directory
echo $CONDA/bin >> $GITHUB_PATH
- name: Install dependencies
run: |
conda env create --file conda_env/environment.yaml --name pso
conda activate pso
python mnist.py
+41
View File
@@ -0,0 +1,41 @@
name: Python package test
on:
push:
paths:
- "pso/**"
- "pyproject.toml"
- "uv.lock"
- ".python-version"
- "README.md"
- "tests/**"
- ".github/workflows/python-package.yml"
pull_request:
paths:
- "pso/**"
- "pyproject.toml"
- "uv.lock"
- ".python-version"
- "README.md"
- "tests/**"
- ".github/workflows/python-package.yml"
jobs:
test:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.10", "3.11"]
steps:
- uses: actions/checkout@v4
- name: Install uv and Python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v9
with:
version: "0.12.7"
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Sync dependencies
run: uv sync --locked --group dev
- name: Run tests
run: uv run --no-sync pytest -q
+8
View File
@@ -27,3 +27,11 @@ logs/
.vscode/ .vscode/
metacode/ metacode/
# uv / venv
.venv/
.uv/
# Local research execution state and large downloaded datasets
.omc/
runs/
+1
View File
@@ -0,0 +1 @@
3.11
+899 -226
View File
File diff suppressed because it is too large Load Diff
+796
View File
@@ -0,0 +1,796 @@
# PSO v4.0.0 실증 벤치마크 평가 보고서 (Benchmark 2.0.0 + Tuning 1.0.0 + Replication 1.0.0 + Epoch Convergence 1.0.0 + Full MNIST 1.0.0 + Deep Accuracy 1.0.0 + MNIST-PSO-RAW-V5 1.0.0 + MNIST-PSO-RAW-V6 1.0.0 + HEAVY-TASK-PSO-V6 1.0.0 + HEAVY-PSO-CROSS-SPLIT 1.0.0)
## 1. 요약 및 핵심 발견사항 (Executive Findings)
본 보고서는 `pso2keras` 라이브러리의 v4.0.0 5단계 플러그인 아키텍처 기반 미분 무관(Derivative-Free) Particle Swarm Optimization (PSO) 알고리즘 수렴 성능 및 특성에 대한 종합 실증 평가 결과입니다. 벤치마크 프로토콜 v2.0.0에 따라 총 225회의 독립 측정 실행(메인 벤치마크 7기법 × 5워크로드 × 5시드 = 175회, MNIST Ablation 10프로필 × 5시드 = 50회)을 수행하였으며, 모든 실행은 에러 없이 100% 성공적으로 완료되었습니다.
추가로 Tuning Protocol 1.0.0에서 32개 하이퍼파라미터 후보의 검증 선택, 5개 기법의 held-out 확인, `adaptive_moment` 파티클 스케일링을 수행했습니다. 이 확장 연구는 161개 결과 레코드(96 search + 25 confirmation + 40 scaling)를 포함하며, 공유 30×80 scaling 셀을 재사용했으므로 실제 scaling 적합 실행은 35회입니다. 이후 120×80 exact replay/fresh-seed 10회, 같은 시드 5개의 240-epoch subset 궤적, 공식 MNIST 60,000/10,000 전체 split의 240-epoch 궤적 5회를 각각 별도 프로토콜로 실행했습니다. Deep Accuracy Protocol 1.0.0은 아키텍처 레인 9개와 최적화기 레인 9개의 논리 레코드를 포함하며, 최적화기 레인의 `adam_only` 3개는 아키텍처 레인의 Compact CNN 결과를 명시적으로 재사용하므로 실제 고유 학습 워크플로는 15개입니다. 이 후속 실행들은 기존 161개 Tuning Protocol 레코드나 225회 Protocol 2.0.0 결과에 합산하지 않습니다.
HEAVY-TASK-PSO-V6 1.0.0은 공식 test split을 사용하지 않고 MNIST/FashionMNIST의 train 60,000개를 각각 search 50,000/validation 10,000으로 분할해, Compact CNN(9,098 params)과 WideCNN(55,338 params)의 전가중치 PSO 실행 가능성을 추가로 평가했습니다. 단일시드 16-cell screen 뒤 G8과 workload별 validation-selected normalized 방법을 3개 시드로 확인했으며, 이 결과는 기존 프로토콜 실행 수에 합산하지 않습니다.
### 핵심 평가 요약
1. **고전 무브먼트 기법의 강세**: 5개 워크로드 종합 평균 순위에서 `constriction` (수축 계수 PSO)과 `inertia` (관성 가중치 감쇄 PSO)가 평가 정확도 평균 순위 **1.60**으로 공동 1위를 기록했습니다. 손실(Loss) 측면에서는 `constriction`이 평균 순위 **1.60**으로 `inertia` (**1.80**) 대비 더 낮은 손실 수렴 성향을 나타냈습니다.
2. **미분 기반 후처리(Adam)의 효과**: MNIST 소형 분류 모델 실험에서 하이브리드 경사하강 미세조정(`tuned_adam_100_lr.01`)을 적용할 경우 검증 정확도 **85.58% ± 0.24%**를 기록하여 최고 성과를 달성했습니다. 다만, 이는 역전파 경사도(`loss.backward()`)를 활용하는 하이브리드 방식입니다.
3. **미분 무관 Ablation 탐색 최고 성과**: pure derivative-free 기법 중 MNIST Ablation 최고 검증 정확도는 `adaptive_moment_.10` (적응형 경로 모멘트 혼합비 0.10)로 **63.00% ± 1.83%**를 기록했습니다 (단, 검증 손실은 **1.243339**로 `inertia_tuned`의 **1.236600** 대비 약간 높음).
4. **평가 방식 및 샘플 수 교란 요인**: 미분 무관 ablation 중 최저 검증 손실은 `tuned_full_evaluation`의 **1.179009 ± 0.050059**였으나, 이는 고정 서브셋(2,000개)이 아닌 전체 학습 데이터(3,000개) 평가 및 배치 처리 차이에 따른 교란 요인(Confound)이 반영된 수치입니다.
5. **파티클 재초기화(Particle Reset)**: `tuned_particle_reset` 프로필은 측정된 시드 세트(46~50)에서 `inertia_tuned`와 동일한 최종 성능 수치(**61.62% / 1.236600**)를 기록했습니다. 재초기화 이벤트 수를 별도로 계측하지 않았으므로, 이벤트가 없었는지 또는 최종 전역 최적해에 영향을 주지 않았는지는 이 결과만으로 구분할 수 없습니다.
6. **확장 튜닝의 동일 예산 확인**: 파티클 30개 × 80세대 held-out 확인에서 `local_best`**62.64% ± 2.94%**, `inertia`**62.30% ± 3.01%**, `adaptive_moment`가 **61.06% ± 0.91%**를 기록했습니다. 따라서 Adaptive Moment는 이 동일 예산 비교의 최상위 기법이 아닙니다.
7. **Adaptive Moment 파티클 증가**: 80세대를 유지하면서 파티클을 30개에서 120개로 늘리면 정확도가 **62.12%에서 72.34%**로 상승했지만 particle-evaluations와 fit 시간도 각각 4배와 **4.16배**로 증가했습니다. 약 2,400 particle-epochs를 고정하면 파티클 증가와 세대 감소 조합의 정확도는 오히려 낮아졌습니다.
8. **120×80 재현성 확인**: 동일 시드 71~75 exact replay는 baseline과 같은 **72.34% ± 1.82%**를 재현했고 시드별 최대 차이는 **0.00%p**였습니다. 독립 시드 81~85는 **73.60% ± 1.66%**로 baseline 대비 **+1.26%p**였으며, 사전 선언한 ±3%p 및 95% t-신뢰구간 중첩 조건을 모두 충족했습니다.
9. **Epoch 80은 조기 수렴 지점이 아님**: 120개 파티클의 연속 실행을 240세대까지 늘리자 테스트 정확도가 **72.34%에서 83.52%**로 **+11.18%p** 상승하고 training best loss가 **47.30%** 감소했습니다. 200→240에서도 loss가 **6.68%** 감소하고 정확도가 **+0.94%p** 상승해 epoch 240에서도 완전한 plateau는 확인되지 않았습니다.
10. **전체 MNIST 학습 결과**: 공식 train 60,000개를 모든 파티클의 매 epoch fitness에 사용하고 test 10,000개 전체를 평가했습니다. 120 particles × 240 epochs에서 정확도는 **87.70% ± 0.36%**, 테스트 loss는 **0.412078 ± 0.006848**이었습니다. Epoch 200→240에서도 정확도가 **+0.85%p**, training loss가 **6.13%** 개선되어 plateau는 확인되지 않았습니다.
11. **원본 MNIST 이미지 및 딥 신경망 아키텍처/최적화 기법 평가 (Deep Accuracy 1.0.0)**: PCA 없이 공식 train 60,000개와 test 10,000개 원본 $1 \times 28 \times 28$ 입력을 평가했습니다($n=3$, seeds 101~103). Adam 10 epochs에서 Raw Linear(7,850 params)는 **92.45% ± 0.10%**, Raw MLP(109,386 params)는 **97.70% ± 0.08%**, Compact CNN(9,098 params)은 **98.53% ± 0.16%**였고 CNN은 모든 시드가 5 epochs 이내 98%에 도달했습니다. 같은 CNN에서 30 particles × 40 generations·fixed-2k all-weight PSO는 **36.76% ± 3.76%**, PSO→Adam은 **97.30% ± 0.75%**였습니다. 측정한 예산에서는 전가중치 PSO와 PSO 초기화가 pure Adam을 대체하거나 개선하지 못했습니다.
12. **V5 탐색 구조 회귀 원인 분리 (MNIST-PSO-RAW-V6 1.0.0)**: 공식 test split을 로드하지 않고 train 60,000개를 search 50,000/validation 10,000으로 나눈 뒤, Compact CNN 전가중치 PSO의 탐색 구조 9개를 비교했습니다. 3-시드 확인에서 V5 구조 G0은 **79.53% ± 0.58% / NLL 0.689687**, 기존 `Optimizer` 제어군 G8은 **84.92% ± 0.99% / 0.481440**이었습니다. mutation·초기 속도·normalized ±6 경계를 묶은 G5/G6은 각각 **84.24% / 0.510297**, **84.25% / 0.503809**로 사전 회복 기준을 충족했습니다. 단일시드 screen은 mutation과 경계 확장을 유력 요인으로 지목하지만 개별 3-시드 인과 확인은 아직 수행하지 않았습니다.
13. **더 무거운 영상 태스크 실행 가능성 (HEAVY-TASK-PSO-V6 1.0.0)**: 파라미터 수를 **9,098→55,338(6.08배)**로 늘리고 FashionMNIST를 추가한 네 workload에서 G8과 screen-selected G5/G6의 24개 확인 실행이 모두 finite하게 완료됐습니다. 사전 기준(초기 모델 대비 validation NLL 20% 이상 감소와 accuracy 20%p 이상 상승)은 8개 workload-method 집계가 모두 통과했습니다. 그러나 12 particles × 80 epochs·fixed-10k에서 최종 정확도는 **41.03~49.15%**에 그쳐, 이는 계산상 최적화 가능성이지 실용적 학습 성능이나 Adam 대체 가능성을 의미하지 않습니다.
14. **Heavy PSO 교차 분할 강건성 검증 실패 (HEAVY-PSO-CROSS-SPLIT 1.0.0)**: 개발 분할 2개(20260905/20260906) 및 시드 101~103, 매칭 baseline 재실행 조건(12p×80e×fixed10k)에서 동결 정책 `fixed_global_hybrid_v3`를 교차 평가한 결과, 전체 정확도 개선은 +0.1533%p, NLL 감소는 0.5546%에 그쳤고 최악 accuracy 회귀 -7.1767%p, 최악 NLL 회귀 +14.5682%, MNIST Wide acc -0.3617%p(NLL 2.4600% 악화)로 게이트를 통과하지 못해 실패(FAIL)로 판정되었습니다. 총 8회 결정 탐색(9개 개발 변형) 중 최상위 스코어 후보(Iteration 5, 스코어 -185.610686)도 MNIST Wide acc -2.6633%p(NLL 2.1874% 악화)로 탈락했습니다. 이에 따라 보존 정책은 `null`로 확정되었고 확인 분할(20260907) 실행은 과학적 규칙에 따라 보류되었으며 공식 test split은 0회 로드/평가되었습니다 (총 432 runs, 414,720 queries, 41.472억 샘플 평가, 3162.9717s).
---
## 2. 실험 환경 및 워크로드 스펙 (Hardware, Software & Workload Spec)
### 2.1 하드웨어 및 소프트웨어 프로비넌스
| 환경 항목 | 세부 사양 / 버전 |
| --- | --- |
| **플랫폼 (OS)** | macOS 26.5.2 (Darwin 25.5.0 arm64) |
| **프로세서 (CPU/GPU)** | Apple M5 Max (System Apple Silicon, MPS 가속) |
| **Python 버전** | 3.11.15 |
| **PyTorch 버전** | 2.13.0 (`device="mps"`) |
| **패키지 버전** | `pso2keras` v4.0.0 |
| **벤치마크 프로토콜** | Protocol v2.0.0 |
### 2.2 워크로드 및 신경망 모델 매트릭스
본 벤치마크는 비선형 논리 회로(XOR), 소형 다변량 표형 데이터(Iris, Seeds), 중형 이미지 수치 데이터(Digits), PCA 32차원 축소 MNIST 데이터셋을 대상으로 수행되었습니다.
| 워크로드 | 데이터 크기 (학습/검증) | 모델 구조 | 파라미터 수 | 손실 함수 및 작업 | 파티클 수 ($N$) | 세대 수 ($T$) | 평가 대상 집합 | 시드 범위 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **XOR** | 4 / 4 | `Linear(2,4) - Tanh - Linear(4,1)` | 17 | `BCEWithLogitsLoss` (Binary) | 24 | 80 | Train Eval | 41 ~ 45 |
| **Iris** | 120 / 30 | `Linear(4,10) - ReLU - Linear(10,10) - ReLU - Linear(10,3)` | 193 | `CrossEntropyLoss` (Multiclass) | 24 | 60 | Held-out Eval | 41 ~ 45 |
| **Seeds** | 168 / 42 | `Linear(7,16) - ReLU - Linear(16,32) - ReLU - Linear(32,3)` | 771 | `CrossEntropyLoss` (Multiclass) | 24 | 60 | Held-out Eval | 41 ~ 45 |
| **Digits** | 1,437 / 360 | `Linear(64,12) - ReLU - Linear(12,10) - ReLU - Linear(10,10)` | 1,020 | `CrossEntropyLoss` (Multiclass) | 24 | 50 | Held-out Eval | 41 ~ 45 |
| **MNIST** | 3,000 / 1,000 (PCA32) | `Linear(32,10)` | 330 | `CrossEntropyLoss` (Multiclass) | 30 | 80 | Held-out Eval | 41 ~ 45 |
> **참고**: MNIST 실험은 784차원 원본 이미지 텐서가 아닌 PCA 32차원 로짓 분류기 `Linear(32,10)` 환경(학습 3,000개, 검증 1,000개)에서 수행되었습니다.
> **타이밍 예산 및 웜업**: 각 측정은 별도의 모델과 Optimizer로 기법별 비측정 웜업(PSO 2세대)을 수행한 뒤, 동일 시드로 측정 대상을 다시 생성하여 `fit()` 호출만 측정했습니다. Adam 프로필의 웜업은 refinement 1세대를 포함하며, 메인 미분 무관 실험의 웜업은 refinement를 사용하지 않습니다.
---
## 3. 메인 벤치마크 평가 결과 (Main Benchmark Matrix)
7가지 주요 PSO 무브먼트 기법(`original`, `inertia`, `constriction`, `fips`, `clpso`, `bare_bones`, `adaptive_moment`)에 대한 5개 워크로드별 5-시드 평균 및 표준편차(Mean ± SD, $n=5$) 결과입니다.
### 3.1 정확도 Matrix (Eval Accuracy %, Mean ± SD)
| 워크로드 (평가 구분) | original | inertia | constriction | fips | clpso | bare_bones | adaptive_moment |
| --- | --- | --- | --- | --- | --- | --- | --- |
| **XOR** (Train) | 100.00% ± 0.00% | 100.00% ± 0.00% | **100.00% ± 0.00%** | 75.00% ± 25.00% | 60.00% ± 13.69% | 100.00% ± 0.00% | 100.00% ± 0.00% |
| **Iris** (Held-out) | 92.00% ± 8.69% | **94.00% ± 2.79%** | 94.00% ± 3.65% | 74.00% ± 5.48% | 79.33% ± 7.60% | 84.67% ± 5.58% | 84.67% ± 7.67% |
| **Seeds** (Held-out) | 87.62% ± 4.26% | **91.43% ± 5.22%** | 89.05% ± 9.16% | 88.57% ± 3.53% | 85.71% ± 2.92% | 87.14% ± 6.86% | 89.05% ± 4.64% |
| **Digits** (Held-out) | 16.44% ± 2.23% | 30.33% ± 6.17% | **37.28% ± 1.79%** | 24.94% ± 6.15% | 20.78% ± 5.06% | 19.00% ± 5.63% | 26.78% ± 1.83% |
| **MNIST** (Held-out) | 14.40% ± 1.66% | 46.84% ± 7.53% | **52.00% ± 4.62%** | 21.06% ± 4.81% | 20.68% ± 2.28% | 41.26% ± 3.80% | 26.20% ± 1.04% |
### 3.2 손실 Matrix (Eval Raw Loss, Mean ± SD)
| 워크로드 (평가 구분) | original | inertia | constriction | fips | clpso | bare_bones | adaptive_moment |
| --- | --- | --- | --- | --- | --- | --- | --- |
| **XOR** (Train) | 0.004558 ± 0.002672 | 0.004431 ± 0.003308 | **0.004086 ± 0.002357** | 0.546355 ± 0.059307 | 0.548166 ± 0.031946 | 0.082100 ± 0.056963 | 0.190431 ± 0.178122 |
| **Iris** (Held-out) | 0.222324 ± 0.155767 | **0.106901 ± 0.053063** | 0.171796 ± 0.076965 | 0.605010 ± 0.060601 | 0.448270 ± 0.053877 | 0.405791 ± 0.172336 | 0.349701 ± 0.178108 |
| **Seeds** (Held-out) | 0.492240 ± 0.433653 | 0.331656 ± 0.342636 | 0.351420 ± 0.388200 | 0.383732 ± 0.103951 | 0.469817 ± 0.073254 | 0.421920 ± 0.120052 | **0.315947 ± 0.120895** |
| **Digits** (Held-out) | 2.269628 ± 0.025331 | 1.980667 ± 0.143070 | **1.765113 ± 0.057309** | 2.064165 ± 0.091569 | 2.204543 ± 0.023215 | 2.250203 ± 0.084448 | 2.108837 ± 0.023357 |
| **MNIST** (Held-out) | 2.428194 ± 0.091877 | 1.716089 ± 0.260764 | **1.507968 ± 0.149152** | 2.223230 ± 0.073048 | 2.285833 ± 0.068177 | 1.965562 ± 0.175909 | 2.219058 ± 0.104724 |
### 3.3 워크로드별 최고 성과 기법 요약 (Per-workload Winners)
| 워크로드 | 최고 정확도 기법 (Eval Acc) | 최저 손실 기법 (Eval Loss) | 비고 |
| --- | --- | --- | --- |
| **XOR** | `constriction` (100.00% ± 0.00%) | `constriction` (0.004086 ± 0.002357) | 5개 기법 100% 동률 (손실로 순위 구분) |
| **Iris** | `inertia` (94.00% ± 2.79%) | `inertia` (0.106901 ± 0.053063) | `constriction`과 정확도 동률, 손실로 순위 구분 |
| **Seeds** | `inertia` (91.43% ± 5.22%) | `adaptive_moment` (0.315947 ± 0.120895) | 정확도 inertia 우수, 손실 AM 우수 |
| **Digits** | `constriction` (37.28% ± 1.79%) | `constriction` (1.765113 ± 0.057309) | 고차원 다중 분류에서 수축 계수 우수 |
| **MNIST** | `constriction` (52.00% ± 4.62%) | `constriction` (1.507968 ± 0.149152) | 이 고정 예산에서 정확도와 손실 모두 1위 |
### 3.4 평균 순위 및 수렴 실행 시간 (Ranks & Warmed Runtime)
각 워크로드 내에서 1위(최고)부터 7위(최저)까지 순위를 부여한 후 5개 워크로드에 대해 평균한 결과 및 MPS 디바이스 웜업 완료 후 측정된 실행 시간입니다.
| 기법 (`method`) | 정확도 순위 목록 [XOR, Iris, Seeds, Digits, MNIST] | 평균 정확도 순위 | 손실 순위 목록 [XOR, Iris, Seeds, Digits, MNIST] | 평균 손실 순위 | 평균 실행 시간 ($n=25$) |
| --- | --- | --- | --- | --- | --- |
| **`constriction`** | [1, 2, 3, 1, 1] | **1.60** | [1, 2, 3, 1, 1] | **1.60** | 2.0821s ± 0.766s |
| **`inertia`** | [2, 1, 1, 2, 2] | **1.60** | [2, 1, 2, 2, 2] | **1.80** | 1.9824s ± 0.614s |
| **`adaptive_moment`** | [5, 4, 2, 3, 4] | **3.60** | [5, 4, 1, 4, 4] | **3.60** | 2.1264s ± 0.874s |
| **`bare_bones`** | [4, 5, 6, 6, 3] | **4.80** | [4, 5, 5, 6, 3] | **4.60** | 2.2476s ± 0.724s |
| **`original`** | [3, 3, 5, 7, 7] | **5.00** | [3, 3, 7, 7, 7] | **5.40** | 2.1112s ± 0.857s |
| **`fips`** | [6, 7, 4, 4, 5] | **5.20** | [6, 7, 4, 3, 5] | **5.00** | 2.5636s ± 0.708s |
| **`clpso`** | [7, 6, 7, 5, 6] | **6.20** | [7, 6, 6, 5, 6] | **6.00** | 2.3507s ± 0.726s |
> **핵심 요약**: `inertia`와 `constriction`은 평균 정확도 순위 **1.60**으로 공동 1위를 기록했습니다. 평균 손실 순위는 `constriction` **1.60**, `inertia` **1.80**이었으며, 이는 이 다섯 워크로드의 표본 평균 서열입니다.
---
## 4. 메인 시각화 차트 (Main Figures)
벤치마크 결과 시각화 차트는 `history_plt/` 디렉터리에 저장되어 있습니다.
| 정확도 비교 (Accuracy) | 손실 비교 (Loss) |
| --- | --- |
| ![Main Accuracy](history_plt/pso_v4_accuracy.png) | ![Main Loss](history_plt/pso_v4_loss.png) |
| 순위 히트맵 (Rank Heatmap) | 실행 시간 비교 (Runtime) |
| --- | --- |
| ![Rank Heatmap](history_plt/pso_v4_rank_heatmap.png) | ![Runtime](history_plt/pso_v4_runtime.png) |
---
## 5. MNIST Ablation 연구 (MNIST 10-Profile Ablation Study)
MNIST PCA32 `Linear(32,10)` 워크로드(파티클 30, 세대 80, 시드 46~50)에서 초기화, 적합도 평가, 수렴 제어, 미세조정, 적응형 경로 모멘트 하이퍼파라미터 변형 10개 프로필을 비교 분석하였습니다.
### 5.1 Ablation 종합 성과 매트릭스
| 프로필 식별자 (`profile`) | 검증 정확도 (Eval Acc) | 검증 손실 (Eval Loss) | 학습 정확도 (Train Acc) | 학습 손실 (Train Loss) | 실행 시간 (Runtime) | Acc 순위 | Loss 순위 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `tuned_adam_100_lr.01` | **85.58% ± 0.24%** | **0.470271 ± 0.011931** | 91.58% ± 0.39% | 0.298524 ± 0.012987 | 4.991s ± 0.434s | 1 | 1 |
| `adaptive_moment_.10` | **63.00% ± 1.83%** | 1.243339 ± 0.102561 | 70.99% ± 1.28% | 0.978244 ± 0.021198 | 4.287s ± 0.289s | 2 | 5 |
| `tuned_full_evaluation` | **62.54% ± 2.80%** | **1.179009 ± 0.050059** | 68.79% ± 1.07% | 1.005140 ± 0.028281 | 3.377s ± 0.469s | 3 | 2 |
| `inertia_tuned` (기준) | 61.62% ± 5.13% | 1.236600 ± 0.124281 | 69.31% ± 2.93% | 0.999514 ± 0.075496 | 3.132s ± 0.409s | 4 | 3 |
| `tuned_particle_reset` | 61.62% ± 5.13% | 1.236600 ± 0.124281 | 69.31% ± 2.93% | 0.999514 ± 0.075496 | 4.176s ± 0.449s | 5 | 4 |
| `tuned_no_mutation` | 60.42% ± 3.04% | 1.249836 ± 0.084985 | 66.86% ± 1.28% | 1.074396 ± 0.043372 | 3.348s ± 0.343s | 6 | 6 |
| `tuned_uniform_initialization` | 56.98% ± 2.40% | 1.619196 ± 0.157329 | 61.60% ± 0.90% | 1.345857 ± 0.048557 | 4.307s ± 0.443s | 7 | 8 |
| `adaptive_moment_.25` | 56.32% ± 2.94% | 1.499979 ± 0.119449 | 63.79% ± 2.42% | 1.228023 ± 0.088967 | 3.291s ± 0.426s | 8 | 7 |
| `adaptive_moment_.50` | 51.86% ± 3.91% | 1.683781 ± 0.121571 | 57.05% ± 2.75% | 1.463700 ± 0.097485 | 3.203s ± 0.270s | 9 | 9 |
| `inertia_canonical` | 46.76% ± 2.44% | 1.720983 ± 0.118092 | 52.77% ± 2.89% | 1.528197 ± 0.079997 | 3.120s ± 0.306s | 10 | 10 |
### 5.2 MNIST Ablation 시각화 차트
![MNIST Ablation](history_plt/pso_v4_mnist_ablation.png)
### 5.3 `inertia_tuned` 기준 상대 델타 (Paired Deltas vs `inertia_tuned`)
`inertia_tuned` 프로필(Eval Acc: **61.62%**, Eval Loss: **1.236600**, Runtime: **3.132s**) 대비 각 변형 프로필의 절대 편차량입니다:
1. **`inertia_canonical` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **-14.86%p** (46.76% vs 61.62%)
- $\Delta$ Eval Loss: **+0.484383** (1.720983 vs 1.236600)
- $\Delta$ Runtime: **-0.012s** (3.120s vs 3.132s)
- *해석*: 인지·사회 계수, 관성, 속도 제한 및 변이를 함께 조정한 튜닝 프로필이 이 PCA32 워크로드에서 canonical inertia 프로필보다 14.86%p 높은 평균 정확도를 기록했습니다. 개별 요소의 기여는 아래 단일요인 비교로만 제한적으로 해석해야 합니다.
2. **`tuned_no_mutation` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **-1.20%p** (60.42% vs 61.62%)
- $\Delta$ Eval Loss: **+0.013236** (1.249836 vs 1.236600)
- $\Delta$ Runtime: **+0.216s** (3.348s vs 3.132s)
- *해석*: `mutation_swarm=0.02`를 제거한 프로필의 평균 정확도가 1.20%p 낮았습니다. $n=5$ 변동 범위가 겹치므로, 변이가 국소 최적점 탈출을 입증했다고 단정할 수는 없습니다.
3. **`tuned_full_evaluation` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **+0.92%p** (62.54% vs 61.62%)
- $\Delta$ Eval Loss: **-0.057591** (1.179009 vs 1.236600)
- $\Delta$ Runtime: **+0.245s** (3.377s vs 3.132s)
- *해석*: 미분 무관 프로필 중 최저 손실(**1.179009**)을 기록했습니다. 단, 2,000개 고정 서브셋 평가인 `inertia_tuned`와 달리 3,000개 전체 데이터셋 평가 방식이 적용되어 샘플 수 차이 및 배치 연산 차이에 따른 교란 요인(Confound)이 존재합니다.
4. **`tuned_uniform_initialization` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **-4.64%p** (56.98% ± 2.40% vs 61.62%)
- $\Delta$ Eval Loss: **+0.382596** (1.619196 vs 1.236600)
- $\Delta$ Runtime: **+1.175s** (4.307s vs 3.132s)
- *해석*: PyTorch 가중치 기준 노이즈 부가 대신 유니폼 무작위 위치 초기화를 사용할 경우 신경망 적합도 탐색에 불리함을 나타냅니다.
5. **`tuned_particle_reset` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **+0.00%p** (61.62% vs 61.62%)
- $\Delta$ Eval Loss: **0.000000** (1.236600 vs 1.236600)
- $\Delta$ Runtime: **+1.045s** (4.176s vs 3.132s)
- *해석*: 최종 평가지표는 기준과 완전히 동일했지만 재초기화 이벤트 텔레메트리가 없으므로, 정체 조건 미충족과 전역 최적해 비영향을 구분할 수 없습니다. 추가 이벤트 계측 없이 수렴 개선 효과를 주장하지 않습니다.
6. **`tuned_adam_100_lr.01` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **+23.96%p** (85.58% vs 61.62%)
- $\Delta$ Eval Loss: **-0.766329** (0.470271 vs 1.236600)
- $\Delta$ Runtime: **+1.859s** (4.991s vs 3.132s)
- *해석*: 전체 10개 프로필 중 가장 높은 정확도와 가장 낮은 손실을 기록했습니다. 다만, 본 기법은 순수 미분 무관 기법이 아닌 PSO 수렴 위치 후 100 세대 역전파 Adam 미세조정(Refinement)을 수행하는 경사도 활용(Gradient-assisted) 하이브리드 기법입니다.
7. **`adaptive_moment_.10` vs `inertia_tuned`**:
- $\Delta$ Eval Acc: **+1.38%p** (63.00% vs 61.62%)
- $\Delta$ Eval Loss: **+0.006739** (1.243339 vs 1.236600)
- $\Delta$ Runtime: **+1.155s** (4.287s vs 3.132s)
- *해석*: 미분 무관(Derivative-Free) 프로필 중 **최고 검증 정확도(63.00% ± 1.83%)**를 기록했습니다. 정확도는 `inertia_tuned` 대비 1.38%p 우수하나, 검증 손실은 1.243339로 `inertia_tuned` (1.236600) 대비 약간 높게 유지되었습니다.
8. **`adaptive_moment_.25``.50` vs `inertia_tuned`**:
- `.25`: $\Delta$ Eval Acc **-5.30%p** (56.32%), $\Delta$ Eval Loss **+0.263379** (1.499979)
- `.50`: $\Delta$ Eval Acc **-9.76%p** (51.86%), $\Delta$ Eval Loss **+0.447181** (1.683781)
- *해석*: 이 3개 혼합비 설정에서는 $\lambda$가 0.10에서 0.25, 0.50으로 증가할수록 평균 정확도가 낮아지고 손실이 높아지는 패턴이 관찰되었습니다. 다른 모델·예산으로 일반화되는 단조 관계로 해석하지 않습니다.
## 6. 확장 튜닝 및 파티클 스케일링 (Tuning Protocol 1.0.0)
### 6.1 방법론과 데이터 분리
- **모델/데이터**: MNIST 첫 학습 3,000개와 테스트 1,000개, PCA32 whitening, `Linear(32,10)`, `CrossEntropyLoss`.
- **Search**: 첫 3,000개를 stratified 2,400 inner-train / 600 validation으로 분리하고 PCA를 inner-train에만 적합했습니다. 5개 기법의 32개 후보를 파티클 30개, 80세대, 시드 51~53에서 비교하고 평균 validation accuracy 내림차순, 동률 시 loss 오름차순으로 기법별 후보를 선택했습니다.
- **Confirmation**: 선택된 기법별 후보를 전체 학습 3,000개로 다시 적합했습니다. PCA도 전체 학습 데이터에만 다시 적합하고 search에 사용하지 않은 테스트 1,000개를 시드 61~65에서 평가했습니다.
- **Scaling**: 선택된 Adaptive Moment 후보를 시드 71~75에서 파티클 30/60/90/120개로 평가했습니다. 80세대 고정과 약 2,400 particle-epochs 고정을 분리했습니다.
- **공통 조건**: `fixed_subset=2000`, batch 1,000, 반사 경계 ±3, 미분 무관, Adam refinement 없음. 시간은 기법별 비계측 웜업 후 `fit()`만 측정했습니다.
Search가 테스트셋을 사용하지 않도록 데이터와 PCA 적합 범위를 분리했습니다. 모든 161개 레코드는 MPS에서 완료되었고 오류는 0건이었습니다. 총 계측 fit 시간은 search **259.479s**, confirmation **64.745s**, 중복을 제외한 scaling **184.978s**였습니다.
### 6.2 검증 선택 결과
| 기법 | 선택 후보 | Validation Accuracy | Validation Loss |
| --- | --- | ---: | ---: |
| `local_best` | `local_best_r4_constant` | **72.44%** | **0.944913** |
| `constriction` | `constriction_c205_canonical` | 70.89% | 0.984401 |
| `adaptive_moment` | `am_b0.06_s0.5` | 70.17% | 0.950972 |
| `inertia` | `inertia_asymmetric` | 69.72% | 1.029132 |
| `quantum` | `quantum_beta_0.4_0.9` | 56.50% | 1.337709 |
Adaptive Moment 선택값은 $c_0=c_1=1.49618$, $w=0.7298$, velocity limit ratio 0.025, mutation 0.02, `moment_blend=0.06`, `moment_step_size=0.5`, $\beta_1=0.9$입니다. 이는 저장소 기본값이 아니라 이 search 범위에서 선택된 후보입니다.
### 6.3 Held-out 테스트 확인
| 기법 | 선택 후보 | Test Accuracy (Mean ± SD) | Test Loss (Mean ± SD) | Fit Time (Mean ± SD) |
| --- | --- | ---: | ---: | ---: |
| `local_best` | `local_best_r4_constant` | **62.64% ± 2.94%** | **1.211368 ± 0.059575** | 2.512s ± 0.048s |
| `inertia` | `inertia_asymmetric` | 62.30% ± 3.01% | 1.212971 ± 0.078548 | 2.570s ± 0.035s |
| `adaptive_moment` | `am_b0.06_s0.5` | 61.06% ± 0.91% | 1.229627 ± 0.022179 | 2.943s ± 0.256s |
| `constriction` | `constriction_c205_canonical` | 60.66% ± 2.30% | 1.246275 ± 0.044657 | 2.521s ± 0.045s |
| `quantum` | `quantum_beta_0.4_0.9` | 48.58% ± 2.71% | 1.519246 ± 0.052511 | 2.403s ± 0.022s |
![Extended Tuning](history_plt/pso_v4_extended_tuning.png)
검증 1위였던 `local_best`가 held-out 확인에서도 가장 높은 평균 정확도와 가장 낮은 평균 손실을 기록했습니다. `local_best``inertia`는 동일 30×80 예산에서 Adaptive Moment보다 높은 평균 정확도를 기록했습니다. $n=5$이므로 이 순서의 통계적 유의성이나 다른 워크로드로의 일반화를 주장하지 않습니다.
### 6.4 Adaptive Moment 파티클 스케일링
| 비교 방식 | 파티클 | 세대 | Particle-epochs | Test Accuracy (Mean ± SD) | Test Loss (Mean ± SD) | Fit Time (Mean ± SD) | 30×80 대비 정확도 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| Fixed epochs | 30 | 80 | 2,400 | 62.12% ± 3.07% | 1.193469 ± 0.086618 | 2.685s ± 0.090s | 기준 |
| Fixed epochs | 60 | 80 | 4,800 | 65.94% ± 0.87% | 1.074629 ± 0.046106 | 5.586s ± 0.517s | +3.82%p |
| Fixed epochs | 90 | 80 | 7,200 | 70.32% ± 2.16% | 0.931880 ± 0.038073 | 8.627s ± 0.448s | +8.20%p |
| Fixed epochs | 120 | 80 | 9,600 | **72.34% ± 1.82%** | **0.902448 ± 0.058380** | 11.174s ± 0.718s | **+10.22%p** |
| Fixed particle-epochs | 30 | 80 | 2,400 | 62.12% ± 3.07% | 1.193469 ± 0.086618 | 2.685s ± 0.090s | 기준 |
| Fixed particle-epochs | 60 | 40 | 2,400 | 50.74% ± 2.66% | 1.534634 ± 0.078628 | 2.786s ± 0.095s | -11.38%p |
| Fixed particle-epochs | 90 | 27 | 2,430 | 47.82% ± 6.31% | 1.610771 ± 0.098843 | 3.109s ± 0.203s | -14.30%p |
| Fixed particle-epochs | 120 | 20 | 2,400 | 41.06% ± 2.36% | 1.800035 ± 0.072261 | 3.029s ± 0.179s | -21.06%p |
![Adaptive Moment Particle Scaling](history_plt/pso_v4_particle_scaling.png)
80세대를 유지한 비교에서는 60, 90, 120개 파티클이 각각 **+3.82%p**, **+8.20%p**, **+10.22%p**였고 각 파티클 수에서 5개 paired seed가 모두 30개 기준보다 높았습니다. 그러나 120개 설정의 평균 fit 시간은 30개 대비 **4.16배**였습니다. 약 2,400 particle-epochs를 고정하면 더 많은 파티클 때문에 세대가 40/27/20으로 줄어 모든 비교에서 정확도가 낮았습니다. 이 결과는 파티클 수 자체의 무비용 효과가 아니라 추가 평가 예산과 충분한 세대 수의 결합 효과를 보여줍니다.
### 6.5 확장 연구 해석 한계
1. Search는 시드 3개, confirmation/scaling은 시드 5개로 제한됩니다. 통계적 유의성 검정이나 보편적 우위를 주장하지 않습니다.
2. PCA32 선형 MNIST 한 워크로드의 결과이며 전체 MNIST, CNN, 대형 모델로 직접 일반화할 수 없습니다.
3. validation 평균이 가까운 후보는 시드 수가 늘면 선택 순서가 바뀔 수 있습니다.
4. 테스트셋은 search에 사용하지 않았지만, 공개된 scaling 비교를 추가적인 테스트셋 기반 하이퍼파라미터 선택으로 사용해서는 안 됩니다.
5. fixed-epochs 비교는 총 particle-evaluations가 2,400에서 9,600으로 증가하므로 compute-equal 비교가 아닙니다. fixed particle-epochs도 디바이스 벡터화와 세대별 오버헤드까지 동일하게 만들지는 않습니다.
6. 120×80 replication은 같은 PCA32 데이터와 테스트셋을 다시 평가한 수치 재현성 검증입니다. fresh seed는 스웜 난수에 대해서만 독립적이며, 새로운 표본이나 외부 데이터셋에 대한 독립 검증은 아닙니다.
### 6.6 120p×80e 파티클 스케일링 재현성 검증 (Replication Protocol 1.0.0)
발표된 Adaptive Moment 120-particle × 80-epoch fixed-epoch 결과를 별도 수트([`test/reproduce_scaling.py`](test/reproduce_scaling.py))로 다시 실행했습니다. 데이터 fingerprint `dfe645918ece54c0`, 선택 후보 `am_b0.06_s0.5`, MPS, PyTorch 2.13.0, pso2keras 4.0.0을 baseline과 일치시켰습니다.
사전 선언 합격 조건은 (1) 시드 71~75 exact replay의 시드별 테스트 정확도 최대 절대 차이 $\le 0.005$와 초기 모델 fingerprint 일치, (2) 시드 81~85 fresh-seed 평균 정확도의 baseline 대비 절대 차이 $\le 0.03$, (3) 두 집단 95% t-신뢰구간 중첩입니다.
| 집단 | 시드 | Test Accuracy (Mean ± SD) | 95% t-CI | Baseline 대비 | 판정 |
| --- | --- | ---: | ---: | ---: | --- |
| Baseline | 71~75 | 72.34% ± 1.82% | [70.08%, 74.60%] | 기준 | 기준 |
| Exact replay | 71~75 | 72.34% ± 1.82% | [70.08%, 74.60%] | 시드별 최대 차이 **0.00%p** | **PASS** |
| Fresh seed | 81~85 | **73.60% ± 1.66%** | [71.54%, 75.66%] | **+1.26%p** | **PASS** |
Exact replay의 초기 모델 fingerprint는 5개 시드 모두 baseline과 일치했습니다. Fresh-seed 평균 차이는 1.26%p로 3%p 허용 범위 안이고 두 95% t-신뢰구간도 중첩되어 전체 판정은 **PASS**입니다. 추가 적합 실행은 10회이며 모두 MPS에서 오류 없이 완료되었습니다.
- **검증 실행 명령**: `uv run --locked --extra examples python test/reproduce_scaling.py --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v4_120p80_replication.json`](benchmark_results/pso_v4_120p80_replication.json), [`benchmark_results/pso_v4_120p80_replication.csv`](benchmark_results/pso_v4_120p80_replication.csv)
### 6.7 120p epoch 확장 수렴 진단 (Epoch Convergence Protocol 1.0.0)
`am_b0.06_s0.5`, 파티클 120개, 시드 71~75를 각각 240세대까지 한 번에 연속 실행하고 20세대 간격의 global-best 체크포인트를 같은 테스트셋에서 진단했습니다. Epoch 80의 시드별 정확도는 기존 120×80 baseline과 최대 차이 0.00%p로 일치하여 궤적 prefix가 재현됐습니다.
| Epoch | Particle-epochs | Training Best Loss (Mean ± SD) | Test Accuracy (Mean ± SD) | Test Loss (Mean ± SD) |
| ---: | ---: | ---: | ---: | ---: |
| 80 | 9,600 | 0.684245 ± 0.046414 | 72.34% ± 1.82% | 0.902448 ± 0.058380 |
| 120 | 14,400 | 0.508942 ± 0.026551 | 78.56% ± 0.88% | 0.686080 ± 0.033467 |
| 160 | 19,200 | 0.426009 ± 0.011667 | 81.58% ± 1.17% | 0.599888 ± 0.027074 |
| 200 | 24,000 | 0.385320 ± 0.008996 | 82.58% ± 0.64% | 0.555652 ± 0.020276 |
| 240 | 28,800 | **0.359582 ± 0.009652** | **83.52% ± 1.05%** | **0.515820 ± 0.018827** |
사전 기준은 (1) 80→240 mean training loss 감소율 1% 이상이면 post-80 optimization 지속, (2) mean test accuracy +1%p 이상이면 유의미한 held-out 개선, (3) 200→240 loss 감소율 1% 미만과 정확도 절대 변화 0.5%p 미만을 동시에 만족하면 late plateau로 분류하는 방식입니다.
실측 80→240 training loss 감소율은 **47.30%**, 정확도 증가는 **+11.18%p**였고 5개 시드 모두 정확도가 상승했습니다. 200→240에서도 loss가 **6.68%** 감소하고 정확도가 평균 **+0.94%p** 변했으며 5개 중 4개 시드가 상승했습니다. 각 시드의 마지막 training-best 갱신 epoch는 240/240/240/239/240이었습니다. 따라서 early stagnation, overfitting, late plateau는 모두 false입니다. 다만 구간별 정확도 이득은 80→120 **+6.22%p**, 120→160 **+3.02%p**, 160→200 **+1.00%p**, 200→240 **+0.94%p**로 감소해 한계효용은 줄고 있습니다.
![Adaptive Moment Epoch Convergence](history_plt/pso_v4_epoch_convergence.png)
- **실행 명령**: `uv run --locked --extra examples python test/epoch_convergence.py --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v4_epoch_convergence.json`](benchmark_results/pso_v4_epoch_convergence.json), [`benchmark_results/pso_v4_epoch_convergence.csv`](benchmark_results/pso_v4_epoch_convergence.csv)
- **해석 제한**: 체크포인트 테스트 정확도는 같은 테스트셋을 반복 관찰한 진단값이며, epoch 선택이나 자동 중단에 사용해서는 안 됩니다. 실제 stopping rule은 별도 validation split에 정의해야 합니다.
### 6.8 전체 MNIST 60,000/10,000 학습 (Full MNIST Protocol 1.0.0)
공식 MNIST train 60,000개와 test 10,000개 전체를 사용했습니다. 픽셀 정규화와 flatten 후 PCA32 whitening을 train 60,000개에만 적합했으며 설명 분산 비율 합은 0.743600입니다. 고정된 `am_b0.06_s0.5``evaluation="full"`, `fitness_size=None`, batch 60,000, 파티클 120개, 240 epochs, 시드 71~75로 실행했습니다. 따라서 각 파티클은 매 epoch마다 학습 60,000개 전체에서 평가됐습니다.
| Epoch | Training Best Loss (Mean ± SD) | Full Test Accuracy (Mean ± SD) | Full Test Loss (Mean ± SD) | 2k-fitness study 대비 |
| ---: | ---: | ---: | ---: | ---: |
| 80 | 0.768448 ± 0.026961 | 77.67% ± 1.50% | 0.725023 ± 0.033552 | +5.33%p |
| 120 | 0.589822 ± 0.014141 | 83.28% ± 0.83% | 0.551133 ± 0.018616 | +4.72%p |
| 160 | 0.511683 ± 0.013526 | 85.56% ± 0.56% | 0.482441 ± 0.012016 | +3.98%p |
| 200 | 0.465194 ± 0.010498 | 86.85% ± 0.32% | 0.438875 ± 0.008499 | +4.27%p |
| 240 | **0.436664 ± 0.008101** | **87.70% ± 0.36%** | **0.412078 ± 0.006848** | **+4.18%p** |
80→240에서 training best loss는 **43.18%** 감소하고 테스트 정확도는 **+10.03%p** 상승했습니다. 200→240에서도 loss가 **6.13%** 감소하고 정확도가 **+0.85%p** 상승했으며 모든 시드가 개선됐습니다. 5개 시드 모두 마지막 training-best가 epoch 240에서 갱신되어 full-data 조건에서도 late plateau는 false입니다.
이 프로토콜은 5개 시드 합계 144,000 particle-epochs와 8,640,000,000 particle-sample evaluations를 수행했습니다. 계측된 `fit()` 합계는 165.856초입니다. Full-data 정확도는 공통 checkpoint마다 2k-fitness 연구보다 3.98~5.33%p 높았지만, PCA 적합 범위, fitness objective, evaluation plugin의 RNG 소비가 함께 바뀌므로 정확도 차이는 기술적 비교이며 인과 추정이 아닙니다. 서로 다른 학습 objective의 training loss 절댓값도 직접 비교하지 않습니다.
![Full MNIST Trajectory](history_plt/pso_v4_full_mnist.png)
- **실행 명령**: `uv run --locked --extra examples python test/full_mnist_study.py --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v4_full_mnist.json`](benchmark_results/pso_v4_full_mnist.json), [`benchmark_results/pso_v4_full_mnist.csv`](benchmark_results/pso_v4_full_mnist.csv)
- **해석 제한**: 공식 split 전체를 사용했지만 입력은 PCA32이고 모델은 `Linear(32,10)`입니다. 원본 784차원 PSO나 CNN 결과가 아닙니다. 반복 test checkpoint는 사후 진단일 뿐 stopping rule 선택에 사용하지 않습니다.
### 6.9 원본 MNIST 딥 신경망 아키텍처 및 최적화기 비교 (Deep Accuracy Protocol 1.0.0)
PCA 차원 축소 없이 공식 MNIST 데이터셋 전체(학습 60,000개, 테스트 10,000개) 원본 $1 \times 28 \times 28$ 입력을 대상으로 딥 신경망 아키텍처 및 최적화기 특성을 다중 트랙으로 평가했습니다 (`Deep Accuracy Protocol 1.0.0`, $n=3$, seeds 101~103). 픽셀 정규화 mean(`0.13066`)과 std(`0.308108`)는 학습 60,000개에서만 산출하여 평가 데이터 누수를 차단했습니다. 테스트 10,000개는 히스토리 모니터링 전용으로 사용되었으며, 테스트 성능에 기반한 하이퍼파라미터 선택이나 조기 종료(stopping rule)는 수행하지 않았습니다.
실험 조건: Apple Silicon MPS, PyTorch 2.13.0, batch size 256, lr 0.001. Adam 실행은 10 epochs, PSO 실행은 파티클 30개 × 40세대에 2,000개 고정 서브셋(`fitness_size=2000`) 평가 및 선택된 Adaptive Moment 후보 설정(`am_b0.06_s0.5`, `c0=c1=1.49618`, `w=0.7298`, `velocity_limit_ratio=0.025`, `mutation_swarm=0.02`, `moment_step_size=0.5`)을 적용했습니다. 하이브리드(PSO→Adam) 방식은 PSO 40세대 탐색 후 Adam 10세대를 수행하므로 pure Adam 대비 추가 PSO 계산량을 포함하는 비동등 계산량 설정입니다.
#### 트랙 1: 아키텍처 레인 성과 (Adam 10 Epochs, Mean ± Sample SD, $n=3$)
| 아키텍처 (`arch`) | 모델 구조 및 파라미터 수 | 테스트 정확도 (Mean ± SD) | 테스트 손실 (Mean ± SD) | 98% 정확도 도달 속도 |
| --- | --- | ---: | ---: | --- |
| `raw_linear` | `Linear(784, 10)` (7,850 params) | 92.45% ± 0.10% | 0.268945 ± 0.002050 | 미도달 |
| `raw_mlp` | `Linear(784,128) - ReLU - Linear(128,64) - ReLU - Linear(64,10)` (109,386 params) | 97.70% ± 0.08% | 0.078368 ± 0.002818 | 미도달 |
| `compact_cnn` | `Conv2d(1,8,3) - ReLU - MaxPool2d - Conv2d(8,16,3) - ReLU - MaxPool2d - Linear(784,10)` (9,098 params) | **98.53% ± 0.16%** | **0.043809 ± 0.004907** | **5 Epoch 이내 전 시드 달성** |
`compact_cnn` 아키텍처의 경우 시드 101(4 epoch), 시드 102(3 epoch), 시드 103(5 epoch)에서 모두 5 epoch 이내에 테스트 정확도 98% 이상을 달성했습니다.
#### 트랙 2: 최적화기 레인 성과 (Compact CNN 9,098 Params, Mean ± Sample SD, $n=3$)
| 최적화 기법 (`optimizer`) | 실행 구성 | 테스트 정확도 (Mean ± SD) | 테스트 손실 (Mean ± SD) | 판정 및 특성 |
| --- | --- | ---: | ---: | --- |
| `adam_only` | Adam 10 epochs | **98.53% ± 0.16%** | **0.043809 ± 0.004907** | 역전파 경사하강법 기반 최적화 |
| `pso_only` | PSO 40 epochs (30p × 40e) | 36.76% ± 3.76% | 14.104417 ± 8.075411 | 고차원 전가중치 수렴 실패 |
| `hybrid` (PSO→Adam) | PSO 40e + Adam 10e | 97.30% ± 0.75% | 0.086542 ± 0.023969 | 추가 탐색 연산에도 pure Adam 대비 저조 |
![Deep Accuracy Trajectory](history_plt/pso_v4_deep_accuracy.png)
- **실행 명령**: `uv run --locked --extra examples python test/deep_accuracy_study.py --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v4_deep_accuracy.json`](benchmark_results/pso_v4_deep_accuracy.json), [`benchmark_results/pso_v4_deep_accuracy.csv`](benchmark_results/pso_v4_deep_accuracy.csv)
- **결과 해석 및 한계점**:
1. **아키텍처 인덕티브 바이어스**: Adam 조건을 고정한 아키텍처 레인에서 Compact CNN은 MLP보다 약 1/12의 파라미터로 정확도가 0.83%p 높았습니다. 원본 이미지의 공간적 구조를 사용하는 것이 측정된 차이에 중요했습니다.
2. **고차원 전가중치 PSO의 한계**: 이 프로토콜의 30p × 40e·fixed-2k 예산에서 9,098개 CNN 파라미터를 직접 탐색한 PSO는 36.76% ± 3.76%였습니다. 이 결과는 해당 설정에서 역전파를 대체하지 못했음을 보이지만, 더 큰 예산이나 다른 간접 인코딩을 포함한 PSO의 이론적 한계를 증명하지는 않습니다.
3. **하이브리드 초기화 결과**: PSO→Adam은 97.30% ± 0.75%로 동일한 10-epoch pure Adam의 98.53% ± 0.16%보다 낮았습니다. 추가 PSO 계산량도 포함되므로 측정한 초기화 방식의 실용적 이점은 관측되지 않았습니다.
4. **표본 및 적용 범위 한계**: 본 실험은 $n=3$ 기술적(descriptive) 표본 측정 결과이며 통계적 유의성이나 일반적 보편 우위로 확언하지 않습니다.
### 6.10 희소 부호 해시 부분공간·단계적 평가·스웜 앙상블 탐색 (MNIST-PSO-RAW-V5 1.0.0)
공식 MNIST 데이터셋(학습 60,000개, 테스트 10,000개) 원본 $1 \times 28 \times 28$ 입력을 대상으로 Compact CNN 아키텍처(9,098개 파라미터)의 고정 희소 부호 해시 부분공간, 단계적 표본 확장 평가, 검증 다양성 기반 스웜 앙상블 탐색을 수행했습니다 (`MNIST-PSO-RAW-V5 1.0.0`).
학습 데이터 60,000개는 탐색 전용 학습 세트 50,000개와 검증 세트 10,000개로 분할되었으며 (`split_seed=20260902`, `split_fingerprint=51b289d9f503a9f3`), 픽셀 정규화 평균(`0.130682`) 및 표준편차(`0.308127`)는 50,000개 탐색 학습 세트에서만 산출하여 데이터 누수를 차단했습니다. 부분공간 차원과 최종 단일 모델·앙상블 구성은 검증 세트에서 선택했으며, 테스트 세트 10,000개는 선택 완료 후 단일 모델과 앙상블의 최종 엔드포인트에만 사용했습니다.
#### 트랙 1: 부분공간 차원 탐색 파일럿 (Pilot Subspace Search, 30p × 160e, fixed-2k subset)
파티클 30개 × 160세대 고정 2,000개 서브셋 조건에서 결정론적 희소 부호 해시 부분공간(290, 1024, 4096차원)과 전가중치(9,098차원) 탐색 성능을 비교했습니다 (`pilot_seed=91`). 손실 함수는 Cross-Entropy Loss를 주 목적(primary)으로 하고 Accuracy를 동률 처리(tiebreak) 지표로 사용했습니다.
| 부분공간 차원 (`dimension`) | 검증 정확도 (%) | 검증 손실 (CE Loss) | 쿼리 수 (Queries) | 샘플 평가 수 (Sample Evals) | 계산 시간 (Wall Time) |
| --- | ---: | ---: | ---: | ---: | ---: |
| `290` | 22.33% | 2.216933 | 4,800 | 9,600,000 | 6.0698s |
| `1024` | 41.34% | 1.960143 | 4,800 | 9,600,000 | 6.0666s |
| `4096` | 55.97% | 1.550738 | 4,800 | 9,600,000 | 6.2638s |
| `full` (9,098-D) | **67.18%** | **1.205564** | 4,800 | 9,600,000 | 6.1629s |
파일럿 예산에서는 전가중치 9,098차원 탐색의 검증 정확도(67.18%)가 가장 높고 손실(1.205564)이 가장 낮아, 확인 프로토콜의 차원으로 선택했습니다.
#### 트랙 2: 단계적 표본 확장 확인 프로토콜 (Confirmation Protocol, 60p × 600e, $n=3$)
파티클 60개 × 600세대 설정으로 3개 무작위 시드(101, 102, 103)에 대해 단계적 표본 확장 스케줄(`2000:420,10000:135,50000:45`)을 적용했습니다. Epoch 1~420은 2,000개 서브셋, 421~555는 10,000개 서브셋, 556~600은 50,000개 전체 탐색 세트로 계층적 평가를 진행했습니다. Epoch 421 및 556의 새 목적함수 평가 전에 모든 파티클의 pbest 적합도를 새 서브셋으로 전수 재평가한 후 gbest를 재구성하여 서로 다른 목적함수의 과거 점수를 직접 비교하지 않았습니다.
- **확인 실행 검증 요약** ($n=3$, seeds 101~103):
- Validation Best-pbest Accuracy (Mean ± SD): **82.34% ± 0.35%** (std: 0.346987%, 95% t-CI: ±0.861973%)
- Validation Best-pbest NLL (Mean ± SD): **0.592376 ± 0.018705** (95% t-CI: ±0.046467)
#### 트랙 3: 최종 엔드포인트 및 스웜 앙상블 성과 (Final Endpoint & Swarm Ensemble)
검증 세트 성능에 따라 선택된 단일 최적 모델(Seed 103, Particle 54)과 검증 다양성 기준 상위 Top-5 파티클로 구성된 스웜 앙상블(Top-5 distinct pbest models across seeds: 103/P54, 102/P50, 101/P48, 102/P29, 101/P22)의 최종 공식 Held-out 테스트(10,000개) 성과 비교입니다.
| 엔드포인트 구분 (`endpoint`) | 모델 구성 및 선택 기준 | 검증 정확도 | 검증 NLL | 테스트 정확도 | 테스트 NLL | 테스트 Brier | 테스트 ECE | 상호 불일치도 (Disagreement) |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| **단일 최적 모델** (`single_model`) | Validation NLL 최저 (Seed 103, P54) | 82.64% | 0.571431 | 83.23% | 0.556332 | 0.253921 | 0.085242 | - |
| **스웜 앙상블 (Top-5)** (`ensemble`) | Validation 다양성 Top-5 pbest | 86.60% | 0.548785 | **87.00%** | **0.535928** | **0.236233** | 0.164284 | 0.1585 |
| **앙상블 대비 단일 차이** ($\Delta$) | Ensemble - Single | +3.96%p | -0.022646 | **+3.77%p** | **-0.020404** | **-0.017688** | **+0.079042** | - |
#### 자원 사용량 계측 (Resource Accounting)
- **총 후보 목적함수 평가 쿼리 수**: 127,560 회 (파일럿 19,200 + 확인 108,360)
- **총 샘플 수준 평가 수**: 848,400,000 회 (848.4M)
- **합산 최적화 벽시계 시간**: 473.4657 초 (파일럿 24.5631초 + 확인 448.9026초)
![V5 Deep Methods Trajectory](history_plt/pso_v5_deep_methods.png)
- **실행 명령**: `uv run --locked --extra examples python test/deep_pso_methods.py --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v5_deep_methods.json`](benchmark_results/pso_v5_deep_methods.json), [`benchmark_results/pso_v5_deep_methods.csv`](benchmark_results/pso_v5_deep_methods.csv)
- **결과 해석 및 특성**:
1. **파일럿 차원 비교**: 290~4096차원 희소 부호 해시 부분공간은 동일한 30p × 160e 파일럿 예산에서 전가중치(9,098차원)보다 검증 정확도가 낮고 NLL이 높았습니다. 다른 투영, 차원, 예산의 결과까지 일반화하지 않습니다.
2. **단계적 표본 전환과 회복**: 2k→10k→50k 전환 시 새 목적함수에서 pbest를 재평가하자 손실이 일시 상승하고 정확도가 하락했으며, 이후 각 단계에서 다시 개선됐습니다. 이 점프는 서로 다른 표본 목적함수의 난이도 차이를 반영하며 동일 궤적 손실의 악화로 해석하지 않습니다.
3. **최종 단일 모델 수렴 한계**: 60p × 600e 단계적 탐색의 단일 모델 테스트 정확도는 83.23%였습니다. 보존된 v4 30p × 40e accuracy-primary PSO의 36.76%보다 높지만 목적함수·데이터 분리·예산이 모두 달라 인과적 개선량으로 해석할 수 없습니다. v4 Adam 10-epoch 평균 98.53%와도 계산 방식·예산이 비동등하며, 측정 정확도는 여전히 15.30%p 낮았습니다.
4. **앙상블 다양성 및 확률 보정(ECE) 트레이드오프**: 검증 다양성으로 선택한 Top-5 앙상블은 단일 모델보다 테스트 정확도가 3.77%p 높고 NLL/Brier가 낮았지만 ECE는 0.079042 높았습니다(0.085242 → 0.164284). 이 실행은 예측 불일치가 활용 가능한 앙상블 이득과 함께 나타날 수 있음을 보였지만, 높은 손실이나 다양성만으로 일반화·보정 향상을 판정할 수는 없습니다.
5. **종료 시점의 수렴 상태**: 50k 최종 단계에서 세 시드 모두 기록된 최저 학습 손실이 마지막 Epoch 600에 갱신됐습니다. 따라서 600세대 결과를 수렴 한계로 해석할 수 없으며, 더 긴 50k 단계의 효용은 별도 validation 기반 중단 규칙으로 확인해야 합니다.
### 6.11 V5 탐색 구조 회귀 원인 분리 (MNIST-PSO-RAW-V6 1.0.0)
V5의 83.23% 단일 모델 결과가 고차원 자체의 한계인지, 탐색 구조 변경에 따른 회귀인지 분리하기 위해 Phase A/B 진단을 수행했습니다. 공식 MNIST test split은 로드하지 않았고, train 60,000개만 `split_seed=20260902`로 search 50,000개와 validation 10,000개로 분할했습니다. 모든 비교는 동일한 결정론적 2,000개 search subset, Compact CNN 초기 모델 seed 41, CE loss-primary/accuracy-tiebreak 선택을 사용했습니다.
#### 단일시드 구조 screen (seed 91, 30p × 160e)
| ID | 핵심 변경 | Validation Accuracy | Validation NLL |
| --- | --- | ---: | ---: |
| G8 | 기존 `Optimizer` 의미론 제어군 | **70.99%** | **0.883557** |
| G5 | per-tensor SD + velocity + mutation + bound ±6 | 70.93% | 0.901510 |
| G6 | G5 + initial radius 1.5 | 69.56% | 0.961290 |
| G4 | per-tensor SD + velocity + mutation + bound ±3 | 65.76% | 1.116280 |
| G3 | G0 + mutation 0.02 | 68.07% | 1.130839 |
| G0 | V5 구조 제어군 | 67.73% | 1.183117 |
| G7 | G2 + independent positions | 58.79% | 1.291266 |
| G2 | G0 + initial velocity | 60.72% | 1.296105 |
| G1 | global RMS coordinate scale | 62.32% | 1.437604 |
한 시드에서 G3 mutation은 G0 대비 NLL을 0.052278 낮췄고, G5의 ±6 경계는 G4 대비 정확도를 5.17%p 높이고 NLL을 0.214770 낮췄습니다. 반면 초기 속도만 추가한 G2와 global RMS scale G1은 G0보다 낮았습니다. 이 screen은 확인 대상 선택용이며 단일시드 차이를 일반적 인과 효과로 확정하지 않습니다.
#### 3-시드 확인 (seeds 101103, 60p × 420e)
| ID | Validation Accuracy (Mean ± Sample SD) | Validation NLL (Mean ± Sample SD) | G8 대비 Accuracy | G8 대비 NLL |
| --- | ---: | ---: | ---: | ---: |
| G8 | **84.92% ± 0.99%** | **0.481440 ± 0.029447** | 기준 | 기준 |
| G6 | 84.25% ± 0.22% | 0.503809 ± 0.015789 | -0.67%p | +0.022369 |
| G5 | 84.24% ± 0.90% | 0.510297 ± 0.024899 | -0.68%p | +0.028857 |
| G0 | 79.53% ± 0.58% | 0.689687 ± 0.008079 | -5.39%p | +0.208247 |
| G1 | 76.95% ± 2.49% | 0.833759 ± 0.123667 | -7.96%p | +0.352319 |
G5와 G6은 사전 선언한 회복 기준(G8 대비 accuracy 1.0%p 이내, NLL 0.05 이내)을 모두 통과했습니다. 따라서 V5 G0의 격차는 9,098차원 자체만으로 설명되지 않으며, 제거했던 탐색 다양성과 좁은 normalized 경계의 묶음이 주요 원인입니다. G6은 사전 순위 규칙인 validation NLL 오름차순에서 G5보다 0.006488 낮아 다음 단계 기준으로 선택됐지만, 차이는 회복 기준보다 작습니다.
개별 요인 판정은 제한적입니다. G1은 G0보다 정확도가 2.57%p 낮고 NLL이 0.144072 높아 per-tensor SD scaling을 global RMS로 교체하는 가설은 기각됐습니다. G5/G6의 회복은 확인됐지만 G2–G4를 3개 시드로 확인하지 않았으므로 초기 속도, mutation, mutation 상호작용, 경계 확장의 독립 효과는 아직 unresolved입니다. G6과 G5 차이는 accuracy +0.01%p/NLL -0.006488로 넓은 초기 반경의 material improvement 기준을 충족하지 못했습니다.
#### 자원·누수 계측 및 다음 단계
- 후보 목적함수 쿼리: **421,200**
- 후보 샘플 평가: **842,400,000**
- 합산 최적화 시간: **596.9980초**
- 합산 validation 평가 시간: **9.1062초**
- 공식 test 데이터 로드 및 평가: **0회**
![V6 Geometry Ablation](history_plt/pso_v6_phase_b.png)
- **실행 명령**: `uv run --locked --extra examples python test/deep_pso_v6.py --phase all --device mps`
- **출력 아티팩트**: [`benchmark_results/pso_v6_phase_b.json`](benchmark_results/pso_v6_phase_b.json), [`benchmark_results/pso_v6_phase_b.csv`](benchmark_results/pso_v6_phase_b.csv)
- **다음 단계**: G6 구조를 기준으로 `sqrt(9098/d)` 반경·속도·경계 보정을 적용한 공정한 subspace 비교(Phase C)를 수행합니다. 이후에만 objective schedule과 transition moment reset을 분리합니다. CCPSO/CSO는 whole-vector validation 성능이 90% 미만에서 plateau일 때까지 보류합니다.
### 6.12 더 무거운 태스크의 전가중치 PSO 실행 가능성 (HEAVY-TASK-PSO-V6 1.0.0)
동일한 보존 기법 G0/G5/G6/G8을 더 큰 모델 및 다른 영상 분류 데이터에 적용했습니다. MNIST와 FashionMNIST 모두 공식 train split만 로드한 뒤 `split_seed=20260902`로 search 50,000/validation 10,000을 층화 분할하고, search 50,000개에서만 정규화 통계를 적합했습니다. 모델 축은 Compact CNN 9,098개와 Conv16→32·FC32 구조의 WideCNN 55,338개 파라미터입니다. 공식 test split 로드와 평가는 모두 0회입니다.
단일시드 screen은 네 workload와 G0/G5/G6/G8의 16개 셀을 12 particles × 40 epochs·fixed-2k로 평가했습니다. Validation NLL 우선, accuracy 동률 판정으로 선택된 normalized 방법은 MNIST Compact/Fashion Compact에서 G6, MNIST Wide/Fashion Wide에서 G5였습니다. 이 선택은 같은 validation split을 사용했으므로 독립적인 일반화 추정치가 아니라 확인 대상 축소 절차입니다.
3-시드 확인은 workload마다 G8과 선택된 normalized 방법을 12 particles × 80 epochs·fixed-10k로 실행했습니다.
| Workload | Method | Validation Accuracy (Mean ± Sample SD) | Validation NLL (Mean ± Sample SD) | 초기 모델 대비 Accuracy | NLL 감소율 | 사전 최적화 기준 |
| --- | --- | ---: | ---: | ---: | ---: | --- |
| MNIST Compact (9,098) | G8 | **49.15% ± 1.32%** | **1.518089 ± 0.061523** | +42.50%p | 34.71% | 통과 |
| MNIST Compact (9,098) | G6 | 45.97% ± 3.55% | 1.642081 ± 0.091398 | +39.32%p | 29.38% | 통과 |
| MNIST Wide (55,338) | G8 | 41.03% ± 4.06% | 1.733351 ± 0.084331 | +31.72%p | 25.43% | 통과 |
| MNIST Wide (55,338) | G5 | **43.86% ± 3.51%** | **1.721259 ± 0.098656** | +34.55%p | 25.95% | 통과 |
| Fashion Compact (9,098) | G8 | **47.00% ± 5.23%** | **1.511217 ± 0.168628** | +38.92%p | 34.87% | 통과 |
| Fashion Compact (9,098) | G6 | 45.62% ± 3.59% | 1.584443 ± 0.053077 | +37.54%p | 31.71% | 통과 |
| Fashion Wide (55,338) | G8 | 41.67% ± 2.27% | 1.615937 ± 0.036692 | +32.90%p | 30.08% | 통과 |
| Fashion Wide (55,338) | G5 | **46.31% ± 7.57%** | **1.525747 ± 0.149706** | +37.54%p | 33.99% | 통과 |
모든 실행은 수치적으로 finite했고 사전 feasibility 기준을 통과했으므로, 약 55k 파라미터 및 FashionMNIST 범위에서도 이 PSO 기법들이 목적함수를 낮추고 초기 정확도를 높이는 것은 확인됐습니다. 다만 최고 평균 정확도가 49.15%에 불과해 유용한 분류기 학습이나 수렴 완료는 확인되지 않았습니다. 80 epochs의 endpoint만 비교했고 plateau 판정용 궤적을 보존하지 않았으므로, 이 프로토콜은 수렴 한계나 추가 세대의 효용을 결정하지 않습니다.
모델 파라미터가 6.08배 증가하면서 12-particle core swarm state는 custom 방법 기준 약 2.08 MiB에서 12.67 MiB, G8 기준 약 2.12 MiB에서 12.88 MiB로 선형 증가했습니다. 확인 실행 처리량은 Compact CNN의 약 1.90~2.19M samples/s에서 WideCNN의 약 0.98~1.04M samples/s로 감소했습니다. G8의 Wide 모델은 Compact 모델보다 MNIST에서 정확도가 8.12%p, FashionMNIST에서 5.34%p 낮았습니다. 반면 screen-selected G5는 두 Wide workload에서 G8 평균을 앞섰지만, 방법 선택에 사용한 validation 재사용과 $n=3$의 큰 변동성(Fashion Wide G5 SD 7.57%p) 때문에 고차원 우월성으로 일반화하지 않습니다.
- 후보 목적함수 쿼리: **30,720**
- 후보 샘플 평가: **245,760,000**
- 합산 최적화 시간: **188.7283초**
- 합산 validation 평가 시간: **6.3826초**
- 공식 test 데이터 로드 및 평가: **0회**
![Heavy Task Feasibility](history_plt/pso_v6_heavy_tasks.png)
- **실행 명령**: `uv run --no-sync python test/heavy_task_feasibility.py --stage screen --device mps --out-dir /tmp/pso-v6-heavy-screen` 이후 `uv run --no-sync python test/heavy_task_feasibility.py --stage confirm --device mps --screen-artifact /tmp/pso-v6-heavy-screen/pso_v6_heavy_tasks_screen.json`
- **출력 아티팩트**: [`benchmark_results/pso_v6_heavy_tasks.json`](benchmark_results/pso_v6_heavy_tasks.json), [`benchmark_results/pso_v6_heavy_tasks.csv`](benchmark_results/pso_v6_heavy_tasks.csv)
- **판정**: 더 무거운 measured workload에서도 PSO 실행과 제한적 최적화는 가능하지만, 현재 예산의 전가중치 탐색은 실용적인 학습기로 판정하지 않습니다.
### 6.13 고정 부분공간 PSO의 품질·상태 Pareto 반복 연구 (HEAVY-PSO-AUTORESEARCH 1.0.0)
6.12의 네 workload를 대상으로 방법 제안 → 고정 평가 → 분석 → 유지/기각을 반복했습니다. 공식 test split은 끝까지 로드하지 않았고, primary evaluator는 12 particles × 80 epochs × fixed-10k, seeds 101~103, workload별 validation accuracy/NLL, 그리고 persistent core swarm state를 고정했습니다. 후보는 모든 workload에서 baseline state의 50% 이하, accuracy 하락 1%p 이하, NLL 악화 5% 이하를 만족하고, 가장 약한 baseline인 MNIST Wide에서 accuracy 2%p 또는 NLL 5% 이상을 개선해야 통과합니다. 점수는 평균 상대 NLL 개선율 + 평균 accuracy 개선(%p) + 상태 절감 보너스에서 실패 gate당 100점을 차감합니다.
#### 반복 결과와 유지 정책
초기 전역 signed-hash 부분공간은 swarm seed마다 projection도 함께 바꾸어 optimizer 변동과 표현 변동을 혼합했습니다. 별도 projection replica에서 이 문제가 확인됐고, tensor-local 비례 hash는 모든 품질 gate를 크게 악화시켜 기각했습니다. 이후 하나의 전역 projection을 swarm seed 전체에서 고정했습니다. 세 workload에는 ratio 0.5 고정 projection을 사용하고, MNIST Wide에는 기존 단일 실행에서 발견된 projection 592157828을 세 matched seed에서 다시 확인한 뒤 사용했습니다. 반경/속도/경계 multiplier 0.75와 0.5는 MNIST Wide 성능을 각각 41.71%와 36.78%로 낮춰 기각했으며 multiplier 1.0을 유지했습니다.
| Workload | Latent 구성 | State ratio | Baseline Acc / NLL | Development 101~103 Acc / NLL | Confirmation 111~113 Acc / NLL |
| --- | --- | ---: | ---: | ---: | ---: |
| MNIST Compact | fixed global, ratio 0.5, projection 1800044939 | 0.4918 | 49.1533% / 1.518089 | 49.0533% / 1.525704 | 48.4667% / 1.550212 |
| MNIST Wide | fixed global, ratio 0.5, projection 592157828 | 0.5000 | 43.8600% / 1.721259 | 48.3467% / 1.677712 | 45.7233% / 1.734905 |
| Fashion Compact | fixed global, ratio 0.5, projection 1363313651 | 0.4918 | 47.0033% / 1.511217 | 50.7967% / 1.385324 | 53.1967% / 1.328168 |
| Fashion Wide | fixed global, ratio 0.5, projection 189641451 | 0.5000 | 46.3100% / 1.525747 | 48.2633% / 1.531421 | 49.4767% / 1.513876 |
Development 정책 `fixed_global_hybrid_v3`는 모든 고정 gate를 통과했습니다. Score는 **15.030089**, 평균 accuracy 개선은 **+2.5333%p**, 평균 상대 NLL 개선은 **2.4968%**, 최대 state ratio는 **0.5**였습니다. 그러나 정책을 재선택하지 않고 swarm seeds 111~113에서 실행한 confirmation은 **한 gate만 실패**했습니다. 모든 workload의 accuracy/NLL 비열화 gate와 state/safety/accounting gate는 통과했지만 MNIST Wide 개선이 **+1.8633%p**로 사전 기준 +2%p에 0.1367%p 미달했습니다. 따라서 독립 확인된 Pareto 승리로 판정하지 않습니다.
두 seed 집합을 합친 6-seed 수치는 사후 기술 통계일 뿐 gate 판정값이 아닙니다. MNIST Compact/MNIST Wide/Fashion Compact/Fashion Wide accuracy 변화는 각각 **-0.3933/+3.1750/+4.9933/+2.5600%p**, 상대 NLL 변화는 **-1.3088/+0.8686/+10.2216/+0.2031%**였습니다. 이 결과는 고정 projection이 projection-coupled 정책보다 해석 가능하고 평균 품질·상태 Pareto를 개선할 가능성을 보이지만, 새 validation split이나 공식 test 일반화 증거는 아닙니다.
- 총 실제 실행: **312 runs**
- 총 목적함수 쿼리: **299,520**
- 총 샘플 평가: **2,995,200,000**
- 실행 wall time 합: **2,264.4978초**
- 공식 test 데이터 로드 및 평가: **0회**
- 실행기/평가기: [`test/heavy_pso_autoresearch.py`](test/heavy_pso_autoresearch.py), [`test/evaluate_heavy_autoresearch.py`](test/evaluate_heavy_autoresearch.py)
- 요약 아티팩트: [`benchmark_results/pso_v6_heavy_autoresearch.json`](benchmark_results/pso_v6_heavy_autoresearch.json), [`benchmark_results/pso_v6_heavy_autoresearch.csv`](benchmark_results/pso_v6_heavy_autoresearch.csv)
- 전체 반복 결정 로그: [`.omc/autoresearch/heavy-pso-progressive-improvement/runs/20260902T153426Z/decision-log.md`](.omc/autoresearch/heavy-pso-progressive-improvement/runs/20260902T153426Z/decision-log.md)
- **※ 주의 (후속 검증 결과)**: 후속 교차 분할 평가([`REPORT.md` §6.14](#614-heavy-pso-교차-분할-강건성-검증-heavy-pso-cross-split-100))에서 동일 정책이 개발 분할 평가 게이트를 통과하지 못했습니다. 따라서 본 절의 단일 개발 분할 수치는 새 validation split에 대한 강건성 증거가 아닙니다.
![Heavy PSO Autoresearch](history_plt/pso_v6_heavy_autoresearch.png)
### 6.14 Heavy PSO 교차 분할 강건성 검증 (HEAVY-PSO-CROSS-SPLIT 1.0.0)
§6.13의 고정 부분공간 수축 정책(`fixed_global_hybrid_v3`)이 단일 개발 분할(split 20260902) 이외의 validation 분할에서도 결과를 재현하는지 확인하기 위해, 개발 분할 20260905/20260906과 시드 101~103에서 매칭 baseline을 다시 실행했습니다. 예산은 12 particles × 80 epochs × fixed-10k로 고정했습니다 (`HEAVY-PSO-CROSS-SPLIT 1.0.0`).
#### 6.14.1 실험 설계 및 개념적 구분
1. **결정 탐색(Decision Iterations) vs 개발 변형(Development Variants)**:
- 총 8회 결정 탐색(Decision Iterations 1~8)을 통해 다양한 부분공간 하이퍼파라미터 및 해시 프로젝션을 탐색했습니다.
- Iteration 3에서 projection seed replica 1과 replica 2를 별도 평가했으므로, 8회 결정에서 총 9개 개발 변형(Development Variants)을 평가했습니다.
2. **개발 단계(Development Phase) vs 확인 단계(Confirmation Phase)**:
- **개발 단계**: 2개 무작위 개발 분할(20260905, 20260906) 및 시드 101~103 (변형당 8개 셀, 48개 실행)을 기반으로 엄격한 품질/회귀 게이트를 적용했습니다.
- **확인 단계**: 개발 단계의 모든 게이트를 통과한 보존 후보에 한해 세 번째 확인 분할(20260907) 및 시드 111~113에서 독립 확인 평가를 수행하도록 정의했습니다.
3. **검증 분할(Validation Split) vs 공식 테스트(Official Test Split)**:
- 학습 50,000개 / 검증 10,000개 층화 분할(Search/Validation)을 사용했으며, 데이터 정규화 통계는 Search 50,000개에서만 산출했습니다.
- 공식 테스트 데이터셋(10,000개)은 0회 로드 및 0회 평가로 미사용 완전히 봉인 유지되었습니다 (`official_test_data_loaded = false`, `official_test_evaluations = 0`).
4. **관측 최상위 후보(Best Observed Candidate) vs 최종 보존 정책(Retained Policy)**:
- 평가된 9개 개발 변형 중 탐색 스코어가 가장 높은 후보(Best Observed Candidate)와 모든 게이트를 통과하여 채택되는 최종 보존 정책(Retained Policy)을 명확히 구분했습니다.
#### 6.14.2 주요 결과 및 게이트 평가
1. **동결 정책 (`fixed_global_hybrid_v3`, Iteration 1)**:
- §6.13에서 수립된 고정 global projection 및 latent ratio 0.5 정책을 동결하여 교차 평가한 결과, 전체 평균 accuracy 개선은 **+0.1533%p**, NLL 감소는 **0.5546%**에 그쳤습니다.
- Accuracy·NLL·MNIST Wide 개선의 3개 development 품질 gate를 위반하여 **FAIL**로 판정했습니다. 개발 gate가 닫혔으므로 confirmation gate는 의도대로 실행하지 않았습니다.
2. **관측 최상위 후보 (Iteration 5, Largest-Tensor Hash)**:
- 8회 결정 탐색 중 최고 스코어(**-185.610686**)를 기록한 Iteration 5(텐서 크기순 비례 해시)는 overall accuracy 개선 **+0.4400%p**, NLL 감소 **3.9493%**, 최악 accuracy 회귀 **-3.0667%p**를 기록했습니다.
- 그러나 가장 민감한 게이트인 MNIST Wide accuracy에서 **-2.6633%p** (NLL **2.1874%** 악화)로 여전히 회귀가 관측되어 게이트 미달로 **FAIL** 판정되었습니다.
3. **확인 단계 과학적 보류 및 보존 실패**:
- 9개 개발 변형 전체가 개발 단계 하드 게이트를 통과하지 못함에 따라, 사전 선언된 과학적 엄격성 규칙에 의거하여 확인 분할(20260907) 평가 실행은 **완전히 보류(withheld, confirmation_executed = false)**되었습니다.
- 최종 보존 정책은 `retained_policy = null`로 확정되었으며, 공식 test split 역시 0회 평가로 미사용 봉인 상태를 유지했습니다.
#### 6.14.3 실험 및 성과 요약 표
| 항목 (Category) | 세부 사양 및 평가 수치 (Details & Metrics) |
| --- | --- |
| **프로토콜 명칭** | `HEAVY-PSO-CROSS-SPLIT 1.0.0` (Publish Protocol: `1.0.0`) |
| **평가 대상 모델/데이터** | MNIST Compact (9,098), MNIST Wide (55,338), Fashion Compact (9,098), Fashion Wide (55,338) |
| **스웜/평가 하이퍼파라미터** | 12 particles × 80 epochs × fixed-10k subset, seeds 101~103, matched baseline 재실행 |
| **데이터 분할 구성** | 개발 분할 2개 (20260905, 20260906) / 확인 분할 1개 (20260907, 실행 보류) |
| **공식 테스트 데이터** | 0회 로드, 0회 평가 (완전 봉인 유지) |
| **결정 탐색 및 개발 변형** | 8회 결정 탐색 (Decision Iterations 1~8) / 9개 개발 변형 (Development Variants, Iter 3 Replica 1/2) |
| **동결 정책 (Iter 1) 결과** | Overall Acc **+0.1533%p**, NLL 감소 **0.5546%**, Worst Acc 회귀 **-7.1767%p**, Worst NLL 회귀 **+14.5682%**, MNIST Wide Acc **-0.3617%p** (FAIL) |
| **관측 최상위 (Iter 5) 결과** | Score **-185.610686**, Overall Acc **+0.4400%p**, NLL 감소 **3.9493%**, Worst Acc 회귀 **-3.0667%p**, MNIST Wide Acc **-2.6633%p** (FAIL) |
| **최종 판정 및 보존 정책** | **FAIL** (`retained_policy = null`), 확인 단계 보류 (`confirmation_executed = false`) |
| **누적 자원 사용 계측** | 432 runs, 414,720 queries, 4,147,200,000 sample evaluations, 3,162.9717s wall time (~52.7 min) |
| **공개 아티팩트 및 시각화** | [`benchmark_results/pso_v7_heavy_cross_split.json`](benchmark_results/pso_v7_heavy_cross_split.json), [`benchmark_results/pso_v7_heavy_cross_split.csv`](benchmark_results/pso_v7_heavy_cross_split.csv), [`history_plt/pso_v7_heavy_cross_split.png`](history_plt/pso_v7_heavy_cross_split.png) |
| **반복 결정 로그 경로** | [`.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/decision-log.md`](.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/decision-log.md) |
![Heavy PSO Cross-Split Robustness](history_plt/pso_v7_heavy_cross_split.png)
#### 6.14.4 재현 실행 및 아티팩트 발행 명령
```shell
# 1. 교차 분할 탐색 미션 실행 (개발 단계)
uv run --no-sync python test/heavy_pso_cross_split.py --phase development --device mps
# 2. 개별 변형 평가 실행 예시
uv run --no-sync python test/evaluate_heavy_cross_split.py --development .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json --output .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
# 3. 공개 아티팩트 및 시각화 생성 명령
uv run --no-sync python test/publish_heavy_cross_split.py --source-dir .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z --output-json benchmark_results/pso_v7_heavy_cross_split.json --output-csv benchmark_results/pso_v7_heavy_cross_split.csv --output-plot history_plt/pso_v7_heavy_cross_split.png
```
---
### 6.15 학습 후 예측공간 PSO 앙상블 연구 (POST-TRAINING-PSO-ENSEMBLE 1.1.0)
#### 6.15.1 연구 질문과 설계
본 연구는 이미 학습된 CompactCNN의 예측을 재사용하여, 다섯 모델의 출력 확률을 하나의 가중 평균으로 결합할 때 저차원 PSO가 유용한지를 평가했습니다. MNIST와 FashionMNIST 각각에 대해 split seed **20260904**, 50,000개 search 샘플과 10,000개 validation 샘플을 사용했고, 정규화 통계도 search 샘플에서만 계산했습니다. 모델 pool은 seeds 201~205의 독립 CompactCNN(각 **9,098 parameters**) 다섯 개이며, 각 모델은 Adam 10 epochs, learning rate 0.001, batch size 256으로 학습했습니다. 다섯 모델의 validation 확률 캐시 shape은 `[5, 10000, 10]`이고, 최적화 중 base CNN forward pass는 0회였습니다.
최적화 변수는 다섯 출력의 비음수 합이 1인 simplex 가중치 $w$뿐입니다. 따라서 이는 **prediction-space ensemble**이며, 파라미터를 평균내는 model soup가 아닙니다. Model soup의 weight averaging과 permutation 정렬을 다루는 문헌은 이 연구의 조작이나 결론을 뒷받침하는 직접적인 비교가 아닙니다 [arXiv:2203.05482](https://arxiv.org/abs/2203.05482), [arXiv:2209.04836](https://arxiv.org/abs/2209.04836). 비교 방법은 uniform, validation NLL에 대한 uniform temperature scaling, simplex 제약 SLSQP, 그리고 PSO입니다. PSO는 constriction movement, loss renewal, 30 particles, particle bounds $[-4,4]$, reflective boundary, velocity limit ratio 0.1, initial position noise 0, swarm seeds 301~303을 고정했습니다.
#### 6.15.2 누수 방지와 사전 선언된 반복
- 모델 학습, temperature fitting, SLSQP/PSO 가중치 선택은 모두 search/validation 단계에서만 수행했습니다. 공식 test split은 정책 동결 전 로드 0회·평가 0회였습니다.
- Iteration 0의 독립 evaluator가 per-workload test-seal field lookup을 교정한 뒤 wall-efficiency gate만 실패한 것을 확인했습니다. 품질·안정성 gate와 세 swarm seed의 수렴 결과는 유지하고, 결정 로그에 따라 PSO epochs만 50에서 30으로 줄였습니다. pool, objective, seed, split, baseline, threshold, selection rule은 바꾸지 않았습니다.
- 동결된 정책이 모든 development gate를 통과한 뒤에만 공식 test를 dataset별 1회 로드하여 pool 5회 forward와 50-epoch single 1회 forward로 확인했습니다. 그 뒤의 tuning/rerun은 0회입니다. 이 순서로 validation 선택과 one-shot test 확인을 분리했지만, 하나의 split과 하나의 final test endpoint만 사용했다는 한계는 남습니다.
Iteration 0(30 particles × 50 epochs)은 seed당 1,500 objective evaluations를 사용했습니다. PSO는 SLSQP와 같은 six-decimal validation NLL에 도달했지만, median one-seed wall ratio가 MNIST **13.14%**, FashionMNIST **12.05%**로 frozen 10% ceiling을 넘었습니다. MNIST validation NLL은 PSO/SLSQP **0.045902**, uniform **0.046385**, 10-epoch reference **0.060105**, equal-epoch-budget 50-epoch single **0.073988**였고, FashionMNIST는 각각 **0.285338**, **0.286751**, **0.303799**, **0.289660**이었습니다. SLSQP는 workload당 23 evaluations와 약 0.011초, PSO는 workload당 1,500 evaluations와 약 2.98~3.29초를 사용했으며, uniform temperature는 두 workload에서 PSO보다 낮은 validation NLL을 보였습니다. 이 결정과 실패 사유는 [post-training decision log](.omc/autoresearch/post-training-pso-ensemble/runs/20260904T093144Z/decision-log.md)에 기록되어 있습니다.
Iteration 1은 다른 조건을 바꾸지 않고 30 particles × 30 epochs로 축소했습니다. seed당 **900 queries**와 **9,000,000 candidate-sample evaluations**를 사용했고, 세 swarm seed가 모두 같은 six-decimal PSO NLL을 재현했습니다. Runner의 13개 집계 gate와 독립 evaluator가 재계산한 14개 development hard gate가 모두 통과했습니다.
#### 6.15.3 Validation 및 one-shot test 결과
아래 값은 accuracy(%) / NLL이며, PSO 행은 validation에서 frozen selected seed 301을 사용한 결과입니다. SLSQP와 PSO의 validation NLL은 두 dataset 모두 six-decimal 수준에서 같지만, test에서는 마지막 decimal 차이가 남습니다.
| 방법 | MNIST validation | MNIST test (one-shot) | FashionMNIST validation | FashionMNIST test (one-shot) |
| --- | ---: | ---: | ---: | ---: |
| 10-epoch single | 98.23 / 0.060105 | 98.47 / 0.044991 | 89.47 / 0.303799 | 88.90 / 0.314516 |
| 50-epoch single (equal epoch budget) | 98.52 / 0.073988 | 98.60 / 0.062102 | 90.32 / 0.289660 | 89.93 / 0.302348 |
| Uniform ensemble | 98.63 / 0.046385 | 98.86 / 0.036184 | 90.28 / 0.286751 | 89.65 / 0.293522 |
| Uniform + temperature | 98.63 / 0.045355 | 98.86 / 0.034129 | 90.28 / 0.285048 | 89.65 / 0.291996 |
| SLSQP simplex weights | 98.60 / 0.045902 | 98.83 / 0.036179 | 90.42 / 0.285338 | 89.54 / 0.291696 |
| PSO simplex weights | 98.61 / 0.045902 | 98.83 / 0.036178 | 90.42 / 0.285338 | 89.54 / 0.291700 |
Evaluator의 cross-dataset mean은 equal-budget single 대비 validation NLL **19.726% 감소**와 accuracy **+0.095%p**, test NLL **22.633% 감소**와 accuracy **-0.080%p**였습니다. 이는 PSO가 단일 모델보다 NLL을 낮추면서도 uniform 대비 사전 선언된 regression gate를 넘지 않았다는 기술통계이지, PSO의 일반적 정확도 우월성은 아닙니다. Test에서 uniform+temperature는 MNIST에서 PSO보다 NLL이 낮고 accuracy가 **0.03%p 높았습니다**. FashionMNIST에서는 uniform+temperature NLL이 **0.291996**으로 PSO의 **0.291700**보다 높았지만, accuracy는 **0.11%p 높았습니다**.
#### 6.15.4 계산비용과 full-weight G8과의 기술적 비교
두 iteration을 합친 post-training study의 PSO 연구 비용은 **14,400 queries**, **144,000,000 candidate-sample evaluations**, research wall time **30.2907초**였습니다. 이 중 최종 Iteration 1은 **5,400 queries**, **54,000,000 candidate-sample evaluations**, research wall time **11.1512초**였고, validation에서 선택된 endpoint의 production wall time은 **3.7888초**였습니다. 다음 비율은 모두 **Iteration 1 내부 비교**이며 두 iteration 합산 비용을 한 iteration의 Adam/pool 학습 비용으로 나눈 값이 아닙니다. 선택된 production PSO 총시간을 Iteration 1의 다섯 모델 pool 학습 총시간 **44.4624초**로 나눈 비율은 정확히 **8.521376%**(소수 셋째 자리 반올림 **8.521%**)입니다. Development gate가 직접 확인한 workload별 median one-seed 비율은 MNIST **8.563655%**, FashionMNIST **8.479599%**이며, Iteration 1 artifact의 `pso_to_pool_wall_ratio`는 이 두 workload 비율의 중앙값 **8.521627%**(소수 셋째 자리 반올림 **8.522%**)입니다. 두 workload 모두 10% ceiling 아래였습니다. Iteration 1의 SLSQP는 workload당 23회, 총 **46 evaluations**, 총 **0.0201초**였습니다. Validation cache는 dataset당 pool 5회와 long single 1회, 총 12 forward passes를 사용했습니다. 앙상블은 모델 다섯 개를 보존하고 prediction inference도 다섯 번 수행하므로 single model 대비 storage/inference 경로가 **5배**입니다. 이 비용을 포함해도 …
다음 표는 같은 CompactCNN parameter count를 사용한 [HEAVY-TASK-PSO-V6 artifact](benchmark_results/pso_v6_heavy_tasks.json)의 full-weight G8 semantic control과의 **기술적(descriptive) 비교**입니다. G8은 9,098개 전가중치를 직접 탐색한 12 particles × 80 epochs, fixed-10k validation, seeds 101~103 실행입니다. Post-training 행은 validation에서 선택된 PSO seed 301의 endpoint이고, G8 행은 세 seed의 평균입니다. 또한 post-training은 5개 모델의 5차원 prediction-space weight를 탐색하므로 두 행은 요약 통계, 목적함수, 최적화 공간이 모두 다릅니다.
| 연구/방법 | 요약 통계 | 최적화 공간 | MNIST validation accuracy / NLL | FashionMNIST validation accuracy / NLL | 공식 test |
| --- | --- | --- | ---: | ---: | --- |
| Post-training PSO (Iteration 1) | validation-selected seed 301 | 5-way prediction simplex | 98.61% / 0.045902 | 90.42% / 0.285338 | 98.83% / 0.036178; 89.54% / 0.291700 |
| Full-weight CompactCNN G8 (V6) | seeds 101~103 mean | 9,098 weights | 49.1533% / 1.518089 | 47.0033% / 1.511217 | 로드/평가 0회 |
선택 endpoint와 3-seed mean 사이의 기술통계 차이는 MNIST에서 **+49.4567%p, NLL -1.472187**, FashionMNIST에서 **+43.4167%p, NLL -1.225879**(post-training PSO minus G8)입니다. 그러나 이 차이는 짝지어진 추정량이 아니며, model-soup 대 full-weight PSO의 인과 비교나 일반화 증거도 아닙니다. V6 G8의 split seed는 **20260902**, 공식 test는 봉인되었고, post-training study는 split seed **20260904**에서 이미 Adam으로 학습된 pool을 사용했습니다. V6 전체 study의 자원은 30,720 queries, 245,760,000 sample evaluations, summed optimization wall time 188.7283초, validation wall time 6.3826초였으며, 이 수치 역시 네 workload와 G0/G5/G6/G8 실행을 합친 값입니다. 따라서 full-weight G8 수치는 “전가중치 PSO를 실용적 학습기로 판정하지 않는다”는 기존 feasibility 결과를 보강하지만, post-training 앙상블의 test accuracy를 설명하는 대조군으로 사용하지 않습니다.
#### 6.15.5 문헌과 해석의 경계
Deep ensembles는 여러 독립 predictor의 predictive uncertainty를 실용적으로 추정할 수 있음을 보였고([arXiv:1612.01474](https://arxiv.org/abs/1612.01474)), temperature scaling은 단일 scalar로 calibration을 개선하는 간단한 post-processing으로 제시되었습니다([arXiv:1706.04599](https://arxiv.org/abs/1706.04599)). 본 연구에서 uniform+temperature가 validation/test NLL과 ECE를 개선한 관측은 이 두 문헌과 방향이 일치하지만, 두 dataset·한 split의 결과를 넘어선 보장은 아닙니다. PSO로 diversity와 accuracy를 함께 고려한 weighted ensemble을 구성한 선행 연구([DOI:10.3390/a13100255](https://doi.org/10.3390/a13100255))도 있으나, 그 연구는 mixed-binary learner selection과 UCI dataset들을 포함하는 다른 설계입니다. 또한 초기 신경망 ensemble 연구([DOI:10.1016/0893-6080(92)90023-1](https://doi.org/10.1016/0893-6080(92)90023-1))는 ensemble의 일반적 동기를 제공할 수 있을 뿐, 본 PSO 비용 비교의 실험적 근거는 아닙니다.
#### 6.15.6 한계와 결정
1. **선택 편향과 표본 범위**: validation split 하나, dataset 두 개, CompactCNN 하나, PSO swarm seed 세 개뿐입니다. validation에서 방법과 PSO seed를 선택한 뒤 test를 한 번 확인했으므로 test leakage는 피했지만, 반복 split·외부 dataset·독립 replication은 없습니다.
2. **비동등한 대조군**: 50-epoch single은 pool의 합산 epoch와 맞춘 equal-epoch-budget 비교이며, 정확한 hardware FLOP 또는 병렬화 비용 동등성을 증명하지 않습니다. V6 G8은 전가중치 직접 탐색이고 split과 objective가 달라 post-training과의 accuracy/NLL 차이를 causal effect로 읽을 수 없습니다.
3. **저차원 smooth objective의 특수성**: 다섯 확률 출력의 simplex NLL은 비교적 매끄러운 저차원 함수라서, PSO 900 evaluations가 SLSQP 23 evaluations와 같은 NLL을 얻은 사실은 이 설정에서의 redundancy를 보여줍니다. 모든 비선형·불연속 목적함수에서 PSO가 중복된다는 뜻은 아닙니다.
4. **저장·추론 비용**: 다섯 모델을 보존하고 다섯 출력 forward를 수행해야 하므로 single model보다 5배 경로가 필요합니다. 본 연구는 이 ensemble overhead를 제거하거나 model soup으로 대체하지 않았습니다.
**결정**: smooth prediction-space NLL에는 먼저 **uniform + temperature**를 사용하고, 명시적인 simplex weight가 필요하면 **SLSQP**를 우선합니다. PSO는 현재 연구에서 SLSQP보다 품질을 추가로 개선하지 못하면서 더 많은 query와 wall time을 사용했으므로 이 경로의 기본 optimizer로 채택하지 않습니다. 향후 PSO는 gradient가 없거나 불연속·이산인 architecture/subset/diversity 선택 문제처럼 SLSQP가 직접 다루기 어려운 목적함수에서만 별도 protocol과 leakage 방지 확인을 거쳐 연구합니다. 이 결론은 PSO 전체의 실패 선언이나 full-weight G8과의 일반적 우열 주장이 아니라, 본 post-training prediction-space study의 bounded decision입니다.
**출력 아티팩트**: [`benchmark_results/pso_v8_post_training_ensemble.json`](benchmark_results/pso_v8_post_training_ensemble.json), [`benchmark_results/pso_v8_post_training_ensemble.csv`](benchmark_results/pso_v8_post_training_ensemble.csv), [`benchmark_results/pso_v8_post_training_ensemble_evaluation.json`](benchmark_results/pso_v8_post_training_ensemble_evaluation.json), [`history_plt/pso_v8_post_training_ensemble.png`](history_plt/pso_v8_post_training_ensemble.png), [decision log](.omc/autoresearch/post-training-pso-ensemble/runs/20260904T093144Z/decision-log.md).
## 7. 결과 해석 및 실무 권고사항 (Interpretation & Recommendations)
1. **고전 무브먼트 기법 선택**:
- Protocol 2.0.0의 5개 워크로드 고정 예산에서는 `constriction``inertia`가 가장 낮은 평균 순위를 기록했습니다. 새로운 워크로드에서는 둘을 우선 비교하되 보편적 우위로 간주하지 않아야 합니다.
- 확장 MNIST 연구에서는 새 `local_best`의 반경 4 후보가 동일 30×80 held-out 확인에서 가장 높은 평균 정확도(62.64%)와 가장 낮은 손실(1.211368)을 기록했습니다. 다른 데이터셋에서는 별도 검증이 필요합니다.
2. **`adaptive_moment` 독자 기법**:
- 기존 ablation의 $\lambda=0.10$은 해당 10개 프로필 중 미분 무관 최고 정확도였지만, 확장 search에서 선택된 $\lambda=0.06$/step 0.5는 동일 예산 held-out 확인에서 `local_best``inertia`보다 낮았습니다.
- 더 많은 파티클은 80세대를 유지해 총 평가량을 늘릴 때 성능을 높였고, particle-epochs를 고정하면 낮아졌습니다. 파티클 수와 계산 예산을 함께 보고 선택해야 합니다.
- 120개 파티클에서는 80세대가 충분한 수렴 horizon이 아니었습니다. 이 워크로드에서 계산 예산이 허용되면 더 긴 horizon을 사용하되, 테스트 체크포인트가 아니라 별도 validation metric으로 중단 시점을 결정해야 합니다.
- 공식 MNIST 전체 학습에서는 240세대 정확도가 87.70%로 2k-fitness 연구보다 높았습니다. 다만 full objective와 PCA 적합 범위가 함께 달라졌으므로 full-data 사용 하나의 인과 효과로 분리하지 않습니다.
3. **`quantum` 기법**:
- 이 후보 범위의 QPSO는 held-out 정확도 48.58%로 가장 낮았습니다. 하나의 워크로드 결과이며 QPSO 일반 성능으로 해석하지 않습니다.
4. **Adam refinement**:
- 경사도 사용이 허용될 때 `tuned_adam_100_lr.01`은 기존 ablation에서 가장 높은 정확도와 가장 낮은 손실을 기록했습니다. 순수 미분 무관 방법과 별도 범주로 비교해야 합니다.
5. **초기화 선택**:
- 기존 PCA32 ablation에서 `uniform` 초기화는 `model_noise`보다 평균 정확도가 4.64%p 낮았습니다. 이는 해당 경계·모델·예산에 한정된 관찰입니다.
6. **딥러닝 아키텍처 및 PSO 역할 권고사항 (Deep Accuracy Insights)**:
- 측정된 설정에서 실무적 권장 경로는 **Compact CNN + Adam**입니다.
- 9,098개 CNN 전가중치를 직접 탐색한 PSO는 이 고정 예산에서 역전파를 대체하지 못했습니다. PSO를 딥러닝 파이프라인에 유지하려면 전가중치 대체보다 저차원 아키텍처·하이퍼파라미터 탐색 후 Adam으로 가중치를 학습하는 역할 분담이 합리적입니다. 이 역할 분담 자체는 본 프로토콜에서 비교하지 않은 공학적 권고입니다.
7. **희소 부호 해시 부분공간·단계적 평가·스웜 앙상블 탐색 권고사항 (V5 Insights)**:
- V5의 동일 30p × 160e 파일럿에서는 희소 부호 해시 부분공간(290~4096차원)보다 전가중치(9,098차원) 검증 성능이 높았습니다. 그러나 V6 진단에서 동일 latent radius가 차원이 작을수록 decoded per-parameter RMS를 축소하는 교란이 확인됐으므로, 이 결과는 부분공간 자체의 열위 근거가 아닙니다.
- 단계적 표본 확장(2k→10k→50k) 적용 시 목적 함수 변경에 맞춰 pbest 재평가를 수행하여 수렴 편향을 방지해야 합니다.
- 스웜 내 검증 상위 다양성 파티클 기반 Top-5 앙상블은 단일 모델 대비 정확도(+3.77%p)와 NLL/Brier를 개선하였으나, ECE가 악화(+0.079042)되었습니다. 단순히 파티클 다양성이나 손실 잔여물이 높다고 해서 일반화나 신뢰성이 비례하여 향상된다고 가정하지 않아야 합니다.
8. **V6 탐색 구조 권고사항**:
- V5의 zero-velocity/no-mutation/normalized ±3 묶음은 동일 2k·60p×420e 조건에서 기존 `Optimizer`보다 5.39%p 낮았습니다. mutation·초기 속도·±6 경계를 복원한 G5/G6은 사전 회복 기준을 통과했으므로 다음 탐색은 G6을 기준으로 진행합니다.
- global RMS coordinate scale은 per-tensor SD보다 낮았으므로 좌표 정규화 자체를 제거하지 않습니다. mutation과 경계 확장의 독립 효과는 아직 3-시드 확인 전이므로 각각을 확정 원인으로 표현하지 않습니다.
9. **더 무거운 태스크에 대한 PSO 역할 권고사항**:
- 55,338개 파라미터와 FashionMNIST에서도 모든 실행이 finite했고 초기 모델 대비 개선됐으므로 기술적 실행 가능성은 확인됐습니다. 그러나 41.03~49.15% validation accuracy는 전가중치 PSO를 실용적 학습 경로로 권고할 근거가 아닙니다.
- 파라미터 수 증가로 swarm state가 6.08배 증가하고 처리량이 대략 절반으로 감소했습니다. 다음 확장 실험은 더 큰 전가중치 모델보다 저차원 subspace/협력적 block 탐색 또는 PSO 기반 하이퍼파라미터 탐색을 우선해야 합니다. 이 대안들의 성능은 본 프로토콜에서 측정하지 않았습니다.
---
## 8. 연구 한계점 (Limitations)
1. **소규모 샘플 사이즈 ($n=5$) 및 신뢰구간 측정 한계**:
- 본 벤치마크는 무작위 시드 5개(41~45 및 46~50)에 대한 측정을 바탕으로 하였습니다. 샘플 크기가 $n=5$로 제한되어 일부 지표에서 표준편차가 크며, 95% 신뢰구간(CI)의 범위를 좁히는 데 한계가 있습니다.
2. **순위(Rank) 지표의 서열적(Ordinal) 특성**:
- 평균 순위(Mean Rank) 지표는 절대적인 성능 격차 수치를 반영하지 않고 상대적인 순위 수치만을 반영합니다. 따라서 순위 차이가 절대적인 손실/정확도 차이와 정비례하지 않습니다.
3. **`tuned_full_evaluation` 비교 시 교란 요인 (Confounders)**:
- `tuned_full_evaluation` (3,000개 전체 데이터)과 고정 서브셋 기법(2,000개 서브셋) 간의 손실 수치 비교는 평가에 사용된 데이터 샘플 수 및 배치 연산 차이가 개입된 교란 요인을 포함하고 있습니다.
4. **소형/얕은 신경망 모델 범위 한계**:
- 메인 및 튜닝 평가 대부분은 1~2개 레이어의 소형 MLP와 PCA32 선형 모델이며, 딥 프로토콜도 9,098개 파라미터 Compact CNN 하나로 제한됩니다. 수만~수억 개 파라미터 모델로의 직접 일반화에는 근거가 부족합니다.
5. **확장 튜닝의 선택 불확실성**:
- 32개 후보 search는 시드 3개만 사용했습니다. validation 차이가 작은 후보의 순위는 추가 시드에서 바뀔 수 있습니다.
6. **파티클 스케일링의 계산량 차이**:
- 80세대 고정 비교는 파티클 수와 함께 총 평가량이 증가합니다. 약 2,400 particle-epochs 고정 비교는 평가 횟수만 근사적으로 맞추며 실제 MPS 실행 비용은 동일하지 않습니다.
7. **Epoch checkpoint 테스트 반복 관찰**:
- 80~240세대 궤적은 같은 테스트 1,000개를 반복 평가한 진단 결과입니다. 이를 근거로 epoch를 선택하면 테스트셋 누수가 되므로 실제 stopping rule에는 별도 validation split이 필요합니다.
8. **Full MNIST 모델 범위와 반복 테스트 관찰**:
- train/test 전체 split을 사용했지만 PCA32 선형 모델 실험입니다. 원본 영상 공간 및 CNN으로 일반화하지 않으며, test 10,000개 checkpoint를 반복 관찰해 epoch를 선택하지 않습니다.
9. **Deep Accuracy 프로토콜의 표본 수($n=3$) 및 탐색 공간 제약**:
- Deep Accuracy 실험은 $n=3$ 시드로 수행된 기술적(descriptive) 비교이며 통계적 유의성 검정을 제공하지 않습니다. 또한 PSO 30 파티클 × 40 세대의 고정 예산과 고정 서브셋(2,000개) 평가 조건을 사용하였으므로, 파티클 수나 세대를 무한히 늘린 경우의 가설적 한계 성능을 수식적으로 증명한 것은 아닙니다. 그럼에도 9,098개 파라미터 전가중치 탐색에서 경사도 대비 극심한 열위(36.76% vs 98.53%)는 고차원 전가중치 PSO 적용의 실질적 한계를 명확히 나타냅니다.
10. **MNIST-PSO-RAW-V5 프로토콜의 범위와 불확실성**:
- 부분공간 선택은 단일 파일럿 시드(91), 확인은 3개 시드와 Compact CNN 한 구조, 원본 MNIST 한 데이터셋에 한정됩니다. 60p × 600e 단계적 탐색의 단일 모델 정확도(83.23%)와 앙상블 정확도(87.00%)는 비동등 계산량의 v4 Adam 10-epoch 평균(98.53% ± 0.16%)보다 각각 15.30%p와 11.53%p 낮았습니다.
- 앙상블은 단일 모델보다 정확도와 NLL/Brier가 개선되고 예측 불일치도 0.1585를 보였지만, ECE도 0.085242에서 0.164284로 높아졌습니다. 이 동시 관찰만으로 다양성이나 손실이 개선 또는 보정 악화의 원인이라고 결론 내릴 수 없습니다.
11. **MNIST-PSO-RAW-V6 screen/confirmation 범위**:
- G0G8 screen은 단일 seed 91이므로 요인별 material 판정은 후보 선별 근거입니다. 3-시드 confirmation은 G0/G1/G5/G6/G8만 포함하여 전체 구조 회복과 scale/radius 판단만 확인했습니다.
- G5와 G6이 G8에 근접했다는 결과는 동일 2k 목적함수와 60p×420e 예산에 한정됩니다. 50k objective, 더 긴 horizon, 다른 초기 모델, 외부 데이터에 대한 성능을 보장하지 않습니다.
- 공식 test split을 사용하지 않았으므로 V6 Phase B 수치는 validation 결과이며 V5의 공식 test 정확도와 직접적인 paired test 비교가 아닙니다.
12. **HEAVY-TASK-PSO-V6의 validation 재사용 및 예산 한계**:
- 단일 seed screen에서 normalized 방법을 선택한 뒤 같은 validation 10,000개로 3-시드 확인 결과를 보고했으므로 선택 편향이 남습니다. 공식 test split을 로드하지 않았고 외부 일반화 성능은 측정하지 않았습니다.
- 12 particles × 80 epochs·fixed-10k endpoint는 계산상 feasibility만 판정합니다. 세대 궤적, validation 기반 조기 중단, 50k objective, Adam 동예산 비교가 없으므로 수렴 완료·최대 도달 성능·계산 효율 우월성을 주장하지 않습니다.
- FashionMNIST는 설계상 데이터 난도 축으로 사용했지만 measured endpoint가 MNIST보다 일관되게 낮지 않았습니다. 따라서 이 실행만으로 데이터 난도 증가의 인과 효과를 분리하지 않습니다.
13. **Heavy PSO 교차 분할 강건성 한계 (Cross-Split Robustness Failure)**: §6.14의 두 개발 분할(20260905/20260906)에서 동결 정책과 8회 결정의 9개 변형 모두 gate를 통과하지 못했습니다. 따라서 `fixed_global_hybrid_v3`의 이전 단일 분할 개선은 측정한 새 분할에서 재현되지 않았고, 이 정책은 보존하지 않습니다(`retained_policy = null`). 이 결과는 두 validation 분할과 고정 예산에 한정되며, 공식 test나 외부 데이터 일반화 성능을 측정하지 않았습니다.
---
## 9. 재현 명령 및 결과 데이터 아티팩트 (Reproducibility & Artifacts)
### 9.1 재현 실행 명령 (Reproducibility Command)
```shell
# uv 환경에서 동일한 벤치마크 수트 전체 실행 (MPS 디바이스 사용)
uv run --locked --extra examples python test/benchmark_suite.py --device mps
# 검증 선택, held-out 확인, Adaptive Moment 파티클 스케일링
uv run --locked --extra examples python test/tuning_suite.py --device mps
# 120p×80e Adaptive Moment 파티클 스케일링 재현성 검증 (exact-replay + independent seeds)
uv run --locked --extra examples python test/reproduce_scaling.py --device mps
# 120p Adaptive Moment 연속 240-epoch 수렴 진단
uv run --locked --extra examples python test/epoch_convergence.py --device mps
# 공식 MNIST 60k/10k 전체 학습, 120p×240e
uv run --locked --extra examples python test/full_mnist_study.py --device mps
# 공식 MNIST 딥 신경망 아키텍처 및 최적화기 비교 (Deep Accuracy 1.0.0)
uv run --locked --extra examples python test/deep_accuracy_study.py --device mps
# 희소 부호 해시 부분공간·단계적 평가·스웜 앙상블 탐색 (MNIST-PSO-RAW-V5 1.0.0)
uv run --locked --extra examples python test/deep_pso_methods.py --device mps
# V5 탐색 구조 회귀 원인 분리 (MNIST-PSO-RAW-V6 1.0.0 Phase A/B)
uv run --locked --extra examples python test/deep_pso_v6.py --phase all --device mps
# 더 큰 CNN 및 FashionMNIST 전가중치 PSO feasibility screen/confirmation
uv run --no-sync python test/heavy_task_feasibility.py --stage all --device mps
# Heavy PSO 교차 분할 강건성 탐색/평가/발행
uv run --no-sync python test/heavy_pso_cross_split.py --phase development --device mps
uv run --no-sync python test/evaluate_heavy_cross_split.py --development .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json --output .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
uv run --no-sync python test/publish_heavy_cross_split.py --source-dir .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z --output-json benchmark_results/pso_v7_heavy_cross_split.json --output-csv benchmark_results/pso_v7_heavy_cross_split.csv --output-plot history_plt/pso_v7_heavy_cross_split.png
```
### 9.2 원천 결과 데이터 및 아티팩트 경로
- **종합 JSON 벤치마크 데이터**: [`benchmark_results/pso_v4_benchmark.json`](benchmark_results/pso_v4_benchmark.json)
- **메인 벤치마크 CSV**: [`benchmark_results/pso_v4_main_benchmark.csv`](benchmark_results/pso_v4_main_benchmark.csv)
- **Ablation 벤치마크 CSV**: [`benchmark_results/pso_v4_ablation_benchmark.csv`](benchmark_results/pso_v4_ablation_benchmark.csv)
- **확장 튜닝 JSON**: [`benchmark_results/pso_v4_tuning.json`](benchmark_results/pso_v4_tuning.json)
- **후보 Search CSV**: [`benchmark_results/pso_v4_tuning_search.csv`](benchmark_results/pso_v4_tuning_search.csv)
- **Held-out Confirmation CSV**: [`benchmark_results/pso_v4_tuning_confirmation.csv`](benchmark_results/pso_v4_tuning_confirmation.csv)
- **Particle Scaling CSV**: [`benchmark_results/pso_v4_particle_scaling.csv`](benchmark_results/pso_v4_particle_scaling.csv)
- **120p×80e 재현성 검증 JSON**: [`benchmark_results/pso_v4_120p80_replication.json`](benchmark_results/pso_v4_120p80_replication.json)
- **120p×80e 재현성 검증 CSV**: [`benchmark_results/pso_v4_120p80_replication.csv`](benchmark_results/pso_v4_120p80_replication.csv)
- **120p epoch 수렴 진단 JSON**: [`benchmark_results/pso_v4_epoch_convergence.json`](benchmark_results/pso_v4_epoch_convergence.json)
- **120p epoch 수렴 진단 CSV**: [`benchmark_results/pso_v4_epoch_convergence.csv`](benchmark_results/pso_v4_epoch_convergence.csv)
- **Full MNIST JSON**: [`benchmark_results/pso_v4_full_mnist.json`](benchmark_results/pso_v4_full_mnist.json)
- **Full MNIST CSV**: [`benchmark_results/pso_v4_full_mnist.csv`](benchmark_results/pso_v4_full_mnist.csv)
- **Deep Accuracy JSON**: [`benchmark_results/pso_v4_deep_accuracy.json`](benchmark_results/pso_v4_deep_accuracy.json)
- **Deep Accuracy CSV**: [`benchmark_results/pso_v4_deep_accuracy.csv`](benchmark_results/pso_v4_deep_accuracy.csv)
- **V5 Deep Methods JSON**: [`benchmark_results/pso_v5_deep_methods.json`](benchmark_results/pso_v5_deep_methods.json)
- **V5 Deep Methods CSV**: [`benchmark_results/pso_v5_deep_methods.csv`](benchmark_results/pso_v5_deep_methods.csv)
- **V6 Geometry Ablation JSON**: [`benchmark_results/pso_v6_phase_b.json`](benchmark_results/pso_v6_phase_b.json)
- **V6 Geometry Ablation CSV**: [`benchmark_results/pso_v6_phase_b.csv`](benchmark_results/pso_v6_phase_b.csv)
- **Heavy Task Feasibility JSON**: [`benchmark_results/pso_v6_heavy_tasks.json`](benchmark_results/pso_v6_heavy_tasks.json)
- **Heavy Task Feasibility CSV**: [`benchmark_results/pso_v6_heavy_tasks.csv`](benchmark_results/pso_v6_heavy_tasks.csv)
- **Heavy PSO Cross-Split JSON**: [`benchmark_results/pso_v7_heavy_cross_split.json`](benchmark_results/pso_v7_heavy_cross_split.json)
- **Heavy PSO Cross-Split CSV**: [`benchmark_results/pso_v7_heavy_cross_split.csv`](benchmark_results/pso_v7_heavy_cross_split.csv)
- **결과 시각화 이미지**:
- [`history_plt/pso_v4_accuracy.png`](history_plt/pso_v4_accuracy.png)
- [`history_plt/pso_v4_loss.png`](history_plt/pso_v4_loss.png)
- [`history_plt/pso_v4_rank_heatmap.png`](history_plt/pso_v4_rank_heatmap.png)
- [`history_plt/pso_v4_runtime.png`](history_plt/pso_v4_runtime.png)
- [`history_plt/pso_v4_mnist_ablation.png`](history_plt/pso_v4_mnist_ablation.png)
- [`history_plt/pso_v4_extended_tuning.png`](history_plt/pso_v4_extended_tuning.png)
- [`history_plt/pso_v4_particle_scaling.png`](history_plt/pso_v4_particle_scaling.png)
- [`history_plt/pso_v4_epoch_convergence.png`](history_plt/pso_v4_epoch_convergence.png)
- [`history_plt/pso_v4_full_mnist.png`](history_plt/pso_v4_full_mnist.png)
- [`history_plt/pso_v4_deep_accuracy.png`](history_plt/pso_v4_deep_accuracy.png)
- [`history_plt/pso_v5_deep_methods.png`](history_plt/pso_v5_deep_methods.png)
- [`history_plt/pso_v6_phase_b.png`](history_plt/pso_v6_phase_b.png)
- [`history_plt/pso_v6_heavy_tasks.png`](history_plt/pso_v6_heavy_tasks.png)
- [`history_plt/pso_v7_heavy_cross_split.png`](history_plt/pso_v7_heavy_cross_split.png)
### 9.3 소스 코드 및 레퍼런스 문헌 참조
- **5단계 플러그인 구현 소스**: [`pso/plugins.py`](pso/plugins.py) 및 [`pso/optimizer.py`](pso/optimizer.py)
- **V5 탐색 및 앙상블 스크립트**: [`test/deep_pso_methods.py`](test/deep_pso_methods.py)
- **V6 탐색 구조 진단 스크립트**: [`test/deep_pso_v6.py`](test/deep_pso_v6.py)
- **Heavy Task Feasibility 스크립트**: [`test/heavy_task_feasibility.py`](test/heavy_task_feasibility.py)
- **Heavy PSO Cross-Split 탐색/평가/발행 스크립트**: [`test/heavy_pso_cross_split.py`](test/heavy_pso_cross_split.py), [`test/evaluate_heavy_cross_split.py`](test/evaluate_heavy_cross_split.py), [`test/publish_heavy_cross_split.py`](test/publish_heavy_cross_split.py)
- **고전 PSO 논문 DOI 및 알고리즘 구현 레퍼런스**: [`README.md` 참고 문헌 섹션](README.md#참고-문헌-primary-references--dois) 참조
@@ -0,0 +1,16 @@
cohort,method,candidate_label,regimen,seed,n_particles,epochs,particle_epochs,train_loss,train_acc,test_loss,test_acc,test_mse,fit_time_sec,data_fingerprint,model_fingerprint,device,completed,error
baseline,adaptive_moment,am_b0.06_s0.5,fixed_epoch,71,120,80,9600,0.6516683101654053,0.7950000166893005,0.8584634065628052,0.7360000014305115,0.037673790007829666,11.962705624988303,dfe645918ece54c0,0777bd52fd76272d,mps,True,
baseline,adaptive_moment,am_b0.06_s0.5,fixed_epoch,72,120,80,9600,0.6982801556587219,0.7914999723434448,0.9025362133979797,0.7239999771118164,0.038956169039011,11.906837583053857,dfe645918ece54c0,6fcb6e473bdacbd2,mps,True,
baseline,adaptive_moment,am_b0.06_s0.5,fixed_epoch,73,120,80,9600,0.7527623772621155,0.7749999761581421,1.001193642616272,0.6919999718666077,0.04309915751218796,10.664651792030782,dfe645918ece54c0,2e6c351372592f10,mps,True,
baseline,adaptive_moment,am_b0.06_s0.5,fixed_epoch,74,120,80,9600,0.6858600974082947,0.784500002861023,0.8601324558258057,0.7350000143051147,0.03845023736357689,10.921957665821537,dfe645918ece54c0,4000fe3fb26ef207,mps,True,
baseline,adaptive_moment,am_b0.06_s0.5,fixed_epoch,75,120,80,9600,0.6326538324356079,0.8125,0.889915406703949,0.7300000190734863,0.03931796923279762,10.411899874918163,dfe645918ece54c0,0966039f5ef7af88,mps,True,
replay,adaptive_moment,am_b0.06_s0.5,fixed_epoch,71,120,80,9600,0.6516683101654053,0.7950000166893005,0.8584634065628052,0.7360000014305115,0.037673790007829666,11.090910458937287,dfe645918ece54c0,0777bd52fd76272d,mps,True,
replay,adaptive_moment,am_b0.06_s0.5,fixed_epoch,72,120,80,9600,0.6982801556587219,0.7914999723434448,0.9025362133979797,0.7239999771118164,0.038956169039011,10.668682500021532,dfe645918ece54c0,6fcb6e473bdacbd2,mps,True,
replay,adaptive_moment,am_b0.06_s0.5,fixed_epoch,73,120,80,9600,0.7527623772621155,0.7749999761581421,1.001193642616272,0.6919999718666077,0.04309915751218796,11.617381499847397,dfe645918ece54c0,2e6c351372592f10,mps,True,
replay,adaptive_moment,am_b0.06_s0.5,fixed_epoch,74,120,80,9600,0.6858600974082947,0.784500002861023,0.8601324558258057,0.7350000143051147,0.03845023736357689,12.46007908298634,dfe645918ece54c0,4000fe3fb26ef207,mps,True,
replay,adaptive_moment,am_b0.06_s0.5,fixed_epoch,75,120,80,9600,0.6326538324356079,0.8125,0.889915406703949,0.7300000190734863,0.03931796923279762,11.499297459144145,dfe645918ece54c0,0966039f5ef7af88,mps,True,
independent,adaptive_moment,am_b0.06_s0.5,fixed_epoch,81,120,80,9600,0.7000858783721924,0.781000018119812,0.9081020355224609,0.7250000238418579,0.039418451488018036,10.6923490408808,dfe645918ece54c0,b9e1b8cbb9345add,mps,True,
independent,adaptive_moment,am_b0.06_s0.5,fixed_epoch,82,120,80,9600,0.6899945139884949,0.796999990940094,0.9245963096618652,0.718999981880188,0.03965267166495323,11.110405791085213,dfe645918ece54c0,f1a0025f5b3b5b7c,mps,True,
independent,adaptive_moment,am_b0.06_s0.5,fixed_epoch,83,120,80,9600,0.6943607330322266,0.7929999828338623,0.8056192398071289,0.7609999775886536,0.034950967878103256,10.71645870897919,dfe645918ece54c0,9cb7fe904bf992ac,mps,True,
independent,adaptive_moment,am_b0.06_s0.5,fixed_epoch,84,120,80,9600,0.7076351046562195,0.7885000109672546,0.8160682320594788,0.7429999709129333,0.0366184301674366,10.859140583081171,dfe645918ece54c0,b833ebd886fce382,mps,True,
independent,adaptive_moment,am_b0.06_s0.5,fixed_epoch,85,120,80,9600,0.6802449822425842,0.7875000238418579,0.8681835532188416,0.7319999933242798,0.038917701691389084,10.97266870806925,dfe645918ece54c0,e6bdf9e5d849521b,mps,True,
1 cohort method candidate_label regimen seed n_particles epochs particle_epochs train_loss train_acc test_loss test_acc test_mse fit_time_sec data_fingerprint model_fingerprint device completed error
2 baseline adaptive_moment am_b0.06_s0.5 fixed_epoch 71 120 80 9600 0.6516683101654053 0.7950000166893005 0.8584634065628052 0.7360000014305115 0.037673790007829666 11.962705624988303 dfe645918ece54c0 0777bd52fd76272d mps True
3 baseline adaptive_moment am_b0.06_s0.5 fixed_epoch 72 120 80 9600 0.6982801556587219 0.7914999723434448 0.9025362133979797 0.7239999771118164 0.038956169039011 11.906837583053857 dfe645918ece54c0 6fcb6e473bdacbd2 mps True
4 baseline adaptive_moment am_b0.06_s0.5 fixed_epoch 73 120 80 9600 0.7527623772621155 0.7749999761581421 1.001193642616272 0.6919999718666077 0.04309915751218796 10.664651792030782 dfe645918ece54c0 2e6c351372592f10 mps True
5 baseline adaptive_moment am_b0.06_s0.5 fixed_epoch 74 120 80 9600 0.6858600974082947 0.784500002861023 0.8601324558258057 0.7350000143051147 0.03845023736357689 10.921957665821537 dfe645918ece54c0 4000fe3fb26ef207 mps True
6 baseline adaptive_moment am_b0.06_s0.5 fixed_epoch 75 120 80 9600 0.6326538324356079 0.8125 0.889915406703949 0.7300000190734863 0.03931796923279762 10.411899874918163 dfe645918ece54c0 0966039f5ef7af88 mps True
7 replay adaptive_moment am_b0.06_s0.5 fixed_epoch 71 120 80 9600 0.6516683101654053 0.7950000166893005 0.8584634065628052 0.7360000014305115 0.037673790007829666 11.090910458937287 dfe645918ece54c0 0777bd52fd76272d mps True
8 replay adaptive_moment am_b0.06_s0.5 fixed_epoch 72 120 80 9600 0.6982801556587219 0.7914999723434448 0.9025362133979797 0.7239999771118164 0.038956169039011 10.668682500021532 dfe645918ece54c0 6fcb6e473bdacbd2 mps True
9 replay adaptive_moment am_b0.06_s0.5 fixed_epoch 73 120 80 9600 0.7527623772621155 0.7749999761581421 1.001193642616272 0.6919999718666077 0.04309915751218796 11.617381499847397 dfe645918ece54c0 2e6c351372592f10 mps True
10 replay adaptive_moment am_b0.06_s0.5 fixed_epoch 74 120 80 9600 0.6858600974082947 0.784500002861023 0.8601324558258057 0.7350000143051147 0.03845023736357689 12.46007908298634 dfe645918ece54c0 4000fe3fb26ef207 mps True
11 replay adaptive_moment am_b0.06_s0.5 fixed_epoch 75 120 80 9600 0.6326538324356079 0.8125 0.889915406703949 0.7300000190734863 0.03931796923279762 11.499297459144145 dfe645918ece54c0 0966039f5ef7af88 mps True
12 independent adaptive_moment am_b0.06_s0.5 fixed_epoch 81 120 80 9600 0.7000858783721924 0.781000018119812 0.9081020355224609 0.7250000238418579 0.039418451488018036 10.6923490408808 dfe645918ece54c0 b9e1b8cbb9345add mps True
13 independent adaptive_moment am_b0.06_s0.5 fixed_epoch 82 120 80 9600 0.6899945139884949 0.796999990940094 0.9245963096618652 0.718999981880188 0.03965267166495323 11.110405791085213 dfe645918ece54c0 f1a0025f5b3b5b7c mps True
14 independent adaptive_moment am_b0.06_s0.5 fixed_epoch 83 120 80 9600 0.6943607330322266 0.7929999828338623 0.8056192398071289 0.7609999775886536 0.034950967878103256 10.71645870897919 dfe645918ece54c0 9cb7fe904bf992ac mps True
15 independent adaptive_moment am_b0.06_s0.5 fixed_epoch 84 120 80 9600 0.7076351046562195 0.7885000109672546 0.8160682320594788 0.7429999709129333 0.0366184301674366 10.859140583081171 dfe645918ece54c0 b833ebd886fce382 mps True
16 independent adaptive_moment am_b0.06_s0.5 fixed_epoch 85 120 80 9600 0.6802449822425842 0.7875000238418579 0.8681835532188416 0.7319999933242798 0.038917701691389084 10.97266870806925 dfe645918ece54c0 e6bdf9e5d849521b mps True
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
profile,dataset,method,n_particles,epochs,n_seeds,eval_acc_mean,eval_acc_std,eval_acc_median,eval_acc_iqr,eval_acc_ci95,eval_loss_mean,eval_loss_std,eval_loss_median,eval_loss_iqr,eval_loss_ci95,eval_mse_mean,eval_mse_std,train_acc_mean,train_loss_mean,runtime_seconds_mean,runtime_seconds_std,rank_acc,rank_loss
adaptive_moment_.10,MNIST,adaptive_moment,30,80,5,0.63,0.018276,0.641,0.027,0.022692,1.243339,0.102561,1.253713,0.068197,0.127344,0.051633,0.002578,0.7099,0.978244,4.286662,0.289397,2,5
adaptive_moment_.25,MNIST,adaptive_moment,30,80,5,0.5632,0.02938,0.569,0.034,0.03648,1.499979,0.119449,1.483628,0.043413,0.148313,0.06119,0.003677,0.6379,1.228023,3.290951,0.425997,8,7
adaptive_moment_.50,MNIST,adaptive_moment,30,80,5,0.5186,0.039087,0.51,0.019,0.048532,1.683781,0.121571,1.683062,0.08627,0.150948,0.067331,0.003822,0.5705,1.4637,3.202502,0.269844,9,9
inertia_canonical,MNIST,inertia,30,80,5,0.4676,0.024358,0.472,0.033,0.030244,1.720983,0.118092,1.699598,0.020267,0.146628,0.070356,0.002531,0.5277,1.528197,3.119898,0.30594,10,10
inertia_tuned,MNIST,inertia,30,80,5,0.6162,0.05131,0.635,0.007,0.063709,1.2366,0.124281,1.194831,0.029657,0.154312,0.05278,0.005923,0.6931,0.999514,3.131882,0.408534,4,3
tuned_adam_100_lr.01,MNIST,inertia,30,80,5,0.8558,0.002387,0.856,0.003,0.002964,0.470271,0.011931,0.472233,0.019931,0.014814,0.02164,0.000368,0.9158,0.298524,4.990688,0.434352,1,1
tuned_full_evaluation,MNIST,inertia,30,80,5,0.6254,0.02805,0.624,0.029,0.034828,1.179009,0.050059,1.194071,0.052015,0.062156,0.051537,0.00241,0.687867,1.00514,3.377083,0.469302,3,2
tuned_no_mutation,MNIST,inertia,30,80,5,0.6042,0.030376,0.607,0.003,0.037716,1.249836,0.084985,1.215945,0.022012,0.105521,0.053483,0.003055,0.6686,1.074396,3.347668,0.3431,6,6
tuned_particle_reset,MNIST,inertia,30,80,5,0.6162,0.05131,0.635,0.007,0.063709,1.2366,0.124281,1.194831,0.029657,0.154312,0.05278,0.005923,0.6931,0.999514,4.176388,0.449217,5,4
tuned_uniform_initialization,MNIST,inertia,30,80,5,0.5698,0.023952,0.575,0.017,0.02974,1.619196,0.157329,1.641514,0.149905,0.195347,0.061757,0.0042,0.616,1.345857,4.306866,0.442651,7,8
1 profile dataset method n_particles epochs n_seeds eval_acc_mean eval_acc_std eval_acc_median eval_acc_iqr eval_acc_ci95 eval_loss_mean eval_loss_std eval_loss_median eval_loss_iqr eval_loss_ci95 eval_mse_mean eval_mse_std train_acc_mean train_loss_mean runtime_seconds_mean runtime_seconds_std rank_acc rank_loss
2 adaptive_moment_.10 MNIST adaptive_moment 30 80 5 0.63 0.018276 0.641 0.027 0.022692 1.243339 0.102561 1.253713 0.068197 0.127344 0.051633 0.002578 0.7099 0.978244 4.286662 0.289397 2 5
3 adaptive_moment_.25 MNIST adaptive_moment 30 80 5 0.5632 0.02938 0.569 0.034 0.03648 1.499979 0.119449 1.483628 0.043413 0.148313 0.06119 0.003677 0.6379 1.228023 3.290951 0.425997 8 7
4 adaptive_moment_.50 MNIST adaptive_moment 30 80 5 0.5186 0.039087 0.51 0.019 0.048532 1.683781 0.121571 1.683062 0.08627 0.150948 0.067331 0.003822 0.5705 1.4637 3.202502 0.269844 9 9
5 inertia_canonical MNIST inertia 30 80 5 0.4676 0.024358 0.472 0.033 0.030244 1.720983 0.118092 1.699598 0.020267 0.146628 0.070356 0.002531 0.5277 1.528197 3.119898 0.30594 10 10
6 inertia_tuned MNIST inertia 30 80 5 0.6162 0.05131 0.635 0.007 0.063709 1.2366 0.124281 1.194831 0.029657 0.154312 0.05278 0.005923 0.6931 0.999514 3.131882 0.408534 4 3
7 tuned_adam_100_lr.01 MNIST inertia 30 80 5 0.8558 0.002387 0.856 0.003 0.002964 0.470271 0.011931 0.472233 0.019931 0.014814 0.02164 0.000368 0.9158 0.298524 4.990688 0.434352 1 1
8 tuned_full_evaluation MNIST inertia 30 80 5 0.6254 0.02805 0.624 0.029 0.034828 1.179009 0.050059 1.194071 0.052015 0.062156 0.051537 0.00241 0.687867 1.00514 3.377083 0.469302 3 2
9 tuned_no_mutation MNIST inertia 30 80 5 0.6042 0.030376 0.607 0.003 0.037716 1.249836 0.084985 1.215945 0.022012 0.105521 0.053483 0.003055 0.6686 1.074396 3.347668 0.3431 6 6
10 tuned_particle_reset MNIST inertia 30 80 5 0.6162 0.05131 0.635 0.007 0.063709 1.2366 0.124281 1.194831 0.029657 0.154312 0.05278 0.005923 0.6931 0.999514 4.176388 0.449217 5 4
11 tuned_uniform_initialization MNIST inertia 30 80 5 0.5698 0.023952 0.575 0.017 0.02974 1.619196 0.157329 1.641514 0.149905 0.195347 0.061757 0.0042 0.616 1.345857 4.306866 0.442651 7 8
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
lane,profile_or_arch,seed,model_name,param_count,initial_test_acc,final_test_acc,final_test_loss,subset_fitness_acc,subset_fitness_loss,pso_epochs,adam_epochs,elapsed_sec,model_fingerprint,data_fingerprint
architecture,raw_linear,101,Raw Linear (784->10),7850,0.1449,0.9239,0.269691,,,0,10,2.6001,12cef21a80b85b0b,8dd702555745641a
architecture,raw_linear,102,Raw Linear (784->10),7850,0.1308,0.924,0.270518,,,0,10,2.6265,0f099d5ae79e7863,8dd702555745641a
architecture,raw_linear,103,Raw Linear (784->10),7850,0.0559,0.9257,0.266626,,,0,10,2.8535,23a4a6362abdad3d,8dd702555745641a
architecture,raw_mlp,101,Raw MLP (784->128->64->10),109386,0.0812,0.9773,0.076661,,,0,10,8.8041,9ac96d5f45d71cb3,8dd702555745641a
architecture,raw_mlp,102,Raw MLP (784->128->64->10),109386,0.0522,0.976,0.081621,,,0,10,5.7141,55966246db4dfff1,8dd702555745641a
architecture,raw_mlp,103,Raw MLP (784->128->64->10),109386,0.1111,0.9776,0.076823,,,0,10,4.5491,723217e2418edcfd,8dd702555745641a
architecture,compact_cnn,101,"Compact CNN (9,098 params)",9098,0.0851,0.9866,0.039444,,,0,10,6.228,db41fb515dcb49fa,8dd702555745641a
architecture,compact_cnn,102,"Compact CNN (9,098 params)",9098,0.0963,0.9858,0.042864,,,0,10,5.396,efd743cad60c7530,8dd702555745641a
architecture,compact_cnn,103,"Compact CNN (9,098 params)",9098,0.1072,0.9836,0.04912,,,0,10,5.4935,17891ec08e79ee74,8dd702555745641a
optimizer,adam_only,101,Compact CNN (Adam-Only),9098,0.0851,0.9866,0.039444,,,0,10,6.228,db41fb515dcb49fa,8dd702555745641a
optimizer,adam_only,102,Compact CNN (Adam-Only),9098,0.0963,0.9858,0.042864,,,0,10,5.396,efd743cad60c7530,8dd702555745641a
optimizer,adam_only,103,Compact CNN (Adam-Only),9098,0.1072,0.9836,0.04912,,,0,10,5.4935,17891ec08e79ee74,8dd702555745641a
optimizer,pso_only,101,Compact CNN (PSO-Only),9098,0.0851,0.3901,22.182518,0.399,21.674469,40,0,2.5761,db41fb515dcb49fa,8dd702555745641a
optimizer,pso_only,102,Compact CNN (PSO-Only),9098,0.0963,0.3886,6.031698,0.3925,5.977414,40,0,2.331,efd743cad60c7530,8dd702555745641a
optimizer,pso_only,103,Compact CNN (PSO-Only),9098,0.1072,0.3242,14.099036,0.3405,14.065619,40,0,2.7469,17891ec08e79ee74,8dd702555745641a
optimizer,hybrid,101,Compact CNN (Hybrid),9098,0.0851,0.9658,0.109308,0.399,21.674469,40,10,7.4414,db41fb515dcb49fa,8dd702555745641a
optimizer,hybrid,102,Compact CNN (Hybrid),9098,0.0963,0.9807,0.061528,0.3925,5.977414,40,10,7.226,efd743cad60c7530,8dd702555745641a
optimizer,hybrid,103,Compact CNN (Hybrid),9098,0.1072,0.9725,0.088789,0.3405,14.065619,40,10,7.7426,17891ec08e79ee74,8dd702555745641a
1 lane profile_or_arch seed model_name param_count initial_test_acc final_test_acc final_test_loss subset_fitness_acc subset_fitness_loss pso_epochs adam_epochs elapsed_sec model_fingerprint data_fingerprint
2 architecture raw_linear 101 Raw Linear (784->10) 7850 0.1449 0.9239 0.269691 0 10 2.6001 12cef21a80b85b0b 8dd702555745641a
3 architecture raw_linear 102 Raw Linear (784->10) 7850 0.1308 0.924 0.270518 0 10 2.6265 0f099d5ae79e7863 8dd702555745641a
4 architecture raw_linear 103 Raw Linear (784->10) 7850 0.0559 0.9257 0.266626 0 10 2.8535 23a4a6362abdad3d 8dd702555745641a
5 architecture raw_mlp 101 Raw MLP (784->128->64->10) 109386 0.0812 0.9773 0.076661 0 10 8.8041 9ac96d5f45d71cb3 8dd702555745641a
6 architecture raw_mlp 102 Raw MLP (784->128->64->10) 109386 0.0522 0.976 0.081621 0 10 5.7141 55966246db4dfff1 8dd702555745641a
7 architecture raw_mlp 103 Raw MLP (784->128->64->10) 109386 0.1111 0.9776 0.076823 0 10 4.5491 723217e2418edcfd 8dd702555745641a
8 architecture compact_cnn 101 Compact CNN (9,098 params) 9098 0.0851 0.9866 0.039444 0 10 6.228 db41fb515dcb49fa 8dd702555745641a
9 architecture compact_cnn 102 Compact CNN (9,098 params) 9098 0.0963 0.9858 0.042864 0 10 5.396 efd743cad60c7530 8dd702555745641a
10 architecture compact_cnn 103 Compact CNN (9,098 params) 9098 0.1072 0.9836 0.04912 0 10 5.4935 17891ec08e79ee74 8dd702555745641a
11 optimizer adam_only 101 Compact CNN (Adam-Only) 9098 0.0851 0.9866 0.039444 0 10 6.228 db41fb515dcb49fa 8dd702555745641a
12 optimizer adam_only 102 Compact CNN (Adam-Only) 9098 0.0963 0.9858 0.042864 0 10 5.396 efd743cad60c7530 8dd702555745641a
13 optimizer adam_only 103 Compact CNN (Adam-Only) 9098 0.1072 0.9836 0.04912 0 10 5.4935 17891ec08e79ee74 8dd702555745641a
14 optimizer pso_only 101 Compact CNN (PSO-Only) 9098 0.0851 0.3901 22.182518 0.399 21.674469 40 0 2.5761 db41fb515dcb49fa 8dd702555745641a
15 optimizer pso_only 102 Compact CNN (PSO-Only) 9098 0.0963 0.3886 6.031698 0.3925 5.977414 40 0 2.331 efd743cad60c7530 8dd702555745641a
16 optimizer pso_only 103 Compact CNN (PSO-Only) 9098 0.1072 0.3242 14.099036 0.3405 14.065619 40 0 2.7469 17891ec08e79ee74 8dd702555745641a
17 optimizer hybrid 101 Compact CNN (Hybrid) 9098 0.0851 0.9658 0.109308 0.399 21.674469 40 10 7.4414 db41fb515dcb49fa 8dd702555745641a
18 optimizer hybrid 102 Compact CNN (Hybrid) 9098 0.0963 0.9807 0.061528 0.3925 5.977414 40 10 7.226 efd743cad60c7530 8dd702555745641a
19 optimizer hybrid 103 Compact CNN (Hybrid) 9098 0.1072 0.9725 0.088789 0.3405 14.065619 40 10 7.7426 17891ec08e79ee74 8dd702555745641a
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,61 @@
seed,epoch,train_loss,train_acc,train_mse,test_loss,test_acc,test_mse,fit_time_sec
71,20,1.5967158079147339,0.4830000102519989,0.0660470575094223,1.7100492715835571,0.43799999356269836,0.07075551897287369,32.5467
71,40,1.1239886283874512,0.6434999704360962,0.04822305589914322,1.3276519775390625,0.578000009059906,0.05614830181002617,32.5467
71,60,0.8301568627357483,0.7429999709129333,0.03629826754331589,1.0045816898345947,0.6800000071525574,0.04412851482629776,32.5467
71,80,0.6516683101654053,0.7950000166893005,0.028850017115473747,0.8584634065628052,0.7360000014305115,0.037673790007829666,32.5467
71,100,0.5542685985565186,0.824999988079071,0.024905934929847717,0.7590173482894897,0.7620000243186951,0.03418450802564621,32.5467
71,120,0.49202004075050354,0.8399999737739563,0.021997792646288872,0.7061744332313538,0.7889999747276306,0.03144212067127228,32.5467
71,140,0.45162707567214966,0.8665000200271606,0.01984463632106781,0.6375347971916199,0.796999990940094,0.029180902987718582,32.5467
71,160,0.4246227741241455,0.8740000128746033,0.018856780603528023,0.6248053312301636,0.8009999990463257,0.028511321172118187,32.5467
71,180,0.39958131313323975,0.8799999952316284,0.01768091320991516,0.6031026244163513,0.8159999847412109,0.027248937636613846,32.5467
71,200,0.3831179141998291,0.8924999833106995,0.016940467059612274,0.5841547846794128,0.8180000185966492,0.026774849742650986,32.5467
71,220,0.36869707703590393,0.8970000147819519,0.016194477677345276,0.5548750162124634,0.8259999752044678,0.025661807507276535,32.5467
71,240,0.3557758927345276,0.8989999890327454,0.015656106173992157,0.5201151371002197,0.8420000076293945,0.023789195343852043,32.5467
72,20,1.673862338066101,0.46149998903274536,0.0692000463604927,1.8141505718231201,0.4099999964237213,0.07267315685749054,32.606
72,40,1.1490310430526733,0.6324999928474426,0.04914539307355881,1.372908592224121,0.5709999799728394,0.05662866309285164,32.606
72,60,0.8807169198989868,0.7369999885559082,0.03719912841916084,1.1267516613006592,0.6420000195503235,0.04854830726981163,32.606
72,80,0.6982801556587219,0.7914999723434448,0.030153820291161537,0.9025362133979797,0.7239999771118164,0.038956169039011,32.606
72,100,0.5910305380821228,0.8165000081062317,0.026041019707918167,0.7481632232666016,0.7570000290870667,0.03314316272735596,32.606
72,120,0.5200771689414978,0.8464999794960022,0.022959064692258835,0.6597934365272522,0.7889999747276306,0.029753563925623894,32.606
72,140,0.4719507694244385,0.8554999828338623,0.020908480510115623,0.58547043800354,0.8100000023841858,0.02705816738307476,32.606
72,160,0.4356405735015869,0.871999979019165,0.019257094711065292,0.583936333656311,0.8220000267028809,0.026691943407058716,32.606
72,180,0.4127918779850006,0.8725000023841858,0.018300892785191536,0.5632762312889099,0.8199999928474426,0.025847580283880234,32.606
72,200,0.3940938115119934,0.8805000185966492,0.017671920359134674,0.5363063216209412,0.8339999914169312,0.024680841714143753,32.606
72,220,0.37782323360443115,0.8845000267028809,0.017062701284885406,0.508123517036438,0.8399999737739563,0.023583590984344482,32.606
72,240,0.36558520793914795,0.8899999856948853,0.01645873300731182,0.49120959639549255,0.8429999947547913,0.022604094818234444,32.606
73,20,1.7135908603668213,0.4449999928474426,0.07240503281354904,1.8838708400726318,0.375,0.0778227150440216,32.7255
73,40,1.2155654430389404,0.6340000033378601,0.051504913717508316,1.4863072633743286,0.5569999814033508,0.06111995875835419,32.7255
73,60,0.9407793283462524,0.722000002861023,0.03982725366950035,1.1702646017074585,0.6439999938011169,0.049435749650001526,32.7255
73,80,0.7527623772621155,0.7749999761581421,0.032556530088186264,1.001193642616272,0.6919999718666077,0.04309915751218796,32.7255
73,100,0.6268301606178284,0.8140000104904175,0.027759678661823273,0.8296219706535339,0.7319999933242798,0.03728931397199631,32.7255
73,120,0.5454505681991577,0.8320000171661377,0.02451479621231556,0.7347204089164734,0.7730000019073486,0.03278779238462448,32.7255
73,140,0.46880221366882324,0.8575000166893005,0.021052677184343338,0.6670262813568115,0.7889999747276306,0.030432477593421936,32.7255
73,160,0.43280255794525146,0.8659999966621399,0.01961500570178032,0.6248233914375305,0.8080000281333923,0.028731103986501694,32.7255
73,180,0.4033927321434021,0.8790000081062317,0.017949724569916725,0.5680584907531738,0.8230000138282776,0.02612500637769699,32.7255
73,200,0.3850976228713989,0.8880000114440918,0.017122117802500725,0.5508874654769897,0.8209999799728394,0.025604458525776863,32.7255
73,220,0.3726767599582672,0.8899999856948853,0.016703158617019653,0.5412379503250122,0.8209999799728394,0.025124624371528625,32.7255
73,240,0.3600545823574066,0.8989999890327454,0.016138451173901558,0.5263404250144958,0.8190000057220459,0.024716168642044067,32.7255
74,20,1.5981744527816772,0.49950000643730164,0.06673180311918259,1.7434691190719604,0.42399999499320984,0.07240629941225052,32.9017
74,40,1.1163724660873413,0.6600000262260437,0.047663308680057526,1.2812504768371582,0.6079999804496765,0.05496295168995857,32.9017
74,60,0.8247233629226685,0.7429999709129333,0.03677733987569809,0.996366560459137,0.6800000071525574,0.044562000781297684,32.9017
74,80,0.6858600974082947,0.784500002861023,0.030640259385108948,0.8601324558258057,0.7350000143051147,0.03845023736357689,32.9017
74,100,0.5762760043144226,0.8274999856948853,0.025603292509913445,0.7484227418899536,0.7580000162124634,0.03352699801325798,32.9017
74,120,0.5109706521034241,0.8500000238418579,0.02288639359176159,0.6725562810897827,0.7960000038146973,0.030220312997698784,32.9017
74,140,0.4668312966823578,0.8600000143051147,0.021057307720184326,0.6381762623786926,0.8069999814033508,0.028759241104125977,32.9017
74,160,0.430540531873703,0.8709999918937683,0.01937052235007286,0.6036783456802368,0.8169999718666077,0.026846695691347122,32.9017
74,180,0.4092761278152466,0.8755000233650208,0.018336936831474304,0.5836188793182373,0.8199999928474426,0.0263815987855196,32.9017
74,200,0.3926721215248108,0.8865000009536743,0.017603637650609016,0.5680423378944397,0.8270000219345093,0.02558077871799469,32.9017
74,220,0.38078930974006653,0.8865000009536743,0.017137007787823677,0.5442376136779785,0.8379999995231628,0.024594470858573914,32.9017
74,240,0.37086427211761475,0.8880000114440918,0.016686489805579185,0.5385385155677795,0.8299999833106995,0.024481549859046936,32.9017
75,20,1.635738730430603,0.46950000524520874,0.06960180401802063,1.8486356735229492,0.4059999883174896,0.07580890506505966,32.7233
75,40,1.0605015754699707,0.6604999899864197,0.04849516972899437,1.2981681823730469,0.5630000233650208,0.05867462605237961,32.7233
75,60,0.7738548517227173,0.7615000009536743,0.03458299860358238,1.0294034481048584,0.671999990940094,0.04577324911952019,32.7233
75,80,0.6326538324356079,0.8125,0.02839995175600052,0.889915406703949,0.7300000190734863,0.03931796923279762,32.7233
75,100,0.529951810836792,0.8370000123977661,0.02384800836443901,0.762531042098999,0.7620000243186951,0.034515053033828735,32.7233
75,120,0.47619307041168213,0.8575000166893005,0.021528739482164383,0.6571563482284546,0.781000018119812,0.030564110726118088,32.7233
75,140,0.43610602617263794,0.8709999918937683,0.019426772370934486,0.6097874641418457,0.8140000104904175,0.028030628338456154,32.7233
75,160,0.4064372479915619,0.8794999718666077,0.01836566999554634,0.5621976256370544,0.8309999704360962,0.025522591546177864,32.7233
75,180,0.3866714835166931,0.8865000009536743,0.01732715405523777,0.557114839553833,0.8270000219345093,0.025415310636162758,32.7233
75,200,0.37161633372306824,0.8855000138282776,0.016865627840161324,0.5388693809509277,0.8289999961853027,0.025061823427677155,32.7233
75,220,0.3574405014514923,0.8939999938011169,0.016058821231126785,0.528910756111145,0.8289999961853027,0.024624072015285492,32.7233
75,240,0.3456318974494934,0.8945000171661377,0.0156160369515419,0.5028988122940063,0.8420000076293945,0.023111792281270027,32.7233
1 seed epoch train_loss train_acc train_mse test_loss test_acc test_mse fit_time_sec
2 71 20 1.5967158079147339 0.4830000102519989 0.0660470575094223 1.7100492715835571 0.43799999356269836 0.07075551897287369 32.5467
3 71 40 1.1239886283874512 0.6434999704360962 0.04822305589914322 1.3276519775390625 0.578000009059906 0.05614830181002617 32.5467
4 71 60 0.8301568627357483 0.7429999709129333 0.03629826754331589 1.0045816898345947 0.6800000071525574 0.04412851482629776 32.5467
5 71 80 0.6516683101654053 0.7950000166893005 0.028850017115473747 0.8584634065628052 0.7360000014305115 0.037673790007829666 32.5467
6 71 100 0.5542685985565186 0.824999988079071 0.024905934929847717 0.7590173482894897 0.7620000243186951 0.03418450802564621 32.5467
7 71 120 0.49202004075050354 0.8399999737739563 0.021997792646288872 0.7061744332313538 0.7889999747276306 0.03144212067127228 32.5467
8 71 140 0.45162707567214966 0.8665000200271606 0.01984463632106781 0.6375347971916199 0.796999990940094 0.029180902987718582 32.5467
9 71 160 0.4246227741241455 0.8740000128746033 0.018856780603528023 0.6248053312301636 0.8009999990463257 0.028511321172118187 32.5467
10 71 180 0.39958131313323975 0.8799999952316284 0.01768091320991516 0.6031026244163513 0.8159999847412109 0.027248937636613846 32.5467
11 71 200 0.3831179141998291 0.8924999833106995 0.016940467059612274 0.5841547846794128 0.8180000185966492 0.026774849742650986 32.5467
12 71 220 0.36869707703590393 0.8970000147819519 0.016194477677345276 0.5548750162124634 0.8259999752044678 0.025661807507276535 32.5467
13 71 240 0.3557758927345276 0.8989999890327454 0.015656106173992157 0.5201151371002197 0.8420000076293945 0.023789195343852043 32.5467
14 72 20 1.673862338066101 0.46149998903274536 0.0692000463604927 1.8141505718231201 0.4099999964237213 0.07267315685749054 32.606
15 72 40 1.1490310430526733 0.6324999928474426 0.04914539307355881 1.372908592224121 0.5709999799728394 0.05662866309285164 32.606
16 72 60 0.8807169198989868 0.7369999885559082 0.03719912841916084 1.1267516613006592 0.6420000195503235 0.04854830726981163 32.606
17 72 80 0.6982801556587219 0.7914999723434448 0.030153820291161537 0.9025362133979797 0.7239999771118164 0.038956169039011 32.606
18 72 100 0.5910305380821228 0.8165000081062317 0.026041019707918167 0.7481632232666016 0.7570000290870667 0.03314316272735596 32.606
19 72 120 0.5200771689414978 0.8464999794960022 0.022959064692258835 0.6597934365272522 0.7889999747276306 0.029753563925623894 32.606
20 72 140 0.4719507694244385 0.8554999828338623 0.020908480510115623 0.58547043800354 0.8100000023841858 0.02705816738307476 32.606
21 72 160 0.4356405735015869 0.871999979019165 0.019257094711065292 0.583936333656311 0.8220000267028809 0.026691943407058716 32.606
22 72 180 0.4127918779850006 0.8725000023841858 0.018300892785191536 0.5632762312889099 0.8199999928474426 0.025847580283880234 32.606
23 72 200 0.3940938115119934 0.8805000185966492 0.017671920359134674 0.5363063216209412 0.8339999914169312 0.024680841714143753 32.606
24 72 220 0.37782323360443115 0.8845000267028809 0.017062701284885406 0.508123517036438 0.8399999737739563 0.023583590984344482 32.606
25 72 240 0.36558520793914795 0.8899999856948853 0.01645873300731182 0.49120959639549255 0.8429999947547913 0.022604094818234444 32.606
26 73 20 1.7135908603668213 0.4449999928474426 0.07240503281354904 1.8838708400726318 0.375 0.0778227150440216 32.7255
27 73 40 1.2155654430389404 0.6340000033378601 0.051504913717508316 1.4863072633743286 0.5569999814033508 0.06111995875835419 32.7255
28 73 60 0.9407793283462524 0.722000002861023 0.03982725366950035 1.1702646017074585 0.6439999938011169 0.049435749650001526 32.7255
29 73 80 0.7527623772621155 0.7749999761581421 0.032556530088186264 1.001193642616272 0.6919999718666077 0.04309915751218796 32.7255
30 73 100 0.6268301606178284 0.8140000104904175 0.027759678661823273 0.8296219706535339 0.7319999933242798 0.03728931397199631 32.7255
31 73 120 0.5454505681991577 0.8320000171661377 0.02451479621231556 0.7347204089164734 0.7730000019073486 0.03278779238462448 32.7255
32 73 140 0.46880221366882324 0.8575000166893005 0.021052677184343338 0.6670262813568115 0.7889999747276306 0.030432477593421936 32.7255
33 73 160 0.43280255794525146 0.8659999966621399 0.01961500570178032 0.6248233914375305 0.8080000281333923 0.028731103986501694 32.7255
34 73 180 0.4033927321434021 0.8790000081062317 0.017949724569916725 0.5680584907531738 0.8230000138282776 0.02612500637769699 32.7255
35 73 200 0.3850976228713989 0.8880000114440918 0.017122117802500725 0.5508874654769897 0.8209999799728394 0.025604458525776863 32.7255
36 73 220 0.3726767599582672 0.8899999856948853 0.016703158617019653 0.5412379503250122 0.8209999799728394 0.025124624371528625 32.7255
37 73 240 0.3600545823574066 0.8989999890327454 0.016138451173901558 0.5263404250144958 0.8190000057220459 0.024716168642044067 32.7255
38 74 20 1.5981744527816772 0.49950000643730164 0.06673180311918259 1.7434691190719604 0.42399999499320984 0.07240629941225052 32.9017
39 74 40 1.1163724660873413 0.6600000262260437 0.047663308680057526 1.2812504768371582 0.6079999804496765 0.05496295168995857 32.9017
40 74 60 0.8247233629226685 0.7429999709129333 0.03677733987569809 0.996366560459137 0.6800000071525574 0.044562000781297684 32.9017
41 74 80 0.6858600974082947 0.784500002861023 0.030640259385108948 0.8601324558258057 0.7350000143051147 0.03845023736357689 32.9017
42 74 100 0.5762760043144226 0.8274999856948853 0.025603292509913445 0.7484227418899536 0.7580000162124634 0.03352699801325798 32.9017
43 74 120 0.5109706521034241 0.8500000238418579 0.02288639359176159 0.6725562810897827 0.7960000038146973 0.030220312997698784 32.9017
44 74 140 0.4668312966823578 0.8600000143051147 0.021057307720184326 0.6381762623786926 0.8069999814033508 0.028759241104125977 32.9017
45 74 160 0.430540531873703 0.8709999918937683 0.01937052235007286 0.6036783456802368 0.8169999718666077 0.026846695691347122 32.9017
46 74 180 0.4092761278152466 0.8755000233650208 0.018336936831474304 0.5836188793182373 0.8199999928474426 0.0263815987855196 32.9017
47 74 200 0.3926721215248108 0.8865000009536743 0.017603637650609016 0.5680423378944397 0.8270000219345093 0.02558077871799469 32.9017
48 74 220 0.38078930974006653 0.8865000009536743 0.017137007787823677 0.5442376136779785 0.8379999995231628 0.024594470858573914 32.9017
49 74 240 0.37086427211761475 0.8880000114440918 0.016686489805579185 0.5385385155677795 0.8299999833106995 0.024481549859046936 32.9017
50 75 20 1.635738730430603 0.46950000524520874 0.06960180401802063 1.8486356735229492 0.4059999883174896 0.07580890506505966 32.7233
51 75 40 1.0605015754699707 0.6604999899864197 0.04849516972899437 1.2981681823730469 0.5630000233650208 0.05867462605237961 32.7233
52 75 60 0.7738548517227173 0.7615000009536743 0.03458299860358238 1.0294034481048584 0.671999990940094 0.04577324911952019 32.7233
53 75 80 0.6326538324356079 0.8125 0.02839995175600052 0.889915406703949 0.7300000190734863 0.03931796923279762 32.7233
54 75 100 0.529951810836792 0.8370000123977661 0.02384800836443901 0.762531042098999 0.7620000243186951 0.034515053033828735 32.7233
55 75 120 0.47619307041168213 0.8575000166893005 0.021528739482164383 0.6571563482284546 0.781000018119812 0.030564110726118088 32.7233
56 75 140 0.43610602617263794 0.8709999918937683 0.019426772370934486 0.6097874641418457 0.8140000104904175 0.028030628338456154 32.7233
57 75 160 0.4064372479915619 0.8794999718666077 0.01836566999554634 0.5621976256370544 0.8309999704360962 0.025522591546177864 32.7233
58 75 180 0.3866714835166931 0.8865000009536743 0.01732715405523777 0.557114839553833 0.8270000219345093 0.025415310636162758 32.7233
59 75 200 0.37161633372306824 0.8855000138282776 0.016865627840161324 0.5388693809509277 0.8289999961853027 0.025061823427677155 32.7233
60 75 220 0.3574405014514923 0.8939999938011169 0.016058821231126785 0.528910756111145 0.8289999961853027 0.024624072015285492 32.7233
61 75 240 0.3456318974494934 0.8945000171661377 0.0156160369515419 0.5028988122940063 0.8420000076293945 0.023111792281270027 32.7233
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
seed,epoch,train_loss,train_acc,train_mse,test_loss,test_acc,test_mse,fit_time_sec
71,20,1.6637481451034546,0.4778333306312561,0.06879015266895294,1.6244964599609375,0.4961000084877014,0.0671548843383789,33.649
71,40,1.165610432624817,0.6545166373252869,0.04875154793262482,1.0854827165603638,0.6718000173568726,0.046549420803785324,33.649
71,60,0.905483067035675,0.7269166707992554,0.0389271154999733,0.8291375041007996,0.7445999979972839,0.036550913006067276,33.649
71,80,0.7415720224380493,0.7783499956130981,0.03182263299822807,0.6753337979316711,0.7944999933242798,0.029618915170431137,33.649
71,100,0.6563442945480347,0.8064000010490417,0.02799912355840206,0.5993074178695679,0.8216999769210815,0.025761328637599945,33.649
71,120,0.588559091091156,0.8279500007629395,0.025105126202106476,0.5335480570793152,0.8414999842643738,0.022965701296925545,33.649
71,140,0.5534469485282898,0.8387500047683716,0.023768901824951172,0.5105180144309998,0.8489999771118164,0.022098202258348465,33.649
71,160,0.5169084668159485,0.8464000225067139,0.022431854158639908,0.4750884771347046,0.8555999994277954,0.02082662656903267,33.649
71,180,0.48863890767097473,0.8547000288963318,0.021347153931856155,0.4477296769618988,0.8686000108718872,0.01953563280403614,33.649
71,200,0.4686008393764496,0.8610333204269409,0.020526422187685966,0.43183833360671997,0.8719000220298767,0.018971838057041168,33.649
71,220,0.45222264528274536,0.8653833270072937,0.0198610108345747,0.4211212396621704,0.8756999969482422,0.01851881667971611,33.649
71,240,0.438436359167099,0.8698999881744385,0.019294776022434235,0.41157108545303345,0.878000020980835,0.018099937587976456,33.649
72,20,1.7392971515655518,0.4580833315849304,0.07111531496047974,1.7067090272903442,0.4602999985218048,0.07063549011945724,33.7062
72,40,1.2618558406829834,0.6182666420936584,0.05362313985824585,1.2296088933944702,0.6273000240325928,0.05239206552505493,33.7062
72,60,0.9506728053092957,0.7125833630561829,0.04073568433523178,0.90444415807724,0.725600004196167,0.03892349451780319,33.7062
72,80,0.7690414786338806,0.7677000164985657,0.0332583524286747,0.7225316762924194,0.7825000286102295,0.03151436522603035,33.7062
72,100,0.6607407331466675,0.7977833151817322,0.029053399339318275,0.6213417053222656,0.8091999888420105,0.02738099917769432,33.7062
72,120,0.5884125232696533,0.8225333094596863,0.02557925134897232,0.5514547824859619,0.8324999809265137,0.024056605994701385,33.7062
72,140,0.5509796142578125,0.8328999876976013,0.02395990863442421,0.5114421844482422,0.8463000059127808,0.022018717601895332,33.7062
72,160,0.5122449398040771,0.8464499711990356,0.022468935698270798,0.4830068349838257,0.8574000000953674,0.021029997617006302,33.7062
72,180,0.4828203022480011,0.8543833494186401,0.021225325763225555,0.45465460419654846,0.8646000027656555,0.01990104280412197,33.7062
72,200,0.46293872594833374,0.8621000051498413,0.020341966301202774,0.4369881749153137,0.8700000047683716,0.019186409190297127,33.7062
72,220,0.4459497630596161,0.8664166927337646,0.0197211392223835,0.41942450404167175,0.8730000257492065,0.0184775423258543,33.7062
72,240,0.4350419342517853,0.871566653251648,0.01916772872209549,0.41060513257980347,0.875,0.018150048330426216,33.7062
73,20,1.703110694885254,0.4322333335876465,0.07236693799495697,1.662993311882019,0.4422999918460846,0.07139508426189423,33.0256
73,40,1.234723448753357,0.605400025844574,0.05369972065091133,1.2019942998886108,0.6128000020980835,0.05271701514720917,33.0256
73,60,0.9583781361579895,0.7071499824523926,0.04126281663775444,0.9148314595222473,0.7215999960899353,0.03978179767727852,33.0256
73,80,0.781466543674469,0.7578666806221008,0.034513432532548904,0.7499686479568481,0.7670999765396118,0.03327890485525131,33.0256
73,100,0.6772935390472412,0.7944999933242798,0.029873577877879143,0.6402292251586914,0.8046000003814697,0.02839311771094799,33.0256
73,120,0.6110365986824036,0.8133666515350342,0.027105839923024178,0.5728253722190857,0.821399986743927,0.02574964240193367,33.0256
73,140,0.5631463527679443,0.8302500247955322,0.024875810369849205,0.5334057807922363,0.8353999853134155,0.023713810369372368,33.0256
73,160,0.5311679840087891,0.8410833477973938,0.023450637236237526,0.501768171787262,0.8464000225067139,0.022286171093583107,33.0256
73,180,0.5000290870666504,0.8493833541870117,0.022157883271574974,0.4740225672721863,0.8550000190734863,0.021153515204787254,33.0256
73,200,0.48113930225372314,0.8564833402633667,0.02121545933187008,0.4526425898075104,0.8634999990463257,0.020089736208319664,33.0256
73,220,0.46380415558815,0.8618666529655457,0.020463503897190094,0.4359147846698761,0.8684999942779541,0.019339669495821,33.0256
73,240,0.449687123298645,0.8664166927337646,0.019846484065055847,0.4234127998352051,0.8718000054359436,0.018784264102578163,33.0256
74,20,1.696816325187683,0.4334833323955536,0.07303200662136078,1.685478925704956,0.4397999942302704,0.07274489104747772,32.3565
74,40,1.2137997150421143,0.6062666773796082,0.053883783519268036,1.193790316581726,0.6158999800682068,0.05286615714430809,32.3565
74,60,0.9459328651428223,0.7093166708946228,0.04073885455727577,0.926419734954834,0.7172999978065491,0.03994119539856911,32.3565
74,80,0.7440351843833923,0.7731500267982483,0.03230465576052666,0.7158955335617065,0.7832000255584717,0.0311629269272089,32.3565
74,100,0.6222507357597351,0.8106666803359985,0.02721019648015499,0.5782299041748047,0.826200008392334,0.025308359414339066,32.3565
74,120,0.5712149143218994,0.8261666893959045,0.025098947808146477,0.5316479206085205,0.8402000069618225,0.023361243307590485,32.3565
74,140,0.5278127193450928,0.8391500115394592,0.02332845889031887,0.5003852248191833,0.8485000133514404,0.022033091634511948,32.3565
74,160,0.4974406957626343,0.8500166535377502,0.02210366539657116,0.4702143669128418,0.8614000082015991,0.02081647887825966,32.3565
74,180,0.4760556221008301,0.8567833304405212,0.0210970938205719,0.4514496922492981,0.8648999929428101,0.01998041942715645,32.3565
74,200,0.4600929617881775,0.8631166815757751,0.020222675055265427,0.43229734897613525,0.8694999814033508,0.01901264674961567,32.3565
74,220,0.44254931807518005,0.8678500056266785,0.019469955936074257,0.417081743478775,0.8754000067710876,0.018321329727768898,32.3565
74,240,0.43052077293395996,0.8725666403770447,0.018974633887410164,0.40485620498657227,0.8798999786376953,0.01778729446232319,32.3565
75,20,1.7082723379135132,0.42640000581741333,0.07211916148662567,1.6778934001922607,0.43220001459121704,0.07167963683605194,33.1184
75,40,1.2474853992462158,0.628333330154419,0.05103134736418724,1.1880637407302856,0.6402000188827515,0.04922466725111008,33.1184
75,60,0.9827106595039368,0.7029333114624023,0.041922468692064285,0.9340476393699646,0.7074000239372253,0.040672920644283295,33.1184
75,80,0.8061239719390869,0.7508833408355713,0.035569027066230774,0.7613834738731384,0.7562999725341797,0.03404615819454193,33.1184
75,100,0.6677705645561218,0.7959833145141602,0.029427148401737213,0.6365524530410767,0.8015999794006348,0.027956314384937286,33.1184
75,120,0.5898874998092651,0.8204500079154968,0.025896690785884857,0.5661898255348206,0.8285999894142151,0.024743687361478806,33.1184
75,140,0.5386065244674683,0.8367666602134705,0.023783763870596886,0.5188254714012146,0.8396999835968018,0.022942783311009407,33.1184
75,160,0.5006535053253174,0.8497833609580994,0.022141018882393837,0.48212501406669617,0.8569999933242798,0.021169248968362808,33.1184
75,180,0.47602227330207825,0.8565999865531921,0.021099673584103584,0.45692178606987,0.861299991607666,0.020099563524127007,33.1184
75,200,0.4532005488872528,0.8648999929428101,0.020130231976509094,0.4406105577945709,0.8676999807357788,0.019379951059818268,33.1184
75,220,0.4393870532512665,0.8705000281333923,0.019430004060268402,0.42279207706451416,0.8766999840736389,0.018535815179347992,33.1184
75,240,0.4296344220638275,0.8724499940872192,0.019024165347218513,0.40994536876678467,0.8802000284194946,0.01800552010536194,33.1184
1 seed epoch train_loss train_acc train_mse test_loss test_acc test_mse fit_time_sec
2 71 20 1.6637481451034546 0.4778333306312561 0.06879015266895294 1.6244964599609375 0.4961000084877014 0.0671548843383789 33.649
3 71 40 1.165610432624817 0.6545166373252869 0.04875154793262482 1.0854827165603638 0.6718000173568726 0.046549420803785324 33.649
4 71 60 0.905483067035675 0.7269166707992554 0.0389271154999733 0.8291375041007996 0.7445999979972839 0.036550913006067276 33.649
5 71 80 0.7415720224380493 0.7783499956130981 0.03182263299822807 0.6753337979316711 0.7944999933242798 0.029618915170431137 33.649
6 71 100 0.6563442945480347 0.8064000010490417 0.02799912355840206 0.5993074178695679 0.8216999769210815 0.025761328637599945 33.649
7 71 120 0.588559091091156 0.8279500007629395 0.025105126202106476 0.5335480570793152 0.8414999842643738 0.022965701296925545 33.649
8 71 140 0.5534469485282898 0.8387500047683716 0.023768901824951172 0.5105180144309998 0.8489999771118164 0.022098202258348465 33.649
9 71 160 0.5169084668159485 0.8464000225067139 0.022431854158639908 0.4750884771347046 0.8555999994277954 0.02082662656903267 33.649
10 71 180 0.48863890767097473 0.8547000288963318 0.021347153931856155 0.4477296769618988 0.8686000108718872 0.01953563280403614 33.649
11 71 200 0.4686008393764496 0.8610333204269409 0.020526422187685966 0.43183833360671997 0.8719000220298767 0.018971838057041168 33.649
12 71 220 0.45222264528274536 0.8653833270072937 0.0198610108345747 0.4211212396621704 0.8756999969482422 0.01851881667971611 33.649
13 71 240 0.438436359167099 0.8698999881744385 0.019294776022434235 0.41157108545303345 0.878000020980835 0.018099937587976456 33.649
14 72 20 1.7392971515655518 0.4580833315849304 0.07111531496047974 1.7067090272903442 0.4602999985218048 0.07063549011945724 33.7062
15 72 40 1.2618558406829834 0.6182666420936584 0.05362313985824585 1.2296088933944702 0.6273000240325928 0.05239206552505493 33.7062
16 72 60 0.9506728053092957 0.7125833630561829 0.04073568433523178 0.90444415807724 0.725600004196167 0.03892349451780319 33.7062
17 72 80 0.7690414786338806 0.7677000164985657 0.0332583524286747 0.7225316762924194 0.7825000286102295 0.03151436522603035 33.7062
18 72 100 0.6607407331466675 0.7977833151817322 0.029053399339318275 0.6213417053222656 0.8091999888420105 0.02738099917769432 33.7062
19 72 120 0.5884125232696533 0.8225333094596863 0.02557925134897232 0.5514547824859619 0.8324999809265137 0.024056605994701385 33.7062
20 72 140 0.5509796142578125 0.8328999876976013 0.02395990863442421 0.5114421844482422 0.8463000059127808 0.022018717601895332 33.7062
21 72 160 0.5122449398040771 0.8464499711990356 0.022468935698270798 0.4830068349838257 0.8574000000953674 0.021029997617006302 33.7062
22 72 180 0.4828203022480011 0.8543833494186401 0.021225325763225555 0.45465460419654846 0.8646000027656555 0.01990104280412197 33.7062
23 72 200 0.46293872594833374 0.8621000051498413 0.020341966301202774 0.4369881749153137 0.8700000047683716 0.019186409190297127 33.7062
24 72 220 0.4459497630596161 0.8664166927337646 0.0197211392223835 0.41942450404167175 0.8730000257492065 0.0184775423258543 33.7062
25 72 240 0.4350419342517853 0.871566653251648 0.01916772872209549 0.41060513257980347 0.875 0.018150048330426216 33.7062
26 73 20 1.703110694885254 0.4322333335876465 0.07236693799495697 1.662993311882019 0.4422999918460846 0.07139508426189423 33.0256
27 73 40 1.234723448753357 0.605400025844574 0.05369972065091133 1.2019942998886108 0.6128000020980835 0.05271701514720917 33.0256
28 73 60 0.9583781361579895 0.7071499824523926 0.04126281663775444 0.9148314595222473 0.7215999960899353 0.03978179767727852 33.0256
29 73 80 0.781466543674469 0.7578666806221008 0.034513432532548904 0.7499686479568481 0.7670999765396118 0.03327890485525131 33.0256
30 73 100 0.6772935390472412 0.7944999933242798 0.029873577877879143 0.6402292251586914 0.8046000003814697 0.02839311771094799 33.0256
31 73 120 0.6110365986824036 0.8133666515350342 0.027105839923024178 0.5728253722190857 0.821399986743927 0.02574964240193367 33.0256
32 73 140 0.5631463527679443 0.8302500247955322 0.024875810369849205 0.5334057807922363 0.8353999853134155 0.023713810369372368 33.0256
33 73 160 0.5311679840087891 0.8410833477973938 0.023450637236237526 0.501768171787262 0.8464000225067139 0.022286171093583107 33.0256
34 73 180 0.5000290870666504 0.8493833541870117 0.022157883271574974 0.4740225672721863 0.8550000190734863 0.021153515204787254 33.0256
35 73 200 0.48113930225372314 0.8564833402633667 0.02121545933187008 0.4526425898075104 0.8634999990463257 0.020089736208319664 33.0256
36 73 220 0.46380415558815 0.8618666529655457 0.020463503897190094 0.4359147846698761 0.8684999942779541 0.019339669495821 33.0256
37 73 240 0.449687123298645 0.8664166927337646 0.019846484065055847 0.4234127998352051 0.8718000054359436 0.018784264102578163 33.0256
38 74 20 1.696816325187683 0.4334833323955536 0.07303200662136078 1.685478925704956 0.4397999942302704 0.07274489104747772 32.3565
39 74 40 1.2137997150421143 0.6062666773796082 0.053883783519268036 1.193790316581726 0.6158999800682068 0.05286615714430809 32.3565
40 74 60 0.9459328651428223 0.7093166708946228 0.04073885455727577 0.926419734954834 0.7172999978065491 0.03994119539856911 32.3565
41 74 80 0.7440351843833923 0.7731500267982483 0.03230465576052666 0.7158955335617065 0.7832000255584717 0.0311629269272089 32.3565
42 74 100 0.6222507357597351 0.8106666803359985 0.02721019648015499 0.5782299041748047 0.826200008392334 0.025308359414339066 32.3565
43 74 120 0.5712149143218994 0.8261666893959045 0.025098947808146477 0.5316479206085205 0.8402000069618225 0.023361243307590485 32.3565
44 74 140 0.5278127193450928 0.8391500115394592 0.02332845889031887 0.5003852248191833 0.8485000133514404 0.022033091634511948 32.3565
45 74 160 0.4974406957626343 0.8500166535377502 0.02210366539657116 0.4702143669128418 0.8614000082015991 0.02081647887825966 32.3565
46 74 180 0.4760556221008301 0.8567833304405212 0.0210970938205719 0.4514496922492981 0.8648999929428101 0.01998041942715645 32.3565
47 74 200 0.4600929617881775 0.8631166815757751 0.020222675055265427 0.43229734897613525 0.8694999814033508 0.01901264674961567 32.3565
48 74 220 0.44254931807518005 0.8678500056266785 0.019469955936074257 0.417081743478775 0.8754000067710876 0.018321329727768898 32.3565
49 74 240 0.43052077293395996 0.8725666403770447 0.018974633887410164 0.40485620498657227 0.8798999786376953 0.01778729446232319 32.3565
50 75 20 1.7082723379135132 0.42640000581741333 0.07211916148662567 1.6778934001922607 0.43220001459121704 0.07167963683605194 33.1184
51 75 40 1.2474853992462158 0.628333330154419 0.05103134736418724 1.1880637407302856 0.6402000188827515 0.04922466725111008 33.1184
52 75 60 0.9827106595039368 0.7029333114624023 0.041922468692064285 0.9340476393699646 0.7074000239372253 0.040672920644283295 33.1184
53 75 80 0.8061239719390869 0.7508833408355713 0.035569027066230774 0.7613834738731384 0.7562999725341797 0.03404615819454193 33.1184
54 75 100 0.6677705645561218 0.7959833145141602 0.029427148401737213 0.6365524530410767 0.8015999794006348 0.027956314384937286 33.1184
55 75 120 0.5898874998092651 0.8204500079154968 0.025896690785884857 0.5661898255348206 0.8285999894142151 0.024743687361478806 33.1184
56 75 140 0.5386065244674683 0.8367666602134705 0.023783763870596886 0.5188254714012146 0.8396999835968018 0.022942783311009407 33.1184
57 75 160 0.5006535053253174 0.8497833609580994 0.022141018882393837 0.48212501406669617 0.8569999933242798 0.021169248968362808 33.1184
58 75 180 0.47602227330207825 0.8565999865531921 0.021099673584103584 0.45692178606987 0.861299991607666 0.020099563524127007 33.1184
59 75 200 0.4532005488872528 0.8648999929428101 0.020130231976509094 0.4406105577945709 0.8676999807357788 0.019379951059818268 33.1184
60 75 220 0.4393870532512665 0.8705000281333923 0.019430004060268402 0.42279207706451416 0.8766999840736389 0.018535815179347992 33.1184
61 75 240 0.4296344220638275 0.8724499940872192 0.019024165347218513 0.40994536876678467 0.8802000284194946 0.01800552010536194 33.1184
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,36 @@
dataset,method,n_particles,epochs,n_seeds,eval_acc_mean,eval_acc_std,eval_acc_median,eval_acc_iqr,eval_acc_ci95,eval_loss_mean,eval_loss_std,eval_loss_median,eval_loss_iqr,eval_loss_ci95,eval_mse_mean,eval_mse_std,train_acc_mean,train_loss_mean,runtime_seconds_mean,runtime_seconds_std,rank_acc,rank_loss
Digits,adaptive_moment,24,50,5,0.267778,0.018279,0.272222,0.002778,0.022695,2.108837,0.023357,2.10196,0.039265,0.029001,0.084021,0.001996,0.2718,2.086962,2.499448,0.161274,3,4
Digits,bare_bones,24,50,5,0.19,0.056266,0.155556,0.1,0.069863,2.250203,0.084448,2.251535,0.020136,0.104855,0.088622,0.003141,0.2,2.230929,2.564876,0.124195,6,6
Digits,clpso,24,50,5,0.207778,0.050629,0.213889,0.05,0.062863,2.204543,0.023215,2.210108,0.008743,0.028825,0.087396,0.001398,0.223,2.176502,2.519316,0.326734,5,5
Digits,constriction,24,50,5,0.372778,0.017938,0.380556,0.025,0.022272,1.765113,0.057309,1.760801,0.0677,0.071157,0.074815,0.001608,0.382,1.720719,2.330013,0.148841,1,1
Digits,fips,24,50,5,0.249444,0.06147,0.263889,0.069444,0.076324,2.064165,0.091569,2.064055,0.108705,0.113696,0.084041,0.002578,0.2662,2.056891,2.621102,0.126036,4,3
Digits,inertia,24,50,5,0.303333,0.061658,0.305556,0.088889,0.076557,1.980667,0.14307,1.956439,0.144242,0.177642,0.080715,0.004026,0.297,2.014632,2.006851,0.09822,2,2
Digits,original,24,50,5,0.164444,0.022343,0.158333,0.036111,0.027743,2.269628,0.025331,2.255083,0.030712,0.031453,0.089494,0.000451,0.1798,2.266102,2.272324,0.113557,7,7
Iris,adaptive_moment,24,60,5,0.846667,0.076739,0.866667,0.133333,0.095283,0.349701,0.178108,0.334009,0.15044,0.221147,0.071416,0.035403,0.89,0.294127,1.392557,0.004708,4,4
Iris,bare_bones,24,60,5,0.846667,0.055777,0.833333,0.066667,0.069256,0.405791,0.172336,0.367909,0.160981,0.21398,0.072802,0.025814,0.885,0.30248,1.66449,0.144454,5,5
Iris,clpso,24,60,5,0.793333,0.076012,0.766667,0.066667,0.094379,0.44827,0.053877,0.474597,0.049216,0.066896,0.091464,0.016877,0.78,0.462534,1.86465,0.145025,6,6
Iris,constriction,24,60,5,0.94,0.036515,0.966667,0.066667,0.045338,0.171796,0.076965,0.148386,0.105914,0.095563,0.033793,0.017284,0.991667,0.032715,1.4309,0.079265,2,2
Iris,fips,24,60,5,0.74,0.054772,0.733333,0.033333,0.068008,0.60501,0.060601,0.625894,0.064748,0.075245,0.117407,0.008091,0.766667,0.595101,2.072251,0.154361,7,7
Iris,inertia,24,60,5,0.94,0.027889,0.933333,0.033333,0.034628,0.106901,0.053063,0.108104,0.053939,0.065885,0.024266,0.011903,0.983333,0.040899,1.569569,0.121148,1,1
Iris,original,24,60,5,0.92,0.086923,0.966667,0.033333,0.107927,0.222324,0.155767,0.192657,0.065706,0.193407,0.043721,0.035081,0.966667,0.08742,1.502316,0.042929,3,3
MNIST,adaptive_moment,30,80,5,0.262,0.01044,0.269,0.014,0.012963,2.219058,0.104724,2.175837,0.105964,0.13003,0.087089,0.001349,0.2678,2.184856,3.547601,0.614982,4,4
MNIST,bare_bones,30,80,5,0.4126,0.037958,0.408,0.057,0.04713,1.965562,0.175909,1.989087,0.298842,0.218416,0.077204,0.004802,0.4408,1.8435,3.435835,0.303983,3,3
MNIST,clpso,30,80,5,0.2068,0.022775,0.2,0.019,0.028278,2.285833,0.068177,2.307686,0.037065,0.084651,0.089514,0.00163,0.2342,2.199232,3.536445,0.226486,6,6
MNIST,constriction,30,80,5,0.52,0.046244,0.528,0.047,0.057418,1.507968,0.149152,1.509533,0.108216,0.185194,0.063327,0.005427,0.5968,1.284408,3.298112,0.589527,1,1
MNIST,fips,30,80,5,0.2106,0.048066,0.229,0.04,0.05968,2.22323,0.073048,2.176908,0.130584,0.0907,0.088006,0.001556,0.2302,2.177675,3.806571,0.141017,5,5
MNIST,inertia,30,80,5,0.4684,0.075252,0.497,0.108,0.093436,1.716089,0.260764,1.620183,0.331954,0.323776,0.070705,0.008553,0.552,1.44457,3.048485,0.155303,2,2
MNIST,original,30,80,5,0.144,0.016628,0.143,0.026,0.020646,2.428194,0.091877,2.415645,0.154207,0.114079,0.093035,0.001872,0.1563,2.385448,3.490117,0.862784,7,7
Seeds,adaptive_moment,24,60,5,0.890476,0.046413,0.904762,0.047619,0.057629,0.315947,0.120895,0.278367,0.077865,0.150108,0.053174,0.012296,0.917857,0.225176,1.407326,0.041435,2,1
Seeds,bare_bones,24,60,5,0.871429,0.068595,0.857143,0.095238,0.08517,0.42192,0.120052,0.439151,0.13714,0.149062,0.069299,0.016028,0.882143,0.348336,1.54165,0.018779,6,5
Seeds,clpso,24,60,5,0.857143,0.029161,0.857143,0.023809,0.036207,0.469817,0.073254,0.510473,0.12722,0.090955,0.0841,0.011752,0.892857,0.369897,1.537528,0.018265,7,6
Seeds,constriction,24,60,5,0.890476,0.091597,0.880952,0.095238,0.113731,0.35142,0.3882,0.252366,0.243302,0.482006,0.056642,0.052203,0.960714,0.100819,1.36371,0.053261,3,3
Seeds,fips,24,60,5,0.885714,0.035315,0.880952,0.02381,0.043849,0.383732,0.103951,0.392303,0.094168,0.12907,0.067541,0.016554,0.9,0.336734,1.815592,0.045394,4,4
Seeds,inertia,24,60,5,0.914286,0.052164,0.928571,0.023809,0.064769,0.331656,0.342636,0.202104,0.068956,0.425432,0.048999,0.036322,0.963095,0.103132,1.318196,0.015319,1,2
Seeds,original,24,60,5,0.87619,0.042592,0.880952,0.071429,0.052884,0.49224,0.433653,0.324954,0.104917,0.538443,0.065762,0.028096,0.916667,0.246239,1.385803,0.082957,5,7
XOR,adaptive_moment,24,80,5,1.0,0.0,1.0,0.0,0.0,0.190431,0.178122,0.197448,0.188515,0.221164,0.04504,0.056878,1.0,0.190431,1.785274,0.092073,5,5
XOR,bare_bones,24,80,5,1.0,0.0,1.0,0.0,0.0,0.0821,0.056963,0.081613,0.055056,0.070728,0.010907,0.010338,1.0,0.0821,2.031221,0.13707,4,4
XOR,clpso,24,80,5,0.6,0.136931,0.5,0.25,0.170019,0.548166,0.031946,0.55628,0.024237,0.039666,0.183052,0.012602,0.6,0.548166,2.295572,0.260868,7,7
XOR,constriction,24,80,5,1.0,0.0,1.0,0.0,0.0,0.004086,0.002357,0.005281,0.001585,0.002927,3.2e-05,1.9e-05,1.0,0.004086,1.987776,0.182034,1,1
XOR,fips,24,80,5,0.75,0.25,0.75,0.5,0.310411,0.546355,0.059307,0.568609,0.092147,0.073638,0.178887,0.029466,0.75,0.546355,2.502509,0.055447,6,6
XOR,inertia,24,80,5,1.0,0.0,1.0,0.0,0.0,0.004431,0.003308,0.003801,0.001559,0.004108,3.6e-05,3.1e-05,1.0,0.004431,1.969097,0.170892,2,2
XOR,original,24,80,5,1.0,0.0,1.0,0.0,0.0,0.004558,0.002672,0.004584,0.001454,0.003318,4.2e-05,2.8e-05,1.0,0.004558,1.905231,0.246635,3,3
1 dataset method n_particles epochs n_seeds eval_acc_mean eval_acc_std eval_acc_median eval_acc_iqr eval_acc_ci95 eval_loss_mean eval_loss_std eval_loss_median eval_loss_iqr eval_loss_ci95 eval_mse_mean eval_mse_std train_acc_mean train_loss_mean runtime_seconds_mean runtime_seconds_std rank_acc rank_loss
2 Digits adaptive_moment 24 50 5 0.267778 0.018279 0.272222 0.002778 0.022695 2.108837 0.023357 2.10196 0.039265 0.029001 0.084021 0.001996 0.2718 2.086962 2.499448 0.161274 3 4
3 Digits bare_bones 24 50 5 0.19 0.056266 0.155556 0.1 0.069863 2.250203 0.084448 2.251535 0.020136 0.104855 0.088622 0.003141 0.2 2.230929 2.564876 0.124195 6 6
4 Digits clpso 24 50 5 0.207778 0.050629 0.213889 0.05 0.062863 2.204543 0.023215 2.210108 0.008743 0.028825 0.087396 0.001398 0.223 2.176502 2.519316 0.326734 5 5
5 Digits constriction 24 50 5 0.372778 0.017938 0.380556 0.025 0.022272 1.765113 0.057309 1.760801 0.0677 0.071157 0.074815 0.001608 0.382 1.720719 2.330013 0.148841 1 1
6 Digits fips 24 50 5 0.249444 0.06147 0.263889 0.069444 0.076324 2.064165 0.091569 2.064055 0.108705 0.113696 0.084041 0.002578 0.2662 2.056891 2.621102 0.126036 4 3
7 Digits inertia 24 50 5 0.303333 0.061658 0.305556 0.088889 0.076557 1.980667 0.14307 1.956439 0.144242 0.177642 0.080715 0.004026 0.297 2.014632 2.006851 0.09822 2 2
8 Digits original 24 50 5 0.164444 0.022343 0.158333 0.036111 0.027743 2.269628 0.025331 2.255083 0.030712 0.031453 0.089494 0.000451 0.1798 2.266102 2.272324 0.113557 7 7
9 Iris adaptive_moment 24 60 5 0.846667 0.076739 0.866667 0.133333 0.095283 0.349701 0.178108 0.334009 0.15044 0.221147 0.071416 0.035403 0.89 0.294127 1.392557 0.004708 4 4
10 Iris bare_bones 24 60 5 0.846667 0.055777 0.833333 0.066667 0.069256 0.405791 0.172336 0.367909 0.160981 0.21398 0.072802 0.025814 0.885 0.30248 1.66449 0.144454 5 5
11 Iris clpso 24 60 5 0.793333 0.076012 0.766667 0.066667 0.094379 0.44827 0.053877 0.474597 0.049216 0.066896 0.091464 0.016877 0.78 0.462534 1.86465 0.145025 6 6
12 Iris constriction 24 60 5 0.94 0.036515 0.966667 0.066667 0.045338 0.171796 0.076965 0.148386 0.105914 0.095563 0.033793 0.017284 0.991667 0.032715 1.4309 0.079265 2 2
13 Iris fips 24 60 5 0.74 0.054772 0.733333 0.033333 0.068008 0.60501 0.060601 0.625894 0.064748 0.075245 0.117407 0.008091 0.766667 0.595101 2.072251 0.154361 7 7
14 Iris inertia 24 60 5 0.94 0.027889 0.933333 0.033333 0.034628 0.106901 0.053063 0.108104 0.053939 0.065885 0.024266 0.011903 0.983333 0.040899 1.569569 0.121148 1 1
15 Iris original 24 60 5 0.92 0.086923 0.966667 0.033333 0.107927 0.222324 0.155767 0.192657 0.065706 0.193407 0.043721 0.035081 0.966667 0.08742 1.502316 0.042929 3 3
16 MNIST adaptive_moment 30 80 5 0.262 0.01044 0.269 0.014 0.012963 2.219058 0.104724 2.175837 0.105964 0.13003 0.087089 0.001349 0.2678 2.184856 3.547601 0.614982 4 4
17 MNIST bare_bones 30 80 5 0.4126 0.037958 0.408 0.057 0.04713 1.965562 0.175909 1.989087 0.298842 0.218416 0.077204 0.004802 0.4408 1.8435 3.435835 0.303983 3 3
18 MNIST clpso 30 80 5 0.2068 0.022775 0.2 0.019 0.028278 2.285833 0.068177 2.307686 0.037065 0.084651 0.089514 0.00163 0.2342 2.199232 3.536445 0.226486 6 6
19 MNIST constriction 30 80 5 0.52 0.046244 0.528 0.047 0.057418 1.507968 0.149152 1.509533 0.108216 0.185194 0.063327 0.005427 0.5968 1.284408 3.298112 0.589527 1 1
20 MNIST fips 30 80 5 0.2106 0.048066 0.229 0.04 0.05968 2.22323 0.073048 2.176908 0.130584 0.0907 0.088006 0.001556 0.2302 2.177675 3.806571 0.141017 5 5
21 MNIST inertia 30 80 5 0.4684 0.075252 0.497 0.108 0.093436 1.716089 0.260764 1.620183 0.331954 0.323776 0.070705 0.008553 0.552 1.44457 3.048485 0.155303 2 2
22 MNIST original 30 80 5 0.144 0.016628 0.143 0.026 0.020646 2.428194 0.091877 2.415645 0.154207 0.114079 0.093035 0.001872 0.1563 2.385448 3.490117 0.862784 7 7
23 Seeds adaptive_moment 24 60 5 0.890476 0.046413 0.904762 0.047619 0.057629 0.315947 0.120895 0.278367 0.077865 0.150108 0.053174 0.012296 0.917857 0.225176 1.407326 0.041435 2 1
24 Seeds bare_bones 24 60 5 0.871429 0.068595 0.857143 0.095238 0.08517 0.42192 0.120052 0.439151 0.13714 0.149062 0.069299 0.016028 0.882143 0.348336 1.54165 0.018779 6 5
25 Seeds clpso 24 60 5 0.857143 0.029161 0.857143 0.023809 0.036207 0.469817 0.073254 0.510473 0.12722 0.090955 0.0841 0.011752 0.892857 0.369897 1.537528 0.018265 7 6
26 Seeds constriction 24 60 5 0.890476 0.091597 0.880952 0.095238 0.113731 0.35142 0.3882 0.252366 0.243302 0.482006 0.056642 0.052203 0.960714 0.100819 1.36371 0.053261 3 3
27 Seeds fips 24 60 5 0.885714 0.035315 0.880952 0.02381 0.043849 0.383732 0.103951 0.392303 0.094168 0.12907 0.067541 0.016554 0.9 0.336734 1.815592 0.045394 4 4
28 Seeds inertia 24 60 5 0.914286 0.052164 0.928571 0.023809 0.064769 0.331656 0.342636 0.202104 0.068956 0.425432 0.048999 0.036322 0.963095 0.103132 1.318196 0.015319 1 2
29 Seeds original 24 60 5 0.87619 0.042592 0.880952 0.071429 0.052884 0.49224 0.433653 0.324954 0.104917 0.538443 0.065762 0.028096 0.916667 0.246239 1.385803 0.082957 5 7
30 XOR adaptive_moment 24 80 5 1.0 0.0 1.0 0.0 0.0 0.190431 0.178122 0.197448 0.188515 0.221164 0.04504 0.056878 1.0 0.190431 1.785274 0.092073 5 5
31 XOR bare_bones 24 80 5 1.0 0.0 1.0 0.0 0.0 0.0821 0.056963 0.081613 0.055056 0.070728 0.010907 0.010338 1.0 0.0821 2.031221 0.13707 4 4
32 XOR clpso 24 80 5 0.6 0.136931 0.5 0.25 0.170019 0.548166 0.031946 0.55628 0.024237 0.039666 0.183052 0.012602 0.6 0.548166 2.295572 0.260868 7 7
33 XOR constriction 24 80 5 1.0 0.0 1.0 0.0 0.0 0.004086 0.002357 0.005281 0.001585 0.002927 3.2e-05 1.9e-05 1.0 0.004086 1.987776 0.182034 1 1
34 XOR fips 24 80 5 0.75 0.25 0.75 0.5 0.310411 0.546355 0.059307 0.568609 0.092147 0.073638 0.178887 0.029466 0.75 0.546355 2.502509 0.055447 6 6
35 XOR inertia 24 80 5 1.0 0.0 1.0 0.0 0.0 0.004431 0.003308 0.003801 0.001559 0.004108 3.6e-05 3.1e-05 1.0 0.004431 1.969097 0.170892 2 2
36 XOR original 24 80 5 1.0 0.0 1.0 0.0 0.0 0.004558 0.002672 0.004584 0.001454 0.003318 4.2e-05 2.8e-05 1.0 0.004558 1.905231 0.246635 3 3
@@ -0,0 +1,41 @@
method,candidate_label,regimen,n_particles,epochs,particle_epochs,seed,train_loss,train_acc,test_loss,test_acc,test_mse,fit_time_sec,data_fingerprint,model_fingerprint,device
adaptive_moment,am_b0.06_s0.5,fixed_epoch,30,80,2400,71,1.0238605737686157,0.6834999918937683,1.2778329849243164,0.5789999961853027,0.05719960853457451,2.591740084113553,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,30,80,2400,72,1.0670214891433716,0.6604999899864197,1.199388861656189,0.6060000061988831,0.052410222589969635,2.7087553329765797,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,30,80,2400,73,0.9569582939147949,0.7014999985694885,1.1562552452087402,0.6389999985694885,0.051246583461761475,2.6669440830592066,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,30,80,2400,74,0.8988860845565796,0.718999981880188,1.0667707920074463,0.6589999794960022,0.047151338309049606,2.6316912909969687,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,30,80,2400,75,0.9674410820007324,0.7139999866485596,1.2670954465866089,0.6230000257492065,0.052300941199064255,2.8266918330918998,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,60,80,4800,71,0.8341852426528931,0.7419999837875366,1.021918535232544,0.6729999780654907,0.0449918694794178,6.297774499980733,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,60,80,4800,72,0.8334679007530212,0.7580000162124634,1.1486669778823853,0.6499999761581421,0.048473432660102844,5.944013542030007,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,60,80,4800,73,0.7845665216445923,0.7599999904632568,1.059795618057251,0.6589999794960022,0.04623967781662941,5.409191000042483,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,60,80,4800,74,0.8425021767616272,0.7400000095367432,1.0703872442245483,0.6610000133514404,0.046978432685136795,5.092122667003423,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,60,80,4800,75,0.7727756500244141,0.753000020980835,1.0723776817321777,0.6539999842643738,0.04744390770792961,5.18646250013262,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,90,80,7200,71,0.6898615956306458,0.7910000085830688,0.8688502311706543,0.7310000061988831,0.03894031420350075,8.216789624886587,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,90,80,7200,72,0.7159585952758789,0.781499981880188,0.9239839911460876,0.718999981880188,0.040356870740652084,8.96910895803012,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,90,80,7200,73,0.738656759262085,0.7760000228881836,0.9498289227485657,0.6959999799728394,0.04225980117917061,9.117825584020466,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,90,80,7200,74,0.7267816066741943,0.7770000100135803,0.954045295715332,0.6769999861717224,0.04218713194131851,8.722332582809031,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,90,80,7200,75,0.7104133367538452,0.7889999747276306,0.962693989276886,0.6930000185966492,0.04196783900260925,8.110321166925132,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,120,80,9600,71,0.6516683101654053,0.7950000166893005,0.8584634065628052,0.7360000014305115,0.037673790007829666,11.962705624988303,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,120,80,9600,72,0.6982801556587219,0.7914999723434448,0.9025362133979797,0.7239999771118164,0.038956169039011,11.906837583053857,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,120,80,9600,73,0.7527623772621155,0.7749999761581421,1.001193642616272,0.6919999718666077,0.04309915751218796,10.664651792030782,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,120,80,9600,74,0.6858600974082947,0.784500002861023,0.8601324558258057,0.7350000143051147,0.03845023736357689,10.921957665821537,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_epoch,120,80,9600,75,0.6326538324356079,0.8125,0.889915406703949,0.7300000190734863,0.03931796923279762,10.411899874918163,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,30,80,2400,71,1.0238605737686157,0.6834999918937683,1.2778329849243164,0.5789999961853027,0.05719960853457451,2.591740084113553,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,30,80,2400,72,1.0670214891433716,0.6604999899864197,1.199388861656189,0.6060000061988831,0.052410222589969635,2.7087553329765797,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,30,80,2400,73,0.9569582939147949,0.7014999985694885,1.1562552452087402,0.6389999985694885,0.051246583461761475,2.6669440830592066,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,30,80,2400,74,0.8988860845565796,0.718999981880188,1.0667707920074463,0.6589999794960022,0.047151338309049606,2.6316912909969687,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,30,80,2400,75,0.9674410820007324,0.7139999866485596,1.2670954465866089,0.6230000257492065,0.052300941199064255,2.8266918330918998,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,60,40,2400,71,1.3702143430709839,0.5690000057220459,1.5396113395690918,0.49000000953674316,0.06583584100008011,2.6427467500325292,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,60,40,2400,72,1.3834563493728638,0.5490000247955322,1.564630150794983,0.5270000100135803,0.06432151794433594,2.897082625189796,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,60,40,2400,73,1.239696979522705,0.6129999756813049,1.4340611696243286,0.5109999775886536,0.06299576163291931,2.8362932079471648,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,60,40,2400,74,1.3051151037216187,0.593500018119812,1.4914799928665161,0.5370000004768372,0.06303515285253525,2.7601085831411183,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,60,40,2400,75,1.23734712600708,0.6150000095367432,1.6433888673782349,0.47200000286102295,0.07071221619844437,2.791566374944523,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,90,27,2430,71,1.454797387123108,0.5099999904632568,1.7139235734939575,0.40400001406669617,0.07408219575881958,3.001450875075534,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,90,27,2430,72,1.436651349067688,0.5364999771118164,1.525922417640686,0.5180000066757202,0.06329239904880524,2.9281624578870833,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,90,27,2430,73,1.5700972080230713,0.4805000126361847,1.7064924240112305,0.4180000126361847,0.07409033179283142,3.446030291961506,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,90,27,2430,74,1.4872608184814453,0.5425000190734863,1.6065630912780762,0.5059999823570251,0.06642712652683258,3.13685941696167,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,90,27,2430,75,1.4402897357940674,0.5699999928474426,1.5009552240371704,0.5450000166893005,0.06177634745836258,3.0350140419322997,dfe645918ece54c0,0966039f5ef7af88,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,120,20,2400,71,1.5967158079147339,0.4830000102519989,1.7100492715835571,0.43799999356269836,0.07075551897287369,3.14462749985978,dfe645918ece54c0,0777bd52fd76272d,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,120,20,2400,72,1.673862338066101,0.46149998903274536,1.8141505718231201,0.4099999964237213,0.07267315685749054,3.102293625008315,dfe645918ece54c0,6fcb6e473bdacbd2,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,120,20,2400,73,1.7135908603668213,0.4449999928474426,1.8838708400726318,0.375,0.0778227150440216,2.7550693340599537,dfe645918ece54c0,2e6c351372592f10,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,120,20,2400,74,1.5981744527816772,0.49950000643730164,1.7434691190719604,0.42399999499320984,0.07240629941225052,2.9445771670434624,dfe645918ece54c0,4000fe3fb26ef207,mps
adaptive_moment,am_b0.06_s0.5,fixed_budget,120,20,2400,75,1.635738730430603,0.46950000524520874,1.8486356735229492,0.4059999883174896,0.07580890506505966,3.1960245410446078,dfe645918ece54c0,0966039f5ef7af88,mps
1 method candidate_label regimen n_particles epochs particle_epochs seed train_loss train_acc test_loss test_acc test_mse fit_time_sec data_fingerprint model_fingerprint device
2 adaptive_moment am_b0.06_s0.5 fixed_epoch 30 80 2400 71 1.0238605737686157 0.6834999918937683 1.2778329849243164 0.5789999961853027 0.05719960853457451 2.591740084113553 dfe645918ece54c0 0777bd52fd76272d mps
3 adaptive_moment am_b0.06_s0.5 fixed_epoch 30 80 2400 72 1.0670214891433716 0.6604999899864197 1.199388861656189 0.6060000061988831 0.052410222589969635 2.7087553329765797 dfe645918ece54c0 6fcb6e473bdacbd2 mps
4 adaptive_moment am_b0.06_s0.5 fixed_epoch 30 80 2400 73 0.9569582939147949 0.7014999985694885 1.1562552452087402 0.6389999985694885 0.051246583461761475 2.6669440830592066 dfe645918ece54c0 2e6c351372592f10 mps
5 adaptive_moment am_b0.06_s0.5 fixed_epoch 30 80 2400 74 0.8988860845565796 0.718999981880188 1.0667707920074463 0.6589999794960022 0.047151338309049606 2.6316912909969687 dfe645918ece54c0 4000fe3fb26ef207 mps
6 adaptive_moment am_b0.06_s0.5 fixed_epoch 30 80 2400 75 0.9674410820007324 0.7139999866485596 1.2670954465866089 0.6230000257492065 0.052300941199064255 2.8266918330918998 dfe645918ece54c0 0966039f5ef7af88 mps
7 adaptive_moment am_b0.06_s0.5 fixed_epoch 60 80 4800 71 0.8341852426528931 0.7419999837875366 1.021918535232544 0.6729999780654907 0.0449918694794178 6.297774499980733 dfe645918ece54c0 0777bd52fd76272d mps
8 adaptive_moment am_b0.06_s0.5 fixed_epoch 60 80 4800 72 0.8334679007530212 0.7580000162124634 1.1486669778823853 0.6499999761581421 0.048473432660102844 5.944013542030007 dfe645918ece54c0 6fcb6e473bdacbd2 mps
9 adaptive_moment am_b0.06_s0.5 fixed_epoch 60 80 4800 73 0.7845665216445923 0.7599999904632568 1.059795618057251 0.6589999794960022 0.04623967781662941 5.409191000042483 dfe645918ece54c0 2e6c351372592f10 mps
10 adaptive_moment am_b0.06_s0.5 fixed_epoch 60 80 4800 74 0.8425021767616272 0.7400000095367432 1.0703872442245483 0.6610000133514404 0.046978432685136795 5.092122667003423 dfe645918ece54c0 4000fe3fb26ef207 mps
11 adaptive_moment am_b0.06_s0.5 fixed_epoch 60 80 4800 75 0.7727756500244141 0.753000020980835 1.0723776817321777 0.6539999842643738 0.04744390770792961 5.18646250013262 dfe645918ece54c0 0966039f5ef7af88 mps
12 adaptive_moment am_b0.06_s0.5 fixed_epoch 90 80 7200 71 0.6898615956306458 0.7910000085830688 0.8688502311706543 0.7310000061988831 0.03894031420350075 8.216789624886587 dfe645918ece54c0 0777bd52fd76272d mps
13 adaptive_moment am_b0.06_s0.5 fixed_epoch 90 80 7200 72 0.7159585952758789 0.781499981880188 0.9239839911460876 0.718999981880188 0.040356870740652084 8.96910895803012 dfe645918ece54c0 6fcb6e473bdacbd2 mps
14 adaptive_moment am_b0.06_s0.5 fixed_epoch 90 80 7200 73 0.738656759262085 0.7760000228881836 0.9498289227485657 0.6959999799728394 0.04225980117917061 9.117825584020466 dfe645918ece54c0 2e6c351372592f10 mps
15 adaptive_moment am_b0.06_s0.5 fixed_epoch 90 80 7200 74 0.7267816066741943 0.7770000100135803 0.954045295715332 0.6769999861717224 0.04218713194131851 8.722332582809031 dfe645918ece54c0 4000fe3fb26ef207 mps
16 adaptive_moment am_b0.06_s0.5 fixed_epoch 90 80 7200 75 0.7104133367538452 0.7889999747276306 0.962693989276886 0.6930000185966492 0.04196783900260925 8.110321166925132 dfe645918ece54c0 0966039f5ef7af88 mps
17 adaptive_moment am_b0.06_s0.5 fixed_epoch 120 80 9600 71 0.6516683101654053 0.7950000166893005 0.8584634065628052 0.7360000014305115 0.037673790007829666 11.962705624988303 dfe645918ece54c0 0777bd52fd76272d mps
18 adaptive_moment am_b0.06_s0.5 fixed_epoch 120 80 9600 72 0.6982801556587219 0.7914999723434448 0.9025362133979797 0.7239999771118164 0.038956169039011 11.906837583053857 dfe645918ece54c0 6fcb6e473bdacbd2 mps
19 adaptive_moment am_b0.06_s0.5 fixed_epoch 120 80 9600 73 0.7527623772621155 0.7749999761581421 1.001193642616272 0.6919999718666077 0.04309915751218796 10.664651792030782 dfe645918ece54c0 2e6c351372592f10 mps
20 adaptive_moment am_b0.06_s0.5 fixed_epoch 120 80 9600 74 0.6858600974082947 0.784500002861023 0.8601324558258057 0.7350000143051147 0.03845023736357689 10.921957665821537 dfe645918ece54c0 4000fe3fb26ef207 mps
21 adaptive_moment am_b0.06_s0.5 fixed_epoch 120 80 9600 75 0.6326538324356079 0.8125 0.889915406703949 0.7300000190734863 0.03931796923279762 10.411899874918163 dfe645918ece54c0 0966039f5ef7af88 mps
22 adaptive_moment am_b0.06_s0.5 fixed_budget 30 80 2400 71 1.0238605737686157 0.6834999918937683 1.2778329849243164 0.5789999961853027 0.05719960853457451 2.591740084113553 dfe645918ece54c0 0777bd52fd76272d mps
23 adaptive_moment am_b0.06_s0.5 fixed_budget 30 80 2400 72 1.0670214891433716 0.6604999899864197 1.199388861656189 0.6060000061988831 0.052410222589969635 2.7087553329765797 dfe645918ece54c0 6fcb6e473bdacbd2 mps
24 adaptive_moment am_b0.06_s0.5 fixed_budget 30 80 2400 73 0.9569582939147949 0.7014999985694885 1.1562552452087402 0.6389999985694885 0.051246583461761475 2.6669440830592066 dfe645918ece54c0 2e6c351372592f10 mps
25 adaptive_moment am_b0.06_s0.5 fixed_budget 30 80 2400 74 0.8988860845565796 0.718999981880188 1.0667707920074463 0.6589999794960022 0.047151338309049606 2.6316912909969687 dfe645918ece54c0 4000fe3fb26ef207 mps
26 adaptive_moment am_b0.06_s0.5 fixed_budget 30 80 2400 75 0.9674410820007324 0.7139999866485596 1.2670954465866089 0.6230000257492065 0.052300941199064255 2.8266918330918998 dfe645918ece54c0 0966039f5ef7af88 mps
27 adaptive_moment am_b0.06_s0.5 fixed_budget 60 40 2400 71 1.3702143430709839 0.5690000057220459 1.5396113395690918 0.49000000953674316 0.06583584100008011 2.6427467500325292 dfe645918ece54c0 0777bd52fd76272d mps
28 adaptive_moment am_b0.06_s0.5 fixed_budget 60 40 2400 72 1.3834563493728638 0.5490000247955322 1.564630150794983 0.5270000100135803 0.06432151794433594 2.897082625189796 dfe645918ece54c0 6fcb6e473bdacbd2 mps
29 adaptive_moment am_b0.06_s0.5 fixed_budget 60 40 2400 73 1.239696979522705 0.6129999756813049 1.4340611696243286 0.5109999775886536 0.06299576163291931 2.8362932079471648 dfe645918ece54c0 2e6c351372592f10 mps
30 adaptive_moment am_b0.06_s0.5 fixed_budget 60 40 2400 74 1.3051151037216187 0.593500018119812 1.4914799928665161 0.5370000004768372 0.06303515285253525 2.7601085831411183 dfe645918ece54c0 4000fe3fb26ef207 mps
31 adaptive_moment am_b0.06_s0.5 fixed_budget 60 40 2400 75 1.23734712600708 0.6150000095367432 1.6433888673782349 0.47200000286102295 0.07071221619844437 2.791566374944523 dfe645918ece54c0 0966039f5ef7af88 mps
32 adaptive_moment am_b0.06_s0.5 fixed_budget 90 27 2430 71 1.454797387123108 0.5099999904632568 1.7139235734939575 0.40400001406669617 0.07408219575881958 3.001450875075534 dfe645918ece54c0 0777bd52fd76272d mps
33 adaptive_moment am_b0.06_s0.5 fixed_budget 90 27 2430 72 1.436651349067688 0.5364999771118164 1.525922417640686 0.5180000066757202 0.06329239904880524 2.9281624578870833 dfe645918ece54c0 6fcb6e473bdacbd2 mps
34 adaptive_moment am_b0.06_s0.5 fixed_budget 90 27 2430 73 1.5700972080230713 0.4805000126361847 1.7064924240112305 0.4180000126361847 0.07409033179283142 3.446030291961506 dfe645918ece54c0 2e6c351372592f10 mps
35 adaptive_moment am_b0.06_s0.5 fixed_budget 90 27 2430 74 1.4872608184814453 0.5425000190734863 1.6065630912780762 0.5059999823570251 0.06642712652683258 3.13685941696167 dfe645918ece54c0 4000fe3fb26ef207 mps
36 adaptive_moment am_b0.06_s0.5 fixed_budget 90 27 2430 75 1.4402897357940674 0.5699999928474426 1.5009552240371704 0.5450000166893005 0.06177634745836258 3.0350140419322997 dfe645918ece54c0 0966039f5ef7af88 mps
37 adaptive_moment am_b0.06_s0.5 fixed_budget 120 20 2400 71 1.5967158079147339 0.4830000102519989 1.7100492715835571 0.43799999356269836 0.07075551897287369 3.14462749985978 dfe645918ece54c0 0777bd52fd76272d mps
38 adaptive_moment am_b0.06_s0.5 fixed_budget 120 20 2400 72 1.673862338066101 0.46149998903274536 1.8141505718231201 0.4099999964237213 0.07267315685749054 3.102293625008315 dfe645918ece54c0 6fcb6e473bdacbd2 mps
39 adaptive_moment am_b0.06_s0.5 fixed_budget 120 20 2400 73 1.7135908603668213 0.4449999928474426 1.8838708400726318 0.375 0.0778227150440216 2.7550693340599537 dfe645918ece54c0 2e6c351372592f10 mps
40 adaptive_moment am_b0.06_s0.5 fixed_budget 120 20 2400 74 1.5981744527816772 0.49950000643730164 1.7434691190719604 0.42399999499320984 0.07240629941225052 2.9445771670434624 dfe645918ece54c0 4000fe3fb26ef207 mps
41 adaptive_moment am_b0.06_s0.5 fixed_budget 120 20 2400 75 1.635738730430603 0.46950000524520874 1.8486356735229492 0.4059999883174896 0.07580890506505966 3.1960245410446078 dfe645918ece54c0 0966039f5ef7af88 mps
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
method,candidate_label,seed,n_particles,epochs,particle_epochs,train_loss,train_acc,test_loss,test_acc,test_mse,fit_time_sec,data_fingerprint,model_fingerprint,device
adaptive_moment,am_b0.06_s0.5,61,30,80,2400,1.0297304391860962,0.6644999980926514,1.2476433515548706,0.5960000157356262,0.054337989538908005,3.3018275420181453,dfe645918ece54c0,2a1760dc9ec95c0a,mps
adaptive_moment,am_b0.06_s0.5,62,30,80,2400,1.0248669385910034,0.6800000071525574,1.2172735929489136,0.6150000095367432,0.05301284044981003,3.082969541894272,dfe645918ece54c0,8722b0d78b78d382,mps
adaptive_moment,am_b0.06_s0.5,63,30,80,2400,1.0421160459518433,0.6690000295639038,1.2344101667404175,0.6190000176429749,0.052518151700496674,2.7472599998582155,dfe645918ece54c0,f9f62f1942f9151e,mps
adaptive_moment,am_b0.06_s0.5,64,30,80,2400,0.9773346185684204,0.7059999704360962,1.1978241205215454,0.6150000095367432,0.052042748779058456,2.670909541891888,dfe645918ece54c0,4cd40d38555ee5ce,mps
adaptive_moment,am_b0.06_s0.5,65,30,80,2400,1.0047204494476318,0.6959999799728394,1.2509828805923462,0.6079999804496765,0.05341806635260582,2.9130489169619977,dfe645918ece54c0,70f99d26e1303b76,mps
inertia,inertia_asymmetric,61,30,80,2400,1.0274338722229004,0.6735000014305115,1.2075469493865967,0.6320000290870667,0.051997773349285126,2.564156916923821,dfe645918ece54c0,2a1760dc9ec95c0a,mps
inertia,inertia_asymmetric,62,30,80,2400,0.9986715912818909,0.6855000257492065,1.2740414142608643,0.578000009059906,0.056466199457645416,2.5913295838981867,dfe645918ece54c0,8722b0d78b78d382,mps
inertia,inertia_asymmetric,63,30,80,2400,0.8828525543212891,0.7315000295639038,1.0797629356384277,0.6620000004768372,0.04682194069027901,2.6188287080731243,dfe645918ece54c0,f9f62f1942f9151e,mps
inertia,inertia_asymmetric,64,30,80,2400,0.9942940473556519,0.6865000128746033,1.2426005601882935,0.621999979019165,0.05173056200146675,2.534188667079434,dfe645918ece54c0,4cd40d38555ee5ce,mps
inertia,inertia_asymmetric,65,30,80,2400,1.0921698808670044,0.6729999780654907,1.2609055042266846,0.6209999918937683,0.052379049360752106,2.542070833966136,dfe645918ece54c0,70f99d26e1303b76,mps
constriction,constriction_c205_canonical,61,30,80,2400,1.0248574018478394,0.6919999718666077,1.2035431861877441,0.6299999952316284,0.05152203515172005,2.5165979999583215,dfe645918ece54c0,2a1760dc9ec95c0a,mps
constriction,constriction_c205_canonical,62,30,80,2400,1.1310851573944092,0.6700000166893005,1.3196481466293335,0.5849999785423279,0.055950723588466644,2.481917541939765,dfe645918ece54c0,8722b0d78b78d382,mps
constriction,constriction_c205_canonical,63,30,80,2400,1.0529502630233765,0.6644999980926514,1.2498247623443604,0.5789999961853027,0.05548400804400444,2.593008500058204,dfe645918ece54c0,f9f62f1942f9151e,mps
constriction,constriction_c205_canonical,64,30,80,2400,0.9444444179534912,0.7145000100135803,1.2384077310562134,0.6179999709129333,0.0526747927069664,2.4862103750929236,dfe645918ece54c0,4cd40d38555ee5ce,mps
constriction,constriction_c205_canonical,65,30,80,2400,0.9420643448829651,0.7105000019073486,1.219950795173645,0.6209999918937683,0.051569391041994095,2.5256920421961695,dfe645918ece54c0,70f99d26e1303b76,mps
local_best,local_best_r4_constant,61,30,80,2400,1.0233083963394165,0.703499972820282,1.1751943826675415,0.6549999713897705,0.050412945449352264,2.535716458922252,dfe645918ece54c0,2a1760dc9ec95c0a,mps
local_best,local_best_r4_constant,62,30,80,2400,1.0379226207733154,0.6970000267028809,1.2656233310699463,0.593999981880188,0.056342579424381256,2.5843862500041723,dfe645918ece54c0,8722b0d78b78d382,mps
local_best,local_best_r4_constant,63,30,80,2400,0.9664440751075745,0.7275000214576721,1.167758584022522,0.6570000052452087,0.049540925770998,2.485073500080034,dfe645918ece54c0,f9f62f1942f9151e,mps
local_best,local_best_r4_constant,64,30,80,2400,1.0115971565246582,0.703000009059906,1.1620168685913086,0.625,0.052118122577667236,2.467889874940738,dfe645918ece54c0,4cd40d38555ee5ce,mps
local_best,local_best_r4_constant,65,30,80,2400,1.0574767589569092,0.6959999799728394,1.286249041557312,0.6010000109672546,0.05573433265089989,2.488020292017609,dfe645918ece54c0,70f99d26e1303b76,mps
quantum,quantum_beta_0.4_0.9,61,30,80,2400,1.4583405256271362,0.5460000038146973,1.464557409286499,0.5320000052452087,0.06282258033752441,2.3820620418991894,dfe645918ece54c0,2a1760dc9ec95c0a,mps
quantum,quantum_beta_0.4_0.9,62,30,80,2400,1.5153175592422485,0.5084999799728394,1.5834708213806152,0.4650000035762787,0.0668596550822258,2.4394417498260736,dfe645918ece54c0,8722b0d78b78d382,mps
quantum,quantum_beta_0.4_0.9,63,30,80,2400,1.4202407598495483,0.5490000247955322,1.5065674781799316,0.4860000014305115,0.06405868381261826,2.3948492500931025,dfe645918ece54c0,f9f62f1942f9151e,mps
quantum,quantum_beta_0.4_0.9,64,30,80,2400,1.3938108682632446,0.5199999809265137,1.4775264263153076,0.46799999475479126,0.06637361645698547,2.3948124169837683,dfe645918ece54c0,4cd40d38555ee5ce,mps
quantum,quantum_beta_0.4_0.9,65,30,80,2400,1.3520328998565674,0.5724999904632568,1.5641074180603027,0.4779999852180481,0.06454122811555862,2.402772541856393,dfe645918ece54c0,70f99d26e1303b76,mps
1 method candidate_label seed n_particles epochs particle_epochs train_loss train_acc test_loss test_acc test_mse fit_time_sec data_fingerprint model_fingerprint device
2 adaptive_moment am_b0.06_s0.5 61 30 80 2400 1.0297304391860962 0.6644999980926514 1.2476433515548706 0.5960000157356262 0.054337989538908005 3.3018275420181453 dfe645918ece54c0 2a1760dc9ec95c0a mps
3 adaptive_moment am_b0.06_s0.5 62 30 80 2400 1.0248669385910034 0.6800000071525574 1.2172735929489136 0.6150000095367432 0.05301284044981003 3.082969541894272 dfe645918ece54c0 8722b0d78b78d382 mps
4 adaptive_moment am_b0.06_s0.5 63 30 80 2400 1.0421160459518433 0.6690000295639038 1.2344101667404175 0.6190000176429749 0.052518151700496674 2.7472599998582155 dfe645918ece54c0 f9f62f1942f9151e mps
5 adaptive_moment am_b0.06_s0.5 64 30 80 2400 0.9773346185684204 0.7059999704360962 1.1978241205215454 0.6150000095367432 0.052042748779058456 2.670909541891888 dfe645918ece54c0 4cd40d38555ee5ce mps
6 adaptive_moment am_b0.06_s0.5 65 30 80 2400 1.0047204494476318 0.6959999799728394 1.2509828805923462 0.6079999804496765 0.05341806635260582 2.9130489169619977 dfe645918ece54c0 70f99d26e1303b76 mps
7 inertia inertia_asymmetric 61 30 80 2400 1.0274338722229004 0.6735000014305115 1.2075469493865967 0.6320000290870667 0.051997773349285126 2.564156916923821 dfe645918ece54c0 2a1760dc9ec95c0a mps
8 inertia inertia_asymmetric 62 30 80 2400 0.9986715912818909 0.6855000257492065 1.2740414142608643 0.578000009059906 0.056466199457645416 2.5913295838981867 dfe645918ece54c0 8722b0d78b78d382 mps
9 inertia inertia_asymmetric 63 30 80 2400 0.8828525543212891 0.7315000295639038 1.0797629356384277 0.6620000004768372 0.04682194069027901 2.6188287080731243 dfe645918ece54c0 f9f62f1942f9151e mps
10 inertia inertia_asymmetric 64 30 80 2400 0.9942940473556519 0.6865000128746033 1.2426005601882935 0.621999979019165 0.05173056200146675 2.534188667079434 dfe645918ece54c0 4cd40d38555ee5ce mps
11 inertia inertia_asymmetric 65 30 80 2400 1.0921698808670044 0.6729999780654907 1.2609055042266846 0.6209999918937683 0.052379049360752106 2.542070833966136 dfe645918ece54c0 70f99d26e1303b76 mps
12 constriction constriction_c205_canonical 61 30 80 2400 1.0248574018478394 0.6919999718666077 1.2035431861877441 0.6299999952316284 0.05152203515172005 2.5165979999583215 dfe645918ece54c0 2a1760dc9ec95c0a mps
13 constriction constriction_c205_canonical 62 30 80 2400 1.1310851573944092 0.6700000166893005 1.3196481466293335 0.5849999785423279 0.055950723588466644 2.481917541939765 dfe645918ece54c0 8722b0d78b78d382 mps
14 constriction constriction_c205_canonical 63 30 80 2400 1.0529502630233765 0.6644999980926514 1.2498247623443604 0.5789999961853027 0.05548400804400444 2.593008500058204 dfe645918ece54c0 f9f62f1942f9151e mps
15 constriction constriction_c205_canonical 64 30 80 2400 0.9444444179534912 0.7145000100135803 1.2384077310562134 0.6179999709129333 0.0526747927069664 2.4862103750929236 dfe645918ece54c0 4cd40d38555ee5ce mps
16 constriction constriction_c205_canonical 65 30 80 2400 0.9420643448829651 0.7105000019073486 1.219950795173645 0.6209999918937683 0.051569391041994095 2.5256920421961695 dfe645918ece54c0 70f99d26e1303b76 mps
17 local_best local_best_r4_constant 61 30 80 2400 1.0233083963394165 0.703499972820282 1.1751943826675415 0.6549999713897705 0.050412945449352264 2.535716458922252 dfe645918ece54c0 2a1760dc9ec95c0a mps
18 local_best local_best_r4_constant 62 30 80 2400 1.0379226207733154 0.6970000267028809 1.2656233310699463 0.593999981880188 0.056342579424381256 2.5843862500041723 dfe645918ece54c0 8722b0d78b78d382 mps
19 local_best local_best_r4_constant 63 30 80 2400 0.9664440751075745 0.7275000214576721 1.167758584022522 0.6570000052452087 0.049540925770998 2.485073500080034 dfe645918ece54c0 f9f62f1942f9151e mps
20 local_best local_best_r4_constant 64 30 80 2400 1.0115971565246582 0.703000009059906 1.1620168685913086 0.625 0.052118122577667236 2.467889874940738 dfe645918ece54c0 4cd40d38555ee5ce mps
21 local_best local_best_r4_constant 65 30 80 2400 1.0574767589569092 0.6959999799728394 1.286249041557312 0.6010000109672546 0.05573433265089989 2.488020292017609 dfe645918ece54c0 70f99d26e1303b76 mps
22 quantum quantum_beta_0.4_0.9 61 30 80 2400 1.4583405256271362 0.5460000038146973 1.464557409286499 0.5320000052452087 0.06282258033752441 2.3820620418991894 dfe645918ece54c0 2a1760dc9ec95c0a mps
23 quantum quantum_beta_0.4_0.9 62 30 80 2400 1.5153175592422485 0.5084999799728394 1.5834708213806152 0.4650000035762787 0.0668596550822258 2.4394417498260736 dfe645918ece54c0 8722b0d78b78d382 mps
24 quantum quantum_beta_0.4_0.9 63 30 80 2400 1.4202407598495483 0.5490000247955322 1.5065674781799316 0.4860000014305115 0.06405868381261826 2.3948492500931025 dfe645918ece54c0 f9f62f1942f9151e mps
25 quantum quantum_beta_0.4_0.9 64 30 80 2400 1.3938108682632446 0.5199999809265137 1.4775264263153076 0.46799999475479126 0.06637361645698547 2.3948124169837683 dfe645918ece54c0 4cd40d38555ee5ce mps
26 quantum quantum_beta_0.4_0.9 65 30 80 2400 1.3520328998565674 0.5724999904632568 1.5641074180603027 0.4779999852180481 0.06454122811555862 2.402772541856393 dfe645918ece54c0 70f99d26e1303b76 mps
@@ -0,0 +1,97 @@
method,candidate_label,seed,n_particles,epochs,particle_epochs,train_loss,train_acc,val_loss,val_acc,val_mse,fit_time_sec,data_fingerprint,model_fingerprint,device
adaptive_moment,am_b0.03_s0.5,51,30,80,2400,1.112807035446167,0.6570000052452087,1.0964455604553223,0.6633333563804626,0.046677425503730774,2.5230953749269247,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.03_s0.5,52,30,80,2400,0.995387077331543,0.6869999766349792,1.0115970373153687,0.6816666722297668,0.04280940815806389,2.5447300830855966,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.03_s0.5,53,30,80,2400,0.9480092525482178,0.7080000042915344,0.9940392971038818,0.6883333325386047,0.0421285405755043,2.493663167115301,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.03_s1.0,51,30,80,2400,0.9984938502311707,0.7024999856948853,1.0240328311920166,0.6850000023841858,0.04404553398489952,2.54264641716145,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.03_s1.0,52,30,80,2400,1.0163198709487915,0.6890000104904175,0.9720749855041504,0.6933333277702332,0.042471032589673996,2.491340707987547,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.03_s1.0,53,30,80,2400,1.0211265087127686,0.6884999871253967,1.081996202468872,0.6850000023841858,0.04483034089207649,2.633713499875739,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.03_s1.5,51,30,80,2400,1.0612047910690308,0.6735000014305115,1.030881404876709,0.6850000023841858,0.04405777156352997,2.8367540831677616,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.03_s1.5,52,30,80,2400,0.9462777972221375,0.7149999737739563,0.8990310430526733,0.7266666889190674,0.03923330828547478,2.5477614579722285,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.03_s1.5,53,30,80,2400,0.9608960747718811,0.7120000123977661,1.0034033060073853,0.6833333373069763,0.042279649525880814,2.5643632498104125,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.06_s0.5,51,30,80,2400,1.068866491317749,0.6855000257492065,1.0220483541488647,0.675000011920929,0.04446922242641449,2.6285997920203954,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.06_s0.5,52,30,80,2400,0.9650915265083313,0.7080000042915344,0.9152445197105408,0.7083333134651184,0.04066821560263634,2.7918378338217735,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.06_s0.5,53,30,80,2400,0.9414446949958801,0.7014999985694885,0.9156243801116943,0.721666693687439,0.03982962667942047,2.729898874880746,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.06_s1.0,51,30,80,2400,1.0662047863006592,0.6754999756813049,0.9721739888191223,0.6899999976158142,0.042344048619270325,2.5812953328713775,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.06_s1.0,52,30,80,2400,0.9808334112167358,0.6924999952316284,0.9488086104393005,0.6933333277702332,0.04174558073282242,2.8018474159762263,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.06_s1.0,53,30,80,2400,0.9464473724365234,0.7085000276565552,0.9320653080940247,0.7083333134651184,0.040938157588243484,2.7171109160408378,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.06_s1.5,51,30,80,2400,1.1136488914489746,0.6549999713897705,1.1235008239746094,0.6650000214576721,0.04781530797481537,2.7500752080231905,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.06_s1.5,52,30,80,2400,1.0324392318725586,0.6825000047683716,1.0213521718978882,0.6833333373069763,0.04513593390583992,3.103288209065795,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.06_s1.5,53,30,80,2400,1.0860146284103394,0.6589999794960022,1.0920277833938599,0.6499999761581421,0.04753243178129196,2.862214791122824,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.1_s0.5,51,30,80,2400,1.0369712114334106,0.6644999980926514,1.0877516269683838,0.6766666769981384,0.04590357467532158,2.8528131251223385,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.1_s0.5,52,30,80,2400,0.9936539530754089,0.6965000033378601,0.9451570510864258,0.6983333230018616,0.040929101407527924,2.7851123749278486,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.1_s0.5,53,30,80,2400,0.9365023374557495,0.7014999985694885,0.8658249974250793,0.7283333539962769,0.038747914135456085,2.7191387079656124,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.1_s1.0,51,30,80,2400,1.0722532272338867,0.6995000243186951,0.9926102757453918,0.7016666531562805,0.04247405380010605,2.8380547501146793,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.1_s1.0,52,30,80,2400,0.9977437853813171,0.6930000185966492,1.009635329246521,0.6966666579246521,0.04288069158792496,2.905045999912545,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.1_s1.0,53,30,80,2400,1.0425642728805542,0.6664999723434448,1.0561128854751587,0.6783333420753479,0.0449373684823513,2.7804793331306428,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.1_s1.5,51,30,80,2400,1.0099478960037231,0.6775000095367432,1.00997793674469,0.6899999976158142,0.042956266552209854,2.8892802079208195,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.1_s1.5,52,30,80,2400,1.0597009658813477,0.6744999885559082,1.063821792602539,0.6966666579246521,0.044883932918310165,2.669369083130732,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.1_s1.5,53,30,80,2400,0.986152708530426,0.7080000042915344,1.0491483211517334,0.6850000023841858,0.04293783754110336,2.626303083030507,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.15_s0.5,51,30,80,2400,1.1084171533584595,0.652999997138977,1.1322306394577026,0.6600000262260437,0.04928039386868477,2.5766194579191506,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.15_s0.5,52,30,80,2400,0.970059335231781,0.7129999995231628,0.9312149882316589,0.7300000190734863,0.03986109420657158,2.731992583023384,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.15_s0.5,53,30,80,2400,0.9970248341560364,0.6995000243186951,0.9656715989112854,0.70333331823349,0.04226723685860634,3.0274115409702063,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.15_s1.0,51,30,80,2400,1.0099536180496216,0.7009999752044678,0.9564719796180725,0.7166666388511658,0.04047820717096329,2.9793542500119656,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.15_s1.0,52,30,80,2400,1.0434995889663696,0.6819999814033508,0.9963273406028748,0.6949999928474426,0.04349374398589134,2.8151185419410467,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.15_s1.0,53,30,80,2400,1.0348174571990967,0.6840000152587891,1.0127019882202148,0.6866666674613953,0.04363732784986496,3.0412363330833614,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.15_s1.5,51,30,80,2400,1.1573493480682373,0.6299999952316284,1.2029211521148682,0.6133333444595337,0.05152761936187744,3.1714885828550905,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.15_s1.5,52,30,80,2400,1.060623288154602,0.6949999928474426,1.0831577777862549,0.6966666579246521,0.04344262182712555,2.993674041936174,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.15_s1.5,53,30,80,2400,1.0753278732299805,0.6769999861717224,0.9602290987968445,0.6983333230018616,0.0414869599044323,2.7658886671997607,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.06_s1.0_beta0.8,51,30,80,2400,1.1594665050506592,0.6480000019073486,1.1239941120147705,0.6700000166893005,0.047122661024332047,2.764871333958581,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.06_s1.0_beta0.8,52,30,80,2400,0.9945915341377258,0.7064999938011169,1.0324488878250122,0.6683333516120911,0.04436164349317551,2.830462665995583,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.06_s1.0_beta0.8,53,30,80,2400,0.9297089576721191,0.7110000252723694,0.938503623008728,0.70333331823349,0.04101715609431267,2.8122877080459148,bfc8de485755d7f3,00868f788440b8a0,mps
adaptive_moment,am_b0.06_s1.0_beta0.95,51,30,80,2400,1.1143780946731567,0.6514999866485596,1.0093973875045776,0.6800000071525574,0.04380199685692787,2.9217382080387324,bfc8de485755d7f3,82b3214d02e2e634,mps
adaptive_moment,am_b0.06_s1.0_beta0.95,52,30,80,2400,1.0144661664962769,0.6884999871253967,0.9828318357467651,0.6833333373069763,0.04375388100743294,2.777603624854237,bfc8de485755d7f3,ddffbe335bef15ad,mps
adaptive_moment,am_b0.06_s1.0_beta0.95,53,30,80,2400,1.1004270315170288,0.6600000262260437,1.0834088325500488,0.6683333516120911,0.04595582187175751,3.0502532080281526,bfc8de485755d7f3,00868f788440b8a0,mps
inertia,inertia_canonical,51,30,80,2400,1.4416260719299316,0.5649999976158142,1.4125466346740723,0.6000000238418579,0.05729174241423607,3.098805333022028,bfc8de485755d7f3,82b3214d02e2e634,mps
inertia,inertia_canonical,52,30,80,2400,1.665074348449707,0.4894999861717224,1.6294100284576416,0.5066666603088379,0.06497763097286224,2.652766958111897,bfc8de485755d7f3,ddffbe335bef15ad,mps
inertia,inertia_canonical,53,30,80,2400,1.4698249101638794,0.5565000176429749,1.4316622018814087,0.5683333277702332,0.059196680784225464,2.529014667030424,bfc8de485755d7f3,00868f788440b8a0,mps
inertia,inertia_tuned,51,30,80,2400,1.0468239784240723,0.699999988079071,1.0345858335494995,0.6816666722297668,0.04465937986969948,2.626064541982487,bfc8de485755d7f3,82b3214d02e2e634,mps
inertia,inertia_tuned,52,30,80,2400,0.9420707821846008,0.703000009059906,0.9224241375923157,0.7250000238418579,0.040230974555015564,2.5009557919111103,bfc8de485755d7f3,ddffbe335bef15ad,mps
inertia,inertia_tuned,53,30,80,2400,1.0711344480514526,0.6754999756813049,1.0678696632385254,0.6766666769981384,0.04436859115958214,2.4420957090333104,bfc8de485755d7f3,00868f788440b8a0,mps
inertia,inertia_low_w,51,30,80,2400,1.113174319267273,0.6365000009536743,1.0575727224349976,0.6850000023841858,0.0461583249270916,2.484879707917571,bfc8de485755d7f3,82b3214d02e2e634,mps
inertia,inertia_low_w,52,30,80,2400,1.0245047807693481,0.6790000200271606,0.9806669354438782,0.70333331823349,0.042586930096149445,2.643615792039782,bfc8de485755d7f3,ddffbe335bef15ad,mps
inertia,inertia_low_w,53,30,80,2400,1.0131726264953613,0.6859999895095825,0.9656168818473816,0.6983333230018616,0.04216707497835159,2.4834530411753803,bfc8de485755d7f3,00868f788440b8a0,mps
inertia,inertia_w_decay,51,30,80,2400,1.0393675565719604,0.6915000081062317,1.025821566581726,0.6983333230018616,0.04372088611125946,2.426888459129259,bfc8de485755d7f3,82b3214d02e2e634,mps
inertia,inertia_w_decay,52,30,80,2400,1.1047112941741943,0.6600000262260437,1.1661359071731567,0.6700000166893005,0.04753515496850014,2.480164624983445,bfc8de485755d7f3,ddffbe335bef15ad,mps
inertia,inertia_w_decay,53,30,80,2400,0.9673198461532593,0.6909999847412109,0.9870904684066772,0.6816666722297668,0.04321449622511864,2.422041916055605,bfc8de485755d7f3,00868f788440b8a0,mps
inertia,inertia_asymmetric,51,30,80,2400,1.0810083150863647,0.6775000095367432,1.0954482555389404,0.6949999928474426,0.04539918154478073,2.485565959010273,bfc8de485755d7f3,82b3214d02e2e634,mps
inertia,inertia_asymmetric,52,30,80,2400,0.9861184358596802,0.6909999847412109,1.0121054649353027,0.6933333277702332,0.043754804879426956,2.5209125420078635,bfc8de485755d7f3,ddffbe335bef15ad,mps
inertia,inertia_asymmetric,53,30,80,2400,0.9526417851448059,0.722000002861023,0.9798429608345032,0.70333331823349,0.04217696189880371,2.5959840829018503,bfc8de485755d7f3,00868f788440b8a0,mps
constriction,constriction_c201,51,30,80,2400,1.0345860719680786,0.6869999766349792,0.9738073945045471,0.6916666626930237,0.042279358953237534,2.698073250008747,bfc8de485755d7f3,82b3214d02e2e634,mps
constriction,constriction_c201,52,30,80,2400,1.0233638286590576,0.6865000128746033,1.0523117780685425,0.6883333325386047,0.043930795043706894,3.4117308750282973,bfc8de485755d7f3,ddffbe335bef15ad,mps
constriction,constriction_c201,53,30,80,2400,1.0302053689956665,0.6880000233650208,0.9275049567222595,0.7166666388511658,0.040264155715703964,3.1805959579069167,bfc8de485755d7f3,00868f788440b8a0,mps
constriction,constriction_c205_canonical,51,30,80,2400,1.0515944957733154,0.6759999990463257,1.0619410276412964,0.6933333277702332,0.044216156005859375,3.237764042103663,bfc8de485755d7f3,82b3214d02e2e634,mps
constriction,constriction_c205_canonical,52,30,80,2400,1.061699390411377,0.6744999885559082,1.016150712966919,0.6866666674613953,0.0442710816860199,2.7161605830769986,bfc8de485755d7f3,ddffbe335bef15ad,mps
constriction,constriction_c205_canonical,53,30,80,2400,0.9327093362808228,0.7289999723434448,0.8751127123832703,0.746666669845581,0.03667077049612999,2.968678707955405,bfc8de485755d7f3,00868f788440b8a0,mps
constriction,constriction_c205_tuned,51,30,80,2400,1.053567886352539,0.6825000047683716,0.991266667842865,0.699999988079071,0.04196731001138687,3.042221749899909,bfc8de485755d7f3,82b3214d02e2e634,mps
constriction,constriction_c205_tuned,52,30,80,2400,0.9388713836669922,0.7139999866485596,0.9303705096244812,0.7116666436195374,0.0403158962726593,2.8448961251415312,bfc8de485755d7f3,ddffbe335bef15ad,mps
constriction,constriction_c205_tuned,53,30,80,2400,0.9965179562568665,0.6995000243186951,0.9846652150154114,0.7133333086967468,0.04187808558344841,3.1848887908272445,bfc8de485755d7f3,00868f788440b8a0,mps
constriction,constriction_c250,51,30,80,2400,2.2795634269714355,0.18150000274181366,2.250452756881714,0.18666666746139526,0.08861013501882553,2.9961123750545084,bfc8de485755d7f3,82b3214d02e2e634,mps
constriction,constriction_c250,52,30,80,2400,2.2547407150268555,0.20350000262260437,2.2187206745147705,0.2150000035762787,0.08802558481693268,3.024313250090927,bfc8de485755d7f3,ddffbe335bef15ad,mps
constriction,constriction_c250,53,30,80,2400,2.19023060798645,0.19300000369548798,2.178219795227051,0.20499999821186066,0.08775272965431213,2.961929291021079,bfc8de485755d7f3,00868f788440b8a0,mps
constriction,constriction_asymmetric,51,30,80,2400,1.2645200490951538,0.6150000095367432,1.2170491218566895,0.6316666603088379,0.05265685170888901,3.023697500117123,bfc8de485755d7f3,82b3214d02e2e634,mps
constriction,constriction_asymmetric,52,30,80,2400,1.2607256174087524,0.6014999747276306,1.2684078216552734,0.5883333086967468,0.05453150346875191,2.8057189998216927,bfc8de485755d7f3,ddffbe335bef15ad,mps
constriction,constriction_asymmetric,53,30,80,2400,1.2320771217346191,0.6140000224113464,1.2376829385757446,0.6200000047683716,0.05186690762639046,2.709051915910095,bfc8de485755d7f3,00868f788440b8a0,mps
local_best,local_best_r1_constant,51,30,80,2400,1.4435429573059082,0.5354999899864197,1.4319947957992554,0.528333306312561,0.062414027750492096,2.6544057081919163,bfc8de485755d7f3,82b3214d02e2e634,mps
local_best,local_best_r1_constant,52,30,80,2400,1.4914970397949219,0.527999997138977,1.4473850727081299,0.5333333611488342,0.06336859613656998,2.7407990000210702,bfc8de485755d7f3,ddffbe335bef15ad,mps
local_best,local_best_r1_constant,53,30,80,2400,1.4734760522842407,0.5680000185966492,1.5045198202133179,0.5366666913032532,0.06426934152841568,2.693694499786943,bfc8de485755d7f3,00868f788440b8a0,mps
local_best,local_best_r2_constant,51,30,80,2400,1.2284750938415527,0.6345000267028809,1.191932201385498,0.628333330154419,0.0514327734708786,2.4563205409795046,bfc8de485755d7f3,82b3214d02e2e634,mps
local_best,local_best_r2_constant,52,30,80,2400,1.1704559326171875,0.6504999995231628,1.1325551271438599,0.6700000166893005,0.049076274037361145,2.5833707919809967,bfc8de485755d7f3,ddffbe335bef15ad,mps
local_best,local_best_r2_constant,53,30,80,2400,1.243313193321228,0.6320000290870667,1.2125868797302246,0.6516666412353516,0.05214923247694969,2.475700625218451,bfc8de485755d7f3,00868f788440b8a0,mps
local_best,local_best_r4_constant,51,30,80,2400,1.0172255039215088,0.6884999871253967,1.0000126361846924,0.699999988079071,0.0425647497177124,2.4682277080137283,bfc8de485755d7f3,82b3214d02e2e634,mps
local_best,local_best_r4_constant,52,30,80,2400,0.9992414712905884,0.7089999914169312,0.9295963644981384,0.7283333539962769,0.04094172641634941,2.4259307920001447,bfc8de485755d7f3,ddffbe335bef15ad,mps
local_best,local_best_r4_constant,53,30,80,2400,0.9578440189361572,0.7139999866485596,0.9051287770271301,0.7450000047683716,0.03751235082745552,2.4171505419071764,bfc8de485755d7f3,00868f788440b8a0,mps
local_best,local_best_r1_decay,51,30,80,2400,1.4906078577041626,0.5640000104904175,1.4867504835128784,0.5400000214576721,0.06405453383922577,2.455931582953781,bfc8de485755d7f3,82b3214d02e2e634,mps
local_best,local_best_r1_decay,52,30,80,2400,1.4803287982940674,0.5559999942779541,1.4699759483337402,0.5733333230018616,0.0630197748541832,2.5800107079558074,bfc8de485755d7f3,ddffbe335bef15ad,mps
local_best,local_best_r1_decay,53,30,80,2400,1.4953768253326416,0.5370000004768372,1.4344968795776367,0.5416666865348816,0.06267009675502777,2.5192452499177307,bfc8de485755d7f3,00868f788440b8a0,mps
quantum,quantum_beta_0.5_1.0,51,30,80,2400,1.4666624069213867,0.5055000185966492,1.4257338047027588,0.5183333158493042,0.06076965853571892,2.411743083037436,bfc8de485755d7f3,82b3214d02e2e634,mps
quantum,quantum_beta_0.5_1.0,52,30,80,2400,1.4086458683013916,0.5370000004768372,1.3660792112350464,0.5383333563804626,0.05962810665369034,2.398069208022207,bfc8de485755d7f3,ddffbe335bef15ad,mps
quantum,quantum_beta_0.5_1.0,53,30,80,2400,1.3147938251495361,0.5580000281333923,1.295978307723999,0.574999988079071,0.05797567218542099,2.4090406668838114,bfc8de485755d7f3,00868f788440b8a0,mps
quantum,quantum_beta_0.6_1.0,51,30,80,2400,1.7944316864013672,0.3955000042915344,1.7316218614578247,0.4050000011920929,0.07277336716651917,2.4751413341145962,bfc8de485755d7f3,82b3214d02e2e634,mps
quantum,quantum_beta_0.6_1.0,52,30,80,2400,1.770385980606079,0.3785000145435333,1.7482523918151855,0.3866666555404663,0.07508829981088638,2.41624170797877,bfc8de485755d7f3,ddffbe335bef15ad,mps
quantum,quantum_beta_0.6_1.0,53,30,80,2400,1.711459994316101,0.4320000112056732,1.684290885925293,0.4399999976158142,0.07037489116191864,2.4244089999701828,bfc8de485755d7f3,00868f788440b8a0,mps
quantum,quantum_beta_0.5_1.2,51,30,80,2400,1.6640392541885376,0.4580000042915344,1.6313401460647583,0.43833333253860474,0.0688972994685173,2.379183917073533,bfc8de485755d7f3,82b3214d02e2e634,mps
quantum,quantum_beta_0.5_1.2,52,30,80,2400,1.5431870222091675,0.49149999022483826,1.515285849571228,0.5133333206176758,0.06496633589267731,2.3730562501586974,bfc8de485755d7f3,ddffbe335bef15ad,mps
quantum,quantum_beta_0.5_1.2,53,30,80,2400,1.7410157918930054,0.4059999883174896,1.7098183631896973,0.3916666805744171,0.07386184483766556,2.438973000040278,bfc8de485755d7f3,00868f788440b8a0,mps
quantum,quantum_beta_0.4_0.9,51,30,80,2400,1.4330989122390747,0.512499988079071,1.369968295097351,0.5433333516120911,0.06111948564648628,2.5881015001796186,bfc8de485755d7f3,82b3214d02e2e634,mps
quantum,quantum_beta_0.4_0.9,52,30,80,2400,1.4685417413711548,0.5249999761581421,1.3982360363006592,0.5716666579246521,0.06072157621383667,2.5672016669996083,bfc8de485755d7f3,ddffbe335bef15ad,mps
quantum,quantum_beta_0.4_0.9,53,30,80,2400,1.3139362335205078,0.5789999961853027,1.244922161102295,0.5799999833106995,0.056043192744255066,2.4583064999897033,bfc8de485755d7f3,00868f788440b8a0,mps
1 method candidate_label seed n_particles epochs particle_epochs train_loss train_acc val_loss val_acc val_mse fit_time_sec data_fingerprint model_fingerprint device
2 adaptive_moment am_b0.03_s0.5 51 30 80 2400 1.112807035446167 0.6570000052452087 1.0964455604553223 0.6633333563804626 0.046677425503730774 2.5230953749269247 bfc8de485755d7f3 82b3214d02e2e634 mps
3 adaptive_moment am_b0.03_s0.5 52 30 80 2400 0.995387077331543 0.6869999766349792 1.0115970373153687 0.6816666722297668 0.04280940815806389 2.5447300830855966 bfc8de485755d7f3 ddffbe335bef15ad mps
4 adaptive_moment am_b0.03_s0.5 53 30 80 2400 0.9480092525482178 0.7080000042915344 0.9940392971038818 0.6883333325386047 0.0421285405755043 2.493663167115301 bfc8de485755d7f3 00868f788440b8a0 mps
5 adaptive_moment am_b0.03_s1.0 51 30 80 2400 0.9984938502311707 0.7024999856948853 1.0240328311920166 0.6850000023841858 0.04404553398489952 2.54264641716145 bfc8de485755d7f3 82b3214d02e2e634 mps
6 adaptive_moment am_b0.03_s1.0 52 30 80 2400 1.0163198709487915 0.6890000104904175 0.9720749855041504 0.6933333277702332 0.042471032589673996 2.491340707987547 bfc8de485755d7f3 ddffbe335bef15ad mps
7 adaptive_moment am_b0.03_s1.0 53 30 80 2400 1.0211265087127686 0.6884999871253967 1.081996202468872 0.6850000023841858 0.04483034089207649 2.633713499875739 bfc8de485755d7f3 00868f788440b8a0 mps
8 adaptive_moment am_b0.03_s1.5 51 30 80 2400 1.0612047910690308 0.6735000014305115 1.030881404876709 0.6850000023841858 0.04405777156352997 2.8367540831677616 bfc8de485755d7f3 82b3214d02e2e634 mps
9 adaptive_moment am_b0.03_s1.5 52 30 80 2400 0.9462777972221375 0.7149999737739563 0.8990310430526733 0.7266666889190674 0.03923330828547478 2.5477614579722285 bfc8de485755d7f3 ddffbe335bef15ad mps
10 adaptive_moment am_b0.03_s1.5 53 30 80 2400 0.9608960747718811 0.7120000123977661 1.0034033060073853 0.6833333373069763 0.042279649525880814 2.5643632498104125 bfc8de485755d7f3 00868f788440b8a0 mps
11 adaptive_moment am_b0.06_s0.5 51 30 80 2400 1.068866491317749 0.6855000257492065 1.0220483541488647 0.675000011920929 0.04446922242641449 2.6285997920203954 bfc8de485755d7f3 82b3214d02e2e634 mps
12 adaptive_moment am_b0.06_s0.5 52 30 80 2400 0.9650915265083313 0.7080000042915344 0.9152445197105408 0.7083333134651184 0.04066821560263634 2.7918378338217735 bfc8de485755d7f3 ddffbe335bef15ad mps
13 adaptive_moment am_b0.06_s0.5 53 30 80 2400 0.9414446949958801 0.7014999985694885 0.9156243801116943 0.721666693687439 0.03982962667942047 2.729898874880746 bfc8de485755d7f3 00868f788440b8a0 mps
14 adaptive_moment am_b0.06_s1.0 51 30 80 2400 1.0662047863006592 0.6754999756813049 0.9721739888191223 0.6899999976158142 0.042344048619270325 2.5812953328713775 bfc8de485755d7f3 82b3214d02e2e634 mps
15 adaptive_moment am_b0.06_s1.0 52 30 80 2400 0.9808334112167358 0.6924999952316284 0.9488086104393005 0.6933333277702332 0.04174558073282242 2.8018474159762263 bfc8de485755d7f3 ddffbe335bef15ad mps
16 adaptive_moment am_b0.06_s1.0 53 30 80 2400 0.9464473724365234 0.7085000276565552 0.9320653080940247 0.7083333134651184 0.040938157588243484 2.7171109160408378 bfc8de485755d7f3 00868f788440b8a0 mps
17 adaptive_moment am_b0.06_s1.5 51 30 80 2400 1.1136488914489746 0.6549999713897705 1.1235008239746094 0.6650000214576721 0.04781530797481537 2.7500752080231905 bfc8de485755d7f3 82b3214d02e2e634 mps
18 adaptive_moment am_b0.06_s1.5 52 30 80 2400 1.0324392318725586 0.6825000047683716 1.0213521718978882 0.6833333373069763 0.04513593390583992 3.103288209065795 bfc8de485755d7f3 ddffbe335bef15ad mps
19 adaptive_moment am_b0.06_s1.5 53 30 80 2400 1.0860146284103394 0.6589999794960022 1.0920277833938599 0.6499999761581421 0.04753243178129196 2.862214791122824 bfc8de485755d7f3 00868f788440b8a0 mps
20 adaptive_moment am_b0.1_s0.5 51 30 80 2400 1.0369712114334106 0.6644999980926514 1.0877516269683838 0.6766666769981384 0.04590357467532158 2.8528131251223385 bfc8de485755d7f3 82b3214d02e2e634 mps
21 adaptive_moment am_b0.1_s0.5 52 30 80 2400 0.9936539530754089 0.6965000033378601 0.9451570510864258 0.6983333230018616 0.040929101407527924 2.7851123749278486 bfc8de485755d7f3 ddffbe335bef15ad mps
22 adaptive_moment am_b0.1_s0.5 53 30 80 2400 0.9365023374557495 0.7014999985694885 0.8658249974250793 0.7283333539962769 0.038747914135456085 2.7191387079656124 bfc8de485755d7f3 00868f788440b8a0 mps
23 adaptive_moment am_b0.1_s1.0 51 30 80 2400 1.0722532272338867 0.6995000243186951 0.9926102757453918 0.7016666531562805 0.04247405380010605 2.8380547501146793 bfc8de485755d7f3 82b3214d02e2e634 mps
24 adaptive_moment am_b0.1_s1.0 52 30 80 2400 0.9977437853813171 0.6930000185966492 1.009635329246521 0.6966666579246521 0.04288069158792496 2.905045999912545 bfc8de485755d7f3 ddffbe335bef15ad mps
25 adaptive_moment am_b0.1_s1.0 53 30 80 2400 1.0425642728805542 0.6664999723434448 1.0561128854751587 0.6783333420753479 0.0449373684823513 2.7804793331306428 bfc8de485755d7f3 00868f788440b8a0 mps
26 adaptive_moment am_b0.1_s1.5 51 30 80 2400 1.0099478960037231 0.6775000095367432 1.00997793674469 0.6899999976158142 0.042956266552209854 2.8892802079208195 bfc8de485755d7f3 82b3214d02e2e634 mps
27 adaptive_moment am_b0.1_s1.5 52 30 80 2400 1.0597009658813477 0.6744999885559082 1.063821792602539 0.6966666579246521 0.044883932918310165 2.669369083130732 bfc8de485755d7f3 ddffbe335bef15ad mps
28 adaptive_moment am_b0.1_s1.5 53 30 80 2400 0.986152708530426 0.7080000042915344 1.0491483211517334 0.6850000023841858 0.04293783754110336 2.626303083030507 bfc8de485755d7f3 00868f788440b8a0 mps
29 adaptive_moment am_b0.15_s0.5 51 30 80 2400 1.1084171533584595 0.652999997138977 1.1322306394577026 0.6600000262260437 0.04928039386868477 2.5766194579191506 bfc8de485755d7f3 82b3214d02e2e634 mps
30 adaptive_moment am_b0.15_s0.5 52 30 80 2400 0.970059335231781 0.7129999995231628 0.9312149882316589 0.7300000190734863 0.03986109420657158 2.731992583023384 bfc8de485755d7f3 ddffbe335bef15ad mps
31 adaptive_moment am_b0.15_s0.5 53 30 80 2400 0.9970248341560364 0.6995000243186951 0.9656715989112854 0.70333331823349 0.04226723685860634 3.0274115409702063 bfc8de485755d7f3 00868f788440b8a0 mps
32 adaptive_moment am_b0.15_s1.0 51 30 80 2400 1.0099536180496216 0.7009999752044678 0.9564719796180725 0.7166666388511658 0.04047820717096329 2.9793542500119656 bfc8de485755d7f3 82b3214d02e2e634 mps
33 adaptive_moment am_b0.15_s1.0 52 30 80 2400 1.0434995889663696 0.6819999814033508 0.9963273406028748 0.6949999928474426 0.04349374398589134 2.8151185419410467 bfc8de485755d7f3 ddffbe335bef15ad mps
34 adaptive_moment am_b0.15_s1.0 53 30 80 2400 1.0348174571990967 0.6840000152587891 1.0127019882202148 0.6866666674613953 0.04363732784986496 3.0412363330833614 bfc8de485755d7f3 00868f788440b8a0 mps
35 adaptive_moment am_b0.15_s1.5 51 30 80 2400 1.1573493480682373 0.6299999952316284 1.2029211521148682 0.6133333444595337 0.05152761936187744 3.1714885828550905 bfc8de485755d7f3 82b3214d02e2e634 mps
36 adaptive_moment am_b0.15_s1.5 52 30 80 2400 1.060623288154602 0.6949999928474426 1.0831577777862549 0.6966666579246521 0.04344262182712555 2.993674041936174 bfc8de485755d7f3 ddffbe335bef15ad mps
37 adaptive_moment am_b0.15_s1.5 53 30 80 2400 1.0753278732299805 0.6769999861717224 0.9602290987968445 0.6983333230018616 0.0414869599044323 2.7658886671997607 bfc8de485755d7f3 00868f788440b8a0 mps
38 adaptive_moment am_b0.06_s1.0_beta0.8 51 30 80 2400 1.1594665050506592 0.6480000019073486 1.1239941120147705 0.6700000166893005 0.047122661024332047 2.764871333958581 bfc8de485755d7f3 82b3214d02e2e634 mps
39 adaptive_moment am_b0.06_s1.0_beta0.8 52 30 80 2400 0.9945915341377258 0.7064999938011169 1.0324488878250122 0.6683333516120911 0.04436164349317551 2.830462665995583 bfc8de485755d7f3 ddffbe335bef15ad mps
40 adaptive_moment am_b0.06_s1.0_beta0.8 53 30 80 2400 0.9297089576721191 0.7110000252723694 0.938503623008728 0.70333331823349 0.04101715609431267 2.8122877080459148 bfc8de485755d7f3 00868f788440b8a0 mps
41 adaptive_moment am_b0.06_s1.0_beta0.95 51 30 80 2400 1.1143780946731567 0.6514999866485596 1.0093973875045776 0.6800000071525574 0.04380199685692787 2.9217382080387324 bfc8de485755d7f3 82b3214d02e2e634 mps
42 adaptive_moment am_b0.06_s1.0_beta0.95 52 30 80 2400 1.0144661664962769 0.6884999871253967 0.9828318357467651 0.6833333373069763 0.04375388100743294 2.777603624854237 bfc8de485755d7f3 ddffbe335bef15ad mps
43 adaptive_moment am_b0.06_s1.0_beta0.95 53 30 80 2400 1.1004270315170288 0.6600000262260437 1.0834088325500488 0.6683333516120911 0.04595582187175751 3.0502532080281526 bfc8de485755d7f3 00868f788440b8a0 mps
44 inertia inertia_canonical 51 30 80 2400 1.4416260719299316 0.5649999976158142 1.4125466346740723 0.6000000238418579 0.05729174241423607 3.098805333022028 bfc8de485755d7f3 82b3214d02e2e634 mps
45 inertia inertia_canonical 52 30 80 2400 1.665074348449707 0.4894999861717224 1.6294100284576416 0.5066666603088379 0.06497763097286224 2.652766958111897 bfc8de485755d7f3 ddffbe335bef15ad mps
46 inertia inertia_canonical 53 30 80 2400 1.4698249101638794 0.5565000176429749 1.4316622018814087 0.5683333277702332 0.059196680784225464 2.529014667030424 bfc8de485755d7f3 00868f788440b8a0 mps
47 inertia inertia_tuned 51 30 80 2400 1.0468239784240723 0.699999988079071 1.0345858335494995 0.6816666722297668 0.04465937986969948 2.626064541982487 bfc8de485755d7f3 82b3214d02e2e634 mps
48 inertia inertia_tuned 52 30 80 2400 0.9420707821846008 0.703000009059906 0.9224241375923157 0.7250000238418579 0.040230974555015564 2.5009557919111103 bfc8de485755d7f3 ddffbe335bef15ad mps
49 inertia inertia_tuned 53 30 80 2400 1.0711344480514526 0.6754999756813049 1.0678696632385254 0.6766666769981384 0.04436859115958214 2.4420957090333104 bfc8de485755d7f3 00868f788440b8a0 mps
50 inertia inertia_low_w 51 30 80 2400 1.113174319267273 0.6365000009536743 1.0575727224349976 0.6850000023841858 0.0461583249270916 2.484879707917571 bfc8de485755d7f3 82b3214d02e2e634 mps
51 inertia inertia_low_w 52 30 80 2400 1.0245047807693481 0.6790000200271606 0.9806669354438782 0.70333331823349 0.042586930096149445 2.643615792039782 bfc8de485755d7f3 ddffbe335bef15ad mps
52 inertia inertia_low_w 53 30 80 2400 1.0131726264953613 0.6859999895095825 0.9656168818473816 0.6983333230018616 0.04216707497835159 2.4834530411753803 bfc8de485755d7f3 00868f788440b8a0 mps
53 inertia inertia_w_decay 51 30 80 2400 1.0393675565719604 0.6915000081062317 1.025821566581726 0.6983333230018616 0.04372088611125946 2.426888459129259 bfc8de485755d7f3 82b3214d02e2e634 mps
54 inertia inertia_w_decay 52 30 80 2400 1.1047112941741943 0.6600000262260437 1.1661359071731567 0.6700000166893005 0.04753515496850014 2.480164624983445 bfc8de485755d7f3 ddffbe335bef15ad mps
55 inertia inertia_w_decay 53 30 80 2400 0.9673198461532593 0.6909999847412109 0.9870904684066772 0.6816666722297668 0.04321449622511864 2.422041916055605 bfc8de485755d7f3 00868f788440b8a0 mps
56 inertia inertia_asymmetric 51 30 80 2400 1.0810083150863647 0.6775000095367432 1.0954482555389404 0.6949999928474426 0.04539918154478073 2.485565959010273 bfc8de485755d7f3 82b3214d02e2e634 mps
57 inertia inertia_asymmetric 52 30 80 2400 0.9861184358596802 0.6909999847412109 1.0121054649353027 0.6933333277702332 0.043754804879426956 2.5209125420078635 bfc8de485755d7f3 ddffbe335bef15ad mps
58 inertia inertia_asymmetric 53 30 80 2400 0.9526417851448059 0.722000002861023 0.9798429608345032 0.70333331823349 0.04217696189880371 2.5959840829018503 bfc8de485755d7f3 00868f788440b8a0 mps
59 constriction constriction_c201 51 30 80 2400 1.0345860719680786 0.6869999766349792 0.9738073945045471 0.6916666626930237 0.042279358953237534 2.698073250008747 bfc8de485755d7f3 82b3214d02e2e634 mps
60 constriction constriction_c201 52 30 80 2400 1.0233638286590576 0.6865000128746033 1.0523117780685425 0.6883333325386047 0.043930795043706894 3.4117308750282973 bfc8de485755d7f3 ddffbe335bef15ad mps
61 constriction constriction_c201 53 30 80 2400 1.0302053689956665 0.6880000233650208 0.9275049567222595 0.7166666388511658 0.040264155715703964 3.1805959579069167 bfc8de485755d7f3 00868f788440b8a0 mps
62 constriction constriction_c205_canonical 51 30 80 2400 1.0515944957733154 0.6759999990463257 1.0619410276412964 0.6933333277702332 0.044216156005859375 3.237764042103663 bfc8de485755d7f3 82b3214d02e2e634 mps
63 constriction constriction_c205_canonical 52 30 80 2400 1.061699390411377 0.6744999885559082 1.016150712966919 0.6866666674613953 0.0442710816860199 2.7161605830769986 bfc8de485755d7f3 ddffbe335bef15ad mps
64 constriction constriction_c205_canonical 53 30 80 2400 0.9327093362808228 0.7289999723434448 0.8751127123832703 0.746666669845581 0.03667077049612999 2.968678707955405 bfc8de485755d7f3 00868f788440b8a0 mps
65 constriction constriction_c205_tuned 51 30 80 2400 1.053567886352539 0.6825000047683716 0.991266667842865 0.699999988079071 0.04196731001138687 3.042221749899909 bfc8de485755d7f3 82b3214d02e2e634 mps
66 constriction constriction_c205_tuned 52 30 80 2400 0.9388713836669922 0.7139999866485596 0.9303705096244812 0.7116666436195374 0.0403158962726593 2.8448961251415312 bfc8de485755d7f3 ddffbe335bef15ad mps
67 constriction constriction_c205_tuned 53 30 80 2400 0.9965179562568665 0.6995000243186951 0.9846652150154114 0.7133333086967468 0.04187808558344841 3.1848887908272445 bfc8de485755d7f3 00868f788440b8a0 mps
68 constriction constriction_c250 51 30 80 2400 2.2795634269714355 0.18150000274181366 2.250452756881714 0.18666666746139526 0.08861013501882553 2.9961123750545084 bfc8de485755d7f3 82b3214d02e2e634 mps
69 constriction constriction_c250 52 30 80 2400 2.2547407150268555 0.20350000262260437 2.2187206745147705 0.2150000035762787 0.08802558481693268 3.024313250090927 bfc8de485755d7f3 ddffbe335bef15ad mps
70 constriction constriction_c250 53 30 80 2400 2.19023060798645 0.19300000369548798 2.178219795227051 0.20499999821186066 0.08775272965431213 2.961929291021079 bfc8de485755d7f3 00868f788440b8a0 mps
71 constriction constriction_asymmetric 51 30 80 2400 1.2645200490951538 0.6150000095367432 1.2170491218566895 0.6316666603088379 0.05265685170888901 3.023697500117123 bfc8de485755d7f3 82b3214d02e2e634 mps
72 constriction constriction_asymmetric 52 30 80 2400 1.2607256174087524 0.6014999747276306 1.2684078216552734 0.5883333086967468 0.05453150346875191 2.8057189998216927 bfc8de485755d7f3 ddffbe335bef15ad mps
73 constriction constriction_asymmetric 53 30 80 2400 1.2320771217346191 0.6140000224113464 1.2376829385757446 0.6200000047683716 0.05186690762639046 2.709051915910095 bfc8de485755d7f3 00868f788440b8a0 mps
74 local_best local_best_r1_constant 51 30 80 2400 1.4435429573059082 0.5354999899864197 1.4319947957992554 0.528333306312561 0.062414027750492096 2.6544057081919163 bfc8de485755d7f3 82b3214d02e2e634 mps
75 local_best local_best_r1_constant 52 30 80 2400 1.4914970397949219 0.527999997138977 1.4473850727081299 0.5333333611488342 0.06336859613656998 2.7407990000210702 bfc8de485755d7f3 ddffbe335bef15ad mps
76 local_best local_best_r1_constant 53 30 80 2400 1.4734760522842407 0.5680000185966492 1.5045198202133179 0.5366666913032532 0.06426934152841568 2.693694499786943 bfc8de485755d7f3 00868f788440b8a0 mps
77 local_best local_best_r2_constant 51 30 80 2400 1.2284750938415527 0.6345000267028809 1.191932201385498 0.628333330154419 0.0514327734708786 2.4563205409795046 bfc8de485755d7f3 82b3214d02e2e634 mps
78 local_best local_best_r2_constant 52 30 80 2400 1.1704559326171875 0.6504999995231628 1.1325551271438599 0.6700000166893005 0.049076274037361145 2.5833707919809967 bfc8de485755d7f3 ddffbe335bef15ad mps
79 local_best local_best_r2_constant 53 30 80 2400 1.243313193321228 0.6320000290870667 1.2125868797302246 0.6516666412353516 0.05214923247694969 2.475700625218451 bfc8de485755d7f3 00868f788440b8a0 mps
80 local_best local_best_r4_constant 51 30 80 2400 1.0172255039215088 0.6884999871253967 1.0000126361846924 0.699999988079071 0.0425647497177124 2.4682277080137283 bfc8de485755d7f3 82b3214d02e2e634 mps
81 local_best local_best_r4_constant 52 30 80 2400 0.9992414712905884 0.7089999914169312 0.9295963644981384 0.7283333539962769 0.04094172641634941 2.4259307920001447 bfc8de485755d7f3 ddffbe335bef15ad mps
82 local_best local_best_r4_constant 53 30 80 2400 0.9578440189361572 0.7139999866485596 0.9051287770271301 0.7450000047683716 0.03751235082745552 2.4171505419071764 bfc8de485755d7f3 00868f788440b8a0 mps
83 local_best local_best_r1_decay 51 30 80 2400 1.4906078577041626 0.5640000104904175 1.4867504835128784 0.5400000214576721 0.06405453383922577 2.455931582953781 bfc8de485755d7f3 82b3214d02e2e634 mps
84 local_best local_best_r1_decay 52 30 80 2400 1.4803287982940674 0.5559999942779541 1.4699759483337402 0.5733333230018616 0.0630197748541832 2.5800107079558074 bfc8de485755d7f3 ddffbe335bef15ad mps
85 local_best local_best_r1_decay 53 30 80 2400 1.4953768253326416 0.5370000004768372 1.4344968795776367 0.5416666865348816 0.06267009675502777 2.5192452499177307 bfc8de485755d7f3 00868f788440b8a0 mps
86 quantum quantum_beta_0.5_1.0 51 30 80 2400 1.4666624069213867 0.5055000185966492 1.4257338047027588 0.5183333158493042 0.06076965853571892 2.411743083037436 bfc8de485755d7f3 82b3214d02e2e634 mps
87 quantum quantum_beta_0.5_1.0 52 30 80 2400 1.4086458683013916 0.5370000004768372 1.3660792112350464 0.5383333563804626 0.05962810665369034 2.398069208022207 bfc8de485755d7f3 ddffbe335bef15ad mps
88 quantum quantum_beta_0.5_1.0 53 30 80 2400 1.3147938251495361 0.5580000281333923 1.295978307723999 0.574999988079071 0.05797567218542099 2.4090406668838114 bfc8de485755d7f3 00868f788440b8a0 mps
89 quantum quantum_beta_0.6_1.0 51 30 80 2400 1.7944316864013672 0.3955000042915344 1.7316218614578247 0.4050000011920929 0.07277336716651917 2.4751413341145962 bfc8de485755d7f3 82b3214d02e2e634 mps
90 quantum quantum_beta_0.6_1.0 52 30 80 2400 1.770385980606079 0.3785000145435333 1.7482523918151855 0.3866666555404663 0.07508829981088638 2.41624170797877 bfc8de485755d7f3 ddffbe335bef15ad mps
91 quantum quantum_beta_0.6_1.0 53 30 80 2400 1.711459994316101 0.4320000112056732 1.684290885925293 0.4399999976158142 0.07037489116191864 2.4244089999701828 bfc8de485755d7f3 00868f788440b8a0 mps
92 quantum quantum_beta_0.5_1.2 51 30 80 2400 1.6640392541885376 0.4580000042915344 1.6313401460647583 0.43833333253860474 0.0688972994685173 2.379183917073533 bfc8de485755d7f3 82b3214d02e2e634 mps
93 quantum quantum_beta_0.5_1.2 52 30 80 2400 1.5431870222091675 0.49149999022483826 1.515285849571228 0.5133333206176758 0.06496633589267731 2.3730562501586974 bfc8de485755d7f3 ddffbe335bef15ad mps
94 quantum quantum_beta_0.5_1.2 53 30 80 2400 1.7410157918930054 0.4059999883174896 1.7098183631896973 0.3916666805744171 0.07386184483766556 2.438973000040278 bfc8de485755d7f3 00868f788440b8a0 mps
95 quantum quantum_beta_0.4_0.9 51 30 80 2400 1.4330989122390747 0.512499988079071 1.369968295097351 0.5433333516120911 0.06111948564648628 2.5881015001796186 bfc8de485755d7f3 82b3214d02e2e634 mps
96 quantum quantum_beta_0.4_0.9 52 30 80 2400 1.4685417413711548 0.5249999761581421 1.3982360363006592 0.5716666579246521 0.06072157621383667 2.5672016669996083 bfc8de485755d7f3 ddffbe335bef15ad mps
97 quantum quantum_beta_0.4_0.9 53 30 80 2400 1.3139362335205078 0.5789999961853027 1.244922161102295 0.5799999833106995 0.056043192744255066 2.4583064999897033 bfc8de485755d7f3 00868f788440b8a0 mps
+10
View File
@@ -0,0 +1,10 @@
section,metric,value
protocol,version,MNIST-PSO-RAW-V5 1.0.0
pilot,selected_dimension,full
single_model,test_accuracy,83.23
single_model,test_nll,0.556332
single_model,test_brier,0.253921
single_model,test_ece,0.085242
ensemble,test_accuracy,87.0
ensemble,test_nll,0.535928
ensemble,pairwise_disagreement,0.1585
1 section metric value
2 protocol version MNIST-PSO-RAW-V5 1.0.0
3 pilot selected_dimension full
4 single_model test_accuracy 83.23
5 single_model test_nll 0.556332
6 single_model test_brier 0.253921
7 single_model test_ece 0.085242
8 ensemble test_accuracy 87.0
9 ensemble test_nll 0.535928
10 ensemble pairwise_disagreement 0.1585
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
workload,split,seeds,accuracy_mean,accuracy_sd,nll_mean,nll_sd,accuracy_gain_vs_baseline_pp,relative_nll_reduction_vs_baseline_pct,state_ratio,ratio,projection_mode,projection_seed
mnist_compact,development_seeds_101_103,101-103,49.053333,0.417652,1.525704,0.01494,-0.1,-0.501617,0.4918032786885246,0.5,fixed,1800044939
mnist_compact,confirmation_seeds_111_113,111-113,48.466667,2.515002,1.550212,0.046101,-0.686666,-2.116016,0.4918032786885246,0.5,fixed,1800044939
mnist_wide,development_seeds_101_103,101-103,48.346667,3.214052,1.677712,0.072138,4.486667,2.52995,0.5,0.5,explicit,592157828
mnist_wide,confirmation_seeds_111_113,111-113,45.723333,1.05633,1.734905,0.034461,1.863333,-0.792792,0.5,0.5,explicit,592157828
fashion_compact,development_seeds_101_103,101-103,50.796667,3.095228,1.385324,0.035646,3.793334,8.330571,0.4918032786885246,0.5,fixed,1363313651
fashion_compact,confirmation_seeds_111_113,111-113,53.196667,2.025669,1.328168,0.078488,6.193334,12.112688,0.4918032786885246,0.5,fixed,1363313651
fashion_wide,development_seeds_101_103,101-103,48.263333,3.71862,1.531421,0.069986,1.953333,-0.371883,0.5,0.5,fixed,189641451
fashion_wide,confirmation_seeds_111_113,111-113,49.476667,4.250251,1.513876,0.067096,3.166667,0.778045,0.5,0.5,fixed,189641451
1 workload split seeds accuracy_mean accuracy_sd nll_mean nll_sd accuracy_gain_vs_baseline_pp relative_nll_reduction_vs_baseline_pct state_ratio ratio projection_mode projection_seed
2 mnist_compact development_seeds_101_103 101-103 49.053333 0.417652 1.525704 0.01494 -0.1 -0.501617 0.4918032786885246 0.5 fixed 1800044939
3 mnist_compact confirmation_seeds_111_113 111-113 48.466667 2.515002 1.550212 0.046101 -0.686666 -2.116016 0.4918032786885246 0.5 fixed 1800044939
4 mnist_wide development_seeds_101_103 101-103 48.346667 3.214052 1.677712 0.072138 4.486667 2.52995 0.5 0.5 explicit 592157828
5 mnist_wide confirmation_seeds_111_113 111-113 45.723333 1.05633 1.734905 0.034461 1.863333 -0.792792 0.5 0.5 explicit 592157828
6 fashion_compact development_seeds_101_103 101-103 50.796667 3.095228 1.385324 0.035646 3.793334 8.330571 0.4918032786885246 0.5 fixed 1363313651
7 fashion_compact confirmation_seeds_111_113 111-113 53.196667 2.025669 1.328168 0.078488 6.193334 12.112688 0.4918032786885246 0.5 fixed 1363313651
8 fashion_wide development_seeds_101_103 101-103 48.263333 3.71862 1.531421 0.069986 1.953333 -0.371883 0.5 0.5 fixed 189641451
9 fashion_wide confirmation_seeds_111_113 111-113 49.476667 4.250251 1.513876 0.067096 3.166667 0.778045 0.5 0.5 fixed 189641451
File diff suppressed because it is too large Load Diff
+164
View File
@@ -0,0 +1,164 @@
section,workload,method,metric,value
protocol,global,all,version,HEAVY-TASK-PSO-V6 1.0.0
protocol,global,all,official_test_data_loaded,False
protocol,global,all,official_test_evaluations,0
baseline,mnist_compact,untrained,val_nll,2.325095
baseline,mnist_compact,untrained,val_accuracy,6.65
baseline,mnist_wide,untrained,val_nll,2.32458
baseline,mnist_wide,untrained,val_accuracy,9.31
baseline,fashion_compact,untrained,val_nll,2.320302
baseline,fashion_compact,untrained,val_accuracy,8.08
baseline,fashion_wide,untrained,val_nll,2.311222
baseline,fashion_wide,untrained,val_accuracy,8.77
screen,mnist_compact,G0,val_nll,2.019988
screen,mnist_compact,G0,val_acc,33.32
screen,mnist_compact,G0,gbest_loss,2.014459
screen,mnist_compact,G0,gbest_acc,34.4
screen,mnist_compact,G0,throughput_sps,1106883.43
screen,mnist_compact,G5,val_nll,1.959743
screen,mnist_compact,G5,val_acc,33.95
screen,mnist_compact,G5,gbest_loss,1.971574
screen,mnist_compact,G5,gbest_acc,32.35
screen,mnist_compact,G5,throughput_sps,1139465.88
screen,mnist_compact,G6,val_nll,1.957307
screen,mnist_compact,G6,val_acc,35.11
screen,mnist_compact,G6,gbest_loss,1.941859
screen,mnist_compact,G6,gbest_acc,35.35
screen,mnist_compact,G6,throughput_sps,1340969.41
screen,mnist_compact,G8,val_nll,1.690442
screen,mnist_compact,G8,val_acc,43.88
screen,mnist_compact,G8,gbest_loss,1.675063
screen,mnist_compact,G8,gbest_acc,43.6
screen,mnist_compact,G8,throughput_sps,984615.38
screen,mnist_wide,G0,val_nll,2.088156
screen,mnist_wide,G0,val_acc,33.19
screen,mnist_wide,G0,gbest_loss,2.087951
screen,mnist_wide,G0,gbest_acc,32.45
screen,mnist_wide,G0,throughput_sps,595459.62
screen,mnist_wide,G5,val_nll,1.913566
screen,mnist_wide,G5,val_acc,39.87
screen,mnist_wide,G5,gbest_loss,1.924357
screen,mnist_wide,G5,gbest_acc,39.5
screen,mnist_wide,G5,throughput_sps,772511.47
screen,mnist_wide,G6,val_nll,2.017511
screen,mnist_wide,G6,val_acc,29.46
screen,mnist_wide,G6,gbest_loss,2.014518
screen,mnist_wide,G6,gbest_acc,27.8
screen,mnist_wide,G6,throughput_sps,777013.35
screen,mnist_wide,G8,val_nll,2.056341
screen,mnist_wide,G8,val_acc,20.43
screen,mnist_wide,G8,gbest_loss,2.062368
screen,mnist_wide,G8,gbest_acc,20.3
screen,mnist_wide,G8,throughput_sps,855005.34
screen,fashion_compact,G0,val_nll,1.969548
screen,fashion_compact,G0,val_acc,28.25
screen,fashion_compact,G0,gbest_loss,1.96974
screen,fashion_compact,G0,gbest_acc,28.15
screen,fashion_compact,G0,throughput_sps,1219977.13
screen,fashion_compact,G5,val_nll,1.892402
screen,fashion_compact,G5,val_acc,34.31
screen,fashion_compact,G5,gbest_loss,1.874629
screen,fashion_compact,G5,gbest_acc,36.6
screen,fashion_compact,G5,throughput_sps,1122281.97
screen,fashion_compact,G6,val_nll,1.824273
screen,fashion_compact,G6,val_acc,35.2
screen,fashion_compact,G6,gbest_loss,1.844914
screen,fashion_compact,G6,gbest_acc,33.5
screen,fashion_compact,G6,throughput_sps,1152322.65
screen,fashion_compact,G8,val_nll,1.88018
screen,fashion_compact,G8,val_acc,30.85
screen,fashion_compact,G8,gbest_loss,1.895269
screen,fashion_compact,G8,gbest_acc,28.85
screen,fashion_compact,G8,throughput_sps,1193139.45
screen,fashion_wide,G0,val_nll,1.981316
screen,fashion_wide,G0,val_acc,36.39
screen,fashion_wide,G0,gbest_loss,1.98436
screen,fashion_wide,G0,gbest_acc,36.05
screen,fashion_wide,G0,throughput_sps,540327.57
screen,fashion_wide,G5,val_nll,1.945862
screen,fashion_wide,G5,val_acc,31.97
screen,fashion_wide,G5,gbest_loss,1.938569
screen,fashion_wide,G5,gbest_acc,33.3
screen,fashion_wide,G5,throughput_sps,807401.18
screen,fashion_wide,G6,val_nll,1.975825
screen,fashion_wide,G6,val_acc,28.64
screen,fashion_wide,G6,gbest_loss,1.960139
screen,fashion_wide,G6,gbest_acc,28.1
screen,fashion_wide,G6,throughput_sps,735350.44
screen,fashion_wide,G8,val_nll,1.987923
screen,fashion_wide,G8,val_acc,28.69
screen,fashion_wide,G8,gbest_loss,1.976977
screen,fashion_wide,G8,gbest_acc,28.45
screen,fashion_wide,G8,throughput_sps,865800.87
confirm,mnist_compact,G8,val_acc_mean,49.153333
confirm,mnist_compact,G8,val_acc_std,1.320656
confirm,mnist_compact,G8,val_nll_mean,1.518089
confirm,mnist_compact,G8,val_nll_std,0.061523
confirm,mnist_compact,G8,wall_time_sec_mean,4.530167
confirm,mnist_compact,G6,val_acc_mean,45.966667
confirm,mnist_compact,G6,val_acc_std,3.545438
confirm,mnist_compact,G6,val_nll_mean,1.642081
confirm,mnist_compact,G6,val_nll_std,0.091398
confirm,mnist_compact,G6,wall_time_sec_mean,5.2706
confirm,mnist_wide,G8,val_acc_mean,41.03
confirm,mnist_wide,G8,val_acc_std,4.055083
confirm,mnist_wide,G8,val_nll_mean,1.733351
confirm,mnist_wide,G8,val_nll_std,0.084331
confirm,mnist_wide,G8,wall_time_sec_mean,9.4569
confirm,mnist_wide,G5,val_acc_mean,43.86
confirm,mnist_wide,G5,val_acc_std,3.512222
confirm,mnist_wide,G5,val_nll_mean,1.721259
confirm,mnist_wide,G5,val_nll_std,0.098656
confirm,mnist_wide,G5,wall_time_sec_mean,9.868467
confirm,fashion_compact,G8,val_acc_mean,47.003333
confirm,fashion_compact,G8,val_acc_std,5.2259
confirm,fashion_compact,G8,val_nll_mean,1.511217
confirm,fashion_compact,G8,val_nll_std,0.168628
confirm,fashion_compact,G8,wall_time_sec_mean,5.0146
confirm,fashion_compact,G6,val_acc_mean,45.616667
confirm,fashion_compact,G6,val_acc_std,3.585392
confirm,fashion_compact,G6,val_nll_mean,1.584443
confirm,fashion_compact,G6,val_nll_std,0.053077
confirm,fashion_compact,G6,wall_time_sec_mean,4.839267
confirm,fashion_wide,G8,val_acc_mean,41.666667
confirm,fashion_wide,G8,val_acc_std,2.269016
confirm,fashion_wide,G8,val_nll_mean,1.615937
confirm,fashion_wide,G8,val_nll_std,0.036692
confirm,fashion_wide,G8,wall_time_sec_mean,9.499333
confirm,fashion_wide,G5,val_acc_mean,46.31
confirm,fashion_wide,G5,val_acc_std,7.568494
confirm,fashion_wide,G5,val_nll_mean,1.525747
confirm,fashion_wide,G5,val_nll_std,0.149706
confirm,fashion_wide,G5,wall_time_sec_mean,10.038833
feasibility,mnist_compact,G8,execution_feasible,True
feasibility,mnist_compact,G8,optimization_feasible,True
feasibility,mnist_compact,G8,mean_val_nll,1.518089
feasibility,mnist_compact,G8,mean_val_acc,49.1533
feasibility,mnist_compact,G6,execution_feasible,True
feasibility,mnist_compact,G6,optimization_feasible,True
feasibility,mnist_compact,G6,mean_val_nll,1.642081
feasibility,mnist_compact,G6,mean_val_acc,45.9667
feasibility,mnist_wide,G8,execution_feasible,True
feasibility,mnist_wide,G8,optimization_feasible,True
feasibility,mnist_wide,G8,mean_val_nll,1.733351
feasibility,mnist_wide,G8,mean_val_acc,41.03
feasibility,mnist_wide,G5,execution_feasible,True
feasibility,mnist_wide,G5,optimization_feasible,True
feasibility,mnist_wide,G5,mean_val_nll,1.721259
feasibility,mnist_wide,G5,mean_val_acc,43.86
feasibility,fashion_compact,G8,execution_feasible,True
feasibility,fashion_compact,G8,optimization_feasible,True
feasibility,fashion_compact,G8,mean_val_nll,1.511217
feasibility,fashion_compact,G8,mean_val_acc,47.0033
feasibility,fashion_compact,G6,execution_feasible,True
feasibility,fashion_compact,G6,optimization_feasible,True
feasibility,fashion_compact,G6,mean_val_nll,1.584443
feasibility,fashion_compact,G6,mean_val_acc,45.6167
feasibility,fashion_wide,G8,execution_feasible,True
feasibility,fashion_wide,G8,optimization_feasible,True
feasibility,fashion_wide,G8,mean_val_nll,1.615937
feasibility,fashion_wide,G8,mean_val_acc,41.6667
feasibility,fashion_wide,G5,execution_feasible,True
feasibility,fashion_wide,G5,optimization_feasible,True
feasibility,fashion_wide,G5,mean_val_nll,1.525747
feasibility,fashion_wide,G5,mean_val_acc,46.31
1 section workload method metric value
2 protocol global all version HEAVY-TASK-PSO-V6 1.0.0
3 protocol global all official_test_data_loaded False
4 protocol global all official_test_evaluations 0
5 baseline mnist_compact untrained val_nll 2.325095
6 baseline mnist_compact untrained val_accuracy 6.65
7 baseline mnist_wide untrained val_nll 2.32458
8 baseline mnist_wide untrained val_accuracy 9.31
9 baseline fashion_compact untrained val_nll 2.320302
10 baseline fashion_compact untrained val_accuracy 8.08
11 baseline fashion_wide untrained val_nll 2.311222
12 baseline fashion_wide untrained val_accuracy 8.77
13 screen mnist_compact G0 val_nll 2.019988
14 screen mnist_compact G0 val_acc 33.32
15 screen mnist_compact G0 gbest_loss 2.014459
16 screen mnist_compact G0 gbest_acc 34.4
17 screen mnist_compact G0 throughput_sps 1106883.43
18 screen mnist_compact G5 val_nll 1.959743
19 screen mnist_compact G5 val_acc 33.95
20 screen mnist_compact G5 gbest_loss 1.971574
21 screen mnist_compact G5 gbest_acc 32.35
22 screen mnist_compact G5 throughput_sps 1139465.88
23 screen mnist_compact G6 val_nll 1.957307
24 screen mnist_compact G6 val_acc 35.11
25 screen mnist_compact G6 gbest_loss 1.941859
26 screen mnist_compact G6 gbest_acc 35.35
27 screen mnist_compact G6 throughput_sps 1340969.41
28 screen mnist_compact G8 val_nll 1.690442
29 screen mnist_compact G8 val_acc 43.88
30 screen mnist_compact G8 gbest_loss 1.675063
31 screen mnist_compact G8 gbest_acc 43.6
32 screen mnist_compact G8 throughput_sps 984615.38
33 screen mnist_wide G0 val_nll 2.088156
34 screen mnist_wide G0 val_acc 33.19
35 screen mnist_wide G0 gbest_loss 2.087951
36 screen mnist_wide G0 gbest_acc 32.45
37 screen mnist_wide G0 throughput_sps 595459.62
38 screen mnist_wide G5 val_nll 1.913566
39 screen mnist_wide G5 val_acc 39.87
40 screen mnist_wide G5 gbest_loss 1.924357
41 screen mnist_wide G5 gbest_acc 39.5
42 screen mnist_wide G5 throughput_sps 772511.47
43 screen mnist_wide G6 val_nll 2.017511
44 screen mnist_wide G6 val_acc 29.46
45 screen mnist_wide G6 gbest_loss 2.014518
46 screen mnist_wide G6 gbest_acc 27.8
47 screen mnist_wide G6 throughput_sps 777013.35
48 screen mnist_wide G8 val_nll 2.056341
49 screen mnist_wide G8 val_acc 20.43
50 screen mnist_wide G8 gbest_loss 2.062368
51 screen mnist_wide G8 gbest_acc 20.3
52 screen mnist_wide G8 throughput_sps 855005.34
53 screen fashion_compact G0 val_nll 1.969548
54 screen fashion_compact G0 val_acc 28.25
55 screen fashion_compact G0 gbest_loss 1.96974
56 screen fashion_compact G0 gbest_acc 28.15
57 screen fashion_compact G0 throughput_sps 1219977.13
58 screen fashion_compact G5 val_nll 1.892402
59 screen fashion_compact G5 val_acc 34.31
60 screen fashion_compact G5 gbest_loss 1.874629
61 screen fashion_compact G5 gbest_acc 36.6
62 screen fashion_compact G5 throughput_sps 1122281.97
63 screen fashion_compact G6 val_nll 1.824273
64 screen fashion_compact G6 val_acc 35.2
65 screen fashion_compact G6 gbest_loss 1.844914
66 screen fashion_compact G6 gbest_acc 33.5
67 screen fashion_compact G6 throughput_sps 1152322.65
68 screen fashion_compact G8 val_nll 1.88018
69 screen fashion_compact G8 val_acc 30.85
70 screen fashion_compact G8 gbest_loss 1.895269
71 screen fashion_compact G8 gbest_acc 28.85
72 screen fashion_compact G8 throughput_sps 1193139.45
73 screen fashion_wide G0 val_nll 1.981316
74 screen fashion_wide G0 val_acc 36.39
75 screen fashion_wide G0 gbest_loss 1.98436
76 screen fashion_wide G0 gbest_acc 36.05
77 screen fashion_wide G0 throughput_sps 540327.57
78 screen fashion_wide G5 val_nll 1.945862
79 screen fashion_wide G5 val_acc 31.97
80 screen fashion_wide G5 gbest_loss 1.938569
81 screen fashion_wide G5 gbest_acc 33.3
82 screen fashion_wide G5 throughput_sps 807401.18
83 screen fashion_wide G6 val_nll 1.975825
84 screen fashion_wide G6 val_acc 28.64
85 screen fashion_wide G6 gbest_loss 1.960139
86 screen fashion_wide G6 gbest_acc 28.1
87 screen fashion_wide G6 throughput_sps 735350.44
88 screen fashion_wide G8 val_nll 1.987923
89 screen fashion_wide G8 val_acc 28.69
90 screen fashion_wide G8 gbest_loss 1.976977
91 screen fashion_wide G8 gbest_acc 28.45
92 screen fashion_wide G8 throughput_sps 865800.87
93 confirm mnist_compact G8 val_acc_mean 49.153333
94 confirm mnist_compact G8 val_acc_std 1.320656
95 confirm mnist_compact G8 val_nll_mean 1.518089
96 confirm mnist_compact G8 val_nll_std 0.061523
97 confirm mnist_compact G8 wall_time_sec_mean 4.530167
98 confirm mnist_compact G6 val_acc_mean 45.966667
99 confirm mnist_compact G6 val_acc_std 3.545438
100 confirm mnist_compact G6 val_nll_mean 1.642081
101 confirm mnist_compact G6 val_nll_std 0.091398
102 confirm mnist_compact G6 wall_time_sec_mean 5.2706
103 confirm mnist_wide G8 val_acc_mean 41.03
104 confirm mnist_wide G8 val_acc_std 4.055083
105 confirm mnist_wide G8 val_nll_mean 1.733351
106 confirm mnist_wide G8 val_nll_std 0.084331
107 confirm mnist_wide G8 wall_time_sec_mean 9.4569
108 confirm mnist_wide G5 val_acc_mean 43.86
109 confirm mnist_wide G5 val_acc_std 3.512222
110 confirm mnist_wide G5 val_nll_mean 1.721259
111 confirm mnist_wide G5 val_nll_std 0.098656
112 confirm mnist_wide G5 wall_time_sec_mean 9.868467
113 confirm fashion_compact G8 val_acc_mean 47.003333
114 confirm fashion_compact G8 val_acc_std 5.2259
115 confirm fashion_compact G8 val_nll_mean 1.511217
116 confirm fashion_compact G8 val_nll_std 0.168628
117 confirm fashion_compact G8 wall_time_sec_mean 5.0146
118 confirm fashion_compact G6 val_acc_mean 45.616667
119 confirm fashion_compact G6 val_acc_std 3.585392
120 confirm fashion_compact G6 val_nll_mean 1.584443
121 confirm fashion_compact G6 val_nll_std 0.053077
122 confirm fashion_compact G6 wall_time_sec_mean 4.839267
123 confirm fashion_wide G8 val_acc_mean 41.666667
124 confirm fashion_wide G8 val_acc_std 2.269016
125 confirm fashion_wide G8 val_nll_mean 1.615937
126 confirm fashion_wide G8 val_nll_std 0.036692
127 confirm fashion_wide G8 wall_time_sec_mean 9.499333
128 confirm fashion_wide G5 val_acc_mean 46.31
129 confirm fashion_wide G5 val_acc_std 7.568494
130 confirm fashion_wide G5 val_nll_mean 1.525747
131 confirm fashion_wide G5 val_nll_std 0.149706
132 confirm fashion_wide G5 wall_time_sec_mean 10.038833
133 feasibility mnist_compact G8 execution_feasible True
134 feasibility mnist_compact G8 optimization_feasible True
135 feasibility mnist_compact G8 mean_val_nll 1.518089
136 feasibility mnist_compact G8 mean_val_acc 49.1533
137 feasibility mnist_compact G6 execution_feasible True
138 feasibility mnist_compact G6 optimization_feasible True
139 feasibility mnist_compact G6 mean_val_nll 1.642081
140 feasibility mnist_compact G6 mean_val_acc 45.9667
141 feasibility mnist_wide G8 execution_feasible True
142 feasibility mnist_wide G8 optimization_feasible True
143 feasibility mnist_wide G8 mean_val_nll 1.733351
144 feasibility mnist_wide G8 mean_val_acc 41.03
145 feasibility mnist_wide G5 execution_feasible True
146 feasibility mnist_wide G5 optimization_feasible True
147 feasibility mnist_wide G5 mean_val_nll 1.721259
148 feasibility mnist_wide G5 mean_val_acc 43.86
149 feasibility fashion_compact G8 execution_feasible True
150 feasibility fashion_compact G8 optimization_feasible True
151 feasibility fashion_compact G8 mean_val_nll 1.511217
152 feasibility fashion_compact G8 mean_val_acc 47.0033
153 feasibility fashion_compact G6 execution_feasible True
154 feasibility fashion_compact G6 optimization_feasible True
155 feasibility fashion_compact G6 mean_val_nll 1.584443
156 feasibility fashion_compact G6 mean_val_acc 45.6167
157 feasibility fashion_wide G8 execution_feasible True
158 feasibility fashion_wide G8 optimization_feasible True
159 feasibility fashion_wide G8 mean_val_nll 1.615937
160 feasibility fashion_wide G8 mean_val_acc 41.6667
161 feasibility fashion_wide G5 execution_feasible True
162 feasibility fashion_wide G5 optimization_feasible True
163 feasibility fashion_wide G5 mean_val_nll 1.525747
164 feasibility fashion_wide G5 mean_val_acc 46.31
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
protocol_version,MNIST-PSO-RAW-V6 1.0.0
--- Phase B Screen Results ---
config_id,description,val_nll,val_acc_%,queries,sample_evals,wall_time_sec
G0,exact V5 control,1.183117,67.73,4800,9600000,8.4356
G1,isolate anisotropic per-tensor scaling,1.437604,62.32,4800,9600000,6.6136
G2,isolate nonzero launch velocity,1.296105,60.72,4800,9600000,6.6528
G3,isolate mutation,1.130839,68.07,4800,9600000,6.4943
G4,velocity x mutation interaction,1.11628,65.76,4800,9600000,6.4837
G5,test sufficient bound expansion,0.90151,70.93,4800,9600000,6.76
G6,test broader normalized initialization,0.96129,69.56,4800,9600000,6.7408
G7,isolate antithetic position coupling against G2,1.291266,58.79,4800,9600000,6.4273
G8,retained semantic control (public Optimizer),0.883557,70.99,4800,9600000,8.7632
--- Phase B Confirmation Aggregates ---
config_id,mean_val_acc_%,std_val_acc,mean_val_nll,std_val_nll,num_seeds
G0,79.526667,0.576397,0.689687,0.008079,3
G1,76.953333,2.490067,0.833759,0.123667,3
G5,84.236667,0.903678,0.510297,0.024899,3
G6,84.25,0.219317,0.503809,0.015789,3
G8,84.916667,0.988804,0.48144,0.029447,3
1 protocol_version,MNIST-PSO-RAW-V6 1.0.0
2 --- Phase B Screen Results ---
3 config_id,description,val_nll,val_acc_%,queries,sample_evals,wall_time_sec
4 G0,exact V5 control,1.183117,67.73,4800,9600000,8.4356
5 G1,isolate anisotropic per-tensor scaling,1.437604,62.32,4800,9600000,6.6136
6 G2,isolate nonzero launch velocity,1.296105,60.72,4800,9600000,6.6528
7 G3,isolate mutation,1.130839,68.07,4800,9600000,6.4943
8 G4,velocity x mutation interaction,1.11628,65.76,4800,9600000,6.4837
9 G5,test sufficient bound expansion,0.90151,70.93,4800,9600000,6.76
10 G6,test broader normalized initialization,0.96129,69.56,4800,9600000,6.7408
11 G7,isolate antithetic position coupling against G2,1.291266,58.79,4800,9600000,6.4273
12 G8,retained semantic control (public Optimizer),0.883557,70.99,4800,9600000,8.7632
13 --- Phase B Confirmation Aggregates ---
14 config_id,mean_val_acc_%,std_val_acc,mean_val_nll,std_val_nll,num_seeds
15 G0,79.526667,0.576397,0.689687,0.008079,3
16 G1,76.953333,2.490067,0.833759,0.123667,3
17 G5,84.236667,0.903678,0.510297,0.024899,3
18 G6,84.25,0.219317,0.503809,0.015789,3
19 G8,84.916667,0.988804,0.48144,0.029447,3
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
variant_id,phase,split_seed,workload_id,baseline_method,baseline_acc,candidate_acc,acc_gain_pp,baseline_nll,candidate_nll,nll_reduction_fraction,state_ratio,score,pass,is_best_observed,candidate_path,evaluation_path
iteration-0001-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260905,mnist_wide,G5,46.656667,46.04,-0.6166669999999996,1.677872,1.675644,0.0013278724479579603,0.5,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260905,fashion_wide,G5,48.516667,45.01,-3.506667,1.490304,1.584923,-0.06348973095422142,0.5,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260906,mnist_wide,G5,44.936667,44.83,-0.10666700000000162,1.665007,1.749135,-0.050527114901018556,0.5,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0001-development,development,20260906,fashion_wide,G5,48.37,41.193333,-7.176666999999995,1.490657,1.707819,-0.14568207173078723,0.5,-289.2921035434933,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
iteration-0002-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260905,mnist_wide,G5,46.656667,45.193333,-1.4633339999999961,1.677872,1.733563,-0.03319144726176963,0.5,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260905,fashion_wide,G5,48.516667,47.223333,-1.2933340000000015,1.490304,1.536216,-0.03080713733573818,0.5,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260906,mnist_wide,G5,44.936667,45.09,0.1533330000000035,1.665007,1.734207,-0.04156138682900441,0.5,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0002-development,development,20260906,fashion_wide,G5,48.37,46.813333,-1.5566669999999974,1.490657,1.475127,0.010418224984016995,0.5,-186.3459040597986,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
iteration-0003-replica1-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260905,mnist_wide,G5,46.656667,44.923333,-1.7333339999999993,1.677872,1.728906,-0.030415907768888223,0.5,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260905,fashion_wide,G5,48.516667,47.176667,-1.3399999999999963,1.490304,1.56267,-0.048557878124194744,0.5,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260906,mnist_wide,G5,44.936667,42.113333,-2.8233340000000027,1.665007,1.713134,-0.028904983582651624,0.5,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica1-development,development,20260906,fashion_wide,G5,48.37,46.433333,-1.936667,1.490657,1.581386,-0.06086510847230454,0.5,-287.7250973286179,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
iteration-0003-replica2-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260905,mnist_wide,G5,46.656667,46.01,-0.6466670000000008,1.677872,1.72888,-0.0304004119503752,0.5,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260905,fashion_wide,G5,48.516667,48.613333,0.09666599999999903,1.490304,1.48049,0.006585233616765431,0.5,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260906,mnist_wide,G5,44.936667,41.556667,-3.3800000000000026,1.665007,1.801698,-0.08209635154687045,0.5,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0003-replica2-development,development,20260906,fashion_wide,G5,48.37,45.913333,-2.456666999999996,1.490657,1.516614,-0.017413127231817923,0.5,-286.97652369317115,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
iteration-0004-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260905,mnist_wide,G5,46.656667,44.763333,-1.8933339999999959,1.677872,1.698999,-0.012591544527830428,0.5,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260905,fashion_wide,G5,48.516667,44.816667,-3.6999999999999957,1.490304,1.582588,-0.06192293652838617,0.5,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260906,mnist_wide,G5,44.936667,38.946667,-5.990000000000002,1.665007,1.819662,-0.09288549537629572,0.5,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0004-development,development,20260906,fashion_wide,G5,48.37,43.9,-4.469999999999999,1.490657,1.658117,-0.11233972671110803,0.5,-390.14004501856266,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
iteration-0005-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260905,mnist_wide,G5,46.656667,43.723333,-2.933334000000002,1.677872,1.675155,0.0016193130346057866,0.5,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260905,fashion_wide,G5,48.516667,45.45,-3.0666669999999954,1.490304,1.441667,0.03263562333591002,0.5,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260906,mnist_wide,G5,44.936667,42.543333,-2.393334000000003,1.665007,1.740544,-0.04536737683385127,0.5,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0005-development,development,20260906,fashion_wide,G5,48.37,47.65,-0.7199999999999989,1.490657,1.454402,0.02432149045689245,0.5,-185.61068572934795,False,True,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
iteration-0006-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260905,mnist_wide,G5,46.656667,42.006667,-4.649999999999999,1.677872,1.749895,-0.04292520525999596,0.5,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260905,fashion_wide,G5,48.516667,49.08,0.5633330000000001,1.490304,1.466855,0.015734373657991962,0.5,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260906,mnist_wide,G5,44.936667,40.84,-4.0966669999999965,1.665007,1.73617,-0.04274036085133581,0.5,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0006-development,development,20260906,fashion_wide,G5,48.37,46.946667,-1.4233329999999995,1.490657,1.508747,-0.012135588535793386,0.5,-186.8633001166316,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
iteration-0007-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260905,mnist_wide,G5,46.656667,41.05,-5.606667000000002,1.677872,1.794261,-0.06936703157332626,0.5,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260905,fashion_wide,G5,48.516667,42.513333,-6.003333999999995,1.490304,1.615206,-0.08380974619943303,0.5,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260906,mnist_wide,G5,44.936667,40.666667,-4.270000000000003,1.665007,1.764515,-0.059764313303187405,0.5,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0007-development,development,20260906,fashion_wide,G5,48.37,39.916667,-8.453333,1.490657,1.609595,-0.07978897895357565,0.5,-391.33742460463645,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
iteration-0008-development,development,20260905,mnist_compact,G8,50.713333,50.723333,0.00999999999999801,1.534298,1.492805,0.0270436381980554,0.4918032786885246,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260905,mnist_wide,G5,46.656667,41.14,-5.516666999999998,1.677872,1.748292,-0.041969828449369154,0.5,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260905,fashion_compact,G8,46.366667,49.576667,3.210000000000001,1.497844,1.400574,0.06494000710354347,0.4918032786885246,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260905,fashion_wide,G5,48.516667,45.726667,-2.789999999999999,1.490304,1.558163,-0.04553366293051612,0.5,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260906,mnist_compact,G8,46.81,51.663333,4.853332999999999,1.58986,1.436337,0.09656384838916639,0.4918032786885246,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260906,mnist_wide,G5,44.936667,39.596667,-5.340000000000003,1.665007,1.763671,-0.059257408527411654,0.5,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260906,fashion_compact,G8,44.193333,48.753333,4.559999999999995,1.553097,1.375751,0.11418861796784104,0.4918032786885246,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
iteration-0008-development,development,20260906,fashion_wide,G5,48.37,43.246667,-5.123332999999995,1.490657,1.649349,-0.10645775654627461,0.5,-390.14811518493707,False,False,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json,.omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
1 variant_id phase split_seed workload_id baseline_method baseline_acc candidate_acc acc_gain_pp baseline_nll candidate_nll nll_reduction_fraction state_ratio score pass is_best_observed candidate_path evaluation_path
2 iteration-0001-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
3 iteration-0001-development development 20260905 mnist_wide G5 46.656667 46.04 -0.6166669999999996 1.677872 1.675644 0.0013278724479579603 0.5 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
4 iteration-0001-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
5 iteration-0001-development development 20260905 fashion_wide G5 48.516667 45.01 -3.506667 1.490304 1.584923 -0.06348973095422142 0.5 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
6 iteration-0001-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
7 iteration-0001-development development 20260906 mnist_wide G5 44.936667 44.83 -0.10666700000000162 1.665007 1.749135 -0.050527114901018556 0.5 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
8 iteration-0001-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
9 iteration-0001-development development 20260906 fashion_wide G5 48.37 41.193333 -7.176666999999995 1.490657 1.707819 -0.14568207173078723 0.5 -289.2921035434933 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0001-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0001-development.json
10 iteration-0002-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
11 iteration-0002-development development 20260905 mnist_wide G5 46.656667 45.193333 -1.4633339999999961 1.677872 1.733563 -0.03319144726176963 0.5 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
12 iteration-0002-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
13 iteration-0002-development development 20260905 fashion_wide G5 48.516667 47.223333 -1.2933340000000015 1.490304 1.536216 -0.03080713733573818 0.5 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
14 iteration-0002-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
15 iteration-0002-development development 20260906 mnist_wide G5 44.936667 45.09 0.1533330000000035 1.665007 1.734207 -0.04156138682900441 0.5 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
16 iteration-0002-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
17 iteration-0002-development development 20260906 fashion_wide G5 48.37 46.813333 -1.5566669999999974 1.490657 1.475127 0.010418224984016995 0.5 -186.3459040597986 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0002-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0002-development.json
18 iteration-0003-replica1-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
19 iteration-0003-replica1-development development 20260905 mnist_wide G5 46.656667 44.923333 -1.7333339999999993 1.677872 1.728906 -0.030415907768888223 0.5 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
20 iteration-0003-replica1-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
21 iteration-0003-replica1-development development 20260905 fashion_wide G5 48.516667 47.176667 -1.3399999999999963 1.490304 1.56267 -0.048557878124194744 0.5 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
22 iteration-0003-replica1-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
23 iteration-0003-replica1-development development 20260906 mnist_wide G5 44.936667 42.113333 -2.8233340000000027 1.665007 1.713134 -0.028904983582651624 0.5 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
24 iteration-0003-replica1-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
25 iteration-0003-replica1-development development 20260906 fashion_wide G5 48.37 46.433333 -1.936667 1.490657 1.581386 -0.06086510847230454 0.5 -287.7250973286179 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica1-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica1-development.json
26 iteration-0003-replica2-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
27 iteration-0003-replica2-development development 20260905 mnist_wide G5 46.656667 46.01 -0.6466670000000008 1.677872 1.72888 -0.0304004119503752 0.5 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
28 iteration-0003-replica2-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
29 iteration-0003-replica2-development development 20260905 fashion_wide G5 48.516667 48.613333 0.09666599999999903 1.490304 1.48049 0.006585233616765431 0.5 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
30 iteration-0003-replica2-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
31 iteration-0003-replica2-development development 20260906 mnist_wide G5 44.936667 41.556667 -3.3800000000000026 1.665007 1.801698 -0.08209635154687045 0.5 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
32 iteration-0003-replica2-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
33 iteration-0003-replica2-development development 20260906 fashion_wide G5 48.37 45.913333 -2.456666999999996 1.490657 1.516614 -0.017413127231817923 0.5 -286.97652369317115 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0003-replica2-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0003-replica2-development.json
34 iteration-0004-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
35 iteration-0004-development development 20260905 mnist_wide G5 46.656667 44.763333 -1.8933339999999959 1.677872 1.698999 -0.012591544527830428 0.5 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
36 iteration-0004-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
37 iteration-0004-development development 20260905 fashion_wide G5 48.516667 44.816667 -3.6999999999999957 1.490304 1.582588 -0.06192293652838617 0.5 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
38 iteration-0004-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
39 iteration-0004-development development 20260906 mnist_wide G5 44.936667 38.946667 -5.990000000000002 1.665007 1.819662 -0.09288549537629572 0.5 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
40 iteration-0004-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
41 iteration-0004-development development 20260906 fashion_wide G5 48.37 43.9 -4.469999999999999 1.490657 1.658117 -0.11233972671110803 0.5 -390.14004501856266 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0004-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0004-development.json
42 iteration-0005-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
43 iteration-0005-development development 20260905 mnist_wide G5 46.656667 43.723333 -2.933334000000002 1.677872 1.675155 0.0016193130346057866 0.5 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
44 iteration-0005-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
45 iteration-0005-development development 20260905 fashion_wide G5 48.516667 45.45 -3.0666669999999954 1.490304 1.441667 0.03263562333591002 0.5 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
46 iteration-0005-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
47 iteration-0005-development development 20260906 mnist_wide G5 44.936667 42.543333 -2.393334000000003 1.665007 1.740544 -0.04536737683385127 0.5 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
48 iteration-0005-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
49 iteration-0005-development development 20260906 fashion_wide G5 48.37 47.65 -0.7199999999999989 1.490657 1.454402 0.02432149045689245 0.5 -185.61068572934795 False True .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0005-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0005-development.json
50 iteration-0006-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
51 iteration-0006-development development 20260905 mnist_wide G5 46.656667 42.006667 -4.649999999999999 1.677872 1.749895 -0.04292520525999596 0.5 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
52 iteration-0006-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
53 iteration-0006-development development 20260905 fashion_wide G5 48.516667 49.08 0.5633330000000001 1.490304 1.466855 0.015734373657991962 0.5 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
54 iteration-0006-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
55 iteration-0006-development development 20260906 mnist_wide G5 44.936667 40.84 -4.0966669999999965 1.665007 1.73617 -0.04274036085133581 0.5 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
56 iteration-0006-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
57 iteration-0006-development development 20260906 fashion_wide G5 48.37 46.946667 -1.4233329999999995 1.490657 1.508747 -0.012135588535793386 0.5 -186.8633001166316 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0006-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0006-development.json
58 iteration-0007-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
59 iteration-0007-development development 20260905 mnist_wide G5 46.656667 41.05 -5.606667000000002 1.677872 1.794261 -0.06936703157332626 0.5 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
60 iteration-0007-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
61 iteration-0007-development development 20260905 fashion_wide G5 48.516667 42.513333 -6.003333999999995 1.490304 1.615206 -0.08380974619943303 0.5 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
62 iteration-0007-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
63 iteration-0007-development development 20260906 mnist_wide G5 44.936667 40.666667 -4.270000000000003 1.665007 1.764515 -0.059764313303187405 0.5 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
64 iteration-0007-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
65 iteration-0007-development development 20260906 fashion_wide G5 48.37 39.916667 -8.453333 1.490657 1.609595 -0.07978897895357565 0.5 -391.33742460463645 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0007-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0007-development.json
66 iteration-0008-development development 20260905 mnist_compact G8 50.713333 50.723333 0.00999999999999801 1.534298 1.492805 0.0270436381980554 0.4918032786885246 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
67 iteration-0008-development development 20260905 mnist_wide G5 46.656667 41.14 -5.516666999999998 1.677872 1.748292 -0.041969828449369154 0.5 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
68 iteration-0008-development development 20260905 fashion_compact G8 46.366667 49.576667 3.210000000000001 1.497844 1.400574 0.06494000710354347 0.4918032786885246 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
69 iteration-0008-development development 20260905 fashion_wide G5 48.516667 45.726667 -2.789999999999999 1.490304 1.558163 -0.04553366293051612 0.5 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
70 iteration-0008-development development 20260906 mnist_compact G8 46.81 51.663333 4.853332999999999 1.58986 1.436337 0.09656384838916639 0.4918032786885246 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
71 iteration-0008-development development 20260906 mnist_wide G5 44.936667 39.596667 -5.340000000000003 1.665007 1.763671 -0.059257408527411654 0.5 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
72 iteration-0008-development development 20260906 fashion_compact G8 44.193333 48.753333 4.559999999999995 1.553097 1.375751 0.11418861796784104 0.4918032786885246 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
73 iteration-0008-development development 20260906 fashion_wide G5 48.37 43.246667 -5.123332999999995 1.490657 1.649349 -0.10645775654627461 0.5 -390.14811518493707 False False .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/candidates/iteration-0008-development.json .omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z/evaluations/iteration-0008-development.json
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
Workload,Phase,Method,Accuracy,NLL,Brier,ECE,Margin,WallTimeSeconds,ParameterMultiplier,InferenceMultiplier
mnist,validation,reference_single_10e,98.2300,0.060105,0.027982,0.003458,0.969858,0.0000,1.0,1.0
mnist,validation,best_single_10e,98.2900,0.054029,0.025526,0.002795,0.971419,0.0000,1.0,1.0
mnist,validation,single_50e,98.5200,0.073988,0.024922,0.009463,0.989411,0.0000,1.0,1.0
mnist,validation,uniform_ensemble,98.6300,0.046385,0.021527,0.006228,0.965339,0.0000,5.0,5.0
mnist,validation,uniform_temperature,98.6300,0.045355,0.021099,0.002518,0.972273,0.0412,5.0,5.0
mnist,validation,slsqp_weights,98.6000,0.045902,0.021307,0.005994,0.965581,0.0097,5.0,5.0
mnist,validation,pso_weights,98.6100,0.045902,0.021307,0.005893,0.965582,1.8925,5.0,5.0
mnist,official_test,reference_single_10e,98.4700,0.044991,0.022366,0.003516,0.970575,0.0000,1.0,1.0
mnist,official_test,best_single_10e,98.5500,0.044944,0.022052,0.002515,0.974650,0.0000,1.0,1.0
mnist,official_test,single_50e,98.6000,0.062102,0.022912,0.008283,0.988829,0.0000,1.0,1.0
mnist,official_test,uniform_ensemble,98.8600,0.036184,0.018010,0.006311,0.968415,0.0000,5.0,5.0
mnist,official_test,uniform_temperature,98.8600,0.034129,0.017658,0.003340,0.974869,0.0000,5.0,5.0
mnist,official_test,slsqp_weights,98.8300,0.036179,0.017989,0.005818,0.969195,0.0000,5.0,5.0
mnist,official_test,pso_weights,98.8300,0.036178,0.017989,0.005817,0.969196,0.0000,5.0,5.0
fashion_mnist,validation,reference_single_10e,89.4700,0.303799,0.150671,0.012197,0.808017,0.0000,1.0,1.0
fashion_mnist,validation,best_single_10e,89.4700,0.303799,0.150671,0.012197,0.808017,0.0000,1.0,1.0
fashion_mnist,validation,single_50e,90.3200,0.289660,0.142115,0.024556,0.869484,0.0000,1.0,1.0
fashion_mnist,validation,uniform_ensemble,90.2800,0.286751,0.142881,0.023702,0.795588,0.0000,5.0,5.0
fashion_mnist,validation,uniform_temperature,90.2800,0.285048,0.141557,0.011886,0.814383,0.0414,5.0,5.0
fashion_mnist,validation,slsqp_weights,90.4200,0.285338,0.142289,0.023705,0.798413,0.0104,5.0,5.0
fashion_mnist,validation,pso_weights,90.4200,0.285338,0.142289,0.023661,0.798397,1.8963,5.0,5.0
fashion_mnist,official_test,reference_single_10e,88.9000,0.314516,0.161465,0.005320,0.805307,0.0000,1.0,1.0
fashion_mnist,official_test,best_single_10e,88.9000,0.314516,0.161465,0.005320,0.805307,0.0000,1.0,1.0
fashion_mnist,official_test,single_50e,89.9300,0.302348,0.148289,0.026401,0.865728,0.0000,1.0,1.0
fashion_mnist,official_test,uniform_ensemble,89.6500,0.293522,0.151400,0.019111,0.791698,0.0000,5.0,5.0
fashion_mnist,official_test,uniform_temperature,89.6500,0.291996,0.150581,0.007597,0.810594,0.0000,5.0,5.0
fashion_mnist,official_test,slsqp_weights,89.5400,0.291696,0.150558,0.017186,0.794293,0.0000,5.0,5.0
fashion_mnist,official_test,pso_weights,89.5400,0.291700,0.150559,0.017378,0.794274,0.0000,5.0,5.0
1 Workload Phase Method Accuracy NLL Brier ECE Margin WallTimeSeconds ParameterMultiplier InferenceMultiplier
2 mnist validation reference_single_10e 98.2300 0.060105 0.027982 0.003458 0.969858 0.0000 1.0 1.0
3 mnist validation best_single_10e 98.2900 0.054029 0.025526 0.002795 0.971419 0.0000 1.0 1.0
4 mnist validation single_50e 98.5200 0.073988 0.024922 0.009463 0.989411 0.0000 1.0 1.0
5 mnist validation uniform_ensemble 98.6300 0.046385 0.021527 0.006228 0.965339 0.0000 5.0 5.0
6 mnist validation uniform_temperature 98.6300 0.045355 0.021099 0.002518 0.972273 0.0412 5.0 5.0
7 mnist validation slsqp_weights 98.6000 0.045902 0.021307 0.005994 0.965581 0.0097 5.0 5.0
8 mnist validation pso_weights 98.6100 0.045902 0.021307 0.005893 0.965582 1.8925 5.0 5.0
9 mnist official_test reference_single_10e 98.4700 0.044991 0.022366 0.003516 0.970575 0.0000 1.0 1.0
10 mnist official_test best_single_10e 98.5500 0.044944 0.022052 0.002515 0.974650 0.0000 1.0 1.0
11 mnist official_test single_50e 98.6000 0.062102 0.022912 0.008283 0.988829 0.0000 1.0 1.0
12 mnist official_test uniform_ensemble 98.8600 0.036184 0.018010 0.006311 0.968415 0.0000 5.0 5.0
13 mnist official_test uniform_temperature 98.8600 0.034129 0.017658 0.003340 0.974869 0.0000 5.0 5.0
14 mnist official_test slsqp_weights 98.8300 0.036179 0.017989 0.005818 0.969195 0.0000 5.0 5.0
15 mnist official_test pso_weights 98.8300 0.036178 0.017989 0.005817 0.969196 0.0000 5.0 5.0
16 fashion_mnist validation reference_single_10e 89.4700 0.303799 0.150671 0.012197 0.808017 0.0000 1.0 1.0
17 fashion_mnist validation best_single_10e 89.4700 0.303799 0.150671 0.012197 0.808017 0.0000 1.0 1.0
18 fashion_mnist validation single_50e 90.3200 0.289660 0.142115 0.024556 0.869484 0.0000 1.0 1.0
19 fashion_mnist validation uniform_ensemble 90.2800 0.286751 0.142881 0.023702 0.795588 0.0000 5.0 5.0
20 fashion_mnist validation uniform_temperature 90.2800 0.285048 0.141557 0.011886 0.814383 0.0414 5.0 5.0
21 fashion_mnist validation slsqp_weights 90.4200 0.285338 0.142289 0.023705 0.798413 0.0104 5.0 5.0
22 fashion_mnist validation pso_weights 90.4200 0.285338 0.142289 0.023661 0.798397 1.8963 5.0 5.0
23 fashion_mnist official_test reference_single_10e 88.9000 0.314516 0.161465 0.005320 0.805307 0.0000 1.0 1.0
24 fashion_mnist official_test best_single_10e 88.9000 0.314516 0.161465 0.005320 0.805307 0.0000 1.0 1.0
25 fashion_mnist official_test single_50e 89.9300 0.302348 0.148289 0.026401 0.865728 0.0000 1.0 1.0
26 fashion_mnist official_test uniform_ensemble 89.6500 0.293522 0.151400 0.019111 0.791698 0.0000 5.0 5.0
27 fashion_mnist official_test uniform_temperature 89.6500 0.291996 0.150581 0.007597 0.810594 0.0000 5.0 5.0
28 fashion_mnist official_test slsqp_weights 89.5400 0.291696 0.150558 0.017186 0.794293 0.0000 5.0 5.0
29 fashion_mnist official_test pso_weights 89.5400 0.291700 0.150559 0.017378 0.794274 0.0000 5.0 5.0
@@ -0,0 +1,683 @@
{
"protocol_version": "POST-TRAINING-PSO-ENSEMBLE 1.1.0",
"config": {
"iteration": 1,
"archived_iteration0_reference": {
"epochs": 50,
"queries_per_seed": 1500,
"sample_evaluations_per_seed": 15000000,
"reason": "wall_time_ratio_gate_exceeded"
},
"datasets": [
"mnist",
"fashion_mnist"
],
"split_seed": 20260904,
"search_samples": 50000,
"validation_samples": 10000,
"pool_seeds": [
201,
202,
203,
204,
205
],
"reference_single_seed": 201,
"equal_budget_single_epochs": 50,
"adam_lr": 0.001,
"adam_batch_size": 256,
"pso": {
"method": "constriction",
"evaluation": "full",
"renewal": "loss",
"particles": 30,
"epochs": 30,
"swarm_seeds": [
301,
302,
303
],
"queries_per_seed": 900,
"sample_evaluations_per_seed": 9000000,
"particle_bounds": [
-4.0,
4.0
],
"boundary_strategy": "reflect",
"velocity_limit_ratio": 0.1,
"initial_position_noise": 0.0
},
"device": "mps"
},
"workloads": {
"mnist": {
"provenance": {
"dataset_name": "MNIST",
"split_seed": 20260904,
"search_samples": 50000,
"validation_samples": 10000,
"normalization": {
"mean": 0.1307128667831421,
"std": 0.3081730008125305
},
"data_fingerprint": "861b07884cffefb9",
"split_fingerprint": "258b42dde7da6324"
},
"training": {
"architecture": "CompactCNN",
"parameters": 9098,
"pool_seeds": [
201,
202,
203,
204,
205
],
"pool_epochs_each": 10,
"adam_pool_epochs": 50,
"adam_lr": 0.001,
"adam_batch_size": 256,
"adam_pool_wall_time_seconds": 22.098886500985827,
"single_50e_epochs": 50,
"single_50e_wall_time_seconds": 22.06671220799035,
"model_fingerprints": {
"201": "046605462c4fc294",
"single_50e": "53ff4c61db919c1e",
"202": "cd3150799e0db43e",
"203": "1feb0f4dd7630ec2",
"204": "9dc0606cd8076cb2",
"205": "5bc16dc8f86cc869"
}
},
"validation_cache": {
"valid": true,
"pool_forward_passes": 5,
"long_single_forward_passes": 1,
"base_cnn_forward_passes_during_optimization": 0,
"shape": [
5,
10000,
10
],
"memory_bytes": 2400000,
"wall_time_seconds": 0.09316845799912699
},
"validation": {
"methods": {
"reference_single_10e": {
"accuracy": 98.23,
"nll": 0.060105,
"brier": 0.027982,
"ece": 0.003458,
"margin": 0.969858
},
"best_single_10e": {
"accuracy": 98.29,
"nll": 0.054029,
"brier": 0.025526,
"ece": 0.002795,
"margin": 0.971419,
"selected_seed": 205
},
"single_50e": {
"accuracy": 98.52,
"nll": 0.073988,
"brier": 0.024922,
"ece": 0.009463,
"margin": 0.989411
},
"uniform_ensemble": {
"accuracy": 98.63,
"nll": 0.046385,
"brier": 0.021527,
"ece": 0.006228,
"margin": 0.965339
},
"uniform_temperature": {
"fitted_temperature": 0.854572,
"wall_time_seconds": 0.04119049999280833,
"evaluations": 23,
"metrics": {
"accuracy": 98.63,
"nll": 0.045355,
"brier": 0.021099,
"ece": 0.002518,
"margin": 0.972273
}
},
"slsqp_weights": {
"weights": [
0.05767137130295378,
0.2585033373652759,
0.10841769748910005,
0.25254869759276993,
0.3228588962499004
],
"evaluations": 23,
"wall_time_seconds": 0.009686834004241973,
"success": true,
"message": "Optimization terminated successfully",
"metrics": {
"accuracy": 98.6,
"nll": 0.045902,
"brier": 0.021307,
"ece": 0.005994,
"margin": 0.965581
}
},
"pso_weights": {
"per_seed_runs": [
{
"seed": 301,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 1.8924723340023775,
"metrics": {
"accuracy": 98.61,
"nll": 0.045902,
"brier": 0.021307,
"ece": 0.005893,
"margin": 0.965582
},
"weights": [
0.057233214378356934,
0.2590548098087311,
0.10862385481595993,
0.25228697061538696,
0.3228012025356293
]
},
{
"seed": 302,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 1.7105932499980554,
"metrics": {
"accuracy": 98.6,
"nll": 0.045902,
"brier": 0.021306,
"ece": 0.006183,
"margin": 0.965578
},
"weights": [
0.058317527174949646,
0.25828817486763,
0.10924994200468063,
0.252756267786026,
0.3213881254196167
]
},
{
"seed": 303,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 1.8959678749961313,
"metrics": {
"accuracy": 98.61,
"nll": 0.045902,
"brier": 0.021307,
"ece": 0.005833,
"margin": 0.965583
},
"weights": [
0.056311722844839096,
0.2563577890396118,
0.11076968908309937,
0.25350210070610046,
0.3230587840080261
]
}
],
"selected_seed": 301,
"selected_weights": [
0.057233214378356934,
0.2590548098087311,
0.10862385481595993,
0.25228697061538696,
0.3228012025356293
],
"metrics": {
"accuracy": 98.61,
"nll": 0.045902,
"brier": 0.021307,
"ece": 0.005893,
"margin": 0.965582
},
"queries_per_seed": 900,
"sample_evaluations_per_seed": 9000000,
"total_queries": 2700,
"total_sample_evaluations": 27000000,
"median_one_seed_wall_time_seconds": 1.8924723340023775,
"total_wall_time_seconds": 5.499033458996564
}
}
},
"official_test_data_loaded_before_freeze": false,
"official_test_evaluations_before_freeze": 0,
"confirmation": {
"official_test_data_loaded": true,
"test_cache_counts": {
"dataset_loads": 1,
"pool_forward_passes": 5,
"long_single_forward_passes": 1,
"base_cnn_forward_passes_during_optimization": 0,
"memory_bytes": 2400000,
"wall_time_seconds": 0.11975424998672679
},
"frozen_methods": {
"selected_pso_seed": 301,
"selected_pso_weights": [
0.057233214378356934,
0.2590548098087311,
0.10862385481595993,
0.25228697061538696,
0.3228012025356293
],
"slsqp_weights": [
0.05767137130295378,
0.2585033373652759,
0.10841769748910005,
0.25254869759276993,
0.3228588962499004
],
"fitted_temperature": 0.854572
},
"methods": {
"reference_single_10e": {
"accuracy": 98.47,
"nll": 0.044991,
"brier": 0.022366,
"ece": 0.003516,
"margin": 0.970575
},
"best_single_10e": {
"accuracy": 98.55,
"nll": 0.044944,
"brier": 0.022052,
"ece": 0.002515,
"margin": 0.97465
},
"single_50e": {
"accuracy": 98.6,
"nll": 0.062102,
"brier": 0.022912,
"ece": 0.008283,
"margin": 0.988829
},
"uniform_ensemble": {
"accuracy": 98.86,
"nll": 0.036184,
"brier": 0.01801,
"ece": 0.006311,
"margin": 0.968415
},
"uniform_temperature": {
"accuracy": 98.86,
"nll": 0.034129,
"brier": 0.017658,
"ece": 0.00334,
"margin": 0.974869
},
"slsqp_weights": {
"accuracy": 98.83,
"nll": 0.036179,
"brier": 0.017989,
"ece": 0.005818,
"margin": 0.969195
},
"pso_weights": {
"accuracy": 98.83,
"nll": 0.036178,
"brier": 0.017989,
"ece": 0.005817,
"margin": 0.969196
}
},
"confirmation_gates": {
"all_values_finite": true,
"official_test_dataset_loads": 1,
"official_test_pool_forward_passes": 5,
"official_test_long_single_forward_passes": 1,
"maximum_pso_accuracy_regression_vs_uniform_pp": true,
"pso_nll_below_reference_single": true,
"maximum_pso_nll_regression_vs_equal_budget_single": true,
"pass": true
}
}
},
"fashion_mnist": {
"provenance": {
"dataset_name": "FashionMNIST",
"split_seed": 20260904,
"search_samples": 50000,
"validation_samples": 10000,
"normalization": {
"mean": 0.28573352098464966,
"std": 0.35278451442718506
},
"data_fingerprint": "b56a39e2abf7d9ad",
"split_fingerprint": "489c1a04fb44b59a"
},
"training": {
"architecture": "CompactCNN",
"parameters": 9098,
"pool_seeds": [
201,
202,
203,
204,
205
],
"pool_epochs_each": 10,
"adam_pool_epochs": 50,
"adam_lr": 0.001,
"adam_batch_size": 256,
"adam_pool_wall_time_seconds": 22.363562541009742,
"single_50e_epochs": 50,
"single_50e_wall_time_seconds": 22.406685083013144,
"model_fingerprints": {
"201": "8189d6d2cce8bde8",
"single_50e": "5a5b8444219bf301",
"202": "a5492b64bfda73cd",
"203": "664e02f11e62031f",
"204": "be2a23fca4153703",
"205": "d3a37cfa6ee482bf"
}
},
"validation_cache": {
"valid": true,
"pool_forward_passes": 5,
"long_single_forward_passes": 1,
"base_cnn_forward_passes_during_optimization": 0,
"shape": [
5,
10000,
10
],
"memory_bytes": 2400000,
"wall_time_seconds": 0.08030625000537839
},
"validation": {
"methods": {
"reference_single_10e": {
"accuracy": 89.47,
"nll": 0.303799,
"brier": 0.150671,
"ece": 0.012197,
"margin": 0.808017
},
"best_single_10e": {
"accuracy": 89.47,
"nll": 0.303799,
"brier": 0.150671,
"ece": 0.012197,
"margin": 0.808017,
"selected_seed": 201
},
"single_50e": {
"accuracy": 90.32,
"nll": 0.28966,
"brier": 0.142115,
"ece": 0.024556,
"margin": 0.869484
},
"uniform_ensemble": {
"accuracy": 90.28,
"nll": 0.286751,
"brier": 0.142881,
"ece": 0.023702,
"margin": 0.795588
},
"uniform_temperature": {
"fitted_temperature": 0.906309,
"wall_time_seconds": 0.04135037500236649,
"evaluations": 21,
"metrics": {
"accuracy": 90.28,
"nll": 0.285048,
"brier": 0.141557,
"ece": 0.011886,
"margin": 0.814383
}
},
"slsqp_weights": {
"weights": [
0.23910131729416598,
0.2493341982620314,
0.18054455195387528,
0.021147090032828317,
0.30987284245709906
],
"evaluations": 23,
"wall_time_seconds": 0.010389625007519498,
"success": true,
"message": "Optimization terminated successfully",
"metrics": {
"accuracy": 90.42,
"nll": 0.285338,
"brier": 0.142289,
"ece": 0.023705,
"margin": 0.798413
}
},
"pso_weights": {
"per_seed_runs": [
{
"seed": 301,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 1.8963403329980792,
"metrics": {
"accuracy": 90.42,
"nll": 0.285338,
"brier": 0.142289,
"ece": 0.023661,
"margin": 0.798397
},
"weights": [
0.2385721057653427,
0.24956248700618744,
0.1805897355079651,
0.021864112466573715,
0.30941155552864075
]
},
{
"seed": 302,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 1.7477522080007475,
"metrics": {
"accuracy": 90.43,
"nll": 0.285338,
"brier": 0.142286,
"ece": 0.023912,
"margin": 0.79841
},
"weights": [
0.2415684461593628,
0.2483692765235901,
0.18002673983573914,
0.02121216244995594,
0.30882343649864197
]
},
{
"seed": 303,
"queries": 900,
"sample_evaluations": 9000000,
"wall_time_seconds": 2.0081020840007113,
"metrics": {
"accuracy": 90.41,
"nll": 0.285338,
"brier": 0.14229,
"ece": 0.023601,
"margin": 0.798444
},
"weights": [
0.23893120884895325,
0.24851834774017334,
0.18254171311855316,
0.019977180287241936,
0.31003159284591675
]
}
],
"selected_seed": 301,
"selected_weights": [
0.2385721057653427,
0.24956248700618744,
0.1805897355079651,
0.021864112466573715,
0.30941155552864075
],
"metrics": {
"accuracy": 90.42,
"nll": 0.285338,
"brier": 0.142289,
"ece": 0.023661,
"margin": 0.798397
},
"queries_per_seed": 900,
"sample_evaluations_per_seed": 9000000,
"total_queries": 2700,
"total_sample_evaluations": 27000000,
"median_one_seed_wall_time_seconds": 1.8963403329980792,
"total_wall_time_seconds": 5.652194624999538
}
}
},
"official_test_data_loaded_before_freeze": false,
"official_test_evaluations_before_freeze": 0,
"confirmation": {
"official_test_data_loaded": true,
"test_cache_counts": {
"dataset_loads": 1,
"pool_forward_passes": 5,
"long_single_forward_passes": 1,
"base_cnn_forward_passes_during_optimization": 0,
"memory_bytes": 2400000,
"wall_time_seconds": 0.11122124998655636
},
"frozen_methods": {
"selected_pso_seed": 301,
"selected_pso_weights": [
0.2385721057653427,
0.24956248700618744,
0.1805897355079651,
0.021864112466573715,
0.30941155552864075
],
"slsqp_weights": [
0.23910131729416598,
0.2493341982620314,
0.18054455195387528,
0.021147090032828317,
0.30987284245709906
],
"fitted_temperature": 0.906309
},
"methods": {
"reference_single_10e": {
"accuracy": 88.9,
"nll": 0.314516,
"brier": 0.161465,
"ece": 0.00532,
"margin": 0.805307
},
"best_single_10e": {
"accuracy": 88.9,
"nll": 0.314516,
"brier": 0.161465,
"ece": 0.00532,
"margin": 0.805307
},
"single_50e": {
"accuracy": 89.93,
"nll": 0.302348,
"brier": 0.148289,
"ece": 0.026401,
"margin": 0.865728
},
"uniform_ensemble": {
"accuracy": 89.65,
"nll": 0.293522,
"brier": 0.1514,
"ece": 0.019111,
"margin": 0.791698
},
"uniform_temperature": {
"accuracy": 89.65,
"nll": 0.291996,
"brier": 0.150581,
"ece": 0.007597,
"margin": 0.810594
},
"slsqp_weights": {
"accuracy": 89.54,
"nll": 0.291696,
"brier": 0.150558,
"ece": 0.017186,
"margin": 0.794293
},
"pso_weights": {
"accuracy": 89.54,
"nll": 0.2917,
"brier": 0.150559,
"ece": 0.017378,
"margin": 0.794274
}
},
"confirmation_gates": {
"all_values_finite": true,
"official_test_dataset_loads": 1,
"official_test_pool_forward_passes": 5,
"official_test_long_single_forward_passes": 1,
"maximum_pso_accuracy_regression_vs_uniform_pp": true,
"pso_nll_below_reference_single": true,
"maximum_pso_nll_regression_vs_equal_budget_single": true,
"pass": true
}
}
}
},
"development_pass": true,
"development_gates": {
"pass": true,
"failed_hard_gate_count": 0,
"gate_results": {
"all_values_finite": true,
"validation_pool_forward_passes_exact": true,
"optimization_base_model_forward_passes": true,
"official_test_data_loaded_before_freeze": true,
"slsqp_solver_success": true,
"query_and_sample_accounting_exact": true,
"maximum_pso_nll_regression_vs_uniform": true,
"maximum_pso_accuracy_regression_vs_uniform_pp": true,
"pso_nll_below_reference_single": true,
"maximum_pso_nll_regression_vs_equal_budget_single": true,
"maximum_relative_pso_nll_gap_vs_slsqp": true,
"cross_dataset_mean_relative_pso_nll_reduction_vs_uniform_minimum": true,
"maximum_median_one_seed_pso_to_pool_training_wall_ratio": true
},
"issues": []
},
"policy_frozen": true,
"official_test_data_loaded": true,
"official_test_evaluations_before_freeze": 0,
"post_test_tuning_or_reruns": 0,
"resource_totals": {
"adam_pool_epochs": 100,
"adam_pool_wall_time_seconds": 44.46244904199557,
"single_50e_wall_time_seconds": 44.473397291003494,
"validation_cache_forward_passes": 12,
"pso_total_queries": 5400,
"pso_total_sample_evaluations": 54000000,
"pso_research_wall_time_seconds": 11.151228083996102,
"pso_production_wall_time_seconds": 3.7888126670004567,
"pso_to_pool_wall_ratio": 0.0852162664640409,
"slsqp_total_evaluations": 46,
"slsqp_total_wall_time_seconds": 0.02007645901176147,
"official_test_cache_forward_passes": 12
}
}
@@ -0,0 +1,53 @@
{
"evaluator_version": "POST-TRAINING-PSO-ENSEMBLE-EVALUATOR 1.2.0",
"pass": true,
"score": 22.55299842776338,
"development_pass": true,
"confirmation_pass": true,
"failed_hard_gate_count": 0,
"issues": {
"schema": [],
"config": [],
"finite": [],
"weights": [],
"accounting": [],
"leakage": [],
"tuning": [],
"slsqp": [],
"consistency": [],
"gates": []
},
"development_gates": {
"all_values_finite": true,
"simplex_tolerance": true,
"validation_pool_forward_passes_each_dataset": true,
"optimization_base_model_forward_passes": true,
"official_test_data_loaded_before_freeze": true,
"official_test_evaluations_before_freeze": true,
"query_and_sample_accounting_exact": true,
"maximum_pso_nll_regression_vs_uniform": true,
"maximum_pso_accuracy_regression_vs_uniform_pp": true,
"pso_nll_below_reference_single": true,
"maximum_pso_nll_regression_vs_equal_budget_single": true,
"maximum_relative_pso_nll_gap_vs_slsqp": true,
"cross_dataset_mean_relative_pso_nll_reduction_vs_uniform_minimum": true,
"maximum_median_one_seed_pso_to_pool_training_wall_ratio": true
},
"confirmation_gates": {
"all_values_finite": true,
"official_test_dataset_loads_each_dataset": true,
"official_test_pool_forward_passes_each_dataset": true,
"official_test_long_single_forward_passes_each_dataset": true,
"frozen_policy_consistency": true,
"maximum_pso_accuracy_regression_vs_uniform_pp": true,
"pso_nll_below_reference_single": true,
"maximum_pso_nll_regression_vs_equal_budget_single": true,
"post_test_tuning_or_reruns": true
},
"metrics": {
"mean_val_relative_nll_reduction_vs_equal_budget_single": 0.197261519715641,
"mean_val_accuracy_gain_vs_equal_budget_single_pp": 0.09500000000000597,
"mean_test_relative_nll_reduction_vs_equal_budget_single": 0.2263299842776338,
"mean_test_accuracy_gain_vs_equal_budget_single_pp": -0.0799999999999983
}
}
-15
View File
@@ -1,15 +0,0 @@
name: pso
channels:
- conda-forge
- defaults
dependencies:
- cudatoolkit=11.2
- cudnn=8.1.0
- pandas=1.5.3
- pip=23.0.1
- python=3.9.16
- tqdm=4.65.0
- pip:
- numpy==1.25.0
- tensorflow==2.11.0
- tensorboard==2.11.0
+281 -2440
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

+26 -19
View File
@@ -1,22 +1,29 @@
import tensorflow as tf from ._version import __version__
import os from .optimizer import Optimizer
from .optimizer import Optimizer as optimizer from .particle import Particle
from .particle import Particle as particle from .plugins import (
BasePlugin,
__version__ = "1.0.5.1" InitializationPlugin,
EvaluationPlugin,
print("pso2keras version : " + __version__) MovementPlugin,
ConvergencePlugin,
gpus = tf.config.experimental.list_physical_devices("GPU") RefinementPlugin,
if gpus: PluginMetadata,
try: SwarmState,
tf.config.experimental.set_memory_growth(gpus[0], True) available_plugins,
except RuntimeError as r: )
print(r)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
__all__ = [ __all__ = [
"optimizer", "Optimizer",
"particle", "Particle",
"__version__",
"BasePlugin",
"InitializationPlugin",
"EvaluationPlugin",
"MovementPlugin",
"ConvergencePlugin",
"RefinementPlugin",
"PluginMetadata",
"SwarmState",
"available_plugins",
] ]
+22
View File
@@ -0,0 +1,22 @@
from pathlib import Path
import sys
from importlib.metadata import PackageNotFoundError, version
try:
__version__ = version("pso2keras")
except PackageNotFoundError:
pyproject_path = Path(__file__).resolve().parent.parent / "pyproject.toml"
if pyproject_path.exists():
if sys.version_info >= (3, 11):
import tomllib
else:
try:
import tomllib # type: ignore
except ImportError:
import tomli as tomllib # type: ignore
with pyproject_path.open("rb") as f:
data = tomllib.load(f)
__version__ = data["project"]["version"]
else:
__version__ = "0.0.0"
+88
View File
@@ -0,0 +1,88 @@
import collections
import torch
import torch.nn as nn
class ParameterCodec:
"""
Private parameter encoder/decoder for flattening and reconstructing PyTorch nn.Module parameters.
"""
def __init__(self, model: nn.Module):
if not isinstance(model, nn.Module):
raise TypeError("model must be an instance of torch.nn.Module")
params = list(model.named_parameters())
if not params:
raise ValueError("model contains zero trainable parameters")
self.names: list[str] = []
self.shapes: list[tuple[int, ...]] = []
self.numels: list[int] = []
dtypes: set[torch.dtype] = set()
total_size = 0
for name, p in params:
if not p.dtype.is_floating_point:
raise ValueError(
f"Parameter '{name}' has non-floating dtype {p.dtype}. Only floating-point parameters are supported."
)
self.names.append(name)
self.shapes.append(tuple(p.shape))
n = p.numel()
self.numels.append(n)
total_size += n
dtypes.add(p.dtype)
if len(dtypes) > 1:
raise ValueError(f"Mixed parameter dtypes found in model: {dtypes}")
self.dtype: torch.dtype = next(iter(dtypes))
self.size: int = total_size
def encode(self, model: nn.Module) -> torch.Tensor:
"""
Flattens model parameters into a single detached 1D torch.Tensor.
"""
params = [p for _, p in model.named_parameters()]
return nn.utils.parameters_to_vector(params).detach()
def apply_vector(self, vector: torch.Tensor, model: nn.Module) -> None:
"""
Applies a validated 1D parameter vector to model.parameters() in-place without storage aliasing.
"""
if not isinstance(vector, torch.Tensor) or vector.ndim != 1:
raise ValueError("vector must be a 1D torch.Tensor")
if vector.numel() != self.size:
raise ValueError(
f"Vector size {vector.numel()} does not match codec size {self.size}"
)
if vector.dtype != self.dtype:
raise ValueError(
f"Vector dtype {vector.dtype} does not match codec dtype {self.dtype}"
)
params = [p for _, p in model.named_parameters()]
if params:
target_device = params[0].device
if vector.device != target_device:
vector = vector.to(target_device)
with torch.no_grad():
offset = 0
for p, shape, numel in zip(params, self.shapes, self.numels):
p.copy_(vector[offset : offset + numel].reshape(shape))
offset += numel
def to_state_dict(
self, vector: torch.Tensor, eval_model: nn.Module
) -> collections.OrderedDict[str, torch.Tensor]:
"""
Applies vector to eval_model and returns a defensive CPU-cloned ordered state dict including buffers.
"""
self.apply_vector(vector, eval_model)
state_dict = eval_model.state_dict()
res = collections.OrderedDict()
for k, v in state_dict.items():
res[k] = v.detach().cpu().clone()
return res
+1613 -695
View File
File diff suppressed because it is too large Load Diff
+53 -389
View File
@@ -1,409 +1,73 @@
from typing import Any from typing import Any, Literal
import torch
import numpy as np from .plugins import FitContext, InitializationPlugin
from tensorflow import keras
class Particle: class Particle:
""" """
Particle Swarm Optimization의 Particle을 구현한 클래스 Particle Swarm Optimization particle (engine-owned state, plugin-driven movement).
한 파티클의 life cycle은 다음과 같다. Each particle owns position, velocity, personal best score, personal best weights,
1. 초기화 and monitor state on device.
2. 손실 함수 계산
3. 속도 업데이트
4. 가중치 업데이트
5. 2번으로 돌아가서 반복
""" """
g_best_score = [np.inf, 0, np.inf]
g_best_weights = None
count = 0
MODEL_IS_NONE = "model is None"
def __init__( def __init__(
self, self,
model: keras.Model, index: int,
loss: Any = None, base_vector: torch.Tensor,
context: FitContext,
init_plugin: InitializationPlugin,
negative: bool = False, negative: bool = False,
mutation: float = 0,
converge_reset: bool = False,
converge_reset_patience: int = 10,
converge_reset_monitor: str = "loss",
converge_reset_min_delta: float = 0.0001,
): ):
""" self.index = index
Args:
model (keras.models): 학습 및 검증을 위한 모델
loss (str|): 손실 함수
negative (bool, optional): 음의 가중치 사용 여부 - 전역 탐색 용도(조기 수렴 방지). Defaults to False.
mutation (float, optional): 돌연변이 확률. Defaults to 0.
converge_reset (bool, optional): 조기 종료 사용 여부. Defaults to False.
converge_reset_patience (int, optional): 조기 종료를 위한 기다리는 횟수. Defaults to 10.
"""
self.set_model(model)
self.weights = self._encode(model.get_weights())
self.loss = loss
try:
if converge_reset and converge_reset_monitor not in [
"acc",
"accuracy",
"loss",
"mse",
]:
raise ValueError(
"converge_reset_monitor must be 'acc' or 'accuracy' or 'loss'"
)
if converge_reset and converge_reset_min_delta < 0:
raise ValueError("converge_reset_min_delta must be positive")
if converge_reset and converge_reset_patience < 0:
raise ValueError("converge_reset_patience must be positive")
except ValueError as e:
print(e)
exit(1)
self.velocities = np.zeros(len(self.weights))
self.__reset_particle()
self.best_weights = self.weights
self.negative = negative self.negative = negative
self.mutation = mutation
self.local_best_score = [np.inf, 0, np.inf]
self.score_history = []
self.converge_reset = converge_reset
self.converge_reset_patience = converge_reset_patience
self.converge_reset_monitor = converge_reset_monitor
self.converge_reset_min_delta = converge_reset_min_delta
Particle.count += 1
def __del__(self): pos, vel = init_plugin.initialize(index, base_vector, context)
del self.model self.position: torch.Tensor = pos
del self.loss self.velocity: torch.Tensor = vel
del self.velocities
del self.negative
del self.local_best_score
del self.best_weights
Particle.count -= 1
def set_shape(self, weights: list): self.personal_best_score: tuple[float, float, float] | None = None
""" self.personal_best_weights: torch.Tensor | None = None
가중치의 shape을 설정 self.personal_best_monitor_value: float | None = None
Args: def reset(
weights (list): keras model의 가중치
"""
self.shape = [layer.shape for layer in weights]
def get_shape(self):
return self.shape
def _encode(self, weights: list):
"""
가중치를 1차원으로 풀어서 반환
Args:
weights (list) : keras model의 가중치
Returns:
(numpy array) : 가중치 - 1차원으로 풀어서 반환
(list) : 가중치의 원본 shape
(list) : 가중치의 원본 shape의 길이
"""
w_gpu = np.array([])
for layer in weights:
w_tmp = layer.reshape(-1)
w_gpu = np.append(w_gpu, w_tmp)
return w_gpu
def _decode(self, weight: np.ndarray):
"""
_encode 로 인코딩된 가중치를 원본 shape으로 복원
파라미터는 encode의 리턴값을 그대로 사용을 권장
Args:
weight (numpy array): 가중치 - 1차원으로 풀어서 반환
shape (list): 가중치의 원본 shape
length (list): 가중치의 원본 shape의 길이
Returns:
(list) : 가중치 원본 shape으로 복원
"""
weights = []
start = 0
for i in range(len(self.shape)):
end = start + np.prod(self.shape[i])
w_ = weight[start:end]
w_ = np.reshape(w_, self.shape[i])
weights.append(w_)
start = end
del start, end, w_
del weight
return weights
def get_model(self):
if self.model is None:
raise ValueError(self.MODEL_IS_NONE)
return self.model
def set_model(self, model: keras.Model):
self.model = model
self.set_shape(self.model.get_weights())
def compile(self):
if self.model is None:
raise ValueError(self.MODEL_IS_NONE)
self.model.compile(
optimizer="adam",
loss=self.loss,
metrics=["accuracy", "mse"],
)
def get_weights(self):
weights = self._decode(self.weights)
return weights
def evaluate(self, x, y):
if self.model is None:
raise ValueError(self.MODEL_IS_NONE)
return self.model.evaluate(x, y, verbose=0) # type: ignore
def get_score(self, x, y, renewal: str = "acc"):
"""
모델의 성능을 평가하여 점수를 반환
Args:
x (list): 입력 데이터
y (list): 출력 데이터
renewal (str, optional): 점수 갱신 방식. Defaults to "acc" | "acc" or "loss".
Returns:
(float): 점수
"""
score = self.evaluate(x, y)
if renewal == "loss":
if score[0] < self.local_best_score[0]:
self.local_best_score = score
self.best_weights = self.weights
elif renewal == "acc":
if score[1] > self.local_best_score[1]:
self.local_best_score = score
self.best_weights = self.weights
elif renewal == "mse":
if score[2] < self.local_best_score[2]:
self.local_best_score = score
self.best_weights = self.weights
else:
raise ValueError("renewal must be 'acc' or 'loss' or 'mse'")
return score
def __check_converge_reset(
self, self,
score, base_vector: torch.Tensor,
monitor: str = "auto", context: FitContext,
patience: int = 10, init_plugin: InitializationPlugin,
min_delta: float = 0.0001, ) -> None:
):
""" """
early stop을 구현한 함수 Resets particle position, velocity, and personal best state using the initialization plugin.
Args:
score (float): 현재 점수 [0] - loss, [1] - acc
monitor (str, optional): 감시할 점수. Defaults to acc. | "acc" or "loss" or "mse"
patience (int, optional): early stop을 위한 기다리는 횟수. Defaults to 10.
min_delta (float, optional): early stop을 위한 최소 변화량. Defaults to 0.0001.
""" """
if monitor == "auto": pos, vel = init_plugin.initialize(self.index, base_vector, context)
monitor = "acc" self.position = pos
if monitor in ["loss"]: self.velocity = vel
self.score_history.append(score[0]) self.personal_best_score = None
elif monitor in ["acc", "accuracy"]: self.personal_best_weights = None
self.score_history.append(score[1]) self.personal_best_monitor_value = None
elif monitor in ["mse"]:
self.score_history.append(score[2])
else:
raise ValueError("monitor must be 'acc' or 'accuracy' or 'loss' or 'mse'")
if len(self.score_history) > patience: def apply_boundary_strategy(
last_scores = self.score_history[-patience:] self,
if max(last_scores) - min(last_scores) < min_delta: particle_min: float | None,
return True particle_max: float | None,
return False boundary_strategy: str = "clip",
) -> None:
def __reset_particle(self):
self.model = keras.models.model_from_json(self.model.to_json())
self.model.compile(
optimizer="adam",
loss=self.loss,
metrics=["accuracy", "mse"],
)
self.weights = self._encode(self.model.get_weights())
rng = np.random.default_rng()
self.velocities = rng.uniform(-0.2, 0.2, len(self.weights))
self.score_history = []
def _velocity_calculation(self, local_rate, global_rate, w):
""" """
현재 속도 업데이트 Applies boundary constraints (clip or reflect) to particle position and velocity.
Args:
local_rate (float): 지역 최적해의 영향력
global_rate (float): 전역 최적해의 영향력
w (float): 현재 속도의 영향력 - 관성 | 0.9 ~ 0.4 이 적당
""" """
# 0회차 전역 최적해가 없을 경우 현재 파티클의 최적해로 설정 - 전역최적해의 방향을 0으로 만들기 위함 if particle_min is not None and particle_max is not None:
best_particle_weights = ( if boundary_strategy == "reflect":
self.best_weights span = float(particle_max - particle_min)
if Particle.g_best_weights is None shift = self.position - particle_min
else Particle.g_best_weights q = torch.floor(shift / span).to(torch.int64)
) m = torch.remainder(shift, 2.0 * span)
bounded_pos = torch.where(
rng = np.random.default_rng(seed=42) m <= span,
r_0 = rng.random() particle_min + m,
r_1 = rng.random() particle_min + (2.0 * span - m),
)
if self.negative: self.velocity = torch.where(q % 2 != 0, -self.velocity, self.velocity)
# 지역 최적해와 전역 최적해를 음수로 사용하여 전역 탐색을 유도 self.position = bounded_pos
new_v = ( else:
w * self.velocities self.position = torch.clamp(
+ local_rate * r_0 * (self.best_weights - self.weights) self.position, particle_min, particle_max
- global_rate * r_1 * (best_particle_weights - self.weights) )
)
if (
len(self.score_history) > 10
and max(self.score_history[-10:]) - min(self.score_history[-10:]) < 0.01
):
self.__reset_particle()
else:
# 전역 최적해의 acc 가 높을수록 더 빠르게 수렴
# 하지만 loss 가 커진 상태에서는 전역 최적해의 영향이
new_v = (
w * self.velocities
+ local_rate
* self.local_best_score[1]
* r_0
* (self.best_weights - self.weights)
+ global_rate
# * Particle.g_best_score[1]
* r_1 * (best_particle_weights - self.weights)
)
if self.mutation != 0.0 and rng.random() < self.mutation:
m_v = rng.uniform(-0.2, 0.2, len(self.velocities))
new_v = m_v
self.velocities = new_v
del r_0, r_1
def _position_update(self):
"""
가중치 업데이트
"""
self.weights = np.add(self.weights, self.velocities)
self.model.set_weights(self.get_weights())
def step(self, x, y, local_rate, global_rate, w, renewal: str = "acc"):
"""
파티클의 한 스텝을 진행합니다.
Args:
x (list): 입력 데이터
y (list): 출력 데이터
local_rate (float): 지역최적해의 영향력
global_rate (float): 전역최적해의 영향력
w (float): 관성
g_best (list): 전역최적해
renewal (str, optional): 최고점수 갱신 방식. Defaults to "acc" | "acc" or "loss"
Returns:
list: 현재 파티클의 점수
"""
self._velocity_calculation(local_rate, global_rate, w)
self._position_update()
score = self.get_score(x, y, renewal)
if self.converge_reset and self.__check_converge_reset(
score,
self.converge_reset_monitor,
self.converge_reset_patience,
self.converge_reset_min_delta,
):
self.__reset_particle()
score = self.get_score(x, y, renewal)
while (
np.isnan(score[0])
or np.isnan(score[1])
or np.isnan(score[2])
or score[0] == 0
or score[1] == 0
or score[2] == 0
or np.isinf(score[0])
or np.isinf(score[1])
or np.isinf(score[2])
or score[0] > 1000
or score[1] > 1
or score[2] > 1000
):
self.__reset_particle()
score = self.get_score(x, y, renewal)
return score
def get_best_score(self):
"""
파티클의 최고점수를 반환합니다.
Returns:
float: 최고점수
"""
return self.local_best_score
def get_best_weights(self):
"""
파티클의 최고점수를 받은 가중치를 반환합니다
Returns:
list: 가중치 리스트
"""
return self._decode(self.best_weights)
def set_global_score(self):
"""전역 최고점수를 현재 파티클의 최고점수로 설정합니다"""
Particle.g_best_score = self.local_best_score
def set_global_weights(self):
"""전역 최고점수를 받은 가중치를 현재 파티클의 최고점수를 받은 가중치로 설정합니다"""
Particle.g_best_weights = self.best_weights
def update_global_best(self):
"""현재 파티클의 점수와 가중치를 전역 최고점수와 가중치로 설정합니다"""
self.set_global_score()
self.set_global_weights()
def check_global_best(self, renewal: str = "loss"):
if (
(renewal == "loss" and self.local_best_score[0] < Particle.g_best_score[0])
or (
renewal == "acc" and self.local_best_score[1] > Particle.g_best_score[1]
)
or (
renewal == "mse" and self.local_best_score[2] < Particle.g_best_score[2]
)
):
self.update_global_best()
# 끝
+1566
View File
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
[build-system]
requires = ["setuptools>=77", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pso2keras"
version = "4.0.0"
description = "Particle Swarm Optimization for PyTorch models"
readme = "README.md"
requires-python = ">=3.10,<3.12"
license = "MIT"
license-files = ["LICENSE"]
authors = [
{ name = "pieroot", email = "jgbong0306@gmail.com" }
]
keywords = [
"pso",
"pytorch",
"torch",
"optimization",
"particle swarm optimization",
"pso2keras",
]
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
dependencies = [
"torch>=2.13,<3",
"numpy<2",
"scikit-learn",
"tqdm",
"tensorboard",
"tomli>=2; python_version < '3.11'",
]
[project.urls]
Homepage = "https://github.com/jung-geun/PSO"
Repository = "https://github.com/jung-geun/PSO"
[project.optional-dependencies]
examples = [
"pandas",
"ucimlrepo",
"torchvision>=0.28,<1",
"matplotlib>=3.8,<4",
]
detection = [
"ultralytics==8.4.142",
"ensemble-boxes==1.0.9",
]
[dependency-groups]
dev = [
"pytest>=9,<10",
"build>=1.3,<2",
"twine>=6,<8",
]
[tool.setuptools.packages.find]
where = ["."]
include = ["pso*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
-7
View File
@@ -1,7 +0,0 @@
ipython
numpy
pandas
tensorflow==2.15.1
tqdm==4.66.4
scikit-learn==1.4.2
tensorboard==2.15.1
-41
View File
@@ -1,41 +0,0 @@
from setuptools import find_packages, setup
import pso
VERSION = pso.__version__
def get_requirements(path: str):
return [l.strip() for l in open(path)]
setup(
name="pso2keras",
version=VERSION,
description="Particle Swarm Optimization on tensorflow package",
author="pieroot",
author_email="jgbong0306@gmail.com",
url="https://github.com/jung-geun/PSO",
install_requires=get_requirements("requirements.txt"),
packages=find_packages(exclude=[]),
keywords=[
"pso",
"tensorflow",
"keras",
"optimization",
"particle swarm optimization",
"pso2keras",
],
python_requires=">=3.10",
package_data={},
zip_safe=False,
long_description=open("README.md", encoding="UTF8").read(),
long_description_content_type="text/markdown",
license="MIT",
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
)
+119 -53
View File
@@ -1,72 +1,138 @@
import os """Dry Bean dataset gradient baseline (PyTorch standard backprop optimizer, non-PSO)."""
from keras.layers import Dense import copy
from keras.models import Sequential import numpy as np
from keras.utils import to_categorical import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split from sklearn.model_selection import train_test_split
from tensorflow import keras from sklearn.preprocessing import LabelEncoder
from ucimlrepo import fetch_ucirepo from ucimlrepo import fetch_ucirepo
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" class BeanModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(16, 12),
nn.ReLU(),
nn.Linear(12, 8),
nn.ReLU(),
nn.Linear(8, 7),
)
def forward(self, x):
return self.net(x)
def make_model(): def get_data(seed: int = 42):
model = Sequential()
model.add(Dense(12, input_dim=16, activation="relu"))
model.add(Dense(8, activation="relu"))
model.add(Dense(7, activation="softmax"))
return model
def get_data():
# fetch dataset
dry_bean_dataset = fetch_ucirepo(id=602) dry_bean_dataset = fetch_ucirepo(id=602)
# data (as pandas dataframes)
X = dry_bean_dataset.data.features X = dry_bean_dataset.data.features
y = dry_bean_dataset.data.targets y = dry_bean_dataset.data.targets
x = X.to_numpy() x = X.to_numpy().astype("float32")
# object to categorical encoder = LabelEncoder()
y_encoded = encoder.fit_transform(y.values.ravel()).astype("int64")
x = x.astype("float32")
y_class = to_categorical(y)
# metadata
# print(dry_bean_dataset.metadata)
# variable information
# print(dry_bean_dataset.variables)
# print(X.head())
# print(y.head())
# y_class = to_categorical(y)
x_train, x_test, y_train, y_test = train_test_split( x_train, x_test, y_train, y_test = train_test_split(
x, y_class, test_size=0.2, random_state=42, shuffle=True x, y_encoded, test_size=0.2, random_state=seed, shuffle=True
)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(y_test, dtype=torch.int64),
) )
return x_train, x_test, y_train, y_test
x_train, x_test, y_train, y_test = get_data() def get_device() -> torch.device:
model = make_model() if (
early_stopping = keras.callbacks.EarlyStopping( hasattr(torch.backends, "mps")
patience=10, min_delta=0.001, restore_best_weights=True and torch.backends.mps.is_built()
) and torch.backends.mps.is_available()
):
return torch.device("mps")
elif torch.cuda.is_available():
return torch.device("cuda")
return torch.device("cpu")
model.compile( def main():
loss="sparse_categorical_crossentropy", torch.manual_seed(42)
optimizer="adam", np.random.seed(42)
metrics=["accuracy", "mse"],
)
model.summary() device = get_device()
print(f"Selected device: {device}")
history = model.fit( x_train, x_test, y_train, y_test = get_data(seed=42)
x_train, y_train, epochs=150, batch_size=10, callbacks=[early_stopping] train_dataset = TensorDataset(x_train, y_train)
) val_dataset = TensorDataset(x_test, y_test)
score = model.evaluate(x_test, y_test, verbose=2) train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=10, shuffle=False)
model = BeanModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
best_val_loss = float("inf")
best_state = None
patience = 10
min_delta = 0.001
patience_counter = 0
for epoch in range(150):
model.train()
for bx, by in train_loader:
bx, by = bx.to(device), by.to(device)
optimizer.zero_grad()
out = model(bx)
loss = criterion(out, by)
loss.backward()
optimizer.step()
model.eval()
val_loss = 0.0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
val_loss += loss.item() * bx.size(0)
total += bx.size(0)
val_loss /= total
if val_loss < best_val_loss - min_delta:
best_val_loss = val_loss
best_state = copy.deepcopy(model.state_dict())
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= patience:
break
if best_state is not None:
model.load_state_dict(best_state)
model.eval()
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
test_loss += loss.item() * bx.size(0)
preds = out.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
test_loss /= total
test_acc = correct / total
print(f"Final test loss: {test_loss:.4f}, accuracy: {test_acc:.4f}")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+490
View File
@@ -0,0 +1,490 @@
"""
Command-Line Interface Helpers for PSO Experiments.
Provides standard argparse argument groups and helper functions for PSO stage selectors
(method, initialization, evaluation, convergence, refinement) and common execution parameters.
"""
import argparse
from typing import Any
# Supported stage plugin selector options
STAGE_SELECTORS: dict[str, list[str]] = {
"method": [
"original",
"inertia",
"constriction",
"fips",
"clpso",
"bare_bones",
"adaptive_moment",
],
"initialization": ["model_noise", "uniform"],
"evaluation": ["full", "fixed_subset"],
"convergence": ["none", "particle_reset", "early_stopping"],
"refinement": ["none", "adam"],
}
def add_stage_selector_args(
parser: argparse.ArgumentParser, defaults: dict[str, Any] | None = None
) -> argparse.ArgumentParser:
"""Adds the 5 explicit stage selector arguments to an argparse parser."""
defaults = defaults or {}
parser.add_argument(
"--method",
type=str,
choices=STAGE_SELECTORS["method"],
default=defaults.get("method", "original"),
help="PSO movement stage method (default: %(default)s)",
)
parser.add_argument(
"--initialization",
type=str,
choices=STAGE_SELECTORS["initialization"],
default=defaults.get("initialization", "model_noise"),
help="PSO particle initialization stage (default: %(default)s)",
)
parser.add_argument(
"--evaluation",
type=str,
choices=STAGE_SELECTORS["evaluation"],
default=defaults.get("evaluation", "full"),
help="PSO objective evaluation stage (default: %(default)s)",
)
parser.add_argument(
"--convergence",
type=str,
choices=STAGE_SELECTORS["convergence"],
default=defaults.get("convergence", "none"),
help="PSO convergence behavior stage (default: %(default)s)",
)
parser.add_argument(
"--refinement",
type=str,
choices=STAGE_SELECTORS["refinement"],
default=defaults.get("refinement", "none"),
help="PSO post-search refinement stage (default: %(default)s)",
)
return parser
def add_pso_args(
parser: argparse.ArgumentParser, defaults: dict[str, Any] | None = None
) -> argparse.ArgumentParser:
"""Adds stage selectors and common PSO hyperparameter arguments to an argparse parser."""
defaults = defaults or {}
# Add 5 stage selectors
add_stage_selector_args(parser, defaults)
# Core execution and hyperparameter options
parser.add_argument(
"--seed",
type=int,
default=defaults.get("seed", 42),
help="Random seed for reproducibility (default: %(default)s)",
)
parser.add_argument(
"--device",
type=str,
default=defaults.get("device", None),
help="Execution target device (cpu, cuda, mps) (default: auto)",
)
parser.add_argument(
"--n-particles",
"--particles",
dest="n_particles",
type=int,
default=defaults.get("n_particles", 30),
help="Number of swarm particles (default: %(default)s)",
)
parser.add_argument(
"--epochs",
type=int,
default=defaults.get("epochs", 80),
help="Number of PSO optimization epochs (default: %(default)s)",
)
parser.add_argument(
"--batch-size",
"--batch",
dest="batch_size",
type=int,
default=defaults.get("batch_size", None),
help="Batch size for objective evaluation (default: %(default)s)",
)
parser.add_argument(
"--fitness-size",
type=int,
default=defaults.get("fitness_size", None),
help="Fixed subset sample count (required when evaluation='fixed_subset')",
)
parser.add_argument(
"--refinement-epochs",
type=int,
default=defaults.get("refinement_epochs", 0),
help="Refinement epoch count (required when refinement='adam')",
)
parser.add_argument(
"--refinement-lr",
type=float,
default=defaults.get("refinement_lr", 0.001),
help="Refinement learning rate for Adam optimizer (default: %(default)s)",
)
parser.add_argument(
"--renewal",
type=str,
choices=["acc", "loss", "mse"],
default=defaults.get("renewal", "loss"),
help="Primary metric for global best selection (default: %(default)s)",
)
parser.add_argument(
"--output-dir",
type=str,
default=defaults.get("output_dir", None),
help="Directory to save model checkpoints and logs",
)
# Optional coefficient overrides
parser.add_argument(
"--c0",
type=float,
default=defaults.get("c0", None),
help="Cognitive acceleration coefficient override",
)
parser.add_argument(
"--c1",
type=float,
default=defaults.get("c1", None),
help="Social acceleration coefficient override",
)
parser.add_argument(
"--w-min",
dest="w_min",
type=float,
default=defaults.get("w_min", None),
help="Minimum inertia weight override",
)
parser.add_argument(
"--w-max",
dest="w_max",
type=float,
default=defaults.get("w_max", None),
help="Maximum inertia weight override",
)
parser.add_argument(
"--negative-swarm",
type=float,
default=defaults.get("negative_swarm", 0.0),
help="Negative swarm velocity coefficient (default: %(default)s)",
)
parser.add_argument(
"--mutation-swarm",
type=float,
default=defaults.get("mutation_swarm", 0.0),
help="Swarm mutation probability (default: %(default)s)",
)
parser.add_argument(
"--particle-min",
type=float,
default=defaults.get("particle_min", None),
help="Lower bound for particle position clamping",
)
parser.add_argument(
"--particle-max",
type=float,
default=defaults.get("particle_max", None),
help="Upper bound for particle position clamping",
)
parser.add_argument(
"--velocity-limit-ratio",
type=float,
default=defaults.get("velocity_limit_ratio", None),
help="Maximum velocity limit ratio relative to search domain",
)
parser.add_argument(
"--boundary-strategy",
type=str,
choices=["clip", "reflect"],
default=defaults.get("boundary_strategy", "clip"),
help="Position boundary handling strategy (default: %(default)s)",
)
parser.add_argument(
"--initial-position-noise",
type=float,
default=defaults.get("initial_position_noise", 0.05),
help="Initial position noise scale (default: %(default)s)",
)
# Convergence stage options
parser.add_argument(
"--convergence-patience",
dest="convergence_patience",
type=int,
default=defaults.get("convergence_patience", 10),
help="Convergence reset/early stopping patience epochs (default: %(default)s)",
)
parser.add_argument(
"--convergence-min-delta",
dest="convergence_min_delta",
type=float,
default=defaults.get("convergence_min_delta", 0.0001),
help="Minimum improvement delta for convergence (default: %(default)s)",
)
parser.add_argument(
"--convergence-monitor",
dest="convergence_monitor",
type=str,
choices=["loss", "acc", "accuracy", "mse"],
default=defaults.get("convergence_monitor", "loss"),
help="Metric monitored for convergence (default: %(default)s)",
)
# Adaptive moment options
parser.add_argument(
"--moment-blend",
dest="moment_blend",
type=float,
default=defaults.get("moment_blend", None),
help="Adaptive moment blend factor (default: 0.25 when method='adaptive_moment', else 0.0)",
)
parser.add_argument(
"--moment-beta1",
dest="moment_beta1",
type=float,
default=defaults.get("moment_beta1", None),
help="Adaptive moment beta1 parameter (default: 0.9 when method='adaptive_moment')",
)
parser.add_argument(
"--moment-beta2",
dest="moment_beta2",
type=float,
default=defaults.get("moment_beta2", None),
help="Adaptive moment beta2 parameter (default: 0.999 when method='adaptive_moment')",
)
parser.add_argument(
"--moment-step-size",
dest="moment_step_size",
type=float,
default=defaults.get("moment_step_size", None),
help="Adaptive moment step size (default: 1.0 when method='adaptive_moment')",
)
parser.add_argument(
"--moment-epsilon",
dest="moment_epsilon",
type=float,
default=defaults.get("moment_epsilon", None),
help="Adaptive moment epsilon parameter (default: 1e-8 when method='adaptive_moment')",
)
# Repeatable method options
parser.add_argument(
"--method-option",
dest="method_options",
action="append",
metavar="KEY=VALUE",
help="Additional key=value option for movement method (repeatable)",
)
return parser
def parse_method_options(options: list[str] | None) -> dict[str, Any]:
"""Parses a list of 'KEY=VALUE' strings into a dictionary with typed values."""
res: dict[str, Any] = {}
if not options:
return res
for opt in options:
if "=" not in opt:
raise ValueError(f"Invalid --method-option format '{opt}', expected 'KEY=VALUE'")
key, val = opt.split("=", 1)
key = key.strip()
val = val.strip()
val_lower = val.lower()
if val_lower == "true":
parsed_val: Any = True
elif val_lower == "false":
parsed_val = False
else:
try:
parsed_val = int(val)
except ValueError:
try:
parsed_val = float(val)
except ValueError:
parsed_val = val
res[key] = parsed_val
return res
def build_optimizer_kwargs(
args: argparse.Namespace,
*,
model: Any = None,
loss: Any = None,
task: str | None = None,
inertia_profile: dict[str, float] | None = None,
**extra_kwargs: Any,
) -> dict[str, Any]:
"""Builds Optimizer constructor keyword arguments from parsed CLI arguments.
Applies method-specific parameter compatibility rules, workload inertia profiles
(only when method='inertia'), repeatable method options (--method-option), and
adaptive moment flags (only when method='adaptive_moment').
"""
method = getattr(args, "method", "original")
parsed_method_opts = parse_method_options(getattr(args, "method_options", None))
if method == "inertia":
profile = inertia_profile or {}
c0 = (
parsed_method_opts["c0"]
if "c0" in parsed_method_opts
else (args.c0 if getattr(args, "c0", None) is not None else profile.get("c0"))
)
c1 = (
parsed_method_opts["c1"]
if "c1" in parsed_method_opts
else (args.c1 if getattr(args, "c1", None) is not None else profile.get("c1"))
)
w_min = (
parsed_method_opts["w_min"]
if "w_min" in parsed_method_opts
else (args.w_min if getattr(args, "w_min", None) is not None else profile.get("w_min"))
)
w_max = (
parsed_method_opts["w_max"]
if "w_max" in parsed_method_opts
else (args.w_max if getattr(args, "w_max", None) is not None else profile.get("w_max"))
)
elif method in ("original", "constriction", "fips"):
c0 = (
parsed_method_opts["c0"]
if "c0" in parsed_method_opts
else getattr(args, "c0", None)
)
c1 = (
parsed_method_opts["c1"]
if "c1" in parsed_method_opts
else getattr(args, "c1", None)
)
w_min = None
w_max = None
elif method == "bare_bones":
c0 = None
c1 = None
w_min = None
w_max = None
else:
c0 = (
parsed_method_opts["c0"]
if "c0" in parsed_method_opts
else getattr(args, "c0", None)
)
c1 = (
parsed_method_opts["c1"]
if "c1" in parsed_method_opts
else getattr(args, "c1", None)
)
w_min = (
parsed_method_opts["w_min"]
if "w_min" in parsed_method_opts
else getattr(args, "w_min", None)
)
w_max = (
parsed_method_opts["w_max"]
if "w_max" in parsed_method_opts
else getattr(args, "w_max", None)
)
if method in ("fips", "clpso", "bare_bones"):
neg_swarm = 0.0
else:
neg_swarm = (
float(parsed_method_opts["negative_swarm"])
if "negative_swarm" in parsed_method_opts
else float(getattr(args, "negative_swarm", 0.0))
)
if method == "bare_bones":
mut_swarm = 0.0
else:
mut_swarm = (
float(parsed_method_opts["mutation_swarm"])
if "mutation_swarm" in parsed_method_opts
else float(getattr(args, "mutation_swarm", 0.0))
)
if method == "bare_bones":
vel_ratio = None
else:
vel_ratio = (
parsed_method_opts["velocity_limit_ratio"]
if "velocity_limit_ratio" in parsed_method_opts
else getattr(args, "velocity_limit_ratio", None)
)
fitness_size = (
getattr(args, "fitness_size", None)
if getattr(args, "evaluation", None) == "fixed_subset"
else None
)
refinement_epochs = (
getattr(args, "refinement_epochs", 0)
if getattr(args, "refinement", None) == "adam"
else 0
)
kwargs: dict[str, Any] = {
"model": model,
"loss": loss,
"task": task,
"method": method,
"initialization": getattr(args, "initialization", "model_noise"),
"evaluation": getattr(args, "evaluation", "full"),
"convergence": getattr(args, "convergence", "none"),
"refinement": getattr(args, "refinement", "none"),
"method_options": parsed_method_opts,
"n_particles": getattr(args, "n_particles", 30),
"c0": c0,
"c1": c1,
"w_min": w_min,
"w_max": w_max,
"negative_swarm": neg_swarm,
"mutation_swarm": mut_swarm,
"particle_min": getattr(args, "particle_min", None),
"particle_max": getattr(args, "particle_max", None),
"velocity_limit_ratio": vel_ratio,
"boundary_strategy": getattr(args, "boundary_strategy", "clip"),
"initial_position_noise": getattr(args, "initial_position_noise", 0.05),
"seed": getattr(args, "seed", None),
"device": getattr(args, "device", None),
"fitness_size": fitness_size,
"convergence_patience": getattr(args, "convergence_patience", 10),
"convergence_min_delta": getattr(args, "convergence_min_delta", 0.0001),
"convergence_monitor": getattr(args, "convergence_monitor", "loss"),
"refinement_epochs": refinement_epochs,
"refinement_lr": getattr(args, "refinement_lr", 0.001),
}
if method == "adaptive_moment":
if "moment_blend" in parsed_method_opts:
m_blend = float(parsed_method_opts["moment_blend"])
elif getattr(args, "moment_blend", None) is not None:
m_blend = float(getattr(args, "moment_blend"))
else:
m_blend = 0.25
kwargs["moment_blend"] = m_blend
for param_name in (
"moment_beta1",
"moment_beta2",
"moment_step_size",
"moment_epsilon",
):
if param_name in parsed_method_opts:
kwargs[param_name] = float(parsed_method_opts[param_name])
elif getattr(args, param_name, None) is not None:
kwargs[param_name] = float(getattr(args, param_name))
kwargs.update(extra_kwargs)
return kwargs
+427
View File
@@ -0,0 +1,427 @@
#!/usr/bin/env python3
"""
PSO Stage Plugin Method Comparison Tool.
Compares PSO movement methods (original, inertia, constriction, fips, clpso, bare_bones,
adaptive_moment, local_best, quantum)
across benchmark datasets (xor, iris, mnist) with reproducible model initialization, dataset splits,
and clean console output & JSON result reporting.
"""
import argparse
import json
import sys
import time
from typing import Any
import torch
import torch.nn as nn
from cli import add_pso_args, parse_method_options
from pso import Optimizer
from pso.plugins import available_plugins
def print_available_methods():
"""Prints available movement method plugins and metadata provenance."""
movement_plugins = available_plugins("movement")
print("Available PSO Movement Method Plugins:")
print("=" * 80)
for name, meta in movement_plugins.items():
print(f" Stage Key : {name}")
print(f" Title : {meta.title}")
print(f" Source (DOI) : {meta.source or 'N/A'}")
print(f" Fidelity : {meta.fidelity}")
print(f" Needs Grad : {meta.gradient_required}")
print("-" * 80)
def get_xor_workload(seed: int):
"""Builds identical XOR dataset and PyTorch model state for a given seed."""
torch.manual_seed(seed)
x_train = torch.tensor(
[[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]], dtype=torch.float32
)
y_train = torch.tensor([[0.0], [1.0], [1.0], [0.0]], dtype=torch.float32)
model = nn.Sequential(
nn.Linear(2, 4),
nn.Tanh(),
nn.Linear(4, 1),
)
loss_fn = nn.BCEWithLogitsLoss()
return x_train, y_train, None, model, loss_fn, "binary"
def get_iris_workload(seed: int):
"""Builds identical Iris dataset and PyTorch model state for a given seed."""
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
torch.manual_seed(seed)
iris = load_iris()
X = iris.data.astype("float32")
y = iris.target.astype("int64")
x_train, x_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=True, stratify=y, random_state=seed
)
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
x_tr = torch.tensor(x_train, dtype=torch.float32)
y_tr = torch.tensor(y_train, dtype=torch.int64)
x_te = torch.tensor(x_test, dtype=torch.float32)
y_te = torch.tensor(y_test, dtype=torch.int64)
model = nn.Sequential(
nn.Linear(4, 10),
nn.ReLU(),
nn.Linear(10, 10),
nn.ReLU(),
nn.Linear(10, 3),
)
loss_fn = nn.CrossEntropyLoss()
return x_tr, y_tr, (x_te, y_te), model, loss_fn, "multiclass"
def get_mnist_workload(seed: int):
"""Builds identical PCA32 MNIST dataset and PyTorch model state for a given seed."""
from sklearn.decomposition import PCA
from torchvision.datasets import MNIST
torch.manual_seed(seed)
train_ds = MNIST(root="./data", train=True, download=True)
test_ds = MNIST(root="./data", train=False, download=True)
x_tr_raw = (train_ds.data[:3000].float() / 255.0).reshape(3000, -1).numpy()
y_tr = train_ds.targets[:3000].long()
x_te_raw = (test_ds.data[:1000].float() / 255.0).reshape(1000, -1).numpy()
y_te = test_ds.targets[:1000].long()
pca = PCA(n_components=32, whiten=True, random_state=seed)
x_tr_pca = pca.fit_transform(x_tr_raw)
x_te_pca = pca.transform(x_te_raw)
x_tr = torch.tensor(x_tr_pca, dtype=torch.float32)
x_te = torch.tensor(x_te_pca, dtype=torch.float32)
model = nn.Linear(32, 10)
loss_fn = nn.CrossEntropyLoss()
return x_tr, y_tr, (x_te, y_te), model, loss_fn, "multiclass"
DATASET_LOADERS = {
"xor": get_xor_workload,
"iris": get_iris_workload,
"mnist": get_mnist_workload,
}
def main():
parser = argparse.ArgumentParser(
description="PSO Stage Plugin Method Comparison Surface"
)
parser.add_argument(
"--list-methods",
action="store_true",
help="List available movement methods and exit",
)
parser.add_argument(
"--dataset",
choices=["xor", "iris", "mnist"],
default="xor",
help="Target dataset workload (default: %(default)s)",
)
parser.add_argument(
"--methods",
nargs="+",
default=["all"],
help="Movement method keys to evaluate or 'all' (default: all)",
)
parser.add_argument(
"--seeds",
type=int,
nargs="+",
default=[42],
help="Random seed list (default: 42)",
)
parser.add_argument(
"--json-path",
"--output-json",
"--json",
dest="json_path",
type=str,
default=None,
help="Optional JSON file path to save detailed evaluation metrics",
)
# Add standard stage selector and hyperparameter options
add_pso_args(parser, defaults={"n_particles": 20, "epochs": 30})
args = parser.parse_args()
if args.list_methods:
print_available_methods()
sys.exit(0)
# Determine movement methods to test
available_m_plugins = available_plugins("movement")
if "all" in args.methods or "ALL" in args.methods:
methods_to_test = list(available_m_plugins.keys())
else:
methods_to_test = []
for m in args.methods:
if m not in available_m_plugins:
raise ValueError(
f"Unknown movement method '{m}'. Available: {list(available_m_plugins.keys())}"
)
methods_to_test.append(m)
loader = DATASET_LOADERS[args.dataset]
eval_stage = args.evaluation
fitness_size = args.fitness_size if eval_stage == "fixed_subset" else None
if eval_stage == "fixed_subset" and fitness_size is None:
if args.dataset == "xor":
fitness_size = 4
elif args.dataset == "iris":
fitness_size = 100
elif args.dataset == "mnist":
fitness_size = 2000
refine_stage = args.refinement
refinement_epochs = args.refinement_epochs if refine_stage == "adam" else 0
results: list[dict[str, Any]] = []
parsed_method_opts = parse_method_options(args.method_options)
has_val = args.dataset != "xor"
print("=" * 80)
print(f"PSO Movement Method Comparison on '{args.dataset}' Dataset")
print(
f"Stages: initialization='{args.initialization}', evaluation='{eval_stage}', "
f"convergence='{args.convergence}', refinement='{refine_stage}'"
)
print(
f"Parameters: particles={args.n_particles}, epochs={args.epochs}, seeds={args.seeds}"
)
print("=" * 80)
if has_val:
print(
f"{'Method':<18} {'Seed':<6} {'Val Loss':<12} {'Val Accuracy':<12} {'Val MSE':<12} {'Time (s)':<10}"
)
else:
print(
f"{'Method':<18} {'Seed':<6} {'Loss':<12} {'Accuracy':<12} {'MSE':<12} {'Time (s)':<10}"
)
print("-" * 80)
for method_key in methods_to_test:
for seed in args.seeds:
x_tr, y_tr, val_data, model, loss_fn, task = loader(seed)
eff_fitness_size = (
min(fitness_size, x_tr.shape[0])
if fitness_size is not None
else None
)
c0 = args.c0
c1 = args.c1
w_min = args.w_min
w_max = args.w_max
neg_swarm = (
args.negative_swarm
if method_key not in ("fips", "clpso", "bare_bones")
else 0.0
)
mut_swarm = args.mutation_swarm if method_key != "bare_bones" else 0.0
vel_ratio = (
args.velocity_limit_ratio if method_key != "bare_bones" else None
)
kwargs: dict[str, Any] = {
"model": model,
"loss": loss_fn,
"task": task,
"method": method_key,
"initialization": args.initialization,
"evaluation": eval_stage,
"convergence": args.convergence,
"refinement": refine_stage,
"method_options": parsed_method_opts,
"n_particles": args.n_particles,
"c0": c0,
"c1": c1,
"w_min": w_min,
"w_max": w_max,
"negative_swarm": neg_swarm,
"mutation_swarm": mut_swarm,
"particle_min": args.particle_min,
"particle_max": args.particle_max,
"velocity_limit_ratio": vel_ratio,
"boundary_strategy": args.boundary_strategy,
"initial_position_noise": args.initial_position_noise,
"seed": seed,
"device": args.device,
"fitness_size": eff_fitness_size,
"convergence_patience": args.convergence_patience,
"convergence_min_delta": args.convergence_min_delta,
"convergence_monitor": args.convergence_monitor,
"refinement_epochs": refinement_epochs,
"refinement_lr": args.refinement_lr,
}
if method_key == "adaptive_moment":
if args.moment_blend is not None:
kwargs["moment_blend"] = args.moment_blend
elif "moment_blend" in parsed_method_opts:
kwargs["moment_blend"] = float(parsed_method_opts["moment_blend"])
else:
kwargs["moment_blend"] = 0.25
if args.moment_beta1 is not None:
kwargs["moment_beta1"] = args.moment_beta1
if args.moment_beta2 is not None:
kwargs["moment_beta2"] = args.moment_beta2
if args.moment_step_size is not None:
kwargs["moment_step_size"] = args.moment_step_size
if args.moment_epsilon is not None:
kwargs["moment_epsilon"] = args.moment_epsilon
opt = Optimizer(**kwargs)
start_t = time.perf_counter()
best_score = opt.fit(
x_tr,
y_tr,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=eff_fitness_size,
renewal=args.renewal,
validation_data=val_data,
output_dir=None,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
elapsed_t = time.perf_counter() - start_t
tr_loss, tr_acc, tr_mse = best_score
if val_data is not None:
val_x, val_y = val_data
val_score = opt.evaluate(val_x, val_y)
eval_loss, eval_acc, eval_mse = val_score
score_src = "validation"
else:
eval_loss, eval_acc, eval_mse = tr_loss, tr_acc, tr_mse
score_src = "training"
results.append(
{
"method": method_key,
"seed": seed,
"score_source": score_src,
"train_loss": tr_loss,
"train_accuracy": tr_acc,
"train_mse": tr_mse,
"eval_loss": eval_loss,
"eval_accuracy": eval_acc,
"eval_mse": eval_mse,
"loss": eval_loss,
"accuracy": eval_acc,
"mse": eval_mse,
"elapsed_time": elapsed_t,
}
)
print(
f"{method_key:<18} {seed:<6} {eval_loss:<12.6f} {eval_acc:<12.6f} {eval_mse:<12.6f} {elapsed_t:<10.4f}"
)
print("-" * 80)
print("\nAggregate Summary (Mean across seeds):")
print("=" * 80)
if has_val:
print(
f"{'Method':<18} {'Mean Val Loss':<14} {'Mean Val Acc':<14} {'Mean Val MSE':<14} {'Mean Time (s)':<12}"
)
else:
print(
f"{'Method':<18} {'Mean Loss':<12} {'Mean Acc':<12} {'Mean MSE':<12} {'Mean Time (s)':<12}"
)
print("-" * 80)
summary_list: list[dict[str, Any]] = []
for method_key in methods_to_test:
method_runs = [r for r in results if r["method"] == method_key]
if not method_runs:
continue
n_runs = len(method_runs)
mean_tr_loss = sum(r["train_loss"] for r in method_runs) / n_runs
mean_tr_acc = sum(r["train_accuracy"] for r in method_runs) / n_runs
mean_tr_mse = sum(r["train_mse"] for r in method_runs) / n_runs
mean_eval_loss = sum(r["eval_loss"] for r in method_runs) / n_runs
mean_eval_acc = sum(r["eval_accuracy"] for r in method_runs) / n_runs
mean_eval_mse = sum(r["eval_mse"] for r in method_runs) / n_runs
mean_time = sum(r["elapsed_time"] for r in method_runs) / n_runs
score_src = method_runs[0]["score_source"]
summary_entry = {
"method": method_key,
"title": available_m_plugins[method_key].title,
"source": available_m_plugins[method_key].source,
"score_source": score_src,
"mean_train_loss": mean_tr_loss,
"mean_train_accuracy": mean_tr_acc,
"mean_train_mse": mean_tr_mse,
"mean_eval_loss": mean_eval_loss,
"mean_eval_accuracy": mean_eval_acc,
"mean_eval_mse": mean_eval_mse,
"mean_loss": mean_eval_loss,
"mean_accuracy": mean_eval_acc,
"mean_mse": mean_eval_mse,
"mean_elapsed_time": mean_time,
"runs": n_runs,
}
summary_list.append(summary_entry)
if has_val:
print(
f"{method_key:<18} {mean_eval_loss:<14.6f} {mean_eval_acc:<14.6f} {mean_eval_mse:<14.6f} {mean_time:<12.4f}"
)
else:
print(
f"{method_key:<18} {mean_eval_loss:<12.6f} {mean_eval_acc:<12.6f} {mean_eval_mse:<12.6f} {mean_time:<12.4f}"
)
print("=" * 80)
if args.json_path:
payload = {
"dataset": args.dataset,
"score_source": "validation" if has_val else "training",
"selectors": {
"initialization": args.initialization,
"evaluation": eval_stage,
"convergence": args.convergence,
"refinement": refine_stage,
},
"parameters": {
"n_particles": args.n_particles,
"epochs": args.epochs,
"batch_size": args.batch_size,
"fitness_size": fitness_size,
"refinement_epochs": refinement_epochs,
"refinement_lr": args.refinement_lr,
"seeds": args.seeds,
},
"results": results,
"summary": summary_list,
}
with open(args.json_path, "w", encoding="utf-8") as f:
json.dump(payload, f, indent=2)
print(f"\nSaved comparison results JSON to: {args.json_path}")
if __name__ == "__main__":
main()
+932
View File
@@ -0,0 +1,932 @@
"""
MNIST Deep Accuracy Study: Architecture vs Optimizer Profiles
Evaluates official MNIST (60,000 train / 10,000 test) across:
1. Architecture Lane: Raw Linear, Raw MLP, Compact CNN under standard full-data Adam.
2. Optimizer Lane: Adam-Only, PSO-Only (adaptive_moment on 2k subset), and Hybrid (PSO warm start + Adam fine-tuning) on Compact CNN.
Contract:
- Official 60k train / 10k test split with train-only statistics normalization (no PCA).
- Avoid BatchNorm/Dropout so PSO and eval semantics match.
- Seed model construction identically per seed to preserve explicit initial state fingerprint across lanes.
- Fixed no-scheduler contract for Adam with CrossEntropyLoss and lr=1e-3.
"""
import argparse
import csv
import datetime
import json
import math
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Tuple
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
# Ensure test/ directory is in sys.path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from benchmark_suite import (
calc_stats,
compute_data_fingerprint,
compute_model_fingerprint,
extract_plugin_metadata,
get_hardware_provenance,
resolve_execution_device,
save_json_atomic,
sync_device,
)
from pso import Optimizer, __version__ as pso_version
DEEP_ACCURACY_PROTOCOL_VERSION = "1.0.0"
# ==========================================
# Data Preparation (No PCA, Raw 1x28x28)
# ==========================================
def prepare_deep_accuracy_mnist_data() -> Tuple[
torch.Tensor,
torch.Tensor,
torch.Tensor,
torch.Tensor,
str,
Dict[str, Any],
]:
"""
Loads official torchvision MNIST dataset (60,000 train / 10,000 test).
Normalizes images using train-only mean and std (no PCA).
Validates sample counts and label range [0, 9].
Returns (x_train, y_train, x_test, y_test, data_fingerprint, provenance_dict).
"""
from torchvision.datasets import MNIST
cache_dir = Path("result/cache")
cache_dir.mkdir(parents=True, exist_ok=True)
train_dataset = MNIST(root=str(cache_dir), train=True, download=True)
test_dataset = MNIST(root=str(cache_dir), train=False, download=True)
n_train = len(train_dataset.data)
n_test = len(test_dataset.data)
if n_train != 60000:
raise ValueError(f"Expected 60,000 training samples; got {n_train}")
if n_test != 10000:
raise ValueError(f"Expected 10,000 test samples; got {n_test}")
y_train = train_dataset.targets.long()
y_test = test_dataset.targets.long()
min_tr, max_tr = int(y_train.min()), int(y_train.max())
min_te, max_te = int(y_test.min()), int(y_test.max())
if min_tr != 0 or max_tr != 9:
raise ValueError(f"Train label range must be [0, 9]; got [{min_tr}, {max_tr}]")
if min_te != 0 or max_te != 9:
raise ValueError(f"Test label range must be [0, 9]; got [{min_te}, {max_te}]")
x_train_raw = train_dataset.data.float() / 255.0 # (60000, 28, 28)
x_test_raw = test_dataset.data.float() / 255.0 # (10000, 28, 28)
# Compute normalization statistics from TRAIN split only
mean_val = float(x_train_raw.mean())
std_val = float(x_train_raw.std())
x_train_norm = ((x_train_raw - mean_val) / std_val).unsqueeze(1) # (60000, 1, 28, 28)
x_test_norm = ((x_test_raw - mean_val) / std_val).unsqueeze(1) # (10000, 1, 28, 28)
data_fp = compute_data_fingerprint(x_train_norm, x_test_norm, y_train, y_test)
normalization_provenance = {
"input_shape": [1, 28, 28],
"pca": False,
"raw_inputs": True,
"normalization_scope": "official_train_split_60000_only",
"train_mean": round(mean_val, 6),
"train_std": round(std_val, 6),
"train_samples": 60000,
"test_samples": 10000,
}
return x_train_norm, y_train, x_test_norm, y_test, data_fp, normalization_provenance
# ==========================================
# Architectures (No BatchNorm / No Dropout)
# ==========================================
def count_parameters(model: nn.Module) -> int:
return sum(p.numel() for p in model.parameters() if p.requires_grad)
def make_raw_linear(seed: int = 41) -> nn.Module:
torch.manual_seed(seed)
return nn.Sequential(
nn.Flatten(),
nn.Linear(784, 10),
)
def make_raw_mlp(seed: int = 41) -> nn.Module:
torch.manual_seed(seed)
return nn.Sequential(
nn.Flatten(),
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10),
)
class CompactCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 8, kernel_size=3, padding=1)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(8, 16, kernel_size=3, padding=1)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(2, 2)
self.flatten = nn.Flatten()
self.fc = nn.Linear(784, 10)
def forward(self, x: torch.Tensor) -> torch.Tensor:
if x.dim() == 2 and x.shape[1] == 784:
x = x.view(-1, 1, 28, 28)
out = self.pool1(self.relu1(self.conv1(x)))
out = self.pool2(self.relu2(self.conv2(out)))
out = self.flatten(out)
return self.fc(out)
def make_compact_cnn(seed: int = 41) -> nn.Module:
torch.manual_seed(seed)
return CompactCNN()
# ==========================================
# Evaluation & Training Routines
# ==========================================
def evaluate_model_on_test(
model: nn.Module,
x_test: torch.Tensor,
y_test: torch.Tensor,
device: torch.device,
batch_size: int = 512,
) -> Tuple[float, float]:
"""
Evaluates model on test data without gradients.
Returns (test_loss, test_accuracy).
"""
model.to(device)
model.eval()
criterion = nn.CrossEntropyLoss()
total_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
n_test = x_test.shape[0]
for i in range(0, n_test, batch_size):
bx = x_test[i : i + batch_size].to(device)
by = y_test[i : i + batch_size].to(device)
outputs = model(bx)
loss = criterion(outputs, by)
total_loss += loss.item() * bx.size(0)
preds = outputs.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
avg_loss = total_loss / total if total > 0 else 0.0
accuracy = correct / total if total > 0 else 0.0
return float(avg_loss), float(accuracy)
def train_adam_routine(
model: nn.Module,
x_train: torch.Tensor,
y_train: torch.Tensor,
x_test: torch.Tensor,
y_test: torch.Tensor,
epochs: int,
batch_size: int,
lr: float,
seed: int,
device: torch.device,
) -> Tuple[List[Dict[str, Any]], float, float, float]:
"""
Standard full-data Adam training routine with CrossEntropyLoss, Adam lr, fixed no-scheduler contract.
Returns (history, final_test_loss, final_test_acc, elapsed_sec).
"""
model.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
generator = torch.Generator().manual_seed(seed)
n_train = x_train.shape[0]
history = []
init_loss, init_acc = evaluate_model_on_test(model, x_test, y_test, device)
history.append({"epoch": 0, "test_loss": round(init_loss, 6), "test_acc": round(init_acc, 6)})
sync_device(device)
t0 = time.time()
for epoch in range(1, epochs + 1):
model.train()
perm = torch.randperm(n_train, generator=generator)
for i in range(0, n_train, batch_size):
indices = perm[i : i + batch_size]
bx = x_train[indices].to(device)
by = y_train[indices].to(device)
optimizer.zero_grad()
outputs = model(bx)
loss = criterion(outputs, by)
loss.backward()
optimizer.step()
test_loss, test_acc = evaluate_model_on_test(model, x_test, y_test, device)
history.append({
"epoch": epoch,
"test_loss": round(test_loss, 6),
"test_acc": round(test_acc, 6),
})
sync_device(device)
elapsed = time.time() - t0
final_loss = history[-1]["test_loss"]
final_acc = history[-1]["test_acc"]
return history, final_loss, final_acc, elapsed
def run_pso_routine(
model: nn.Module,
x_train: torch.Tensor,
y_train: torch.Tensor,
x_test: torch.Tensor,
y_test: torch.Tensor,
pso_epochs: int,
n_particles: int,
fitness_size: int,
seed: int,
device: torch.device,
) -> Tuple[nn.Module, Dict[str, float], float, float, Dict[str, Any]]:
"""
PSO-only adaptive_moment on fixed train subset without gradient refinement.
Returns (best_model, fitness_score_dict, test_loss, test_acc, pso_metadata).
"""
model.to(device)
loss_fn = nn.CrossEntropyLoss()
opt = Optimizer(
model=model,
loss=loss_fn,
task="multiclass",
method="adaptive_moment",
evaluation="fixed_subset",
fitness_size=fitness_size,
n_particles=n_particles,
c0=1.49618,
c1=1.49618,
w_min=0.7298,
w_max=0.7298,
particle_min=-3.0,
particle_max=3.0,
boundary_strategy="reflect",
velocity_limit_ratio=0.025,
mutation_swarm=0.02,
initialization="model_noise",
initial_position_noise=0.05,
moment_blend=0.06,
moment_step_size=0.5,
moment_beta1=0.9,
seed=seed,
device=device,
)
t0 = time.time()
opt.fit(x_train, y_train, epochs=pso_epochs)
sync_device(device)
elapsed = time.time() - t0
best_model = opt.get_best_model()
best_score_tuple = opt.get_best_score()
fitness_score = {
"subset_loss": round(float(best_score_tuple[0]), 6),
"subset_acc": round(float(best_score_tuple[1]), 6),
"subset_mse": round(float(best_score_tuple[2]), 6),
}
test_loss, test_acc = evaluate_model_on_test(best_model, x_test, y_test, device)
plugin_meta = extract_plugin_metadata(opt)
pso_meta = {
"elapsed_sec": round(elapsed, 4),
"particles": n_particles,
"pso_epochs": pso_epochs,
"fitness_size": fitness_size,
"plugins": plugin_meta,
}
return best_model, fitness_score, float(test_loss), float(test_acc), pso_meta
# ==========================================
# Output Generation (CSV, Plot)
# ==========================================
def save_csv_records(records: List[Dict[str, Any]], csv_path: Path):
csv_path.parent.mkdir(parents=True, exist_ok=True)
fieldnames = [
"lane",
"profile_or_arch",
"seed",
"model_name",
"param_count",
"initial_test_acc",
"final_test_acc",
"final_test_loss",
"subset_fitness_acc",
"subset_fitness_loss",
"pso_epochs",
"adam_epochs",
"elapsed_sec",
"model_fingerprint",
"data_fingerprint",
]
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for r in records:
writer.writerow({
"lane": r.get("lane"),
"profile_or_arch": r.get("profile_or_arch"),
"seed": r.get("seed"),
"model_name": r.get("model_name"),
"param_count": r.get("param_count"),
"initial_test_acc": r.get("initial_test_acc"),
"final_test_acc": r.get("final_test_acc"),
"final_test_loss": r.get("final_test_loss"),
"subset_fitness_acc": r.get("subset_fitness_acc"),
"subset_fitness_loss": r.get("subset_fitness_loss"),
"pso_epochs": r.get("pso_epochs"),
"adam_epochs": r.get("adam_epochs"),
"elapsed_sec": r.get("elapsed_sec"),
"model_fingerprint": r.get("model_fingerprint"),
"data_fingerprint": r.get("data_fingerprint"),
})
def render_plots(
arch_summary: Dict[str, Dict[str, float]],
opt_summary: Dict[str, Dict[str, float]],
figure_path: Path,
):
figure_path.parent.mkdir(parents=True, exist_ok=True)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
if arch_summary:
arch_labels = list(arch_summary.keys())
arch_means = [arch_summary[k]["mean"] * 100 for k in arch_labels]
arch_sds = [arch_summary[k]["std"] * 100 for k in arch_labels]
arch_display = {
"raw_linear": "Raw Linear",
"raw_mlp": "Raw MLP",
"compact_cnn": "Compact CNN",
}
x_arch = np.arange(len(arch_labels))
ax1.bar(x_arch, arch_means, yerr=arch_sds, capsize=5, color="#56B4E9", edgecolor="black", alpha=0.85)
ax1.axhline(98.0, color="red", linestyle="--", linewidth=1.5, label="98% Target")
ax1.set_xticks(x_arch)
ax1.set_xticklabels([arch_display[k] for k in arch_labels], rotation=15)
ax1.set_ylabel("Final Test Accuracy (%)")
ax1.set_title("Architecture Lane (Full-Data Adam)")
ax1.set_ylim(0, 110)
ax1.set_axisbelow(True)
ax1.grid(axis="y", linestyle=":", alpha=0.6)
ax1.legend(loc="lower left")
for i, (m, sd) in enumerate(zip(arch_means, arch_sds)):
ax1.text(
i,
105.0 if m >= 90.0 else min(m + sd + 1.5, 102.5),
f"{m:.2f}%",
ha="center",
va="bottom",
fontsize=9,
fontweight="bold",
bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.9, "pad": 1.5},
)
if opt_summary:
opt_labels = list(opt_summary.keys())
opt_means = [opt_summary[k]["mean"] * 100 for k in opt_labels]
opt_sds = [opt_summary[k]["std"] * 100 for k in opt_labels]
opt_display = {
"adam_only": "Adam Only",
"pso_only": "PSO Only",
"hybrid": "PSO → Adam",
}
x_opt = np.arange(len(opt_labels))
colors = ["#009E73", "#E69F00", "#CC79A7"]
ax2.bar(x_opt, opt_means, yerr=opt_sds, capsize=5, color=colors[:len(opt_labels)], edgecolor="black", alpha=0.85)
ax2.axhline(98.0, color="red", linestyle="--", linewidth=1.5, label="98% Target")
ax2.set_xticks(x_opt)
ax2.set_xticklabels([opt_display[k] for k in opt_labels], rotation=15)
ax2.set_ylabel("Final Test Accuracy (%)")
ax2.set_title("Optimizer Lane (Compact CNN)")
ax2.set_ylim(0, 110)
ax2.set_axisbelow(True)
ax2.grid(axis="y", linestyle=":", alpha=0.6)
ax2.legend(loc="lower left")
for i, (m, sd) in enumerate(zip(opt_means, opt_sds)):
ax2.text(
i,
105.0 if m >= 90.0 else min(m + sd + 1.5, 102.5),
f"{m:.2f}%",
ha="center",
va="bottom",
fontsize=9,
fontweight="bold",
bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.9, "pad": 1.5},
)
plt.suptitle("MNIST Deep Accuracy Study: Architectures & Optimizer Profiles", fontsize=14, fontweight="bold")
plt.tight_layout()
fig.savefig(figure_path, dpi=300)
plt.close(fig)
# ==========================================
# Main CLI & Runner
# ==========================================
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="MNIST Deep Accuracy Study: Architecture vs Optimizer Profiles"
)
parser.add_argument(
"--seeds",
type=str,
default="101,102,103",
help="Comma-separated random seeds (default: 101,102,103)",
)
parser.add_argument(
"--adam-epochs",
type=int,
default=10,
help="Full-data Adam training epochs (default: 10)",
)
parser.add_argument(
"--pso-epochs",
type=int,
default=40,
help="PSO swarm optimization epochs (default: 40)",
)
parser.add_argument(
"--particles",
"--n-particles",
type=int,
default=30,
dest="particles",
help="Number of PSO particles (default: 30)",
)
parser.add_argument(
"--fitness-size",
type=int,
default=2000,
help="Fixed train subset fitness size for PSO (default: 2000)",
)
parser.add_argument(
"--batch-size",
type=int,
default=256,
help="Batch size for Adam DataLoader (default: 256)",
)
parser.add_argument(
"--lr",
"--learning-rate",
type=float,
default=1e-3,
dest="lr",
help="Learning rate for Adam optimizer (default: 1e-3)",
)
parser.add_argument(
"--device",
type=str,
default=None,
help="Target PyTorch device ('cpu', 'cuda', 'mps'; default: auto-detect)",
)
parser.add_argument(
"--json-path",
type=str,
default="benchmark_results/pso_v4_deep_accuracy.json",
help="JSON result output path (default: benchmark_results/pso_v4_deep_accuracy.json)",
)
parser.add_argument(
"--csv-path",
type=str,
default="benchmark_results/pso_v4_deep_accuracy.csv",
help="CSV result output path (default: benchmark_results/pso_v4_deep_accuracy.csv)",
)
parser.add_argument(
"--figure-path",
"--plot-path",
type=str,
default="history_plt/pso_v4_deep_accuracy.png",
dest="figure_path",
help="Figure output path (default: history_plt/pso_v4_deep_accuracy.png)",
)
parser.add_argument(
"--lanes",
"--profiles",
type=str,
choices=["all", "architectures", "optimizers"],
default="all",
help="Lanes/profiles to evaluate (choices: all, architectures, optimizers; default: all)",
)
return parser.parse_args()
def main():
args = parse_args()
seed_list = [int(s.strip()) for s in args.seeds.split(",") if s.strip()]
if not seed_list:
raise ValueError("--seeds must contain at least one integer")
if len(seed_list) != len(set(seed_list)):
raise ValueError("--seeds must not contain duplicates")
if any(seed < 0 for seed in seed_list):
raise ValueError("--seeds values must be non-negative")
for name, value in (
("--adam-epochs", args.adam_epochs),
("--pso-epochs", args.pso_epochs),
("--particles", args.particles),
("--fitness-size", args.fitness_size),
("--batch-size", args.batch_size),
):
if value <= 0:
raise ValueError(f"{name} must be positive")
if not math.isfinite(args.lr) or args.lr <= 0.0:
raise ValueError("--lr must be a positive finite number")
device = resolve_execution_device(args.device)
json_path = Path(args.json_path)
csv_path = Path(args.csv_path)
figure_path = Path(args.figure_path)
print(f"=== Starting MNIST Deep Accuracy Study (Protocol v{DEEP_ACCURACY_PROTOCOL_VERSION}) ===")
print(f"Device: {device} | Seeds: {seed_list} | Adam Epochs: {args.adam_epochs} | PSO Epochs: {args.pso_epochs}")
print(f"Particles: {args.particles} | Fitness Size: {args.fitness_size} | Batch Size: {args.batch_size} | LR: {args.lr}")
# Prepare data
x_train, y_train, x_test, y_test, data_fp, norm_provenance = prepare_deep_accuracy_mnist_data()
print(f"Data Loaded: Train {x_train.shape[0]} / Test {x_test.shape[0]} | Fingerprint: {data_fp[:12]}...")
hardware_prov = get_hardware_provenance(device)
all_csv_records: List[Dict[str, Any]] = []
architecture_lane_runs: List[Dict[str, Any]] = []
optimizer_lane_runs: List[Dict[str, Any]] = []
# Map architecture factories
arch_factories = {
"raw_linear": ("Raw Linear (784->10)", make_raw_linear),
"raw_mlp": ("Raw MLP (784->128->64->10)", make_raw_mlp),
"compact_cnn": ("Compact CNN (9,098 params)", make_compact_cnn),
}
try:
# ----------------------------------------------------
# 1. Architecture Lane
# ----------------------------------------------------
compact_cnn_adam_cache: Dict[int, Dict[str, Any]] = {}
if args.lanes in ("all", "architectures"):
print("\n--- Running Architecture Lane (Full-Data Adam) ---")
for arch_key, (arch_name, factory) in arch_factories.items():
for s in seed_list:
# Construct base model and record initial fingerprint
model = factory(seed=s)
p_count = count_parameters(model)
init_fp = compute_model_fingerprint(model)
init_loss, init_acc = evaluate_model_on_test(model, x_test, y_test, device)
print(f"[Arch: {arch_key} | Seed: {s}] Params: {p_count} | Init Acc: {init_acc*100:.2f}% | Training Adam...")
history, final_loss, final_acc, elapsed = train_adam_routine(
model=model,
x_train=x_train,
y_train=y_train,
x_test=x_test,
y_test=y_test,
epochs=args.adam_epochs,
batch_size=args.batch_size,
lr=args.lr,
seed=s,
device=device,
)
run_record = {
"lane": "architecture",
"profile_or_arch": arch_key,
"seed": s,
"model_name": arch_name,
"param_count": p_count,
"initial_test_acc": round(init_acc, 6),
"final_test_acc": round(final_acc, 6),
"final_test_loss": round(final_loss, 6),
"subset_fitness_acc": None,
"subset_fitness_loss": None,
"pso_epochs": 0,
"adam_epochs": args.adam_epochs,
"elapsed_sec": round(elapsed, 4),
"model_fingerprint": init_fp,
"data_fingerprint": data_fp,
"epoch_history": history,
}
architecture_lane_runs.append(run_record)
all_csv_records.append(run_record)
if arch_key == "compact_cnn":
compact_cnn_adam_cache[s] = run_record
print(f" -> Final Test Acc: {final_acc*100:.2f}% | Loss: {final_loss:.4f} | Time: {elapsed:.2f}s")
# ----------------------------------------------------
# 2. Optimizer Lane (Compact CNN)
# ----------------------------------------------------
if args.lanes in ("all", "optimizers"):
print("\n--- Running Optimizer Lane (Compact CNN) ---")
profiles = ["adam_only", "pso_only", "hybrid"]
for prof in profiles:
for s in seed_list:
# Construct Compact CNN with seed s to ensure same base initial state
model_base = make_compact_cnn(seed=s)
p_count = count_parameters(model_base)
init_fp = compute_model_fingerprint(model_base)
init_loss, init_acc = evaluate_model_on_test(model_base, x_test, y_test, device)
if prof == "adam_only":
if s in compact_cnn_adam_cache:
# Explicitly reuse record from Architecture Lane
cached = compact_cnn_adam_cache[s]
run_record = {
"lane": "optimizer",
"profile_or_arch": "adam_only",
"seed": s,
"model_name": "Compact CNN (Adam-Only)",
"param_count": p_count,
"initial_test_acc": cached["initial_test_acc"],
"final_test_acc": cached["final_test_acc"],
"final_test_loss": cached["final_test_loss"],
"subset_fitness_acc": None,
"subset_fitness_loss": None,
"pso_epochs": 0,
"adam_epochs": args.adam_epochs,
"elapsed_sec": cached["elapsed_sec"],
"model_fingerprint": init_fp,
"data_fingerprint": data_fp,
"reused_from_architecture_lane": True,
"epoch_history": cached["epoch_history"],
}
print(f"[Opt: adam_only | Seed: {s}] Reused from Architecture Lane | Final Acc: {cached['final_test_acc']*100:.2f}%")
else:
print(f"[Opt: adam_only | Seed: {s}] Training Adam...")
history, final_loss, final_acc, elapsed = train_adam_routine(
model=model_base,
x_train=x_train,
y_train=y_train,
x_test=x_test,
y_test=y_test,
epochs=args.adam_epochs,
batch_size=args.batch_size,
lr=args.lr,
seed=s,
device=device,
)
run_record = {
"lane": "optimizer",
"profile_or_arch": "adam_only",
"seed": s,
"model_name": "Compact CNN (Adam-Only)",
"param_count": p_count,
"initial_test_acc": round(init_acc, 6),
"final_test_acc": round(final_acc, 6),
"final_test_loss": round(final_loss, 6),
"subset_fitness_acc": None,
"subset_fitness_loss": None,
"pso_epochs": 0,
"adam_epochs": args.adam_epochs,
"elapsed_sec": round(elapsed, 4),
"model_fingerprint": init_fp,
"data_fingerprint": data_fp,
"reused_from_architecture_lane": False,
"epoch_history": history,
}
print(f" -> Final Test Acc: {final_acc*100:.2f}% | Loss: {final_loss:.4f} | Time: {elapsed:.2f}s")
optimizer_lane_runs.append(run_record)
all_csv_records.append(run_record)
elif prof == "pso_only":
print(
f"[Opt: pso_only | Seed: {s}] Running PSO Adaptive Moment "
f"on {args.fitness_size:,} fixed-subset samples..."
)
best_model, fitness_score, pso_test_loss, pso_test_acc, pso_meta = run_pso_routine(
model=model_base,
x_train=x_train,
y_train=y_train,
x_test=x_test,
y_test=y_test,
pso_epochs=args.pso_epochs,
n_particles=args.particles,
fitness_size=args.fitness_size,
seed=s,
device=device,
)
run_record = {
"lane": "optimizer",
"profile_or_arch": "pso_only",
"seed": s,
"model_name": "Compact CNN (PSO-Only)",
"param_count": p_count,
"initial_test_acc": round(init_acc, 6),
"final_test_acc": round(pso_test_acc, 6),
"final_test_loss": round(pso_test_loss, 6),
"subset_fitness_acc": fitness_score["subset_acc"],
"subset_fitness_loss": fitness_score["subset_loss"],
"pso_epochs": args.pso_epochs,
"adam_epochs": 0,
"elapsed_sec": pso_meta["elapsed_sec"],
"model_fingerprint": init_fp,
"data_fingerprint": data_fp,
"pso_metadata": pso_meta,
}
optimizer_lane_runs.append(run_record)
all_csv_records.append(run_record)
print(f" -> Fitness Subset Acc: {fitness_score['subset_acc']*100:.2f}% | Full Test Acc: {pso_test_acc*100:.2f}% | Time: {pso_meta['elapsed_sec']:.2f}s")
elif prof == "hybrid":
print(f"[Opt: hybrid | Seed: {s}] Running Hybrid (PSO Warm Start + Adam Fine-Tuning)...")
# 1. PSO Warm Start
t_hyb_start = time.time()
pso_best_model, fitness_score, pso_test_loss, pso_test_acc, pso_meta = run_pso_routine(
model=model_base,
x_train=x_train,
y_train=y_train,
x_test=x_test,
y_test=y_test,
pso_epochs=args.pso_epochs,
n_particles=args.particles,
fitness_size=args.fitness_size,
seed=s,
device=device,
)
# 2. Continue with full-data Adam
post_adam_history, final_test_loss, final_test_acc, adam_elapsed = train_adam_routine(
model=pso_best_model,
x_train=x_train,
y_train=y_train,
x_test=x_test,
y_test=y_test,
epochs=args.adam_epochs,
batch_size=args.batch_size,
lr=args.lr,
seed=s,
device=device,
)
hyb_total_elapsed = time.time() - t_hyb_start
run_record = {
"lane": "optimizer",
"profile_or_arch": "hybrid",
"seed": s,
"model_name": "Compact CNN (Hybrid)",
"param_count": p_count,
"initial_test_acc": round(init_acc, 6),
"final_test_acc": round(final_test_acc, 6),
"final_test_loss": round(final_test_loss, 6),
"subset_fitness_acc": fitness_score["subset_acc"],
"subset_fitness_loss": fitness_score["subset_loss"],
"pso_epochs": args.pso_epochs,
"adam_epochs": args.adam_epochs,
"elapsed_sec": round(hyb_total_elapsed, 4),
"model_fingerprint": init_fp,
"data_fingerprint": data_fp,
"post_pso_test_acc": round(pso_test_acc, 6),
"post_pso_test_loss": round(pso_test_loss, 6),
"post_adam_history": post_adam_history,
"efficiency_label": "HYBRID_GETS_EXTRA_WORK_UNFAIR_EFFICIENCY_COMPARISON",
}
optimizer_lane_runs.append(run_record)
all_csv_records.append(run_record)
print(f" -> Post-PSO Test Acc: {pso_test_acc*100:.2f}% | Final Hybrid Test Acc: {final_test_acc*100:.2f}% | Time: {hyb_total_elapsed:.2f}s")
# Compute summary statistics
arch_summary: Dict[str, Dict[str, float]] = {}
for arch_key in arch_factories.keys():
vals = [r["final_test_acc"] for r in architecture_lane_runs if r["profile_or_arch"] == arch_key]
if vals:
arch_summary[arch_key] = calc_stats(vals)
opt_summary: Dict[str, Dict[str, float]] = {}
for prof in ["adam_only", "pso_only", "hybrid"]:
vals = [r["final_test_acc"] for r in optimizer_lane_runs if r["profile_or_arch"] == prof]
if vals:
opt_summary[prof] = calc_stats(vals)
# Structure complete JSON output
result_payload = {
"protocol_version": DEEP_ACCURACY_PROTOCOL_VERSION,
"pso_version": pso_version,
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"completed": True,
"error": None,
"hardware_provenance": hardware_prov,
"data_provenance": norm_provenance,
"data_fingerprint": data_fp,
"configuration": {
"seeds": seed_list,
"adam_epochs": args.adam_epochs,
"pso_epochs": args.pso_epochs,
"particles": args.particles,
"fitness_size": args.fitness_size,
"batch_size": args.batch_size,
"lr": args.lr,
"device": str(device),
},
"summaries": {
"architecture_lane": arch_summary,
"optimizer_lane": opt_summary,
},
"architecture_lane_runs": architecture_lane_runs,
"optimizer_lane_runs": optimizer_lane_runs,
}
# Save JSON output atomically
save_json_atomic(result_payload, json_path)
print(f"\nSaved JSON results to {json_path}")
# Save CSV records and render plot only after all runs succeed
save_csv_records(all_csv_records, csv_path)
print(f"Saved CSV records to {csv_path}")
render_plots(arch_summary, opt_summary, figure_path)
print(f"Saved summary figure to {figure_path}")
# Print final summary table
print("\n========================================================")
print(" FINAL SUMMARY TABLE ")
print("========================================================")
if arch_summary:
print("Architecture Lane (Full-Data Adam):")
for arch_key, stats in arch_summary.items():
print(f" - {arch_key:15s}: Mean Acc = {stats['mean']*100:6.2f}% ± {stats['std']*100:5.2f}% (Median: {stats['median']*100:.2f}%)")
if opt_summary:
print("\nOptimizer Lane (Compact CNN):")
for prof, stats in opt_summary.items():
print(f" - {prof:15s}: Mean Acc = {stats['mean']*100:6.2f}% ± {stats['std']*100:5.2f}% (Median: {stats['median']*100:.2f}%)")
print("========================================================\n")
except Exception as e:
error_payload = {
"protocol_version": DEEP_ACCURACY_PROTOCOL_VERSION,
"pso_version": pso_version,
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"completed": False,
"error": str(e),
"hardware_provenance": hardware_prov,
"configuration": {
"seeds": seed_list,
"adam_epochs": args.adam_epochs,
"pso_epochs": args.pso_epochs,
"particles": args.particles,
"fitness_size": args.fitness_size,
"batch_size": args.batch_size,
"lr": args.lr,
"device": str(device),
},
"architecture_lane_runs": architecture_lane_runs,
"optimizer_lane_runs": optimizer_lane_runs,
}
save_json_atomic(error_payload, json_path)
print(f"\n[ERROR] Study failed: {e}")
print(f"Saved failure audit record to {json_path}")
sys.exit(1)
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+1464
View File
File diff suppressed because it is too large Load Diff
+94 -54
View File
@@ -1,71 +1,111 @@
import os import argparse
import sys import torch
import torch.nn as nn
from keras.layers import Dense
from keras.models import Sequential
from keras.utils import to_categorical
from sklearn.datasets import load_digits from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from pso import optimizer from pso import Optimizer
from cli import add_pso_args, build_optimizer_kwargs
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
def make_model(): def make_model(seed: int = 42):
model = Sequential() torch.manual_seed(seed)
model.add(Dense(12, input_dim=64, activation="relu")) return nn.Sequential(
model.add(Dense(10, activation="relu")) nn.Linear(64, 12),
model.add(Dense(10, activation="softmax")) nn.ReLU(),
nn.Linear(12, 10),
return model nn.ReLU(),
nn.Linear(10, 10),
)
def get_data(): def get_data(seed: int = 42):
digits = load_digits() digits = load_digits()
X = digits.data x = digits.data.astype("float32")
y = digits.target y = digits.target.astype("int64")
x = X.astype("float32")
y_class = to_categorical(y)
x_train, x_test, y_train, y_test = train_test_split( x_train, x_test, y_train, y_test = train_test_split(
x, y_class, test_size=0.2, random_state=42, shuffle=True x, y, test_size=0.2, random_state=seed, shuffle=True
)
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(y_test, dtype=torch.int64),
) )
return x_train, x_test, y_train, y_test
x_train, x_test, y_train, y_test = get_data() def main():
model = make_model() parser = argparse.ArgumentParser(description="PSO Digits Benchmark Script")
add_pso_args(
parser,
defaults={
"method": "original",
"initialization": "model_noise",
"evaluation": "fixed_subset",
"convergence": "particle_reset",
"refinement": "adam",
"n_particles": 30,
"c0": None,
"c1": None,
"w_min": None,
"w_max": None,
"negative_swarm": 0.0,
"mutation_swarm": 0.1,
"particle_min": -3.0,
"particle_max": 3.0,
"velocity_limit_ratio": 0.1,
"boundary_strategy": "reflect",
"seed": 42,
"epochs": 80,
"batch_size": 200,
"fitness_size": 1000,
"renewal": "loss",
"output_dir": "output/digits",
"refinement_epochs": 10,
"refinement_lr": 0.001,
},
)
args = parser.parse_args()
digits_pso = optimizer( x_train, x_test, y_train, y_test = get_data(seed=args.seed)
model, model = make_model(seed=args.seed)
loss="categorical_crossentropy",
n_particles=300,
c0=0.5,
c1=0.3,
w_min=0.2,
w_max=0.9,
negative_swarm=0,
mutation_swarm=0.1,
convergence_reset=True,
convergence_reset_patience=10,
convergence_reset_monitor="loss",
convergence_reset_min_delta=0.001,
)
digits_pso.fit( fitness_size = args.fitness_size if args.evaluation == "fixed_subset" else None
x_train, refinement_epochs = args.refinement_epochs if args.refinement == "adam" else 0
y_train,
epochs=500,
validate_data=(x_test, y_test),
log=2,
save_info=True,
renewal="loss",
log_name="digits",
)
print("Done!") kwargs = build_optimizer_kwargs(
args,
model=model,
loss=nn.CrossEntropyLoss(),
task="multiclass",
inertia_profile={"c0": 0.5, "c1": 0.3, "w_min": 0.2, "w_max": 0.9},
)
digits_pso = Optimizer(**kwargs)
sys.exit(0) print(f"Optimizer device: {digits_pso.device}")
best_score = digits_pso.fit(
x_train,
y_train,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=fitness_size,
renewal=args.renewal,
validation_data=(x_test, y_test),
output_dir=args.output_dir,
save_info=True,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
print(f"Done! Best score: {best_score}")
if __name__ == "__main__":
main()
-81
View File
@@ -1,81 +0,0 @@
import os
import sys
import pandas as pd
import tensorflow as tf
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import to_categorical
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
tf.config.experimental.set_memory_growth(gpus[0], True)
except RuntimeError as r:
print(r)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
def make_model():
model = Sequential()
model.add(Dense(12, input_dim=64, activation="relu"))
model.add(Dense(12, activation="relu"))
model.add(Dense(10, activation="softmax"))
return model
def get_data():
digits = load_digits()
X = digits.data
y = digits.target
x = X.astype("float32")
y_class = to_categorical(y)
x_train, x_test, y_train, y_test = train_test_split(
x, y_class, test_size=0.2, random_state=42, shuffle=True
)
return x_train, x_test, y_train, y_test
if __name__ == "__main__":
model = make_model()
x_train, x_test, y_train, y_test = get_data()
callbacks = [
tf.keras.callbacks.EarlyStopping(
monitor="val_loss", patience=10, restore_best_weights=True
)
]
print(x_train.shape, y_train.shape)
model.compile(
optimizer="adam",
loss="categorical_crossentropy",
metrics=["accuracy", "mse"],
)
print(model.summary())
history = model.fit(
x_train,
y_train,
epochs=500,
batch_size=32,
verbose=1,
validation_data=(x_test, y_test),
callbacks=callbacks,
)
print("Done!")
sys.exit(0)
+134
View File
@@ -0,0 +1,134 @@
"""Digits dataset gradient baseline (PyTorch standard backprop optimizer, non-PSO)."""
import copy
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
class DigitsModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(64, 12),
nn.ReLU(),
nn.Linear(12, 12),
nn.ReLU(),
nn.Linear(12, 10),
)
def forward(self, x):
return self.net(x)
def get_data(seed: int = 42):
digits = load_digits()
X = digits.data.astype("float32")
y = digits.target.astype("int64")
x_train, x_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=seed, shuffle=True
)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(y_test, dtype=torch.int64),
)
def get_device() -> torch.device:
if (
hasattr(torch.backends, "mps")
and torch.backends.mps.is_built()
and torch.backends.mps.is_available()
):
return torch.device("mps")
elif torch.cuda.is_available():
return torch.device("cuda")
return torch.device("cpu")
def main():
torch.manual_seed(42)
np.random.seed(42)
device = get_device()
print(f"Selected device: {device}")
x_train, x_test, y_train, y_test = get_data(seed=42)
train_loader = DataLoader(
TensorDataset(x_train, y_train), batch_size=32, shuffle=True
)
val_loader = DataLoader(
TensorDataset(x_test, y_test), batch_size=32, shuffle=False
)
model = DigitsModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
best_val_loss = float("inf")
best_state = None
patience = 10
patience_counter = 0
for epoch in range(500):
model.train()
for bx, by in train_loader:
bx, by = bx.to(device), by.to(device)
optimizer.zero_grad()
out = model(bx)
loss = criterion(out, by)
loss.backward()
optimizer.step()
model.eval()
val_loss = 0.0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
val_loss += loss.item() * bx.size(0)
total += bx.size(0)
val_loss /= total
if val_loss < best_val_loss:
best_val_loss = val_loss
best_state = copy.deepcopy(model.state_dict())
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= patience:
break
if best_state is not None:
model.load_state_dict(best_state)
model.eval()
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
test_loss += loss.item() * bx.size(0)
preds = out.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
test_loss /= total
test_acc = correct / total
print(f"Final test loss: {test_loss:.4f}, accuracy: {test_acc:.4f}")
if __name__ == "__main__":
main()
+673
View File
@@ -0,0 +1,673 @@
"""
Adaptive Moment 120-Particle x 240-Epoch MNIST Convergence & Trajectory Analysis
Evaluates whether the published 120-particle x 80-epoch MNIST PCA32 Adaptive Moment run
benefits from continued optimization up to 240 epochs or enters a generalization plateau.
Predeclared Contract Criteria:
1. Exact Replay Verification at Epoch 80 (seeds 71-75):
Per-seed test accuracy absolute delta vs baseline <= 0.005 (0.5%p).
2. Primary Diagnostic Endpoints: Epochs 80, 120, 160, 200, 240.
3. Classifications:
- Training Still Improving: Mean global-best fitness loss falls >= 1% from epoch 80 to 240.
- Meaningful Held-Out Gain: Mean test accuracy at epoch 240 rises >= 1 percentage point vs epoch 80.
- Overfitting Signal: Training loss improves but epoch 240 test accuracy falls >= 1 point.
- Early Stagnation: Training loss improves < 1% and absolute test change remains < 1 point.
- Generalization Plateau: Training loss improves >= 1% while absolute test gain remains < 1 point.
- Late Plateau Diagnostic: 200->240 training-loss improvement < 1% AND absolute test-accuracy change < 0.5 points.
"""
import argparse
import csv
import datetime
import sys
import tempfile
import time
from pathlib import Path
from typing import Any, Dict, List
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
# Ensure test/ directory is in sys.path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from benchmark_suite import (
calc_stats,
compute_model_fingerprint,
get_hardware_provenance,
make_mnist_model,
resolve_execution_device,
save_json_atomic,
sync_device,
)
from pso import Optimizer, __version__ as pso_version
from reproduce_scaling import (
REPLAY_SEEDS,
REPLAY_TOLERANCE,
prepare_full_pca_data,
validate_and_load_baseline,
)
from tuning_suite import TUNING_PROTOCOL_VERSION
EPOCH_CONVERGENCE_PROTOCOL_VERSION = "1.0.0"
CHECKPOINT_EPOCHS = [20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240]
POST80_LOSS_REDUCTION_THRESHOLD = 0.01
TEST_ACCURACY_GAIN_THRESHOLD = 0.01
OVERFITTING_ACCURACY_DROP_THRESHOLD = -0.01
LATE_LOSS_REDUCTION_THRESHOLD = 0.01
LATE_ACCURACY_CHANGE_THRESHOLD = 0.005
def run_epoch_convergence_analysis(
baseline_path: Path,
output_json_path: Path,
output_csv_path: Path,
figure_path: Path,
device_str: str | None = None,
) -> bool:
device = resolve_execution_device(device_str)
hw_provenance = get_hardware_provenance(device)
# 1. Validate and load baseline
baseline_data, baseline_records, winner_cfg, expected_fp = validate_and_load_baseline(
baseline_path
)
if baseline_data.get("device") != device.type:
raise ValueError(
f"Exact trajectory extension requires baseline device "
f"{baseline_data.get('device')!r}; got {device.type!r}."
)
if baseline_data.get("pso_version") != pso_version:
raise ValueError(
f"Exact trajectory extension requires pso version "
f"{baseline_data.get('pso_version')!r}; got {pso_version!r}."
)
if baseline_data.get("torch_version") != torch.__version__:
raise ValueError(
f"Exact trajectory extension requires torch version "
f"{baseline_data.get('torch_version')!r}; got {torch.__version__!r}."
)
base_rec_by_seed = {r["seed"]: r for r in baseline_records}
# 2. Prepare full PCA dataset
x_full_tr, y_train_3000, x_full_test, y_test_1000, data_fp = prepare_full_pca_data()
if data_fp != expected_fp:
raise ValueError(
f"Data fingerprint mismatch: computed {data_fp}, baseline expected {expected_fp}"
)
opt_kwargs = winner_cfg.to_optimizer_kwargs(quick=False)
n_particles = 120
target_epochs = 240
batch_size = 1000
runs: List[Dict[str, Any]] = []
flat_csv_rows: List[Dict[str, Any]] = []
fidelity_passed = True
seed_fidelity_deltas: Dict[int, float] = {}
# 3. Process seeds 71-75
for seed in sorted(REPLAY_SEEDS):
base_rec = base_rec_by_seed[seed]
expected_model_fp = base_rec["model_fingerprint"]
baseline_test_acc = float(base_rec["test_acc"])
# --- Untimed 2-Epoch Warmup Phase ---
warmup_model = make_mnist_model(seed=seed)
warmup_loss = nn.CrossEntropyLoss()
warmup_opt = Optimizer(
model=warmup_model,
loss=warmup_loss,
task="multiclass",
n_particles=n_particles,
seed=seed,
device=device,
**opt_kwargs,
)
warmup_opt.fit(
x_full_tr,
y_train_3000,
epochs=2,
batch_size=batch_size,
renewal="loss",
)
sync_device(device)
del warmup_opt, warmup_model, warmup_loss
# --- Timed 240-Epoch Continuous Trajectory ---
model = make_mnist_model(seed=seed)
model_fp = compute_model_fingerprint(model)
fp_match = (model_fp == expected_model_fp)
if not fp_match:
print(
f"[WARNING] Seed {seed} model fingerprint mismatch: "
f"got {model_fp}, expected {expected_model_fp}"
)
fidelity_passed = False
loss_inst = nn.CrossEntropyLoss()
opt = Optimizer(
model=model,
loss=loss_inst,
task="multiclass",
n_particles=n_particles,
seed=seed,
device=device,
**opt_kwargs,
)
with tempfile.TemporaryDirectory() as temp_dir_str:
output_dir = Path(temp_dir_str)
sync_device(device)
t0 = time.perf_counter()
train_loss_final, train_acc_final, train_mse_final = opt.fit(
x_full_tr,
y_train_3000,
epochs=target_epochs,
batch_size=batch_size,
renewal="loss",
output_dir=output_dir,
log_format="csv",
checkpoint_interval=20,
)
sync_device(device)
t1 = time.perf_counter()
fit_time_sec = t1 - t0
# Read full epoch history from history.csv
history_csv_path = output_dir / "history.csv"
epoch_history = []
with open(history_csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
epoch_history.append({
"epoch": int(row["epoch"]),
"loss": float(row["loss"]),
"accuracy": float(row["accuracy"]),
"mse": float(row["mse"]),
})
# Track training global-best improvement epochs
prev_best_loss = float("inf")
improvement_count = 0
last_improvement_epoch = 1
for row in epoch_history:
ep_num = row["epoch"]
l_val = row["loss"]
if l_val < prev_best_loss:
improvement_count += 1
last_improvement_epoch = ep_num
prev_best_loss = l_val
# Read and evaluate checkpoints
checkpoints: List[Dict[str, Any]] = []
ckpt_dir = output_dir / "checkpoints"
epoch80_test_acc = None
for ep in CHECKPOINT_EPOCHS:
ckpt_path = ckpt_dir / f"epoch-{ep}.pt"
if not ckpt_path.exists():
raise FileNotFoundError(f"Missing checkpoint file: {ckpt_path}")
payload = torch.load(ckpt_path, map_location=device, weights_only=True)
ckpt_train_loss, ckpt_train_acc, ckpt_train_mse = payload["score"]
# Load checkpoint state_dict into opt.eval_model and evaluate on held-out test tensor
opt.eval_model.load_state_dict(payload["model_state_dict"])
opt._global_best_weights = opt.codec.encode(opt.eval_model)
test_loss, test_acc, test_mse = opt.evaluate(x_full_test, y_test_1000)
ckpt_record = {
"epoch": ep,
"train_loss": float(ckpt_train_loss),
"train_acc": float(ckpt_train_acc),
"train_mse": float(ckpt_train_mse),
"test_loss": float(test_loss),
"test_acc": float(test_acc),
"test_mse": float(test_mse),
}
checkpoints.append(ckpt_record)
if ep == 80:
epoch80_test_acc = float(test_acc)
flat_csv_rows.append({
"seed": seed,
"epoch": ep,
"train_loss": float(ckpt_train_loss),
"train_acc": float(ckpt_train_acc),
"train_mse": float(ckpt_train_mse),
"test_loss": float(test_loss),
"test_acc": float(test_acc),
"test_mse": float(test_mse),
"fit_time_sec": round(fit_time_sec, 4),
})
# Fidelity check at epoch 80 vs baseline
assert epoch80_test_acc is not None
delta_ep80 = abs(epoch80_test_acc - baseline_test_acc)
seed_fidelity_deltas[seed] = delta_ep80
if delta_ep80 > REPLAY_TOLERANCE:
print(
f"[WARNING] Seed {seed} epoch 80 test accuracy delta {delta_ep80:.6f} "
f"exceeds tolerance {REPLAY_TOLERANCE} (actual={epoch80_test_acc:.4f}, baseline={baseline_test_acc:.4f})"
)
fidelity_passed = False
runs.append({
"seed": seed,
"model_fingerprint": model_fp,
"expected_model_fingerprint": expected_model_fp,
"fingerprint_matched": fp_match,
"baseline_epoch80_test_acc": baseline_test_acc,
"epoch80_test_acc": epoch80_test_acc,
"epoch80_abs_delta": delta_ep80,
"fit_time_sec": round(fit_time_sec, 4),
"improvement_count": improvement_count,
"last_improvement_epoch": last_improvement_epoch,
"checkpoints": checkpoints,
})
max_ep80_delta = max(seed_fidelity_deltas.values()) if seed_fidelity_deltas else 0.0
# 4. Aggregations and Statistical Summaries
checkpoint_stats: Dict[int, Dict[str, Any]] = {}
for ep in CHECKPOINT_EPOCHS:
ep_train_losses = [next(c["train_loss"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_train_accs = [next(c["train_acc"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_train_mses = [next(c["train_mse"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_losses = [next(c["test_loss"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_accs = [next(c["test_acc"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_mses = [next(c["test_mse"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
checkpoint_stats[ep] = {
"train_loss": calc_stats(ep_train_losses),
"train_acc": calc_stats(ep_train_accs),
"train_mse": calc_stats(ep_train_mses),
"test_loss": calc_stats(ep_test_losses),
"test_acc": calc_stats(ep_test_accs),
"test_mse": calc_stats(ep_test_mses),
}
# Endpoint paired deltas (80->240 and 200->240)
deltas_80_to_240_train_rel = []
deltas_80_to_240_test_acc = []
deltas_200_to_240_train_rel = []
deltas_200_to_240_test_acc = []
deltas_200_to_240_test_acc_abs = []
for r in runs:
ckpt_map = {c["epoch"]: c for c in r["checkpoints"]}
# 80 to 240
tl80 = ckpt_map[80]["train_loss"]
tl240 = ckpt_map[240]["train_loss"]
rel_red_80_240 = (tl80 - tl240) / tl80 if tl80 > 0 else 0.0
deltas_80_to_240_train_rel.append(rel_red_80_240)
ta80 = ckpt_map[80]["test_acc"]
ta240 = ckpt_map[240]["test_acc"]
acc_delta_80_240 = ta240 - ta80
deltas_80_to_240_test_acc.append(acc_delta_80_240)
# 200 to 240
tl200 = ckpt_map[200]["train_loss"]
rel_red_200_240 = (tl200 - tl240) / tl200 if tl200 > 0 else 0.0
deltas_200_to_240_train_rel.append(rel_red_200_240)
ta200 = ckpt_map[200]["test_acc"]
acc_delta_200_240 = ta240 - ta200
acc_abs_change_200_240 = abs(ta240 - ta200)
deltas_200_to_240_test_acc.append(acc_delta_200_240)
deltas_200_to_240_test_acc_abs.append(acc_abs_change_200_240)
# Calculate overall mean metrics for classifications
mean_train_loss_80 = checkpoint_stats[80]["train_loss"]["mean"]
mean_train_loss_200 = checkpoint_stats[200]["train_loss"]["mean"]
mean_train_loss_240 = checkpoint_stats[240]["train_loss"]["mean"]
mean_test_acc_80 = checkpoint_stats[80]["test_acc"]["mean"]
mean_test_acc_200 = checkpoint_stats[200]["test_acc"]["mean"]
mean_test_acc_240 = checkpoint_stats[240]["test_acc"]["mean"]
rel_train_loss_reduction_80_240 = (
(mean_train_loss_80 - mean_train_loss_240) / mean_train_loss_80
)
test_acc_gain_80_240 = mean_test_acc_240 - mean_test_acc_80
rel_train_loss_reduction_200_240 = (
(mean_train_loss_200 - mean_train_loss_240) / mean_train_loss_200
)
abs_test_acc_change_200_240 = abs(mean_test_acc_240 - mean_test_acc_200)
# 5. Shared Contract Predeclared Classifications
post_80_training_converging = bool(
rel_train_loss_reduction_80_240 >= POST80_LOSS_REDUCTION_THRESHOLD
)
meaningful_held_out_gain = bool(
test_acc_gain_80_240 >= TEST_ACCURACY_GAIN_THRESHOLD
)
overfitting_signal = bool(
post_80_training_converging
and test_acc_gain_80_240 <= OVERFITTING_ACCURACY_DROP_THRESHOLD
)
early_stagnation = bool(
not post_80_training_converging
and abs(test_acc_gain_80_240) < TEST_ACCURACY_GAIN_THRESHOLD
)
generalization_plateau = bool(
post_80_training_converging
and not meaningful_held_out_gain
and not overfitting_signal
)
late_plateau_diagnostic = bool(
rel_train_loss_reduction_200_240 < LATE_LOSS_REDUCTION_THRESHOLD
and abs_test_acc_change_200_240 < LATE_ACCURACY_CHANGE_THRESHOLD
)
if meaningful_held_out_gain:
summary_verdict = (
f"Training beyond epoch 80 continues to improve held-out test accuracy by "
f"{test_acc_gain_80_240 * 100:.2f} percentage points "
f"(from {mean_test_acc_80 * 100:.2f}% to {mean_test_acc_240 * 100:.2f}%)."
)
elif overfitting_signal:
summary_verdict = (
f"Training beyond epoch 80 exhibits overfitting: training loss falls by "
f"{rel_train_loss_reduction_80_240 * 100:.2f}% while held-out test accuracy drops by "
f"{abs(test_acc_gain_80_240) * 100:.2f} percentage points."
)
elif early_stagnation:
summary_verdict = (
f"Optimization has effectively stagnated after epoch 80: training loss falls by only "
f"{rel_train_loss_reduction_80_240 * 100:.2f}% and held-out accuracy changes by "
f"{test_acc_gain_80_240 * 100:+.2f} percentage points through epoch 240."
)
elif generalization_plateau:
summary_verdict = (
f"Training loss continues to improve after epoch 80, but held-out accuracy plateaus: "
f"{test_acc_gain_80_240 * 100:+.2f} percentage points "
f"(from {mean_test_acc_80 * 100:.2f}% to {mean_test_acc_240 * 100:.2f}%)."
)
else:
summary_verdict = (
"The fixed endpoint criteria are inconclusive; inspect the paired checkpoint "
"trajectory before extending the epoch horizon."
)
last_improvement_epochs_dict = {r["seed"]: r["last_improvement_epoch"] for r in runs}
paired_endpoint_deltas = []
for r in runs:
ckpt_map = {c["epoch"]: c for c in r["checkpoints"]}
paired_endpoint_deltas.append(
{
"seed": r["seed"],
"train_loss_relative_reduction_80_to_240": (
ckpt_map[80]["train_loss"] - ckpt_map[240]["train_loss"]
)
/ ckpt_map[80]["train_loss"],
"test_accuracy_delta_80_to_240": (
ckpt_map[240]["test_acc"] - ckpt_map[80]["test_acc"]
),
"train_loss_relative_reduction_200_to_240": (
ckpt_map[200]["train_loss"] - ckpt_map[240]["train_loss"]
)
/ ckpt_map[200]["train_loss"],
"test_accuracy_delta_200_to_240": (
ckpt_map[240]["test_acc"] - ckpt_map[200]["test_acc"]
),
}
)
# Build final payload
payload = {
"epoch_convergence_protocol_version": EPOCH_CONVERGENCE_PROTOCOL_VERSION,
"tuning_protocol_version": TUNING_PROTOCOL_VERSION,
"pso_version": pso_version,
"torch_version": torch.__version__,
"hardware": hw_provenance,
"device": device.type,
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"baseline_path": str(baseline_path),
"data_fingerprint": data_fp,
"candidate_label": winner_cfg.candidate_label,
"config": {
**opt_kwargs,
"n_particles": n_particles,
"epochs": target_epochs,
"batch_size": batch_size,
"renewal": "loss",
"checkpoint_interval": 20,
},
"contract_criteria": {
"primary_endpoints": [80, 120, 160, 200, 240],
"post_80_convergence_threshold_loss_reduction": POST80_LOSS_REDUCTION_THRESHOLD,
"meaningful_gain_threshold_test_acc": TEST_ACCURACY_GAIN_THRESHOLD,
"overfitting_threshold_test_acc": OVERFITTING_ACCURACY_DROP_THRESHOLD,
"late_plateau_200_240_loss_threshold": LATE_LOSS_REDUCTION_THRESHOLD,
"late_plateau_200_240_acc_threshold": LATE_ACCURACY_CHANGE_THRESHOLD,
"replay_tolerance": REPLAY_TOLERANCE,
},
"fidelity_validation": {
"replay_seeds": sorted(REPLAY_SEEDS),
"max_epoch80_test_acc_delta": round(max_ep80_delta, 6),
"tolerance": REPLAY_TOLERANCE,
"passed": fidelity_passed,
},
"predeclared_classifications": {
"post_80_training_converging": post_80_training_converging,
"meaningful_held_out_gain": meaningful_held_out_gain,
"overfitting_signal": overfitting_signal,
"early_stagnation": early_stagnation,
"generalization_plateau": generalization_plateau,
"late_plateau_diagnostic": late_plateau_diagnostic,
"summary_verdict": summary_verdict,
},
"summary": {
"epochs": CHECKPOINT_EPOCHS,
"checkpoint_stats": {str(ep): stats for ep, stats in checkpoint_stats.items()},
"paired_deltas": {
"80_to_240": {
"train_loss_rel_reduction": calc_stats(deltas_80_to_240_train_rel),
"test_acc_delta": calc_stats(deltas_80_to_240_test_acc),
},
"200_to_240": {
"train_loss_rel_reduction": calc_stats(deltas_200_to_240_train_rel),
"test_acc_delta": calc_stats(deltas_200_to_240_test_acc),
"test_acc_abs_change": calc_stats(deltas_200_to_240_test_acc_abs),
},
},
"paired_endpoint_deltas_by_seed": paired_endpoint_deltas,
"last_training_best_improvement_epochs": last_improvement_epochs_dict,
},
"runs": runs,
"completed": True,
"valid": fidelity_passed,
"error": None,
}
# Write output JSON
save_json_atomic(payload, output_json_path)
print(f"Saved analysis JSON to {output_json_path}")
# Write output CSV
output_csv_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_csv_path, "w", newline="", encoding="utf-8") as f:
fieldnames = [
"seed",
"epoch",
"train_loss",
"train_acc",
"train_mse",
"test_loss",
"test_acc",
"test_mse",
"fit_time_sec",
]
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(flat_csv_rows)
print(f"Saved flat checkpoint CSV to {output_csv_path}")
# 6. Render Figure
render_convergence_figure(CHECKPOINT_EPOCHS, runs, checkpoint_stats, figure_path)
print(f"Rendered plot to {figure_path}")
# Print summary block
print("\n" + "=" * 70)
print("EPOCH CONVERGENCE & TRAJECTORY ANALYSIS RESULTS")
print("=" * 70)
print(f"Device: {device.type} | Seeds: {sorted(REPLAY_SEEDS)}")
print(f"Fidelity Replay Check (Epoch 80 <= {REPLAY_TOLERANCE}): Max Delta = {max_ep80_delta:.6f} -> Passed: {fidelity_passed}")
print("-" * 70)
print("Predeclared Classifications (Epoch 80 -> 240):")
print(f" Post-80 Training Converging (Loss Drop >= 1%): {post_80_training_converging} ({rel_train_loss_reduction_80_240 * 100:.2f}%)")
print(f" Meaningful Held-Out Gain (Acc Rise >= 1%p): {meaningful_held_out_gain} ({test_acc_gain_80_240 * 100:+.2f}%p)")
print(f" Overfitting Signal: {overfitting_signal}")
print(f" Early Stagnation: {early_stagnation}")
print(f" Generalization Plateau: {generalization_plateau}")
print(f" Late Plateau Diagnostic (200 -> 240): {late_plateau_diagnostic}")
print("-" * 70)
print(f"Verdict: {summary_verdict}")
print("=" * 70 + "\n")
return fidelity_passed
def render_convergence_figure(
epochs: List[int],
runs: List[Dict[str, Any]],
checkpoint_stats: Dict[int, Dict[str, Any]],
figure_path: Path,
):
figure_path.parent.mkdir(parents=True, exist_ok=True)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5.5))
mean_train_loss = [checkpoint_stats[ep]["train_loss"]["mean"] for ep in epochs]
std_train_loss = [checkpoint_stats[ep]["train_loss"]["std"] for ep in epochs]
mean_test_acc = [checkpoint_stats[ep]["test_acc"]["mean"] for ep in epochs]
std_test_acc = [checkpoint_stats[ep]["test_acc"]["std"] for ep in epochs]
epochs_arr = np.array(epochs)
mean_tl_arr = np.array(mean_train_loss)
std_tl_arr = np.array(std_train_loss)
mean_ta_arr = np.array(mean_test_acc)
std_ta_arr = np.array(std_test_acc)
# Subplot 1: Training Loss
for r in runs:
r_epochs = [c["epoch"] for c in r["checkpoints"]]
r_losses = [c["train_loss"] for c in r["checkpoints"]]
ax1.plot(r_epochs, r_losses, color="#1f77b4", alpha=0.25, linestyle=":", linewidth=1.2)
ax1.plot(epochs_arr, mean_tl_arr, marker="o", color="#1f77b4", linewidth=2.2, label="Mean Training Loss")
ax1.fill_between(
epochs_arr,
mean_tl_arr - std_tl_arr,
mean_tl_arr + std_tl_arr,
color="#1f77b4",
alpha=0.15,
label="±1 Std Dev",
)
ax1.axvline(80, color="#d62728", linestyle="--", linewidth=1.5, label="Baseline Horizon (Epoch 80)")
ax1.set_xlabel("Epoch", fontsize=11)
ax1.set_ylabel("Global-Best Training Loss", fontsize=11)
ax1.set_title("Global-Best Training Loss Trajectory", fontsize=12, fontweight="bold")
ax1.grid(True, alpha=0.3)
ax1.legend(loc="upper right", frameon=True)
# Subplot 2: Held-Out Test Accuracy
for r in runs:
r_epochs = [c["epoch"] for c in r["checkpoints"]]
r_accs = [c["test_acc"] for c in r["checkpoints"]]
ax2.plot(r_epochs, r_accs, color="#2ca02c", alpha=0.25, linestyle=":", linewidth=1.2)
ax2.plot(epochs_arr, mean_ta_arr, marker="s", color="#2ca02c", linewidth=2.2, label="Mean Test Accuracy")
ax2.fill_between(
epochs_arr,
mean_ta_arr - std_ta_arr,
mean_ta_arr + std_ta_arr,
color="#2ca02c",
alpha=0.15,
label="±1 Std Dev",
)
ax2.axvline(80, color="#d62728", linestyle="--", linewidth=1.5, label="Baseline Horizon (Epoch 80)")
ax2.set_xlabel("Epoch", fontsize=11)
ax2.set_ylabel("Held-Out Test Accuracy", fontsize=11)
ax2.set_title("Held-Out Test Accuracy Trajectory", fontsize=12, fontweight="bold")
ax2.grid(True, alpha=0.3)
ax2.legend(loc="lower right", frameon=True)
fig.suptitle(
"Adaptive Moment 120-Particle MNIST 240-Epoch Convergence & Trajectory Analysis",
fontsize=13,
fontweight="bold",
)
plt.tight_layout(rect=(0.0, 0.0, 1.0, 0.95))
plt.savefig(figure_path, dpi=300, bbox_inches="tight")
plt.close(fig)
def main():
parser = argparse.ArgumentParser(
description="Adaptive Moment 120-Particle x 240-Epoch MNIST Convergence Analysis"
)
parser.add_argument(
"--baseline-json",
type=Path,
default=Path("benchmark_results/pso_v4_tuning.json"),
help="Path to baseline tuning JSON file",
)
parser.add_argument(
"--output-json",
type=Path,
default=Path("benchmark_results/pso_v4_epoch_convergence.json"),
help="Path to output analysis JSON file",
)
parser.add_argument(
"--output-csv",
type=Path,
default=Path("benchmark_results/pso_v4_epoch_convergence.csv"),
help="Path to output checkpoint CSV file",
)
parser.add_argument(
"--figure",
type=Path,
default=Path("history_plt/pso_v4_epoch_convergence.png"),
help="Path to output convergence PNG figure",
)
parser.add_argument(
"--device",
type=str,
default=None,
help="Execution device (mps, cuda, cpu; default: auto-detect)",
)
args = parser.parse_args()
passed = run_epoch_convergence_analysis(
baseline_path=args.baseline_json,
output_json_path=args.output_json,
output_csv_path=args.output_csv,
figure_path=args.figure,
device_str=args.device,
)
if not passed:
sys.exit(1)
if __name__ == "__main__":
main()
+539
View File
@@ -0,0 +1,539 @@
"""
Strict Pareto Evaluator for Heavy Task PSO Autoresearch.
Evaluates candidate equalized signed-hash subspace experiment artifacts against
baseline heavy task benchmark results (benchmark_results/pso_v6_heavy_tasks.json).
Baseline Policy:
- mnist_compact: G8
- mnist_wide: G5
- fashion_compact: G8
- fashion_wide: G5
Evaluates 7 Hard Gates:
1. Finite Metrics (all validation metrics finite across runs)
2. Test-Sealed (zero official test data loaded & 0 test evaluations)
3. Config Matched (12 particles, 80 epochs, 10k subset, seeds 101-103)
4. State Ratio Boundary (max state ratio <= 0.5 across all workloads)
5. Workload Accuracy Regression Boundary (each workload acc regression <= 1.0 pp)
6. Workload NLL Regression Boundary (each workload NLL regression <= 5.0%)
7. Worst-Workload (mnist_wide) Improvement (acc gain >= 2.0 pp OR NLL reduction >= 5.0%)
Numeric Score:
Score = mean_rel_nll_reduction_pct + mean_acc_gain_pp + 10 * log2(1 / max_state_ratio) - 100 * failed_gate_count
"""
from __future__ import annotations
import argparse
import json
import math
import os
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union
# Ensure test directory and repo root are in Python path
REPO_ROOT = Path(__file__).resolve().parent.parent if Path(__file__).resolve().parent.name != "PSO" else Path(__file__).resolve().parent
TEST_DIR = REPO_ROOT / "test"
if str(TEST_DIR) not in sys.path:
sys.path.insert(0, str(TEST_DIR))
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from benchmark_suite import save_json_atomic
from heavy_pso_autoresearch import compute_latent_dim, compute_core_swarm_state_bytes
EVALUATOR_VERSION = "EVALUATE-HEAVY-AUTORESEARCH 1.0.0"
BASELINE_POLICY: Dict[str, str] = {
"mnist_compact": "G8",
"mnist_wide": "G5",
"fashion_compact": "G8",
"fashion_wide": "G5",
}
EXPECTED_SEEDS = [101, 102, 103]
EXPECTED_PARTICLES = 12
EXPECTED_EPOCHS = 80
EXPECTED_SUBSET_SIZE = 10000
EXPECTED_WORKLOADS = ["mnist_compact", "mnist_wide", "fashion_compact", "fashion_wide"]
def evaluate_heavy_autoresearch(
baseline_path: Union[str, Path],
candidate_path: Union[str, Path],
output_path: Optional[Union[str, Path]] = None,
) -> Dict[str, Any]:
"""
Evaluates a candidate experiment artifact against baseline heavy task results.
Validates schemas, configurations, seeds, test-sealed constraints, finiteness,
calculates every hard gate and numeric score, selects the winning candidate,
and returns a structured evaluator payload.
"""
baseline_path = Path(baseline_path)
candidate_path = Path(candidate_path)
if not baseline_path.is_file():
raise FileNotFoundError(f"Baseline artifact not found at '{baseline_path}'")
if not candidate_path.is_file():
raise FileNotFoundError(f"Candidate artifact not found at '{candidate_path}'")
with open(baseline_path, "r", encoding="utf-8") as f:
baseline_data = json.load(f)
with open(candidate_path, "r", encoding="utf-8") as f:
candidate_data = json.load(f)
WORKLOAD_TOTAL_DIMS: Dict[str, int] = {
"mnist_compact": 9098,
"mnist_wide": 55338,
"fashion_compact": 9098,
"fashion_wide": 55338,
}
if (
baseline_data.get("official_test_data_loaded") is not False
or baseline_data.get("official_test_evaluations") != 0
):
raise ValueError("Baseline artifact must explicitly seal official test data.")
# Validate baseline JSON schema & confirmation results
if "confirmation_results" not in baseline_data:
raise KeyError("Baseline JSON missing top-level 'confirmation_results' key")
baseline_confirm = baseline_data["confirmation_results"]
baseline_metrics: Dict[str, Dict[str, float]] = {}
baseline_core_bytes: Dict[str, int] = {}
for wl in EXPECTED_WORKLOADS:
if wl not in baseline_confirm:
raise KeyError(f"Baseline confirmation results missing workload '{wl}'")
selected_method = BASELINE_POLICY[wl]
if selected_method not in baseline_confirm[wl]:
raise KeyError(
f"Baseline confirmation results for '{wl}' missing policy method '{selected_method}'"
)
entry = baseline_confirm[wl][selected_method]
stats = entry.get("stats", {}) if isinstance(entry, dict) else {}
val_nll_mean = float(stats.get("val_nll", {}).get("mean", float("nan")))
val_acc_mean = float(stats.get("val_acc", {}).get("mean", float("nan")))
if not (math.isfinite(val_nll_mean) and math.isfinite(val_acc_mean)):
raise ValueError(f"Baseline metrics for '{wl}' method '{selected_method}' contain NaN or non-finite value")
baseline_metrics[wl] = {
"val_nll": val_nll_mean,
"val_acc": val_acc_mean,
"val_brier": float(stats.get("val_brier", {}).get("mean", 0.0) or 0.0),
"val_ece": float(stats.get("val_ece", {}).get("mean", 0.0) or 0.0),
"method_id": selected_method,
}
bytes_list = []
if (
isinstance(entry, dict)
and isinstance(entry.get("per_seed_runs"), list)
):
bytes_list = [
int(run["core_swarm_state_bytes"])
for run in entry["per_seed_runs"]
if (
isinstance(run, dict)
and "core_swarm_state_bytes" in run
and math.isfinite(float(run["core_swarm_state_bytes"]))
)
]
if bytes_list and len(set(bytes_list)) != 1:
raise ValueError(f"Baseline core-state bytes vary across seeds for '{wl}'.")
if bytes_list:
b_bytes = bytes_list[0]
else:
states = 5 * EXPECTED_PARTICLES + (1 if selected_method == "G8" else 0)
b_bytes = states * WORKLOAD_TOTAL_DIMS[wl] * 4
baseline_core_bytes[wl] = b_bytes
# Validate candidate JSON schema
if "candidate_runs" not in candidate_data:
raise KeyError("Candidate JSON missing top-level 'candidate_runs' key")
candidate_runs = candidate_data["candidate_runs"]
if not isinstance(candidate_runs, dict) or len(candidate_runs) == 0:
raise ValueError("Candidate JSON 'candidate_runs' must be a non-empty dictionary")
# Global candidate test-sealed checks
top_test_loaded_present = "official_test_data_loaded" in candidate_data
top_test_loaded_val = candidate_data.get("official_test_data_loaded")
top_test_evals_present = "official_test_evaluations" in candidate_data
top_test_evals_val = candidate_data.get("official_test_evaluations")
top_test_sealed = (
top_test_loaded_present
and top_test_loaded_val is False
and top_test_evals_present
and top_test_evals_val == 0
)
candidate_evaluations: Dict[str, Dict[str, Any]] = {}
for cand_id, wl_map in candidate_runs.items():
gate_finite = True
gate_test_sealed = bool(top_test_sealed)
gate_config_matched = True
if not isinstance(wl_map, dict):
gate_config_matched = False
gate_finite = False
wl_map = {}
if set(wl_map) != set(EXPECTED_WORKLOADS):
gate_config_matched = False
for wl in EXPECTED_WORKLOADS:
if wl not in wl_map:
gate_config_matched = False
gate_finite = False
derived_ratios: Dict[str, float] = {}
per_workload_deltas: Dict[str, Dict[str, float]] = {}
acc_regressions_valid = True
nll_regressions_valid = True
for wl in EXPECTED_WORKLOADS:
if wl not in wl_map or not isinstance(wl_map[wl], dict):
acc_regressions_valid = False
nll_regressions_valid = False
continue
wl_entry = wl_map[wl]
p_val = wl_entry.get("particles")
e_val = wl_entry.get("epochs")
sub_val = wl_entry.get("subset_size")
seeds_val = wl_entry.get("seeds")
if (
p_val != EXPECTED_PARTICLES
or e_val != EXPECTED_EPOCHS
or sub_val != EXPECTED_SUBSET_SIZE
or seeds_val != EXPECTED_SEEDS
):
gate_config_matched = False
per_seed = wl_entry.get("per_seed_runs")
if not isinstance(per_seed, list) or len(per_seed) != len(EXPECTED_SEEDS):
gate_config_matched = False
gate_finite = False
actual_seeds = []
else:
actual_seeds = [
s_run.get("seed") for s_run in per_seed if isinstance(s_run, dict) and "seed" in s_run
]
if actual_seeds != EXPECTED_SEEDS:
gate_config_matched = False
expected_queries = EXPECTED_PARTICLES * EXPECTED_EPOCHS
expected_samples = expected_queries * EXPECTED_SUBSET_SIZE
for s_run in per_seed:
if (
not isinstance(s_run, dict)
or s_run.get("total_queries") != expected_queries
or s_run.get("total_sample_evaluations") != expected_samples
):
gate_config_matched = False
stats = wl_entry.get("stats") if isinstance(wl_entry.get("stats"), dict) else {}
val_nll_dict = stats.get("val_nll") if isinstance(stats.get("val_nll"), dict) else {}
val_acc_dict = stats.get("val_acc") if isinstance(stats.get("val_acc"), dict) else {}
c_nll_raw = val_nll_dict.get("mean")
c_acc_raw = val_acc_dict.get("mean")
if (
c_nll_raw is None
or c_acc_raw is None
or not isinstance(c_nll_raw, (int, float))
or not isinstance(c_acc_raw, (int, float))
or not (math.isfinite(float(c_nll_raw)) and math.isfinite(float(c_acc_raw)))
):
gate_finite = False
c_nll = float("nan")
c_acc = float("nan")
else:
c_nll = float(c_nll_raw)
c_acc = float(c_acc_raw)
if isinstance(per_seed, list):
for s_run in per_seed:
if not isinstance(s_run, dict):
gate_finite = False
gate_config_matched = False
continue
if "official_test_evaluations" not in s_run or s_run["official_test_evaluations"] != 0:
gate_test_sealed = False
if s_run.get("is_finite") is not True:
gate_finite = False
s_nll = s_run.get("val_selected_loss")
s_acc = s_run.get("val_selected_acc")
g_nll = s_run.get("gbest_loss")
g_acc = s_run.get("gbest_acc")
w_time = s_run.get("wall_time_sec")
val_m = s_run.get("val_metrics")
val_m_ok = (
isinstance(val_m, dict)
and len(val_m) > 0
and all(
isinstance(v, (int, float)) and math.isfinite(float(v))
for v in val_m.values()
)
)
scalars_for_s_run = [s_nll, s_acc, g_nll, g_acc, w_time]
s_scalars_ok = all(
v is not None and isinstance(v, (int, float)) and math.isfinite(float(v))
for v in scalars_for_s_run
)
if not (val_m_ok and s_scalars_ok):
gate_finite = False
expected_total_dim = WORKLOAD_TOTAL_DIMS[wl]
total_dim = wl_entry.get("total_dim")
raw_ratio = wl_entry.get("ratio")
if (
total_dim != expected_total_dim
or not isinstance(raw_ratio, (int, float))
or not (0.0 < float(raw_ratio) <= 1.0)
):
gate_config_matched = False
total_dim = expected_total_dim
raw_ratio = 1.0
latent_dim = compute_latent_dim(total_dim, float(raw_ratio))
if wl_entry.get("latent_dim") != latent_dim:
gate_config_matched = False
analytical_cand_bytes = compute_core_swarm_state_bytes(EXPECTED_PARTICLES, latent_dim)
b_bytes = baseline_core_bytes[wl]
derived_ratio = float(analytical_cand_bytes) / float(b_bytes)
derived_ratios[wl] = derived_ratio
if wl_entry.get("core_swarm_state_bytes") != analytical_cand_bytes:
gate_config_matched = False
if isinstance(per_seed, list):
for s_run in per_seed:
if isinstance(s_run, dict) and s_run.get("core_swarm_state_bytes") != analytical_cand_bytes:
gate_config_matched = False
reported_state_ratio = wl_entry.get("state_ratio")
if (
not isinstance(reported_state_ratio, (int, float))
or not math.isfinite(float(reported_state_ratio))
or not math.isclose(
float(reported_state_ratio),
derived_ratio,
rel_tol=1e-5,
abs_tol=1e-5,
)
):
gate_config_matched = False
b_metrics = baseline_metrics[wl]
b_nll = b_metrics["val_nll"]
b_acc = b_metrics["val_acc"]
if math.isfinite(c_nll) and math.isfinite(c_acc) and math.isfinite(b_nll) and math.isfinite(b_acc):
nll_delta = c_nll - b_nll
rel_nll_reduction_pct = ((b_nll - c_nll) / b_nll) * 100.0 if b_nll > 0 else 0.0
acc_gain_pp = c_acc - b_acc
per_workload_deltas[wl] = {
"state_ratio": derived_ratio,
"baseline_nll": b_nll,
"candidate_nll": c_nll,
"nll_delta": nll_delta,
"rel_nll_reduction_pct": rel_nll_reduction_pct,
"baseline_acc": b_acc,
"candidate_acc": c_acc,
"acc_gain_pp": acc_gain_pp,
}
if acc_gain_pp < -1.0:
acc_regressions_valid = False
if rel_nll_reduction_pct < -5.0:
nll_regressions_valid = False
else:
acc_regressions_valid = False
nll_regressions_valid = False
gate_finite = False
if derived_ratios and len(derived_ratios) == len(EXPECTED_WORKLOADS):
max_state_ratio = max(derived_ratios.values())
else:
max_state_ratio = 1.0
gate_state_ratio = bool(0.0 < max_state_ratio <= 0.5 and math.isfinite(max_state_ratio))
gate_acc_regression = bool(acc_regressions_valid)
gate_nll_regression = bool(nll_regressions_valid)
if "mnist_wide" in per_workload_deltas:
mw_delta = per_workload_deltas["mnist_wide"]
mw_acc_gain = mw_delta["acc_gain_pp"]
mw_nll_red = mw_delta["rel_nll_reduction_pct"]
gate_baseline_worst_improvement = bool((mw_acc_gain >= 2.0) or (mw_nll_red >= 5.0))
else:
gate_baseline_worst_improvement = False
gates = {
"gate_finite": bool(gate_finite),
"gate_test_sealed": bool(gate_test_sealed),
"gate_config_matched": bool(gate_config_matched),
"gate_state_ratio": bool(gate_state_ratio),
"gate_acc_regression": bool(gate_acc_regression),
"gate_nll_regression": bool(gate_nll_regression),
"gate_baseline_worst_improvement": bool(gate_baseline_worst_improvement),
}
failed_gates = [g_name for g_name, g_pass in gates.items() if not g_pass]
failed_gate_count = len(failed_gates)
cand_pass = bool(failed_gate_count == 0)
if len(per_workload_deltas) == len(EXPECTED_WORKLOADS):
mean_rel_nll_reduction_pct = float(
sum(d["rel_nll_reduction_pct"] for d in per_workload_deltas.values())
/ len(per_workload_deltas)
)
mean_acc_gain_pp = float(
sum(d["acc_gain_pp"] for d in per_workload_deltas.values())
/ len(per_workload_deltas)
)
else:
mean_rel_nll_reduction_pct = 0.0
mean_acc_gain_pp = 0.0
if 0.0 < max_state_ratio <= 1.0 and math.isfinite(max_state_ratio):
state_efficiency_bonus = 10.0 * math.log2(1.0 / max_state_ratio)
else:
state_efficiency_bonus = 0.0
score = (
mean_rel_nll_reduction_pct
+ mean_acc_gain_pp
+ state_efficiency_bonus
- (100.0 * failed_gate_count)
)
if not math.isfinite(score):
score = -100.0 * max(1, failed_gate_count)
candidate_evaluations[cand_id] = {
"candidate_id": cand_id,
"pass": cand_pass,
"score": float(score),
"failed_gates": failed_gates,
"failed_gate_count": failed_gate_count,
"gate_details": gates,
"per_workload": per_workload_deltas,
"state_ratios": derived_ratios,
"summary_metrics": {
"mean_rel_nll_reduction_pct": mean_rel_nll_reduction_pct,
"mean_acc_gain_pp": mean_acc_gain_pp,
"max_state_ratio": max_state_ratio,
"state_efficiency_bonus": state_efficiency_bonus,
},
}
passing_cand_ids = [
c_id for c_id, c_eval in candidate_evaluations.items() if c_eval["pass"]
]
if passing_cand_ids:
selected_candidate_id = max(
passing_cand_ids, key=lambda c_id: candidate_evaluations[c_id]["score"]
)
overall_pass = True
else:
selected_candidate_id = max(
candidate_evaluations.keys(),
key=lambda c_id: candidate_evaluations[c_id]["score"],
)
overall_pass = False
selected_eval = candidate_evaluations[selected_candidate_id]
evaluator_output = {
"pass": overall_pass,
"score": float(selected_eval["score"]),
"selected_candidate_id": selected_candidate_id,
"evaluator_version": EVALUATOR_VERSION,
"source_paths": {
"baseline_path": str(baseline_path),
"candidate_path": str(candidate_path),
},
"baseline_policy": BASELINE_POLICY,
"candidate_evaluations": candidate_evaluations,
"per_workload_deltas": selected_eval["per_workload"],
"state_ratios": selected_eval["state_ratios"],
}
if output_path is not None:
save_json_atomic(evaluator_output, Path(output_path))
return evaluator_output
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Strict Pareto Evaluator for Heavy Task PSO Autoresearch"
)
parser.add_argument(
"--baseline",
type=str,
default="benchmark_results/pso_v6_heavy_tasks.json",
help="Path to baseline heavy tasks JSON artifact",
)
parser.add_argument(
"--candidate",
type=str,
default=None,
help="Path to candidate heavy autoresearch JSON artifact",
)
parser.add_argument(
"candidate_pos",
nargs="?",
type=str,
default=None,
help="Positional candidate JSON path fallback",
)
parser.add_argument(
"--output",
type=str,
default=None,
help="Optional path to write evaluator result JSON",
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
cand_path = args.candidate or args.candidate_pos
if not cand_path:
parser.error("Candidate JSON path must be supplied via --candidate or positional argument.")
out_path = Path(args.output) if args.output else None
result = evaluate_heavy_autoresearch(
baseline_path=args.baseline,
candidate_path=cand_path,
output_path=out_path,
)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
+741
View File
@@ -0,0 +1,741 @@
"""Strict evaluator for the Heavy PSO cross-split robustness mission.
Every candidate is compared with a baseline rerun on the same train/validation
partition and swarm seeds. Official test data must remain sealed. Development
may qualify a policy for one-shot confirmation, but mission ``pass`` is true
only when both phases satisfy the frozen evaluator contract.
"""
from __future__ import annotations
import argparse
import json
import math
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence, Tuple
REPO_ROOT = Path(__file__).resolve().parent.parent
TEST_DIR = REPO_ROOT / "test"
if str(TEST_DIR) not in sys.path:
sys.path.insert(0, str(TEST_DIR))
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from benchmark_suite import save_json_atomic
from heavy_pso_autoresearch import compute_core_swarm_state_bytes, compute_latent_dim
EVALUATOR_VERSION = "HEAVY-PSO-CROSS-SPLIT-EVALUATOR 1.0.0"
EXPECTED_PARTICLES = 12
EXPECTED_EPOCHS = 80
EXPECTED_SUBSET_SIZE = 10000
EXPECTED_QUERIES = EXPECTED_PARTICLES * EXPECTED_EPOCHS
EXPECTED_SAMPLES = EXPECTED_QUERIES * EXPECTED_SUBSET_SIZE
EXPECTED_DEV_SPLIT_SEEDS = [20260905, 20260906]
EXPECTED_DEV_SWARM_SEEDS = [101, 102, 103]
EXPECTED_CONF_SPLIT_SEEDS = [20260907]
EXPECTED_CONF_SWARM_SEEDS = [111, 112, 113]
WORKLOADS = ["mnist_compact", "mnist_wide", "fashion_compact", "fashion_wide"]
BASELINE_METHODS = {
"mnist_compact": "G8",
"mnist_wide": "G5",
"fashion_compact": "G8",
"fashion_wide": "G5",
}
TOTAL_DIMS = {
"mnist_compact": 9098,
"mnist_wide": 55338,
"fashion_compact": 9098,
"fashion_wide": 55338,
}
PHASE_SPECS = {
"development": (EXPECTED_DEV_SPLIT_SEEDS, EXPECTED_DEV_SWARM_SEEDS),
"confirmation": (EXPECTED_CONF_SPLIT_SEEDS, EXPECTED_CONF_SWARM_SEEDS),
}
def load_artifact(path: Path) -> Dict[str, Any]:
if not path.is_file():
raise FileNotFoundError(f"Artifact file not found: {path}")
with path.open("r", encoding="utf-8") as handle:
data = json.load(handle)
if not isinstance(data, dict):
raise ValueError(f"Artifact at {path} must be a JSON object")
return data
def _is_finite_number(value: Any) -> bool:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
)
def _mean(values: Sequence[float]) -> float:
return float(math.fsum(values) / len(values))
def _append_issue(issues: Dict[str, List[str]], category: str, message: str) -> None:
issues[category].append(message)
def _validate_stats(
entry: Dict[str, Any],
per_seed_runs: List[Dict[str, Any]],
label: str,
issues: Dict[str, List[str]],
) -> Optional[Tuple[float, float]]:
stats = entry.get("stats")
if not isinstance(stats, dict):
_append_issue(issues, "schema", f"{label}: missing stats object")
return None
try:
acc_mean = stats["val_acc"]["mean"]
nll_mean = stats["val_nll"]["mean"]
except (KeyError, TypeError):
_append_issue(issues, "schema", f"{label}: missing val_acc/val_nll means")
return None
if not (_is_finite_number(acc_mean) and _is_finite_number(nll_mean)):
_append_issue(issues, "finite", f"{label}: non-finite aggregate metrics")
return None
if len(per_seed_runs) > 0:
run_accs: List[float] = []
run_nlls: List[float] = []
for run in per_seed_runs:
if not isinstance(run, dict):
continue
seed_label = f"{label}/seed={run.get('seed')}"
if "val_selected_acc" not in run or run.get("val_selected_acc") is None:
_append_issue(issues, "schema", f"{seed_label}: missing val_selected_acc")
else:
val_acc = run["val_selected_acc"]
if not isinstance(val_acc, (int, float)) or isinstance(val_acc, bool):
_append_issue(issues, "schema", f"{seed_label}: non-numeric val_selected_acc")
elif not math.isfinite(float(val_acc)):
_append_issue(issues, "finite", f"{seed_label}: non-finite val_selected_acc")
else:
run_accs.append(float(val_acc))
if "val_selected_loss" not in run or run.get("val_selected_loss") is None:
_append_issue(issues, "schema", f"{seed_label}: missing val_selected_loss")
else:
val_nll = run["val_selected_loss"]
if not isinstance(val_nll, (int, float)) or isinstance(val_nll, bool):
_append_issue(issues, "schema", f"{seed_label}: non-numeric val_selected_loss")
elif not math.isfinite(float(val_nll)):
_append_issue(issues, "finite", f"{seed_label}: non-finite val_selected_loss")
else:
run_nlls.append(float(val_nll))
if len(run_accs) == len(per_seed_runs):
if not math.isclose(float(acc_mean), _mean(run_accs), rel_tol=1e-6, abs_tol=1e-5):
_append_issue(issues, "schema", f"{label}: val_acc mean disagrees with per-seed runs")
if len(run_nlls) == len(per_seed_runs):
if not math.isclose(float(nll_mean), _mean(run_nlls), rel_tol=1e-6, abs_tol=1e-5):
_append_issue(issues, "schema", f"{label}: val_nll mean disagrees with per-seed runs")
return float(acc_mean), float(nll_mean)
def _validate_runs(
entry: Dict[str, Any],
expected_seeds: List[int],
expected_state_bytes: int,
label: str,
candidate: bool,
issues: Dict[str, List[str]],
) -> Tuple[List[Dict[str, Any]], int, int]:
runs = entry.get("per_seed_runs")
if not isinstance(runs, list) or len(runs) != len(expected_seeds):
_append_issue(issues, "schema", f"{label}: expected {len(expected_seeds)} per-seed runs")
return [], 0, 0
if [run.get("seed") if isinstance(run, dict) else None for run in runs] != expected_seeds:
_append_issue(issues, "config", f"{label}: per-seed run order/content does not match {expected_seeds}")
total_queries = 0
total_samples = 0
numeric_fields = (
"val_selected_loss",
"val_selected_acc",
"gbest_loss",
"gbest_acc",
"wall_time_sec",
"optimization_wall_time_sec",
"validation_wall_time_sec",
"throughput_samples_per_sec",
)
valid_runs: List[Dict[str, Any]] = []
for run in runs:
if not isinstance(run, dict):
_append_issue(issues, "schema", f"{label}: non-object seed record")
continue
seed_label = f"{label}/seed={run.get('seed')}"
for field in numeric_fields:
if not _is_finite_number(run.get(field)):
_append_issue(issues, "finite", f"{seed_label}: missing/non-finite {field}")
val_metrics = run.get("val_metrics")
if (
not isinstance(val_metrics, dict)
or not val_metrics
or any(not _is_finite_number(value) for value in val_metrics.values())
):
_append_issue(issues, "finite", f"{seed_label}: missing/non-finite val_metrics")
if run.get("official_test_evaluations") != 0:
_append_issue(issues, "test", f"{seed_label}: official_test_evaluations must be 0")
if candidate and run.get("is_finite") is not True:
_append_issue(issues, "finite", f"{seed_label}: candidate is_finite must be true")
if run.get("total_queries") != EXPECTED_QUERIES:
_append_issue(issues, "accounting", f"{seed_label}: total_queries must be {EXPECTED_QUERIES}")
if run.get("total_sample_evaluations") != EXPECTED_SAMPLES:
_append_issue(issues, "accounting", f"{seed_label}: total_sample_evaluations must be {EXPECTED_SAMPLES}")
if run.get("core_swarm_state_bytes") != expected_state_bytes:
_append_issue(issues, "state", f"{seed_label}: incorrect core_swarm_state_bytes")
if isinstance(run.get("total_queries"), int):
total_queries += run["total_queries"]
if isinstance(run.get("total_sample_evaluations"), int):
total_samples += run["total_sample_evaluations"]
valid_runs.append(run)
return valid_runs, total_queries, total_samples
def _validate_artifact(artifact: Dict[str, Any], phase: str) -> Dict[str, Any]:
expected_splits, expected_seeds = PHASE_SPECS[phase]
issues: Dict[str, List[str]] = {
"schema": [],
"test": [],
"finite": [],
"provenance": [],
"config": [],
"accounting": [],
"state": [],
}
cells: List[Dict[str, Any]] = []
state_ratios: Dict[str, float] = {}
expected_split_keys = {str(seed) for seed in expected_splits}
if not isinstance(artifact, dict):
_append_issue(issues, "schema", f"{phase}: artifact must be an object")
return {
"issues": issues,
"cells": cells,
"state_ratios": state_ratios,
"max_state_ratio": 1.0,
"policy_signature": None,
}
if artifact.get("phase") != phase:
_append_issue(issues, "schema", f"{phase}: phase field mismatch")
if artifact.get("split_seeds") != expected_splits:
_append_issue(issues, "config", f"{phase}: split_seeds must be {expected_splits}")
if artifact.get("swarm_seeds") != expected_seeds:
_append_issue(issues, "config", f"{phase}: swarm_seeds must be {expected_seeds}")
if artifact.get("official_test_data_loaded") is not False:
_append_issue(issues, "test", f"{phase}: official_test_data_loaded must be false")
if artifact.get("official_test_evaluations") != 0:
_append_issue(issues, "test", f"{phase}: official_test_evaluations must be 0")
candidate_config = artifact.get("candidate_config")
if not isinstance(candidate_config, dict):
_append_issue(issues, "schema", f"{phase}: missing candidate_config")
candidate_config = {}
for field, expected in (
("particles", EXPECTED_PARTICLES),
("epochs", EXPECTED_EPOCHS),
("subset_size", EXPECTED_SUBSET_SIZE),
):
if candidate_config.get(field) != expected:
_append_issue(issues, "config", f"{phase}: candidate_config.{field} must be {expected}")
workload_config = artifact.get("workloads")
if not isinstance(workload_config, dict) or set(workload_config) != set(WORKLOADS):
_append_issue(issues, "schema", f"{phase}: workloads metadata must contain exactly {WORKLOADS}")
workload_config = {}
splits = artifact.get("splits")
if not isinstance(splits, dict) or set(splits) != expected_split_keys:
_append_issue(issues, "schema", f"{phase}: splits must contain exactly {sorted(expected_split_keys)}")
splits = splits if isinstance(splits, dict) else {}
observed_runs = 0
observed_queries = 0
observed_samples = 0
for split_seed in expected_splits:
split_key = str(split_seed)
split_entry = splits.get(split_key)
if not isinstance(split_entry, dict):
_append_issue(issues, "schema", f"{phase}/{split_key}: missing split object")
continue
if split_entry.get("split_seed") != split_seed:
_append_issue(issues, "provenance", f"{phase}/{split_key}: split_seed mismatch")
baselines = split_entry.get("baselines")
candidates = split_entry.get("candidates")
if not isinstance(baselines, dict) or set(baselines) != set(WORKLOADS):
_append_issue(issues, "schema", f"{phase}/{split_key}: baseline workloads incomplete")
baselines = baselines if isinstance(baselines, dict) else {}
if not isinstance(candidates, dict) or set(candidates) != set(WORKLOADS):
_append_issue(issues, "schema", f"{phase}/{split_key}: candidate workloads incomplete")
candidates = candidates if isinstance(candidates, dict) else {}
for workload in WORKLOADS:
baseline = baselines.get(workload)
candidate_entry = candidates.get(workload)
label = f"{phase}/{split_key}/{workload}"
if not isinstance(baseline, dict) or not isinstance(candidate_entry, dict):
_append_issue(issues, "schema", f"{label}: missing baseline or candidate entry")
continue
if baseline.get("method_id") != BASELINE_METHODS[workload]:
_append_issue(issues, "config", f"{label}: wrong baseline method")
for mode, entry in (("baseline", baseline), ("candidate", candidate_entry)):
if entry.get("workload_id") != workload:
_append_issue(issues, "schema", f"{label}/{mode}: workload_id mismatch")
if entry.get("split_seed") != split_seed:
_append_issue(issues, "provenance", f"{label}/{mode}: split_seed mismatch")
if entry.get("particles") != EXPECTED_PARTICLES:
_append_issue(issues, "config", f"{label}/{mode}: particles mismatch")
if entry.get("epochs") != EXPECTED_EPOCHS:
_append_issue(issues, "config", f"{label}/{mode}: epochs mismatch")
if entry.get("subset_size") != EXPECTED_SUBSET_SIZE:
_append_issue(issues, "config", f"{label}/{mode}: subset_size mismatch")
if entry.get("seeds") != expected_seeds:
_append_issue(issues, "config", f"{label}/{mode}: seeds mismatch")
fingerprints = (
baseline.get("split_fingerprint"),
candidate_entry.get("split_fingerprint"),
baseline.get("data_fingerprint"),
candidate_entry.get("data_fingerprint"),
)
if any(not isinstance(value, str) or not value for value in fingerprints):
_append_issue(issues, "provenance", f"{label}: fingerprints must be non-empty strings")
elif fingerprints[0] != fingerprints[1] or fingerprints[2] != fingerprints[3]:
_append_issue(issues, "provenance", f"{label}: baseline/candidate fingerprints differ")
total_dim = TOTAL_DIMS[workload]
baseline_states = 5 * EXPECTED_PARTICLES + (1 if BASELINE_METHODS[workload] == "G8" else 0)
expected_baseline_bytes = baseline_states * total_dim * 4
raw_ratio = candidate_entry.get("ratio")
if not _is_finite_number(raw_ratio) or not (0.0 < float(raw_ratio) <= 1.0):
_append_issue(issues, "state", f"{label}: invalid candidate ratio")
expected_candidate_bytes = -1
else:
expected_latent_dim = compute_latent_dim(total_dim, float(raw_ratio))
expected_candidate_bytes = compute_core_swarm_state_bytes(EXPECTED_PARTICLES, expected_latent_dim)
if candidate_entry.get("total_dim") != total_dim:
_append_issue(issues, "state", f"{label}: total_dim mismatch")
if candidate_entry.get("latent_dim") != expected_latent_dim:
_append_issue(issues, "state", f"{label}: latent_dim mismatch")
if candidate_entry.get("core_swarm_state_bytes") != expected_candidate_bytes:
_append_issue(issues, "state", f"{label}: candidate state bytes mismatch")
if candidate_entry.get("baseline_core_swarm_state_bytes") != expected_baseline_bytes:
_append_issue(issues, "state", f"{label}: candidate baseline state bytes mismatch")
ratio = expected_candidate_bytes / expected_baseline_bytes
state_ratios[workload] = max(state_ratios.get(workload, 0.0), ratio)
if not _is_finite_number(candidate_entry.get("state_ratio")) or not math.isclose(
float(candidate_entry.get("state_ratio", -1.0)), ratio, rel_tol=1e-6, abs_tol=1e-6
):
_append_issue(issues, "state", f"{label}: reported state_ratio mismatch")
baseline_runs, b_queries, b_samples = _validate_runs(
baseline,
expected_seeds,
expected_baseline_bytes,
f"{label}/baseline",
False,
issues,
)
candidate_runs, c_queries, c_samples = _validate_runs(
candidate_entry,
expected_seeds,
expected_candidate_bytes,
f"{label}/candidate",
True,
issues,
)
observed_runs += len(baseline_runs) + len(candidate_runs)
observed_queries += b_queries + c_queries
observed_samples += b_samples + c_samples
baseline_stats = _validate_stats(baseline, baseline_runs, f"{label}/baseline", issues)
candidate_stats = _validate_stats(candidate_entry, candidate_runs, f"{label}/candidate", issues)
if baseline_stats is not None and candidate_stats is not None:
baseline_acc, baseline_nll = baseline_stats
candidate_acc, candidate_nll = candidate_stats
nll_reduction = (
(baseline_nll - candidate_nll) / baseline_nll
if baseline_nll > 0.0
else float("nan")
)
if not math.isfinite(nll_reduction):
_append_issue(issues, "finite", f"{label}: NLL reduction is non-finite")
else:
cells.append(
{
"phase": phase,
"split_seed": split_seed,
"workload_id": workload,
"baseline_acc": baseline_acc,
"candidate_acc": candidate_acc,
"baseline_nll": baseline_nll,
"candidate_nll": candidate_nll,
"acc_gain_pp": candidate_acc - baseline_acc,
"nll_reduction_fraction": nll_reduction,
}
)
expected_runs = len(expected_splits) * len(WORKLOADS) * len(expected_seeds) * 2
if observed_runs != expected_runs:
_append_issue(issues, "accounting", f"{phase}: observed {observed_runs} runs, expected {expected_runs}")
resources = artifact.get("resource_totals")
if not isinstance(resources, dict):
_append_issue(issues, "accounting", f"{phase}: missing resource_totals")
resources = {}
if resources.get("total_runs") != observed_runs:
_append_issue(issues, "accounting", f"{phase}: total_runs does not match records")
if resources.get("total_queries") != observed_queries:
_append_issue(issues, "accounting", f"{phase}: total_queries does not match records")
if resources.get("total_samples_evaluated") != observed_samples:
_append_issue(issues, "accounting", f"{phase}: total_samples_evaluated does not match records")
if resources.get("official_test_evaluations") != 0:
_append_issue(issues, "test", f"{phase}: resource official_test_evaluations must be 0")
max_state_ratio = max(state_ratios.values(), default=1.0)
policy_signature = {
"candidate_config": candidate_config,
"workloads": workload_config,
}
return {
"issues": issues,
"cells": cells,
"state_ratios": state_ratios,
"max_state_ratio": max_state_ratio,
"policy_signature": policy_signature,
}
def evaluate_heavy_cross_split(
development_artifact: Dict[str, Any],
confirmation_artifact: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
dev_result = _validate_artifact(development_artifact, "development")
conf_result = (
_validate_artifact(confirmation_artifact, "confirmation")
if confirmation_artifact is not None
else None
)
gates: Dict[str, Dict[str, Any]] = {}
failed_gates: List[str] = []
development_gate_names: List[str] = []
def record_gate(
name: str,
passed: bool,
observed: Any,
expected: Any,
details: str,
development_gate: bool = False,
) -> None:
gates[name] = {
"pass": bool(passed),
"observed": observed,
"expected": expected,
"details": details,
}
if not passed:
failed_gates.append(name)
if development_gate:
development_gate_names.append(name)
all_results = [dev_result] + ([conf_result] if conf_result is not None else [])
record_gate(
"schema_and_phase_seeds",
all(not result["issues"]["schema"] for result in all_results),
[message for result in all_results for message in result["issues"]["schema"]][:10],
"complete artifacts with exact declared phase/split/swarm seeds",
"Missing evidence is rejected rather than defaulted",
True,
)
record_gate(
"official_test_sealed",
all(not result["issues"]["test"] for result in all_results),
[message for result in all_results for message in result["issues"]["test"]][:10],
"loaded=false and evaluations=0 at artifact, resource, and run levels",
"Official test data must never be loaded or evaluated",
True,
)
record_gate(
"all_runs_finite",
all(not result["issues"]["finite"] for result in all_results),
[message for result in all_results for message in result["issues"]["finite"]][:10],
"all aggregate and per-run validation metrics and times finite",
"Every recorded validation metric must be finite",
True,
)
record_gate(
"split_and_fingerprint_matched",
all(not result["issues"]["provenance"] for result in all_results),
[message for result in all_results for message in result["issues"]["provenance"]][:10],
"non-empty matching split/data fingerprints and split seeds per baseline/candidate cell",
"Every delta must use a baseline rerun on the identical partition",
True,
)
policy_matches = conf_result is None or (
dev_result["policy_signature"] == conf_result["policy_signature"]
)
config_issues = [message for result in all_results for message in result["issues"]["config"]]
if not policy_matches:
config_issues.append("confirmation candidate policy differs from the frozen development policy")
record_gate(
"configuration_and_policy_matched",
not config_issues,
config_issues[:10],
"12p x 80e x fixed10k, exact seeds, matching baseline, identical frozen candidate policy",
"Confirmation cannot change the development-selected policy",
True,
)
record_gate(
"query_and_sample_accounting_exact",
all(not result["issues"]["accounting"] for result in all_results),
[message for result in all_results for message in result["issues"]["accounting"]][:10],
f"{EXPECTED_QUERIES} queries and {EXPECTED_SAMPLES} sample evaluations per run",
"Per-run and aggregate accounting must agree exactly",
True,
)
max_state_ratio = max(result["max_state_ratio"] for result in all_results)
state_issues = [message for result in all_results for message in result["issues"]["state"]]
state_ok = not state_issues and 0.0 < max_state_ratio <= 0.5 + 1e-12
record_gate(
"maximum_state_ratio_each_workload",
state_ok,
{"max_state_ratio": max_state_ratio, "issues": state_issues[:10]},
"analytically verified state ratio <= 0.5 for every cell",
"No absent or reported-only state evidence is accepted",
True,
)
dev_cells = dev_result["cells"]
conf_cells = conf_result["cells"] if conf_result is not None else []
all_cells = dev_cells + conf_cells
expected_dev_cells = len(EXPECTED_DEV_SPLIT_SEEDS) * len(WORKLOADS)
complete_dev_cells = len(dev_cells) == expected_dev_cells
def nonregression(cells: List[Dict[str, Any]]) -> Tuple[bool, float, float]:
if not cells:
return False, float("inf"), float("inf")
max_acc_regression = max(-cell["acc_gain_pp"] for cell in cells)
max_nll_regression = max(-cell["nll_reduction_fraction"] for cell in cells)
return (
max_acc_regression <= 1.0 + 1e-12 and max_nll_regression <= 0.05 + 1e-12,
max_acc_regression,
max_nll_regression,
)
all_nonreg, max_acc_reg, max_nll_reg = nonregression(all_cells)
record_gate(
"maximum_accuracy_regression_percentage_points_each_split_workload",
bool(all_cells) and max_acc_reg <= 1.0 + 1e-12,
max_acc_reg,
"<= 1.0 pp",
"No evaluated split-workload cell may regress accuracy by more than 1 pp",
True,
)
record_gate(
"maximum_nll_regression_fraction_each_split_workload",
bool(all_cells) and max_nll_reg <= 0.05 + 1e-12,
max_nll_reg,
"<= 0.05",
"No evaluated split-workload cell may regress NLL by more than 5%",
True,
)
dev_nonreg, dev_acc_reg, dev_nll_reg = nonregression(dev_cells)
dev_acc_mean = _mean([cell["acc_gain_pp"] for cell in dev_cells]) if dev_cells else float("-inf")
dev_nll_mean = _mean([cell["nll_reduction_fraction"] for cell in dev_cells]) if dev_cells else float("-inf")
record_gate(
"development_grand_mean_accuracy_gain_minimum_pp",
complete_dev_cells and dev_acc_mean >= 0.0,
dev_acc_mean,
">= 0.0 pp",
"Development grand mean accuracy must not regress",
True,
)
record_gate(
"development_grand_mean_nll_reduction_minimum_fraction",
complete_dev_cells and dev_nll_mean >= 0.0,
dev_nll_mean,
">= 0.0",
"Development grand mean NLL must not regress",
True,
)
dev_mw = [cell for cell in dev_cells if cell["workload_id"] == "mnist_wide"]
dev_mw_acc = _mean([cell["acc_gain_pp"] for cell in dev_mw]) if dev_mw else float("-inf")
dev_mw_nll = _mean([cell["nll_reduction_fraction"] for cell in dev_mw]) if dev_mw else float("-inf")
record_gate(
"development_mnist_wide_improvement",
len(dev_mw) == len(EXPECTED_DEV_SPLIT_SEEDS) and (dev_mw_acc >= 2.0 or dev_mw_nll >= 0.05),
{"accuracy_gain_pp": dev_mw_acc, "nll_reduction_fraction": dev_mw_nll},
"mean accuracy gain >=2pp OR mean NLL reduction >=5%",
"The prior worst baseline workload must materially improve across development partitions",
True,
)
development_pass = (
all(not messages for messages in dev_result["issues"].values())
and 0.0 < dev_result["max_state_ratio"] <= 0.5 + 1e-12
and complete_dev_cells
and dev_nonreg
and dev_acc_mean >= 0.0
and dev_nll_mean >= 0.0
and len(dev_mw) == len(EXPECTED_DEV_SPLIT_SEEDS)
and (dev_mw_acc >= 2.0 or dev_mw_nll >= 0.05)
)
if confirmation_artifact is None:
record_gate(
"confirmation_executed",
False,
"not executed",
"one exact confirmation artifact after development_pass",
"A development pass only qualifies the frozen policy for one-shot confirmation",
)
else:
record_gate(
"confirmation_executed",
True,
{"split_seeds": confirmation_artifact.get("split_seeds"), "swarm_seeds": confirmation_artifact.get("swarm_seeds")},
{"split_seeds": EXPECTED_CONF_SPLIT_SEEDS, "swarm_seeds": EXPECTED_CONF_SWARM_SEEDS},
"Confirmation evidence is evaluated only with the exact sealed phase contract",
)
expected_conf_cells = len(EXPECTED_CONF_SPLIT_SEEDS) * len(WORKLOADS)
complete_conf_cells = len(conf_cells) == expected_conf_cells
conf_nonreg, conf_acc_reg, conf_nll_reg = nonregression(conf_cells)
record_gate(
"confirmation_per_cell_non_regression",
complete_conf_cells and conf_nonreg,
{"cells": len(conf_cells), "max_acc_regression_pp": conf_acc_reg, "max_nll_regression_fraction": conf_nll_reg},
"4 cells; accuracy regression <=1pp and NLL regression <=5% in each",
"The sealed partition must remain safe workload by workload",
)
conf_acc_mean = _mean([cell["acc_gain_pp"] for cell in conf_cells]) if conf_cells else float("-inf")
conf_nll_mean = _mean([cell["nll_reduction_fraction"] for cell in conf_cells]) if conf_cells else float("-inf")
record_gate(
"confirmation_grand_mean_accuracy_gain_minimum_pp",
complete_conf_cells and conf_acc_mean >= 1.5,
conf_acc_mean,
">= 1.5 pp",
"One-shot confirmation must retain the predeclared accuracy effect",
)
record_gate(
"confirmation_grand_mean_nll_reduction_minimum_fraction",
complete_conf_cells and conf_nll_mean >= 0.02,
conf_nll_mean,
">= 0.02",
"One-shot confirmation must retain the predeclared NLL effect",
)
conf_mw = [cell for cell in conf_cells if cell["workload_id"] == "mnist_wide"]
conf_mw_acc = _mean([cell["acc_gain_pp"] for cell in conf_mw]) if conf_mw else float("-inf")
conf_mw_nll = _mean([cell["nll_reduction_fraction"] for cell in conf_mw]) if conf_mw else float("-inf")
record_gate(
"confirmation_mnist_wide_improvement",
len(conf_mw) == 1 and (conf_mw_acc >= 1.0 or conf_mw_nll >= 0.03),
{"accuracy_gain_pp": conf_mw_acc, "nll_reduction_fraction": conf_mw_nll},
"accuracy gain >=1pp OR NLL reduction >=3%",
"The prior worst workload must improve on the sealed partition",
)
combined_mw = dev_mw + conf_mw
combined_mw_acc = _mean([cell["acc_gain_pp"] for cell in combined_mw]) if combined_mw else float("-inf")
combined_mw_nll = _mean([cell["nll_reduction_fraction"] for cell in combined_mw]) if combined_mw else float("-inf")
record_gate(
"combined_mnist_wide_improvement",
len(combined_mw) == 3 and (combined_mw_acc >= 2.0 or combined_mw_nll >= 0.05),
{"accuracy_gain_pp": combined_mw_acc, "nll_reduction_fraction": combined_mw_nll},
"three-split mean accuracy gain >=2pp OR NLL reduction >=5%",
"The material worst-workload improvement must hold across all new partitions",
)
score_cells = all_cells if confirmation_artifact is not None else dev_cells
mean_acc_gain = _mean([cell["acc_gain_pp"] for cell in score_cells]) if score_cells else 0.0
mean_nll_reduction = _mean([cell["nll_reduction_fraction"] for cell in score_cells]) if score_cells else 0.0
score_gate_names = list(gates) if confirmation_artifact is not None else development_gate_names
score_failed_gates = sum(not gates[name]["pass"] for name in score_gate_names)
state_points = 10.0 * math.log2(1.0 / max_state_ratio) if 0.0 < max_state_ratio <= 1.0 else 0.0
score = (
100.0 * mean_nll_reduction
+ mean_acc_gain
+ state_points
- 100.0 * score_failed_gates
)
mission_pass = confirmation_artifact is not None and not failed_gates
return {
"pass": bool(mission_pass),
"development_pass": bool(development_pass),
"eligible_for_confirmation": bool(development_pass and confirmation_artifact is None),
"score": float(score),
"evaluator_version": EVALUATOR_VERSION,
"failed_hard_gate_count": len(failed_gates),
"failed_gates": failed_gates,
"score_failed_gate_count": score_failed_gates,
"gates": gates,
"score_components": {
"mean_relative_nll_reduction_pct": 100.0 * mean_nll_reduction,
"mean_accuracy_gain_pp": mean_acc_gain,
"state_efficiency_points": state_points,
"gate_penalty_points": 100.0 * score_failed_gates,
"max_state_ratio": max_state_ratio,
},
"summary_metrics": {
"development_cells": len(dev_cells),
"confirmation_cells": len(conf_cells),
"development_grand_mean_accuracy_gain_pp": dev_acc_mean,
"development_grand_mean_nll_reduction_fraction": dev_nll_mean,
"development_mnist_wide_accuracy_gain_pp": dev_mw_acc,
"development_mnist_wide_nll_reduction_fraction": dev_mw_nll,
},
"cell_metrics": all_cells,
"state_ratios": {
"development": dev_result["state_ratios"],
"confirmation": conf_result["state_ratios"] if conf_result is not None else None,
},
}
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Strict Heavy PSO cross-split evaluator")
parser.add_argument("--development", required=True, help="Development artifact JSON")
parser.add_argument("--confirmation", default=None, help="Optional one-shot confirmation artifact JSON")
parser.add_argument("--output", default=None, help="Optional evaluation JSON output")
return parser
def main() -> None:
args = build_parser().parse_args()
development = load_artifact(Path(args.development))
confirmation = load_artifact(Path(args.confirmation)) if args.confirmation else None
result = evaluate_heavy_cross_split(development, confirmation)
if args.output:
save_json_atomic(result, Path(args.output))
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,974 @@
"""Independent evaluator for the post-training model-convergence protocol.
This module is deliberately data-only: it reads JSON manifests, result records and
stored prediction records. It never imports an adapter, optional detection
package, dataset, checkpoint, or model. A result is useful only when all of the
frozen matrix and sealing invariants can be demonstrated from the saved evidence.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import os
import random
import sys
import tempfile
from pathlib import Path
from typing import Any, Iterable, Mapping, Sequence
EVALUATOR_VERSION = "POST-TRAINING-MODEL-CONVERGENCE-EVALUATOR 1.0.0"
PROTOCOL_VERSION = "post-training-model-convergence-1.0.0"
WORKLOADS = ("cifar10_resnet18", "cifar10_resnet50", "voc_yolo11n")
CLASSIFICATION_WORKLOADS = WORKLOADS[:2]
DETECTION_WORKLOAD = WORKLOADS[2]
BASE_SEEDS = (501, 502, 503)
SWARM_SEEDS = (601, 602, 603)
SPLIT_SEED = 20260908
PROJECTION_SEED = 20260909
BOOTSTRAP_SEED = 20260910
PARTICLES = 12
PRIMARY_GENERATIONS = 60
ENSEMBLE_GENERATIONS = 20
PRIMARY_QUERIES = PARTICLES * PRIMARY_GENERATIONS
ENSEMBLE_QUERIES = PARTICLES * ENSEMBLE_GENERATIONS
PRIMARY_RUNS = len(WORKLOADS) * len(BASE_SEEDS) * len(SWARM_SEEDS)
ENSEMBLE_RUNS = len(WORKLOADS) * len(SWARM_SEEDS)
TOTAL_PSO_QUERIES = PRIMARY_RUNS * PRIMARY_QUERIES + ENSEMBLE_RUNS * ENSEMBLE_QUERIES
OBJECTIVE_SAMPLES = {"cifar10_resnet18": 1024, "cifar10_resnet50": 1024, "voc_yolo11n": 512}
TOTAL_CANDIDATE_SAMPLES = sum(
(len(BASE_SEEDS) * len(SWARM_SEEDS) * PRIMARY_QUERIES + len(SWARM_SEEDS) * ENSEMBLE_QUERIES)
* OBJECTIVE_SAMPLES[w] for w in WORKLOADS
)
BOOTSTRAP_RESAMPLES = 2000
BOOTSTRAP_ALPHA = 0.05 / 6.0
ISSUE_CATEGORIES = (
"schema", "provenance", "matrix", "accounting", "seal", "selection",
"finite", "metrics", "plateau", "overfit", "leakage", "bootstrap", "gates",
)
def _issue(issues: dict[str, list[str]], category: str, message: str) -> None:
issues.setdefault(category, []).append(message)
def _finite(value: Any) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(float(value))
def _walk_nonfinite(value: Any, path: str = "") -> list[str]:
out: list[str] = []
if isinstance(value, float) and not math.isfinite(value):
out.append(path or "$")
elif isinstance(value, Mapping):
for key, item in value.items():
out.extend(_walk_nonfinite(item, f"{path}.{key}" if path else str(key)))
elif isinstance(value, (list, tuple)):
for i, item in enumerate(value):
out.extend(_walk_nonfinite(item, f"{path}[{i}]"))
return out
def _json(path: Path) -> Any:
with path.open("r", encoding="utf-8") as handle:
return json.load(handle)
def _canonical(value: Any) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _atomic_json(path: Path, value: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(value, handle, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(name, path)
except BaseException:
try:
os.unlink(name)
except OSError:
pass
raise
def _number(value: Any, *keys: str) -> float | None:
if isinstance(value, Mapping):
for key in keys:
candidate = value.get(key)
if _finite(candidate):
return float(candidate)
for candidate in value.values():
found = _number(candidate, *keys)
if found is not None:
return found
elif isinstance(value, (list, tuple)):
for candidate in value:
found = _number(candidate, *keys)
if found is not None:
return found
return None
def _int(value: Any, *keys: str) -> int | None:
number = _number(value, *keys)
if number is None or not number.is_integer():
return None
return int(number)
def _same(a: Any, b: Any, tol: float = 1e-9) -> bool:
return _finite(a) and _finite(b) and math.isclose(float(a), float(b), rel_tol=tol, abs_tol=tol)
def _resolve_json(root: Path, value: Any) -> Any:
"""Resolve a saved JSON prediction reference without opening model/data files."""
if isinstance(value, Mapping):
for key in ("path", "file", "artifact", "prediction_artifact", "predictions_path"):
ref = value.get(key)
if isinstance(ref, str) and ref.lower().endswith((".json", ".jsonl", ".pt")):
return _resolve_json(root, ref)
return value
if isinstance(value, str) and value.lower().endswith((".json", ".jsonl", ".pt")):
path = (root / value).resolve()
if root.resolve() not in path.parents:
raise ValueError(f"prediction artifact escapes run root: {value}")
if not path.is_file():
raise FileNotFoundError(path)
if path.suffix == ".jsonl":
return [_json_line for _json_line in (json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()) if _json_line]
if path.suffix == ".pt":
try:
import torch
return torch.load(path, map_location="cpu", weights_only=True)
except (ImportError, OSError, RuntimeError, TypeError, ValueError) as exc:
raise ValueError(f"unable to load weights-only prediction artifact: {path}: {exc}") from exc
return _json(path)
return value
def _hash_manifest(root: Path, manifest: Mapping[str, Any], issues: dict[str, list[str]]) -> bool:
good = True
if manifest.get("protocol_version") != PROTOCOL_VERSION:
_issue(issues, "provenance", f"frozen manifest protocol mismatch: {manifest.get('protocol_version')!r}")
good = False
if manifest.get("state") != "frozen":
_issue(issues, "seal", f"frozen manifest state must be 'frozen', got {manifest.get('state')!r}")
good = False
declared = manifest.get("manifest_hash")
payload = {key: manifest[key] for key in ("protocol_version", "config", "artifacts", "state") if key in manifest}
if not isinstance(declared, str) or hashlib.sha256(_canonical(payload)).hexdigest() != declared:
_issue(issues, "seal", "frozen_manifest.json self-hash mismatch")
good = False
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, Mapping) or not artifacts:
_issue(issues, "schema", "frozen manifest requires a non-empty artifacts map")
return False
for name, expected in artifacts.items():
if not isinstance(name, str) or not isinstance(expected, str) or len(expected) != 64:
_issue(issues, "schema", f"invalid frozen artifact hash declaration: {name!r}")
good = False
continue
path = (root / name).resolve()
if root.resolve() not in path.parents:
_issue(issues, "seal", f"frozen artifact escapes run root: {name}")
good = False
elif not path.is_file():
_issue(issues, "seal", f"frozen artifact is missing: {name}")
good = False
elif _sha256(path) != expected:
_issue(issues, "seal", f"frozen artifact hash drift: {name}")
good = False
return good
def _config_checks(config: Any, issues: dict[str, list[str]], workload_id: str | None = None) -> None:
if not isinstance(config, Mapping):
_issue(issues, "schema", "missing or non-object config")
return
expected: dict[str, Any] = {
"protocol_version": PROTOCOL_VERSION, "split_seed": SPLIT_SEED,
"projection_seed": PROJECTION_SEED, "bootstrap_seed": BOOTSTRAP_SEED,
"particle_count": PARTICLES, "pso_generations": PRIMARY_GENERATIONS,
"residual_dimension": 64, "residual_bound": 1.0, "initial_radius": 0.25,
"objective_checkpoints": [0, 10, 20, 30, 40, 50, 60],
"base_seeds": list(BASE_SEEDS), "swarm_seeds": list(SWARM_SEEDS),
}
for key, value in expected.items():
got = config.get(key)
if got != value and not (isinstance(got, tuple) and list(got) == value):
_issue(issues, "provenance", f"config.{key} must be {value!r}, got {got!r}")
ids = config.get("workload_ids")
if ids is not None and sorted(ids) != sorted(WORKLOADS):
_issue(issues, "matrix", f"config.workload_ids must be {list(WORKLOADS)!r}")
if workload_id and config.get("workload_id") not in (None, workload_id):
_issue(issues, "matrix", f"config.workload_id disagrees with {workload_id}")
def _counter(value: Any, keys: Sequence[str]) -> int | None:
if not isinstance(value, Mapping):
return None
for key in keys:
candidate = value.get(key)
if isinstance(candidate, int) and not isinstance(candidate, bool):
return candidate
return None
def _check_leakage(result: Mapping[str, Any], issues: dict[str, list[str]], workload: str) -> None:
leakage = result.get("leakage_counters")
if not isinstance(leakage, Mapping):
_issue(issues, "schema", f"{workload}: missing leakage_counters")
leakage = {}
loaded_before = leakage.get("official_test_data_loaded_before_freeze", leakage.get("test_data_loaded_before_freeze"))
if loaded_before is not False:
_issue(issues, "leakage", f"{workload}: official test data must be explicitly marked not loaded before freeze")
evaluated_before = leakage.get("official_test_evaluations_before_freeze", leakage.get("test_evaluations_before_freeze", leakage.get("official_test_forward_passes_before_freeze")))
if evaluated_before != 0:
_issue(issues, "leakage", f"{workload}: official test exposure before freeze must be explicitly zero")
for key in ("official_test_data_loaded_before_freeze", "official_test_evaluations_before_freeze"):
if key in result and ((key.endswith("freeze") and result[key] not in (False, 0))):
_issue(issues, "leakage", f"{workload}: contradictory top-level {key}")
construction = _counter(leakage, ("official_test_construction", "official_test_dataset_construction"))
if construction != 1:
_issue(issues, "leakage", f"{workload}: official test construction must equal one, got {construction}")
forwards = _counter(leakage, ("official_test_forward_passes", "official_test_evaluations"))
if forwards is None or forwards < 1:
_issue(issues, "leakage", f"{workload}: official test forward/evaluation ledger must be positive, got {forwards}")
confirmation = result.get("confirmation")
if not isinstance(confirmation, Mapping):
_issue(issues, "schema", f"{workload}: missing confirmation record")
return
for key in ("second_confirmation", "confirmation_repeated", "post_test_tuning", "post_test_reruns"):
if confirmation.get(key) not in (None, False, 0, []):
_issue(issues, "leakage", f"{workload}: forbidden repeated confirmation/tuning flag {key}")
for key in ("official_test_data_loaded_before_freeze", "official_test_evaluations_before_freeze"):
if key in confirmation and confirmation[key] not in (False, 0):
_issue(issues, "leakage", f"{workload}: confirmation contradicts sealed pre-freeze {key}")
def _record_queries(record: Mapping[str, Any]) -> tuple[int | None, int | None]:
counters = record.get("counters") if isinstance(record.get("counters"), Mapping) else record
queries = _int(counters, "objective_queries", "queries", "query_count", "total_queries", "evaluated_queries")
samples = _int(counters, "objective_samples", "samples", "sample_evaluations", "candidate_sample_evaluations", "total_sample_evaluations")
return queries, samples
def _seed_from_key(key: Any) -> int | None:
try:
text = str(key)
if text.isdigit():
return int(text)
except Exception:
pass
return None
def _collect_cells(node: Any, base: int | None = None, swarm: int | None = None) -> list[dict[str, Any]]:
"""Collect cells from either explicit records or base->swarm mappings."""
found: list[dict[str, Any]] = []
if isinstance(node, Mapping):
b = _int(node, "base_seed") if _int(node, "base_seed") is not None else base
s = _int(node, "swarm_seed") if _int(node, "swarm_seed") is not None else swarm
if s is None:
s = _int(node, "seed")
if b is not None and s is not None and any(k in node for k in ("counters", "objective_queries", "objective_samples", "queries", "samples", "generation", "endpoint", "metrics")):
item = dict(node); item.setdefault("base_seed", b); item.setdefault("swarm_seed", s); found.append(item)
for key, value in node.items():
key_seed = _seed_from_key(key)
if key_seed in BASE_SEEDS:
found.extend(_collect_cells(value, key_seed, s))
elif key_seed in SWARM_SEEDS:
found.extend(_collect_cells(value, b, key_seed))
elif key not in {"base_seed", "swarm_seed"}:
found.extend(_collect_cells(value, b, s))
elif isinstance(node, list):
for value in node:
found.extend(_collect_cells(value, base, swarm))
return found
def _collect_base_cells(node: Any, base: int | None = None) -> list[dict[str, Any]]:
"""Collect one record per base seed from flattened or base->record schemas."""
found: list[dict[str, Any]] = []
if isinstance(node, Mapping):
b = _int(node, "base_seed") if _int(node, "base_seed") is not None else base
if b is not None and any(k in node for k in ("updates", "gradient_updates", "counters", "metrics", "checkpoint", "endpoint", "objective")):
item = dict(node); item.setdefault("base_seed", b); found.append(item)
for key, value in node.items():
key_seed = _seed_from_key(key)
if key_seed in BASE_SEEDS: found.extend(_collect_base_cells(value, key_seed))
elif key not in {"base_seed", "swarm_seed"}: found.extend(_collect_base_cells(value, b))
elif isinstance(node, list):
for value in node: found.extend(_collect_base_cells(value, base))
return found
def _method_base_cells(result: Mapping[str, Any], method: str) -> list[dict[str, Any]]:
arms = result.get("arms")
return _collect_base_cells(arms.get(method)) if isinstance(arms, Mapping) and method in arms else []
def _method_evidence(value: Any, method: str, family: str, root: Path) -> bool:
"""Require non-empty metric or prediction evidence below an exact method key."""
if isinstance(value, Mapping):
for key, item in value.items():
normalized = str(key).lower().replace("-", "_")
if normalized == method:
if _prediction_records(item, root) is not None: return True
if isinstance(item, Mapping) and (_number(item, "nll", "loss", "accuracy", "map50_95", "map50", "mAP50-95") is not None): return True
if _method_evidence(item, method, family, root): return True
if _method_evidence(item, method, family, root): return True
elif isinstance(value, list):
return bool(value) and any(_method_evidence(item, method, family, root) for item in value)
return False
def _require_confirmation_methods(result: Mapping[str, Any], workload: str, family: str, root: Path, issues: dict[str, list[str]]) -> None:
confirmation = result.get("confirmation")
required = {"feature_pso", "feature_random", "feature_adam", "head_adam"}
required |= ({"uniform", "uniform_temperature", "slsqp_weights", "ensemble_pso"} if family == "classification" else {"uniform_wbf", "ensemble_pso", "ensemble_random"})
for method in sorted(required):
if not _method_evidence(confirmation, method, family, root):
_issue(issues, "metrics", f"{workload}: confirmation lacks predictions/metrics for required method {method}")
def _method_cells(result: Mapping[str, Any], method: str) -> list[dict[str, Any]]:
arms = result.get("arms")
if not isinstance(arms, Mapping):
return []
value = arms.get(method)
return _collect_cells(value) if value is not None else []
def _verify_matrix(result: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> dict[str, Any]:
arms = result.get("arms")
if not isinstance(arms, Mapping):
_issue(issues, "schema", f"{workload}: arms must be an object")
arms = {}
required = {"feature_pso", "feature_random", "feature_adam", "head_adam"}
missing = required - set(arms)
if missing:
_issue(issues, "matrix", f"{workload}: missing arms {sorted(missing)}")
stats: dict[str, Any] = {"primary_queries": 0, "primary_random_queries": 0, "ensemble_queries": 0, "primary_samples": 0, "primary_random_samples": 0, "ensemble_samples": 0, "cells": {}}
for method in ("feature_pso", "feature_random"):
cells = _method_cells(result, method)
stats["cells"][method] = len(cells)
expected = len(BASE_SEEDS) * len(SWARM_SEEDS)
if len(cells) != expected:
_issue(issues, "matrix", f"{workload}: {method} requires {expected} base/swarm cells, got {len(cells)}")
seen: set[tuple[int, int]] = set()
for cell in cells:
key = (_int(cell, "base_seed") or -1, _int(cell, "swarm_seed") or -1)
if key in seen or key[0] not in BASE_SEEDS or key[1] not in SWARM_SEEDS:
_issue(issues, "matrix", f"{workload}: invalid or duplicate {method} cell {key}")
seen.add(key)
queries, samples = _record_queries(cell)
if queries != PRIMARY_QUERIES:
_issue(issues, "accounting", f"{workload}: {method} {key} queries must be {PRIMARY_QUERIES}, got {queries}")
if samples != PRIMARY_QUERIES * OBJECTIVE_SAMPLES[workload]:
_issue(issues, "accounting", f"{workload}: {method} {key} samples must be {PRIMARY_QUERIES * OBJECTIVE_SAMPLES[workload]}, got {samples}")
if method == "feature_pso":
if queries is not None: stats["primary_queries"] += queries
if samples is not None: stats["primary_samples"] += samples
else:
if queries is not None: stats["primary_random_queries"] += queries
if samples is not None: stats["primary_random_samples"] += samples
for method in ("feature_adam", "head_adam"):
cells = _method_base_cells(result, method); stats["cells"][method] = len(cells)
if len(cells) != len(BASE_SEEDS): _issue(issues, "matrix", f"{workload}: {method} requires exactly three base cells, got {len(cells)}")
seen = set()
for cell in cells:
seed = _int(cell, "base_seed")
if seed in seen or seed not in BASE_SEEDS: _issue(issues, "matrix", f"{workload}: invalid or duplicate {method} base cell {seed}")
if seed is not None: seen.add(seed)
ensemble = result.get("ensemble")
if not isinstance(ensemble, Mapping):
_issue(issues, "schema", f"{workload}: ensemble must be an object")
ensemble = {}
required_ensemble = ("uniform", "uniform_temperature", "slsqp_weights", "ensemble_pso") if workload in CLASSIFICATION_WORKLOADS else ("uniform_wbf", "ensemble_pso", "ensemble_random")
for method in required_ensemble:
if method not in ensemble or ensemble.get(method) in (None, {}, []): _issue(issues, "matrix", f"{workload}: missing required ensemble method {method}")
ens_pso = ensemble.get("ensemble_pso", ensemble.get("pso"))
cells = _collect_cells(ens_pso) if ens_pso is not None else []
stats["cells"]["ensemble_pso"] = len(cells)
if len(cells) != len(SWARM_SEEDS):
_issue(issues, "matrix", f"{workload}: ensemble_pso requires three swarm cells, got {len(cells)}")
seen_swarm: set[int] = set()
for cell in cells:
seed = _int(cell, "swarm_seed", "seed")
if seed in seen_swarm or seed not in SWARM_SEEDS:
_issue(issues, "matrix", f"{workload}: invalid or duplicate ensemble swarm cell {seed}")
if seed is not None: seen_swarm.add(seed)
queries, samples = _record_queries(cell)
if queries != ENSEMBLE_QUERIES:
_issue(issues, "accounting", f"{workload}: ensemble_pso queries must be {ENSEMBLE_QUERIES}, got {queries}")
if samples != ENSEMBLE_QUERIES * OBJECTIVE_SAMPLES[workload]:
_issue(issues, "accounting", f"{workload}: ensemble_pso samples must be {ENSEMBLE_QUERIES * OBJECTIVE_SAMPLES[workload]}, got {samples}")
if queries is not None: stats["ensemble_queries"] += queries
if samples is not None: stats["ensemble_samples"] += samples
if workload == DETECTION_WORKLOAD:
random_cells = _collect_cells(ensemble.get("ensemble_random")) if ensemble.get("ensemble_random") is not None else []
stats["cells"]["ensemble_random"] = len(random_cells)
if len(random_cells) != len(SWARM_SEEDS): _issue(issues, "matrix", f"{workload}: ensemble_random requires three swarm cells, got {len(random_cells)}")
seen_random = set()
for cell in random_cells:
seed = _int(cell, "swarm_seed", "seed")
if seed in seen_random or seed not in SWARM_SEEDS: _issue(issues, "matrix", f"{workload}: invalid or duplicate ensemble_random seed {seed}")
if seed is not None: seen_random.add(seed)
return stats
def _verify_selection(result: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> None:
selection = result.get("development_selection")
if not isinstance(selection, Mapping):
_issue(issues, "selection", f"{workload}: missing development_selection")
return
selected = selection.get("primary", selection.get("feature_pso", selection.get("selected")))
if not isinstance(selected, Mapping):
_issue(issues, "selection", f"{workload}: missing primary selected endpoints")
return
for base in BASE_SEEDS:
entry = selected.get(str(base), selected.get(base))
if not isinstance(entry, Mapping):
_issue(issues, "selection", f"{workload}: no selected endpoint for base seed {base}")
continue
swarm = _int(entry, "swarm_seed", "seed")
generation = _int(entry, "generation", "final_generation")
if swarm not in SWARM_SEEDS:
_issue(issues, "selection", f"{workload}: selected seed {base} has invalid swarm {swarm}")
if generation != PRIMARY_GENERATIONS:
_issue(issues, "selection", f"{workload}: selected endpoint {base} is not final generation 60")
matches = [c for c in _method_cells(result, "feature_pso") if _int(c, "base_seed") == base and _int(c, "swarm_seed") == swarm]
if not matches:
_issue(issues, "selection", f"{workload}: selected endpoint {base}/{swarm} is not a feature_pso cell")
elif entry.get("vector_hash") and matches[0].get("vector_hash") and entry["vector_hash"] != matches[0]["vector_hash"]:
_issue(issues, "selection", f"{workload}: selected vector hash drift for base {base}")
def _prediction_records(value: Any, root: Path) -> list[dict[str, Any]] | None:
try: value = _resolve_json(root, value)
except (OSError, ValueError, json.JSONDecodeError): return None
if isinstance(value, Mapping):
if "probabilities" in value and "targets" in value:
probabilities, targets = value["probabilities"], value["targets"]
for attr in ("detach", "cpu"):
if hasattr(probabilities, attr): probabilities = getattr(probabilities, attr)()
if hasattr(targets, attr): targets = getattr(targets, attr)()
if hasattr(probabilities, "tolist"): probabilities = probabilities.tolist()
if hasattr(targets, "tolist"): targets = targets.tolist()
if isinstance(probabilities, (list, tuple)) and isinstance(targets, (list, tuple)) and len(probabilities) == len(targets):
return [{"probabilities": list(probability), "target": int(target)} for probability, target in zip(probabilities, targets)]
return None
for key in ("records", "predictions", "images", "examples", "data"):
if key in value:
got = _prediction_records(value[key], root)
if got is not None: return got
return None
if isinstance(value, list) and all(isinstance(x, Mapping) for x in value):
return [dict(x) for x in value]
return None
def _find_prediction_sets(value: Any, root: Path, prefix: str = "$") -> dict[str, list[dict[str, Any]]]:
found: dict[str, list[dict[str, Any]]] = {}
if isinstance(value, Mapping):
for key, item in value.items():
name = f"{prefix}.{key}"
if "prediction" in str(key).lower() or str(key).lower() in {"base", "pso", "feature_pso", "test"}:
records = _prediction_records(item, root)
if records is not None: found[name] = records
found.update(_find_prediction_sets(item, root, name))
elif isinstance(value, list) and value and isinstance(value[0], Mapping):
records = _prediction_records(value, root)
if records is not None: found[prefix] = records
return found
def classification_metrics(records: Sequence[Mapping[str, Any]]) -> dict[str, Any]:
"""Recompute unrounded NLL and accuracy from per-image probabilities/targets."""
probs: list[list[float]] = []; targets: list[int] = []
for i, record in enumerate(records):
p = record.get("probabilities", record.get("probs", record.get("prob")))
target = record.get("target", record.get("label", record.get("class_id")))
if not isinstance(p, (list, tuple)) or not p or not isinstance(target, int) or isinstance(target, bool) or target < 0 or target >= len(p):
raise ValueError(f"invalid classification prediction at index {i}")
vals = [float(x) for x in p]
if not all(math.isfinite(x) and x >= 0.0 for x in vals) or not math.isclose(math.fsum(vals), 1.0, rel_tol=1e-6, abs_tol=1e-6):
raise ValueError(f"probabilities must be finite and sum to one at index {i}")
probs.append(vals); targets.append(target)
nll = math.fsum(
-math.log(max(p[t], 1e-300)) for p, t in zip(probs, targets)
) / len(probs)
predictions = [
max(range(len(p)), key=p.__getitem__) for p in probs
]
accuracy = sum(
prediction == target
for prediction, target in zip(predictions, targets)
) / len(probs)
brier = math.fsum(
math.fsum(
(probability - float(index == target)) ** 2
for index, probability in enumerate(p)
)
for p, target in zip(probs, targets)
) / len(probs)
confidences = [p[prediction] for p, prediction in zip(probs, predictions)]
ece = 0.0
for bin_index in range(15):
lower = bin_index / 15.0
upper = (bin_index + 1) / 15.0
members = [
index
for index, confidence in enumerate(confidences)
if lower <= confidence <= upper
if bin_index == 14 or confidence < upper
]
if members:
bin_accuracy = math.fsum(
predictions[index] == targets[index] for index in members
) / len(members)
bin_confidence = math.fsum(
confidences[index] for index in members
) / len(members)
ece += (
abs(bin_accuracy - bin_confidence)
* len(members)
/ len(probs)
)
return {
"n": len(probs),
"nll": nll,
"accuracy": accuracy,
"brier": brier,
"ece15": ece,
"probabilities": probs,
"targets": targets,
}
def _box(record: Mapping[str, Any]) -> tuple[float, float, float, float] | None:
value = record.get("box", record.get("bbox", record.get("xyxy")))
if not isinstance(value, (list, tuple)) or len(value) != 4 or not all(_finite(x) for x in value): return None
x1, y1, x2, y2 = map(float, value)
return (x1, y1, x2, y2) if x2 >= x1 and y2 >= y1 else None
def _iou(a: Sequence[float], b: Sequence[float]) -> float:
x1, y1 = max(a[0], b[0]), max(a[1], b[1]); x2, y2 = min(a[2], b[2]), min(a[3], b[3])
inter = max(0.0, x2 - x1) * max(0.0, y2 - y1)
area_a = max(0.0, a[2] - a[0]) * max(0.0, a[3] - a[1]); area_b = max(0.0, b[2] - b[0]) * max(0.0, b[3] - b[1])
return inter / (area_a + area_b - inter) if area_a + area_b - inter > 0 else 0.0
def _interp_ap_101(recall: Sequence[float], precision: Sequence[float]) -> float:
"""Ultralytics compute_ap: precision envelope and 101-point trapezoid."""
mrec = [0.0, *map(float, recall), 1.0]
mpre = [1.0, *map(float, precision), 0.0]
for i in range(len(mpre) - 2, -1, -1): mpre[i] = max(mpre[i], mpre[i + 1])
values: list[float] = []
for k in range(101):
x = k / 100.0; j = 0
while j + 1 < len(mrec) and mrec[j + 1] <= x: j += 1
if j + 1 >= len(mrec): values.append(mpre[-1]); continue
span = mrec[j + 1] - mrec[j]
values.append(mpre[j] if span <= 0 else mpre[j] + (mpre[j + 1] - mpre[j]) * (x - mrec[j]) / span)
return sum((values[i] + values[i + 1]) * 0.5 / 100.0 for i in range(100))
def detection_metrics(records: Sequence[Mapping[str, Any]], class_count: int | None = None) -> dict[str, Any]:
"""Recompute Ultralytics-style whole-dataset AP at IoU .50:.95.
Matching is performed independently per image. Candidate matches are sorted
by IoU and deduplicated by prediction and ground truth, as in
DetectionValidator.process_batch; AP then uses the pinned 101-point
interpolated trapezoid.
"""
thresholds = [0.50 + 0.05 * i for i in range(10)]
parsed: list[tuple[list[dict[str, Any]], list[dict[str, Any]]]] = []; max_class = -1
for i, image in enumerate(records):
predictions = image.get("predictions", image.get("detections", image.get("pred", [])))
truth = image.get("ground_truth", image.get("targets", image.get("gt", image.get("labels", []))))
if not isinstance(predictions, list) or not isinstance(truth, list): raise ValueError(f"invalid detection image record {i}")
pp: list[dict[str, Any]] = []; gg: list[dict[str, Any]] = []
for item in predictions:
if not isinstance(item, Mapping) or _box(item) is None: raise ValueError(f"invalid detection prediction {i}")
cls = item.get("class_id", item.get("class", item.get("cls"))); score = item.get("score", item.get("confidence", item.get("conf")))
if not isinstance(cls, int) or isinstance(cls, bool) or not _finite(score): raise ValueError(f"invalid detection prediction fields {i}")
pp.append({"box": _box(item), "class_id": cls, "score": float(score)}); max_class = max(max_class, cls)
for item in truth:
if not isinstance(item, Mapping) or _box(item) is None: raise ValueError(f"invalid ground truth {i}")
cls = item.get("class_id", item.get("class", item.get("cls")))
if not isinstance(cls, int) or isinstance(cls, bool): raise ValueError(f"invalid ground truth class {i}")
gg.append({"box": _box(item), "class_id": cls}); max_class = max(max_class, cls)
parsed.append((pp, gg))
present = sorted({g["class_id"] for _, gt in parsed for g in gt})
classes = present if class_count is None else [c for c in range(class_count) if c in present]
if not classes: classes = list(range(class_count or (max_class + 1))) or [0]
aps: dict[str, list[float]] = {}; precision50: list[float] = []; recall50: list[float] = []
for cls in classes:
gt_count = sum(sum(x["class_id"] == cls for x in gt) for _, gt in parsed); class_aps: list[float] = []
for threshold in thresholds:
true_by_image: list[list[bool]] = []
for preds, gt in parsed:
candidates = []
for pi, pred in enumerate(preds):
if pred["class_id"] != cls: continue
for gi, target in enumerate(gt):
if target["class_id"] == cls:
overlap = _iou(pred["box"], target["box"])
if overlap >= threshold: candidates.append((overlap, pi, gi))
candidates.sort(key=lambda x: -x[0]); used_pred: set[int] = set(); used_gt: set[int] = set(); matched: set[int] = set()
for overlap, pi, gi in candidates:
if pi not in used_pred and gi not in used_gt: used_pred.add(pi); used_gt.add(gi); matched.add(pi)
true_by_image.append([i in matched for i in range(len(preds))])
ranked = sorted(((pred["score"], hit) for (preds, _), hits in zip(parsed, true_by_image) for pred, hit in zip(preds, hits) if pred["class_id"] == cls), key=lambda x: -x[0])
tp=[]; fp=[]; ctp=cfp=0
for _, hit in ranked:
ctp += int(hit); cfp += int(not hit); tp.append(ctp); fp.append(cfp)
if gt_count == 0 or not ranked:
# Ultralytics ap_per_class skips classes with no predictions; AP is zero.
class_aps.append(0.0); continue
recalls = [x / gt_count for x in tp]; precisions = [x / max(x + y, 1) for x, y in zip(tp, fp)]
class_aps.append(_interp_ap_101(recalls, precisions))
if threshold == 0.5:
precision50.append(precisions[-1] if precisions else 0.0); recall50.append(recalls[-1] if recalls else 0.0)
aps[str(cls)] = class_aps
map50 = math.fsum(v[0] for v in aps.values()) / len(aps); map5095 = math.fsum(x for values in aps.values() for x in values) / (len(aps) * 10)
return {"per_class_ap": aps, "n": len(records), "map50": map50, "map50_95": map5095,
"precision": math.fsum(precision50) / len(precision50) if precision50 else 0.0,
"recall": math.fsum(recall50) / len(recall50) if recall50 else 0.0,
"ground_truth": sum(len(gt) for _, gt in parsed), "predictions": sum(len(preds) for preds, _ in parsed)}
def _metric_from_record(record: Any, family: str) -> dict[str, float] | None:
if not isinstance(record, Mapping): return None
keys = ("nll", "loss") if family == "classification" else ("map50_95", "map50-95", "mAP50-95", "map5095")
primary = _number(record, *keys); accuracy = _number(record, "accuracy", "acc")
map50 = _number(record, "map50", "mAP50")
if family == "classification" and primary is not None and accuracy is not None: return {"nll": primary, "accuracy": accuracy}
if family == "detection" and primary is not None: return {"map50_95": primary, **({"map50": map50} if map50 is not None else {})}
return None
def _quantile(values: Sequence[float], q: float) -> float:
ordered = sorted(float(x) for x in values)
if not ordered: raise ValueError("cannot quantile an empty sequence")
position = (len(ordered) - 1) * q; lower = int(math.floor(position)); upper = min(lower + 1, len(ordered) - 1)
return ordered[lower] + (ordered[upper] - ordered[lower]) * (position - lower)
def _record_identity(record: Mapping[str, Any], index: int) -> Any:
for key in ("image_id", "id", "key", "filename", "path", "index"):
if key in record: return (key, str(record[key]))
return ("position", index)
def _record_ground_truth(record: Mapping[str, Any]) -> Any:
return record.get("ground_truth", record.get("targets", record.get("gt", record.get("labels", []))))
def _bootstrap_alignment(pairs: Sequence[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]], family: str) -> tuple[bool, str]:
if not pairs or any(len(a) != len(b) or not a for a, b in pairs): return False, "incomplete or length-mismatched paired records"
reference_ids = [_record_identity(x, i) for i, x in enumerate(pairs[0][0])]
reference_gt = [_record_ground_truth(x) for x in pairs[0][0]]
for pair_index, (base, pso) in enumerate(pairs):
if [_record_identity(x, i) for i, x in enumerate(base)] != reference_ids or [_record_identity(x, i) for i, x in enumerate(pso)] != reference_ids:
return False, f"pair {pair_index} image IDs/order differ"
if family == "classification":
base_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in base]
pso_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in pso]
ref_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in pairs[0][0]]
if base_targets != ref_targets or pso_targets != ref_targets: return False, f"pair {pair_index} targets differ"
elif [_canonical(_record_ground_truth(x)) for x in base] != [_canonical(x) for x in reference_gt] or [_canonical(_record_ground_truth(x)) for x in pso] != [_canonical(x) for x in reference_gt]:
return False, f"pair {pair_index} ground truth differs"
return True, "aligned"
def _bootstrap_from_records(pairs: Sequence[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]], family: str) -> dict[str, Any]:
aligned, reason = _bootstrap_alignment(pairs, family)
if not aligned: return {"available": False, "seed": BOOTSTRAP_SEED, "resamples": BOOTSTRAP_RESAMPLES, "reason": reason}
rng = random.Random(BOOTSTRAP_SEED); stats: list[float] = []
if family == "classification":
parsed = [(classification_metrics(a), classification_metrics(b)) for a, b in pairs]
by_class: dict[int, list[int]] = {}
for i, target in enumerate(parsed[0][0]["targets"]): by_class.setdefault(target, []).append(i)
for _ in range(BOOTSTRAP_RESAMPLES):
# One class-stratified draw is shared by every paired base/PSO model.
indices = [rng.choice(class_indices) for class_indices in by_class.values() for _ in class_indices]
deltas = []
for base, pso in parsed:
b_nll = math.fsum(-math.log(max(base["probabilities"][i][base["targets"][i]], 1e-300)) for i in indices) / len(indices)
p_nll = math.fsum(-math.log(max(pso["probabilities"][i][pso["targets"][i]], 1e-300)) for i in indices) / len(indices)
deltas.append((b_nll - p_nll) / b_nll if b_nll else 0.0)
stats.append(math.fsum(deltas) / len(deltas))
else:
for _ in range(BOOTSTRAP_RESAMPLES):
# One whole-image draw is shared by every paired base/PSO model.
indices = [rng.randrange(len(pairs[0][0])) for _ in pairs[0][0]]
deltas = []
for base, pso in pairs:
b = detection_metrics([base[i] for i in indices])["map50_95"]
p = detection_metrics([pso[i] for i in indices])["map50_95"]
deltas.append(p - b)
stats.append(math.fsum(deltas) / len(deltas))
lo = _quantile(stats, BOOTSTRAP_ALPHA); hi = _quantile(stats, 1.0 - BOOTSTRAP_ALPHA)
return {"available": True, "seed": BOOTSTRAP_SEED, "resamples": BOOTSTRAP_RESAMPLES, "alpha": BOOTSTRAP_ALPHA, "lower": lo, "upper": hi, "statistic": math.fsum(stats) / len(stats), "excludes_zero": lo > 0 or hi < 0}
def _prediction_pairs(result: Mapping[str, Any], root: Path, family: str) -> list[tuple[int, list[dict[str, Any]], list[dict[str, Any]]]]:
"""Find one test base/selected pair per frozen base seed by explicit names."""
sets = _find_prediction_sets(result.get("confirmation", {}), root)
out: list[tuple[int, list[dict[str, Any]], list[dict[str, Any]]]] = []
for seed in BASE_SEEDS:
candidates = [(name, records) for name, records in sets.items() if str(seed) in name]
base = next((records for name, records in candidates if any(x in name.lower() for x in ("base", "frozen"))), None)
pso = next((records for name, records in candidates if any(x in name.lower() for x in ("feature_pso", "selected", "pso")) and "ensemble" not in name.lower()), None)
if base is not None and pso is not None: out.append((seed, base, pso))
return out
def _audit_series(value: Any) -> list[dict[str, Any]]:
found: list[dict[str, Any]] = []
if isinstance(value, Mapping):
for item in value.values(): found.extend(_audit_series(item))
elif isinstance(value, list) and value and all(isinstance(item, Mapping) for item in value):
if any("epoch" in item or "step" in item for item in value) and any(_number(item, "loss", "audit_loss") is not None for item in value):
found.append({"records": value})
else:
for item in value: found.extend(_audit_series(item))
return found
def _plateau_flags(result: Mapping[str, Any], workload: str, family: str, issues: dict[str, list[str]]) -> dict[str, Any]:
baselines = result.get("baselines", {})
if not isinstance(baselines, Mapping): return {"available": False, "passed": False}
per_seed: dict[str, bool] = {}; details: dict[str, Any] = {}
for seed in BASE_SEEDS:
entry = baselines.get(str(seed), baselines.get(seed))
series = _audit_series(entry)
records = series[0]["records"] if series else []
records = records[-11:] if len(records) >= 11 else []
losses = [_number(x, "loss", "audit_loss") for x in records]
metric_keys = ("accuracy", "primary_metric", "selection_accuracy") if family == "classification" else ("map50_95", "mAP50-95", "primary_metric", "selection_metric")
metrics = [_number(x, *metric_keys) for x in records]
loss_ok = len(losses) == 11 and all(x is not None for x in losses)
metric_ok = len(metrics) == 11 and all(x is not None for x in metrics)
if loss_ok:
mean = math.fsum(float(x) for x in losses) / len(losses)
loss_ok = (max(losses) - min(losses)) / max(abs(mean), 1e-12) <= 0.01
if metric_ok: metric_ok = max(metrics) - min(metrics) <= 0.005
passed = bool(loss_ok and metric_ok); per_seed[str(seed)] = passed
details[str(seed)] = {"loss_ok": bool(loss_ok), "metric_ok": bool(metric_ok), "observations": len(records)}
declared = _number(entry, "baseline_plateau") if isinstance(entry, Mapping) else None
if isinstance(entry, Mapping) and "baseline_plateau" in entry and bool(entry["baseline_plateau"]) != passed:
_issue(issues, "plateau", f"{workload}: baseline seed {seed} inflated/incorrect plateau flag")
return {"available": bool(per_seed), "per_seed": per_seed, "details": details, "passed": bool(per_seed) and all(per_seed.values())}
def _workload_gates(result: Mapping[str, Any], workload: str, family: str, root: Path, issues: dict[str, list[str]]) -> dict[str, Any]:
pairs = _prediction_pairs(result, root, family)
per_seed: dict[str, Any] = {}
pair_records: list[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]] = []
for seed, base_records, pso_records in pairs:
try:
base = classification_metrics(base_records) if family == "classification" else detection_metrics(base_records, 20)
pso = classification_metrics(pso_records) if family == "classification" else detection_metrics(pso_records, 20)
except (ValueError, ZeroDivisionError) as exc:
_issue(issues, "metrics", f"{workload}: test pair {seed} cannot be recomputed: {exc}"); continue
pair_records.append((base_records, pso_records))
if family == "classification": per_seed[str(seed)] = {"base": {"nll": base["nll"], "accuracy": base["accuracy"]}, "pso": {"nll": pso["nll"], "accuracy": pso["accuracy"]}, "relative_nll_reduction": (base["nll"] - pso["nll"]) / base["nll"], "accuracy_delta": pso["accuracy"] - base["accuracy"]}
else: per_seed[str(seed)] = {"base": {"map50_95": base["map50_95"], "map50": base["map50"]}, "pso": {"map50_95": pso["map50_95"], "map50": pso["map50"]}, "map50_95_delta": pso["map50_95"] - base["map50_95"], "map50_delta": pso["map50"] - base["map50"]}
ci = _bootstrap_from_records(pair_records, family)
# Confirmation predictions and the paired CI are required integrity evidence.
# A numerically negative CI is a valid scientific result; an unavailable CI
# means the frozen/confirmed artifact set is incomplete or tampered.
if len(pairs) != len(BASE_SEEDS):
_issue(issues, "metrics", f"{workload}: required confirmation pairs are incomplete ({len(pairs)}/{len(BASE_SEEDS)})")
if not ci.get("available", False):
_issue(issues, "metrics", f"{workload}: required paired bootstrap unavailable: {ci.get('reason', 'missing prediction evidence')}")
if family == "classification" and per_seed:
reductions = [x["relative_nll_reduction"] for x in per_seed.values()]; acc_deltas = [x["accuracy_delta"] for x in per_seed.values()]
gate = len(reductions) == 3 and math.fsum(reductions) / 3 >= 0.01 and math.fsum(acc_deltas) / 3 >= -0.002 and min(acc_deltas) >= -0.005 and sum(x > 0 for x in reductions) >= 2 and ci.get("excludes_zero", False)
elif family == "detection" and per_seed:
deltas = [x["map50_95_delta"] for x in per_seed.values()]
gate = len(deltas) == 3 and math.fsum(deltas) / 3 >= 0.005 and min(deltas) >= -0.005 and sum(x > 0 for x in deltas) >= 2 and ci.get("excludes_zero", False)
else: gate = False
if len(pairs) != len(BASE_SEEDS): _issue(issues, "bootstrap", f"{workload}: missing complete official-test base/feature_pso prediction pairs ({len(pairs)}/3)")
return {"available": bool(per_seed), "per_seed": per_seed, "bootstrap": ci, "generalization_pass": bool(gate)}
def _verify_hashes(result: Mapping[str, Any], root: Path, issues: dict[str, list[str]], workload: str) -> None:
declared = result.get("artifact_hashes")
if not isinstance(declared, Mapping):
_issue(issues, "schema", f"{workload}: missing artifact_hashes")
return
for name, expected in declared.items():
if not isinstance(name, str) or not isinstance(expected, str):
_issue(issues, "seal", f"{workload}: malformed artifact hash entry")
continue
path = (root / name).resolve()
if root.resolve() not in path.parents or not path.is_file():
_issue(issues, "seal", f"{workload}: missing/escaping artifact {name}")
elif _sha256(path) != expected:
_issue(issues, "seal", f"{workload}: artifact hash drift {name}")
def _compare_development_snapshot(development: Any, current: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> None:
"""Ensure confirmation did not alter any sealed development decision/state."""
if not isinstance(development, Mapping):
_issue(issues, "schema", f"{workload}: development_result.json must contain an object")
return
development_leakage = development.get("leakage_counters")
if not isinstance(development_leakage, Mapping):
_issue(issues, "leakage", f"{workload}: development snapshot lacks leakage_counters")
else:
loaded = development_leakage.get("official_test_data_loaded_before_freeze", development_leakage.get("test_data_loaded_before_freeze"))
evaluated = development_leakage.get("official_test_evaluations_before_freeze", development_leakage.get("test_evaluations_before_freeze", development_leakage.get("official_test_forward_passes_before_freeze")))
if loaded is not False or evaluated != 0:
_issue(issues, "leakage", f"{workload}: development snapshot records pre-freeze official-test exposure")
for key in ("official_test_construction", "official_test_dataset_construction", "official_test_forward_passes", "official_test_evaluations"):
if key in development_leakage and development_leakage[key] != 0:
_issue(issues, "leakage", f"{workload}: development snapshot {key} must be zero")
development_confirmation = development.get("confirmation")
if development_confirmation not in ({}, None):
_issue(issues, "leakage", f"{workload}: development snapshot confirmation must be empty")
fields = ("config", "manifests", "provenance", "baselines", "arms", "ensemble", "development_selection", "integrity", "resource_ledger", "artifact_hashes")
for field in fields:
if field not in development:
_issue(issues, "seal", f"{workload}: development_result.json missing sealed field {field}")
continue
if field not in current:
_issue(issues, "seal", f"{workload}: current result missing sealed field {field}")
continue
if field == "artifact_hashes":
# Confirmation may add test prediction hashes. Every development hash
# must nevertheless remain present and byte-identical.
old_hashes = development[field]; new_hashes = current[field]
if not isinstance(old_hashes, Mapping) or not isinstance(new_hashes, Mapping):
_issue(issues, "seal", f"{workload}: artifact_hashes changed shape across confirmation")
else:
for name, value in old_hashes.items():
if new_hashes.get(name) != value: _issue(issues, "seal", f"{workload}: sealed artifact hash drift for {name}")
elif field == "integrity":
old_integrity = development[field]; new_integrity = current[field]
if not isinstance(old_integrity, Mapping) or not isinstance(new_integrity, Mapping):
if old_integrity != new_integrity: _issue(issues, "seal", f"{workload}: pre-confirmation integrity changed")
else:
for name, value in old_integrity.items():
if new_integrity.get(name) != value: _issue(issues, "seal", f"{workload}: pre-confirmation integrity field changed: {name}")
elif _canonical(development[field]) != _canonical(current[field]):
_issue(issues, "seal", f"{workload}: sealed pre-confirmation field changed: {field}")
def _evaluate_workload(result: Any, root: Path, workload: str, issues: dict[str, list[str]], development: Any = None) -> dict[str, Any]:
if not isinstance(result, Mapping):
_issue(issues, "schema", f"{workload}: result must be an object"); return {"workload_id": workload, "valid": False}
if result.get("workload_id") != workload: _issue(issues, "matrix", f"{workload}: workload_id mismatch")
if development is not None: _compare_development_snapshot(development, result, workload, issues)
family = "detection" if workload == DETECTION_WORKLOAD else "classification"
if result.get("family") != family: _issue(issues, "matrix", f"{workload}: family must be {family}")
for key in ("manifests", "provenance", "baselines", "arms", "ensemble", "development_selection", "confirmation", "integrity", "leakage_counters", "resource_ledger", "artifact_hashes"):
if key not in result: _issue(issues, "schema", f"{workload}: missing top-level {key}")
_config_checks(result.get("config"), issues, workload); _check_leakage(result, issues, workload); _verify_hashes(result, root, issues, workload)
accounting = _verify_matrix(result, workload, issues); _verify_selection(result, workload, issues); _require_confirmation_methods(result, workload, family, root, issues)
if result.get("integrity", {}).get("confirmed") is False if isinstance(result.get("integrity"), Mapping) else False:
_issue(issues, "seal", f"{workload}: integrity declares confirmation failure")
prediction_sets = _find_prediction_sets(result.get("confirmation", {}), root)
recomputed: dict[str, Any] = {}
for name, records in prediction_sets.items():
try: recomputed[name] = classification_metrics(records) if family == "classification" else detection_metrics(records, 20)
except (ValueError, ZeroDivisionError) as exc: _issue(issues, "metrics", f"{workload}: invalid stored predictions at {name}: {exc}")
# Check every explicitly stored metric that has a corresponding recomputation.
for name, metric in recomputed.items():
if "pso" in name.lower() and isinstance(metric, Mapping):
stored = _metric_from_record(result.get("confirmation"), family)
if stored and family == "classification" and not (_same(stored.get("nll"), metric.get("nll"), 1e-7) and _same(stored.get("accuracy"), metric.get("accuracy"), 1e-7)):
_issue(issues, "metrics", f"{workload}: stored classification metric disagrees with probabilities at {name}")
plateau = _plateau_flags(result, workload, family, issues)
gates = _workload_gates(result, workload, family, root, issues)
objective = _number(result.get("development_selection"), "objective_improvement", "relative_objective_improvement")
selection_metric = _number(result.get("development_selection"), "selection_metric", "selection_nll", "selection_map50_95")
overfit = bool(objective is not None and objective >= 0.01 and selection_metric is not None and ((family == "classification" and selection_metric > 0.01) or (family == "detection" and selection_metric < -0.005)))
if overfit: _issue(issues, "overfit", f"{workload}: objective improvement conflicts with held-out/selection metric")
return {"workload_id": workload, "family": family, "valid": True, "accounting": accounting, "recomputed": recomputed, "plateau": plateau, "gates": gates, "overfit_signal": overfit}
def evaluate_run(run_root: str | os.PathLike[str]) -> dict[str, Any]:
"""Evaluate one frozen run, returning findings even when artifacts are malformed."""
root = Path(run_root); issues = {key: [] for key in ISSUE_CATEGORIES}; workloads: dict[str, Any] = {}
manifest: Any = None
try: manifest = _json(root / "frozen_manifest.json")
except (OSError, ValueError, json.JSONDecodeError) as exc: _issue(issues, "schema", f"cannot load frozen_manifest.json: {exc}")
if isinstance(manifest, Mapping):
for location in _walk_nonfinite(manifest): _issue(issues, "finite", f"non-finite value in frozen manifest at {location}")
_hash_manifest(root, manifest, issues); _config_checks(manifest.get("config"), issues)
frozen_config = manifest.get("config") if isinstance(manifest.get("config"), Mapping) else {}
if sorted(frozen_config.get("workload_ids", ())) != sorted(WORKLOADS): _issue(issues, "matrix", "frozen manifest does not seal all three workloads")
for workload in WORKLOADS:
path = root / "workloads" / workload / "result.json"
development_path = root / "workloads" / workload / "development_result.json"
if isinstance(manifest, Mapping):
manifest_artifacts = manifest.get("artifacts", {})
expected_development = f"workloads/{workload}/development_result.json"
if not isinstance(manifest_artifacts, Mapping) or expected_development not in manifest_artifacts:
_issue(issues, "seal", f"{workload}: frozen manifest must seal {expected_development}")
try: result = _json(path)
except (OSError, ValueError, json.JSONDecodeError) as exc:
_issue(issues, "schema", f"{workload}: cannot load result.json: {exc}"); continue
try: development = _json(development_path)
except (OSError, ValueError, json.JSONDecodeError) as exc:
_issue(issues, "seal", f"{workload}: cannot load development_result.json: {exc}"); development = None
for location in _walk_nonfinite(result): _issue(issues, "finite", f"{workload}: non-finite value at {location}")
if development is not None:
for location in _walk_nonfinite(development): _issue(issues, "finite", f"{workload}: non-finite development value at {location}")
workloads[workload] = _evaluate_workload(result, root, workload, issues, development)
# Cross-workload exact accounting is intentionally independent of stored totals.
totals = {key: sum(int(w.get("accounting", {}).get(key, 0)) for w in workloads.values()) for key in ("primary_queries", "primary_random_queries", "ensemble_queries", "primary_samples", "primary_random_samples", "ensemble_samples")}
totals["pso_queries"] = totals["primary_queries"] + totals["ensemble_queries"]
totals["candidate_samples"] = totals["primary_samples"] + totals["ensemble_samples"]
if totals["pso_queries"] != TOTAL_PSO_QUERIES: _issue(issues, "accounting", f"total PSO queries must be {TOTAL_PSO_QUERIES}, got {totals['pso_queries']}")
if totals["candidate_samples"] != TOTAL_CANDIDATE_SAMPLES: _issue(issues, "accounting", f"total candidate-sample evaluations must be {TOTAL_CANDIDATE_SAMPLES}, got {totals['candidate_samples']}")
# A success flag is never consumed; it is checked against independently observed integrity.
integrity_ok = not any(issues[key] for key in ("schema", "provenance", "matrix", "accounting", "seal", "selection", "finite", "leakage", "metrics"))
payload = {"evaluator_version": EVALUATOR_VERSION, "protocol_version": PROTOCOL_VERSION, "run_root": str(root), "pass": integrity_ok, "integrity_pass": integrity_ok, "workloads": workloads, "accounting": {**totals, "expected_pso_queries": TOTAL_PSO_QUERIES, "expected_candidate_samples": TOTAL_CANDIDATE_SAMPLES}, "issues": issues, "issue_counts": {key: len(value) for key, value in issues.items()}}
return payload
def build_cli_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Evaluate frozen post-training model-convergence artifacts")
parser.add_argument("--run-root", type=Path, required=True)
parser.add_argument("--output", type=Path, default=None)
return parser
def main(argv: Sequence[str] | None = None) -> int:
args = build_cli_parser().parse_args(argv); payload = evaluate_run(args.run_root)
destination = args.output or args.run_root / "evaluation.json"
try: _atomic_json(destination, payload)
except OSError as exc:
print(f"evaluator output failed: {exc}", file=sys.stderr); return 2
print(json.dumps(payload, sort_keys=True, separators=(",", ":")))
return 0 if payload["pass"] else 1
__all__ = [
"BASE_SEEDS", "BOOTSTRAP_ALPHA", "BOOTSTRAP_RESAMPLES", "BOOTSTRAP_SEED", "CLASSIFICATION_WORKLOADS",
"DETECTION_WORKLOAD", "EVALUATOR_VERSION", "ENSEMBLE_QUERIES", "TOTAL_CANDIDATE_SAMPLES", "TOTAL_PSO_QUERIES",
"WORKLOADS", "classification_metrics", "detection_metrics", "evaluate_run", "main", "build_cli_parser",
]
if __name__ == "__main__":
raise SystemExit(main())
+95 -72
View File
@@ -1,87 +1,110 @@
# %% import argparse
import json import torch
import os import torch.nn as nn
import sys
import numpy as np from pso import Optimizer
import tensorflow as tf from cli import add_pso_args, build_optimizer_kwargs
from keras.datasets import fashion_mnist
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
from keras.models import Sequential
from pso import optimizer
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
def get_data(): def get_data(seed: int = 42):
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data() from sklearn.decomposition import PCA
from torchvision.datasets import FashionMNIST
x_train, x_test = x_train / 255.0, x_test / 255.0 train_dataset = FashionMNIST(root="./data", train=True, download=True)
x_train = x_train.reshape((60000, 28, 28, 1)) test_dataset = FashionMNIST(root="./data", train=False, download=True)
x_test = x_test.reshape((10000, 28, 28, 1))
y_train, y_test = tf.one_hot(y_train, 10), tf.one_hot(y_test, 10) x_train_raw = (train_dataset.data[:3000].float() / 255.0).reshape(3000, -1).numpy()
y_train = train_dataset.targets[:3000].long()
x_train, x_test = tf.convert_to_tensor(x_train), tf.convert_to_tensor(x_test) x_test_raw = (test_dataset.data[:1000].float() / 255.0).reshape(1000, -1).numpy()
y_train, y_test = tf.convert_to_tensor(y_train), tf.convert_to_tensor(y_test) y_test = test_dataset.targets[:1000].long()
print(f"x_train : {x_train[0].shape} | y_train : {y_train[0].shape}") pca = PCA(n_components=32, whiten=True, random_state=seed)
print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}") x_train_pca = pca.fit_transform(x_train_raw)
x_test_pca = pca.transform(x_test_raw)
x_train = torch.tensor(x_train_pca, dtype=torch.float32)
x_test = torch.tensor(x_test_pca, dtype=torch.float32)
print(f"x_train : {x_train.shape} | y_train : {y_train.shape}")
print(f"x_test : {x_test.shape} | y_test : {y_test.shape}")
return x_train, y_train, x_test, y_test return x_train, y_train, x_test, y_test
def make_model(): def make_model(seed: int = 42):
model = Sequential() torch.manual_seed(seed)
model.add( return nn.Linear(32, 10)
Conv2D(32, kernel_size=(5, 5), activation="relu", input_shape=(28, 28, 1))
def main():
parser = argparse.ArgumentParser(description="PSO Fashion-MNIST Benchmark Script")
add_pso_args(
parser,
defaults={
"method": "original",
"initialization": "model_noise",
"evaluation": "fixed_subset",
"convergence": "particle_reset",
"refinement": "adam",
"n_particles": 30,
"c0": None,
"c1": None,
"w_min": None,
"w_max": None,
"negative_swarm": 0.0,
"mutation_swarm": 0.05,
"particle_min": -3.0,
"particle_max": 3.0,
"velocity_limit_ratio": 0.1,
"boundary_strategy": "reflect",
"seed": 42,
"epochs": 80,
"batch_size": 1000,
"fitness_size": 2000,
"renewal": "loss",
"output_dir": "output/fashion_mnist",
"checkpoint_interval": 25,
"refinement_epochs": 10,
"refinement_lr": 0.001,
},
) )
model.add(MaxPooling2D(pool_size=(2, 2))) args = parser.parse_args()
model.add(Conv2D(64, kernel_size=(3, 3), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.25))
model.add(Dense(256, activation="relu"))
model.add(Dense(128, activation="relu"))
model.add(Dense(10, activation="softmax"))
return model model = make_model(seed=args.seed)
x_train, y_train, x_test, y_test = get_data(seed=args.seed)
fitness_size = args.fitness_size if args.evaluation == "fixed_subset" else None
refinement_epochs = args.refinement_epochs if args.refinement == "adam" else 0
kwargs = build_optimizer_kwargs(
args,
model=model,
loss=nn.CrossEntropyLoss(),
task="multiclass",
inertia_profile={"c0": 0.7, "c1": 0.5, "w_min": 0.1, "w_max": 0.8},
)
pso_fashion = Optimizer(**kwargs)
print(f"Optimizer device: {pso_fashion.device}")
best_score = pso_fashion.fit(
x_train,
y_train,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=fitness_size,
renewal=args.renewal,
validation_data=(x_test, y_test),
output_dir=args.output_dir,
checkpoint_interval=25,
save_info=True,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
print(f"Done! Best score: {best_score}")
# %% if __name__ == "__main__":
model = make_model() main()
x_train, y_train, x_test, y_test = get_data()
pso_mnist = optimizer(
model,
loss="categorical_crossentropy",
n_particles=200,
c0=0.7,
c1=0.5,
w_min=0.1,
w_max=0.8,
negative_swarm=0.0,
mutation_swarm=0.05,
convergence_reset=True,
convergence_reset_patience=10,
convergence_reset_monitor="loss",
)
best_score = pso_mnist.fit(
x_train,
y_train,
epochs=1000,
save_info=True,
log=2,
log_name="fashion_mnist",
renewal="loss",
check_point=25,
batch_size=5000,
)
print("Done!")
sys.exit(0)
-115
View File
@@ -1,115 +0,0 @@
from keras.models import Sequential
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
from keras.datasets import mnist, fashion_mnist
from keras.utils import to_categorical
# from tensorflow.data.Dataset import from_tensor_slices
import tensorflow as tf
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
tf.config.experimental.set_memory_growth(gpus[0], True)
except Exception as e:
print(e)
finally:
del gpus
def get_data():
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
print(f"y_train : {y_train[0]} | y_test : {y_test[0]}")
x_train, x_test = x_train / 255.0, x_test / 255.0
x_train = x_train.reshape((60000, 28, 28, 1))
x_test = x_test.reshape((10000, 28, 28, 1))
print(f"x_train : {x_train[0].shape} | y_train : {y_train[0].shape}")
print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}")
return x_train, y_train, x_test, y_test
class _batch_generator:
def __init__(self, x, y, batch_size: int = 32):
self.batch_size = batch_size
self.index = 0
self.x = x
self.y = y
self.setBatchSize(batch_size)
def next(self):
self.index += 1
if self.index >= self.max_index:
self.index = 0
return self.dataset[self.index][0], self.dataset[self.index][1]
def getMaxIndex(self):
return self.max_index
def getIndex(self):
return self.index
def setIndex(self, index):
self.index = index
def getBatchSize(self):
return self.batch_size
def setBatchSize(self, batch_size):
self.batch_size = batch_size
self.dataset = list(
tf.data.Dataset.from_tensor_slices(
(self.x, self.y)).batch(batch_size)
)
self.max_index = len(self.dataset)
def getDataset(self):
return self.dataset
def make_model():
model = Sequential()
model.add(
Conv2D(32, kernel_size=(5, 5), activation="sigmoid",
input_shape=(28, 28, 1))
)
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, kernel_size=(3, 3), activation="sigmoid"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.25))
model.add(Dense(128, activation="sigmoid"))
model.add(Dense(10, activation="softmax"))
return model
model = make_model()
x_train, y_train, x_test, y_test = get_data()
print(x_train.shape)
y_train = tf.one_hot(y_train, 10)
y_test = tf.one_hot(y_test, 10)
dataset = _batch_generator(x_train, y_train, 32)
model.compile(optimizer="adam", loss="mse", metrics=["accuracy"])
count = 0
while count < 100:
x_batch, y_batch = dataset.next()
count += 1
print("Training model...")
model.fit(x_batch, y_batch, epochs=1, batch_size=1, verbose=1)
print(count)
print(f"Max index : {dataset.getMaxIndex()}")
print("Evaluating model...")
model.evaluate(x_test, y_test, verbose=2)
weights = model.get_weights()
+129
View File
@@ -0,0 +1,129 @@
"""Fashion-MNIST dataset gradient baseline (PyTorch standard backprop optimizer, non-PSO)."""
import copy
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
class FashionMNISTModel(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, kernel_size=5)
self.sig1 = nn.Sigmoid()
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
self.sig2 = nn.Sigmoid()
self.pool2 = nn.MaxPool2d(2, 2)
self.drop = nn.Dropout(0.25)
self.fc1 = nn.Linear(64 * 5 * 5, 128)
self.sig3 = nn.Sigmoid()
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.pool1(self.sig1(self.conv1(x)))
x = self.pool2(self.sig2(self.conv2(x)))
x = torch.flatten(x, 1)
x = self.drop(x)
x = self.sig3(self.fc1(x))
x = self.fc2(x)
return x
def get_data(download: bool = True):
from torchvision import datasets, transforms
transform = transforms.ToTensor()
train_dataset = datasets.FashionMNIST(
root="./data", train=True, transform=transform, download=download
)
test_dataset = datasets.FashionMNIST(
root="./data", train=False, transform=transform, download=download
)
return train_dataset, test_dataset
def get_device() -> torch.device:
if (
hasattr(torch.backends, "mps")
and torch.backends.mps.is_built()
and torch.backends.mps.is_available()
):
return torch.device("mps")
elif torch.cuda.is_available():
return torch.device("cuda")
return torch.device("cpu")
def main():
torch.manual_seed(42)
np.random.seed(42)
device = get_device()
print(f"Selected device: {device}")
train_dataset, test_dataset = get_data(download=True)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
model = FashionMNISTModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
best_val_loss = float("inf")
best_state = None
for epoch in range(10):
model.train()
for bx, by in train_loader:
bx, by = bx.to(device), by.to(device)
optimizer.zero_grad()
out = model(bx)
loss = criterion(out, by)
loss.backward()
optimizer.step()
model.eval()
val_loss = 0.0
total = 0
with torch.no_grad():
for bx, by in test_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
val_loss += loss.item() * bx.size(0)
total += bx.size(0)
val_loss /= total
if val_loss < best_val_loss:
best_val_loss = val_loss
best_state = copy.deepcopy(model.state_dict())
if best_state is not None:
model.load_state_dict(best_state)
model.eval()
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for bx, by in test_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
test_loss += loss.item() * bx.size(0)
preds = out.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
test_loss /= total
test_acc = correct / total
print(f"Final test loss: {test_loss:.4f}, accuracy: {test_acc:.4f}")
if __name__ == "__main__":
main()
+707
View File
@@ -0,0 +1,707 @@
"""
Full-MNIST 60k/10k Particle Swarm Optimization Trajectory Analysis
Evaluates the selected Adaptive Moment PSO configuration on full official MNIST (60,000 train / 10,000 test)
across 120 particles for 240 continuous epochs, scoring all 60,000 training examples for every particle
at every epoch.
Predeclared Diagnostic Criteria:
1. Post-80 Training Convergence: Mean training loss falls >= 1% from epoch 80 to epoch 240.
2. Epoch 240 Test Gain: Mean test accuracy at epoch 240 rises >= 1 percentage point vs epoch 80.
3. Late Plateau (200->240): Training loss improvement < 1% AND absolute test accuracy change < 0.5 percentage points.
"""
import argparse
import csv
import datetime
import json
import math
import sys
import tempfile
import time
from pathlib import Path
from typing import Any, Dict, List, Tuple
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
# Ensure test/ directory is in sys.path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from benchmark_suite import (
calc_stats,
extract_plugin_metadata,
compute_data_fingerprint,
compute_model_fingerprint,
get_hardware_provenance,
make_mnist_model,
resolve_execution_device,
save_json_atomic,
sync_device,
)
from pso import Optimizer, __version__ as pso_version
from reproduce_scaling import validate_and_load_baseline
FULL_MNIST_PROTOCOL_VERSION = "1.0.0"
def prepare_full_mnist_data() -> Tuple[
torch.Tensor,
torch.Tensor,
torch.Tensor,
torch.Tensor,
str,
Dict[str, Any],
]:
"""
Load official torchvision MNIST full train (60,000) and test (10,000).
Normalize pixels, flatten to 784, fit PCA(n_components=32, whiten=True, random_state=42)
on train only, transform test. Validate 60,000/10,000 sample counts and label range [0, 9].
"""
from torchvision.datasets import MNIST
from sklearn.decomposition import PCA
cache_dir = Path("result/cache")
cache_dir.mkdir(parents=True, exist_ok=True)
train_dataset = MNIST(root=str(cache_dir), train=True, download=True)
test_dataset = MNIST(root=str(cache_dir), train=False, download=True)
n_train = len(train_dataset.data)
n_test = len(test_dataset.data)
if n_train != 60000:
raise ValueError(f"Expected 60,000 training samples; got {n_train}")
if n_test != 10000:
raise ValueError(f"Expected 10,000 test samples; got {n_test}")
y_train_60000 = train_dataset.targets.long()
y_test_10000 = test_dataset.targets.long()
min_tr_lbl, max_tr_lbl = int(y_train_60000.min()), int(y_train_60000.max())
min_te_lbl, max_te_lbl = int(y_test_10000.min()), int(y_test_10000.max())
if min_tr_lbl != 0 or max_tr_lbl != 9:
raise ValueError(f"Train label range must be [0, 9]; got [{min_tr_lbl}, {max_tr_lbl}]")
if min_te_lbl != 0 or max_te_lbl != 9:
raise ValueError(f"Test label range must be [0, 9]; got [{min_te_lbl}, {max_te_lbl}]")
x_train_raw = (train_dataset.data.float() / 255.0).reshape(60000, -1).numpy()
x_test_raw = (test_dataset.data.float() / 255.0).reshape(10000, -1).numpy()
pca = PCA(n_components=32, whiten=True, random_state=42)
x_full_tr = torch.tensor(pca.fit_transform(x_train_raw), dtype=torch.float32)
x_full_test = torch.tensor(pca.transform(x_test_raw), dtype=torch.float32)
data_fp = compute_data_fingerprint(x_full_tr, x_full_test, y_train_60000, y_test_10000)
pca_provenance = {
"n_components": 32,
"whiten": True,
"random_state": 42,
"fit_scope": "official_train_split_60000_only",
"train_samples_fit": 60000,
"test_samples_transformed": 10000,
"explained_variance_ratio_sum": float(np.sum(pca.explained_variance_ratio_)),
}
return (
x_full_tr,
y_train_60000,
x_full_test,
y_test_10000,
data_fp,
pca_provenance,
)
def render_trajectory_plot(
checkpoint_stats: Dict[int, Dict[str, Any]],
subset_comparison: Dict[str, Any],
figure_path: Path,
):
"""
Render a readable two-panel mean +/- SD trajectory plot for training loss and test accuracy,
with optional dashed subset-study mean comparison and epoch 80 marker.
"""
figure_path.parent.mkdir(parents=True, exist_ok=True)
eps = sorted(list(checkpoint_stats.keys()))
tr_loss_mean = [checkpoint_stats[ep]["train_loss"]["mean"] for ep in eps]
tr_loss_std = [checkpoint_stats[ep]["train_loss"]["std"] for ep in eps]
te_acc_mean = [checkpoint_stats[ep]["test_acc"]["mean"] for ep in eps]
te_acc_std = [checkpoint_stats[ep]["test_acc"]["std"] for ep in eps]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
# Panel 1: Training Loss
ax1.plot(eps, tr_loss_mean, "o-", color="tab:blue", linewidth=2, label="Full MNIST Train Loss (Mean)")
ax1.fill_between(
eps,
np.array(tr_loss_mean) - np.array(tr_loss_std),
np.array(tr_loss_mean) + np.array(tr_loss_std),
color="tab:blue",
alpha=0.2,
label="±1 SD",
)
if "matching_epoch_deltas" in subset_comparison:
sub_deltas = subset_comparison["matching_epoch_deltas"]
sub_eps = sorted([ep for ep in eps if str(ep) in sub_deltas or ep in sub_deltas])
if sub_eps:
sub_tr_loss = [sub_deltas.get(str(ep), sub_deltas.get(ep, {}))["subset_study_train_loss_mean"] for ep in sub_eps]
ax1.plot(sub_eps, sub_tr_loss, "--", color="gray", alpha=0.8, label="Subset Study (2k sample) Train Loss")
if 80 in eps:
ax1.axvline(80, color="red", linestyle=":", label="Epoch 80 Marker")
ax1.set_ylabel("Training Loss")
ax1.set_title("Full MNIST (60,000 Train / 10,000 Test) PSO Trajectory (120 Particles, AM)")
ax1.grid(True, linestyle="--", alpha=0.5)
ax1.legend(loc="upper right")
# Panel 2: Test Accuracy
ax2.plot(eps, te_acc_mean, "s-", color="tab:green", linewidth=2, label="Full MNIST Test Accuracy (Mean)")
ax2.fill_between(
eps,
np.array(te_acc_mean) - np.array(te_acc_std),
np.array(te_acc_mean) + np.array(te_acc_std),
color="tab:green",
alpha=0.2,
label="±1 SD",
)
if "matching_epoch_deltas" in subset_comparison:
sub_deltas = subset_comparison["matching_epoch_deltas"]
sub_eps = sorted([ep for ep in eps if str(ep) in sub_deltas or ep in sub_deltas])
if sub_eps:
sub_te_acc = [sub_deltas.get(str(ep), sub_deltas.get(ep, {}))["subset_study_test_acc_mean"] for ep in sub_eps]
ax2.plot(sub_eps, sub_te_acc, "--", color="gray", alpha=0.8, label="Subset Study (2k sample) Test Acc")
if 80 in eps:
ax2.axvline(80, color="red", linestyle=":", label="Epoch 80 Marker")
ax2.set_xlabel("Epoch")
ax2.set_ylabel("Test Accuracy")
ax2.grid(True, linestyle="--", alpha=0.5)
ax2.legend(loc="lower right")
plt.tight_layout()
fig.savefig(figure_path, dpi=300)
plt.close(fig)
def run_full_mnist_study(
baseline_path: Path,
subset_study_path: Path,
output_json_path: Path,
output_csv_path: Path,
figure_path: Path,
device_str: str = "auto",
epochs: int = 240,
seeds: List[int] = None,
) -> bool:
if seeds is None:
seeds = [71, 72, 73, 74, 75]
if not isinstance(epochs, int) or isinstance(epochs, bool) or epochs <= 0:
raise ValueError("epochs must be a positive integer")
if epochs % 20 != 0:
raise ValueError("epochs must be a multiple of 20 so every final checkpoint exists")
if not seeds or len(seeds) != len(set(seeds)):
raise ValueError("seeds must be a non-empty list of unique integers")
if any(isinstance(seed, bool) or not isinstance(seed, int) or seed < 0 for seed in seeds):
raise ValueError("every seed must be a non-negative integer")
dev_input = None if device_str == "auto" else device_str
device = resolve_execution_device(dev_input)
hw_provenance = get_hardware_provenance(device)
# 1. Validate baseline JSON & load winner config
baseline_data, _baseline_records, winner_cfg, _expected_fp = validate_and_load_baseline(
baseline_path
)
if baseline_data.get("device") != device.type:
raise ValueError(
f"Baseline device mismatch: baseline requires {baseline_data.get('device')!r}, got {device.type!r}"
)
if baseline_data.get("pso_version") != pso_version:
raise ValueError(
f"PSO version mismatch: baseline requires {baseline_data.get('pso_version')!r}, got {pso_version!r}"
)
if baseline_data.get("torch_version") != torch.__version__:
raise ValueError(
f"Torch version mismatch: baseline requires {baseline_data.get('torch_version')!r}, got {torch.__version__!r}"
)
# 2. Prepare Full MNIST PCA Data (60k train / 10k test)
(
x_full_tr,
y_train_60000,
x_full_test,
y_test_10000,
data_fp,
pca_provenance,
) = prepare_full_mnist_data()
opt_kwargs = winner_cfg.to_optimizer_kwargs(quick=False)
opt_kwargs["evaluation"] = "full"
opt_kwargs.pop("fitness_size", None)
n_particles = 120
target_epochs = epochs
batch_size = 60000
checkpoint_interval = 20
ckpt_epochs = [ep for ep in range(checkpoint_interval, target_epochs + 1, checkpoint_interval)]
if not ckpt_epochs or ckpt_epochs[-1] != target_epochs:
if target_epochs not in ckpt_epochs:
ckpt_epochs.append(target_epochs)
ckpt_epochs = sorted(list(set(ckpt_epochs)))
runs: List[Dict[str, Any]] = []
flat_csv_rows: List[Dict[str, Any]] = []
plugin_meta: Dict[str, Any] | None = None
# 3. Seed Runs
for seed in sorted(seeds):
# Warmup Phase (2 epochs full eval)
warmup_model = make_mnist_model(seed=seed)
warmup_loss = nn.CrossEntropyLoss()
warmup_opt = Optimizer(
model=warmup_model,
loss=warmup_loss,
task="multiclass",
n_particles=n_particles,
seed=seed,
device=device,
**opt_kwargs,
)
warmup_opt.fit(
x_full_tr,
y_train_60000,
epochs=2,
batch_size=batch_size,
renewal="loss",
)
sync_device(device)
del warmup_opt, warmup_model, warmup_loss
# Timed Continuous Trajectory
model = make_mnist_model(seed=seed)
model_fp = compute_model_fingerprint(model)
loss_inst = nn.CrossEntropyLoss()
opt = Optimizer(
model=model,
loss=loss_inst,
task="multiclass",
n_particles=n_particles,
seed=seed,
device=device,
**opt_kwargs,
)
current_plugin_meta = extract_plugin_metadata(opt)
if plugin_meta is None:
plugin_meta = current_plugin_meta
elif current_plugin_meta != plugin_meta:
raise RuntimeError("Resolved plugin metadata changed across seeds")
with tempfile.TemporaryDirectory() as temp_dir_str:
output_dir = Path(temp_dir_str)
sync_device(device)
t0 = time.perf_counter()
train_loss_final, train_acc_final, train_mse_final = opt.fit(
x_full_tr,
y_train_60000,
epochs=target_epochs,
batch_size=batch_size,
renewal="loss",
output_dir=output_dir,
log_format="csv",
checkpoint_interval=checkpoint_interval,
)
sync_device(device)
t1 = time.perf_counter()
fit_time_sec = t1 - t0
if not (math.isfinite(train_loss_final) and math.isfinite(train_acc_final) and math.isfinite(train_mse_final)):
raise RuntimeError(f"Seed {seed} final metrics non-finite: loss={train_loss_final}, acc={train_acc_final}")
history_csv_path = output_dir / "history.csv"
epoch_history = []
if history_csv_path.exists():
with open(history_csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
epoch_history.append({
"epoch": int(row["epoch"]),
"loss": float(row["loss"]),
"accuracy": float(row["accuracy"]),
"mse": float(row["mse"]),
})
prev_best_loss = float("inf")
improvement_count = 0
last_improvement_epoch = 1
for row in epoch_history:
ep_num = row["epoch"]
l_val = row["loss"]
if l_val < prev_best_loss:
improvement_count += 1
last_improvement_epoch = ep_num
prev_best_loss = l_val
checkpoints: List[Dict[str, Any]] = []
ckpt_dir = output_dir / "checkpoints"
for ep in ckpt_epochs:
ckpt_path = ckpt_dir / f"epoch-{ep}.pt"
if not ckpt_path.exists():
raise FileNotFoundError(f"Missing checkpoint file: {ckpt_path}")
payload = torch.load(ckpt_path, map_location=device, weights_only=True)
ckpt_tr_loss, ckpt_tr_acc, ckpt_tr_mse = payload["score"]
if not (math.isfinite(ckpt_tr_loss) and math.isfinite(ckpt_tr_acc) and math.isfinite(ckpt_tr_mse)):
raise RuntimeError(f"Seed {seed} epoch {ep} score non-finite: {payload['score']}")
opt.eval_model.load_state_dict(payload["model_state_dict"])
opt._global_best_weights = opt.codec.encode(opt.eval_model)
test_loss, test_acc, test_mse = opt.evaluate(x_full_test, y_test_10000)
if not (math.isfinite(test_loss) and math.isfinite(test_acc) and math.isfinite(test_mse)):
raise RuntimeError(f"Seed {seed} epoch {ep} test metrics non-finite: loss={test_loss}, acc={test_acc}")
ckpt_record = {
"epoch": ep,
"train_loss": float(ckpt_tr_loss),
"train_acc": float(ckpt_tr_acc),
"train_mse": float(ckpt_tr_mse),
"test_loss": float(test_loss),
"test_acc": float(test_acc),
"test_mse": float(test_mse),
}
checkpoints.append(ckpt_record)
flat_csv_rows.append({
"seed": seed,
"epoch": ep,
"train_loss": float(ckpt_tr_loss),
"train_acc": float(ckpt_tr_acc),
"train_mse": float(ckpt_tr_mse),
"test_loss": float(test_loss),
"test_acc": float(test_acc),
"test_mse": float(test_mse),
"fit_time_sec": round(fit_time_sec, 4),
})
runs.append({
"seed": seed,
"model_fingerprint": model_fp,
"fit_time_sec": round(fit_time_sec, 4),
"improvement_count": improvement_count,
"last_improvement_epoch": last_improvement_epoch,
"checkpoints": checkpoints,
"completed": True,
"error": None,
"plugins": current_plugin_meta,
"epoch_history": epoch_history,
})
# 4. Aggregations & Statistics
checkpoint_stats: Dict[int, Dict[str, Any]] = {}
for ep in ckpt_epochs:
ep_train_losses = [next(c["train_loss"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_train_accs = [next(c["train_acc"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_train_mses = [next(c["train_mse"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_losses = [next(c["test_loss"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_accs = [next(c["test_acc"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
ep_test_mses = [next(c["test_mse"] for c in r["checkpoints"] if c["epoch"] == ep) for r in runs]
checkpoint_stats[ep] = {
"train_loss": calc_stats(ep_train_losses),
"train_acc": calc_stats(ep_train_accs),
"train_mse": calc_stats(ep_train_mses),
"test_loss": calc_stats(ep_test_losses),
"test_acc": calc_stats(ep_test_accs),
"test_mse": calc_stats(ep_test_mses),
}
# Endpoint paired deltas (80->240 and 200->240 if available)
paired_deltas: Dict[str, Any] = {}
paired_endpoint_deltas_by_seed: List[Dict[str, Any]] = []
has_80 = 80 in checkpoint_stats
has_200 = 200 in checkpoint_stats
has_240 = 240 in checkpoint_stats
if has_80 and has_240:
deltas_80_to_240_train_rel = []
deltas_80_to_240_test_acc = []
for r in runs:
c80 = next(c for c in r["checkpoints"] if c["epoch"] == 80)
c240 = next(c for c in r["checkpoints"] if c["epoch"] == 240)
tl80, tl240 = c80["train_loss"], c240["train_loss"]
ta80, ta240 = c80["test_acc"], c240["test_acc"]
rel_red = (tl80 - tl240) / tl80 if tl80 > 0 else 0.0
acc_delta = ta240 - ta80
deltas_80_to_240_train_rel.append(rel_red)
deltas_80_to_240_test_acc.append(acc_delta)
paired_rec = {
"seed": r["seed"],
"train_loss_relative_reduction_80_to_240": rel_red,
"test_accuracy_delta_80_to_240": acc_delta,
}
if has_200:
c200 = next(c for c in r["checkpoints"] if c["epoch"] == 200)
tl200, ta200 = c200["train_loss"], c200["test_acc"]
rel_red_200 = (tl200 - tl240) / tl200 if tl200 > 0 else 0.0
acc_delta_200 = ta240 - ta200
paired_rec["train_loss_relative_reduction_200_to_240"] = rel_red_200
paired_rec["test_accuracy_delta_200_to_240"] = acc_delta_200
paired_endpoint_deltas_by_seed.append(paired_rec)
paired_deltas["80_to_240"] = {
"train_loss_rel_reduction": calc_stats(deltas_80_to_240_train_rel),
"test_acc_delta": calc_stats(deltas_80_to_240_test_acc),
}
if has_200 and has_240:
deltas_200_to_240_train_rel = []
deltas_200_to_240_test_acc = []
deltas_200_to_240_test_acc_abs = []
for r in runs:
c200 = next(c for c in r["checkpoints"] if c["epoch"] == 200)
c240 = next(c for c in r["checkpoints"] if c["epoch"] == 240)
tl200, tl240 = c200["train_loss"], c240["train_loss"]
ta200, ta240 = c200["test_acc"], c240["test_acc"]
rel_red_200 = (tl200 - tl240) / tl200 if tl200 > 0 else 0.0
acc_delta_200 = ta240 - ta200
acc_abs_200 = abs(ta240 - ta200)
deltas_200_to_240_train_rel.append(rel_red_200)
deltas_200_to_240_test_acc.append(acc_delta_200)
deltas_200_to_240_test_acc_abs.append(acc_abs_200)
paired_deltas["200_to_240"] = {
"train_loss_rel_reduction": calc_stats(deltas_200_to_240_train_rel),
"test_acc_delta": calc_stats(deltas_200_to_240_test_acc),
"test_acc_abs_change": calc_stats(deltas_200_to_240_test_acc_abs),
}
# Predeclared Diagnostics
diagnostics = {}
if has_80 and has_240:
mean_tl80 = checkpoint_stats[80]["train_loss"]["mean"]
mean_tl240 = checkpoint_stats[240]["train_loss"]["mean"]
mean_ta80 = checkpoint_stats[80]["test_acc"]["mean"]
mean_ta240 = checkpoint_stats[240]["test_acc"]["mean"]
post80_train_improvement_pct = (mean_tl80 - mean_tl240) / mean_tl80 if mean_tl80 > 0 else 0.0
epoch240_test_gain = mean_ta240 - mean_ta80
diagnostics["post80_train_improvement_pct"] = round(post80_train_improvement_pct, 6)
diagnostics["post80_train_improvement_passed"] = bool(post80_train_improvement_pct >= 0.01)
diagnostics["epoch240_test_gain"] = round(epoch240_test_gain, 6)
diagnostics["epoch240_test_gain_passed"] = bool(epoch240_test_gain >= 0.01)
if has_200:
mean_tl200 = checkpoint_stats[200]["train_loss"]["mean"]
mean_ta200 = checkpoint_stats[200]["test_acc"]["mean"]
late_rel_red = (mean_tl200 - mean_tl240) / mean_tl200 if mean_tl200 > 0 else 0.0
late_acc_abs = abs(mean_ta240 - mean_ta200)
is_late_plateau = bool(late_rel_red < 0.01 and late_acc_abs < 0.005)
diagnostics["late_plateau_200_240"] = is_late_plateau
diagnostics["late_plateau_train_loss_rel_reduction_200_240"] = round(late_rel_red, 6)
diagnostics["late_plateau_test_acc_abs_change_200_240"] = round(late_acc_abs, 6)
# 5. Descriptive Comparison vs Subset Study
subset_comparison: Dict[str, Any] = {}
if subset_study_path.exists():
try:
with open(subset_study_path, "r", encoding="utf-8") as f:
sub_json_data = json.load(f)
sub_ckpt_stats = sub_json_data.get("summary", {}).get("checkpoint_stats", {})
matching_stats = {}
for ep in ckpt_epochs:
ep_str = str(ep)
if ep_str in sub_ckpt_stats:
sub_e = sub_ckpt_stats[ep_str]
full_tr_l = checkpoint_stats[ep]["train_loss"]["mean"]
sub_tr_l = sub_e["train_loss"]["mean"]
full_te_a = checkpoint_stats[ep]["test_acc"]["mean"]
sub_te_a = sub_e["test_acc"]["mean"]
matching_stats[ep_str] = {
"full_mnist_train_loss_mean": full_tr_l,
"subset_study_train_loss_mean": sub_tr_l,
"train_loss_delta_full_minus_subset": round(full_tr_l - sub_tr_l, 6),
"full_mnist_test_acc_mean": full_te_a,
"subset_study_test_acc_mean": sub_te_a,
"test_acc_delta_full_minus_subset": round(full_te_a - sub_te_a, 6),
}
subset_comparison = {
"subset_study_path": str(subset_study_path),
"disclaimer": (
"The full and subset studies optimize different training objectives and the evaluation "
"plugin changes RNG consumption (all 60,000 train samples versus a fixed 2,000-sample "
"fitness subset). Matching-seed and matching-epoch comparisons are descriptive, not an "
"exact paired causal isolation."
),
"matching_epoch_deltas": matching_stats,
}
except Exception as err:
subset_comparison = {"error": f"Failed to parse subset study JSON: {err}"}
# 6. Save Artifacts
# JSON output
out_dict = {
"full_mnist_protocol_version": FULL_MNIST_PROTOCOL_VERSION,
"pso_version": pso_version,
"torch_version": torch.__version__,
"hardware": hw_provenance,
"device": device.type,
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"baseline_path": str(baseline_path),
"subset_study_path": str(subset_study_path),
"sample_counts": {
"train_samples": 60000,
"test_samples": 10000,
},
"pca_provenance": pca_provenance,
"data_fingerprint": data_fp,
"fitness_evaluation_contract": {
"selector": "full",
"fitness_size": None,
"train_samples_per_particle_per_epoch": 60000,
"particle_evaluations": n_particles * target_epochs * len(seeds),
"particle_sample_evaluations": (
n_particles * target_epochs * len(seeds) * 60000
),
"test_samples_per_checkpoint": 10000,
},
"candidate_label": winner_cfg.candidate_label,
"config": {
**opt_kwargs,
"n_particles": n_particles,
"epochs": target_epochs,
"batch_size": batch_size,
"renewal": "loss",
"checkpoint_interval": checkpoint_interval,
},
"plugins": plugin_meta,
"timing_scope": "fit_only_after_full-evaluation_two-epoch_warmup",
"diagnostics": diagnostics,
"descriptive_subset_comparison": subset_comparison,
"summary": {
"epochs": ckpt_epochs,
"checkpoint_stats": {str(k): v for k, v in checkpoint_stats.items()},
"paired_deltas": paired_deltas,
"paired_endpoint_deltas_by_seed": paired_endpoint_deltas_by_seed,
},
"runs": runs,
"completed": True,
"valid": True,
"error": None,
}
save_json_atomic(out_dict, output_json_path)
# CSV output
output_csv_path.parent.mkdir(parents=True, exist_ok=True)
fieldnames = [
"seed",
"epoch",
"train_loss",
"train_acc",
"train_mse",
"test_loss",
"test_acc",
"test_mse",
"fit_time_sec",
]
with open(output_csv_path, "w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for r in flat_csv_rows:
writer.writerow(r)
# Plot output
render_trajectory_plot(checkpoint_stats, subset_comparison, figure_path)
return True
def main():
parser = argparse.ArgumentParser(
description="Full-MNIST 60k/10k Particle Swarm Optimization Trajectory Analysis"
)
parser.add_argument(
"--baseline-json",
type=Path,
default=Path("benchmark_results/pso_v4_tuning.json"),
help="Path to baseline tuning JSON (default: benchmark_results/pso_v4_tuning.json)",
)
parser.add_argument(
"--subset-study-json",
type=Path,
default=Path("benchmark_results/pso_v4_epoch_convergence.json"),
help="Path to subset epoch convergence JSON (default: benchmark_results/pso_v4_epoch_convergence.json)",
)
parser.add_argument(
"--output-json",
type=Path,
default=Path("benchmark_results/pso_v4_full_mnist.json"),
help="Path to output JSON (default: benchmark_results/pso_v4_full_mnist.json)",
)
parser.add_argument(
"--output-csv",
type=Path,
default=Path("benchmark_results/pso_v4_full_mnist.csv"),
help="Path to output CSV (default: benchmark_results/pso_v4_full_mnist.csv)",
)
parser.add_argument(
"--figure",
type=Path,
default=Path("history_plt/pso_v4_full_mnist.png"),
help="Path to output plot figure PNG (default: history_plt/pso_v4_full_mnist.png)",
)
parser.add_argument(
"--device",
type=str,
default="auto",
help="Execution device: auto, mps, cpu, cuda (default: auto)",
)
parser.add_argument(
"--epochs",
type=int,
default=240,
help="Number of PSO training epochs (default: 240)",
)
parser.add_argument(
"--seeds",
type=str,
default="71,72,73,74,75",
help="Comma-separated random seeds (default: 71,72,73,74,75)",
)
args = parser.parse_args()
seed_list = [int(s.strip()) for s in args.seeds.split(",") if s.strip()]
run_full_mnist_study(
baseline_path=args.baseline_json,
subset_study_path=args.subset_study_json,
output_json_path=args.output_json,
output_csv_path=args.output_csv,
figure_path=args.figure,
device_str=args.device,
epochs=args.epochs,
seeds=seed_list,
)
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+335
View File
@@ -0,0 +1,335 @@
"""
Heavy PSO Cross-Split Experiment Runner.
Protocol Version: HEAVY-PSO-CROSS-SPLIT 1.0.0
Runs matching baseline and candidate PSO experiments across development or confirmation
data splits under sealed official test conditions (official_test_evaluations = 0).
Phase Specifications:
- Development: split_seeds = (20260905, 20260906), swarm_seeds = (101, 102, 103)
- Confirmation: split_seeds = (20260907,), swarm_seeds = (111, 112, 113)
"""
from __future__ import annotations
import argparse
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
# Ensure test directory and repo root are in Python path
REPO_ROOT = Path(__file__).resolve().parent.parent if Path(__file__).resolve().parent.name != "PSO" else Path(__file__).resolve().parent
TEST_DIR = REPO_ROOT / "test"
if str(TEST_DIR) not in sys.path:
sys.path.insert(0, str(TEST_DIR))
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from benchmark_suite import (
get_hardware_provenance,
resolve_execution_device,
save_json_atomic,
)
from heavy_pso_autoresearch import (
DEFAULT_GEOMETRY_MULTIPLIER,
parse_projection_seed_arg,
parse_projection_scope_arg,
validate_projection_scope_config,
get_effective_projection_scope,
run_heavy_pso_autoresearch,
)
from heavy_task_feasibility import (
WORKLOADS,
run_heavy_task_confirm,
)
from pso import __version__ as pso_version
PROTOCOL_VERSION = "HEAVY-PSO-CROSS-SPLIT 1.0.0"
PHASE_CONFIGS = {
"development": {
"split_seeds": [20260905, 20260906],
"swarm_seeds": [101, 102, 103],
},
"confirmation": {
"split_seeds": [20260907],
"swarm_seeds": [111, 112, 113],
},
}
WORKLOAD_BASELINE_METHODS = {
"mnist_compact": "G8",
"mnist_wide": "G5",
"fashion_compact": "G8",
"fashion_wide": "G5",
}
FROZEN_PROJECTION_SEEDS = {
"mnist_compact": 1800044939,
"mnist_wide": 592157828,
"fashion_compact": 1363313651,
"fashion_wide": 189641451,
}
def run_heavy_pso_cross_split(
phase: str = "development",
ratio: float = 0.5,
geometry_policy: str = "baseline_aligned",
projection_scope: Union[str, Dict[str, str]] = "global",
projection_seed_mode: str = "explicit",
projection_seed: Optional[Union[int, Dict[str, int]]] = None,
geometry_multiplier: float = DEFAULT_GEOMETRY_MULTIPLIER,
particles: int = 12,
epochs: int = 80,
subset_size: int = 10000,
device_str: Optional[str] = None,
cache_dir: Optional[Path] = None,
output_path: Optional[Path] = None,
) -> Dict[str, Any]:
"""
Runs cross-split evaluation for development or confirmation phase.
Enforces exact split and swarm seed contracts for each phase.
Reruns matching baseline and candidate models per split seed.
"""
projection_scope = parse_projection_scope_arg(projection_scope)
validate_projection_scope_config(projection_scope)
if phase not in PHASE_CONFIGS:
raise ValueError(
f"Invalid phase '{phase}'. Must be one of {list(PHASE_CONFIGS.keys())}"
)
phase_spec = PHASE_CONFIGS[phase]
split_seeds = phase_spec["split_seeds"]
swarm_seeds = phase_spec["swarm_seeds"]
if projection_seed_mode == "explicit" and projection_seed is None:
projection_seed = dict(FROZEN_PROJECTION_SEEDS)
start_time = time.time()
device = resolve_execution_device(device_str)
hardware_info = get_hardware_provenance(device)
if cache_dir is None:
cache_dir = REPO_ROOT / "result" / "cache"
splits_payload: Dict[str, Any] = {}
total_runs = 0
total_queries = 0
total_samples_evaluated = 0
for split_seed in split_seeds:
# 1. Baseline runs for this split seed
selected_baseline_methods = {
wl_id: [WORKLOAD_BASELINE_METHODS[wl_id]] for wl_id in WORKLOADS
}
baseline_res = run_heavy_task_confirm(
workloads=WORKLOADS,
selected_methods=selected_baseline_methods,
particles=particles,
epochs=epochs,
seeds=swarm_seeds,
split_seed=split_seed,
device=device,
cache_dir=cache_dir,
)
# 2. Candidate runs for this split seed
candidate_res = run_heavy_pso_autoresearch(
ratios=[ratio],
particles=particles,
epochs=epochs,
subset_size=subset_size,
seeds=swarm_seeds,
geometry_policy=geometry_policy,
device_str=device_str,
cache_dir=cache_dir,
split_seed=split_seed,
projection_scope=projection_scope,
projection_seed_mode=projection_seed_mode,
projection_seed=projection_seed,
geometry_multiplier=geometry_multiplier,
)
# Extract candidate ratio payload
candidate_ratio_runs = list(candidate_res["candidate_runs"].values())[0]
baselines_split: Dict[str, Any] = {}
candidates_split: Dict[str, Any] = {}
dataset_fingerprints: Dict[str, str] = {}
split_fingerprints: Dict[str, str] = {}
for wl_id in WORKLOADS:
b_method = WORKLOAD_BASELINE_METHODS[wl_id]
b_entry = baseline_res[wl_id][b_method]
baselines_split[wl_id] = b_entry
c_entry = candidate_ratio_runs[wl_id]
candidates_split[wl_id] = c_entry
dataset_name = WORKLOADS[wl_id].dataset_name
dataset_fingerprints[dataset_name] = c_entry["data_fingerprint"]
split_fingerprints[dataset_name] = c_entry["split_fingerprint"]
# Resource accumulation
for r in b_entry["per_seed_runs"]:
total_runs += 1
total_queries += r["total_queries"]
total_samples_evaluated += r["total_sample_evaluations"]
for r in c_entry["per_seed_runs"]:
total_runs += 1
total_queries += r["total_queries"]
total_samples_evaluated += r["total_sample_evaluations"]
splits_payload[str(split_seed)] = {
"split_seed": split_seed,
"data_fingerprints": dataset_fingerprints,
"split_fingerprints": split_fingerprints,
"baselines": baselines_split,
"candidates": candidates_split,
}
payload = {
"version": PROTOCOL_VERSION,
"protocol_version": PROTOCOL_VERSION,
"phase": phase,
"split_seeds": list(split_seeds),
"swarm_seeds": list(swarm_seeds),
"official_test_data_loaded": False,
"official_test_evaluations": 0,
"candidate_config": {
"ratio": ratio,
"geometry_policy": geometry_policy,
"projection_scope": projection_scope,
"projection_seed_mode": projection_seed_mode,
"projection_seed": projection_seed,
"geometry_multiplier": float(geometry_multiplier),
"particles": particles,
"epochs": epochs,
"subset_size": subset_size,
},
"workloads": {
wl_id: {
"workload_id": wl_id,
"dataset_name": wl_cfg.dataset_name,
"model_name": wl_cfg.model_name,
"baseline_method": WORKLOAD_BASELINE_METHODS[wl_id],
"projection_scope": get_effective_projection_scope(projection_scope, wl_id),
"effective_projection_seed": (
projection_seed[wl_id]
if isinstance(projection_seed, dict)
else projection_seed
),
}
for wl_id, wl_cfg in WORKLOADS.items()
},
"splits": splits_payload,
"resource_totals": {
"total_runs": total_runs,
"total_queries": total_queries,
"total_samples_evaluated": total_samples_evaluated,
"official_test_evaluations": 0,
"wall_time_sec": round(time.time() - start_time, 4),
},
"provenance": {
"hardware": hardware_info,
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"pso_version": pso_version,
},
}
if output_path is not None:
save_json_atomic(payload, output_path)
return payload
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Heavy PSO Cross-Split Experiment Runner (Development / Confirmation)"
)
parser.add_argument(
"--phase",
type=str,
default="development",
choices=list(PHASE_CONFIGS.keys()),
help="Experiment phase ('development' or 'confirmation')",
)
parser.add_argument("--device", type=str, default=None, help="Device (cpu, mps, cuda)")
parser.add_argument("--cache-dir", type=str, default=None, help="Dataset cache directory")
parser.add_argument("--output", type=str, default=None, help="Output artifact JSON path")
parser.add_argument(
"--ratio",
type=float,
default=0.5,
help="Subspace ratio for candidate PSO (default: 0.5)",
)
parser.add_argument(
"--geometry-policy",
type=str,
default="baseline_aligned",
help="Geometry policy (default: 'baseline_aligned')",
)
parser.add_argument(
"--projection-scope",
type=parse_projection_scope_arg,
default="global",
help="Projection scope ('global', 'tensor_local', 'balanced_global', 'two_hash_global', 'largest_tensor_hash', 'largest_tensor_row_hash', 'adjacent_pair', 'adjacent_difference', or workload dict)",
)
parser.add_argument(
"--projection-seed-mode",
type=str,
default="explicit",
help="Projection seed mode (default: 'explicit')",
)
parser.add_argument(
"--projection-seed",
type=parse_projection_seed_arg,
default=None,
help="Exact projection seed (int or dict) for explicit mode",
)
parser.add_argument(
"--geometry-multiplier",
type=float,
default=DEFAULT_GEOMETRY_MULTIPLIER,
help="Geometry multiplier (default: 1.0)",
)
parser.add_argument("--particles", type=int, default=12, help="Swarm size (default: 12)")
parser.add_argument("--epochs", type=int, default=80, help="PSO epochs (default: 80)")
parser.add_argument(
"--subset-size",
type=int,
default=10000,
help="Subset size (default: 10000)",
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
cache_path = Path(args.cache_dir) if args.cache_dir else None
out_path = Path(args.output) if args.output else None
run_heavy_pso_cross_split(
phase=args.phase,
ratio=args.ratio,
geometry_policy=args.geometry_policy,
projection_scope=args.projection_scope,
projection_seed_mode=args.projection_seed_mode,
projection_seed=args.projection_seed,
geometry_multiplier=args.geometry_multiplier,
particles=args.particles,
epochs=args.epochs,
subset_size=args.subset_size,
device_str=args.device,
cache_dir=cache_path,
output_path=out_path,
)
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+100 -62
View File
@@ -1,73 +1,111 @@
import gc import argparse
import os import torch
import sys import torch.nn as nn
from sklearn.datasets import load_iris from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split from sklearn.model_selection import train_test_split
from tensorflow import keras from sklearn.preprocessing import StandardScaler
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
from pso import optimizer from pso import Optimizer
from cli import add_pso_args, build_optimizer_kwargs
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
def make_model(): def make_model(seed: int = 42):
model = Sequential() torch.manual_seed(seed)
model.add(layers.Dense(10, activation="relu", input_shape=(4,))) return nn.Sequential(
model.add(layers.Dense(10, activation="relu")) nn.Linear(4, 10),
model.add(layers.Dense(3, activation="softmax")) nn.ReLU(),
nn.Linear(10, 10),
return model nn.ReLU(),
nn.Linear(10, 3),
def load_data():
iris = load_iris()
x = iris.data
y = iris.target
y = keras.utils.to_categorical(y, 3)
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.2, shuffle=True, stratify=y
) )
return x_train, x_test, y_train, y_test
def load_data(seed: int = 42):
iris = load_iris()
x = iris.data.astype("float32")
y = iris.target.astype("int64")
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.2, shuffle=True, stratify=y, random_state=seed
)
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(y_test, dtype=torch.int64),
)
model = make_model() def main():
x_train, x_test, y_train, y_test = load_data() parser = argparse.ArgumentParser(description="PSO Iris Benchmark Script")
add_pso_args(
parser,
defaults={
"method": "original",
"initialization": "model_noise",
"evaluation": "full",
"convergence": "particle_reset",
"refinement": "adam",
"n_particles": 24,
"c0": None,
"c1": None,
"w_min": None,
"w_max": None,
"negative_swarm": 0.0,
"mutation_swarm": 0.1,
"particle_min": -3.0,
"particle_max": 3.0,
"velocity_limit_ratio": 0.1,
"boundary_strategy": "reflect",
"seed": 42,
"epochs": 70,
"renewal": "loss",
"output_dir": "output/iris",
"checkpoint_interval": 25,
"refinement_epochs": 10,
"refinement_lr": 0.001,
},
)
args = parser.parse_args()
model = make_model(seed=args.seed)
x_train, x_test, y_train, y_test = load_data(seed=args.seed)
fitness_size = args.fitness_size if args.evaluation == "fixed_subset" else None
refinement_epochs = args.refinement_epochs if args.refinement == "adam" else 0
kwargs = build_optimizer_kwargs(
args,
model=model,
loss=nn.CrossEntropyLoss(),
task="multiclass",
inertia_profile={"c0": 0.5, "c1": 0.3, "w_min": 0.1, "w_max": 0.9},
)
pso_iris = Optimizer(**kwargs)
print(f"Optimizer device: {pso_iris.device}")
best_score = pso_iris.fit(
x_train,
y_train,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=fitness_size,
renewal=args.renewal,
validation_data=(x_test, y_test),
output_dir=args.output_dir,
checkpoint_interval=25,
save_info=True,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
print(f"Done! Best score: {best_score}")
pso_iris = optimizer( if __name__ == "__main__":
model=model, main()
loss="categorical_crossentropy",
n_particles=100,
c0=0.5,
c1=0.3,
w_min=0.1,
w_max=0.9,
negative_swarm=0,
mutation_swarm=0.1,
convergence_reset=True,
convergence_reset_patience=10,
convergence_reset_monitor="loss",
convergence_reset_min_delta=0.001,
)
best_score = pso_iris.fit(
x_train,
y_train,
epochs=500,
save_info=True,
log=2,
log_name="iris",
renewal="loss",
check_point=25,
validate_data=(x_test, y_test),
)
gc.collect()
print("Done!")
sys.exit(0)
-55
View File
@@ -1,55 +0,0 @@
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
import tensorflow as tf
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
# tf.config.experimental.set_visible_devices(gpus[0], "GPU")
tf.config.experimental.set_memory_growth(gpus[0], True)
except RuntimeError as e:
print(e)
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
def make_model():
model = Sequential()
model.add(layers.Dense(10, activation="relu", input_shape=(4,)))
model.add(layers.Dense(10, activation="relu"))
model.add(layers.Dense(3, activation="softmax"))
return model
def load_data():
iris = load_iris()
x = iris.data
y = iris.target
y = keras.utils.to_categorical(y, 3)
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.2, shuffle=True, stratify=y
)
return x_train, x_test, y_train, y_test
if __name__ == "__main__":
model = make_model()
x_train, x_test, y_train, y_test = load_data()
print(x_train.shape, y_train.shape)
loss = ["categorical_crossentropy", "accuracy", "mse"]
metrics = ["accuracy"]
model.compile(optimizer="sgd", loss=loss[0], metrics=metrics[0])
model.fit(x_train, y_train, epochs=200, batch_size=32, validation_split=0.2)
model.evaluate(x_test, y_test, batch_size=32)
+127
View File
@@ -0,0 +1,127 @@
"""Iris dataset gradient baseline (PyTorch standard backprop optimizer, non-PSO)."""
import copy
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
class IrisModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(4, 10),
nn.ReLU(),
nn.Linear(10, 10),
nn.ReLU(),
nn.Linear(10, 3),
)
def forward(self, x):
return self.net(x)
def load_data(seed: int = 42):
iris = load_iris()
X = iris.data.astype("float32")
y = iris.target.astype("int64")
x_train, x_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=True, stratify=y, random_state=seed
)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(y_test, dtype=torch.int64),
)
def get_device() -> torch.device:
if (
hasattr(torch.backends, "mps")
and torch.backends.mps.is_built()
and torch.backends.mps.is_available()
):
return torch.device("mps")
elif torch.cuda.is_available():
return torch.device("cuda")
return torch.device("cpu")
def main():
torch.manual_seed(42)
np.random.seed(42)
device = get_device()
print(f"Selected device: {device}")
x_train, x_test, y_train, y_test = load_data(seed=42)
train_loader = DataLoader(
TensorDataset(x_train, y_train), batch_size=32, shuffle=True
)
val_loader = DataLoader(
TensorDataset(x_test, y_test), batch_size=32, shuffle=False
)
model = IrisModel().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()
best_val_loss = float("inf")
best_state = None
for epoch in range(200):
model.train()
for bx, by in train_loader:
bx, by = bx.to(device), by.to(device)
optimizer.zero_grad()
out = model(bx)
loss = criterion(out, by)
loss.backward()
optimizer.step()
model.eval()
val_loss = 0.0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
val_loss += loss.item() * bx.size(0)
total += bx.size(0)
val_loss /= total
if val_loss < best_val_loss:
best_val_loss = val_loss
best_state = copy.deepcopy(model.state_dict())
if best_state is not None:
model.load_state_dict(best_state)
model.eval()
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for bx, by in val_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
test_loss += loss.item() * bx.size(0)
preds = out.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
test_loss /= total
test_acc = correct / total
print(f"Final test loss: {test_loss:.4f}, accuracy: {test_acc:.4f}")
if __name__ == "__main__":
main()
+94 -71
View File
@@ -1,88 +1,111 @@
# %% import argparse
import os import torch
import sys import torch.nn as nn
from pso import optimizer from pso import Optimizer
from cli import add_pso_args, build_optimizer_kwargs
import tensorflow as tf
from keras.datasets import mnist
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
from keras.models import Sequential
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" def get_data(seed: int = 42):
from sklearn.decomposition import PCA
from torchvision.datasets import MNIST
train_dataset = MNIST(root="./data", train=True, download=True)
test_dataset = MNIST(root="./data", train=False, download=True)
def get_data(): x_train_raw = (train_dataset.data[:3000].float() / 255.0).reshape(3000, -1).numpy()
(x_train, y_train), (x_test, y_test) = mnist.load_data() y_train = train_dataset.targets[:3000].long()
x_train, x_test = x_train / 255.0, x_test / 255.0 x_test_raw = (test_dataset.data[:1000].float() / 255.0).reshape(1000, -1).numpy()
x_train = x_train.reshape((60000, 28, 28, 1)) y_test = test_dataset.targets[:1000].long()
x_test = x_test.reshape((10000, 28, 28, 1))
y_train, y_test = tf.one_hot(y_train, 10), tf.one_hot(y_test, 10) pca = PCA(n_components=32, whiten=True, random_state=seed)
x_train_pca = pca.fit_transform(x_train_raw)
x_test_pca = pca.transform(x_test_raw)
x_train, x_test = tf.convert_to_tensor(x_train), tf.convert_to_tensor(x_test) x_train = torch.tensor(x_train_pca, dtype=torch.float32)
y_train, y_test = tf.convert_to_tensor(y_train), tf.convert_to_tensor(y_test) x_test = torch.tensor(x_test_pca, dtype=torch.float32)
print(f"x_train : {x_train[0].shape} | y_train : {y_train[0].shape}") print(f"x_train : {x_train.shape} | y_train : {y_train.shape}")
print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}") print(f"x_test : {x_test.shape} | y_test : {y_test.shape}")
return x_train, y_train, x_test, y_test return x_train, y_train, x_test, y_test
def make_model(): def make_model(seed: int = 42):
model = Sequential() torch.manual_seed(seed)
model.add( return nn.Linear(32, 10)
Conv2D(32, kernel_size=(5, 5), activation="relu", input_shape=(28, 28, 1))
def main():
parser = argparse.ArgumentParser(description="PSO MNIST Benchmark Script")
add_pso_args(
parser,
defaults={
"method": "inertia",
"initialization": "model_noise",
"evaluation": "fixed_subset",
"convergence": "none",
"refinement": "adam",
"n_particles": 30,
"c0": None,
"c1": None,
"w_min": None,
"w_max": None,
"negative_swarm": 0.0,
"mutation_swarm": 0.02,
"particle_min": -3.0,
"particle_max": 3.0,
"velocity_limit_ratio": 0.025,
"boundary_strategy": "reflect",
"initial_position_noise": 0.05,
"seed": 42,
"epochs": 80,
"batch_size": 1000,
"fitness_size": 2000,
"renewal": "loss",
"output_dir": "output/mnist",
"checkpoint_interval": 25,
"refinement_epochs": 100,
"refinement_lr": 0.01,
},
) )
model.add(MaxPooling2D(pool_size=(2, 2))) args = parser.parse_args()
model.add(Dropout(0.5))
model.add(Conv2D(64, kernel_size=(3, 3), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.5))
model.add(Dense(256, activation="relu"))
model.add(Dense(128, activation="relu"))
model.add(Dense(10, activation="softmax"))
return model model = make_model(seed=args.seed)
x_train, y_train, x_test, y_test = get_data(seed=args.seed)
fitness_size = args.fitness_size if args.evaluation == "fixed_subset" else None
refinement_epochs = args.refinement_epochs if args.refinement == "adam" else 0
kwargs = build_optimizer_kwargs(
args,
model=model,
loss=nn.CrossEntropyLoss(),
task="multiclass",
inertia_profile={"c0": 1.49618, "c1": 1.49618, "w_min": 0.7298, "w_max": 0.7298},
)
pso_mnist = Optimizer(**kwargs)
print(f"Optimizer device: {pso_mnist.device}")
best_score = pso_mnist.fit(
x_train,
y_train,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=fitness_size,
renewal=args.renewal,
validation_data=(x_test, y_test),
output_dir=args.output_dir,
checkpoint_interval=25,
save_info=True,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
print(f"Done! Best score: {best_score}")
# %% if __name__ == "__main__":
model = make_model() main()
x_train, y_train, x_test, y_test = get_data()
pso_mnist = optimizer(
model,
loss="categorical_crossentropy",
n_particles=200,
c0=0.7,
c1=0.4,
w_min=0.1,
w_max=0.9,
negative_swarm=0.0,
mutation_swarm=0.05,
convergence_reset=True,
convergence_reset_patience=10,
convergence_reset_monitor="loss",
convergence_reset_min_delta=0.005,
)
best_score = pso_mnist.fit(
x_train,
y_train,
epochs=1000,
save_info=True,
log=2,
log_name="mnist",
renewal="loss",
check_point=25,
batch_size=5000,
validate_data=(x_test, y_test),
)
print("Done!")
sys.exit(0)
-132
View File
@@ -1,132 +0,0 @@
from keras.models import Sequential
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
from keras.datasets import mnist
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
# from tensorflow.data.Dataset import from_tensor_slices
import tensorflow as tf
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
tf.config.experimental.set_memory_growth(gpus[0], True)
except Exception as e:
print(e)
finally:
del gpus
def get_data():
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
x_train = x_train.reshape((60000, 28, 28, 1))
x_test = x_test.reshape((10000, 28, 28, 1))
print(f"x_train : {x_train[0].shape} | y_train : {y_train[0].shape}")
print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}")
return x_train, y_train, x_test, y_test
class _batch_generator_:
def __init__(self, x, y, batch_size: int = None):
self.index = 0
self.x = x
self.y = y
self.setBatchSize(batch_size)
def next(self):
self.index += 1
if self.index >= self.max_index:
self.index = 0
self.__getBatchSlice(self.batch_size)
return self.dataset[self.index][0], self.dataset[self.index][1]
def getMaxIndex(self):
return self.max_index
def getIndex(self):
return self.index
def setIndex(self, index):
self.index = index
def getBatchSize(self):
return self.batch_size
def setBatchSize(self, batch_size: int = None):
if batch_size is None:
batch_size = len(self.x) // 10
elif batch_size > len(self.x):
batch_size = len(self.x)
self.batch_size = batch_size
print(f"batch size : {self.batch_size}")
self.dataset = self.__getBatchSlice(self.batch_size)
self.max_index = len(self.dataset)
def __getBatchSlice(self, batch_size):
return list(
tf.data.Dataset.from_tensor_slices((self.x, self.y))
.shuffle(len(self.x))
.batch(batch_size)
)
def getDataset(self):
return self.dataset
def make_model():
model = Sequential()
model.add(
Conv2D(64, kernel_size=(5, 5), activation="relu", input_shape=(28, 28, 1))
)
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Conv2D(128, kernel_size=(3, 3), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.5))
model.add(Dense(2048, activation="relu"))
model.add(Dropout(0.8))
model.add(Dense(1024, activation="relu"))
model.add(Dropout(0.8))
model.add(Dense(10, activation="softmax"))
return model
model = make_model()
x_train, y_train, x_test, y_test = get_data()
y_train = tf.one_hot(y_train, 10)
y_test = tf.one_hot(y_test, 10)
batch = 64
dataset = _batch_generator_(x_train, y_train, batch)
model.compile(
optimizer="adam",
loss="categorical_crossentropy",
metrics=["accuracy", "mse"],
)
count = 0
print(f"batch size : {batch}")
print("iter " + str(dataset.getMaxIndex()))
print("Training model...")
# while count < dataset.getMaxIndex():
# x_batch, y_batch = dataset.next()
# count += 1
# print(f"iter {count}/{dataset.getMaxIndex()}")
model.fit(x_train, y_train, epochs=1000, batch_size=batch, verbose=1)
print(count)
print("Evaluating model...")
model.evaluate(x_test, y_test, verbose=1)
weights = model.get_weights()
+136
View File
@@ -0,0 +1,136 @@
"""MNIST dataset gradient baseline (PyTorch standard backprop optimizer, non-PSO)."""
import copy
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
class MNISTModel(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 64, kernel_size=5)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(2, 2)
self.drop1 = nn.Dropout(0.5)
self.conv2 = nn.Conv2d(64, 128, kernel_size=3)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(2, 2)
self.drop2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(128 * 5 * 5, 2048)
self.relu3 = nn.ReLU()
self.drop3 = nn.Dropout(0.8)
self.fc2 = nn.Linear(2048, 1024)
self.relu4 = nn.ReLU()
self.drop4 = nn.Dropout(0.8)
self.fc3 = nn.Linear(1024, 10)
def forward(self, x):
x = self.drop1(self.pool1(self.relu1(self.conv1(x))))
x = self.pool2(self.relu2(self.conv2(x)))
x = torch.flatten(x, 1)
x = self.drop3(self.relu3(self.fc1(self.drop2(x))))
x = self.drop4(self.relu4(self.fc2(x)))
x = self.fc3(x)
return x
def get_data(download: bool = True):
from torchvision import datasets, transforms
transform = transforms.ToTensor()
train_dataset = datasets.MNIST(
root="./data", train=True, transform=transform, download=download
)
test_dataset = datasets.MNIST(
root="./data", train=False, transform=transform, download=download
)
return train_dataset, test_dataset
def get_device() -> torch.device:
if (
hasattr(torch.backends, "mps")
and torch.backends.mps.is_built()
and torch.backends.mps.is_available()
):
return torch.device("mps")
elif torch.cuda.is_available():
return torch.device("cuda")
return torch.device("cpu")
def main():
torch.manual_seed(42)
np.random.seed(42)
device = get_device()
print(f"Selected device: {device}")
train_dataset, test_dataset = get_data(download=True)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
model = MNISTModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
best_val_loss = float("inf")
best_state = None
for epoch in range(10):
model.train()
for bx, by in train_loader:
bx, by = bx.to(device), by.to(device)
optimizer.zero_grad()
out = model(bx)
loss = criterion(out, by)
loss.backward()
optimizer.step()
model.eval()
val_loss = 0.0
total = 0
with torch.no_grad():
for bx, by in test_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
val_loss += loss.item() * bx.size(0)
total += bx.size(0)
val_loss /= total
if val_loss < best_val_loss:
best_val_loss = val_loss
best_state = copy.deepcopy(model.state_dict())
if best_state is not None:
model.load_state_dict(best_state)
model.eval()
test_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for bx, by in test_loader:
bx, by = bx.to(device), by.to(device)
out = model(bx)
loss = criterion(out, by)
test_loss += loss.item() * bx.size(0)
preds = out.argmax(dim=1)
correct += (preds == by).sum().item()
total += bx.size(0)
test_loss /= total
test_acc = correct / total
print(f"Final test loss: {test_loss:.4f}, accuracy: {test_acc:.4f}")
if __name__ == "__main__":
main()
@@ -0,0 +1,269 @@
"""Emit live, read-only TensorBoard progress for a convergence-study run."""
from __future__ import annotations
import argparse
import csv
import json
import time
from pathlib import Path
from typing import Any
from torch.utils.tensorboard import SummaryWriter
BASE_SEEDS = (501, 502, 503)
SWARM_SEEDS = (601, 602, 603)
CIFAR_WORKLOADS = ("cifar10_resnet18", "cifar10_resnet50")
YOLO_WORKLOAD = "voc_yolo11n"
def _cifar_seed(root: Path, seed: int) -> dict[str, Any]:
baseline = root / f"baseline-{seed}.pt"
searches = sum(
(root / f"feature-{method}-{seed}-{swarm}.json").is_file()
for method in ("pso", "random")
for swarm in SWARM_SEEDS
)
controls = sum(
(root / f"{method}-{seed}.pt").is_file()
for method in ("feature-adam", "head-adam")
)
complete = baseline.is_file() and searches == 6 and controls == 2
if complete:
stage = "complete"
elif not baseline.is_file():
stage = "baseline_training"
elif searches < 6:
stage = f"pso_random_search_{searches}_of_6"
else:
stage = f"adam_controls_{controls}_of_2"
return {
"seed": seed,
"stage": stage,
"complete": complete,
"searches": searches,
"controls": controls,
}
def _read_yolo_metrics(path: Path) -> list[dict[str, float]]:
if not path.is_file():
return []
try:
with path.open(newline="", encoding="utf-8") as stream:
rows = []
for source in csv.DictReader(stream):
rows.append(
{
key.strip(): float(value)
for key, value in source.items()
if key is not None
and value is not None
and value.strip()
}
)
return rows
except (OSError, ValueError):
return []
def _yolo_seed(
root: Path,
run_root: Path,
seed: int,
) -> dict[str, Any]:
baseline = root / "baselines" / str(seed) / "ema_fp32.pt"
training_root = (
run_root / "ultralytics" / f"base-{seed}-100e"
)
metrics = _read_yolo_metrics(training_root / "results.csv")
baseline_complete = (
baseline.is_file()
and len(metrics) >= 100
and (training_root / "weights" / "last.pt").is_file()
)
arm_root = root / "arms" / str(seed)
searches = sum(
(arm_root / f"feature_{method}-{swarm}.pt").is_file()
for method in ("pso", "random")
for swarm in SWARM_SEEDS
)
controls = sum(
(arm_root / f"{method}.pt").is_file()
for method in ("feature_adam", "head_adam")
)
complete = (arm_root / "record.json").is_file()
if complete:
stage = "complete"
elif not baseline_complete:
stage = f"baseline_training_epoch_{len(metrics)}_of_100"
elif searches < 6:
stage = f"pso_random_search_{searches}_of_6"
elif controls < 2:
stage = f"adam_controls_{controls}_of_2"
else:
stage = "selection"
return {
"seed": seed,
"stage": stage,
"complete": complete,
"searches": searches,
"controls": controls,
"training_metrics": metrics,
}
def snapshot(run_root: Path) -> dict[str, Any]:
workloads = run_root / "workloads"
status: dict[str, Any] = {}
completed = 0
for workload in CIFAR_WORKLOADS:
seeds = [
_cifar_seed(workloads / workload, seed)
for seed in BASE_SEEDS
]
completed += sum(item["complete"] for item in seeds)
status[workload] = seeds
yolo = [
_yolo_seed(
workloads / YOLO_WORKLOAD,
run_root,
seed,
)
for seed in BASE_SEEDS
]
completed += sum(item["complete"] for item in yolo)
status[YOLO_WORKLOAD] = yolo
state_path = run_root / "state.json"
state = "missing"
if state_path.is_file():
try:
state = str(
json.loads(
state_path.read_text(encoding="utf-8")
).get("state", "unknown")
)
except (OSError, ValueError):
state = "unreadable"
active = next(
(
f"{workload}/seed-{item['seed']}/{item['stage']}"
for workload, items in status.items()
for item in items
if not item["complete"]
),
"",
)
if not active:
for workload in CIFAR_WORKLOADS:
selected = sum(
(
workloads
/ workload
/ f"selected-feature_{method}-{seed}.pt"
).is_file()
for method in ("pso", "random")
for seed in BASE_SEEDS
)
if selected < 6:
active = (
f"{workload}/development_selection_"
f"{selected}_of_6"
)
break
if not active:
active = "development_complete"
return {
"state": state,
"active": active,
"completed_base_seeds": int(completed),
"total_base_seeds": 9,
"workloads": status,
"observed_at": time.time(),
}
def emit(
writer: SummaryWriter,
value: dict[str, Any],
step: int,
) -> None:
writer.add_scalar(
"progress/completed_base_seeds",
value["completed_base_seeds"],
step,
)
writer.add_scalar(
"progress/completion_fraction",
value["completed_base_seeds"] / value["total_base_seeds"],
step,
)
for workload, seeds in value["workloads"].items():
writer.add_scalar(
f"progress/{workload}/completed_base_seeds",
sum(item["complete"] for item in seeds),
step,
)
for item in value["workloads"][YOLO_WORKLOAD]:
seed = item["seed"]
for row in item["training_metrics"]:
epoch = int(row["epoch"])
for metric, metric_value in row.items():
if metric in {"epoch", "time"}:
continue
writer.add_scalar(
f"training/{YOLO_WORKLOAD}/seed_{seed}/{metric}",
metric_value,
epoch,
)
writer.add_text(
"progress/current",
f"`{value['active']}`",
step,
)
writer.add_text(
"progress/snapshot",
f"```json\n{json.dumps(value, indent=2)}\n```",
step,
)
writer.flush()
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--run-root", required=True, type=Path)
parser.add_argument("--interval", type=float, default=10.0)
parser.add_argument("--once", action="store_true")
args = parser.parse_args()
if args.interval <= 0:
raise SystemExit("--interval must be positive")
log_dir = args.run_root / "tensorboard"
writer = SummaryWriter(log_dir=str(log_dir))
print(f"monitoring {args.run_root} -> {log_dir}", flush=True)
step = 0
try:
while True:
value = snapshot(args.run_root)
emit(writer, value, step)
print(
json.dumps(
{
"active": value["active"],
"completed_base_seeds": value[
"completed_base_seeds"
],
"state": value["state"],
},
sort_keys=True,
),
flush=True,
)
step += 1
if args.once:
break
time.sleep(args.interval)
finally:
writer.close()
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+1751
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+655
View File
@@ -0,0 +1,655 @@
"""
Heavy PSO Cross-Split Experiment Results Publisher.
Protocol Version: HEAVY-PSO-CROSS-SPLIT-PUBLISH 1.0.0
Reads raw candidate and evaluation artifacts from a completed cross-split mission run,
validates integrity, accounting, test seals, and candidate/evaluation alignment across
all development variants, and exports deterministic public benchmark artifacts:
1. benchmark_results/pso_v7_heavy_cross_split.json
2. benchmark_results/pso_v7_heavy_cross_split.csv
3. history_plt/pso_v7_heavy_cross_split.png
"""
from __future__ import annotations
import argparse
import csv
import json
import math
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
# Ensure test directory and repo root are in sys.path
REPO_ROOT = Path(__file__).resolve().parent.parent if Path(__file__).resolve().parent.name != "PSO" else Path(__file__).resolve().parent
TEST_DIR = REPO_ROOT / "test"
if str(TEST_DIR) not in sys.path:
sys.path.insert(0, str(TEST_DIR))
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from benchmark_suite import save_json_atomic
from pso import __version__ as pso_version
PUBLISH_PROTOCOL_VERSION = "HEAVY-PSO-CROSS-SPLIT-PUBLISH 1.0.0"
DEFAULT_SOURCE_DIR = Path(".omc/autoresearch/heavy-pso-cross-split-robustness/runs/20260903T162504Z")
DEFAULT_JSON_OUTPUT = Path("benchmark_results/pso_v7_heavy_cross_split.json")
DEFAULT_CSV_OUTPUT = Path("benchmark_results/pso_v7_heavy_cross_split.csv")
DEFAULT_PLOT_OUTPUT = Path("history_plt/pso_v7_heavy_cross_split.png")
WORKLOADS = ["mnist_compact", "mnist_wide", "fashion_compact", "fashion_wide"]
BASELINE_METHODS = {
"mnist_compact": "G8",
"mnist_wide": "G5",
"fashion_compact": "G8",
"fashion_wide": "G5",
}
EXPECTED_DEV_SPLITS = [20260905, 20260906]
EXPECTED_DEV_SWARM_SEEDS = [101, 102, 103]
EXPECTED_VARIANTS_COUNT = 9
EXPECTED_VARIANT_IDS = (
"iteration-0001-development",
"iteration-0002-development",
"iteration-0003-replica1-development",
"iteration-0003-replica2-development",
"iteration-0004-development",
"iteration-0005-development",
"iteration-0006-development",
"iteration-0007-development",
"iteration-0008-development",
)
EXPECTED_CELLS_PER_VARIANT = 8
EXPECTED_RUNS_PER_VARIANT = 48
EXPECTED_QUERIES_PER_VARIANT = 46080
EXPECTED_SAMPLES_PER_VARIANT = 460800000
EXPECTED_TOTAL_RUNS = EXPECTED_VARIANTS_COUNT * EXPECTED_RUNS_PER_VARIANT # 432
EXPECTED_TOTAL_QUERIES = EXPECTED_VARIANTS_COUNT * EXPECTED_QUERIES_PER_VARIANT # 414720
EXPECTED_TOTAL_SAMPLES = EXPECTED_VARIANTS_COUNT * EXPECTED_SAMPLES_PER_VARIANT # 4147200000
def discover_and_load_variants(
source_dir: Path,
) -> List[Tuple[Path, Path, Dict[str, Any], Dict[str, Any]]]:
"""
Discovers candidate and evaluation JSON file pairs in source_dir.
Supports both subdirectories (candidates/ & evaluations/) and direct directory structure.
"""
if not source_dir.exists():
raise FileNotFoundError(f"Source directory does not exist: {source_dir}")
cand_dir = source_dir / "candidates"
eval_dir = source_dir / "evaluations"
if cand_dir.is_dir() and eval_dir.is_dir():
candidate_files = sorted(cand_dir.glob("*.json"))
else:
candidate_files = sorted(source_dir.glob("*candidate*.json"))
if not candidate_files:
candidate_files = sorted(source_dir.glob("*.json"))
if not candidate_files:
raise ValueError(f"No candidate JSON files found in {source_dir}")
observed_ids = tuple(path.stem for path in candidate_files)
if observed_ids != EXPECTED_VARIANT_IDS:
raise ValueError(
f"Expected exact development variants {list(EXPECTED_VARIANT_IDS)}, "
f"got {list(observed_ids)}"
)
pairs = []
for cf in candidate_files:
if cand_dir.is_dir() and eval_dir.is_dir():
ef = eval_dir / cf.name
else:
ef_name = cf.name.replace("candidate", "evaluation")
ef = source_dir / ef_name
if not ef.exists():
ef = cf
if not ef.exists():
raise FileNotFoundError(f"Missing corresponding evaluation file for candidate {cf.name}: {ef}")
with cf.open("r", encoding="utf-8") as f:
cdata = json.load(f)
with ef.open("r", encoding="utf-8") as f:
edata = json.load(f)
pairs.append((cf, ef, cdata, edata))
return pairs
def validate_variant_pair(
cf_path: Path,
ef_path: Path,
cdata: Dict[str, Any],
edata: Dict[str, Any],
) -> Dict[str, Any]:
"""
Validates each source candidate/evaluation artifact pair for expected IDs,
phase, test seals, no development pass, cell count 8, and resource accounting.
"""
variant_id = cf_path.stem
# Phase check
c_phase = cdata.get("phase")
e_phase = edata.get("phase")
if c_phase != "development":
raise ValueError(f"[{variant_id}] Candidate phase must be 'development', got: {c_phase}")
if e_phase not in (None, "development"):
raise ValueError(f"[{variant_id}] Evaluation phase must be 'development', got: {e_phase}")
# Official test sealed check
if cdata.get("official_test_data_loaded") is not False:
raise ValueError(f"[{variant_id}] candidate.official_test_data_loaded must be False")
if cdata.get("official_test_evaluations") != 0:
raise ValueError(f"[{variant_id}] candidate.official_test_evaluations must be 0")
gates = edata.get("gates", {})
if gates and "official_test_sealed" in gates:
if not gates["official_test_sealed"].get("pass", False):
raise ValueError(f"[{variant_id}] evaluation gate 'official_test_sealed' must pass")
# No development pass check
if edata.get("development_pass") is not False:
raise ValueError(f"[{variant_id}] development_pass must be False for all variants")
if edata.get("pass") is not False:
raise ValueError(f"[{variant_id}] pass must be False for all variants")
if edata.get("eligible_for_confirmation") is not False:
raise ValueError(f"[{variant_id}] eligible_for_confirmation must be False for all variants")
# Matching cell count 8
summary_metrics = edata.get("summary_metrics", {})
dev_cells = summary_metrics.get("development_cells")
cell_metrics = edata.get("cell_metrics", [])
if dev_cells != EXPECTED_CELLS_PER_VARIANT or len(cell_metrics) != EXPECTED_CELLS_PER_VARIANT:
raise ValueError(
f"[{variant_id}] Expected {EXPECTED_CELLS_PER_VARIANT} development cells, got "
f"summary_metrics.development_cells={dev_cells}, len(cell_metrics)={len(cell_metrics)}"
)
# Resource accounting check across per-seed runs
splits = cdata.get("splits", {})
c_runs = 0
c_queries = 0
c_samples = 0
c_test_evals = 0
for split_key, split_data in splits.items():
for role in ("baselines", "candidates"):
for wl_key, wl_data in split_data.get(role, {}).items():
for run in wl_data.get("per_seed_runs", []):
c_runs += 1
c_queries += run.get("total_queries", 0)
c_samples += run.get("total_sample_evaluations", 0)
c_test_evals += run.get("official_test_evaluations", 0)
if c_runs != EXPECTED_RUNS_PER_VARIANT:
raise ValueError(f"[{variant_id}] Expected {EXPECTED_RUNS_PER_VARIANT} runs, got {c_runs}")
if c_queries != EXPECTED_QUERIES_PER_VARIANT:
raise ValueError(f"[{variant_id}] Expected {EXPECTED_QUERIES_PER_VARIANT} total queries, got {c_queries}")
if c_samples != EXPECTED_SAMPLES_PER_VARIANT:
raise ValueError(f"[{variant_id}] Expected {EXPECTED_SAMPLES_PER_VARIANT} total sample evaluations, got {c_samples}")
if c_test_evals != 0:
raise ValueError(f"[{variant_id}] Official test evaluations must be 0, got {c_test_evals}")
resource_totals = cdata.get("resource_totals")
if not isinstance(resource_totals, dict):
raise ValueError(f"[{variant_id}] candidate.resource_totals must be present")
expected_resource_totals = {
"total_runs": c_runs,
"total_queries": c_queries,
"total_samples_evaluated": c_samples,
"official_test_evaluations": c_test_evals,
}
for field, expected in expected_resource_totals.items():
if resource_totals.get(field) != expected:
raise ValueError(
f"[{variant_id}] candidate.resource_totals.{field} must be {expected}, "
f"got {resource_totals.get(field)!r}"
)
wall_time = resource_totals.get("wall_time_sec")
if (
not isinstance(wall_time, (int, float))
or isinstance(wall_time, bool)
or not math.isfinite(float(wall_time))
or wall_time < 0
):
raise ValueError(
f"[{variant_id}] candidate.resource_totals.wall_time_sec must be finite and non-negative"
)
return {
"variant_id": variant_id,
"candidate_file": cf_path.name,
"evaluation_file": ef_path.name,
"candidate_path": _repository_relative_path(cf_path),
"evaluation_path": _repository_relative_path(ef_path),
"phase": "development",
"candidate_config": cdata.get("candidate_config", {}),
"score": float(edata.get("score", 0.0)),
"pass": False,
"development_pass": False,
"eligible_for_confirmation": False,
"failed_hard_gate_count": edata.get("failed_hard_gate_count", 0),
"failed_gates": edata.get("failed_gates", []),
"summary_metrics": summary_metrics,
"state_ratios": edata.get("state_ratios", {}),
"cell_metrics": cell_metrics,
"cdata": cdata,
"edata": edata,
"resources": {
"runs": c_runs,
"queries": c_queries,
"sample_evaluations": c_samples,
"wall_time_sec": float(wall_time),
"official_test_evaluations": 0,
},
}
def validate_all_variants(
variant_summaries: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""
Validates cumulative resources across all variants, identifies the best-observed variant,
and constructs cumulative summary dictionary.
"""
observed_ids = tuple(v["variant_id"] for v in variant_summaries)
if observed_ids != EXPECTED_VARIANT_IDS:
raise ValueError(
f"Expected exact development variants {list(EXPECTED_VARIANT_IDS)}, got {list(observed_ids)}"
)
total_runs = sum(v["resources"]["runs"] for v in variant_summaries)
total_queries = sum(v["resources"]["queries"] for v in variant_summaries)
total_samples = sum(v["resources"]["sample_evaluations"] for v in variant_summaries)
official_test_evals = sum(v["resources"]["official_test_evaluations"] for v in variant_summaries)
wall_time_sec = round(
math.fsum(v["resources"]["wall_time_sec"] for v in variant_summaries),
4,
)
if total_runs != EXPECTED_TOTAL_RUNS:
raise ValueError(f"Cumulative total runs must be {EXPECTED_TOTAL_RUNS}, got {total_runs}")
if total_queries != EXPECTED_TOTAL_QUERIES:
raise ValueError(f"Cumulative total queries must be {EXPECTED_TOTAL_QUERIES}, got {total_queries}")
if total_samples != EXPECTED_TOTAL_SAMPLES:
raise ValueError(f"Cumulative total sample evaluations must be {EXPECTED_TOTAL_SAMPLES}, got {total_samples}")
if official_test_evals != 0:
raise ValueError(f"Cumulative official test evaluations must be 0, got {official_test_evals}")
# Mark best-observed-but-rejected variant (highest evaluation score)
best_variant = max(variant_summaries, key=lambda v: v["score"])
for v in variant_summaries:
v["is_best_observed"] = (v["variant_id"] == best_variant["variant_id"])
return {
"n_variants": len(variant_summaries),
"total_runs": total_runs,
"total_queries": total_queries,
"total_sample_evaluations": total_samples,
"official_test_evaluations": 0,
"best_observed_variant_id": best_variant["variant_id"],
"wall_time_sec": wall_time_sec,
"best_observed_score": best_variant["score"],
}
def build_publish_json(
source_dir: Path,
variant_summaries: List[Dict[str, Any]],
cum_resources: Dict[str, Any],
) -> Dict[str, Any]:
"""
Constructs the compact JSON dictionary matching all publication criteria.
"""
now_iso = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
clean_variants = []
for v in sorted(variant_summaries, key=lambda item: item["variant_id"]):
clean_variants.append({
"variant_id": v["variant_id"],
"candidate_file": v["candidate_file"],
"evaluation_file": v["evaluation_file"],
"phase": v["phase"],
"candidate_config": v["candidate_config"],
"score": v["score"],
"pass": v["pass"],
"development_pass": v["development_pass"],
"eligible_for_confirmation": v["eligible_for_confirmation"],
"failed_hard_gate_count": v["failed_hard_gate_count"],
"failed_gates": v["failed_gates"],
"is_best_observed": v["is_best_observed"],
"summary_metrics": v["summary_metrics"],
"state_ratios": v["state_ratios"],
"cell_metrics": v["cell_metrics"],
"resources": v["resources"],
})
payload = {
"protocol_version": PUBLISH_PROTOCOL_VERSION,
"pso_version": pso_version,
"timestamp": now_iso,
"mission_contract": {
"phase": "development",
"official_test_data_loaded": False,
"official_test_evaluations": 0,
"confirmation_executed": False,
"retained_policy": None,
},
"official_test_data_loaded": False,
"official_test_evaluations": 0,
"confirmation_executed": False,
"retained_policy": None,
"verdict": {
"status": "NO_RETAINED_POLICY_NO_CONFIRMATION",
"retained_policy": None,
"confirmation_executed": False,
"official_test_data_loaded": False,
"official_test_evaluations": 0,
"best_observed_variant_id": cum_resources["best_observed_variant_id"],
"best_observed_score": cum_resources["best_observed_score"],
"description": (
"All 9 development candidates failed the frozen evaluator gates (specifically maximum accuracy "
"regression and/or development wide CNN improvement). No candidate qualified for confirmation. "
"Confirmation split 20260907 and official test data remained completely unexecuted and sealed."
),
},
"source_provenance": {
"source_dir": _repository_relative_path(source_dir),
"n_variants": cum_resources["n_variants"],
"candidate_files": [v["candidate_file"] for v in clean_variants],
"evaluation_files": [v["evaluation_file"] for v in clean_variants],
},
"cumulative_resources": cum_resources,
"total_runs": cum_resources["total_runs"],
"total_wall_time_sec": cum_resources["wall_time_sec"],
"total_queries": cum_resources["total_queries"],
"total_sample_evaluations": cum_resources["total_sample_evaluations"],
"variants": clean_variants,
}
return payload
def build_publish_csv(
variant_summaries: List[Dict[str, Any]],
csv_path: Path,
) -> None:
"""
Writes CSV summary with header + exactly 72 data rows (9 variants x 2 splits x 4 workloads).
Uses deterministic ordering and atomic writing.
"""
csv_path.parent.mkdir(parents=True, exist_ok=True)
fieldnames = [
"variant_id",
"phase",
"split_seed",
"workload_id",
"baseline_method",
"baseline_acc",
"candidate_acc",
"acc_gain_pp",
"baseline_nll",
"candidate_nll",
"nll_reduction_fraction",
"state_ratio",
"score",
"pass",
"is_best_observed",
"candidate_path",
"evaluation_path",
]
rows = []
# Sort variants deterministically
sorted_variants = sorted(variant_summaries, key=lambda v: v["variant_id"])
for v in sorted_variants:
dev_ratios = v.get("state_ratios", {}).get("development", {})
cell_metrics = v.get("cell_metrics", [])
# Sort cells deterministically by split_seed then workload_id order
def cell_sort_key(cm):
wl_idx = WORKLOADS.index(cm["workload_id"]) if cm["workload_id"] in WORKLOADS else 99
return (cm["split_seed"], wl_idx)
sorted_cells = sorted(cell_metrics, key=cell_sort_key)
for cm in sorted_cells:
workload_id = cm["workload_id"]
baseline_method = BASELINE_METHODS.get(workload_id, "G8" if "compact" in workload_id else "G5")
st_ratio = dev_ratios.get(workload_id, 0.5) if isinstance(dev_ratios, dict) else 0.5
row = {
"variant_id": v["variant_id"],
"phase": cm["phase"],
"split_seed": cm["split_seed"],
"workload_id": workload_id,
"baseline_method": baseline_method,
"baseline_acc": cm["baseline_acc"],
"candidate_acc": cm["candidate_acc"],
"acc_gain_pp": cm["acc_gain_pp"],
"baseline_nll": cm["baseline_nll"],
"candidate_nll": cm["candidate_nll"],
"nll_reduction_fraction": cm["nll_reduction_fraction"],
"state_ratio": st_ratio,
"score": v["score"],
"pass": False,
"is_best_observed": v["is_best_observed"],
"candidate_path": v["candidate_path"],
"evaluation_path": v["evaluation_path"],
}
rows.append(row)
tmp_csv = csv_path.with_suffix(".csv.tmp")
with tmp_csv.open("w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
tmp_csv.replace(csv_path)
def _repository_relative_path(path: Path) -> str:
try:
return str(path.resolve().relative_to(REPO_ROOT.resolve()))
except ValueError:
return str(path)
def render_publish_plot(
variant_summaries: List[Dict[str, Any]],
plot_path: Path,
) -> None:
"""
Renders readable 2-panel figure comparing mean gains and worst-cell regressions with frozen thresholds.
Clearly marks all variants failed and confirmation withheld.
"""
plot_path.parent.mkdir(parents=True, exist_ok=True)
sorted_variants = sorted(variant_summaries, key=lambda v: v["variant_id"])
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6.5))
variant_labels = [
v["variant_id"].replace("-development", "").replace("iteration-", "iter-")
for v in sorted_variants
]
x = np.arange(len(variant_labels))
width = 0.35
# Panel 1: Development Grand Mean Performance Gains
acc_gains = [v["summary_metrics"].get("development_grand_mean_accuracy_gain_pp", 0.0) for v in sorted_variants]
nll_reductions = [v["summary_metrics"].get("development_grand_mean_nll_reduction_fraction", 0.0) * 100.0 for v in sorted_variants]
ax1.bar(x - width/2, acc_gains, width, label="Grand Mean Acc Gain (pp)", color="#1f77b4")
ax1.bar(x + width/2, nll_reductions, width, label="Grand Mean NLL Red. (%)", color="#2ca02c")
ax1.axhline(0.0, color="black", linestyle="--", linewidth=1.0, alpha=0.7)
ax1.set_xticks(x)
ax1.set_xticklabels(variant_labels, rotation=35, ha="right", fontsize=9)
ax1.set_ylabel("Percentage Points (pp) / Percentage (%)")
ax1.set_title("Panel A: Development Grand Mean Performance Gains")
ax1.legend(loc="upper left")
ax1.grid(True, linestyle="--", alpha=0.4)
# Panel 2: Worst-Cell Regressions & Wide CNN Improvement vs Gate Thresholds
worst_acc_regs = []
worst_nll_regs = []
mnist_wide_accs = []
for v in sorted_variants:
cell_acc_gains = [cm["acc_gain_pp"] for cm in v["cell_metrics"]]
cell_nll_reds = [cm["nll_reduction_fraction"] * 100.0 for cm in v["cell_metrics"]]
worst_acc_regs.append(min(cell_acc_gains))
worst_nll_regs.append(min(cell_nll_reds))
mnist_wide_accs.append(v["summary_metrics"].get("development_mnist_wide_accuracy_gain_pp", 0.0))
ax2.plot(x, worst_acc_regs, "o-", color="#d62728", linewidth=2, label="Worst-Cell Acc Delta (pp)")
ax2.plot(x, worst_nll_regs, "s--", color="#ff7f0e", linewidth=2, label="Worst-Cell NLL Red. (%)")
ax2.plot(x, mnist_wide_accs, "^-.", color="#9467bd", linewidth=2, label="MNIST Wide Acc Gain (pp)")
# Gate threshold lines
ax2.axhline(-1.0, color="#d62728", linestyle=":", linewidth=1.5, label="Gate: Max Acc Reg. (-1.0 pp)")
ax2.axhline(-5.0, color="#ff7f0e", linestyle=":", linewidth=1.5, label="Gate: Max NLL Reg. (-5.0%)")
ax2.axhline(2.0, color="#2ca02c", linestyle="--", linewidth=1.5, label="Gate: MNIST Wide Gain (>= +2 pp)")
panel_two_values = worst_acc_regs + worst_nll_regs + mnist_wide_accs + [-5.0, 2.0]
panel_two_span = max(panel_two_values) - min(panel_two_values)
panel_two_margin = max(1.0, panel_two_span * 0.08)
ax2.set_ylim(
min(panel_two_values) - panel_two_margin,
max(panel_two_values) + panel_two_margin,
)
ax2.set_xticks(x)
ax2.set_xticklabels(variant_labels, rotation=35, ha="right", fontsize=9)
ax2.set_ylabel("Metrics vs Gate Thresholds")
ax2.set_title("Panel B: Worst-Cell Regressions & Wide CNN Improvement vs Gates")
ax2.legend(loc="lower left", fontsize=8)
ax2.grid(True, linestyle="--", alpha=0.4)
# Highlight best observed candidate
best_idx = next(i for i, v in enumerate(sorted_variants) if v["is_best_observed"])
ax1.annotate(
f"Best observed, still rejected\nScore: {sorted_variants[best_idx]['score']:.2f}",
xy=(best_idx, acc_gains[best_idx]),
xycoords="data",
xytext=(0.62, 0.94),
textcoords="axes fraction",
arrowprops=dict(facecolor="black", shrink=0.05, width=1, headwidth=5),
bbox=dict(boxstyle="round,pad=0.3", facecolor="white", edgecolor="black", alpha=0.95),
fontsize=8,
ha="center",
va="top",
weight="bold",
)
# Mission Outcome Text Banner
fig.suptitle(
"HEAVY PSO CROSS-SPLIT ROBUSTNESS MISSION REPORT\n"
"STATUS: ALL 9 VARIANTS FAILED FROZEN EVALUATOR GATES | CONFIRMATION WITHHELD & SEALED OFFICIAL TEST UNEXECUTED | RETAINED POLICY: NONE",
fontsize=11,
weight="bold",
color="#8b0000",
y=0.99,
)
plt.tight_layout(rect=[0, 0, 1, 0.93])
tmp_plot = plot_path.with_name(plot_path.stem + "_tmp.png")
fig.savefig(tmp_plot, dpi=200, bbox_inches="tight")
plt.close(fig)
tmp_plot.replace(plot_path)
def publish_heavy_cross_split(
source_dir: Union[str, Path] = DEFAULT_SOURCE_DIR,
output_json: Union[str, Path] = DEFAULT_JSON_OUTPUT,
output_csv: Union[str, Path] = DEFAULT_CSV_OUTPUT,
output_plot: Union[str, Path] = DEFAULT_PLOT_OUTPUT,
) -> Dict[str, Any]:
"""
Main programmatic interface for Heavy PSO Cross-Split results publication.
Parses artifacts, validates all contracts and resource totals, and writes
the compact JSON, CSV summary, and PNG plot atomically.
"""
source_dir = Path(source_dir).resolve()
output_json = Path(output_json).resolve()
output_csv = Path(output_csv).resolve()
output_plot = Path(output_plot).resolve()
pairs = discover_and_load_variants(source_dir)
variant_summaries = []
for cf, ef, cdata, edata in pairs:
summary = validate_variant_pair(cf, ef, cdata, edata)
variant_summaries.append(summary)
cum_resources = validate_all_variants(variant_summaries)
json_payload = build_publish_json(source_dir, variant_summaries, cum_resources)
save_json_atomic(json_payload, output_json)
build_publish_csv(variant_summaries, output_csv)
render_publish_plot(variant_summaries, output_plot)
return json_payload
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Publish Heavy PSO Cross-Split Robustness Mission Results"
)
parser.add_argument(
"--source-dir",
type=Path,
default=DEFAULT_SOURCE_DIR,
help=f"Raw experiment runs directory (default: {DEFAULT_SOURCE_DIR})",
)
parser.add_argument(
"--output-json",
type=Path,
default=DEFAULT_JSON_OUTPUT,
help=f"Output compact JSON path (default: {DEFAULT_JSON_OUTPUT})",
)
parser.add_argument(
"--output-csv",
type=Path,
default=DEFAULT_CSV_OUTPUT,
help=f"Output CSV summary path (default: {DEFAULT_CSV_OUTPUT})",
)
parser.add_argument(
"--output-plot",
type=Path,
default=DEFAULT_PLOT_OUTPUT,
help=f"Output PNG plot path (default: {DEFAULT_PLOT_OUTPUT})",
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
publish_heavy_cross_split(
source_dir=args.source_dir,
output_json=args.output_json,
output_csv=args.output_csv,
output_plot=args.output_plot,
)
print(f"[{PUBLISH_PROTOCOL_VERSION}] Successfully published cross-split results!")
print(f" JSON: {args.output_json}")
print(f" CSV: {args.output_csv}")
print(f" Plot: {args.output_plot}")
if __name__ == "__main__":
main()
+464
View File
@@ -0,0 +1,464 @@
"""
Adaptive Moment 120-Particle x 80-Epoch MNIST Scaling Replication Check
Validates the published 120-particle x 80-epoch fixed-epoch Adaptive Moment MNIST scaling result.
Performs exact replay on seeds 71-75 and fresh independent cohort evaluation on seeds 81-85.
Predeclared Acceptance Criteria:
1. Exact Replay (seeds 71-75): Max per-seed absolute test accuracy delta <= 0.005 (0.5%p).
2. Independent Cohort (seeds 81-85): Mean test accuracy absolute difference <= 0.03 (3%p)
AND 95% t-confidence intervals overlap between baseline and independent cohorts.
"""
import argparse
import csv
import datetime
import json
import sys
from pathlib import Path
from typing import Any, Dict, List, Tuple
import torch
from sklearn.decomposition import PCA
# Path setup for imports from test/ directory
sys.path.insert(0, str(Path(__file__).resolve().parent))
from benchmark_suite import (
calc_stats,
compute_data_fingerprint,
get_hardware_provenance,
resolve_execution_device,
save_json_atomic,
)
from pso import __version__ as pso_version
from tuning_suite import (
TUNING_PROTOCOL_VERSION,
CandidateConfig,
get_mnist_raw_data,
get_search_candidates,
run_single_experiment,
)
REPLAY_TOLERANCE = 0.005
INDEPENDENT_MEAN_MARGIN = 0.03
REPLICATION_PROTOCOL_VERSION = "1.0.0"
REPLAY_SEEDS = [71, 72, 73, 74, 75]
INDEPENDENT_SEEDS = [81, 82, 83, 84, 85]
def validate_and_load_baseline(
baseline_path: Path,
) -> Tuple[Dict[str, Any], List[Dict[str, Any]], CandidateConfig, str]:
if not baseline_path.exists():
raise FileNotFoundError(f"Baseline JSON file not found at: {baseline_path}")
with open(baseline_path, "r", encoding="utf-8") as f:
baseline_data = json.load(f)
if baseline_data.get("tuning_protocol_version") != TUNING_PROTOCOL_VERSION:
raise ValueError(
"Baseline tuning protocol mismatch: "
f"expected {TUNING_PROTOCOL_VERSION}, "
f"got {baseline_data.get('tuning_protocol_version')}"
)
if baseline_data.get("quick") is not False:
raise ValueError("Replication requires the full, non-quick tuning baseline.")
winners = baseline_data.get("winners", {})
if "adaptive_moment" not in winners:
raise ValueError(f"Baseline JSON {baseline_path} missing 'adaptive_moment' winner entry.")
am_winner_info = winners["adaptive_moment"]
winner_label = am_winner_info.get("candidate_label")
all_candidates = get_search_candidates()
am_candidates = all_candidates.get("adaptive_moment", [])
winner_cfg = None
for cfg in am_candidates:
if cfg.candidate_label == winner_label:
winner_cfg = cfg
break
if winner_cfg is None:
raise ValueError(
f"Could not find CandidateConfig matching label '{winner_label}' in search candidates."
)
expected_optimizer_config = winner_cfg.to_optimizer_kwargs(quick=False)
if am_winner_info.get("config") != expected_optimizer_config:
raise ValueError(
"Adaptive Moment winner configuration in the baseline no longer matches "
f"CandidateConfig '{winner_label}'."
)
scaling_runs = baseline_data.get("scaling_runs", [])
baseline_records = []
for r in scaling_runs:
if (
r.get("completed")
and r.get("method") == "adaptive_moment"
and r.get("candidate_label") == winner_label
and r.get("n_particles") == 120
and r.get("epochs") == 80
and r.get("regimen") == "fixed_epoch"
and r.get("seed") in REPLAY_SEEDS
):
baseline_records.append(r)
baseline_records.sort(key=lambda x: x["seed"])
if len(baseline_records) != 5:
raise ValueError(
f"Expected exactly 5 baseline records for seeds {REPLAY_SEEDS}, "
f"found {len(baseline_records)} in {baseline_path}."
)
expected_seeds = sorted(REPLAY_SEEDS)
actual_seeds = [r["seed"] for r in baseline_records]
if actual_seeds != expected_seeds:
raise ValueError(f"Baseline seeds mismatch: expected {expected_seeds}, got {actual_seeds}")
expected_fp = baseline_data.get("split_fingerprints", {}).get("full")
if not isinstance(expected_fp, str) or not expected_fp:
raise ValueError("Baseline JSON is missing split_fingerprints.full.")
for r in baseline_records:
if r.get("data_fingerprint") != expected_fp:
raise ValueError(
f"Baseline run seed {r['seed']} data_fingerprint {r.get('data_fingerprint')} "
f"does not match split_fingerprints.full {expected_fp}"
)
run_config = r.get("config", {})
for key, value in expected_optimizer_config.items():
if run_config.get(key) != value:
raise ValueError(
f"Baseline run seed {r['seed']} config[{key!r}]={run_config.get(key)!r} "
f"does not match selected winner value {value!r}."
)
expected_run_config = {
"n_particles": 120,
"epochs": 80,
"batch_size": 1000,
"renewal": "loss",
}
for key, value in expected_run_config.items():
if run_config.get(key) != value:
raise ValueError(
f"Baseline run seed {r['seed']} config[{key!r}]={run_config.get(key)!r}; "
f"expected {value!r}."
)
return baseline_data, baseline_records, winner_cfg, expected_fp
def prepare_full_pca_data() -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, str]:
x_train_raw, x_test_raw, y_train_3000, y_test_1000 = get_mnist_raw_data()
pca_full = PCA(n_components=32, whiten=True, random_state=42)
x_full_tr = torch.tensor(pca_full.fit_transform(x_train_raw), dtype=torch.float32)
x_full_test = torch.tensor(pca_full.transform(x_test_raw), dtype=torch.float32)
data_fp = compute_data_fingerprint(x_full_tr, x_full_test, y_train_3000, y_test_1000)
return x_full_tr, y_train_3000, x_full_test, y_test_1000, data_fp
def run_replication_cohort(
cfg: CandidateConfig,
seeds: List[int],
x_train: torch.Tensor,
y_train: torch.Tensor,
x_eval: torch.Tensor,
y_eval: torch.Tensor,
device: torch.device,
data_fp: str,
run_type: str,
) -> List[Dict[str, Any]]:
runs = []
for seed in seeds:
res = run_single_experiment(
cfg=cfg,
seed=seed,
x_train=x_train,
y_train=y_train,
x_eval=x_eval,
y_eval=y_eval,
n_particles=120,
epochs=80,
batch_size=1000,
device=device,
quick=False,
eval_metric_name="test",
data_fp=data_fp,
run_type=run_type,
extra_meta={"regimen": "fixed_epoch"},
)
runs.append(res)
return runs
def evaluate_replication(
baseline_records: List[Dict[str, Any]],
replay_runs: List[Dict[str, Any]],
independent_runs: List[Dict[str, Any]],
) -> Tuple[Dict[str, Any], Dict[str, float], Dict[str, float], Dict[str, float]]:
base_acc_by_seed = {r["seed"]: float(r["test_acc"]) for r in baseline_records}
replay_acc_by_seed = {r["seed"]: float(r["test_acc"]) for r in replay_runs}
expected_seeds = set(REPLAY_SEEDS)
if set(base_acc_by_seed) != expected_seeds or set(replay_acc_by_seed) != expected_seeds:
raise ValueError("Baseline and replay cohorts must each contain exactly seeds 71-75.")
if len(independent_runs) != len(INDEPENDENT_SEEDS) or {
r["seed"] for r in independent_runs
} != set(INDEPENDENT_SEEDS):
raise ValueError("Independent cohort must contain exactly seeds 81-85.")
baseline_model_fp = {r["seed"]: r.get("model_fingerprint") for r in baseline_records}
replay_model_fp = {r["seed"]: r.get("model_fingerprint") for r in replay_runs}
replay_model_fingerprint_match = baseline_model_fp == replay_model_fp
replay_deltas = {}
max_replay_delta = 0.0
for seed in sorted(base_acc_by_seed.keys()):
b_acc = base_acc_by_seed[seed]
r_acc = replay_acc_by_seed[seed]
delta = abs(r_acc - b_acc)
replay_deltas[str(seed)] = round(delta, 6)
if delta > max_replay_delta:
max_replay_delta = delta
replay_pass = bool(max_replay_delta <= REPLAY_TOLERANCE)
baseline_accs = [base_acc_by_seed[s] for s in sorted(base_acc_by_seed.keys())]
replay_accs = [replay_acc_by_seed[s] for s in sorted(replay_acc_by_seed.keys())]
indep_accs = [float(r["test_acc"]) for r in independent_runs]
baseline_stats = calc_stats(baseline_accs)
replay_stats = calc_stats(replay_accs)
independent_stats = calc_stats(indep_accs)
indep_mean_diff = abs(independent_stats["mean"] - baseline_stats["mean"])
independent_mean_pass = bool(indep_mean_diff <= INDEPENDENT_MEAN_MARGIN)
baseline_ci_low = round(baseline_stats["mean"] - baseline_stats["ci95_t"], 6)
baseline_ci_high = round(baseline_stats["mean"] + baseline_stats["ci95_t"], 6)
indep_ci_low = round(independent_stats["mean"] - independent_stats["ci95_t"], 6)
indep_ci_high = round(independent_stats["mean"] + independent_stats["ci95_t"], 6)
ci_overlap_pass = bool(max(baseline_ci_low, indep_ci_low) <= min(baseline_ci_high, indep_ci_high))
independent_pass = bool(independent_mean_pass and ci_overlap_pass)
comparison = {
"replay_per_seed_deltas": replay_deltas,
"replay_max_abs_delta": round(max_replay_delta, 6),
"replay_model_fingerprint_match": replay_model_fingerprint_match,
"replay_pass": bool(replay_pass and replay_model_fingerprint_match),
"independent_mean_abs_diff": round(indep_mean_diff, 6),
"independent_mean_pass": independent_mean_pass,
"baseline_ci95_t_interval": [baseline_ci_low, baseline_ci_high],
"independent_ci95_t_interval": [indep_ci_low, indep_ci_high],
"ci_overlap_pass": ci_overlap_pass,
"independent_pass": independent_pass,
"overall_pass": bool(
replay_pass and replay_model_fingerprint_match and independent_pass
),
}
return comparison, baseline_stats, replay_stats, independent_stats
def write_replication_csv(
baseline_records: List[Dict[str, Any]],
replay_runs: List[Dict[str, Any]],
independent_runs: List[Dict[str, Any]],
output_csv: Path,
):
output_csv.parent.mkdir(parents=True, exist_ok=True)
fields = [
"cohort",
"method",
"candidate_label",
"regimen",
"seed",
"n_particles",
"epochs",
"particle_epochs",
"train_loss",
"train_acc",
"test_loss",
"test_acc",
"test_mse",
"fit_time_sec",
"data_fingerprint",
"model_fingerprint",
"device",
"completed",
"error",
]
all_rows = []
for r in baseline_records:
row = dict(r)
row["cohort"] = "baseline"
all_rows.append(row)
for r in replay_runs:
row = dict(r)
row["cohort"] = "replay"
all_rows.append(row)
for r in independent_runs:
row = dict(r)
row["cohort"] = "independent"
all_rows.append(row)
with open(output_csv, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
for r in all_rows:
writer.writerow(r)
def main():
parser = argparse.ArgumentParser(
description="Replicate and verify published Adaptive Moment 120p x 80e MNIST scaling result"
)
parser.add_argument(
"--baseline-json",
type=Path,
default=Path("benchmark_results/pso_v4_tuning.json"),
help="Path to baseline tuning JSON",
)
parser.add_argument(
"--output-json",
type=Path,
default=Path("benchmark_results/pso_v4_120p80_replication.json"),
help="Path for replication output JSON",
)
parser.add_argument(
"--output-csv",
type=Path,
default=Path("benchmark_results/pso_v4_120p80_replication.csv"),
help="Path for replication output CSV",
)
parser.add_argument(
"--device",
type=str,
default=None,
help="Execution device (cpu, cuda, mps)",
)
args = parser.parse_args()
device = resolve_execution_device(args.device)
print("=== Adaptive Moment 120p x 80e Replication Check ===")
print(f"Device: {device}")
print(f"Baseline JSON: {args.baseline_json}")
print(f"Output JSON: {args.output_json}")
print(f"Output CSV: {args.output_csv}")
baseline_data, baseline_records, winner_cfg, expected_fp = validate_and_load_baseline(
args.baseline_json
)
print(f"Validated baseline winner '{winner_cfg.candidate_label}' across 5 records.")
if baseline_data.get("device") != str(device):
raise ValueError(
f"Exact replay requires baseline device {baseline_data.get('device')!r}; "
f"got {str(device)!r}."
)
if baseline_data.get("pso_version") != pso_version:
raise ValueError(
f"Exact replay requires pso version {baseline_data.get('pso_version')!r}; "
f"got {pso_version!r}."
)
if baseline_data.get("torch_version") != torch.__version__:
raise ValueError(
f"Exact replay requires torch version {baseline_data.get('torch_version')!r}; "
f"got {torch.__version__!r}."
)
x_full_tr, y_train_3000, x_full_test, y_test_1000, data_fp = prepare_full_pca_data()
if expected_fp and data_fp != expected_fp:
raise ValueError(
f"Reconstructed data fingerprint {data_fp} does not match baseline {expected_fp}"
)
print(f"Reconstructed PCA32 data (fingerprint: {data_fp})")
print("\n--- Running Cohort 1: Exact Replay (Seeds 71-75) ---")
replay_runs = run_replication_cohort(
cfg=winner_cfg,
seeds=REPLAY_SEEDS,
x_train=x_full_tr,
y_train=y_train_3000,
x_eval=x_full_test,
y_eval=y_test_1000,
device=device,
data_fp=data_fp,
run_type="replication_replay",
)
print("\n--- Running Cohort 2: Independent Fresh Seeds (Seeds 81-85) ---")
independent_runs = run_replication_cohort(
cfg=winner_cfg,
seeds=INDEPENDENT_SEEDS,
x_train=x_full_tr,
y_train=y_train_3000,
x_eval=x_full_test,
y_eval=y_test_1000,
device=device,
data_fp=data_fp,
run_type="replication_independent",
)
comparison, baseline_stats, replay_stats, independent_stats = evaluate_replication(
baseline_records, replay_runs, independent_runs
)
payload = {
"replication_protocol_version": REPLICATION_PROTOCOL_VERSION,
"source_tuning_protocol_version": baseline_data["tuning_protocol_version"],
"pso_version": pso_version,
"torch_version": torch.__version__,
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"device": str(device),
"hardware": get_hardware_provenance(device),
"baseline_json": str(args.baseline_json),
"source_tuning_timestamp": baseline_data.get("timestamp"),
"candidate_label": winner_cfg.candidate_label,
"config": winner_cfg.to_optimizer_kwargs(),
"data_fingerprint": data_fp,
"criteria": {
"replay_seeds": REPLAY_SEEDS,
"replay_max_abs_delta_tolerance": REPLAY_TOLERANCE,
"require_replay_model_fingerprint_match": True,
"independent_seeds": INDEPENDENT_SEEDS,
"independent_mean_abs_diff_margin": INDEPENDENT_MEAN_MARGIN,
"require_ci_overlap": True,
},
"summaries": {
"baseline": baseline_stats,
"replay": replay_stats,
"independent": independent_stats,
},
"comparison": comparison,
"baseline_runs": baseline_records,
"replay_runs": replay_runs,
"independent_runs": independent_runs,
"completed": True,
"error": None,
}
save_json_atomic(payload, args.output_json)
write_replication_csv(baseline_records, replay_runs, independent_runs, args.output_csv)
print("\n=== Replication Results Summary ===")
print(f"Baseline Mean Test Acc: {baseline_stats['mean']:.4f} ± {baseline_stats['std']:.4f}")
print(f"Replay Mean Test Acc: {replay_stats['mean']:.4f} ± {replay_stats['std']:.4f}")
print(f"Independent Mean Test Acc: {independent_stats['mean']:.4f} ± {independent_stats['std']:.4f}")
print(f"Max Replay Delta: {comparison['replay_max_abs_delta']:.6f} (Limit: {REPLAY_TOLERANCE}) -> Pass: {comparison['replay_pass']}")
print(f"Indep Mean Diff: {comparison['independent_mean_abs_diff']:.6f} (Limit: {INDEPENDENT_MEAN_MARGIN}) -> Pass: {comparison['independent_mean_pass']}")
print(f"CI Overlap Pass: {comparison['ci_overlap_pass']} (Baseline CI: {comparison['baseline_ci95_t_interval']}, Indep CI: {comparison['independent_ci95_t_interval']})")
print(f"OVERALL PASS: {comparison['overall_pass']}")
if not comparison["overall_pass"]:
print("\nREPLICATION CHECK FAILED!")
sys.exit(1)
else:
print("\nREPLICATION CHECK PASSED!")
if __name__ == "__main__":
main()
+97 -81
View File
@@ -1,23 +1,16 @@
# %%
import json
import os
import sys
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import tensorflow as tf import argparse
from keras.layers import Dense import torch
from keras.models import Sequential import torch.nn as nn
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split from sklearn.model_selection import train_test_split
from tensorflow import keras from sklearn.preprocessing import StandardScaler
from pso import optimizer from pso import Optimizer
from cli import add_pso_args, build_optimizer_kwargs
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
def get_data(): def get_data(seed: int = 42):
with open("data/seeds/seeds_dataset.txt", "r", encoding="utf-8") as f: with open("data/seeds/seeds_dataset.txt", "r", encoding="utf-8") as f:
data = f.readlines() data = f.readlines()
df = pd.DataFrame([d.split() for d in data]) df = pd.DataFrame([d.split() for d in data])
@@ -33,80 +26,103 @@ def get_data():
] ]
df = df.astype(float) df = df.astype(float)
df["target"] = df["target"].astype(int) df["target"] = df["target"].astype(int) - 1
x = df.iloc[:, :-1].values.round(0).astype(int) x = df.iloc[:, :-1].values.astype(np.float32)
y = df.iloc[:, -1].values y = df.iloc[:, -1].values.astype(np.int64)
y_class = to_categorical(y)
x_train, x_test, y_train, y_test = train_test_split( x_train, x_test, y_train, y_test = train_test_split(
x, y_class, test_size=0.2, shuffle=True x, y, test_size=0.2, shuffle=True, random_state=seed
)
scaler = StandardScaler()
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
return (
torch.tensor(x_train, dtype=torch.float32),
torch.tensor(y_train, dtype=torch.int64),
torch.tensor(x_test, dtype=torch.float32),
torch.tensor(y_test, dtype=torch.int64),
) )
return x_train, y_train, x_test, y_test
def make_model(seed: int = 42):
torch.manual_seed(seed)
return nn.Sequential(
nn.Linear(7, 16),
nn.ReLU(),
nn.Linear(16, 32),
nn.ReLU(),
nn.Linear(32, 3),
)
def make_model(): def main():
model = Sequential() parser = argparse.ArgumentParser(description="PSO Seeds Benchmark Script")
model.add(Dense(16, activation="relu", input_shape=(7,))) add_pso_args(
model.add(Dense(32, activation="relu")) parser,
model.add(Dense(4, activation="softmax")) defaults={
"method": "original",
"initialization": "model_noise",
"evaluation": "full",
"convergence": "particle_reset",
"refinement": "adam",
"n_particles": 24,
"c0": None,
"c1": None,
"w_min": None,
"w_max": None,
"negative_swarm": 0.0,
"mutation_swarm": 0.3,
"particle_min": -3.0,
"particle_max": 3.0,
"velocity_limit_ratio": 0.1,
"boundary_strategy": "reflect",
"seed": 42,
"epochs": 80,
"renewal": "acc",
"output_dir": "output/seeds",
"checkpoint_interval": 25,
"refinement_epochs": 10,
"refinement_lr": 0.001,
},
)
args = parser.parse_args()
return model model = make_model(seed=args.seed)
x_train, y_train, x_test, y_test = get_data(seed=args.seed)
fitness_size = args.fitness_size if args.evaluation == "fixed_subset" else None
refinement_epochs = args.refinement_epochs if args.refinement == "adam" else 0
kwargs = build_optimizer_kwargs(
args,
model=model,
loss=nn.CrossEntropyLoss(),
task="multiclass",
inertia_profile={"c0": 0.5, "c1": 1.0, "w_min": 0.7, "w_max": 1.2},
)
pso_seeds = Optimizer(**kwargs)
print(f"Optimizer device: {pso_seeds.device}")
best_score = pso_seeds.fit(
x_train,
y_train,
epochs=args.epochs,
batch_size=args.batch_size,
fitness_size=fitness_size,
renewal=args.renewal,
validation_data=(x_test, y_test),
output_dir=args.output_dir,
checkpoint_interval=25,
save_info=True,
refinement_epochs=refinement_epochs,
refinement_lr=args.refinement_lr,
)
print(f"Done! Best score: {best_score}")
# %% if __name__ == "__main__":
model = make_model() main()
x_train, y_train, x_test, y_test = get_data()
loss = [
"mean_squared_error",
"categorical_crossentropy",
"sparse_categorical_crossentropy",
"binary_crossentropy",
"kullback_leibler_divergence",
"poisson",
"cosine_similarity",
"log_cosh",
"huber_loss",
"mean_absolute_error",
"mean_absolute_percentage_error",
]
# rs = random_state()
pso_mnist = optimizer(
model,
loss="categorical_crossentropy",
n_particles=100,
c0=0.5,
c1=1.0,
w_min=0.7,
w_max=1.2,
negative_swarm=0.0,
mutation_swarm=0.3,
convergence_reset=True,
convergence_reset_patience=10,
convergence_reset_monitor="mse",
convergence_reset_min_delta=0.0005,
)
best_score = pso_mnist.fit(
x_train,
y_train,
epochs=500,
save_info=True,
log=2,
log_name="seeds",
renewal="acc",
check_point=25,
empirical_balance=False,
dispersion=False,
back_propagation=False,
validate_data=(x_test, y_test),
)
print("Done!")
sys.exit(0)

Some files were not shown because too many files have changed in this diff Show More