Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Radio Frequency Machine Learning (RFML) in PyTorch

The concept of deep learning has revitalized machine learning research in recent years. In particular, researchers have demonstrated the use of deep learning for a multitude of tasks in wireless communications, such as signal classification and cognitive radio. These technologies have been colloquially coined Radio Frequency Machine Learning (RFML) by the Defense Advanced Research Projects Agency (DARPA). This repository hosts two key components to enable you to further your RFML research: a library with PyTorch implementations of common RFML networks, wrappers for downloading and utilizing an open source signal classification dataset, and adversarial evasion and training methods along with multiple tutorial notebooks for signal classification, adversarial evasion, and adversarial training.

LicenseUses Python 3Deep Learning by PyTorchBLACK_BADGE


rfml.attack
Implementation of the Fast Gradient Sign Method (FGSM) and Projected Gradient Descent (PGD) that are aware of signal-to-perturbation ratios
rfml.data
Classes for creating datasets from raw-IQ samples, splitting amongst training/validation/test datasets while keeping classes and signal-to-noise ratios (SNR) balanced, and converting into a PyTorch TensorDataset
rfml.data.converters
Wrappers to load open source datasets (including downloading them from the internet if necessary) from DeepSig, Inc
rfml.nn.eval
Compute Top-K accuracy (overall and vs SNR) and confusion matrices from the models and datasets contained in this library
rfml.nn.model
Implementations of state of the art signal classification deep neural networks (DNNs) in PyTorch
rfml.nn.train
Implementation of standard training and adversarial training algorithms for classification problems in PyTorch
rfml.ptradio
PyTorch implementations of linearly modulated modems (such as PSK, QAM, etc) and simple channel models

The rfml library can be installed directly from pip (for Python >= 3.5).

pip install git+https://github.com/brysef/rfml.git@1.0.1

If you plan to directly edit the underlying library then you can install the library as editable after cloning this repository.

git clone git@github.com:brysef/rfml.git # OR https://github.com/brysef/rfml.git
pip install --user -e rfml/
Click to Expand

