Code Setup

Before diving into how to use PyTorchFI, we need to do some preliminary setup. Presumabely, you will already have some similar code in your own project.

Here, we will download a pretrained model of AlexNet from the TorchVision repository (you should be able to use any model) and prepare a generic "image" to run through the AlexNet model.

# Model prep
model = models.alexnet(pretrained=True)
# generic image preperation
batch_size = 1
h = 224
w = 224
c = 3
image = torch.rand((batch_size, c, h, w))

Let's see what class we get when we run our generated "image" through AlexNet.

output = model(image)
golden_label = list(torch.argmax(output, dim=1))[0].item()
print("Error-free label:", golden_label)

Ok, so we see that a regular, error-free inference results in the class 556. Now, for the fun part, let's preturb the network during inference, and see what happens! We'll use PyTorchFI to perform an injection on a single neuron.

Initializing PyTorchFI

In order to perform error injections dynamically (i.e., during an inference), you need to provide PyTorchFI with the following:

  • model being preturbed

  • batch size the model is using

  • shape of the input

  • layer types to perturb

  • CUDA available (optional)

pfi_model = fault_injection(model,

The default input_shape is [3, 224, 224], which is the typical size of an image from the ImageNet dataset. So while it is optional in this tutorial, it is important to ensure the input to your model is specified if it is not ImageNet.

The default layer_type is torch.nn.Conv2d. However, you can include any set of layers to perform injections on. Under the hood, PyTorchFI will only append hooks to the layers specified. If you want to include all layers, you can simple use "all" in the list of layer types. For example, the following are some various ways to initalize PyTorchFI layer_types:

  • layer_types = [torch.nn.Conv2d]

  • layer_types = [torch.nn.Conv2d, torch.nn.Linear]

  • layer_types = [torch.nn.Linear]

  • layer_types = ["all"]

After you run fault_injection(...), PyTorchFI will setup a few internal data structures and profile your network to assist with bounds checking during error injections. To print out what PyTorchFI "sees" after the initialization, you can run print_pytorchfi_layer_summary().


Error Injection Overview

In order to perform an injection, we need to specify two things:

  1. Error site: Where to perform the injection.

  2. Error value: What value to change it to.

To address the where, there are two general error injection types: neurons and weights. We'll begin by explaining some of the subtleties of neuron error sites. Weight injections are similar and thus excluded here.

Neuron Injections Site

Specifying the location of an error injection during a dynamic execution of a DNN requires knowing the shape of the output feature maps. Since the output feature maps (fmaps for short) are dependent on a variety of things (including the stride, padding, kernel size, etc) it might not be obvious apriori what bounds you can use to specify the location of an error.

To make your life easier, we profile the network during the init step to obtain the bounds on your network. For example, if we want to perform an injection in Layer 0 (as depicted in the previous section), we know that the shape of the output feature map of the convolution is [1, 64, 55, 55], which corresponds to the [batch, C, H, W] size.

It is important to note that the Layer number will depend on the layer_types provided. In other words, the Layer # specified by the print_pytorch_fi_summary() is the number that will be used to ID the layer in PyTorchFI.

Neuron Injection Value

The second part in performing an error injection is to define the new value which will be used.

There are two possibilities here:

  1. A new value that is independent of the original value

  2. A new value that is dependent on the original value

The first scenario is straightforward: you can specify any value you want, and tell PyTorchFI to replace the original value with the erronous value.

The second scenario requires more support, in order to read the original value, apply some function on it, and then write the new value in place of the old value. For this scenario, the user needs to provide they own custom error function to PyTorchFI (or use one from the error_models package).

Let's start with a simple injection.

b, layer, C, H, W, err_val = [0], [2], [4], [2], [4], [10000]
inj = pfi_model.declare_neuron_fi(batch=b, layer_num=layer, dim1=C, dim2=H, dim3=W, value=err_val)
inj_output = inj(image)
inj_label = list(torch.argmax(inj_output, dim=1))[0].item()
print("[Single Error] PytorchFI label:", inj_label)

In order to declare a neuron (or weight) injection, we need to call declare_neuron_fi(...) (or declare_weight_fi(...)). We then need to pass in the location of the error, which are specified by the args:

  • batch

  • layer_num

  • dim1, dim2, dim3

and the value, which will either be specied by:

  • value, if it is indepedent

  • function, if it is dependent

In the example above, we are injecting a value of 10000 in batch element 0, in layer 3 at the tensor location [4, 2, 4] (corresponding to CHW).