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.
torch.random.manual_seed(5)# Model prepmodel = models.alexnet(pretrained=True)model.eval()# generic image preperationbatch_size = 1h = 224w = 224c = 3image = 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)).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.
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,batch_size,input_shape=[c,h,w],layer_types=[torch.nn.Conv2d],use_cuda=False,)
[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.
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 = [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
In order to perform an injection, we need to specify two things:
Error site: Where to perform the injection.
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.
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.
The second part in performing an error injection is to define the new value which will be used.
There are two possibilities here:
A new value that is independent of the original value
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
Let's start with a simple injection.
b, layer, C, H, W, err_val = , , , , , 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)).item()print("[Single Error] PytorchFI label:", inj_label)
In order to declare a neuron (or weight) injection, we need to call
declare_weight_fi(...)). We then need to pass in the location of the error, which are specified by the args:
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).