The following code (located at examples/signal_classification.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels
  • Create a Convolutional Neural Network model with PyTorch
  • Train the model to perform modulation classification
  • Evaluate the model on the test set in terms of overall accuracy, accuracy vs SNR, and a confusion matrix amongst classes
  • Save the model weights for later use
1fromrfml.dataimportbuild_dataset2fromrfml.nn.evalimport (
3compute_accuracy,
4compute_accuracy_on_cross_sections,
5compute_confusion,
6 )
7fromrfml.nn.modelimportbuild_model8fromrfml.nn.trainimportbuild_trainer, PrintingTrainingListener910train, val, test, le=build_dataset(dataset_name="RML2016.10a")
11model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
12trainer=build_trainer(
13strategy="standard", max_epochs=3, gpu=True14 ) # Note: Disable the GPU here if you do not have one15trainer.register_listener(PrintingTrainingListener())
16trainer(model=model, training=train, validation=val, le=le)
17acc=compute_accuracy(model=model, data=test, le=le)
18acc_vs_snr, snr=compute_accuracy_on_cross_sections(
19model=model, data=test, le=le, column="SNR"20 )
21cmn=compute_confusion(model=model, data=test, le=le)
2223# Calls to a plotting function could be inserted here24# For simplicity, this script only prints the contents as an example25print("===============================")
26print("Overall Testing Accuracy: {:.4f}".format(acc))
27print("SNR (dB)\tAccuracy (%)")
28print("===============================")
29foracc, snrinzip(acc_vs_snr, snr):
30print("{snr:d}\t{acc:0.1f}".format(snr=snr, acc=acc*100))
31print("===============================")
32print("Confusion Matrix:")
33print(cmn)
3435model.save("cnn.pt")

Running the above code will produce an output similar to the following. Additionally, the weights file will be saved off (cnn.py) along with a local copy of the RML2016.10a dataset (RML2016.10a.*).

> python3 signal_classification.py
.../rfml/data/converters/rml_2016.py:42: UserWarning:
About to attempt downloading the RML2016.10A dataset from deepsig.io/datasets.
Depending on your network connection, this process can be slow and error prone. Any
errors raised during network operations are not silenced and will therefore cause your
code to crash. If you require robustness in your experimentation, you should manually
download the file locally and pass the file path to the load_RML201610a_dataset
function.
Further, this dataset is provided by DeepSig Inc. under Creative Commons Attribution
- NonCommercial - ShareAlike 4.0 License (CC BY-NC-SA 4.0). By calling this function,
you agree to that license -- If an alternative license is needed, please contact DeepSig
Inc. at info@deepsig.io
warn(self.WARNING_MSG)
Epoch 0 completed!
-Mean Training Loss: 1.367
-Mean Validation Loss: 1.226
Epoch 1 completed!
-Mean Training Loss: 1.185
-Mean Validation Loss: 1.180
Epoch 2 completed!
-Mean Training Loss: 1.128
-Mean Validation Loss: 1.158
Training has Completed:
=======================
Best Validation Loss: 1.158
Best Epoch: 2
Total Epochs: 2
=======================
===============================
Overall Testing Accuracy: 0.6024
SNR (dB) Accuracy (%)
===============================
-4 72.3
16 82.8
-12 25.2
10 84.0
-8 49.8
-10 34.8
-14 19.0
18 83.0
-6 63.5
6 83.4
-20 12.0
12 82.2
14 82.5
2 81.3
-2 77.6
-16 13.4
-18 12.3
4 81.6
0 80.9
8 83.3
===============================
Confusion Matrix:
...
Click to Expand

The following code (located at examples/adversarial_evasion.py) will:

  • Download the RML2016.10a Dataset from deepsig.io/datasets
  • Load the dataset into a PyTorch format with categorical labels and only keep high SNR samples
  • Create a Convolutional Neural Network model with PyTorch
  • Load pre-trained weights (see Signal Classification (AMC))
  • Evaluate the model on the dataset with no adversarial evasion for a baseline
  • Perform an FGSM attack with a signal-to-perturbation ratio of 10 dB

Note that its likely that this script would evaluate the network on data it also used for training and that is certainly not desired. This script is merely meant to serve as an easy example and shouldn't be directly used for evaluation.

1fromrfml.attackimportfgsm2fromrfml.dataimportbuild_dataset3fromrfml.nn.evalimportcompute_accuracy4fromrfml.nn.modelimportbuild_model56fromtorch.utils.dataimportDataLoader78_, _, test, le=build_dataset(dataset_name="RML2016.10a", test_pct=0.9)
9mask=test.df["SNR"] >=1810model=build_model(model_name="CNN", input_samples=128, n_classes=len(le))
11model.load("cnn.pt")
1213acc=compute_accuracy(model=model, data=test, le=le, mask=mask)
14print("Normal (no attack) Accuracy on Dataset: {:.3f}".format(acc))
1516spr=10# dB17right=018total=019dl=DataLoader(test.as_torch(le=le, mask=mask), shuffle=True, batch_size=512)
20forx, yindl:
21adv_x=fgsm(x, y, spr=spr, input_size=128, sps=8, net=model)
2223predictions=model.predict(adv_x)
24right+= (predictions==y).sum().item()
25total+=len(y)
2627adv_acc=float(right) /total28print("Adversarial Accuracy with SPR of {} dB attack: {:.3f}".format(spr, adv_acc))
29print("FGSM Degraded Model Accuracy by {:.3f}".format(acc-adv_acc))

Running the above code will produce an output similar to the following.

> python3 examples/adversarial_evasion.py
Normal (no attack) Accuracy on Dataset: 0.831
Adversarial Accuracy with SPR of 10 dB attack: 0.092
FGSM Degraded Model Accuracy by 0.740
Click to Expand

The following code (located at examples/pt_modem.py) will do the following:

  • Generate a random bit stream
  • Modulate that bit stream using a PyTorch implementation of a linear modem (with a symbol mapping, upsampling, and pulse shaping)
  • Corrupt the signal using AWGN generated by a PyTorch module
  • Demodulate the bit stream back using a PyTorch implementation (with match filtering, downsampling, and a hard decision on symbol unmapping)
  • Compute the bit error rate

While it is a simplistic example, the individual pieces (transmit, receive, and channel) can all be reused for your specific application.

1fromrfml.ptradioimportAWGN, Transmitter, Receiver, theoreticalBER23importnumpyasnp45modulation="BPSK"# could be QPSK, 8PSK, QAM16, QAM646tx=Transmitter(modulation=modulation)
7channel=AWGN()
8rx=Receiver(modulation=modulation)
910n_symbols=int(10e3)
11n_bits=int(tx.symbol_encoder.get_bps() *n_symbols)
12snrs=list(range(0, 8))
13n_trials=101415forsnrinrange(0, 8):
16channel.set_snr(snr)
17n_errors=01819for_inrange(n_trials):
20tx_bits=np.random.randint(low=0, high=2, size=n_bits)
21tx_iq=tx.modulate(bits=tx_bits)
2223rx_iq=channel(tx_iq)
2425rx_bits=rx.demodulate(iq=rx_iq)
26rx_bits=np.array(rx_bits)
2728n_errors+=np.sum(np.abs(tx_bits-rx_bits))
2930ber=float(n_errors) /float(n_bits*n_trials)
31theory=theoreticalBER(modulation=modulation, snr=snr)
3233print(
34"BER={:.3e}, "35"theory={:.3e}, "36"|diff|={:.3e}, "37"SNR={:d}, "38"modulation={}".format(ber, theory, np.abs(ber-theory), snr, modulation)
39 )

Running the above code will produce an output similar to the following.

> python3 examples/pt_modem.py
BER=7.763e-02, theory=7.865e-02, |diff|=1.020e-03, SNR=0, modulation=BPSK
BER=5.502e-02, theory=5.628e-02, |diff|=1.262e-03, SNR=1, modulation=BPSK
BER=3.740e-02, theory=3.751e-02, |diff|=1.060e-04, SNR=2, modulation=BPSK
BER=2.340e-02, theory=2.288e-02, |diff|=5.220e-04, SNR=3, modulation=BPSK
BER=1.269e-02, theory=1.250e-02, |diff|=1.890e-04, SNR=4, modulation=BPSK
BER=6.500e-03, theory=5.954e-03, |diff|=5.461e-04, SNR=5, modulation=BPSK
BER=2.250e-03, theory=2.388e-03, |diff|=1.383e-04, SNR=6, modulation=BPSK
BER=8.000e-04, theory=7.727e-04, |diff|=2.733e-05, SNR=7, modulation=BPSK
Click to Expand

The Error Vector Magnitude (EVM) of the symbols can be used as a loss function as well. The following code snippet (located at examples/evm_loss.py) presents a, silly, minimalist example of its use. In this code, a transmit/receive chain is constructed (see PyTorch Implementation of Linear Modulations) and the transmitted symbols are learned from some target received symbols.

1fromrfml.ptradioimportRRC, Upsample, Downsample2fromrfml.ptradio.modemimport_qpsk_constellation3fromrfml.nn.Fimportevm45importnumpyasnp67importtorch8fromtorch.nnimportSequential, Parameter9fromtorch.autogradimportVariable10fromtorch.optimimportSGD1112n_symbols=3213indices=np.random.randint(low=0, high=4, size=n_symbols)
14target_symbols=np.array([_qpsk_constellation[i] foriinindices])
15target_symbols=np.stack((target_symbols.real, target_symbols.imag))
16_target_symbols=torch.from_numpy(
17target_symbols[np.newaxis, np.newaxis, ::].astype(np.float32)
18 )
1920mean=torch.zeros((1, 1, 2, _target_symbols.shape[3]))
21std=torch.ones((1, 1, 2, _target_symbols.shape[3]))
22tx_symbols=torch.nn.Parameter(torch.normal(mean, std))
2324optimizer=SGD((tx_symbols,), lr=10e-2, momentum=0.9)
2526tx_chain=Sequential(
27Upsample(i=8), RRC(alpha=0.35, sps=8, filter_span=8, add_pad=True)
28 )
29rx_chain=Sequential(
30RRC(alpha=0.35, sps=8, filter_span=8, add_pad=False), Downsample(offset=8*8, d=8)
31 )
3233n_epochs=15134foriinrange(n_epochs):
35tx_signal=tx_chain(tx_symbols)
36rx_symbols=rx_chain(tx_signal)
37loss=torch.mean(evm(rx_symbols, _target_symbols))
3839ifi%15==0:
40print("Loss @ epoch {}: {:3f}".format(i, loss))
4142loss.backward()
43optimizer.step()
44tx_symbols.grad.zero_()

The code may be better understood through a diagram.

Overview of simplistic example for utilizing symbol (EVM) loss

If the above code is executed, an output similar to the following should be observed.

> python3 examples/evm_loss.py
Loss @ epoch 0: 1.700565
Loss @ epoch 15: 1.455332
Loss @ epoch 30: 1.062061
Loss @ epoch 45: 0.700792
Loss @ epoch 60: 0.422401
Loss @ epoch 75: 0.220447
Loss @ epoch 90: 0.102916
Loss @ epoch 105: 0.044921
Loss @ epoch 120: 0.021536
Loss @ epoch 135: 0.006125
Loss @ epoch 150: 0.004482

Which may also be better understood through an animation.

Animation of utilizing symbol (EVM) loss
Click to Expand

Nearly all communications systems are frequency limited, therefore, it can be helpful to have a component of the loss function which penalizes the use of spectrum. The following simple example (located at examples/spectral_loss.py) demonstrates a filtering of a signal to adhere to a spectral mask. By itself, it isn't useful as the performance is extremely subpar to a standard digital filter; however, it can be incorportated into a larger machine learning workflow.

1fromrfml.nn.Fimportpsd2fromrfml.ptradioimportRRC34importnumpyasnp56importtorch7fromtorch.nnimportParameter8fromtorch.optimimportSGD910n_time=10241112# Create a white gaussian noise signal -- therefore ~ flat across frequency13mean=torch.zeros((1, 1, 2, n_time))
14std=torch.ones((1, 1, 2, n_time)) /25.015signal=torch.nn.Parameter(torch.normal(mean, std))
16t=np.arange(n_time)
1718# Define our "target" PSD profile to be the spectrum of the root raised cosine19rrc=RRC()
20impulse=rrc.impulse_response21# The impulse response is real valued so we'll make it "complex" by just adding22# another dimension in for IQ and setting the imaginary portion to 023impulse=torch.cat((impulse, impulse), dim=2)
24impulse[:, :, 1, :] =0.02526# In order to match dimensions with our desired frequency resolution by27# setting n_time to be the FFT length -- we must pad with some zeros28_to_pad=torch.zeros(
29 (impulse.shape[0], impulse.shape[1], impulse.shape[2], n_time-impulse.shape[3])
30 )
31impulse=torch.cat((impulse, _to_pad), dim=3)
3233target_psd=psd(impulse)
3435optimizer=SGD((signal,), lr=50e-4, momentum=0.9)
3637n_epochs=15138foriinrange(n_epochs):
39cur_psd=psd(signal)
40loss=torch.mean((cur_psd-target_psd) **2)
4142ifi%15==0:
43print("Loss @ epoch {}: {:3f}".format(i, loss))
4445loss.backward()
46optimizer.step()
47signal.grad.zero_()

