INR-Robustness [ICML 2026]
A research framework for studying adversarial robustness of Implicit Neural Representation (INR)-based classifiers. This project systematically evaluates how classifiers operating on INR weight-space features respond to adversarial attacks across multiple threat models — image space, INR weight space, and embedding space.
By: Jayoung Kim, Kookjin Lee, Noseong Park, and Sanghyun Hong
- Overview
- Project Structure
- Installation
- Data Preparation
- Running Experiments
- Configuration
- Victim Models
- Attack Types
- Defense Mechanisms
- INR Configuration
- Results
- Changelog
INR-based classifiers encode images as neural network weights (SIRENs or Fourier Feature Networks) and classify them using architectures designed for weight-space or embedding-space processing. This project attacks these classifiers from three threat models:
| Threat Model | Description |
|---|---|
| Image space | Adversarial perturbations applied to raw images before INR fitting |
| INR weight space | Perturbations applied directly to fitted SIREN/FFN weights |
| Embedding space | Perturbations applied to latent embeddings extracted from INRs |
Supported victim models: NFT, NFN, DWS, ScaleGMN, MWT, INR2Vec
Supported datasets: MNIST, Fashion-MNIST, CIFAR-10
INR types: SIREN, FFN (Fourier Feature Networks)
INR-Robustness/
├── environment.yml # Conda environment
├── fourier-feature-networks/ # FFN generation scripts
│ ├── make_ffn.py
│ └── run_make_ffn.sh
└── src/
├── attacks/ # Attack implementations
│ ├── run_attacks.py # Main Attacks orchestrator class
│ ├── attack_methods.py # Attack function implementations
│ ├── model_loading.py # Victim model loading utilities
│ ├── utils.py # BatchedINRBuilder, WeightSpaceFeatures
│ ├── evaluation.py # Evaluation metrics
│ ├── training.py # Training utilities
│ ├── data_loading.py # Dataset loaders
│ ├── hypernet/ # Hypernetwork-based attack
│ ├── surrogate/ # Surrogate model training
│ │ ├── train_surrogate.py # SurrogateTrainer (MLP, FD loss, noise augmentation)
│ │ └── utils.py # SurrogateAttacker wrapper
│ ├── transfer/ # Transfer attack training
│ └── torchattacks/ # PGD variants (Linf, L2, L1, weight-space, embedding)
├── basemodels/ # Victim model architectures
│ ├── nft.py # Neural Field Transformer
│ ├── nfn.py # Neural Functional Networks
│ ├── dws.py # Deep Weight Space
│ ├── scalegmn.py # ScaleGMN (graph neural network)
│ ├── mwt.py # Meta-Weight Transformer
│ ├── inr2array.py # INR2Array encoder/decoder
│ ├── inr2vec/ # INR2Vec model
│ └── configs/ # Per-model YAML configs
├── defences/ # Defense mechanisms
├── experiments/
│ ├── suite_runner.py # Experiment suite orchestrator
│ ├── run.sh # Shell runner script
│ └── configs/
│ └── suites/
│ ├── exp1/ # Base experiments (all models × datasets × attacks)
│ ├── exp2/ # Ablation studies (INR config, embedding dim, #INRs)
│ ├── exp3/ # Comprehensive cross-attack experiments
│ ├── exp4/ # Attack space analysis
│ ├── exp5/ # Adversarial training experiments
│ └── transferability/ # Cross-model transferability
├── inr/ # INR generation scripts
└── utils/
├── path_config.py # Centralized path management
├── inr_config.py # INR configuration dataclass
├── config_utils.py # YAML config loading (DictConfig)
├── results_collector.py # CSV results aggregation
└── wandb_manager.py # Weights & Biases integration
conda env create -f environment.yml
conda activate inrKey dependencies: Python 3.11, PyTorch (CUDA 12.4), torch_geometric, perceiver_pytorch, random-fourier-features-pytorch, wandb, einops, scikit-learn.
Run the INR generation pipeline before any experiments. Each dataset requires three initialization strategies, which are used by different victim models.
| Init mode | Used by | Description |
|---|---|---|
shared_init |
INR2Vec | Single shared initialization; all images fitted from the same starting point |
init_pool |
NFN, NFT | Pool of N random inits; each image picks one at random |
random_init |
DWS, ScaleGMN | Independent random init per image; test samples reuse a training init |
Submit all generation jobs (3 datasets × 3 init modes = 9 Slurm jobs):
# All datasets (mnist, fashion, cifar)
bash slurm/make_inrs.sh
# Single dataset only
bash slurm/make_inrs.sh mnistMonitor progress:
squeue -u $USERGenerated files are saved to:
{NFS_ROOT}/assets/sirens/depth1_width32_scale30/{dataset}/
inits/shared_init/shared_init.pth # 1 file
inits/init_pool/pool_id{0..N}.pth # N files (10 for mnist/fashion, 20 for cifar)
{init_mode}_{label}s/net{global_idx}.pth # 70k files (mnist/fashion) or 60k (cifar) per mode
Verify generation completeness:
python src/inr/verify_inrs.py # all datasets × all modes
python src/inr/verify_inrs.py --dataset mnist --init_mode shared_initPre-fitted SIREN weight datasets are available from Google Drive. Place them under the project root:
{base_path}/assets/sirens/depth{D}_width{W}_scale{S}/{dataset}/
{base_path}/assets/ffns/{dataset}/
SIREN configurations: depth ∈ {0,1,3}, width ∈ {16,32,64}, scale ∈ {10,30,60}
Place checkpoints under:
{base_path}/assets/basemodels/{model}/{dataset}/{inr_config}/
For adversarially-trained variants:
{base_path}/assets/basemodels/{model}/{dataset}/{inr_config}/at_{at_type}/
The project root (base_path) is resolved in order of priority:
- CLI
--config_base_path(highest priority): thesrc/directory path. The project root is automatically derived asos.path.dirname(config_base_path). - YAML
defaults.base_path: used as fallback if--config_base_pathis not set.
The suite runner is the primary way to run experiments. It reads a YAML config, expands it into a list of individual experiments, and runs them sequentially.
# Run by experiment number and model name
# Loads: src/experiments/configs/suites/exp{N}/exp{N}_{name}.yaml
python src/experiments/suite_runner.py --exp 1 --name nft
# Run a specific named config (no --exp, loads from configs/ directly)
python src/experiments/suite_runner.py --name suites/exp2/num_inrs/dws_mnist
# Enable WandB logging
python src/experiments/suite_runner.py --exp 1 --name dws --wandb
# Override project root (src/ path → parent is the project root)
python src/experiments/suite_runner.py --exp 1 --name nft --config_base_path /my/project/src
# Dry-run: print experiment plan without executing
python src/experiments/suite_runner.py --exp 1 --name nft --dry-runPath resolution:
config_base_path (default: auto-detected src/ dir)
└── experiments/configs/suites/exp{N}/exp{N}_{name}.yaml ← suite config
└── exp_results/exp{N}/ ← results output
base_path = os.path.dirname(config_base_path) ← project root
└── assets/sirens/... ← INR data
└── assets/basemodels/... ← model checkpoints
CLI Arguments:
| Argument | Default | Description |
|---|---|---|
--exp |
— | Experiment number (1–5). Loads configs/suites/exp{N}/exp{N}_{name}.yaml |
--name |
i2i-fd |
Suite config name or relative path to config |
--config_base_path |
auto-detected src/ path |
Path to src/. Project root = parent dir |
--wandb |
disabled | Enable Weights & Biases experiment tracking |
--dry-run |
disabled | Print experiment plan without executing |
# bash src/experiments/run.sh <device_id> <exp_num> <model_name>
bash src/experiments/run.sh 0 1 nft
bash src/experiments/run.sh 1 2 dwsLogs are written to logs/exp_{N}_{name}.log.
from src.attacks.run_attacks import Attacks
mgr = Attacks(
attack_space="image", # "image", "inr", or "embedding"
attack_type="gaussian",
dataset_name="cifar", # "mnist", "fashion", "cifar"
victim_model="nft", # "nft", "nfn", "dws", "scalegmn", "mwt", "inr2vec"
base_path="/path/to/project/root",
)
result = mgr.run(eps=0.03, steps=100)
# result: {"clean_acc": ..., "adv_acc": ..., "total": ...}suite_name: exp1_nft
defaults:
base_path: /path/to/project/root # Overridden by --config_base_path CLI arg
inr_config:
depth: 1
width: 32
scale: 30
inr_type: siren # "siren" or "ffn"
models: [nft]
datasets: [mnist, fashion, cifar]
attacks:
# Gaussian noise baseline
- attack_space: image
attack_type: gaussian
params: {}
# Transfer attack via ResNet18 surrogate
- attack_space: image
attack_type: transfer
params:
eps: 0.3
steps: 100
norm: Linf
# Surrogate-based image attack (NI-GBMS adaptation)
- attack_space: image
attack_type: i2i-fd
params:
eps: 0.3 # L∞ budget in [0,1] scale (internally scaled ×2 for [-1,1] images)
steps: 100 # PGD steps (default: 100)
n_restarts: 5 # Multi-restart PGD, keep per-sample worst case (default: 5)
aug_eps: 0.6 # Noise augmentation for surrogate OOD robustness (default: 0.0)
norm: Linf
# Direct PGD in INR weight space with image constraint
- attack_space: inr
attack_type: inr
params:
eps: 0.01
steps: 100
proj_mode: icop # "weight_linf", "image_linf", or "icop"
results:
csv_name: exp1_nft_results.csvThe suite runner selects a generation strategy based on keys present in the YAML:
| YAML Key | Mode | Description |
|---|---|---|
inr_ablations |
INR Ablation | Sweep over multiple INR configs (depth/width/scale) |
attack_ablations |
Attack Ablation | Parameter sweep for a single attack type |
defenses |
Defense | Cross-product with defense mechanisms |
embedding_dims |
Embedding Dim | Latent dimension ablation (NFT only) |
inr_type: ffn |
FFN | Experiments using Fourier Feature Networks |
| (none of the above) | Standard | Cartesian product: models × datasets × attacks |
| Model | Space | Description |
|---|---|---|
| NFT | Embedding | Neural Field Transformer. AutoEncoder maps INR → latent vector; Transformer classifies. |
| NFN | Weight | Neural Functional Networks. Permutation-equivariant processing of weight-space features (weights + biases). |
| DWS | Weight | Deep Weight Space. Set-abstraction layers for permutation-invariant weight classification. |
| ScaleGMN | Weight (graph) | Graph Neural Network over the computational graph of the INR. |
| MWT | Meta-learning | Meta-Weight Transformer. Meta-learner that adapts to new INRs in a few-shot manner. |
| INR2Vec | Embedding | Learns a vector embedding of INR weights; classifies with an MLP head. |
attack_type |
Description | Key Params |
|---|---|---|
gaussian |
Gaussian noise added to raw images | eps, norm |
transfer |
PGD attack optimized against a ResNet18 surrogate | eps, steps, norm |
e2e |
End-to-end PGD through the MWT meta-learner | eps, steps |
These attacks train an MLP surrogate S: image → INR weights (or embedding) and use it as a differentiable proxy for PGD. The surrogate is trained with:
- Reconstruction loss —
||S(x) - θ*(x)||² - Derivative matching (FD) —
||J_S(x)·J_render·Δθ - Δθ||², adapted from NI-GBMS (arXiv:2405.02952) to trainJ_S ≈ dθ*/dx - Classification loss —
CE(victim(S(x)), y)
| Parameter | Default | Description |
|---|---|---|
eps |
dataset default | L∞ perturbation budget in [0,1] scale (scaled ×2 internally for [-1,1] images) |
steps |
100 | PGD steps |
n_restarts |
5 | Multi-restart PGD — runs n_restarts times from random start, keeps per-sample worst case |
norm |
Linf |
Perturbation norm (Linf, L2, L1) |
aug_eps |
0.0 | Noise augmentation eps for surrogate training. Trains J_S(x+noise) ≈ J_fit to reduce OOD error at adversarial inputs. Set to 2*eps (i.e., 0.6 for MNIST) for best effect. |
lam_inr |
1.0 | Weight for reconstruction loss |
lam_der |
1.0 | Weight for derivative matching loss |
lam_cls |
1.0 | Weight for classification loss |
epochs |
100 | Surrogate training epochs |
attack_type |
Description | Key Params |
|---|---|---|
i2i-naive |
Gaussian or L∞-bounded perturbation of SIREN weights | eps |
i2i-fd |
Feature deception: optimize INR weights to fool classifier while preserving image reconstruction | eps, steps, aug_eps |
i2i-hypernet |
Hypernetwork trained to generate adversarial SIREN weights | eps, steps, latent_dim |
inr |
Direct PGD on WeightSpaceFeatures (PGDWSFeat) |
eps, steps, proj_mode |
proj_mode options for inr attack:
| Mode | Description |
|---|---|
weight_linf |
Project perturbation to L∞ ball in weight space |
image_linf |
Project to L∞ ball in rendered image space (iterative shrink) |
icop |
ICOP: classification gradient orthogonalized w.r.t. image-space constraint gradient |
attack_type |
Description | Key Params |
|---|---|---|
i2e-naive |
Optimize embedding to fool classifier (std-normalized ε) | eps, steps |
i2e-fd |
Feature deception in embedding space with reconstruction constraint | eps, steps, aug_eps |
embedding |
Direct PGD in std-normalized embedding space (PGDEmbedding) |
eps, steps, norm |
Norm options: Linf, L2, L1
Default epsilon values by dataset: MNIST → 0.3, Fashion-MNIST → 0.1, CIFAR-10 → 0.03
Note: Epsilon values are in
[0,1]pixel scale. Internally, images are normalized to[-1,1], so the actual PGD budget iseps × 2.
Model × Attack Space compatibility:
| Model | Image | INR weight | Embedding |
|---|---|---|---|
| NFT | ✓ | ✓ | ✓ |
| NFN | ✓ | ✓ | — |
| DWS | ✓ | ✓ | — |
| ScaleGMN | ✓ | ✓ | — |
| MWT | gaussian, transfer only |
— | — |
| INR2Vec | ✓ | ✓ | ✓ |
Defenses are specified via the defenses key in the suite YAML or via mgr.set_defense_config().
| Defense | Type | Description |
|---|---|---|
gaussian_noise |
Preprocessing | Adds Gaussian noise to inputs before classification |
diffusion |
Preprocessing | Diffusion model denoiser applied to INR weights |
embedding_at |
Adversarial Training | Robust training with adversarial perturbations in embedding space |
inr_at |
Adversarial Training | Robust training with adversarial perturbations in INR weight space |
from src.utils.inr_config import INRConfig
# SIREN
cfg = INRConfig(depth=1, width=32, scale=30, inr_type="siren", augment_count=1)
# FFN
cfg = INRConfig(inr_type="ffn")| Parameter | Values | Description |
|---|---|---|
depth |
0, 1, 3 | Number of hidden layers |
width |
16, 32, 64 | Hidden features per layer |
scale |
10, 30, 60 | first_omega_0 frequency scaling factor |
augment_count |
1, 5, 10, 20 | Number of augmented INRs fitted per image |
training_steps |
int | SIREN fitting iterations (default: 5000) |
inr_type |
"siren", "ffn" |
INR representation type |
Asset paths:
{base_path}/assets/sirens/depth{D}_width{W}_scale{S}/{dataset}/ # SIREN
{base_path}/assets/ffns/{dataset}/ # FFN
Results are saved as CSV files under:
{config_base_path}/exp_results/{exp_name}/{suite_name}_results.csv
Each row contains the full experiment configuration and the following metrics:
| Column | Description |
|---|---|
clean_acc |
Accuracy on unperturbed inputs |
adv_acc |
Accuracy on adversarial inputs |
total |
Total number of evaluated samples |
training_time_sec |
Wall-clock time for the experiment |
status |
"success" or error message |
With --wandb, runs are grouped by suite name and tagged with model, dataset, and attack type for easy filtering in the WandB dashboard.
Please cite our work if you find this source code helpful.
@inproceedings{kim2026adversarial,
title={Adversarial Robustness of Implicit Neural Representation-Based Classifiers},
author={Jayoung Kim and Kookjin Lee and Noseong Park and Sanghyun Hong},
booktitle={Forty-third International Conference on Machine Learning},
year={2026},
url={https://openreview.net/forum?id=TEcKvsdzA3}
}
Please contact Jayoung Kim (jayoung.kim@kaist.ac.kr) and Sanghyun Hong (sanghyun.hong@oregonstate.edu) for any questions and recommendations.