<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 QNN on Pynq
This notebook covers how to use low quantized Neural Networks on Pynq for inference on MNIST dataset by using LFC network composed of 4 fully connected layers with 1024 neurons each. There are 2 networks using different precision
Step1:  1. LFC and MNIST
This notebook performs inference on MNIST test set from http
Step2:  2. Hardware Inference
First of all a classifier needs to be instantiated. Using the LfcClassifier will allow to classify MNIST formatted images utilizing LFC network. There are two different runtimes available
Step3:  Case 2
Step4:  3. Software accelerated inference
In comparison to previous runs the inference can be performed in pure software runtime utilizing PYNQs ARM core. Let's only take the first 10 pictures to get results within a narrow time frame
Step5:  Start inference
Step6:  As it can be seen, pure software runtime is much slower.
4. Summary
Inference time
Hardware
Results can be visualized using matplotlib. The inference time per image is accessible through the classifier. Here you can see hardware vs software execution times per image in microseconds
Step7:  Accuracy
Step8:  5. Reset the device
<ASSISTANT_TASK:>
Python Code:
import bnn
Explanation: QNN on Pynq
This notebook covers how to use low quantized Neural Networks on Pynq for inference on MNIST dataset by using LFC network composed of 4 fully connected layers with 1024 neurons each. There are 2 networks using different precision: 
LFCW1A1 using 1 bit weights and 1 activation,
LFCW1A2 using 1 bit weights and 2 activation
End of explanation
#get
!wget http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz 
!wget http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz 
#unzip    
!gzip -d t10k-images-idx3-ubyte.gz
!gzip -d t10k-labels-idx1-ubyte.gz
#read labels
labels = []
with open("/home/xilinx/jupyter_notebooks/bnn/t10k-labels-idx1-ubyte","rb") as lbl_file:
    #read magic number and number of labels (MSB first) -> MNIST header
    magicNum = int.from_bytes(lbl_file.read(4), byteorder="big")
    countLbl = int.from_bytes(lbl_file.read(4), byteorder="big")
    #now the labels are following byte-wise
    for idx in range(countLbl):
        labels.append(int.from_bytes(lbl_file.read(1), byteorder="big"))
    lbl_file.close()
len(labels)
Explanation: 1. LFC and MNIST
This notebook performs inference on MNIST test set from http://yann.lecun.com/exdb/mnist/ which contains 10000 pictures of handwritten digits. The LFC network requires MNIST formatted input data, that's why the binary test file can be directly loaded. All other images have to be formatted to this specification (refer to url and LFC webcam examples).
At first you need to download mnist test set and labels using wget and unzip the archive as shown below:
In order to be able to compare the inferred classes against the expected labels we first read the labels:
End of explanation
lfcW1A1_classifier = bnn.LfcClassifier(bnn.NETWORK_LFCW1A1,"mnist",bnn.RUNTIME_HW)
lfcW1A1_classifier.classes
result_W1A1 = lfcW1A1_classifier.classify_mnists("/home/xilinx/jupyter_notebooks/bnn/t10k-images-idx3-ubyte")
Explanation: 2. Hardware Inference
First of all a classifier needs to be instantiated. Using the LfcClassifier will allow to classify MNIST formatted images utilizing LFC network. There are two different runtimes available: hardware accelerated and pure software environment.
Once a classifier is instantiated the inference on MNIST images can be started using classify_mnist or classify_mnists methods - for both single and multiple images.
Case 1:
W1A1 - 1 bit weights and 1 bit activation
End of explanation
lfcW1A2_classifier = bnn.LfcClassifier(bnn.NETWORK_LFCW1A2,"mnist",bnn.RUNTIME_HW)
result_W1A2 = lfcW1A2_classifier.classify_mnists("/home/xilinx/jupyter_notebooks/bnn/t10k-images-idx3-ubyte")
Explanation: Case 2:
W1A2 - 1 bit weights and 2 bit activation
End of explanation
with open("/home/xilinx/jupyter_notebooks/bnn/10_mnist_pictures", "wb") as out_file:
    with open("/home/xilinx/jupyter_notebooks/bnn/t10k-images-idx3-ubyte","rb") as img_file:
        #copy magic number
        out_file.write(img_file.read(4))
        #set number of images
        img_file.read(4)
        out_file.write(bytearray.fromhex('0000000A'))        
        #copy row and column information
        out_file.write(img_file.read(8))
        
        #copy 10 pictures (one is 28x28, 1 pixel is 1 byte)
        out_file.write(img_file.read(28*28*10))
        img_file.close()
        out_file.close()
Explanation: 3. Software accelerated inference
In comparison to previous runs the inference can be performed in pure software runtime utilizing PYNQs ARM core. Let's only take the first 10 pictures to get results within a narrow time frame:
End of explanation
print("SW Inference with W1A1:")
sw_lfcW1A1_classifier = bnn.LfcClassifier(bnn.NETWORK_LFCW1A1,"mnist",bnn.RUNTIME_SW)
sw_resultW1A1 = sw_lfcW1A1_classifier.classify_mnists("/home/xilinx/jupyter_notebooks/bnn/10_mnist_pictures")
print("\nSW Inference with W1A2:")
sw_lfcW1A2_classifier = bnn.LfcClassifier(bnn.NETWORK_LFCW1A2,"mnist",bnn.RUNTIME_SW)
sw_resultW1A2 = sw_lfcW1A2_classifier.classify_mnists("/home/xilinx/jupyter_notebooks/bnn/10_mnist_pictures")
Explanation: Start inference
End of explanation
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
hw_time = [lfcW1A1_classifier.usecPerImage,
           lfcW1A2_classifier.usecPerImage]
sw_time = [sw_lfcW1A1_classifier.usecPerImage,
           sw_lfcW1A2_classifier.usecPerImage]
x_axis = ('W1A1', 'W1A2')
y_pos = np.arange(len(x_axis))
plt.bar(y_pos-0.25, hw_time, 0.25)
plt.bar(y_pos+0.25, sw_time, 0.25)
plt.xticks(y_pos, x_axis)
plt.legend(["hardware","software"])
plt.semilogy()
plt.show()
Explanation: As it can be seen, pure software runtime is much slower.
4. Summary
Inference time
Hardware
Results can be visualized using matplotlib. The inference time per image is accessible through the classifier. Here you can see hardware vs software execution times per image in microseconds:
End of explanation
#compare against labels
countRight = 0
for idx in range(len(labels)):
    if labels[idx] == result_W1A1[idx]:
        countRight += 1
accuracyW1A1 = countRight*100/len(labels)
countRight = 0
for idx in range(len(labels)):
    if labels[idx] == result_W1A2[idx]:
        countRight += 1
accuracyW1A2 = countRight*100/len(labels)
print("Accuracy W1A1: ", accuracyW1A1)
print("Accuracy W1A2: ", accuracyW1A2)
Explanation: Accuracy
End of explanation
from pynq import Xlnk
xlnk = Xlnk()
xlnk.xlnk_reset()
Explanation: 5. Reset the device
End of explanation
<END_TASK>