It may be easier to understand the above code with a diagram.

Overview of simplistic example for utilizing spectral loss

If the example is ran, an output similar to the following will be displayed.

> python3 examples/spectral_loss.py
Loss @ epoch 0: 20.610109
Loss @ epoch 15: 1.159350
Loss @ epoch 30: 0.206273
Loss @ epoch 45: 0.039206
Loss @ epoch 60: 0.007379
Loss @ epoch 75: 0.001740
Loss @ epoch 90: 0.000586
Loss @ epoch 105: 0.000301
Loss @ epoch 120: 0.000195
Loss @ epoch 135: 0.000145
Loss @ epoch 150: 0.000117

Which, again, may be more easily understood through an animation.

Animation of utilizing spectral loss

Clearly, the loss function does a great job at initially killing the out of band energy to comply with the provided spectral mask, however, it only achieves ~20dB of attenuation whereas a digital filter could achieve much greater out of band attenuation.

From the root folder of the repository.

python3 -m pytest

The documentation is a relatively simplistic Sphinx API rendering hosted within the repository by GitHub pages. It can be accessed at brysef.github.io/rfml.

This code was released in support of a tutorial offered at MILCOM 2019 (Adversarial Radio Frequency Machine Learning (RFML) with PyTorch). While the code contained in the library can be applied more broadly, the tutorial was focused on adversarial evasion attacks and defenses on deep learning enabled signal classification systems. The learning objectives and course outline of that tutorial are provided below. Of particular interest, three Jupyter Notebooks are included that demonstrate how to: train an Automatic Modulation Classification Neural Network, evade signal classification with the Fast Gradient Sign Method, and perform adversarial training.

