OpenAMC is a PyTorch framework for automatic modulation recognition (AMC), adversarial attacks, and adversarial defenses. This release contains the code for three research works:
- FIM — Fading-Invariant Adversarial Attacks on Neural Modulation Recognition, published at ICASSP 2025. FIM uses a Neural Inverse Model (NIM) and transmit-power estimation to improve adversarial attacks under random wireless channel effects.
- MVRG — Transferable Adversarial Attacks on Automatic Modulation Classification via Multi-Variance-Reduced Gradients, submitted to ICASSP 2027. Authors: Kun He, Gao Liu, and Xinze Zhang. MVRG combines Nesterov look-ahead gradients, neighborhood variance tuning, and temporal Gaussian smoothing to improve transferability across AMC architectures.
- TFI — Time-Frequency Interactive Cross-Architecture Transfer Attack Method for Automatic Modulation Classification, submitted to TIFS. TFI combines Multi-scale Gradient Generalization (MGG) and Shrinkage-Shift Regularization (SSR) to improve transferability across heterogeneous AMC models.
Throughout this repository, the ICASSP 2025 method is named FIM and its learned inverse component is named NIM, matching the published paper.
- Xinze Zhang, Dengao Zhu, Xiyao Dong, and Kun He, “Fading-Invariant Adversarial Attacks on Neural Modulation Recognition,” Proceedings of the IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), 2025. DOI: 10.1109/ICASSP49660.2025.10890846.
- Kun He, Gao Liu, and Xinze Zhang, “MVRG: Transferable Adversarial Attacks on Automatic Modulation Classification via Multi-Variance-Reduced Gradients,” submitted to ICASSP 2027.
- Kun He, Gao Liu, Xinze Zhang, and Shuo Zhang, “TFI: Time-Frequency Interactive Cross-Architecture Transfer Attack Method for Automatic Modulation Classification,” submitted to TIFS.
Python 3.10--3.13 is supported. We recommend uv:
git clone https://github.com/XinzeZhang/OpenAMC.git
cd OpenAMC
pip install uv
uv syncYou can replace uv run python in the commands below with the Python executable
from another environment that contains the dependencies in pyproject.toml.
Large datasets and model checkpoints are not stored in Git.
Temporary download links -- replace these before the final release:
- Processed datasets: OpenAMC postdata (temporary)
- Pretrained checkpoints: OpenAMC checkpoints (temporary)
After downloading and extracting them, the expected layout is:
OpenAMC/
├── data/
│ └── postdata/
│ ├── RML2016.10a_dict.split.pt
│ └── RML2016.10b_dict.split.pt
└── checkpoints/
├── RML2016.10a/
│ └── nature/
│ ├── RML2016.10a_mcd.best.pt
│ ├── RML2016.10a_awn.best.pt
│ └── ...
└── RML2016.10b/
└── nature/
└── ...
For development or testing, use symbolic links instead of copying large files:
ln -s /absolute/path/to/downloaded/postdata data/postdata
ln -s /absolute/path/to/downloaded/checkpoints checkpointsBoth paths are ignored by Git. Confirm the links before running an experiment:
test -f data/postdata/RML2016.10a_dict.split.pt
test -f checkpoints/RML2016.10a/nature/RML2016.10a_mcd.best.ptCommon dataset identifiers are:
| CLI name | Dataset |
|---|---|
a |
RML2016.10a |
b |
RML2016.10b |
c |
RML2016.04c |
h |
HisarMod2019.1 |
p |
Panoradio.HF |
dr2 |
MIMO.Nt4Nr2 |
dr4 |
MIMO.Nt16Nr4 |
dr16 |
MIMO.Nt64Nr16 |
rml18a |
RML2018.01a |
Common model identifiers include mcd, awn, amcnet, ctdnn, mcl,
res, and vtcnn. The complete registries are in data/__init__.py and
models/__init__.py. Dataset-specific checkpoint paths are configured in
exp/attack/config/.
The public attack registry is intentionally limited to methods evaluated in the released papers:
- FIM:
fgmandpgd, plus the channel-independentpcaandvaebaselines. “Vanilla” in the FIM paper means transmitting the unmodified receiver-side perturbation and is not a separate attack class. - MVRG:
bim,mi,ni,vtmi,vtni,sfaa,feig,fciaa,pngd,mdam, andmvrg. - TFI uses the same ten reported baselines and is registered as
tfi.
The registry keys follow the paper names; in particular, vtmi, vtni, and fciaa correspond to VT-MI, VT-NI, and FCIAA. Implementations unrelated to these papers are not distributed.
Transfer attacks should be compared on the same inputs. OpenAMC can construct a balanced attack set containing only signals that every model in a supplied model list classifies correctly. Selection is performed independently at each SNR, then balanced to the same number of samples for every modulation class and SNR condition.
The MVRG reproduction and attack-set defaults are defined in exp/attack/mvrg.py; the shared taskAttack/Parser.py remains method-agnostic. Running the generator without construction arguments uses these paper-aligned defaults: RML2016.10b, models amcnet awn ctdnn mcd mcl msmc res, SNR bounds 4--25 (the available RML2016.10b SNRs are 4--18), 100 samples per class, the test split, seed 2022, and batch size 512. CUDA remains opt-in. Thus, the shortest GPU command is:
uv run python -m taskAttack.make_attackset -cuda -gid 0The same settings can be written explicitly as:
uv run python -m taskAttack.make_attackset \
-data b \
--models amcnet awn ctdnn mcd mcl msmc res \
--snr-range 4 18 \
--samples-per-class 100 \
--data-tag test \
-batch_size 512 \
-cuda -gid 0The default outputs are:
data/postdata/attack.RML2016.10b_dict.pt
data/postdata/attack.RML2016.10b_dict.pt.json
The .pt file contains a dictionary keyed by SNR. Each value is a tuple of
(signals, labels, original_indices) and is directly consumable by OpenAMC.
The adjacent JSON file records the model list, SNRs, classes, sample count, and
random seed used to construct it.
Configured checkpoints under checkpoints/<dataset>/nature/ are loaded by
default. A checkpoint can be overridden for any model, including models with
custom checkpoint locations:
uv run python -m taskAttack.make_attackset \
-data b --models awn mcd --snr-range 4 18 \
--checkpoint awn=/absolute/path/to/awn.pt \
--checkpoint mcd=/absolute/path/to/mcd.pt \
--samples-per-class 100 -cuda -gid 0Use a constructed attack set by adding -attackset attack to an MVRG or other
standard attack command:
uv run python -m exp.attack.mvrg \
-data b -snr 4 18 -attackset attack \
-surrogate_model awn -target_model mcd \
-bound psr -psr -10 -cuda -gid 0For the HisarMod2019.1 setting in the MVRG manuscript, use -data h, SNR range
4 18, and --samples-per-class 5.
For a TFI attack set, select the TFI profile. It uses all seven paper models, a cap of 100 samples per SNR/class cell, and rounds the common cell size down to a multiple of five. Panoradio.HF starts at 10 dB; the other five paper datasets start at 4 dB.
uv run python -m taskAttack.make_attackset --profile tfi -data b -cuda -gid 0Use -data h, -data p, -data dr2, -data dr4, or -data dr16 to construct the corresponding paper attack set.
MVRG is the method from the manuscript “MVRG: Transferable Adversarial
Attacks on Automatic Modulation Classification via Multi-Variance-Reduced
Gradients,” submitted to ICASSP 2027. It is implemented in
taskAttack/attackmethods/gradient/mvrg.py and registered under the attack
name mvrg. It uses the ordinary attack wrapper; no propagation channel is
modeled for MVRG perturbations.
uv run python -m exp.attack.mvrg \
-data a \
-snr 10 \
-surrogate_model mcd \
-target_model mcd \
-bound psr \
-psr -10 \
-batch_size 512 \
-cuda -gid 0Useful options:
-snr 10: attack one SNR condition.-snr 0 18: attack every available SNR in the inclusive range.-snr all: attack all SNR conditions in the processed dataset.-bound psr -psr -10: constrain perturbation-to-signal ratio to -10 dB.-bound pnr -pnr 0: constrain perturbation-to-noise ratio instead.-surrogate_ckp PATHand-target_ckp PATH: override configured checkpoints.
The manuscript settings are 30 update iterations, momentum decay 1.0, 20
neighboring samples, Gaussian scale 2.0, and randomly selected odd kernel
widths from {3, 5, ..., 63}. These values can be overridden programmatically
through Attack.conduct(..., algo_configs={...}).
This verifies the full data/model/attack path with one update and one neighbor:
import sys
sys.argv = [
"mvrg-smoke", "-data", "a", "-snr", "10",
"-surrogate_model", "mcd", "-target_model", "mcd",
"-algo", "mvrg", "-bound", "psr", "-psr", "-10",
"-batch_size", "12000", "-cuda", "-gid", "0", "-test",
]
from taskAttack.Parser import get_parser
from taskAttack.Wrapper import Attack
args, parser = get_parser()
Attack(args, parser).conduct(
eval=False,
algo_configs={
"epoch": 1,
"num_neighbor": 1,
"window_sizes": [11],
"alpha": 0.01,
},
)TFI is the method from “TFI: Time-Frequency Interactive Cross-Architecture Transfer Attack Method for Automatic Modulation Classification.” It is implemented in taskAttack/attackmethods/gradient/tfi.py and registered as tfi. The launcher keeps all paper-specific defaults in exp/attack/tfi.py, leaving the shared parser method-agnostic.
TFI uses 10 iterations with step size epsilon / 10. The dataset-specific momentum, SSR shrinkage, MGG scale count, and scale interval are selected automatically by the launcher.
uv run python -m exp.attack.tfi \
-data b -snr all -attackset attack \
-surrogate_model ctdnn -target_model awn \
-bound psr -psr -10 -batch_size 512 -cuda -gid 0A reduced smoke run can use the ordinary test split and one SNR; programmatic callers may override epoch through Attack.conduct(..., algo_configs={"epoch": 1}).
FIM is the method from “Fading-Invariant Adversarial Attacks on Neural
Modulation Recognition,” published at ICASSP 2025. It is implemented in
taskAttack/channelAttack/fim.py. It wraps any existing receiver-side attack,
such as FGM or PGD, then:
- Generates receiver-side adversarial perturbations.
- Samples fading channels and constructs
(delta, delta / h)pairs. - Trains or loads the Neural Inverse Model (NIM).
- Uses pilot-based transmit-power estimation when
-reboundis enabled. - Evaluates clean, ideal received, channel-faded, and FIM perturbations.
The NIM implementation is in
taskAttack/channelAttack/neural_inverse_model.py. The ordinary attack and
defense wrappers are unchanged; channel effects are used only by the FIM/channel
wrapper.
uv run python -m exp.attack.fim \
-data a \
-snr 10 \
-algo pgd \
-surrogate_model mcd \
-target_model mcd \
-bound psr \
-psr -10 \
-batch_size 500 \
-nim_model nim \
-nim_seed 2024 \
-nim_num_channels 200 \
-nim_epochs 20 \
-nim_patience 50 \
-nim_batch_size 256 \
-channel_seed 8000 \
-num_channel 30 \
-pilot_seed 3030 \
-pilot_n_channel 200 \
-rebound \
-cuda -gid 0To reproduce the FGM-based setting, change -algo pgd to -algo fgm.
Important FIM/channel options:
| Option | Meaning | Default |
|---|---|---|
-nim_num_channels |
Sampled channel states used to build NIM pairs | 200 |
-nim_epochs |
Maximum NIM training epochs | 20 |
-num_channel |
Random channel realizations used for evaluation | 30 |
-rebound |
Enable transmit-power estimation/amplification | off |
-use_cpls |
Combined path-loss and shadowing channel | on |
-use_rayleigh |
Rayleigh fading channel | on |
-channel_seed |
First evaluation-channel seed | 2024 |
-pilot_seed |
First power-estimation pilot seed | 3030 |
Use a small number of sampled/evaluation channels and one NIM epoch:
uv run python -m exp.attack.fim \
-data a -snr 10 -algo fgm \
-surrogate_model mcd -target_model mcd \
-bound psr -psr -10 -batch_size 12000 \
-nim_num_channels 1 -nim_epochs 1 -nim_patience 1 \
-nim_batch_size 12000 -num_channel 1 -pilot_n_channel 1 \
-rebound -cuda -gid 0 -testFIM saves the NIM checkpoint under the experiment output tree. A repeated run with the same settings loads that checkpoint automatically.
Normal runs write under yield_results/; runs with -test write under
yield_test/. Both are ignored by Git. Important files include:
yield_results/<experiment>/<dataset>/surrogate/<model>/<attack>_results/
├── result.snr<SNR>.pt
├── target.<model>/
│ ├── evaluation.log
│ ├── clean/
│ ├── adv.rece/
│ ├── adv.trans/
│ └── adv.fim/
└── NIM.<model>/snr<SNR>/
For ordinary TFI or MVRG evaluation, the target directory contains clean and
adversarial accuracy tensors and logs. For FIM, adv.rece is the ideal attack
without channel effects, adv.trans is the uncorrected over-the-air attack,
and adv.fim is the FIM result.
- Checkpoint load fails: verify the filename and dataset directory under
checkpoints/<dataset>/nature/, or pass-surrogate_ckp/-target_ckp. - Dataset file is missing: verify the
data/postdatalink and the processed filename expected bydata/RML.py. - CUDA out of memory: lower
-batch_size; FIM also benefits from lowering-nim_batch_size. - A quick run takes too long: use the smoke-test settings, especially one SNR, one iteration for TFI, one neighboring sample for MVRG, or one channel/NIM epoch for FIM.
- Reproducing a result: keep attack, NIM, pilot, and evaluation channel seeds fixed and record the exact checkpoint files used.