Through this tutorial, the attendee will be introduced to the following concepts:

  1. Applications of RFML
  2. The PyTorch toolkit for developing RFML solutions
    • (Hands-On Exercise) Train, validate, and test a simple neural network for spectrum sensing
    • Advanced PyTorch concepts (such as custom loss functions and modules to support advanced digital signal processing functions)
  3. Adversarial machine learning applied to RFML
    • Overview of current state-of-the-art in adversarial RFML
    • (Hands-On Exercise) Develop an adversarial evasion attack against a spectrum sensing network (created by the attendee) using the well-known Fast Gradient Sign Method (FGSM) algorithm
    • Overview of hardening techniques against adversarial RFML
    • (Hands-On Exercise) Utilize adversarial training to harden a RFML model

The primary objective of the tutorial is for the attendee to be hands-on with the code. Therefore, while a lot of information is presented in slide format, the core of the tutorial is code execution through prepared Jupyter Notebooks executed in Google Colaboratory. In the modules listed below, you can click on the solutions notebook to view a pre-ran Jupyter Notebook that is rendered by GitHub, or, click on Open in Colab to open an executable version in Google Colaboratory. Note that when opening Google Colaboratory you should either enable the GPU Hardware Accelerator (click here for how) or disable the GPU flag in the notebooks (this will make execution very slow).

#TimeDescriptionNotes/Solutions/Exercises
010mIntroduction:
Provide an overview of RFML with a focus on signal classification.
110mTutorial Objectives and Software Tools:
Describe the skills that will be learned in this tutorial and introduce the format and software tools utilized for the hands-on exercises.
220mTrain/Evaluate a DNN for AMC:
Train and validate a DNN using a static dataset of raw IQ data to perform an automatic modulation classification (AMC) task. After training, the performance of the network will be evaluated as a function of SNR and an averaged confusion matrix of all possible classes.
Open Solutions Notebook: Train/Evaluate a DNN for AMC
Open Notebook in Colab: Train/Evaluate a DNN for AMC
315mAdversarial RF Machine Learning:
Provide an overview of adversarial machine learning techniques and how they uniquely apply to RFML. In particular, focus on adversarial evasion attacks and the well-known FGSM algorithm.
420mEvade Signal Classification with FGSM:
Develop a white-box, digital, adversarial evasion attack against a trained AMC DNN using the FGSM algorithm.
Open Solutions Notebook: Evade Signal Classification with FGSM
Open Notebook in Colab: Evade Signal Classification with FGSM
515mPhysical Adversarial RF Machine Learning:
Many adversarial ML techniques in the literature focus on attacks that have digital access to the classifier input; however, the primary vulnerability of RFML is to physical attacks, which are transmitted over-the-air and thus perturbations are subject to natural noise and impact their intended receiver.
Break
615mHardening RFML Against Adversarial Evasion:
Provide an overview of techniques by which to harden deep learning solutions against adversarial evasion attacks. In particular, study the unique defense techniques that have been proposed in RFML for both detecting adversarial examples and being robust to those adversarial examples (by still correctly classifiying them).
720mAdversarial Training:
Train a DNN, with portions of the training inputs being adversarial examples generated from FGSM on the fly, in order to gain more robustness against an FGSM attack.
Open Solutions Notebook: Adversarial Training
Open Notebook in Colab: Adversarial Training
910mConclusion:
Summary of current state of adversarial RFML, the proposed next steps for research, and immediate actions to ensure robust RFML devices.
1020mAdvanced Topics in PyTorch:
"Expert" filters, channel models, and custom loss functions for RF.
Bibliography:
Citations used in the slides and code.

If you find any errors, feel free to open an issue; though I can't guarantee how quickly it will be looked at. Pull requests are accepted though 😃! There isn't an extensive contribution guideline, but, please follow the GitHub Flow.

In particular, ensure that you've:

  • written a passing unit test (that would have failed before)
  • formatted the code with black
  • re-built the documentation (if applicable)
  • adequately described why the change was needed (if a bug) or what the change does (if a new feature)

If you've open sourced your own work in machine learning for wireless communications, feel free to drop me a note to be added to the related projects!

  • MeysamSadeghi/Security of DL in Wireless: Attacks on Physical Layer Auto-Encoders in TensorFlow
  • RadioML/Examples: Automatic Modulation Classification using Keras
  • RadioML/Dataset: Recreate the RML Synthetic Datasets using GNU Radio
  • immortal3/AutoEncoder Communication: TensorFlow implementation of "An Introduction to Deep Learning for the Physical Layer"
  • Tensorflow/Cleverhans: Library for adversarial machine learning attacks and defenses with support for Tensorflow (support for other frameworks coming soon) -- This repository also contains tutorials for adversarial machine learning
  • BethgeLab/Foolbox: Library for adversarial machine learning attacks with support for PyTorch, Keras, and TensorFlow
  • MadryLab/robustness: Adversarial training library built with PyTorch.
  • FastAI: An extensive deep learning library along with tutorials built on top of PyTorch
  • PyTorch: The PyTorch library itself comes with excellent documentation and tutorials

This project is licensed under the BSD 3-Clause License -- See LICENSE.rst for more details.

This repository contains implementations of other folk's algorithms (e.g. adversarial attacks, neural network architectures, dataset wrappers, etc.) and therefore, whenever those algorithms are used, their respective works must be cited. The relevant citations for their works have been provided in the docstrings when needed. Since this repository isn't the official code for any publication, you take responsibility for the correctness of the implementations (although we've made every effort to ensure that the code is well tested).

If you find this code useful for your research, please consider referencing it in your work so that others are aware. This repository isn't citable (since that requires archiving and creating a DOI), so a simple footnote would be the best way to reference this repository.

\footnote{Code is available at \textit{github.com/brysef/rfml}}

If your work specifically revolves around adversarial machine learning for wireless communications, consider citing my journal publication (on FGSM physical adversarial attacks for wireless communications) or MILCOM conference paper (on adding communications loss to adversarial attacks).

@article{Flowers2019a,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
doi = {10.1109/TIFS.2019.2934069},
issn = {1556-6013},
journal = {IEEE Transactions on Information Forensics and Security},
month = {},
number = {},
pages = {1-1},
title = {Evaluating Adversarial Evasion Attacks in the Context of Wireless Communications},
volume = {},
year = {2019}
}
@inproceedings{Flowers2019b,
author = {B. {Flowers} and R. M. {Buehrer} and W. C. {Headley}},
booktitle = {MILCOM 2019 - 2019 IEEE Military Communications Conference (MILCOM)},
doi = {10.1109/MILCOM47813.2019.9020716},
issn = {2155-7578},
keywords = {Perturbation methods;Transmitters;Receivers;Machine learning;Bit error rate;Modulation;Neural networks},
month = {Nov},
number = {},
pages = {133-140},
title = {Communications Aware Adversarial Residual Networks for Over the Air Evasion Attacks},
volume = {},
year = {2019}
}
Bryse FlowersPhD student at UCSDbflowers@ucsd.edu
William C. HeadleyAssociate Director of Electronic Systems Laboratory, Hume Center / Research Assistant Professor ECE Virginia Techcheadley@vt.edu

Numerous others have generously contributed to this work -- see CONTRIBUTORS.rst for more details.

Releases

Packages

Used by

Contributors

Languages