diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 9087804..763c70f 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -3,9 +3,7 @@
name: Upload Python Package
-on:
- release:
- types: [created]
+on: workflow_dispatch
jobs:
deploy:
diff --git a/.gitignore b/.gitignore
index 593e276..ea9a550 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,6 @@ build/finn
*.link_summary
*.onnx
*.pyc
+output_*/
+release/
*.egg-info
diff --git a/README.md b/README.md
index 7d2ed9c..932ce49 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-##
Dataflow Accelerator Examples
+##
Dataflow Accelerator Examples
*for PYNQ on Zynq and Alveo*
@@ -42,7 +42,7 @@ Retrieve the example Jupyter notebooks using the PYNQ get-notebooks command:
```shell
# on PYNQ boards, first cd /home/xilinx/jupyter_notebooks
-pynq get-notebooks --from-package finn-examples -p .
+pynq get-notebooks --from-package finn-examples -p . --force
```
You can now navigate the provided Jupyter notebook examples, or just use the
@@ -67,6 +67,11 @@ dummy_out = accel.execute(dummy_in)
| ![](docs/img/mnist.jpg)
MNIST | 3-layer fully-connected | several variants:
1/2-bit weights/activations | all |
| ![](docs/img/imagenet.jpg)
ImageNet | MobileNet-v1 | 4-bit weights and activations
8-bit first layer weights | Alveo U250
ZCU104 |
| ![](docs/img/imagenet.jpg)
ImageNet | ResNet-50 | 1-bit weights 2-bit activations
4-bit residuals
8-bit first/last layer weights | Alveo U250 |
+| ![](docs/img/radioml.png)
RadioML 2018 | 1D CNN (VGG10) | 4-bit weights and activations | ZCU104 |
+| ![](docs/img/maskedfacenet.jpg)
MaskedFace-Net | [BinaryCoP](https://arxiv.org/pdf/2102.03456)
*Contributed by TU Munich+BMW* | 1-bit weights and activations | Pynq-Z1 |
+| ![](docs/img/keyword-spotting.png)
Google Speech Commands v2 | 3-layer fully-connected | 3-bit weights and activations | Pynq-Z1 |
+
+We welcome community contributions to add more examples to this repo!
## Supported Boards
diff --git a/build/get-finn.sh b/build/get-finn.sh
index b178262..c0b41b8 100755
--- a/build/get-finn.sh
+++ b/build/get-finn.sh
@@ -30,7 +30,7 @@
# URL for git repo to be cloned
REPO_URL=https://github.com/Xilinx/finn
# commit hash for repo
-REPO_COMMIT=c8be5048a7f1647f7c72be7c7cd158e851d47a86
+REPO_COMMIT=d1cc9cf94f1c33354cc169c5a6517314d0e94e3b
# directory (under the same folder as this script) to clone to
REPO_DIR=finn
diff --git a/build/kws/README.md b/build/kws/README.md
new file mode 100644
index 0000000..9588204
--- /dev/null
+++ b/build/kws/README.md
@@ -0,0 +1,24 @@
+# The KWS examplee
+
+The KWS example includes an MLP for the Google SpeechCommandsV2 dataset.
+
+## Build bitfiles for BNN-PYNQ examples
+
+The build is currently configured for the PYNQ-Z1 board and a throughput of 200k FPS at a clock frequency of 100 MHz.
+
+1. Download the pretrained MLP ONNX models and pre-processed validation data using the `get-kws-data-model.sh` script.
+
+2. Launch the build as follows:
+```shell
+# update this according to where you cloned this repo:
+FINN_EXAMPLES=/path/to/finn-examples
+# cd into finn submodule
+cd $FINN_EXAMPLES/build/finn
+# launch the build on the bnn-pynq folder
+bash run-docker.sh build_custom /path/to/finn-examples/build/kws
+```
+
+3. The generated outputs will be under `kws/_output__`.
+You can find a description of the generated files [here](https://finn-dev.readthedocs.io/en/latest/command_line.html#simple-dataflow-build-mode).
+The folder will additionally include the quantized inputs for verification (`all_validation_KWS_data_inputs_len_10102.npy`) and the expected outputs (`all_validation_KWS_data_outputs_len_10102.npy`).
+When running the network on hardware the validation should achieve an accuracy of 89.78 % with 9070 of the 10102 samples being classified correctly.
diff --git a/build/kws/build.py b/build/kws/build.py
new file mode 100644
index 0000000..17e6079
--- /dev/null
+++ b/build/kws/build.py
@@ -0,0 +1,159 @@
+# Copyright (c) 2021, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import finn.builder.build_dataflow as build
+import finn.builder.build_dataflow_config as build_cfg
+
+from finn.core.modelwrapper import ModelWrapper
+from finn.builder.build_dataflow_config import DataflowBuildConfig
+from finn.transformation.insert_topk import InsertTopK
+from finn.builder.build_dataflow_steps import build_dataflow_step_lookup
+import time
+import finn.core.onnx_exec as oxe
+import numpy as np
+import datetime
+from glob import glob
+
+
+# Inject the preprocessing step into FINN to enable json serialization later on
+def step_preprocess(model: ModelWrapper, cfg: DataflowBuildConfig):
+ model = model.transform(InsertTopK(k=1))
+ return model
+
+
+build_dataflow_step_lookup["step_preprocess_InsertTopK"] = step_preprocess
+
+estimate_steps = ["step_preprocess_InsertTopK"] + build_cfg.estimate_only_dataflow_steps
+estimate_outputs = [build_cfg.DataflowOutputType.ESTIMATE_REPORTS]
+build_steps = ["step_preprocess_InsertTopK"] + build_cfg.default_build_dataflow_steps
+build_outputs = [
+ build_cfg.DataflowOutputType.ESTIMATE_REPORTS,
+ build_cfg.DataflowOutputType.STITCHED_IP,
+ build_cfg.DataflowOutputType.PYNQ_DRIVER,
+ build_cfg.DataflowOutputType.BITFILE,
+ build_cfg.DataflowOutputType.DEPLOYMENT_PACKAGE,
+]
+verification_steps = [
+ build_cfg.VerificationStepType.QONNX_TO_FINN_PYTHON,
+ build_cfg.VerificationStepType.TIDY_UP_PYTHON,
+ build_cfg.VerificationStepType.STREAMLINED_PYTHON,
+ build_cfg.VerificationStepType.FOLDED_HLS_CPPSIM,
+]
+
+model_name = (
+ "MLP_W3A3_python_speech_features_pre-processing_QONNX"
+)
+model_file = model_name + ".onnx"
+
+# Change the ONNX opset from version 9 to 11, which adds support for the TopK node
+from finn.core.modelwrapper import ModelWrapper
+model = ModelWrapper(model_file)
+model.model.opset_import[0].version = 11
+model_file = model_file.replace(".onnx", "_opset-11.onnx")
+model.save(model_file)
+
+platform_name = "Pynq-Z1"
+output_dir = f"{time.time():.2f}_output_{model_name.replace('/','_')}_{platform_name}"
+
+# Configure build
+cfg = build_cfg.DataflowBuildConfig(
+ # steps=estimate_steps, generate_outputs=estimate_outputs,
+ verify_steps=verification_steps,
+ steps=build_steps,
+ generate_outputs=build_outputs,
+ output_dir=output_dir,
+ target_fps=200000,
+ synth_clk_period_ns=10.0,
+ board=platform_name,
+ shell_flow_type=build_cfg.ShellFlowType.VIVADO_ZYNQ,
+ save_intermediate_models=True,
+ stitched_ip_gen_dcp=True,
+ verify_save_full_context=True,
+)
+# Build the model
+build.build_dataflow_cfg(model_file, cfg)
+
+# Save Build config
+config_json_path = f"{output_dir}/DataflowBuildConfig.json"
+with open(config_json_path, "w") as f:
+ f.write(cfg.to_json())
+print(f"Saved DataflowBuildConfig to: {config_json_path}")
+
+# Export quantized inputs
+print("Quantizing validation dataset.")
+parent_model = ModelWrapper(output_dir + "/intermediate_models/dataflow_parent.onnx")
+input_shape = (1, 1, 10, 49)
+last_node = parent_model.graph.node[-2]
+
+for f_name in glob("*.npz"):
+ print(f"Processing file: {f_name}")
+
+ with open(f_name, "rb") as f:
+ np_f = np.load(f)
+ data_arr = np_f["data_arr"]
+ label_arr = np_f["label_arr"]
+
+ pre_processed_inputs = []
+ start_time = time.time()
+ for i in range(len(data_arr)):
+ input_tensor_finn = data_arr[i].reshape(input_shape)
+
+ # Execute with FINN-ONNX
+ input_dict = {parent_model.graph.input[0].name: input_tensor_finn}
+ output_dict = oxe.execute_onnx(
+ parent_model,
+ input_dict,
+ True,
+ end_node=last_node,
+ )
+ finn_output = output_dict[last_node.output[0]]
+ pre_processed_inputs.append(finn_output)
+
+ diff_time = time.time() - start_time
+ time_per_sample = diff_time / (i + 1)
+ time_left = (len(data_arr) - (i + 1)) * time_per_sample
+ time_left = datetime.timedelta(seconds=time_left)
+ print(
+ f"Processed: {100*(i+1)/len(data_arr):.1f} [%], "
+ f"time left: {str(time_left)}",
+ end="\r",
+ )
+ print()
+
+ # Make compatible with FINN driver
+ pre_processed_inputs = np.asarray(pre_processed_inputs)
+ pre_processed_inputs = np.squeeze(pre_processed_inputs)
+ pre_processed_inputs = pre_processed_inputs.astype(np.int8)
+
+ # Save data
+ export_path = output_dir + "/" + f_name.replace(".npz", "_{}_len_{}.npy")
+ print(f"Saving data to: {export_path}")
+ np.save(
+ export_path.format("inputs", len(pre_processed_inputs)), pre_processed_inputs
+ )
+ np.save(export_path.format("outputs", len(label_arr)), label_arr)
diff --git a/build/kws/expected_output.npy b/build/kws/expected_output.npy
new file mode 100644
index 0000000..0058afe
Binary files /dev/null and b/build/kws/expected_output.npy differ
diff --git a/build/kws/get-kws-data-model.sh b/build/kws/get-kws-data-model.sh
new file mode 100755
index 0000000..50c2314
--- /dev/null
+++ b/build/kws/get-kws-data-model.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copyright (c) 2020, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Download validation data and model
+wget https://github.com/Xilinx/finn-examples/releases/download/kws/python_speech_preprocessing_all_validation_KWS_data.npz
+wget https://github.com/Xilinx/finn-examples/releases/download/kws/MLP_W3A3_python_speech_features_pre-processing_QONNX.onnx
diff --git a/build/kws/input.npy b/build/kws/input.npy
new file mode 100644
index 0000000..d257362
Binary files /dev/null and b/build/kws/input.npy differ
diff --git a/build/mobilenet-v1/custom_steps.py b/build/mobilenet-v1/custom_steps.py
index 9f30597..3643b30 100644
--- a/build/mobilenet-v1/custom_steps.py
+++ b/build/mobilenet-v1/custom_steps.py
@@ -36,7 +36,7 @@
import finn.transformation.streamline.reorder as reorder
from finn.transformation.infer_data_layouts import InferDataLayouts
from finn.transformation.streamline.collapse_repeated import CollapseRepeatedMul
-from finn.transformation.streamline.remove import RemoveIdentityOps
+from finn.transformation.remove import RemoveIdentityOps
from finn.transformation.streamline.round_thresholds import RoundAndClipThresholds
from finn.transformation.lower_convs_to_matmul import LowerConvsToMatMul
from finn.transformation.general import (
@@ -78,6 +78,7 @@ def step_mobilenet_streamline(model: ModelWrapper, cfg: DataflowBuildConfig):
def step_mobilenet_lower_convs(model: ModelWrapper, cfg: DataflowBuildConfig):
model = model.transform(LowerConvsToMatMul())
model = model.transform(absorb.AbsorbTransposeIntoMultiThreshold())
+ model = model.transform(absorb.AbsorbConsecutiveTransposes())
model = model.transform(GiveUniqueNodeNames())
model = model.transform(GiveReadableTensorNames())
model = model.transform(InferDataTypes())
diff --git a/build/resnet50/custom_steps.py b/build/resnet50/custom_steps.py
index 518554c..afa1693 100644
--- a/build/resnet50/custom_steps.py
+++ b/build/resnet50/custom_steps.py
@@ -52,6 +52,7 @@
FactorOutMulSignMagnitude,
Absorb1BitMulIntoMatMul,
Absorb1BitMulIntoConv,
+ AbsorbConsecutiveTransposes,
)
from finn.transformation.streamline.collapse_repeated import (
@@ -80,7 +81,7 @@
)
from finn.transformation.double_to_single_float import DoubleToSingleFloat
-from finn.transformation.streamline.remove import RemoveIdentityOps
+from finn.transformation.remove import RemoveIdentityOps
from finn.core.datatype import DataType
from finn.transformation.infer_shapes import InferShapes
@@ -178,6 +179,7 @@ def step_resnet50_streamline_linear(model: ModelWrapper, cfg: DataflowBuildConfi
AbsorbMulIntoMultiThreshold(),
Absorb1BitMulIntoMatMul(),
Absorb1BitMulIntoConv(),
+ RoundAndClipThresholds(),
]
for trn in streamline_transformations:
model = model.transform(trn)
@@ -213,7 +215,7 @@ def step_resnet50_streamline(model: ModelWrapper, cfg: DataflowBuildConfig):
def step_resnet50_convert_to_hls(model: ModelWrapper, cfg: DataflowBuildConfig):
- model.set_tensor_datatype(model.graph.input[0].name, DataType.UINT8)
+ model.set_tensor_datatype(model.graph.input[0].name, DataType["UINT8"])
model = model.transform(InferDataLayouts())
try:
@@ -239,8 +241,7 @@ def step_resnet50_convert_to_hls(model: ModelWrapper, cfg: DataflowBuildConfig):
AbsorbConsecutiveTransposes,
to_hls.InferConvInpGen,
to_hls.InferDuplicateStreamsLayer,
- to_hls.InferLabelSelectLayer,
-
+ to_hls.InferLabelSelectLayer
]
for trn in to_hls_transformations:
model = model.transform(trn())
@@ -307,6 +308,13 @@ def step_resnet50_set_fifo_depths(model: ModelWrapper, cfg: DataflowBuildConfig)
model, cfg.output_dir + "/final_hw_config.json", hw_attrs
)
+ # after FIFOs are ready to go, call PrepareIP and HLSSynthIP again
+ # this will only run for the new nodes (e.g. FIFOs and DWCs)
+ model = model.transform(
+ PrepareIP(cfg._resolve_fpga_part(), cfg._resolve_hls_clk_period())
+ )
+ model = model.transform(HLSSynthIP())
+ model = model.transform(ReplaceVerilogRelPaths())
return model
diff --git a/build/vgg10-radioml/README.md b/build/vgg10-radioml/README.md
new file mode 100755
index 0000000..17a4524
--- /dev/null
+++ b/build/vgg10-radioml/README.md
@@ -0,0 +1,33 @@
+# VGG10
+
+This 1-dimensional CNN was [introduced](https://arxiv.org/pdf/1712.04578.pdf) by DeepSig alongside their RadioML 2018 dataset for RF modulation classification.
+It consists of 7 1D convolution + maxpooling layers, followed by 2 hidden dense layers and the final dense classification layer. ReLU activations and Batchnorm are applied throughout the network. The input is a frame of 1024 I/Q samples (i.e. shape [1024,2]), the classifier distinguishes 24 classes (i.e. modulation types).
+
+Here, we use a reduced-precision implementation trained on [RadioML 2018.01A](https://www.deepsig.ai/datasets) with Brevitas. The weights and activations are quantized to 4-bit. The number of filters in the convolution layers has been reduced from 64 to 32. The pre-trained model reaches 55.9% overall accuracy and 87.9% at the highest SNR (30 dB). At 250MHz, the accelerator reaches ~230k frames/s (236M samples/s) with the supplied folding configuration.
+
+## Build bitfiles for VGG10
+
+Due to the 1-dimensional topology in VGG10 we use a specialized build script that adds a few custom build steps to the standard steps in FINN.
+**We currently provide bitstreams and the corresponding folding configuration only for the ZCU104, but plan to extend to other boards in the future.**
+
+0. Ensure you have performed the *Setup* steps in the top-level README for setting up the FINN requirements and environment variables.
+
+1. Run the `download_vgg10.sh` script under the `models` directory to download the pretrained VGG10 ONNX model. You should have e.g. `vgg10-radioml/models/radioml_w4a4_small_tidy.onnx` as a result.
+
+2. Launch the build as follows:
+```SHELL
+# update this according to where you cloned this repo:
+FINN_EXAMPLES=/path/to/finn-examples
+# cd into finn submodule
+cd $FINN_EXAMPLES/build/finn
+# launch the build on the vgg10 folder
+./run-docker.sh build_custom $FINN_EXAMPLES/build/vgg10
+```
+
+5. The generated outputs will be under `vgg10-radioml/output__`. You can find a description of the generated files [here](https://finn-dev.readthedocs.io/en/latest/command_line.html#simple-dataflow-build-mode).
+
+## Where did the ONNX model files come from?
+
+The quantized VGG10 is based on the baseline topology for our problem statement in the ITU AI/ML in 5G Challenge. You can find it in our [sandbox repository](https://github.com/Xilinx/brevitas-radioml-challenge-21).
+
+In addition, the ONNX model has been tidied up by removing the input quantization, which we do in software for this example, and by adding a top-k (k=1) node at the output. Thus, the accelerator returns the top-1 class index instead of logits.
diff --git a/build/vgg10-radioml/build.py b/build/vgg10-radioml/build.py
new file mode 100755
index 0000000..623804e
--- /dev/null
+++ b/build/vgg10-radioml/build.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2020, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import finn.builder.build_dataflow as build
+import finn.builder.build_dataflow_config as build_cfg
+from finn.util.basic import alveo_default_platform
+import os
+import shutil
+
+# custom steps
+from custom_steps import step_pre_streamline, step_convert_final_layers
+
+model_name = "radioml_w4a4_small_tidy"
+
+# which platforms to build the networks for
+zynq_platforms = ["ZCU104"]
+alveo_platforms = []
+platforms_to_build = zynq_platforms + alveo_platforms
+
+
+# determine which shell flow to use for a given platform
+def platform_to_shell(platform):
+ if platform in zynq_platforms:
+ return build_cfg.ShellFlowType.VIVADO_ZYNQ
+ elif platform in alveo_platforms:
+ return build_cfg.ShellFlowType.VITIS_ALVEO
+ else:
+ raise Exception("Unknown platform, can't determine ShellFlowType")
+
+
+# select target clock frequency
+def select_clk_period(platform):
+ return 4.0
+
+
+# assemble build flow from custom and pre-existing steps
+def select_build_steps(platform):
+ return [
+ "step_tidy_up",
+ step_pre_streamline,
+ "step_streamline",
+ "step_convert_to_hls",
+ step_convert_final_layers,
+ "step_create_dataflow_partition",
+ "step_target_fps_parallelization",
+ "step_apply_folding_config",
+ "step_generate_estimate_reports",
+ "step_hls_codegen",
+ "step_hls_ipgen",
+ "step_set_fifo_depths",
+ "step_create_stitched_ip",
+ "step_measure_rtlsim_performance",
+ "step_out_of_context_synthesis",
+ "step_synthesize_bitfile",
+ "step_make_pynq_driver",
+ "step_deployment_package",
+ ]
+
+
+# create a release dir, used for finn-examples release packaging
+os.makedirs("release", exist_ok=True)
+
+for platform_name in platforms_to_build:
+ shell_flow_type = platform_to_shell(platform_name)
+ if shell_flow_type == build_cfg.ShellFlowType.VITIS_ALVEO:
+ vitis_platform = alveo_default_platform[platform_name]
+ # for Alveo, use the Vitis platform name as the release name
+ # e.g. xilinx_u250_xdma_201830_2
+ release_platform_name = vitis_platform
+ else:
+ vitis_platform = None
+ # for Zynq, use the board name as the release name
+ # e.g. ZCU104
+ release_platform_name = platform_name
+ platform_dir = "release/%s" % release_platform_name
+ os.makedirs(platform_dir, exist_ok=True)
+
+ cfg = build_cfg.DataflowBuildConfig(
+ steps=select_build_steps(platform_name),
+ output_dir="output_%s_%s" % (model_name, release_platform_name),
+ synth_clk_period_ns=select_clk_period(platform_name),
+ board=platform_name,
+ shell_flow_type=shell_flow_type,
+ vitis_platform=vitis_platform,
+ folding_config_file="folding_config/%s_folding_config.json" % platform_name,
+ auto_fifo_depths=True,
+ standalone_thresholds=False,
+ # enable extra performance optimizations (physopt)
+ vitis_opt_strategy=build_cfg.VitisOptStrategyCfg.PERFORMANCE_BEST,
+ generate_outputs=[
+ build_cfg.DataflowOutputType.ESTIMATE_REPORTS,
+ build_cfg.DataflowOutputType.STITCHED_IP,
+ # build_cfg.DataflowOutputType.OOC_SYNTH,
+ # build_cfg.DataflowOutputType.RTLSIM_PERFORMANCE,
+ build_cfg.DataflowOutputType.BITFILE,
+ build_cfg.DataflowOutputType.DEPLOYMENT_PACKAGE,
+ build_cfg.DataflowOutputType.PYNQ_DRIVER,
+ ],
+ )
+ model_file = "models/%s.onnx" % model_name
+ build.build_dataflow_cfg(model_file, cfg)
+
+ # copy bitfiles and runtime weights into release dir if found
+ bitfile_gen_dir = cfg.output_dir + "/bitfile"
+ files_to_check_and_copy = [
+ "finn-accel.bit",
+ "finn-accel.hwh",
+ "finn-accel.xclbin",
+ ]
+ for f in files_to_check_and_copy:
+ src_file = bitfile_gen_dir + "/" + f
+ dst_file = platform_dir + "/" + f.replace("finn-accel", model_name)
+ if os.path.isfile(src_file):
+ shutil.copy(src_file, dst_file)
+
+ weight_gen_dir = cfg.output_dir + "/driver/runtime_weights"
+ weight_dst_dir = platform_dir + "/%s_runtime_weights" % model_name
+ if os.path.isdir(weight_gen_dir):
+ weight_files = os.listdir(weight_gen_dir)
+ if weight_files:
+ shutil.copytree(weight_gen_dir, weight_dst_dir)
+
+ # create zipfile for all examples for this platform
+ shutil.make_archive(
+ "release/" + release_platform_name,
+ "zip",
+ root_dir="release",
+ base_dir=release_platform_name,
+ )
diff --git a/build/vgg10-radioml/custom_steps.py b/build/vgg10-radioml/custom_steps.py
new file mode 100755
index 0000000..2953fb7
--- /dev/null
+++ b/build/vgg10-radioml/custom_steps.py
@@ -0,0 +1,46 @@
+# Copyright (c) 2020, Xilinx
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of FINN nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from finn.core.modelwrapper import ModelWrapper
+from finn.builder.build_dataflow_config import DataflowBuildConfig
+from finn.transformation.change_3d_tensors_to_4d import Change3DTo4DTensors
+from finn.transformation.general import GiveUniqueNodeNames
+import finn.transformation.fpgadataflow.convert_to_hls_layers as to_hls
+import finn.transformation.streamline.absorb as absorb
+
+
+def step_pre_streamline(model: ModelWrapper, cfg: DataflowBuildConfig):
+ model = model.transform(Change3DTo4DTensors())
+ model = model.transform(absorb.AbsorbScalarMulAddIntoTopK())
+ return model
+
+
+def step_convert_final_layers(model: ModelWrapper, cfg: DataflowBuildConfig):
+ model = model.transform(to_hls.InferChannelwiseLinearLayer())
+ model = model.transform(to_hls.InferLabelSelectLayer())
+ model = model.transform(GiveUniqueNodeNames())
+ return model
diff --git a/build/vgg10-radioml/folding_config/ZCU104_folding_config.json b/build/vgg10-radioml/folding_config/ZCU104_folding_config.json
new file mode 100755
index 0000000..94cd96c
--- /dev/null
+++ b/build/vgg10-radioml/folding_config/ZCU104_folding_config.json
@@ -0,0 +1,136 @@
+{
+ "Defaults": {},
+ "FMPadding_Batch_0": {
+ "SIMD": 2
+ },
+ "ConvolutionInputGenerator1D_0": {
+ "SIMD": 2,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_0": {
+ "PE": 32,
+ "SIMD": 6,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_0": {
+ },
+ "FMPadding_Batch_1": {
+ "SIMD": 16
+ },
+ "ConvolutionInputGenerator1D_1": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_1": {
+ "PE": 16,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_1": {
+ },
+ "FMPadding_Batch_2": {
+ "SIMD": 8
+ },
+ "ConvolutionInputGenerator1D_2": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_2": {
+ "PE": 8,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_2": {
+ },
+ "FMPadding_Batch_3": {
+ "SIMD": 8
+ },
+ "ConvolutionInputGenerator1D_3": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_3": {
+ "PE": 4,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_3": {
+ },
+ "FMPadding_Batch_4": {
+ "SIMD": 4
+ },
+ "ConvolutionInputGenerator1D_4": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_4": {
+ "PE": 2,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_4": {
+ },
+ "FMPadding_Batch_5": {
+ "SIMD": 2
+ },
+ "ConvolutionInputGenerator1D_5": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_5": {
+ "PE": 1,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_5": {
+ },
+ "FMPadding_Batch_6": {
+ "SIMD": 1
+ },
+ "ConvolutionInputGenerator1D_6": {
+ "SIMD": 32,
+ "ram_style": "auto"
+ },
+ "StreamingFCLayer_Batch_6": {
+ "PE": 1,
+ "SIMD": 96,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingMaxPool_Batch_6": {
+ },
+ "StreamingFCLayer_Batch_7": {
+ "PE": 2,
+ "SIMD": 32,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingFCLayer_Batch_8": {
+ "PE": 1,
+ "SIMD": 32,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ },
+ "StreamingFCLayer_Batch_9": {
+ "PE": 1,
+ "SIMD": 8,
+ "ram_style": "auto",
+ "mem_mode": "const",
+ "runtime_writeable_weights": 0
+ }
+}
diff --git a/build/vgg10-radioml/models/download_vgg10.sh b/build/vgg10-radioml/models/download_vgg10.sh
new file mode 100755
index 0000000..1d6bae6
--- /dev/null
+++ b/build/vgg10-radioml/models/download_vgg10.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+wget https://github.com/Xilinx/finn-examples/releases/download/radioml/radioml_w4a4_small_tidy.onnx
diff --git a/docs/img/keyword-spotting.png b/docs/img/keyword-spotting.png
new file mode 100644
index 0000000..dea15b0
Binary files /dev/null and b/docs/img/keyword-spotting.png differ
diff --git a/docs/img/maskedfacenet.jpg b/docs/img/maskedfacenet.jpg
new file mode 100644
index 0000000..de18995
Binary files /dev/null and b/docs/img/maskedfacenet.jpg differ
diff --git a/docs/img/radioml.png b/docs/img/radioml.png
new file mode 100644
index 0000000..caf42b5
Binary files /dev/null and b/docs/img/radioml.png differ
diff --git a/finn_examples/bitfiles/bitfiles.zip.link b/finn_examples/bitfiles/bitfiles.zip.link
index 95e7e0d..9e93b92 100644
--- a/finn_examples/bitfiles/bitfiles.zip.link
+++ b/finn_examples/bitfiles/bitfiles.zip.link
@@ -1,7 +1,7 @@
{
"Pynq-Z1": {
- "url": "https://github.com/Xilinx/finn-examples/releases/download/v0.0.1a/Pynq-Z1.zip",
- "md5sum": "04f4887540a2156bd2886c9bb2bb2ff6"
+ "url": "https://github.com/Xilinx/finn-examples/releases/download/kws/Pynq-Z1.zip",
+ "md5sum": "bf1df783bae7a1a477797d2eaa61eb9f"
},
"Pynq-Z2": {
"url": "https://github.com/Xilinx/finn-examples/releases/download/v0.0.1a/Pynq-Z2.zip",
@@ -12,8 +12,8 @@
"md5sum": "59598d7f36ffdc74a0a0262f5b67423c"
},
"ZCU104": {
- "url": "https://github.com/Xilinx/finn-examples/releases/download/mnv1-zcu104/ZCU104.zip",
- "md5sum": "1ed10d74e85eec70fd094b2947b5b8e3"
+ "url": "https://github.com/Xilinx/finn-examples/releases/download/radioml/ZCU104.zip",
+ "md5sum": "9b7edad0511da9cb3c834a289d6797a2"
},
"xilinx_u250_xdma_201830_2": {
"url": "https://github.com/Xilinx/finn-examples/releases/download/rn50-u250/xilinx_u250_xdma_201830_2.zip",
diff --git a/finn_examples/data/__init__.py b/finn_examples/data/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/finn_examples/data/all_validation_kws_data_preprocessed_py_speech.zip.link b/finn_examples/data/all_validation_kws_data_preprocessed_py_speech.zip.link
new file mode 100644
index 0000000..5071178
--- /dev/null
+++ b/finn_examples/data/all_validation_kws_data_preprocessed_py_speech.zip.link
@@ -0,0 +1,4 @@
+{
+ "url": "https://github.com/Xilinx/finn-examples/releases/download/kws/all_validation_kws_data_preprocessed_py_speech.zip",
+ "md5sum": "58d1435354c7ac7ba5ef9bdf5e2b7210"
+}
diff --git a/finn_examples/data/audio_samples.zip.link b/finn_examples/data/audio_samples.zip.link
new file mode 100644
index 0000000..2b8e06e
--- /dev/null
+++ b/finn_examples/data/audio_samples.zip.link
@@ -0,0 +1,4 @@
+{
+ "url": "https://github.com/Xilinx/finn-examples/releases/download/kws/audio_samples.zip",
+ "md5sum": "e1fd31a78001c9d1d9a4ab53283ca5ce"
+}
\ No newline at end of file
diff --git a/finn_examples/driver.py b/finn_examples/driver.py
index 4dd5a08..d4e69e7 100644
--- a/finn_examples/driver.py
+++ b/finn_examples/driver.py
@@ -418,9 +418,9 @@ def throughput_test(self):
# also benchmark driver-related overheads
input_npy = gen_finn_dt_tensor(self.idt, self.ishape_normal)
# provide as int8/uint8 to support fast packing path where possible
- if self.idt == DataType.UINT8:
+ if self.idt == DataType["UINT8"]:
input_npy = input_npy.astype(np.uint8)
- elif self.idt == DataType.INT8:
+ elif self.idt == DataType["INT8"]:
input_npy = input_npy.astype(np.int8)
start = time.time()
ibuf_folded = self.fold_input(input_npy)
diff --git a/finn_examples/models.py b/finn_examples/models.py
index 9dd69aa..274af04 100644
--- a/finn_examples/models.py
+++ b/finn_examples/models.py
@@ -36,8 +36,8 @@
from finn_examples.driver import FINNExampleOverlay
_mnist_fc_io_shape_dict = {
- "idt": DataType.UINT8,
- "odt": DataType.UINT8,
+ "idt": DataType["UINT8"],
+ "odt": DataType["UINT8"],
"ishape_normal": (1, 784),
"oshape_normal": (1, 1),
"ishape_folded": (1, 1, 784),
@@ -47,8 +47,8 @@
}
_cifar10_cnv_io_shape_dict = {
- "idt": DataType.UINT8,
- "odt": DataType.UINT8,
+ "idt": DataType["UINT8"],
+ "odt": DataType["UINT8"],
"ishape_normal": (1, 32, 32, 3),
"oshape_normal": (1, 1),
"ishape_folded": (1, 1, 32, 32, 1, 3),
@@ -57,9 +57,20 @@
"oshape_packed": (1, 1, 1),
}
+_bincop_cnv_io_shape_dict = {
+ "idt": DataType["UINT8"],
+ "odt": DataType["UINT8"],
+ "ishape_normal": (1, 72, 72, 3),
+ "oshape_normal": (1, 1),
+ "ishape_folded": (1, 1, 72, 72, 1, 3),
+ "oshape_folded": (1, 1, 1),
+ "ishape_packed": (1, 1, 72, 72, 1, 3),
+ "oshape_packed": (1, 1, 1),
+}
+
_imagenet_top5inds_io_shape_dict = {
- "idt": DataType.UINT8,
- "odt": DataType.UINT16,
+ "idt": DataType["UINT8"],
+ "odt": DataType["UINT16"],
"ishape_normal": (1, 224, 224, 3),
"oshape_normal": (1, 1, 1, 5),
"ishape_folded": (1, 224, 224, 1, 3),
@@ -71,18 +82,40 @@
# resnet50 uses a different io_shape_dict due to
# external weights for last layer
_imagenet_resnet50_top5inds_io_shape_dict = {
- "idt" : DataType.UINT8,
- "odt" : DataType.UINT16,
- "ishape_normal" : (1, 224, 224, 3),
- "oshape_normal" : (1, 5),
- "ishape_folded" : (1, 224, 224, 3),
- "oshape_folded" : (1, 5, 1),
- "ishape_packed" : (1, 224, 224, 3),
- "oshape_packed" : (1, 5, 2),
- "input_dma_name" : 'idma1',
- "number_of_external_weights": 1
+ "idt": DataType["UINT8"],
+ "odt": DataType["UINT16"],
+ "ishape_normal": (1, 224, 224, 3),
+ "oshape_normal": (1, 5),
+ "ishape_folded": (1, 224, 224, 3),
+ "oshape_folded": (1, 5, 1),
+ "ishape_packed": (1, 224, 224, 3),
+ "oshape_packed": (1, 5, 2),
+ "input_dma_name": "idma1",
+ "number_of_external_weights": 1,
}
+_radioml_io_shape_dict = {
+ "idt": DataType["INT8"],
+ "odt": DataType["UINT8"],
+ "ishape_normal": (1, 1024, 1, 2),
+ "oshape_normal": (1, 1),
+ "ishape_folded": (1, 1024, 1, 1, 2),
+ "oshape_folded": (1, 1, 1),
+ "ishape_packed": (1, 1024, 1, 1, 2),
+ "oshape_packed": (1, 1, 1),
+}
+
+_gscv2_mlp_io_shape_dict = {
+ "idt" : DataType['INT8'],
+ "odt" : DataType['UINT8'],
+ "ishape_normal" : (1, 490),
+ "oshape_normal" : (1, 1),
+ "ishape_folded" : (1, 49, 10),
+ "oshape_folded" : (1, 1, 1),
+ "ishape_packed" : (1, 49, 10),
+ "oshape_packed" : (1, 1, 1),
+ "input_dma_name" : 'idma0',
+}
# from https://github.com/Xilinx/PYNQ-HelloWorld/blob/master/setup.py
# get current platform: either edge or pcie
@@ -154,6 +187,12 @@ def resolve_target_platform(target_platform):
assert target_platform in [x.name for x in pynq.Device.devices]
return target_platform
+def kws_mlp(target_platform=None):
+ target_platform = resolve_target_platform(target_platform)
+ driver_mode = get_driver_mode()
+ model_name = "kwsmlp-w3a3"
+ filename = find_bitfile(model_name, target_platform)
+ return FINNExampleOverlay(filename, driver_mode, _gscv2_mlp_io_shape_dict)
def tfc_w1a1_mnist(target_platform=None):
target_platform = resolve_target_platform(target_platform)
@@ -203,6 +242,14 @@ def cnv_w2a2_cifar10(target_platform=None):
return FINNExampleOverlay(filename, driver_mode, _cifar10_cnv_io_shape_dict)
+def bincop_cnv(target_platform=None):
+ target_platform = resolve_target_platform(target_platform)
+ driver_mode = get_driver_mode()
+ model_name = "bincop-cnv"
+ filename = find_bitfile(model_name, target_platform)
+ return FINNExampleOverlay(filename, driver_mode, _bincop_cnv_io_shape_dict)
+
+
def mobilenetv1_w4a4_imagenet(target_platform=None):
target_platform = resolve_target_platform(target_platform)
driver_mode = get_driver_mode()
@@ -222,6 +269,7 @@ def mobilenetv1_w4a4_imagenet(target_platform=None):
fclk_mhz=fclk_mhz,
)
+
def resnet50_w1a2_imagenet(target_platform=None):
target_platform = resolve_target_platform(target_platform)
driver_mode = get_driver_mode()
@@ -235,3 +283,16 @@ def resnet50_w1a2_imagenet(target_platform=None):
runtime_weight_dir=runtime_weight_dir,
)
+
+def vgg10_w4a4_radioml(target_platform=None):
+ target_platform = resolve_target_platform(target_platform)
+ driver_mode = get_driver_mode()
+ model_name = "vgg10-radioml-w4a4"
+ filename = find_bitfile(model_name, target_platform)
+ fclk_mhz = 250.0
+ return FINNExampleOverlay(
+ filename,
+ driver_mode,
+ _radioml_io_shape_dict,
+ fclk_mhz=fclk_mhz,
+ )
diff --git a/finn_examples/notebooks/0_mnist_with_fc_networks.ipynb b/finn_examples/notebooks/0_mnist_with_fc_networks.ipynb
index 3899427..a9d6cb7 100644
--- a/finn_examples/notebooks/0_mnist_with_fc_networks.ipynb
+++ b/finn_examples/notebooks/0_mnist_with_fc_networks.ipynb
@@ -2,112 +2,91 @@
"cells": [
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Initialize the accelerator"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 1,
- "metadata": {},
+ "source": [
+ "from finn_examples import models\n",
+ "print(list(filter(lambda x: \"mnist\" in x, dir(models))))"
+ ],
"outputs": [
{
+ "output_type": "display_data",
"data": {
- "application/javascript": [
- "\n",
- "try {\n",
- "require(['notebook/js/codecell'], function(codecell) {\n",
- " codecell.CodeCell.options_default.highlight_modes[\n",
- " 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
- " Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
- " Jupyter.notebook.get_cells().map(function(cell){\n",
- " if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
- " });\n",
- "});\n",
- "} catch (e) {};\n"
- ]
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
},
{
+ "output_type": "display_data",
"data": {
- "application/javascript": [
- "\n",
- "try {\n",
- "require(['notebook/js/codecell'], function(codecell) {\n",
- " codecell.CodeCell.options_default.highlight_modes[\n",
- " 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n",
- " Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
- " Jupyter.notebook.get_cells().map(function(cell){\n",
- " if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
- " });\n",
- "});\n",
- "} catch (e) {};\n"
- ]
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
},
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"['_mnist_fc_io_shape_dict', 'tfc_w1a1_mnist', 'tfc_w1a2_mnist', 'tfc_w2a2_mnist']\n"
]
}
],
- "source": [
- "from finn_examples import models\n",
- "print(list(filter(lambda x: \"mnist\" in x, dir(models))))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 2,
- "metadata": {},
- "outputs": [],
"source": [
"accel = models.tfc_w1a1_mnist()"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 3,
- "metadata": {},
+ "source": [
+ "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
+ "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Expected input shape and datatype: (1, 784) DataType.UINT8\n",
"Expected output shape and datatype: (1, 1) DataType.UINT8\n"
]
}
],
- "source": [
- "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
- "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Load the MNIST dataset\n",
"\n",
"Use the `dataset_loading` package to get easy Python access to MNIST dataset:"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 4,
- "metadata": {},
+ "source": [
+ "from dataset_loading import mnist\n",
+ "trainx, trainy, testx, testy, valx, valy = mnist.load_mnist_data(\"/tmp\", download=True, one_hot=False)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Looking for Train Imgs\n",
"Download URL: http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n",
@@ -128,171 +107,158 @@
]
}
],
- "source": [
- "from dataset_loading import mnist\n",
- "trainx, trainy, testx, testy, valx, valy = mnist.load_mnist_data(\"/tmp\", download=True, one_hot=False)"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 5,
- "metadata": {},
+ "source": [
+ "testx.shape"
+ ],
"outputs": [
{
+ "output_type": "execute_result",
"data": {
"text/plain": [
"(10000, 28, 28, 1)"
]
},
- "execution_count": 5,
"metadata": {},
- "output_type": "execute_result"
+ "execution_count": 5
}
],
- "source": [
- "testx.shape"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Classify a single image"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 6,
- "metadata": {},
- "outputs": [],
"source": [
"test_single_x = testx[0].reshape(28, 28)\n",
"test_single_y = testy[0]"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 8,
- "metadata": {},
+ "source": [
+ "%matplotlib inline\n",
+ "from matplotlib import pyplot as plt\n",
+ "\n",
+ "plt.imshow(test_single_x, cmap='gray')\n",
+ "plt.show()"
+ ],
"outputs": [
{
+ "output_type": "display_data",
"data": {
- "image/png": "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\n",
+ "image/png": "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",
"text/plain": [
""
]
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
}
],
- "source": [
- "from matplotlib import pyplot as plt\n",
- "\n",
- "plt.imshow(test_single_x, cmap='gray')\n",
- "plt.show()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 9,
- "metadata": {},
+ "source": [
+ "print(\"Expected class is %d\" % test_single_y)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Expected class is 7\n"
]
}
],
- "source": [
- "print(\"Expected class is %d\" % test_single_y)"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 10,
- "metadata": {},
+ "source": [
+ "accel_in = test_single_x.reshape(accel.ishape_normal)\n",
+ "print(\"Input buffer shape is %s and datatype is %s\" % (str(accel_in.shape), str(accel_in.dtype)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Input buffer shape is (1, 784) and datatype is uint8\n"
]
}
],
- "source": [
- "accel_in = test_single_x.reshape(accel.ishape_normal)\n",
- "print(\"Input buffer shape is %s and datatype is %s\" % (str(accel_in.shape), str(accel_in.dtype)))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 11,
- "metadata": {},
- "outputs": [],
"source": [
"accel_out = accel.execute(accel_in)"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 12,
- "metadata": {},
+ "source": [
+ "print(\"Returned class is %d\" % accel_out)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Returned class is 7\n"
]
}
],
- "source": [
- "print(\"Returned class is %d\" % accel_out)"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 13,
- "metadata": {},
+ "source": [
+ "%%timeit\n",
+ "accel_out = accel.execute(accel_in)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"1000 loops, best of 3: 808 µs per loop\n"
]
}
],
- "source": [
- "%%timeit\n",
- "accel_out = accel.execute(accel_in)"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Validate accuracy on entire MNIST test set"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Ready to run validation, test images tensor has shape (10, 1000, 784)\n",
- "Accelerator buffer shapes are (1000, 1, 784) for input, (1000, 1, 1) for output\n"
- ]
- }
- ],
"source": [
"import numpy as np\n",
"batch_size = 1000\n",
@@ -305,16 +271,38 @@
"obuf_normal = np.empty_like(accel.obuf_packed_device)\n",
"print(\"Ready to run validation, test images tensor has shape %s\" % str(batch_imgs.shape))\n",
"print(\"Accelerator buffer shapes are %s for input, %s for output\" % (str(accel.ishape_packed), str(accel.oshape_packed)) )"
- ]
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Ready to run validation, test images tensor has shape (10, 1000, 784)\n",
+ "Accelerator buffer shapes are (1000, 1, 784) for input, (1000, 1, 1) for output\n"
+ ]
+ }
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 22,
- "metadata": {},
+ "source": [
+ "ok = 0\n",
+ "nok = 0\n",
+ "for i in range(n_batches):\n",
+ " ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
+ " exp = batch_labels[i]\n",
+ " obuf_normal = accel.execute(ibuf_normal)\n",
+ " ret = np.bincount(obuf_normal.flatten() == exp.flatten())\n",
+ " nok += ret[0]\n",
+ " ok += ret[1]\n",
+ " print(\"batch %d / %d : total OK %d NOK %d\" % (i, n_batches, ok, nok))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"batch 0 / 10 : total OK 913 NOK 87\n",
"batch 1 / 10 : total OK 1800 NOK 200\n",
@@ -329,97 +317,89 @@
]
}
],
- "source": [
- "ok = 0\n",
- "nok = 0\n",
- "for i in range(n_batches):\n",
- " ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
- " exp = batch_labels[i]\n",
- " obuf_normal = accel.execute(ibuf_normal)\n",
- " ret = np.bincount(obuf_normal.flatten() == exp.flatten())\n",
- " nok += ret[0]\n",
- " ok += ret[1]\n",
- " print(\"batch %d / %d : total OK %d NOK %d\" % (i, n_batches, ok, nok))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 23,
- "metadata": {},
+ "source": [
+ "acc = 100.0 * ok / (total)\n",
+ "print(\"Final accuracy: {}%\".format(acc))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Final accuracy: 92.96%\n"
]
}
],
- "source": [
- "acc = 100.0 * ok / (total)\n",
- "print(\"Final accuracy: {}%\".format(acc))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 26,
- "metadata": {},
- "outputs": [],
"source": [
"def run_validation():\n",
" for i in range(n_batches):\n",
" ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
" exp = batch_labels[i]\n",
" accel.execute(ibuf_normal)"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 27,
- "metadata": {},
+ "source": [
+ "full_validation_time = %timeit -n 5 -o run_validation()"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"5 loops, best of 3: 22.6 ms per loop\n"
]
}
],
- "source": [
- "full_validation_time = %timeit -n 5 -o run_validation()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 28,
- "metadata": {},
+ "source": [
+ "print(\"%f images per second including data movement\" % (total / float(full_validation_time.best)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"441567.114603 images per second including data movement\n"
]
}
],
- "source": [
- "print(\"%f images per second including data movement\" % (total / float(full_validation_time.best)))"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"## Run some more built-in benchmarks"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 29,
- "metadata": {},
+ "source": [
+ "accel.throughput_test()"
+ ],
"outputs": [
{
+ "output_type": "execute_result",
"data": {
"text/plain": [
"{'DRAM_in_bandwidth[Mb/s]': 656.2231762123328,\n",
@@ -436,21 +416,18 @@
" 'unpack_output[ms]': 0.0006036758422851562}"
]
},
- "execution_count": 29,
"metadata": {},
- "output_type": "execute_result"
+ "execution_count": 29
}
],
- "source": [
- "accel.throughput_test()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "source": [],
"outputs": [],
- "source": []
+ "metadata": {}
}
],
"metadata": {
@@ -474,4 +451,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
-}
+}
\ No newline at end of file
diff --git a/finn_examples/notebooks/1_cifar10_with_cnv_networks.ipynb b/finn_examples/notebooks/1_cifar10_with_cnv_networks.ipynb
index 9f6a6f1..af305ff 100644
--- a/finn_examples/notebooks/1_cifar10_with_cnv_networks.ipynb
+++ b/finn_examples/notebooks/1_cifar10_with_cnv_networks.ipynb
@@ -2,112 +2,91 @@
"cells": [
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Initialize the accelerator"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 1,
- "metadata": {},
+ "source": [
+ "from finn_examples import models\n",
+ "print(list(filter(lambda x: \"cifar10\" in x, dir(models))))"
+ ],
"outputs": [
{
+ "output_type": "display_data",
"data": {
- "application/javascript": [
- "\n",
- "try {\n",
- "require(['notebook/js/codecell'], function(codecell) {\n",
- " codecell.CodeCell.options_default.highlight_modes[\n",
- " 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
- " Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
- " Jupyter.notebook.get_cells().map(function(cell){\n",
- " if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
- " });\n",
- "});\n",
- "} catch (e) {};\n"
- ]
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
},
{
+ "output_type": "display_data",
"data": {
- "application/javascript": [
- "\n",
- "try {\n",
- "require(['notebook/js/codecell'], function(codecell) {\n",
- " codecell.CodeCell.options_default.highlight_modes[\n",
- " 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n",
- " Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
- " Jupyter.notebook.get_cells().map(function(cell){\n",
- " if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
- " });\n",
- "});\n",
- "} catch (e) {};\n"
- ]
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
},
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"['_cifar10_cnv_io_shape_dict', 'cnv_w1a1_cifar10', 'cnv_w1a2_cifar10', 'cnv_w2a2_cifar10']\n"
]
}
],
- "source": [
- "from finn_examples import models\n",
- "print(list(filter(lambda x: \"cifar10\" in x, dir(models))))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 2,
- "metadata": {},
- "outputs": [],
"source": [
"accel = models.cnv_w1a1_cifar10()"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 3,
- "metadata": {},
+ "source": [
+ "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
+ "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Expected input shape and datatype: (1, 32, 32, 3) DataType.UINT8\n",
"Expected output shape and datatype: (1, 1) DataType.UINT8\n"
]
}
],
- "source": [
- "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
- "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Load the CIFAR-10 dataset\n",
"\n",
"Use the `dataset_loading` package to get easy Python access to CIFAR-10 dataset:"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 5,
- "metadata": {},
+ "source": [
+ "from dataset_loading import cifar\n",
+ "trainx, trainy, testx, testy, valx, valy = cifar.load_cifar_data(\"/tmp\", download=True, one_hot=False)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Tar File found in dest_dir. Not Downloading again\n",
"Extracting Python CIFAR10 data.\n",
@@ -115,172 +94,159 @@
]
}
],
- "source": [
- "from dataset_loading import cifar\n",
- "trainx, trainy, testx, testy, valx, valy = cifar.load_cifar_data(\"/tmp\", download=True, one_hot=False)"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 6,
- "metadata": {},
+ "source": [
+ "testx.shape"
+ ],
"outputs": [
{
+ "output_type": "execute_result",
"data": {
"text/plain": [
"(10000, 32, 32, 3)"
]
},
- "execution_count": 6,
"metadata": {},
- "output_type": "execute_result"
+ "execution_count": 6
}
],
- "source": [
- "testx.shape"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Classify a single image"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 7,
- "metadata": {},
- "outputs": [],
"source": [
"test_single_x = testx[0]\n",
"test_single_y = testy[0]\n",
"cifar10_class_names = ['Airplane', 'Automobile', 'Bird', 'Cat', 'Deer', 'Dog', 'Frog', 'Horse', 'Ship', 'Truck']\n"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 9,
- "metadata": {},
+ "source": [
+ "%matplotlib inline\n",
+ "from matplotlib import pyplot as plt\n",
+ "\n",
+ "plt.imshow(test_single_x)\n",
+ "plt.show()"
+ ],
"outputs": [
{
+ "output_type": "display_data",
"data": {
- "image/png": "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\n",
+ "image/png": "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",
"text/plain": [
""
]
},
- "metadata": {},
- "output_type": "display_data"
+ "metadata": {}
}
],
- "source": [
- "from matplotlib import pyplot as plt\n",
- "\n",
- "plt.imshow(test_single_x)\n",
- "plt.show()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 10,
- "metadata": {},
+ "source": [
+ "print(\"Expected class is %d (%s)\" % (test_single_y, cifar10_class_names[test_single_y]))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Expected class is 3 (Cat)\n"
]
}
],
- "source": [
- "print(\"Expected class is %d (%s)\" % (test_single_y, cifar10_class_names[test_single_y]))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 11,
- "metadata": {},
+ "source": [
+ "accel_in = test_single_x.reshape(accel.ishape_normal)\n",
+ "print(\"Input buffer shape is %s and datatype is %s\" % (str(accel_in.shape), str(accel_in.dtype)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Input buffer shape is (1, 32, 32, 3) and datatype is uint8\n"
]
}
],
- "source": [
- "accel_in = test_single_x.reshape(accel.ishape_normal)\n",
- "print(\"Input buffer shape is %s and datatype is %s\" % (str(accel_in.shape), str(accel_in.dtype)))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 12,
- "metadata": {},
- "outputs": [],
"source": [
"accel_out = accel.execute(accel_in)"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 13,
- "metadata": {},
+ "source": [
+ "print(\"Returned class is %d\" % accel_out)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Returned class is 3\n"
]
}
],
- "source": [
- "print(\"Returned class is %d\" % accel_out)"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 14,
- "metadata": {},
+ "source": [
+ "%%timeit\n",
+ "accel_out = accel.execute(accel_in)"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"100 loops, best of 3: 2.34 ms per loop\n"
]
}
],
- "source": [
- "%%timeit\n",
- "accel_out = accel.execute(accel_in)"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"# Validate accuracy on entire CIFAR-10 test set"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Ready to run validation, test images tensor has shape (10, 1000, 3072)\n",
- "Accelerator buffer shapes are (1000, 1, 32, 32, 1, 3) for input, (1000, 1, 1) for output\n"
- ]
- }
- ],
"source": [
"import numpy as np\n",
"\n",
@@ -294,16 +260,38 @@
"obuf_normal = np.empty_like(accel.obuf_packed_device)\n",
"print(\"Ready to run validation, test images tensor has shape %s\" % str(batch_imgs.shape))\n",
"print(\"Accelerator buffer shapes are %s for input, %s for output\" % (str(accel.ishape_packed), str(accel.oshape_packed)) )"
- ]
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Ready to run validation, test images tensor has shape (10, 1000, 3072)\n",
+ "Accelerator buffer shapes are (1000, 1, 32, 32, 1, 3) for input, (1000, 1, 1) for output\n"
+ ]
+ }
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 16,
- "metadata": {},
+ "source": [
+ "ok = 0\n",
+ "nok = 0\n",
+ "for i in range(n_batches):\n",
+ " ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
+ " exp = batch_labels[i]\n",
+ " obuf_normal = accel.execute(ibuf_normal)\n",
+ " ret = np.bincount(obuf_normal.flatten() == exp.flatten())\n",
+ " nok += ret[0]\n",
+ " ok += ret[1]\n",
+ " print(\"batch %d / %d : total OK %d NOK %d\" % (i, n_batches, ok, nok))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"batch 0 / 10 : total OK 851 NOK 149\n",
"batch 1 / 10 : total OK 1683 NOK 317\n",
@@ -318,97 +306,89 @@
]
}
],
- "source": [
- "ok = 0\n",
- "nok = 0\n",
- "for i in range(n_batches):\n",
- " ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
- " exp = batch_labels[i]\n",
- " obuf_normal = accel.execute(ibuf_normal)\n",
- " ret = np.bincount(obuf_normal.flatten() == exp.flatten())\n",
- " nok += ret[0]\n",
- " ok += ret[1]\n",
- " print(\"batch %d / %d : total OK %d NOK %d\" % (i, n_batches, ok, nok))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 17,
- "metadata": {},
+ "source": [
+ "acc = 100.0 * ok / (total)\n",
+ "print(\"Final accuracy: {}%\".format(acc))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"Final accuracy: 84.19%\n"
]
}
],
- "source": [
- "acc = 100.0 * ok / (total)\n",
- "print(\"Final accuracy: {}%\".format(acc))"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 18,
- "metadata": {},
- "outputs": [],
"source": [
"def run_validation():\n",
" for i in range(n_batches):\n",
" ibuf_normal = batch_imgs[i].reshape(accel.ishape_normal)\n",
" exp = batch_labels[i]\n",
" accel.execute(ibuf_normal)"
- ]
+ ],
+ "outputs": [],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 20,
- "metadata": {},
+ "source": [
+ "full_validation_time = %timeit -n 1 -o run_validation()"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"1 loop, best of 3: 3.34 s per loop\n"
]
}
],
- "source": [
- "full_validation_time = %timeit -n 1 -o run_validation()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 21,
- "metadata": {},
+ "source": [
+ "print(\"%f images per second including data movement\" % (total / float(full_validation_time.best)))"
+ ],
"outputs": [
{
- "name": "stdout",
"output_type": "stream",
+ "name": "stdout",
"text": [
"2995.076851 images per second including data movement\n"
]
}
],
- "source": [
- "print(\"%f images per second including data movement\" % (total / float(full_validation_time.best)))"
- ]
+ "metadata": {}
},
{
"cell_type": "markdown",
- "metadata": {},
"source": [
"## More benchmarking"
- ]
+ ],
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": 23,
- "metadata": {},
+ "source": [
+ "accel.throughput_test()"
+ ],
"outputs": [
{
+ "output_type": "execute_result",
"data": {
"text/plain": [
"{'DRAM_in_bandwidth[Mb/s]': 9.278965852281484,\n",
@@ -425,21 +405,18 @@
" 'unpack_output[ms]': 0.0006213188171386719}"
]
},
- "execution_count": 23,
"metadata": {},
- "output_type": "execute_result"
+ "execution_count": 23
}
],
- "source": [
- "accel.throughput_test()"
- ]
+ "metadata": {}
},
{
"cell_type": "code",
"execution_count": null,
- "metadata": {},
+ "source": [],
"outputs": [],
- "source": []
+ "metadata": {}
}
],
"metadata": {
@@ -463,4 +440,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
-}
+}
\ No newline at end of file
diff --git a/finn_examples/notebooks/2_imagenet_with_cnns.ipynb b/finn_examples/notebooks/2_imagenet_with_cnns.ipynb
index 164f1ef..93ed7ed 100755
--- a/finn_examples/notebooks/2_imagenet_with_cnns.ipynb
+++ b/finn_examples/notebooks/2_imagenet_with_cnns.ipynb
@@ -166,6 +166,7 @@
"def setup_dataloader(val_path, label_file_path = None, batch_size=100, n_images = 50000):\n",
" if label_file_path is None:\n",
" val_folders = [ f.name for f in os.scandir(val_path) if f.is_dir() ]\n",
+ " val_folders = sorted(val_folders)\n",
" assert len(val_folders) == 1000, \"Expected 1000 subfolders in ILSVRC2012 val\"\n",
" files = []\n",
" labels = []\n",
diff --git a/finn_examples/notebooks/3_binarycop_mask_detection.ipynb b/finn_examples/notebooks/3_binarycop_mask_detection.ipynb
new file mode 100644
index 0000000..15688c1
--- /dev/null
+++ b/finn_examples/notebooks/3_binarycop_mask_detection.ipynb
@@ -0,0 +1,457 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "source": [
+ "from finn_examples import models\n",
+ "import os\n",
+ "from PIL import Image\n",
+ "import numpy as np\n",
+ "import cv2"
+ ],
+ "outputs": [
+ {
+ "output_type": "display_data",
+ "data": {
+ "application/javascript": "\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n"
+ },
+ "metadata": {}
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Initialize the Accelerator"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "source": [
+ "# Note: the face mask detection example is only available on Pynq-Z1 at the moment\n",
+ "accel = models.bincop_cnv()"
+ ],
+ "outputs": [],
+ "metadata": {
+ "scrolled": true
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "source": [
+ "class_dict = {0: \"Correctly Masked\", 1: \"Incorrectly Worn\", 2: \"No Mask\"}\n",
+ "\n",
+ "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
+ "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Expected input shape and datatype: (1, 72, 72, 3) DataType.UINT8\n",
+ "Expected output shape and datatype: (1, 1) DataType.UINT8\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Load Mask Examples"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "source": [
+ "mask_examples_dir = \"/tmp/mask_examples\"\n",
+ "if not os.path.exists(mask_examples_dir):\n",
+ " os.makedirs(mask_examples_dir)\n",
+ " \n",
+ "for i in range(6):\n",
+ " if \"{}.jpg\".format(i+1) not in os.listdir(mask_examples_dir):\n",
+ " os.system(\"wget -P \" + mask_examples_dir + \" https://github.com/NaelF/BinaryCoP/raw/master/notebook/pictures/{}.jpg\".format(i+1))"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Run Inference"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "source": [
+ "def resize(img):\n",
+ " img = np.array(img)\n",
+ " if img.shape[0] != 72 or img.shape[1] != 72:\n",
+ " resized_img = cv2.resize(img,(72,72))\n",
+ " return (resized_img) \n",
+ " else: return img"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "source": [
+ "im = Image.open(mask_examples_dir + '/1.jpg')\n",
+ "im = resize(im)\n",
+ "accel_in = im.reshape(accel.ishape_normal)\n",
+ "im = Image.fromarray(im, 'RGB')\n",
+ "display(im)\n",
+ "accel_out = accel.execute(accel_in)\n",
+ "print(\"Returned class is: \" + class_dict[int(accel_out)])\n",
+ "\n",
+ "im = Image.open(mask_examples_dir + '/2.jpg')\n",
+ "im = resize(im)\n",
+ "accel_in = im.reshape(accel.ishape_normal)\n",
+ "im = Image.fromarray(im, 'RGB')\n",
+ "display(im)\n",
+ "accel_out = accel.execute(accel_in)\n",
+ "print(\"Returned class is: \" + class_dict[int(accel_out)])\n",
+ "\n",
+ "im = Image.open(mask_examples_dir + '/3.jpg')\n",
+ "im = resize(im)\n",
+ "accel_in = im.reshape(accel.ishape_normal)\n",
+ "im = Image.fromarray(im, 'RGB')\n",
+ "display(im)\n",
+ "accel_out = accel.execute(accel_in)\n",
+ "print(\"Returned class is: \" + class_dict[int(accel_out)])\n",
+ "\n",
+ "im = Image.open(mask_examples_dir + '/4.jpg')\n",
+ "im = resize(im)\n",
+ "accel_in = im.reshape(accel.ishape_normal)\n",
+ "im = Image.fromarray(im, 'RGB')\n",
+ "display(im)\n",
+ "accel_out = accel.execute(accel_in)\n",
+ "print(\"Returned class is: \" + class_dict[int(accel_out)])\n",
+ "\n",
+ "im = Image.open(mask_examples_dir + '/5.jpg')\n",
+ "im = resize(im)\n",
+ "accel_in = im.reshape(accel.ishape_normal)\n",
+ "im = Image.fromarray(im, 'RGB')\n",
+ "display(im)\n",
+ "accel_out = accel.execute(accel_in)\n",
+ "print(\"Returned class is: \" + class_dict[int(accel_out)])"
+ ],
+ "outputs": [
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Returned class is: Correctly Masked\n"
+ ]
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Returned class is: Correctly Masked\n"
+ ]
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Returned class is: Incorrectly Worn\n"
+ ]
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Returned class is: Incorrectly Worn\n"
+ ]
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAyk0lEQVR4nCXa97tdZ2Eg6q+uvtfuZ+/TdYp0jo6KVSzLtiw3bLDBYAyhhGQgQ2ASSC8zNzd5bjLzJEwyQyaZ3GS4IeTmwqQxGBhCdYIN7kVdVteRdPruffX1tftD/oP3D3jh2Ws749UchhRCJZT4x2d/c9fUh9wiPHPx27W1S6a1cvP22Wa3OYOK26J5z8EnfvTitzFEVobce9cja/Urll589MkPvfDicwcXF7c2b13eeMMytIlKLhjmJOqs7J4/e+nSRHnM0vW5+Se3V88US6Un3vXpxqh9/s0X11c345igMJhdmP7i33/FNsc+9sy7Dh453G+Ner534ODSf/nD3//Vz/zSje36AyceRZr6xjf+fnlpf7s5wlDedddeL4quXz47UZ5/7offeeyJ9w6H7YxdXNtc1aGbz9rwN//w3wMG7j7+4JVTZw7ff/LsW//wyU987ktf+Z1Lb78pIiOVw1xOa3sJCdTqan/xwLgSMo0htWgpn7dsLZPbu295IezDk/cdVkohADe3N05d+f7xgyfOnr+WKeQg7xCYfeDBd9lGrrN5px8kExNVANntm81M1qiMj59+4/Vr585PTo+t7Wwcueu+l069tHvh4PhE4Yff+8H83olKcddrb17eu3+2tlF/8sl3fff5H9y1st80cg5R127cpsp75uM/d2ftot+KC5Xxz33+j5944vj2Hf/uY/vgJ3/hE5/4xGfffvtcpzN49JHHWORv1S+9dfafG42GYVANg8EoHI76GOs5NGON0ZtXLxpG4R0PvaOQn56Zmi8Wx/L5TJImaRrnM85OfVsIVsqWB9075y+dmp5dGvT9yWLZzJSHo16xPOZomt/bxoaNqcsgzFXG+s32VqOjI0pIur3TMlySM6q3blz65je/+YEP/sTk+K5MwTSQeXvzMjVy2ULuL/7sf+xeOvjYI8fbvW7Q7UBK4iQwzbHlg/PPffNFdwzdc/jeOOHwpVMvUiMTeF6721yaX9awfmX95Vde+e6ZMzeOHpgK/aFQPGVgLDc+6R546+pzAhY/8zO/Oj1VZZGvQSxkpBS1dJSknBo6UDSKwsH2Dbc6BaDeG3Zbq6sLy/s4FKaB/KEnWWhk8gTrKQs0oywJcIvVVnfQr++YhWyn1RRSbN3aPP/2pTCO3/u+D7Xa2/12/e3rN37vN379W99/YeB1jt11XM/kzp55FUl57NDyvntPbt5a7bZao8HAMCDWywClC8v74C/89r/90Ac+8ZWv/lW7s75rrEKoMRyKYLgdpr2+FzsEEo2OV0tS4aWp+5qD2z/x3l8pupriUegP0yQEIlYCDrrt8vgsJhrWHRaNEDXPnX3r4MHDRDMUpdlMlqcCgzRRKhx1C27+ytXr5Wo1jnyKJXXybm5MCV7vj1QaszhIwwQR6gW+lS9wwTGQV65cHXZat27W5u+aztHcwbsPv/Hyj944vbp7ZaxoVx86cejMxetNf2u+sndr8/bDJx66fO02fP9P3d/3BymLxkt5xVl3JBwN75rP3Li+DQi2kJkb2+V1+rMLRcOc+PSHP5P6rdjvsYQLpAa9kW1ZGBGJiK6ZmmFQql+5cPHdTz+9sXZue6s5irmpGYVqKVOonn711MT8jL95fd+xY5362jDiSSJgFGsUl2Z2mwblDDIuIEattZvEtkYjEUS+AiJirN1uTVTGVvYdiBS4c/1avdO8cObm1IKdtJM9+/aNT1R0k5x55eyZU2/9zKc/FabS93wShCNMhc50UxO1UaBRks9lJWeZQs4meipijUS2k9s1ceLpx096ve0k9dZrvVKh2Gz2pidniYy3NjZHw+HMwsK+mTmGk+LU5M2N2unTV0bt7pH77g2DAECyfeva0G/Lm2HMEvPOba7Ayz86v7JvftRtm6XyJDV6rRqEkHOBFMwXsmHKWOpJCJqd9kyp6uwav3p9w85Xt9bXS+XyXGHqtdHZfXsf2TEaN9c2L126Nj47MT0z7XvRpStrWzurR+45iqAU3YYnpOwMon4r3bntjUZdRIBmaROT5U4zvr1WD0L14OHlYbe+0xqEsT5Wmijmy3sXd9sUSsZ0lC6vHI4GO/1R76V/eX5rZ+vHz39nz+45I5e/ePZcsVhhaXLu1FuL87OjMMrl9KjfHXYGmMozZy/8+NU3hvWdZ7/x7JnT17/zg+dXr13SLa3RaG+vb2Rt3bHtbLY05AlIrerk5NrtVd3JaTp8481XqtPW1dPXrl15IwnjqzevGQhmx8u6a7x56ceHDh8cdnzic2Dqpm6QrS1veswpL5fDuNftJsNeuJ0Q03AAKv7mv/s5Sq16p1stlgVP09BrrDUlENnKtGEXDz+wePHNF6zC3hs3r+WzWrMdua7jmvTcm2frnVYSD8v5HNX1H//g+1uN7vjkzNxkdWV56myUdFpdkWKWwEG/d/7UhZMnDvp+uL1R02GqmarXq2fy1Uo+d+nazexSGWxeXq81iVW7evn8Rz7yk8N2EMX+3Nw72q3tuz/2MROiO2s3n3ryfeWx+bNn3njy0RPwkaf2dzrdaqV0z5HdtzdqLA26wwABxEJlOs7KwoFKeea9j7+rXu+V804YBUm3EYahglLXjWx5OvJvW4WVMAo3N1cnJse+/rdfnZicvXLl6uKe3UEQt5ud5aWlrc1VzXEJ0VgSI00zDfv0qVcNK5fP5y5fujq/MGsYpuOYq5evnjy29/Fn3veDb/9ozFUKm5nCOCekXJ1uDUaFUimq3bmx3rBM3SlUvPZOL4ijMHrs4SfajYsrew72hHnt8gUp47vve/jC6beQYZC91eLePTMJV/1ODwAys2tJMk4tfdeued8TKyv7T194O5OxvEHT63QDBq1sOWZMQXX5zHebfQBohLGAsd9d39qzuBT1RzPFkostg5DqWDEZDCzNVFFKJLd1o7NTM6i0LKtgW9evXH3ogXtzjt3obEGp7rv/ONXIKy+esbPktTevdlsdxgMTgn67pQL/8ulTIdCWl+eXDx9VqRgJWcq6y3v3v/7Wc2evt/7m619bW7350gsvpJwNRlsWMOD7fvJoRrdKBdkZqds3t2am9tSHOwtzU42tzmAY//zHf35l/yEZhhRAAZRSCkIIRRKF6cS4/pdf+NauPRO6AoD7UvAoVFS38nZmamb+9u0buVwpm3OSMIzCJE1TRXF/4Pmj0drmukhiaJoyZZ1+x81lGAeBP5gYHzNNUzd1TJzKWI6aejQKRBoYmbzQcGlsvtnulG3Z6Ue1YdcEsN1nEEf3HD5I7GI+5968tXpndXvPZKkddc6fepukqdzs7IRbVpo3pQYdc0q01oZ1Lx7SwwcX291Eg4YXNgaDIcY4SZliIdGtUW/1u9+rj1XGZJgABJxMztIsXKWZfCHv5i3bHR8fh5RgqRIWBZ5n5AsiZZ1GjTF5/K6DCkIJlZSy1WlsNrvteqMnUqRw6AW97vDg0Wm3lPP7fpwEGTcXxYkmiEyCIIpETJYmHadY2ry15o+2f/pTn1y/XitWdQidixcuudl8VwSPP/6BglOCDz9+AFC2pzzf4kG7ue46i9OzpN1pY6At7b7rvU98PB402uvrhYlxkcQ6IRDxdvvOf/vcXx194L6jh45NVcqGqSugXNsxNJtgnWCslCQQKQQhhIJzgAlAUEHFwySOPJXECALBJYQwjkKmmM/TVnNQq20MowQokK+4OzsNFbcP3/P4rVuXp2b3ENvxB56WK5Sq40l/EPVrNFe9dfVCbQAPHz44GuzsX9kXRGnoJ1/667/8+c9+ptVqwsffO2M7k4vTC3d2rqnUmJ/J1TuDKPE1lPv5T/0Oxpj7Q8ZDolQ8bEYJilKvdecap5WFxV06wuPjFUp1Q7exEghqGEEIsVIpkBBCCBAEAFLNYFIgLhhLBGNpHAglIJcgTYQEjKcS4c6w2R9Ffa8vVNrte5jqSDeb9VssQX6vMbe8tLD3CKNOvV0XUfLAiQevX77w2gs/2H/vieW7DqW+P5at7NSvTM8da7U3JUPdfoNYhhuEg+ZohyAx8Jht68YowxmplqbPnH3z2N33aQhIoYDiRMun/Z0n3vv03325qUMok7g8swtIhYmuhIBUh1IIxjGmQnKgECIYKoAwSZKYUioglBAhhHTdTFkkpRSEQMYglErIrJWhUCcI9UajwNtx8vl+pzU+NbO2OYyj7e3VtTRihx98rJem19c3/ubmX77nqffP7ts/Vil97+t/VyztyhqZ1drtgzuBD1ir3hHMQ0Ea5UvZ9mAIgDuRdYY7CAE78sIjR0/ec89DBsFR6CXDnuDc93qH77v/C3/wO41mx8Bm0S3AVOhE0wEmCCguFFQKKJ7GgisFpZRSSMmVRBpVGHORQKmU4kJJBQBPmZRcKq4kVEpJhQnVXMc1MZqdmGCex0IvjeWeuXGfgyTlQdjfuHJ2/+H9h47cfWD5UHVyVrdKw17ryWc+dOnW5YnpajToYwf1OkMna91z8jFUmZkbsl6SkHsOPyxMixMjY2UW9izl8xUuw9gfRb2W0M3RaNjY2r549kVGM81GJ+dmDcvVHUsoImTKmJSKJ1HEBReCSZ7yOAIASKXSKGaMx1EoGAcICkykSAXnSgHJBBNKACiVAkpBjAjB+WxeMpi17a3b27durpq6gphSQycCCAFuXTw3v6uQKzurN69aJj128onlPfc+fuKEnnEfe+LdD7zjycnpYrE4RpSPvE7o9ZNc1mw0uuOlidAbjE9UJTedjKVBjFQiqDuo7XTqtVatnnWr9U5zcW4pn3Vt2yRYQzoFCiqgOIsUVEooCCFXXCLMecpZgqBgaSqlRJgKIQCXQkiehozHHAApgUgTFsVcCBYnigtN06sTUwDRuflpopFUohMPP16YWjCKs9ubW+mof+nCzUKh5BD9W9/4yvbO1l//f//ZLeweDLcl0770xS/tWtj/yo+/e/bcabR7aoWlYu32eqNfa0WbPJOs3m4cOnTc6zWQ7iSBF/ldiKTX65OMNbc4k3OMqYkK0S1EsZAQCi44l2nIOZOMMR6zNAJCApYEgx5LojRNeRpJno4G3TAaRLGXJKHkiqUpC70kiRKZMsnSYJTGEVACY+CY2u7FpWplctTu3rh45s7169fOvWWg0LJsijQbpq+89IJTrhy/96nRsFM1rEcfP3H42MMXLp+559iBP/qD392759DO+gBRo5gx8xjgjdqWpWWU0Jb27HFs1w9SxEdEz1CspWHoVGdmFlbOvf4iIlYcxgQqwROAmOJMglRBgKRCQCgWCZbyJEijWEqBMRaSQaiieKSZhuQcKSQlCH0PciW4kjyFTMlUQIABlCyJecwif6TiNOdmKsWqabl7Di12ekGr3jBNJJRIhv3DBw/JsPNTP/Nv9+07lC0ubGxu/tov/MqTT70nX8r94q/8lhBiZf88unjj9SSMZsfLh/ctu3S6Uixrms2D/uTMDEHI6+5kMppVzA+21i2Nra6ucaYsyyIGYVxAyRXgQgjGmBBxmkQQYAUUxURIRhBmScpTlkSRZIIlsUyZVExECcZ6FPqCJUoopZSUjAmOAAYASsmBggIo2zDzhdyd9fVRbWd6pnTn9kYUxQJCt1hQadLqdywbXDtzrlzUhUh/9hc/+fx3f/D9rz9nYVRvro/iBOVssDC+YEo7jdpcpRDIyenc2ERFJV5jYy1XGI8i/8aFK5zgK1c3GIdRHCdcKCWASIFQSgICMAUASiBZwkUsmEog0E2L6kSIVCMoDn0kZeT3JFB+tysSn2gEU0p0E2MMIUBIoxqEQCgFmAgpQSwO0jjUTCfy0rX1G63moDI5FQ677a3b/U43a2r9pve///7vZncvTi0vXTx7WQ9iu5Q+8tjTN5s3hU5Xds8h0yTF0tzs4spTD33QNNjiwqF+d0S1HJIUIQIgu3HuVDafvXz5fKOxs9XsUmpirFiapJwDoACLpUgR4EoyiCiQEGNIAJFSKYgxJFIAE5Nhry65SoKR4ArrBhSprptYKYx1AAEiECIkFRAqoURP4kAqlbIoTYKxYr40NoE1q9NtByEXEmPAesPhvqUJzvCPfvwvrVp9bnEpxhB47qunv3Hz0p1f/cXfO3fuMhpFgDPpuu6br59t1Ya2pu2eX2RhOw7b+VLp1vmz2Ci1Bt1cebw6VtzZ2iIYatRI/ABwmHIGIRCSR0ksFFEQCMYxQWmaACCj0QBhlEQRVyBbmIYQpwnTdaqbVsI4gpgJIUQMEIFQQighhFTXhRAAAJaGCpAgCJrdAbZsPxgmoTBdO5PNKgUMAoNITU3YRw6u7GzVNu9c2tzYeOCxo9PFyQ88/czrb3z3ocfuQSAxKATDZtvO2Pfcfzyfq4RJbCDsuOOjfoPqWj7ntJqDxcUFxyKO5SKEavVNiIhGCZBCcghYSqmuBOcRg1TjXP4rTkKSJqkQXKZp5PeBUARigHAURZbtpEpgQhUhPE2lBHEsFRA8DDGAknOIqJIcERyn6cr+fVPjU8WxbKPRHASeRknidR0DpILW64OCm6cYrOxZ7jeHC3N3B34XcPX/fPGbaGnX3n0H7j584rG5lbtk7NfqGzaifr8TxqPA8yJ/NAzCXMZsbWxsbGxoGgxHfUvPIACF4EmYKKWEhJIjQh1smoToAFFCtSQVMklYGIowZEmsBAAp53EUjUZAgWGvnfqhUhIpjBBSElKoEWwAZHIJCdahUpxJDFGhlNvY2DEdDSMtHgnHKfYGw4TJOBhOTVbLpfzXvvHVpeWVYmWi02oVZ8ae/Ye/GytW/v0v/Sz8k//8O14orazpUDK3Z084HOiQc1QQSY0NeoN2f6teL1Wm+sEIyKDbCaIwOXHs+ES5pGs6hCCfzQJApBBY16QEUnLABZAqDj0pFUiiOE4wgGkaI00nACYi1SBVADCleMoghEqkElFq6ZQYCqQsZEE88KKoM+z2PXbq4sXp5blho49kalEDoaiUdxb2HZYKmhlXzxcNYp+/fG5men5izI2YLsL0hy9//dF3fJhMzEydPn12ac8RIMmg23J0yxsM7Qz2+z4BBGB9ce++73z12fvf9cDanZ5MhQyEAkoRvcNFIuD5rVshi2WUVAtZFoRApIoz08nE3nCsVCQAU0xYHFFKNYDDOFYQhOGAKQAhFAjEPPGSBEKhuhjJyCCG7RYVMIRMDMvRmeeH4bm33vrZT3/2rRf/uVgqibDrZEv17c255X1hnGY1euDo8ZVDR//vv/zvUDsMw1GzVz93er00+Rb8m7/6r9XqkjesaQi1ms28be09cnJr9QIxzMatt3mUWqXxF77/ndzYeL6UQxN3Xbl2vt0aEYgwAW+fOZMypmOqgEAYpQmrTlaP3H18emGxmM2fevH55ZKTN+2MQSg2HVOXaTKIojQKOYQJRzfv3LrjR2OLe0ejkVTpjWs3y+WyaZpJHIdxYtg2hLDTbPXaNSiHP/HgQ5ZrJ71tqrtOsUQUc0oVO5s78siTG1evZcvjV69e2lrfKo/lCIAXLlwgt6+83W92TTsbRtFc3koEu/j2GRsynA4tM3Nn51JU387kM+WJSQwBAzgIklFnc740Tah18OBBz/OETEwjaxjU9z0hxPUrl1avX0sTdfPWtRv7lpb3rox34qlCTgpbRmnfHw3C4dVOZM3N3PDDK2fO0UsXkQIa0ShQzU5TN8yUh1JQAplp62sbNaxZjpOZX95T39hiDLoVl7FAITsMfdN2n/vm/3rkqWf6O83bq9cPrexpdkZ3NlYrlQkyNjm/ev3G8t59c2MZBkhpbEaIIOi2NEMbDGsQ6hnHlMiqb28mXr9mtlMvPnvm0oGPrIyiKIqi7Zu3BOAildjQq9NTKUuBghCmjcYOJmT92s21yzewTk/cf/LuUgawpB3Fz12+2W40xBuvSyWBEkpigqBkkiPAkZJM+j7XDKSQ1DmYKlc2Oi0FC543iPzByPNgY6dYncSm1tzYoAja2eL//trfP/nOx07cf9+f/umfHjxwd65YLZeLBBv54mR2crIMAcnohpUv+LVOvlzevH4tjYJcsRynKmxtx51heWb22o3NgS8MpA2GfqrE+rVLQkAmFQaQ87i2cZuYGcYYYwylMVUAIR1jwLl49dVXXwUCKJSwVElBJMCmQaNI6aZgLCVAcoaJgSOWgkAh6ocRwcAgVAHOBbA0BAC27Uwy6BUKlV6riYxhdnyh12rksTY/Nf3cD1+u3Vp95MS99Vpz7sihzbVbJJ+3NDkX9zq9MJ2eWdCDlutmaxtXTMPhMtm4fCGRiMVpHAZ2AsN+v9ka6qbm9dp+1IujFECsJEgRkEDGgSiZGYSlRY2BHxoEUh2zOFYAciSFEEophBAXgiIiwoBgNAxjTSdxkAIEsQqUUpADTUMEKMmVUkJwgIDK5O3N7Y35iTG/b/T7PSmVqVlZ1zImjgxqq7Qyuzy/p9uprRw4PAzfBFzOLy+h+nYj8Dt9P61ML41NljlXnfqObhUlFoN6PYwYQXqUivGF3Y+fPMm4p5Q0qAYQ6DY7WFOZvB3LWLNNCnTXcYASvU531BkoCBhQI99XQBIM4zhOecJTJqUEADDAARA+C3WM4oRRnUouBIecAYRwnCRUx66hK0CFhEiRKGLVynQSK0iMgq0dPfmgSRGLI91xNSd37cLpwB86tv3S62986EMfxZSOQk6yGVAdP5AmYn5hajjo+71WZxTPVDJcJ04uOya5PxwUs45i4ZX/+Ve5Yr7r9frtToaymcnq5nYLxnzP7FSj3ndcsz/0hUixApZlJGFEIMQEUl2LhESI3FXKzkxUX7t6vStAhlIuoEgAAqiQd+IotYu6TmgcRY6hQYxTKTCEUEOaggKIbqela9i284Wcef7UaWxeV3omDvpJEhAjMzOJBu0d1yzfd/xgq76TyY39wxf/CPWGst8LtExmMPQ27tzgQC3sXmj3+u3GtmVQw3CwUYQYBd2ese/IbKmay1mplIwxjOD8rtLcVNkAqpShJRMVLWpAtTBRKtjU1pCua/lsJqeTkPNJDH/uJz/20Z/44BNHDynAGZSUWI6J3YypC5ahSGMQpgwrQTHSoCyaZs7QKYH5jGOaJsZg0Os12xud3ohQ3GzshL0aoWYchLpGiFUxDcM2wPe/97WIOFDG73z3O1FpPP/Siz80KerUt7JuSSkk08jWDNvM1mo1zTLzOU0k3vTiPBrPTxYKGOuEGFGUUgSpAhaUOcMoWGY4SkxKMraBgUJKWKY5lnGyOs0Yuo7Izz791Pzu+VIhd8/hA2Vip0IZGrCpbVpEQpymAqEUAJnGiU2Jq1MWhiM/dkwTAokxDoPEa7e372yyhDnZahzE/igY9NtEpCMvyldLvfYWY8I1nX/5p2/cvLV6aP/dhA38D3zkpyLPx0DE4UAhMOj3WdgjGi4USqZtDbze/mP3xVFydf0OCXmj0S0W881eUMmSkpvTNAo4ch27XBRcKq44pXoYhCkXUEGp2KbPykAdu/eYk3HSNJ6sTt67MvvtizcNSyoMqZJ2xiCOLZUwkFSujSARXJiEIKHCMBz6KcJ0Zszy/GBiZrpe37n3wQdvnB9JgJTELI0loCL2nPx0JuMWiqV2b8hUzBRATi7b68RhGKcSYt0kkCslCclCoHTTYVGcpknQH4y6rXaz8fCe/QZhnKflpb2YmEoADelU1zTNoAjaulnK5DUIC24m71pZyzQ0PfW89z9yv5vLQSV1TG3bPLR3eU8+U/fCnGkUnVzOMEtZq5TJupmcY5sESaiAppTjWBRgrVAuFbO2qQPB+92ug9G1ixfLEzMEYwjiJBwpwTrtvhJJ8851weKJ6RnTyW+vr6FMcSocbRmGmSQjzOPaRrOQdxViacp0HQkplo8eVwiPzy1nMrbmOJ9/9Okv/s4vrl+5YM7ua0VpEKdBEMVBIBTQCOYCYgwAwjqx1jz/1Fr9wYXpe44dDUcDQ7Mt14VclHL5e5Z2QwV3woii1DQsSinVYBx6cRgyLiWGUtcQwpFb7LWbOcz3WgQTiKUKk5hFwdbaer5SARwKJvudzuatiwSJ8q49o15bt8idy2feOvU6+s43/5FC5DiZ1GchR9WJyX6/aztutVIJgqSyaxZjhEAa+j0Ny/Vub2Z2BkP6nz7xzENlfHj38sh0Xrxx5zYDanyxZeWiYnU1hN+/fOv5ty9Xk+jDBxff8c7HM6Zumw7QNIEw1sxyNru4a+7d+/Ys2c6Zmneq1jy9XtsaBr2EN0K+PgwHxL4+Cje8aJ9B/s2DB+SgJwHstvtRnGAKC6VSJuf4rb5byhOINMlythkkrDwzXZxZypjmI0/+ZM7NEIwsN5/zEl936MzM5PbaHY1occwUltML81Lh1OsRpJrNxsFjx19+/uXp+95pUqJs1yDOwi7nHZTKd70zCZPQG/W7PZFEB6eqQT5DITYsqNsFB+m2ZhlOVjIugHJyWZEG+ub2yp49Mr70dDEHlWAJZ4wJaFIDWk6mUB3TzIMSxhxyDlHGMjRdx0EAeQiQxVKf6maUxsSPoGPQjD3qdjNu0R/1ESJJml64emFqfpbsXdpt2Haj1YyGaae2hRWn1HbyVqvRHC9VBp3txBv4gxHl8uqV20py3bQ9BSmltp2hlCLNABBQ2+IFJxnL+YOhP/ITljS3NhRwjh44GCQBNTOcc9MwlZJxHFEjO14p591i0c29deY0RXBmfiKbcbJugRhmxs0mcRyJ1AsDL/JurK/FiQJIEgwxwNjUOINW1oQQU0q4FAmTmVylXBrnSYRx/rU33irl9HRkIxH3dnbqX/uHr0PJlG5ZTjZTHu/1ejL1hr16mgT5alUoISFkLLWLBWjZlBCoU6lSwdPEGwlvFIee1+36vV6n1fRH/VajXu93oEYyhSzVNM4TzuOg3x416qNGs1uv+UHEQEwJSplIIff8gYIkiQMZh8GwO2i3WOhxxRSE2zsdxhLbMCsT0woKIBWlECpJMOVCaho1KUhknK9MTI1NpFELa+TCuRvDbo3Mz83lCrnf/OzHwyA2NXPYb2l+X0MixSZLAxbGdS/cd9+DZ14/PTVV2Nlef+Xsq8eOHseUMD9mUiKhQsZlkiZxOBwOPc8bdNv1YX96eiaXcWrbO5jiTr2mYQQJDr0wTVPP8/qBHyXB6uqt8bEs58JPgvrWemmsPPSGtmlLAkVCY6niJBY8RQTVW+1SLqeEIkpJkSqlTDcjkgRpRKQRoc72ZmPfwrhtWaVMZlQihx+4m7x99cbcgble3V+YHut6PuQg6rcTniJINAiZBgum097c2LVrfuR1HSe/trW+/oPGB596twCAcaaExEpFaRj4Xtfvjtr9QRLPTs9WqxO6ZW7Ua/1Ob2pxj0Yhkbzb7gjJRhxt3N4QiuVzruVm4143hUY7DNNarVAoCIgghEID2DRhCgVgkivB5cz8TH27CYRPaE6mEWQWwkQlTArlBT0zk19bDzXD7rcbR46eqN1aJWGsLp2+tLiwBMyMCtvlUp4xFjR2NBNtb9zO5sv9aAhJBhPg5scsw2JKbNy+w5VEBComOBM+S/3+aNDvbO7UAAClylihVCmOlRnnEmGGRK9dSxgnCAV+H1Bzc3sTU6xjExKEpHCyrjQDMUL1Vl1A6eayVq4CqJYmvk6ARihQwhslN26sLu/d7XUpF2D/wbs21m6wsG+4FcYE1rR2r+bF9u7F7Gg0/M4/fXP34gEUpYPRyA8GLSlZ1nKElKNOK1PI8NBnCiepsOyMHw6BhBqC/X6HKjE+MyOIRghRQAgoYsZHoX/51g3LzVRmq6XxqdxYDlBku5lM1s7lCxJyL2zWOmu1dm1t/TqQzDRQvlzMl0t61iKmTqhuGiSbKw7CmCXcDz0hYw7kE7/7BQ5VNp8jGk78+M7t9WwhZyK+fusmB0jPTXKsua6za3YBxlzE3mx1at/K4TxBE5UqgmJYyBeu3lkf7mz2e93eIDDc/LDdG/QH2XwRQIBN3TStdmNr0OsZGvXiqJeZW1uvKwWhQgoIFni91pabs6VkcSKARpBpS4iFApholOByZWIsN2HqWTdTKFfGy8Vyrjhuu66hmzxVoQCJgDFXWIeWRRACOgYxE5O/+oU/+4WPlPIljUKhZBSFGMFuv593XQIYACCMYqxkwqHvDwCGPBIvvvLGnuXdHLDRoEm8oeqPegvz4ypN7XI19tsi4gqDOEgQCaYX9u1s3B4N2/sOHV+/djGXK9R2au/+d787YEz86K8JhkoojUrbNigwCSFKA//07HdXe737VpbuO3aPmzF1nVLNcPJFQXAaBhJRIAQDwB8M20G6tll/6fr1pVLx2JFdhpbRpO1Y+gBZT/zHL172klajFo78fC5rmdqo24qDIAlj1y3ohm2kkXKySZIgwNMISoLWL10/+dhs4rUmJnY5GQfFLJ2bHYNcZUrj/rDpmGbfT0MvzZXK1DKTcIANzWs3b1x4QzdyjdqGY2cbL3+5JVDp/b8WSIwRIIZWGqsWs24+XzA1+8l3nHQdK7U0Ysn8TCaoN25ffrt2a62/3Rq0hvXba2uXr7fv3B5tN8y8xkiCgFreNWXapqXTbNbpQ+v4L/2Xf77dDMIQQqjpREkFgDR0zXILjpPxfc/NZZVpIx5jIECaahS6hu4lKdE1PxZrvcbhY4fRyUdOEqqo5g76o1zGbfVCCkGxmKm3t/OlqWAQ+L2GqVEEEZBMCMlY1Hj7NcfG37+1NfvBXz/0oU8hgJycm8lkMORFxx4fy3/2fU+fu7aqCtX8zMLS0+9ceejY/vc8suuBA7msefC9D+++e//h979r/uiCMs0zdzY/9siDc/PTGd00NORB7f7f+O/XoTs7N3Xm9z6hGYaZycapl8tYhuMoluomEZL5QWgatsepbdsIazz0TarvnpvbuHOjtVPf2Q6f++EPkYx8lfLxktkP/UtXL5tUIqL1291KPht0tpGtaQDc/eiTENCEh5KJyPMpgtee+5qey7/eS26A4sKnP6fplmkbCGDbsrBtVqql//rTn1y9cHbgCV+QlNh6vuyWJmYfPunkyub0tIC2yk3cvrXzmQefGC8VqK5sw4yw8+hv/835VHcz8PTVjVuXb/Z7PawYwnrgx4VCXmCVSmBm3F5vQA1DI8j3A6dUrY7PsXhYLJgQa/VG7f/8lV/OGXny4svn5+fHseP0169Mzc73et3p6WkLSWRbBJJ+r0EQ3bhz07DMyO/pVtYIkzOnTt2FtOHCo0SDN5BdUqr0od+OvvH7ubwEkiGVApvqXHvP/ofWnn/NI7w7DO5+9N7ADwQhMvS2375+7fWzD5x46FhxOpJ+qJJxs/zmZvvjn//K883QzWmXrqyFf/HZQoHGXLqZTBp1NVvDGjWB6bpZKKRl2aZuI9AnVI+DIbEcauYNXWhASS5SlVJNwc//wf/V6ET79y8Efj9nayqOczaVUFecxdFIpVxh0t1e90Y9pVShNOWN2o3tnUY71jBf+fN/MTWTC5xyeYTEO9/8EytNLMskjssZiLuhTTKm48QspqahBJSYR6NAJiIKfc/rjeIhzFq6iVd+5U87idwM0l4sJFfuV3/v9dOvWhRDIglCGoKWZfEonN69W6QxRhhThJVUuh1L7mQKmBbawxqBhoWFn8T77znxp3/8eWJbWtGMCAgqE7PN29cqWZowjeKIA4SE4pJpBgaEZIqV/vYW0SnVrH5nBIAyLC34y8/ov/6POZR6lJ6NVf7p3zg6Wdj8n5+HKaOmjsazkeezIBj2A6pB03KDKI6GI8ljZKAIMzpe6sT8fb/whxdDOGK8F4qg29iVBV/5zrcxUUpILvlYIbtrotr3PBsTmQSmkxFKaKalBOBM5Fy3328qFGVc1w8TCmClPNltt8uZEqpOTJTGbDMzLqPh3GQVUG006GULuaxFdcfU3Ew4GGqGkUaBk3XXblwWItB05A0CbJb8jn/1P7yrPhIuUhrSPUBfqPvimV8f+/hv1aWlEeTYZmxAXEARTjt+y49bvhz6luqwpAHgo//HHx/41f922icJ4BSQ6+ffOLQ8P/raF5gSwyClhnXXwf29IE3SmGAsVRoHAQBANy2RKiFY5A05Y7qimVx+GEW6YULIoiTmGE/OTpLTF24SimyzVcxaOzuNuw7vv5NekWncrzUt1+ISKIgmZmdu9RuFsSlCSBhHWNcMi3bqjWa3UykXW5+558EvvnX57VP3HXvgxesb5r7pug+i+z8ybvLKWFXbWN/857+jQiWI95V28Kc/FTiVaj7TVPSVdooRaaXxPHVeufDaYw+/o/M//sOosREEEYeg0++LG4zzBALpB8nMWN73o2xR+N1uyMDywf0D3Nyot2emZ0zXHYxioutAGEbGev6FFx86ejfx+13EU1jJT+QpSu2da2ctPZt4oenm24OBGrUXD9+9vXqTYhAFI01DKTIK2Uyn3QUizuVtrJuWiHe/8Oe1ve+vdTrvOzTbCzmjpq3hW6m8VveIdGY+8Gv9bl1xfmBmV30UckDaARBAmRa6vNo8sDh289atXbsWwy/+Mou54TpkpjrpFLauXm4PR+VCIYoThAChesgiwFNTM/tBt76+Vp6enkJIcuHm3PqZC3sKWUrN/ii+//ChfDGHVi+dbTXWLTLaXN/0vSCTG+MMEkxlHKigV921e9hpBV47l69Yrp2Esd8Phq2OZWXGxqqLs/PtTgvoFCD44K3vhMPG23dqZ99+25ZhzMAXv/W861Lg2jUFw1yVVme2vZRpWkKgIuCbL59nvjy6NLl6a61er5Gv/gFXmpTMRmguQQcW9z36vp84/uQTnep4X7Ow4L1RhCTqhkmjuVPJ5yWx221/bKwCiBkM/ZX9K5ZuMoBLY/lrty4Zpk2Ywu86cVe+WIEg6Q4Bw4aI6ik0SzPTTjGfRkG32daIQ0y3u7NGTaNiWFfPD6lpUYO2+t04jjP5imEZSRDff/OH8pGP3i7s3m61txr9X3rm8WiYtkaqNWwPe4NsLjPmundqzZXFBQrBhx85vLW9Q7qk32/veutvlemiJNAxSUUwXbKCq6fTNA0Zc7vh0rufgjIqaai+fq3fG0xVSxgjpCGiGb1RXK1WUwi3b68duevgj1+5cPj4ESlgr9OCX/jdnytPzGZsrdbwFuYqo3bbNHU9XxVB3/d9oGDseZ1BL5fNdmp1pOS1qxdYKiMGc6ZjFrM4ib35+XdPzAmJBU8jLiMF7tz7sfJYdWOrNgq8XWOV6sT4yIu8MMEYGpTksk6v0/G59AYdiJLi9/4aECRZqmMggFA8vaCZxZ01i2lpEnleEMVpkES1vu9j+M5De6ozk4nfdSrTEbGmJmY5T3OOvdXrJeHoRy+ezWeNIAn27z1ImLLzOfPlNy7ee/weinW3Op0rFIfNDSeb69TrZjafK1eYUhgIqZgfpFYmxyNWNIyS60om03zlUKmMgMS6nihFWEyEmHvty3j5PpRbqBaKN7bW7mzddnNjQglb15up3Fj3ht6onC/GUo6/9GUJUhUrSokEkCIgEFzxBj9aNB7tZEjslAq5YDQMwjRvGhHjja16EqTjCxOamc3aWS+O85lMDOnm1ub83J4jR2YzucLJE49AYsIfP/tnF25sZDPZ8XLRsVynWFYsSqMw6PUata2g3w7CgBKiabRTb2jE9BqtXXMzmqYRQrDlzL//k1vPP6sr8q9TIInjJE29OIQQY6Qlj3zo9YY/Mz0dxWLtzvUo6OdLE9NT8zeun186+60sUinj2KBQAYyxTjGkREaxgsnBZ372VL+fP/MyDqIkClMWM859PzBMk5gWMQzNtab2HDKm99gEVKb31BvXNYyEklACAiRjDD77pT9q9IKSYy2srAT9Tr/Tnpicam2v9XbW/H5n6KdCCYpJ4vnz5UqhUNYIRVhHCClMlt/70VCD6995FhMkUhUGMdb0fr+bRAGDhBMMOAMCyCTaBnhj78l+bb1SHFs8/5xGaCqkhAhSggkFjBmGYdqWkjCOPEJxotuvnLvxmT/8/eH3v5UOfQV5GicKkTQNAMSa4+5+6hPJoD4MvOUDJ918TkpJqakbmpL+rWtnEZLkwpXLE2NVBC026EeDXmW83G3VfW8kOO+N0rHZhV1Ld6u1c4bumrqNKYYQMCmlTAXRVH6iUfuWgBILBCQ1dBWmEdUMyVLAY5nqEIGIBwioYjSyX/9fQgGwylMgeEIkoRAbCAjMOUIEE8o5Z4whAjFBWQj+4598/pXX3jr67g82fvgtEaSW5Qgpo9hIGeNxwqNhYWI5r5ROdEp0CRQUDCiCcGZp/yOEashx7PHShO+NRu2Okcl2NzdjL/K77cTI/5v/9P+awCr79cr4TKFcNVxbs1ysWZqtI2JWn/iZzZG/JnRi2FJKgFKlJIYQQgkJwVhDPE38EKYKQAQQ0XQDU8wRgEQDhkY0XaMYC4wh1ajJRKq4IAhABBAASiaCy8WJ6RaVufd8wHEc07Q1w3BMw3FNyzKprlumk8vnqWlqhkkwJZgoziAA6F8bebUwHYchC+M0L3E4EEAfJOHTv/znrusABPbMzeiGJhkjmokgEYorqTHFPRPtckxoUFA+cfV7n5svzbEkAkBCKUSaUJ0yJommqOIsVjxJlOScJ1IqUzOkUkwKhAUUiiAdQYwIxAwKKakBECQIa5iCWMR499Ir//jl9pHFn8w6JE54igCgGQo5gMXquJXJIYQM3aKUsjSBGGOCFBcIQIkRMg0njJN2r0ctozuIHvroL33w07/rlnRiCEQEpgbCmm64iGgKQYIo1jQA4F1PfbAeRI2UQZCdPpCTLFFcSiG4UARBEUUaAQIoIRNIJJICA4ohIZhSSjBGGsRSICYBwooQJSIuIaA6VQBDDCBSGJCkP8jL4MmPvr9AtytPvJsYpmmYjmNRzbANy7YKmBqabmKClQSEagpiqDDGOGWJjFOytdNYWZqAtvuOD38WY4AR9kOGEAIAKKgAwNi0oQJKSg1BIRQBkNqiFbUKVllj4tsXX73xF2987D2PxHHMoGIxJ0QhgIM4QArq1AlYxDEEjBENQ6EYkBhBKaGGgU4tIBmAEFMgEUBAaVQHAOoYYGo8/7df6Hz4GZ2m06T0xfPffhI5WSSBlLZtKQVMzYWEQgillABInqYSKIg1JbniCpuETM6WH/zAp3RDUxJzJjgUlkETwTBBQIHFJ9/XfeMlJSWlVDAJMRCMKc0omWOB2rpd79hkjfNQQAURAIwDxQUHjDENaRKrKIk1JImuM4SklLpJNIBFEkkogFIMMI0iqBCEikCICAUIapqBMTIN8/DBo6vRmwWtdNl44LeOfJAIsfXtZ5mSEEIIMdZ1BQHECColhJCcAyAUpVJJgJQU7P8HeMHL+UZPZlkAAAAASUVORK5CYII=",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Returned class is: No Mask\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Run Webcam"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "source": [
+ "from IPython.display import clear_output\n",
+ "\n",
+ "def producer_live(cap):\n",
+ " # grab most recent frame in buffer\n",
+ " for i in range(4):\n",
+ " cap.grab()\n",
+ " \n",
+ " flag, frame = cap.read()\n",
+ "\n",
+ " if flag:\n",
+ " frame = webcam_rev(frame)\n",
+ " img = Image.fromarray(frame, 'RGB')\n",
+ " frame = frame.reshape(accel.ishape_normal)\n",
+ " return frame, img\n",
+ "\n",
+ " else:\n",
+ " print (\"frame is not ready\")\n",
+ " cv2.waitKey(1)\n",
+ " \n",
+ "def consumer_live(accel, frame):\n",
+ " class_out = accel.execute(frame)\n",
+ " print(\"Class name: {}\".format(class_dict[int(class_out)]))\n",
+ "\n",
+ "def webcam_rev(img):\n",
+ " img = np.array(img)\n",
+ " img_cropped = img[:, 20:140, :]\n",
+ " img_resized = cv2.resize(img_cropped,(72,72))\n",
+ " img_rev = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)\n",
+ " return img_rev"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "source": [
+ "cap = cv2.VideoCapture(0)\n",
+ "while not cap.isOpened():\n",
+ " cap = cv2.VideoCapture(0)\n",
+ " cv2.waitKey(1)\n",
+ " print (\"Wait for the device\")\n",
+ "\n",
+ "# set small capture resolution for faster processing\n",
+ "cap.set(cv2.CAP_PROP_FRAME_WIDTH, 160)\n",
+ "cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 120)"
+ ],
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 8
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Classify Webcam Input\n",
+ "* Make sure you are in a well-lit environment\n",
+ "* Blue-colored masks are classified best (will be improved in next update)\n",
+ "* Position your face in the center of the frame, close to camera (see examples)\n",
+ "\n",
+ "This notebook is a basic proof-of-concept. Model was trained on simple blue-mask augmentation of Flickr-Faces-HQ (FFHQ). For better results, more mask-types can be supported (e.g. https://github.com/aqeelanwar/MaskTheFace)"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "source": [
+ "clear_output()\n",
+ "frame, img = producer_live(cap)\n",
+ "consumer_live(accel, frame)\n",
+ "img"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Class name: Correctly Masked\n"
+ ]
+ },
+ {
+ "output_type": "execute_result",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "execution_count": 9
+ }
+ ],
+ "metadata": {
+ "scrolled": true
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "source": [
+ "clear_output()\n",
+ "frame, img = producer_live(cap)\n",
+ "consumer_live(accel, frame)\n",
+ "img"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Class name: Incorrectly Worn\n"
+ ]
+ },
+ {
+ "output_type": "execute_result",
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAIAAADajyQQAAAnBElEQVR4nG27Wc9sWXIdtiJiD2fI4ZvvXNU1dnV3NdlqNUiTTcuWRdoSSEGSDcOGAT/ZTzb84h/gR/8H+0EwDMEAIRsgYFgPlkzYJNRkQzS7m+qpqmu8VXXvrW/O6Ux77wg/5L1VRcGJRCKRyDwnY0fEWrEj1qa3liADCMkwGEr0Jfirri/FUAADBDCg4MsHffmGGNHJrGmaKkbv2QszC8g0a5oAkEgxmnLOqmQmxMEH54RgXpxq0pxzzqkUBRVFN0zbYRqmZPqVe9mLO9pXPnzxygwROJWTw+XZncN/9+/81qJ17vn3DSLkQXAiwddp6lLWBDiwQBXYP+nFEwAhRFd7X4Uwb6sqeu+FwMIcmJkCaV1KMUJWzeoUbCVVIRIRAcKkqlpIRdRTUSnFxlzMpCjnRGlvBAP4yrJ+1aQXRqrAEuYzPj1bnN1ZDOPq4YMHjggEEGFSA5P3fkjZKRrPWdQY0xcm8fMrBs8huOB9U9XBSfASozghYXWgIOSEiEhgpVhWc0pmzOJNmQhMAMFQmGBCZlyKmZEVrr3U0Tk2zanPlhUFX4maf8NvewsNSPBRhlSePD0/OTuoq6aOjfsivpzwlHW33lHlq6rSXPo0DbmYfbk2JPBC8zrWdeVY6roSJibyQk7ICZxJIGaQWQGR986ZlaJaDCAXfNFMTExs+79LBMDMkRGBUymhGAhq6rth0xUyGL0w79+w5wvvCdJQfBUWizlgd85OmOm5xwwwMyIQ2TCMfT96D2Ok/CL2MkBwgWZ1mDXRO1FVhglIiMjAICfOQxwxM6mBmVVVRJzzpgC46Bh9BGBmzKKqBUpE3ntHAqWsJSpiU1VVdbPaFL0ZRhhh7zrFV2Lnr4elEbJO/bCZL14KFarKnAFGUEDFMmFI1huKoeSvXEIBQXRUiY8c2UiIXWAiIwGTOWGGOZAIGaBkaqwKEQeGmkHAbMECM5tBxMFQLEcyMJgBMqIiZlGtSgjkqIQ81mubVByD+in1UylfhCK9cKABDHJISmPSxWxxspg7npwYE9QAVXCGZNAXcMQvfmmQgCr4yoXKiRCEWYS8sBMmUwG8CAMCZhIATEZkANiYiIiIhVhMWMwAIitKRiAwGxOxCATMTGZlyi54ZmdKXrbFmNlv+h7ou2Sqfx0b7TmwzZfx7PjorTffYDNTdftcNjMjAPCBgiGP9hyLXsCgCAjKpIB576JnERECmzLBMwcvIgI1s2L24s4GEwVAxERCBBYyM8CMQIXMlAHPRI7ZO4aRQVmiUVM3TRUX86rvxgw4p0yqm6HXL3NtfxPvadbUbV07Ye8kRMdszpmxmRmUEBQUwKApWf6q0wm5QF2RQOxMhL2IE4YqqTrHjsGmZBARJtrbxUwGZSIQmIzJwAqoqu4RnwVkRoCwCYzNmFihzKxmLDR3Vayk77rb9doUgO+mcUxWvoL1IAiblWxpEqr63ZZxwDAXXgQeATBMCoWKgQTZYC/8bgCLhBC9SBNiHbx3jswI6kRCdCAQkQjtPUIgZiIWJtpDH+0DjkAGAphIS4IpAVYMRFJUhAEuMCZmZnISYqjqWNd+2w1hNay23crKPq++gMeSYQ59P2w2UnIZp6mp2YEoMQiIoJoMJJqzAE7AwJgABRimIGVHbtE08yDBex9k7xPVQmJEJILKi5WsamRGZN55Yn5OlESkBjMntM/fbGAIEQHwLF6IACKCeAOImZwrxM4pUXHei4tX16vPV9tSAAb0ef6ngmHKR/PZmFI/JmI2giMQgxgGM3HMwinBCGYwAPx8baJg2VYnB/PDeZtzzw7i2QfnnDALi5mZ964iQk6qqrns+QNkYLbnmExf8BARiRETkYGImNhIiMixI+9Ae3SFoz1TOFhpQx2dF0N6cRXyYIAMjmkYRnhc39yU/HKaimMiT2xQAgDKqoVgjKRQgATesxWtHZ0czM+O5oumIteA4EVi8Pukco6ZyMyoFJRQctFcTIupGZPRvtg0ZNvjCgEg8t7DbI/b+5/b84Qk59xzXiZSU/Zu6seSMxN5wQgYAwVsCExV5ckoT5rILi9vQY6YnDArAyDKhZlZmEouBfpihU3NMxaz5vTo4M7R8nDWsCNxLMR7q2SfTcx93w/jVIwLwWBGZGzg55BLZnD2BamSgTxDDcCeowGISHReRMBUzJgA5lIwlWxMwzQWVQDCUEAVLsM5Y9NpLCJBNX/65Nn1anV6Ujv2RAkMzmoqEpwL41gyjAGBFeRss9o9PD66f3hwejCvYpi1tffinRcRLVqyTSnnVEpJalCyQsiwQomFzUhBZkzERKpaDEYEhQqIxRjExGDHQl6cC16YmJgIagqyqaiQeHbOCZwoQUYIIQHECJ6mUoyIhbxzx8eL6KmNwR0/POnXu2k3srmxFKUsCs8Y6Tn3icOijvdPDo/mTe1lNquaqnbeqaoCydCNabXpximVbAVmgJIvjnJmKwZAzQjGDM0gckULC4EERApjA1gIpKpgiFFRMEoIQYsJM1ESFtIpilNTA6AgAQj7FQRxMZ0tqvt3D3/97beWi9Z5cf/pf/2fYSjTun//nfd+8e6vPjs/5x04v2BnoA5yOI+zys9rHxxISz/026th3e2mgilbN05qJC4QCbGVXMzYzMGCFSVWs6yWhIqqiPPZMgqJk2LQkqDFKzgZkZHkQcl7L0wpKxMhZ3I+MHI3RWLKOTA6QmEoA4ScMapWgR49PPm7v/v9t958VFeAqlNXpEXt49/4/q995ze/PWT943/5w3/yRz/4omDxQpV3R8uZdyzCPsar1fb9x4+fXtxuumk3lmFKIcS6bbxzLN5JaJsZk/PeO2bPDnCliJmxY2NXiA2UMotwKihZvZGpenFkfNONQ1oRihcODprHWRvvnp1UbdP3UzAVgAmZAAYzCbErGhy9/PD49a+dSdk1blbUnBgRigSYZhYJke8/OtUvctzAwnfOztpZq0BSvbk4//jZ+dPL64ub7upmtxvLOGWDBSfC5MMixvl80XvHi3l9sJzB1c4FmBgIbNlMQSBX1FS5G4dd15NZTqmk0g3jlKdhGlTTrImkY125eVtl5LNmISJt0zjsiGEEEDl2gZxJPmjj3/6d3zhZRsulIhrMXERkZFMTZgCO5P7RMSWIoDBQMGtm984Oa6f9uL1d63ufXTy9vN1u+74f06RpSELMzGlKoxryWtJ0291Woc7lDJgJQjVrs45ViJlKzhO45FLGsZDzt+vxdrPLZbpdrXZ9maaBoMGRE9rsOu90oS1CvLwZ5zVR5U6W7TxcrCeYggSKnIRyLq+//tJL909nIUM8NHl2Dihq6oRMC8wUenJy9GvfPvvRL85LgTDuHx80Vdx0w9V699OPPltPrNTU86WrSk6lKcXMcs7DOE7TmPKU0gY+FNVue9tGP1TinBERGzM5ADnnYRh33ViKXl1drzbXUxqmNBUzYThmK6WoQiib7bZdzrnfdcjl0clxM1+G2nOfkGDFFGBnbe3ffP2Vuoolj57ZlAjknGM2IzN2XEomKgb7ztvf+NlPz0nhBEfLmr0bMp7eDndf+sYhQs7CzqvtKyRLKU/TMA7ddrtJ/arb3OaUoWm3uQpsbe1BRZyDUGRSLTlP09QPfbfebG+vV0RqOVtJwiDmIBLq1gl5J06E2JgxpvTp5xek+XS5jFVkft4QEYUjHC7jW2+8PA27eeVQEkgIcEJEcETqhFULC7iU/vJyRjh+0MTZbEj6V+9+wt698cbbt6uukThMmEASK4iA2AAi0pLGcczTdnd7dXt5mccpjf0wbvt+ozYRcwgMLbmMuYzEhTiV0s1nYZomJrec17GOIiLiog8iIiJmWjSZlboJOqx269ujppq3kWz7Bcs7onnDd46X87bWtA0+pGwEuGEYGCxAgtV1pZqYyul89uaj5XU/eF9tEupZO42DQtoopaTJshfvnSuMwmbEqeSpDAm5sFTHZ5VR2m7C6C2nKY1OyHsnZCyKUnLpUx6n1NVNTFN2zqlyKaWkQmAyvdne7Ha7fZ/COa6im3o+W9bLdu653D07rj652kwQAoGc47fefP34eC5kgJnqvvJ1v/rwsQ9h31UhYhDIiBDuz0+GtPrlJ599/ze+951XX/3Rz/9yXF+QVN1Q0liGNKX1jutGJRTmpGqGYUg5Z825CsvEuZ2F28snKZcCautZgQnKmJOJTzrmcSzTOGWduNJq3h6eHhyeDrsN5ymMw+mZCpXgxWwismHY3a42Y6DZ4rBq9KCutuuBjKpqNmtmX3/9taoS9Z0hFVXSQObd1x7e2RemZjaO4ziOmnUxm8dQ3Tl1N7E8unf8yqOzp0+XtXPrTZ8S3W62q27wVWND6VJJhrEoWKZpmtd1LX4ch9rVNG0317d+WTfz9s48sFMmdQ2vVtP1+ma92bCE5eHd6vhufXgsdcvidtGlzQZkmvogPPQbJl22zfHpnSh3oOM777zztdferGetvxnIsfdWVfztt17zZFyI4fYbSAPc8WFDhKKZiIhqVdVsV49viZXVDhZVEwmpqz0dzuo06Gbb5bFHTtvVeHh67+zho8efPYPp4ujo2dNnVd0cLJeXTz/vUy9mvmnX3fpomjlnWjpADpoKEz/NXc7jbHEWj+4limUorEPKudus825bhk3QtLxzfOd4uVldT9NQOTc/Pmiao88vLv7q5+/EZqGEWZRZRX/7+9+9c9KKpv0uR8kAEhRX14HYzIRgIFNNJdvhnTY20PV4enIi7NvYzqvKcakrW855yiLIkNg6aizfO1i8+/6HT29u2Pky7G5L3+3WjkDF7j58pMPqa688OlwurGg2moau367HbhdCXTWLfkKchUqcD1Gr2er6xvlw/nRTUUHO56RnxwdEYV+3ZrXD03u9+pvd7rvf/VaAbq8v337jYRWSMEomkAAKgpK6qgrMBCpmRbUQM7N84+1XHr/3+DIlmS/TaDG0s9mCmOaL2Cybo+Vy0w27Id2sttdPB/LN3YPFZKbsBImTHTbx9molnNuwaNt4enB6e35RV1VyoQAHhwdnp2fZHwzcblPWzUa0atsmMXebXV25ojY7OHjt1a89/fijz89vZ02cNbGYgnm2WD5q5vbpZ08++dSX6fUHd88OZoRsRtnICdt+h2xwVS3MTCSqRbUwiEzvnFb3Xj7+1cfPXmnnw7TaYKNeKqkapweHi26pFze355ernNxm1CF3PjjvgpEbMudxx1M3DynEetlUhyHr7rJq3MSuESfinl6ul9ViLG233jinZq7btGM/VU1w03a72i0czuYVT5uzAz8NHjYW847LoS8zX13m8PB0/Yt3+juLo1k8fLC8R7QDSnAwTWIwIzM4F4NngqkpUWGCkbI6evTwXtu+V3L2wZ1fXIQQrGisY9tW83kgsVKU2bk+dYmHbNmQLIdizMG3onMcnBxHLprXBtQ+OEjt9rVFEna53xHUM0MgZJrHMtmd4yMtdeo70nG92h20ftbUjqummbdVcI6TqnPGKNGXBw8OZksKraa/3o/bPxwziJ/v9phAVgwoSCeHs6NFW0XfLKqri/OXX351c7txIUx5qoKbVdXJ0dyFEHapz9QnTcrFiJXZctv6dd+98q033v3pj4lK5eIsxomcwxgDn5ws3/vogsNBHUJGMc1iI7k4jrs0dI4tULE0NPNYR5rN4qKtmjhHHonKOOWb3S46agK//uq9l4/rQjsQfXVeYWZm5va1g2Pwi30ruIhaHeTO6XGGZ2Jm/+zp+WI274eBOU5Dn41ATiT7oHAinjKYnXdkgVLlablou8snB4H8ODUhLhb1bZc4U123c9VQb/vO6lktUdiKJiqFpdhBvYAVL4ii88rVtZwdL6NnMWjKIDUoEc3qthJnJT186VWpRLX8/3hMiByDYUREZGQEZlJeLKqXHp0Ouf786mbR1rerTef8YjHbdKMjUtCYTU2IlIWEWNizD5XXRuAtn5yevP/B45vV1awJddUoitkk3vvQSD+2TXu5uj30y9lyLsJTP+WMMavAvFBwJNBFExZtiMEHR6yTmc8GtbSIFaXOsrW+funBwyFNIvSlQfS8WcQMY2IxZmUygZFBxDchupTWX3/9FQ+KHicn813fX6264uKIMllJJavBsa8keOiykoW3wxgalv/8P/mPbHd9ILlh8741N1OQ2ADLmosj1wZuaTiucOjcMtaHi1kVZTFv66aO0fvoZm1TVYGZS84pp0yaMk1a9Rl3DyorZX7QzKuqrWpjMytAMVOD7usmgzqAVYvse2S07/0iJ4RQe1+v1j1xGMZdbKvTk8XtanN9cennCyZH4s0yQR0TEwJPs6b55jdeW7bxn/3vf+RZLOfgKEZ2nonI+4rAuYymw6wJTeWmfndweDjmQTXPFm2XiVMhFCErKFNRJ2QEnSyZRomrzaoNjmFaxtfvH58c+RBytkwkgJnhRQkFAF8O/uj5/IHNTBwMOD49Xq23JOI45FyY8nI+66fheihqJbgg7MQTeRe5ms+a5WL26dMnf/H4o0VdK9l6vQ0xzJrYVDLlLOzMYFrIrBI3jzEGV4Z+Npv1pYx5DFwhMBkYKoBa6afsHBwRu/a626pNy1g3sT5czI9ncvfeItvkORRSM9DzGeJzGHFERlz2jXtTgpIZ+vGmbefLg+b8ajOft89uVlUVS0paElE+rOOUCrGpFjZO0zSZEWG92ZKVUf14OwazrGF5UC0O6lhxWo9aMjGz+Bhinsq8brf9OFvQZr2Glxgka1ZVMgBQzcVKEQIom60vrsOMTw/nYZzGYXr50dc2Vx8dHi8hAhNgwnMs/MIuODPApJiRgYz38SixHVM5Pm4++PixlSY64dybBnONsjjVKnphNjMQqVI26JimomasVg39LZexruuqqhfNQrVgPyWDMALEidfZ8uj22cV6s3Wh8tFbMe9sLClrMS3FDKCSyrqod7JYVLPKuO/IOx9sOZ+tLkMMM9YkGDPM8HwY9sWE0ykKVLGfeSM/D1SCqVRVPTuY3Tn65ju/eLy9eaIFIhSErQCmzDKME7Go8VRoLFDlTGFKaRitFTps6lk7I4qplCHnQYWgAY6oOPExlrYJm24XXWSORDyM/TQNaRpTSWYQFh/CrG2qWIskKxOTjNP08KWXhu369M5ZbGaZBpRi8iKxvgxFdrlAmJhZocWyalZVaA4Ui+KNb377k5vZ2Wu//tGfPjuIIbAKcmZvhnGyYmaKSZEKJwjYF1ePqcvsvEflnU3D6ioXrsccBsQy9eIlUhbTmu34cN5frsjXnYXoJbJVVW0lM5QZqsYszJxT7qdtFasxFVdXDKxuzt/+9a8TRmNWqqDTC8wwIpgBpO7xJyshEBkzeUfOsxPykZUUJCBZ9/rom9979MYbP/y//8+b808iW8kgcFbdS04UYuSIAjiAjJFCEHZIaphKKUrBG3s2KSEOxdTMKQFCjqt2kYhYnBqURJgYYMsCCGkpuRSwUCU0DOPZ2UvVcnl492S4vVrMHfGgChTCfvyLLyHRDG5K8E5ERNXyWGxIBk2mARJZepOSDm42dHj08Pf+0X/5/jt/9f67/2939azvOh9jGpO4ACWwI3IKaM5aisLtFJZcFK9gNie+gieXdZi6VLIY5QRmxxLZ4FBAko3NLIgQTNM+cIru52DMy5P7L3/7d1KcldYOXhl15sBdyal2dVLdg8dXCg9z7z4+D8LCVNexroKINU0MJtH5SQdqY0qzpmmJZDuGszd+d/nav79s+l/++E9+/Of/gsE0eZ1QovScrA485Zy2UWwYh2lk9jNxjaifsbcyeBumsRuTdX1WkqqZJVIREaqSJkRHlpw4njggCGnicZ3Xy/snb3/39+LyrQ+edJqXX5s39/xjoKNcMfOEbPudiunzOTpgRu54UaZx3G2HzSppUVUtpYj4OjbtssnVUJ98c9NlH1sRd3O7Xpf6o+uKTv69v/EP/m3Jq7R78uzxv37nZ//q4vF5dMva6zzks6O2aZaffvrk06cfHZ4+jPXBuuuFrWRVxOv1dQheddptrrJpDLGkbROrqshk4yA7hQZX37v36r2TlxYn99vlyVT62+2UzU1DmqZsZqa6ZzxTJbDB9jwGQNUAct/+1qulKIxKKeM05ZKmKe12edgN227Xb6k9UTJroi9ZH9w5rgZc9N1m4F1qs7bV4tHd7/5b3/jt/+Lm2a8+eucvrz74iU+33lHu1/OKNd+u187V3ogTUAxpHId+3a+Hw8MFTMdu2GwpKx+dPOCjh6ePHp08fFQvD6q6EQ7ddkquvckuBm/KvuE0gdihKNt+nGj7ljlgsC+kHzArjiEGFWcxclWzqoCoTIFxaCTW3PnFJ2nqd33f1nXdDdsyuYftoTvLPJfPzsfPPk+fXNLVrKb4jQe/+fa3fnO9wNM//ef/2/s//3Ok7fW2q20n9YbEeR/YiqZhXrs4a/PYZXYHR3d+6+/8/qPXvtXLbMOzYrOksR8KvOl0UzX9wXKW8yS+7a8TOz6cLYA1kRoyE5RMyUj3zPyFiE0BchDyLsDMihIqRwrLiEUzOLjFnXvN9nhy9Uihn3Q7jpfbXazKbFpUG2pd9c0HMNiQy9XtthvLJ4lPD1/67f/wv/rG937rz/6vP/rJu58+uz6/vM3HJ2eOZUi7nPrAhSzfvffg+7/zu4++/t1c3z3PcTuSuAiqqlg1MbV+qKSiwkM/rtYdeMxW+dh0/aainblSkAFRWDHlr8isvoAQ+uUP/ntmGIrZXmliBoxKzkoV76zw9Q/So0vMNtf8yv3YhHHK1UVPKeVY1yQc60igtkHj0HpMY9pttqzkCeK0Hy7+7Ad//LMf/0XedeNmO47btm3u3b3/9q9979t/87d32Y/mRKKwVM3MVY0SjGA6lmFVhtXYbbRkch717OJimkaDTjPq3m5+FO0KNCi0WHguJQLMvoB7c2ZWioK+onEDorpkSvXZJ5+EXdNss6M2/vnPPnvl0Z2zuwtvqdhu3lT9blh/vkoF43xR5lxqzJjefLAw5ffe/2zSGZqvf//3v/4bf4BRra6x25F2Y9Au2vDk/EnbxsPlAsxEVIApld049WMPS3lYLVs/Pzxd366mUrrVNCSaciadGGvlQTUBxUxgnii/8JV9wWZOVQElIjMCKfPe3JHC4tNdfYWjTQojQrtwRy997cPLq8/Wtwft4XI+C5FNowvVetdR1EFzvzU4VEQHS3vj60eXt/3nq5vNbpGcINAwIBuEI2W1qXv04BHYNv142w1wVcrsJJP35qs0mcTZuu+vbm6nadp0/aonXx0FHytfHAVlNTUyEIThzDJghvJFiwogZ2ZEL9RgxnulTEbf06N//WR2yU1bH+RuzJOFCsenB5vbzeVKP7+6+tVHkOCbtq7bOA4lUP/gdEls511/vV2fHIbT43Z2yJ+v0meXu+21N45i+bChg1ra2XyzutiOU19cdnXdHnrjadL1rp+0aB7TuBl3qzL2zodsTn1UV42qRbmKTdGoEEaBmWl5Lh2iPX7sHccOwAsG+FLFM6o+u1mcb++uZ1VlQVQwJlePy4NF3siTzXUBwcVxS9NtX9Jt68rbr5w6o24a+nH74ODuxfnm8vPdwd1jF3B8t6oLckKg0BC69eby/FnbVBb9sB7U1DZdURpHXXe73TiaJuhEGVU9j7Eet52yjMXymCNLXBxojmqBqJiWvbueKy6fK+MYZq6U/dYaXwYoudF//fGTvpQy86cXCctZueo2etV72d0MdpvZCnjqY/BHratju1zUGt3Pn02zIPNw2FnSEIjc04v1xBaXCxGiiO11N+6SDXnQ44utmYs+suZxve0mo360btScICzBOLowTsM4jUoE4WlC62opw6rIln07CVNINJIboRPApgJzwH7aZ64U/aIu3nsuq9308XYXMzuZMDvArHHbyW/KkCbdTXAQ9gjilou2aWIQ8o5gIPjdgNWq/+zz3UsPFk7IVV6TXZ5vp+yNQHmzHXks7ToL1RJtcpo0JRVvKo6NLEP33Qsiz2Q+lwIXiiyTVNuUKbFMbvKNEhEKDKRkEBh/BRgNUKe6V1wZEZjZrBQtn63XH12O0p4czvu+q23oiP3d0/sCGTJyGZ1HVTnvjVCcsBZAScSlMo3jKK756XvrWTNbzuumwe16U6xTG8s0DYNL8XD0LjgIyW43jNuNVpURHKgOvvLCyFAiKt6HUmwslniu3k2lzzmEyY+8KI5Js5mq7pOITOmFfsnMzGmBWSEiIqhmAGpajzcnuuw2q15uxuSGiNlxu1zAG1hQEHN+frWcUrftiBxTMJUQnMxmgAfXQ6Jpi7IqRK4U6vppRFhUB03lSNHtdDPuWqK2WphNVno1EIzZCEqgnEoxKuILuXFAsTz1Q+lznPnsavNQqGEv/dQX5KwveIxcskwKxwwDTGEEKq/dWd0/GZ/cjD+7+Ni530theb4pseHjivbyZyKYmsEIgcB9P5aSWOAdmxZjjVXIKW22vRZzHIehADO3jBR9mkpOJWsuik6Lr4VNvRSGqVBgiSBT7BgpQcVxlNqo23SpW2FS7bDllOYEVTYCBMYGNaipMvFzuIdRGgdyngDvPDvfpylbXbM9mD17dLf86JM/+Xj+Dytnjz/Y3rrw5muRKuy2ULOSteumnHksQsH76Ppcun4oubidIxIins8aJwIU8SFGb4KJZCpSErMlpsTCMC+qnszqGknTNJljCnEZArO76Dfd9mb17Ha4fBKHZ/T56so+57eWdV2lOu2kq6hWVmOFotsNgWuoudxpkGBJyXEqOecBDKBXbbgLP/vnf/r4g2f+D06q+3/Pz+dD6n/wF5+cHLRn9w+UedNP2aSwy0VoYlMiFvGNOM0laylOONY8DDlrqkLMhHFATvtuHwcXmLWgaDZHMXJA0k2yrK3PeeazS7vzZ/3FjlHo5sd/XH34Z+HiccmbJzr+r6Tqt4cPTg4fPPrGb7528uh0lyYTlqbSBBDRf/ff/v79OweLo4WPUlU+kGEc9Xr9o//nL9eP19jRjerj2b2/9d/8k9XZy1t2u+tpvL70kY/OlouDmXieElKGKmIAMabJcipFVURmczHD7e04jpO4OKgTZiI4guUSPRhDDEAZhQtz9o7Hibp1yf0wjjfbdE2QfNHd/un/FD7+s9Mx0W0ex+3yzsIw9eMumyjNz+7P3vz1N1//zuta027qnRcWpkW7xwPUATPgNNBbd+6cQuMY2tg+u/hImuWvwN/5+3/r4O3/eLz3B+dW3V6txm6TUxfYDpeH9+6dVXMURmawwjLMsN7Yuhtn82qzLf2QDUTOVzNqG1gBKfKAqdvA0qz2WobguPKuG6eLm4v+ahXJSxsP7zfdz/786f/yj+/e/pSGvlB12W+L4bBxC2/9SFcTbqfhzbMTF3jQfioDMXsXJs2uS9ARFePAxzcO5o9CczRQKWuq/DZvwCRWh45Wjz9/9dEP19P6+N7fG5Z3Cg9uiP0mffrRxfUn1/PT2eKlw7PjNggKsB1AjkIVbztTkG9j9FACArIBCYEBs6qqS0ZKmoey2m1TmjInK7Y4OIm+Iyrp3Y/P//B/Prj8WYE5asgys+3GnCwOedqmacqko358e3N0EHXccgFlhCo4eCeMlxfh7YPlI4lN0ZLG3hkInAMp+6odbX3qZ+//+Cd/8+2XzizT48GO/p2ndu88eKrWknbrki5uS3N5c3U0u3v/9Oi0YeJxNAiFCsNAqlCgqrCbwAQqIMBR2e66oV8Puw1SRhkPl1XVHExFZ1Uq6ra/+un7//R/uLd+rNOmRAkScrKxWKIMJM2UsinUnN30o6u4lTj2PSmk5KnA/f1X7j7wToYO0+2kcCEGdYVBUwaCny233Q0hxe3w7OMP3vhWube71Jt3N0f/QTz6/tM476LLQ8frJE3b7fJ7v/x4eX7ULg/NRyh2g4VILoAFaYQzlARkO9+snj75xHmqAgusjnFW+ejNzBpvuzyd/+qdx//0f3zj87+ofByUCAVIpZSUx2RIMFGMe/1jdOvtcL1J7mg+luTUKshUBnekypp0r99jytmyZS/eMIIH0opzO0psKvqrn3988PKjmUetH9x97x9fHH14+sofXOAoiaNqpVM3OiUn236Ic5SpbIcpGbyrDWQZu25itjT0m9ubse+Dc3UdqtoFolq4CrIb1oxN3l6f3+6u/9U/O9t9mLpct83IEggopmpZMRmyyag6ZCtsHEg8dWPZjTB4ssxgVXNxKJScaVAzI7AZE+VSUqyExK/HBaZz6aqx3p3bs/efvPnmaTVKr9vw9A8XtI4P/+51PEnUZuSUNIiIczfr2+XR8cFhs1r3Y5dC4/NUeErscuo3galZzKsqJp2YCrPlnLY2qiW7Or+4/BDPflh/+C+b9c67ZtTRmziQ5qIFRqGUKSnDyphR2KJXJhvU+nEIBlWYGam6TIEYoELYz1woK4yJymQskyOWmeu60fUCfvKzT7/50uujXkwcQEme/h/h5udHj/7B1dFvX630II1V0xV1wgfD7U2mUapYSlxvNetUMOpWWt/UsS/TburWzFWoa8uD6SDZd6th+/iHzfpP7OPPptWNcQ4N+p1Fp6xxZ5aYK023BROSBzLQFVWdPGEirNOkakfek/DOmTPLucAsM1R4X6Go5cLE4ghKQmjE52GEane5/cmPfnn4rROiVOWQiy/lIn/4h/XV+/cW31vJYbeGkwReK8UQDqlX0Jokadai3uBHTkYTwTL8lClNetC243a3Pn9fbz6Q9S/tZrP7/EL7XUmiHJNuGS4UlL2+GGpAKlqMzZAVqXAlKmRTKgU2Eo2mDHLZijOyvbIfTMRgkNH+VBFgsMxeXGFVmvrp8bsfoinHDx5seMVOeKBlHjfDD9L2/ar9tdG/lnlWeErYjVP28cSUhAAMXvqU3GA5UXbewVc+VE3k3c2T1bNf8c3Pw/WPF2W1fXK1ffp5iyASwPVkl5UPSbNaMThQMUIuqsT7o2FjVh/Jk/XFMqFD2ZF6iNufWQFIoQZisBGpEbEJMbNJ4FyM6qjjZBnD7fTpDz/J3+3qV++uO0/ckJlZibtL2v0L1D9BeNXRGy7cS6Ub9N2sbNqQhiZUQE82mRhzcEyVavfkk3z1rty853bvHdD17afX5x88qTQYjFyZ8jBlM+FUcgGMAIYS8r5JKigFuZg3c8yqWggdbK3TMcgBYpB9qV+MjARgNoPq/qiPlWK5OHGRmYPb9uVixPCXtydXFl893tUDLNTJC1xyddiONd4d5L2tO0D1iqteJmlJsmFE6VMxs0xl7Pez07SR7Sdx++5s+NjbzcXl6vzD3hUBIlFWzmPaMLcE0QJVBlNR6ItDN8wgRTaMBS2REbJBFbs0nRT//wEqB1RYRNSXxAAAAABJRU5ErkJggg==",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "execution_count": 10
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "source": [
+ "clear_output()\n",
+ "frame, img = producer_live(cap)\n",
+ "consumer_live(accel, frame)\n",
+ "img"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Class name: No Mask\n"
+ ]
+ },
+ {
+ "output_type": "execute_result",
+ "data": {
+ "image/png": "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Erwfc8qqZGYGQiBqo2RwTMyugKkaITgiMyPzZGCghlC07FqviQDMPBMjJLGzx9t3fuv125/7uMXgVLUfW3735Df+zsU7H9z4zL9yUc+mCsYR7PDg3/j5/+Qr/+Bv3n/nW4fVgfYl+PLkcy89970/cXeajQ5ghEJ27wNM90+a09e7k6+049cr3pwc55PX17x2FlgCj+kYicRQiqqamRKzJNVAQ8qTakBMpRSRYppFppIpepeswKUjCGygBo6cmgLRbmcjqkweQREohphHKMLv3blXB5g9eZUOD3ogy44pbb/5v/zBe9988cf+QplfCx4GgrsZPvWln31xLu994w9OT6frN59ZV8vvTFDt43KC00eWi23vfbd69H8dlNeqfJK5fu9hf/z63cVmEwoKx9J0m2FY1EEEiuzEYsOdjGOUBNQIEHLOKmKmpppSqgDdpBPpznsAR4xqKmpmhDv1w4qZZzBllRxCxVOOpYiVb751/2BTrj812l7cxj3RxoXTsH337V/7766+8PnmU/9qCfHRCs4E3niXnm4+3lX9sblHE0+Ip++abqb+/B4df+Pa9K253kW8WJucvv1o/c67cXM+UVAfZpar6ex4HJazToqI6u4DO0Zm74ppQRQk21liZqA7DRdLzv8fz6kM7g19L04AAAAASUVORK5CYII=",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "execution_count": 11
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Release Webcam"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "source": [
+ "cap.release()"
+ ],
+ "outputs": [],
+ "metadata": {}
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
\ No newline at end of file
diff --git a/finn_examples/notebooks/4_keyword_spotting.ipynb b/finn_examples/notebooks/4_keyword_spotting.ipynb
new file mode 100644
index 0000000..860d626
--- /dev/null
+++ b/finn_examples/notebooks/4_keyword_spotting.ipynb
@@ -0,0 +1,634 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Validating network accuracy\n",
+ "In this first part we will be looking at the overall accuracy of the network.\n",
+ "\n",
+ "The keyword spotting (KWS) network was trained on the Google Speech Commands v2 dataset, as published here: https://arxiv.org/abs/1804.03209\n",
+ "\n",
+ "We then used a feature extraction technique called Mel Frequency Cepstral Coefficients or MFCC for short.\n",
+ "This method turns audio waveforms into 2D images with one channel. Similar to the one shown below:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "A more in-depth explenation of MFCC features can be found on wikipedia: https://en.wikipedia.org/wiki/Mel-frequency_cepstrum\n",
+ "\n",
+ "For this concrete case we used the python library [python_speech_featrues](https://github.com/jameslyons/python_speech_features) to produce these features.\n",
+ "\n",
+ "During the training of the KWS network we produce the MFCC features for the training and validation set and then quantize the inputs to the network to eight bit.\n",
+ "We will load the pre-processed and quantized validation dataset in the next step.\n"
+ ],
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### Load preprocessed Google Speech Commands v2 validation dataset"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "source": [
+ "import pkg_resources as pk\n",
+ "import numpy as np\n",
+ "\n",
+ "input_npy = pk.resource_filename(\"finn_examples\", \"data/python_speech_preprocessing_all_validation_KWS_data_inputs_len_10102.npy\")\n",
+ "golden_out_npy = pk.resource_filename(\"finn_examples\", \"data/python_speech_preprocessing_all_validation_KWS_data_outputs_len_10102.npy\")\n",
+ "\n",
+ "input_data = np.load(input_npy)\n",
+ "golden_out_data = np.load(golden_out_npy)\n",
+ "num_samples = input_data.shape[0]\n",
+ "\n",
+ "print(\"Input data shape: \" + str(input_data.shape))\n",
+ "print(\"Label shape: \" + str(golden_out_data.shape))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Input data shape: (10102, 490)\n",
+ "Label shape: (10102,)\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### Initialize the accelerator"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "source": [
+ "from finn_examples import models\n",
+ "print(list(filter(lambda x: \"kws\" in x, dir(models))))"
+ ],
+ "outputs": [
+ {
+ "output_type": "display_data",
+ "data": {
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "['kws_mlp']\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "source": [
+ "accel = models.kws_mlp()"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "source": [
+ "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
+ "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Expected input shape and datatype: (1, 490) INT8\n",
+ "Expected output shape and datatype: (1, 1) UINT8\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### Run validation on the FPGA"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "source": [
+ "accel.batch_size = num_samples\n",
+ "accel_out_data = accel.execute(input_data)\n",
+ "\n",
+ "print(\"Accelerator output shape: \" + str(accel_out_data.shape))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Accelerator output shape: (10102, 1)\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "source": [
+ "score = np.unique(accel_out_data.flatten() == golden_out_data.flatten(), return_counts=True)\n",
+ "print(\"Correctly predicted: %d / %d \" % (score[1][1], num_samples))\n",
+ "print(\"Incorrectly predicted: %d / %d \" % (score[1][0], num_samples))\n",
+ "print(\"Accuracy: %f%%\" % (100.0 * score[1][1] / num_samples))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Correctly predicted: 8967 / 10102 \n",
+ "Incorrectly predicted: 1135 / 10102 \n",
+ "Accuracy: 88.764601%\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "Here you should see an accuracy of about 88.76 %."
+ ],
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Assessing network throughput\n",
+ "\n",
+ "Now we will take a look at how fast the FPGA can process the whole validation dataset."
+ ],
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "### Using a naive timing benchmark from the notebook"
+ ],
+ "metadata": {
+ "pycharm": {
+ "name": "#%% md\n"
+ }
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "source": [
+ "def run_validation():\n",
+ " accel_out_data = accel.execute(input_data)"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "source": [
+ "full_validation_time = %timeit -n 5 -o run_validation()"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "5 loops, best of 3: 70.2 ms per loop\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "source": [
+ "print(f\"{(num_samples / float(full_validation_time.best)):.0f} samples per second including data movement\")"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "143976 samples per second including data movement\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "While the result of over 140 thousand inferences per second is already very good, this naive benchmark\n",
+ "also includes data movement from and to the FPGA and it is dificult to assess how much time is spent on\n",
+ "which part of running the FINN accelerator.\n",
+ "\n",
+ "### Using the built-in performance benchmark\n",
+ "\n",
+ "To measure the performance of indivudual components of the PYNQ stack and the FINN accelerator on the FPGA,\n",
+ "FINN comes with a buit-in benchmark. This benchmark computes the throughput of the FINN accelerator as seen on the FPGA."
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "source": [
+ "accel.throughput_test()"
+ ],
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "{'DRAM_in_bandwidth[Mb/s]': 121.27598463684475,\n",
+ " 'DRAM_out_bandwidth[Mb/s]': 0.24750200946294845,\n",
+ " 'batch_size': 10102,\n",
+ " 'copy_input_data_to_device[ms]': 29.246091842651367,\n",
+ " 'copy_output_data_from_device[ms]': 0.23031234741210938,\n",
+ " 'fclk[mhz]': 100.0,\n",
+ " 'fold_input[ms]': 0.1590251922607422,\n",
+ " 'pack_input[ms]': 0.1087188720703125,\n",
+ " 'runtime[ms]': 40.81583023071289,\n",
+ " 'throughput[images/s]': 247502.00946294848,\n",
+ " 'unfold_output[ms]': 0.1952648162841797,\n",
+ " 'unpack_output[ms]': 1.1382102966308594}"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 10
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Classifying .wav files with the KWS network\n",
+ "\n",
+ "Now we are going to look at how to classify raw .wav files with the KWS network. We include some sample files with finn-examples, but in theory you can also classify your own recordings. To do this one can simply modify where to load the .wav file from. However, one needs to make sure that the file is shorter than one second.\n",
+ "\n",
+ "First we will install python_speech_features, to generate the MFCC features later on"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "source": [
+ "!pip3 install python_speech_features"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Collecting python_speech_features\n",
+ " Using cached python_speech_features-0.6-py3-none-any.whl\n",
+ "Installing collected packages: python-speech-features\n",
+ "Successfully installed python-speech-features-0.6\n",
+ "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "source": [
+ "%matplotlib inline"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "source": [
+ "from python_speech_features import mfcc\n",
+ "import numpy as np\n",
+ "from scipy import signal\n",
+ "import scipy.io.wavfile as wav\n",
+ "from scipy.signal.windows import hann\n",
+ "import matplotlib.pyplot as plt\n",
+ "import IPython"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "source": [
+ "# preprocessing parameters\n",
+ "tf_desired_samples = 16000\n",
+ "tf_window_size_samples = 480\n",
+ "tf_sample_rate = 16000\n",
+ "tf_window_size_ms = 30.\n",
+ "tf_window_stride_ms = 20.\n",
+ "tf_dct_coefficient_count = 10\n",
+ "\n",
+ "# Dataset parameter\n",
+ "tf_dataset_labels = ['down', 'go', 'left', 'no', 'off', 'on', 'right', 'stop', 'up', 'yes', 'SILENCE', 'UNKNOWN']"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "source": [
+ "# Convenience functions\n",
+ "def py_speech_preprocessing(resampled_data, sample_rate,\n",
+ " tf_desired_samples=tf_desired_samples, \n",
+ " tf_window_size_samples=tf_window_size_samples, \n",
+ " tf_sample_rate=tf_sample_rate, \n",
+ " tf_window_size_ms=tf_window_size_ms, \n",
+ " tf_dct_coefficient_count=tf_dct_coefficient_count):\n",
+ " # Resample\n",
+ " num_target_samples = round(tf_sample_rate / sample_rate * len(raw_signal))\n",
+ " resampled_data = signal.resample(raw_signal, num_target_samples)\n",
+ " # Rescale\n",
+ " rescaled_data = resampled_data / np.max(resampled_data)\n",
+ " # Pad\n",
+ " padded_data = np.pad(rescaled_data, [[0, tf_desired_samples - rescaled_data.shape[-1]]], mode=\"constant\")\n",
+ " # Calculate MFCC features\n",
+ " nfft = int(2**np.ceil(np.log2(tf_window_size_samples)))\n",
+ " mfcc_feat_py = mfcc(padded_data, tf_sample_rate, \n",
+ " winlen = tf_window_size_ms / 1000.,\n",
+ " winstep = tf_window_stride_ms / 1000.,\n",
+ " numcep = tf_dct_coefficient_count,\n",
+ " nfilt = 40,\n",
+ " nfft = nfft,\n",
+ " lowfreq = 20.0,\n",
+ " highfreq = 4000.0,\n",
+ " winfunc=hann,\n",
+ " appendEnergy=False,\n",
+ " preemph=0.,\n",
+ " ceplifter=0.,\n",
+ " )\n",
+ " # Cut and transpose MFCC features\n",
+ " mfcc_feat_py = mfcc_feat_py[:-1,:].T\n",
+ " \n",
+ " return mfcc_feat_py\n",
+ "\n",
+ "\n",
+ "def quantize_input(mfcc_feat_py):\n",
+ " # Scaling\n",
+ " quant_mfcc_feat = (mfcc_feat_py / 0.8298503756523132)\n",
+ " # Clamping & rounding\n",
+ " quant_mfcc_feat = np.where(quant_mfcc_feat > 127., 127., quant_mfcc_feat)\n",
+ " quant_mfcc_feat = np.where(quant_mfcc_feat < -127., -127., quant_mfcc_feat)\n",
+ " quant_mfcc_feat = np.round(quant_mfcc_feat)\n",
+ " quant_mfcc_feat = quant_mfcc_feat.astype(np.int8).reshape((1,490))\n",
+ " \n",
+ " return quant_mfcc_feat"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "## Loading and pre-processing the audio file\n",
+ "\n",
+ "The following sample files are included with finn-examples:"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "source": [
+ "# Find sample files\n",
+ "audio_samples_folder = pk.resource_filename(\"finn_examples\", \"data/audio_samples/\")\n",
+ "!ls $audio_samples_folder"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "audio_sample_down.wav audio_sample_off.wav audio_sample_up.wav\r\n",
+ "audio_sample_go.wav audio_sample_on.wav audio_sample_yes.wav\r\n",
+ "audio_sample_left.wav audio_sample_right.wav\r\n",
+ "audio_sample_no.wav audio_sample_stop.wav\r\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "#### Change sample_path variable in the line below to load your own .wav file or to load a different sample file.\n",
+ "Make sure that the file is shorter than one second."
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "source": [
+ "sample_path = f\"{audio_samples_folder}audio_sample_yes.wav\"\n",
+ "IPython.display.Audio(sample_path)"
+ ],
+ "outputs": [],
+ "metadata": {
+ "collapsed": false,
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "source": [
+ "# Read the audio wave file\n",
+ "rate, raw_signal = wav.read(sample_path)"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "source": [
+ "# Run pre-processing\n",
+ "mfcc_feat_py = py_speech_preprocessing(raw_signal, rate)"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stderr",
+ "text": [
+ "/usr/lib/python3/dist-packages/scipy/signal/signaltools.py:2236: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.\n",
+ " Y[sl] = X[sl]\n",
+ "/usr/lib/python3/dist-packages/scipy/signal/signaltools.py:2238: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.\n",
+ " Y[sl] = X[sl]\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "source": [
+ "# Plot the MFCC features\n",
+ "plt.matshow(mfcc_feat_py)\n",
+ "plt.colorbar()\n",
+ "plt.show()"
+ ],
+ "outputs": [
+ {
+ "output_type": "display_data",
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {}
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "source": [
+ "# Quantize MFCC features\n",
+ "quant_mfcc_feat = quantize_input(mfcc_feat_py)"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "## Classifying the pre-processed audio on the FPGA"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "source": [
+ "# Run inference on the FPGA\n",
+ "accel.batch_size = 1\n",
+ "res_acc = accel.execute(quant_mfcc_feat)"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "source": [
+ "res_label = tf_dataset_labels[res_acc[0,0].astype(np.int)]\n",
+ "print(f\"The audio file was classified as: {res_label}\")"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "The audio file was classified as: yes\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "If everything went well you should see the audio file being classified correctly.\n",
+ "\n",
+ "However,you may notice that the 'down' sample is wrongly classified as 'go'. This is likely a side effect of the KWS network being a very simple architecture. This means that the network works better for some classes than others."
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "source": [
+ "sample_classes = ['down', 'go', 'left', 'no', 'off', 'on', 'right', 'stop', 'up', 'yes']\n",
+ "for sample_class in sample_classes:\n",
+ " rate, raw_signal = wav.read(f\"{audio_samples_folder}audio_sample_{sample_class}.wav\")\n",
+ " mfcc_feat_py = py_speech_preprocessing(raw_signal, rate)\n",
+ " quant_mfcc_feat = quantize_input(mfcc_feat_py)\n",
+ " accel.batch_size = 1\n",
+ " res_acc = accel.execute(quant_mfcc_feat)\n",
+ " res_label = tf_dataset_labels[res_acc[0,0].astype(np.int)]\n",
+ " print(f\"The audio file for {sample_class} was classified as: {res_label}\")"
+ ],
+ "outputs": [],
+ "metadata": {}
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/finn_examples/notebooks/5_radioml_with_cnns.ipynb b/finn_examples/notebooks/5_radioml_with_cnns.ipynb
new file mode 100755
index 0000000..ed3093b
--- /dev/null
+++ b/finn_examples/notebooks/5_radioml_with_cnns.ipynb
@@ -0,0 +1,467 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Initialize the accelerator"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "source": [
+ "# remember to install the following dependencies\n",
+ "#! apt-get install libhdf5-dev -y\n",
+ "#! pip3 install versioned-hdf5"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "source": [
+ "from finn_examples import models\n",
+ "print(list(filter(lambda x: \"radioml\" in x, dir(models))))"
+ ],
+ "outputs": [
+ {
+ "output_type": "display_data",
+ "data": {
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "application/javascript": "\ntry {\nrequire(['notebook/js/codecell'], function(codecell) {\n codecell.CodeCell.options_default.highlight_modes[\n 'magic_text/x-csrc'] = {'reg':[/^%%pybind11/]};\n Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n Jupyter.notebook.get_cells().map(function(cell){\n if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n });\n});\n} catch (e) {};\n"
+ },
+ "metadata": {}
+ },
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "['_radioml_io_shape_dict', 'vgg10_w4a4_radioml']\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "source": [
+ "# Note: the RadioML example is only available on the ZCU104 at the moment\n",
+ "accel = models.vgg10_w4a4_radioml()"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "source": [
+ "print(\"Expected input shape and datatype: %s %s\" % (str(accel.ishape_normal), str(accel.idt)))\n",
+ "print(\"Expected output shape and datatype: %s %s\" % (str(accel.oshape_normal), str(accel.odt)))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Expected input shape and datatype: (1, 1024, 1, 2) DataType.INT8\n",
+ "Expected output shape and datatype: (1, 1) DataType.UINT8\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Load RadioML 2018 dataset"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "source": [
+ "import numpy as np\n",
+ "import math\n",
+ "import pickle\n",
+ "import os\n",
+ "import h5py\n",
+ "\n",
+ "dataset_dir = \"/home/xilinx/datasets/radioml_2018\"\n",
+ "print(dataset_dir)"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "/home/xilinx/datasets/radioml_2018\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "source": [
+ "h5_file = h5py.File(dataset_dir + \"/GOLD_XYZ_OSC.0001_1024.hdf5\",'r')\n",
+ "data_h5 = h5_file['X']\n",
+ "label_mod = np.argmax(h5_file['Y'], axis=1) # comes in one-hot encoding\n",
+ "label_snr = h5_file['Z'][:,0]\n",
+ "\n",
+ "# assemble list of test set indices\n",
+ "# do not pre-load large dataset into memory\n",
+ "np.random.seed(2018)\n",
+ "test_indices = []\n",
+ "for mod in range(0, 24): #all modulations (0 to 23)\n",
+ " for snr_idx in range(0, 26): #all SNRs (0 to 25 = -20dB to +30dB)\n",
+ " start_idx = 26*4096*mod + 4096*snr_idx\n",
+ " indices_subclass = list(range(start_idx, start_idx+4096))\n",
+ "\n",
+ " split = int(np.ceil(0.1 * 4096)) #90%/10% split\n",
+ " np.random.shuffle(indices_subclass)\n",
+ " train_indices_subclass, val_indices_subclass = indices_subclass[split:], indices_subclass[:split]\n",
+ "\n",
+ " if snr_idx >= 25: #select which SNRs to test on\n",
+ " test_indices.extend(val_indices_subclass)\n",
+ "\n",
+ "test_indices = sorted(test_indices)\n",
+ "\n",
+ "# note: labels given in the \"classes.txt\" file are not in the correct order (https://github.com/radioML/dataset/issues/25)\n",
+ "mod_classes = ['OOK','4ASK','8ASK','BPSK','QPSK','8PSK','16PSK','32PSK',\n",
+ "'16APSK','32APSK','64APSK','128APSK','16QAM','32QAM','64QAM','128QAM','256QAM',\n",
+ "'AM-SSB-WC','AM-SSB-SC','AM-DSB-WC','AM-DSB-SC','FM','GMSK','OQPSK']\n",
+ "snr_classes = np.arange(-20., 32., 2) # -20dB to 30dB"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "source": [
+ "print(data_h5.shape)\n",
+ "print(label_mod.shape)\n",
+ "print(label_snr.shape)\n",
+ "print(len(test_indices))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "(2555904, 1024, 2)\n",
+ "(2555904,)\n",
+ "(2555904,)\n",
+ "9840\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Inspect a single frame"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "source": [
+ "%matplotlib inline\n",
+ "from matplotlib import pyplot as plt\n",
+ "\n",
+ "# Inspect a frame\n",
+ "mod = 12 # 0 to 23\n",
+ "snr_idx = 25 # 0 to 25 = -20dB to +30dB\n",
+ "sample = 123 # 0 to 4095\n",
+ "#-----------------------#\n",
+ "idx = 26*4096*mod + 4096*snr_idx + sample\n",
+ "data, mod, snr = data_h5[idx], label_mod[idx], label_snr[idx]\n",
+ "plt.figure()\n",
+ "plt.plot(data)\n",
+ "print(\"Modulation: %s, SNR: %.1f dB\" % (mod_classes[mod], snr))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Modulation: 16QAM, SNR: 30.0 dB\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Input quantization\n",
+ "Quantize input data on-the-fly in software before feeding it to the accelerator. Use the uniform quantization range on which the model was trained."
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "source": [
+ "def quantize(data):\n",
+ " quant_min = -2.0\n",
+ " quant_max = 2.0\n",
+ " quant_range = quant_max - quant_min\n",
+ " data_quant = (data - quant_min) / quant_range\n",
+ " data_quant = np.round(data_quant * 256) - 128\n",
+ " data_quant = np.clip(data_quant, -128, 127)\n",
+ " data_quant = data_quant.astype(np.int8)\n",
+ " return data_quant"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Classify a single frame"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "source": [
+ "accel_in = quantize(data).reshape(accel.ishape_normal)\n",
+ "print(\"Input buffer shape is %s and datatype is %s\" % (str(accel_in.shape), str(accel_in.dtype)))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Input buffer shape is (1, 1024, 1, 2) and datatype is int8\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "source": [
+ "accel_out = accel.execute(accel_in)"
+ ],
+ "outputs": [],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "source": [
+ "print(\"Result: \" + str(accel_out))\n",
+ "print(\"Top-1 class predicted by the accelerator: \" + mod_classes[int(accel_out)])"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Result: [[12.]]\n",
+ "Top-1 class predicted by the accelerator: 16QAM\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "source": [
+ "%%timeit\n",
+ "accel_out = accel.execute(accel_in)"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "1000 loops, best of 3: 822 µs per loop\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Validate accuracy on entire test set"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "source": [
+ "batch_size = 1024\n",
+ "accel.batch_size = batch_size\n",
+ "print(\"Accelerator buffer shapes are %s for input, %s for output\" % (str(accel.ishape_packed), str(accel.oshape_packed)) )\n",
+ "print(\"Accelerator buffer shapes are %s for input, %s for output\" % (str(accel.ishape_folded), str(accel.oshape_folded)) )\n",
+ "print(\"Accelerator buffer shapes are %s for input, %s for output\" % (str(accel.ishape_normal), str(accel.oshape_normal)) )"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Accelerator buffer shapes are (1024, 1024, 1, 1, 2) for input, (1024, 1, 1) for output\n",
+ "Accelerator buffer shapes are (1024, 1024, 1, 1, 2) for input, (1024, 1, 1) for output\n",
+ "Accelerator buffer shapes are (1024, 1024, 1, 2) for input, (1024, 1) for output\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "source": [
+ "ok = 0\n",
+ "nok = 0\n",
+ "total = len(test_indices)\n",
+ "for i_batch in range(math.ceil(total/batch_size)):\n",
+ " i_frame = i_batch*batch_size\n",
+ " if i_frame+batch_size > total:\n",
+ " batch_size = total - i_frame\n",
+ " accel.batch_size = batch_size\n",
+ " batch_indices = test_indices[i_frame:i_frame+batch_size]\n",
+ " data, mod, snr = data_h5[batch_indices], label_mod[batch_indices], label_snr[batch_indices]\n",
+ "\n",
+ " ibuf = quantize(data).reshape(accel.ishape_normal)\n",
+ " obuf = accel.execute(ibuf)\n",
+ "\n",
+ " pred = obuf.reshape(batch_size).astype(int)\n",
+ "\n",
+ " ok += np.equal(pred, mod).sum().item()\n",
+ " nok += np.not_equal(pred, mod).sum().item()\n",
+ " \n",
+ " print(\"batch %d : total OK %d NOK %d\" % (i_batch, ok, nok))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "batch 0 : total OK 1018 NOK 6\n",
+ "batch 1 : total OK 2041 NOK 7\n",
+ "batch 2 : total OK 3059 NOK 13\n",
+ "batch 3 : total OK 4082 NOK 14\n",
+ "batch 4 : total OK 4948 NOK 172\n",
+ "batch 5 : total OK 5682 NOK 462\n",
+ "batch 6 : total OK 6314 NOK 854\n",
+ "batch 7 : total OK 7039 NOK 1153\n",
+ "batch 8 : total OK 8024 NOK 1192\n",
+ "batch 9 : total OK 8648 NOK 1192\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "scrolled": true
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "source": [
+ "acc = 100.0 * ok / (total)\n",
+ "print(\"Overall top-1 accuracy: {}%\".format(acc))"
+ ],
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Overall top-1 accuracy: 87.88617886178862%\n"
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "## More benchmarking"
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "source": [
+ "accel.batch_size = 1024\n",
+ "accel.throughput_test()"
+ ],
+ "outputs": [
+ {
+ "output_type": "execute_result",
+ "data": {
+ "text/plain": [
+ "{'DRAM_in_bandwidth[Mb/s]': 473.18806940706867,\n",
+ " 'DRAM_out_bandwidth[Mb/s]': 0.23104886201517025,\n",
+ " 'batch_size': 1024,\n",
+ " 'copy_input_data_to_device[ms]': 2.1643638610839844,\n",
+ " 'copy_output_data_from_device[ms]': 0.08821487426757812,\n",
+ " 'fclk[mhz]': 249.9975,\n",
+ " 'fold_input[ms]': 0.1418590545654297,\n",
+ " 'pack_input[ms]': 0.110626220703125,\n",
+ " 'runtime[ms]': 4.431962966918945,\n",
+ " 'throughput[images/s]': 231048.86201517025,\n",
+ " 'unfold_output[ms]': 0.08678436279296875,\n",
+ " 'unpack_output[ms]': 0.6284713745117188}"
+ ]
+ },
+ "metadata": {},
+ "execution_count": 17
+ }
+ ],
+ "metadata": {}
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "source": [],
+ "outputs": [],
+ "metadata": {}
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
\ No newline at end of file
diff --git a/finn_examples/notebooks/images/mfcc_py.png b/finn_examples/notebooks/images/mfcc_py.png
new file mode 100644
index 0000000..7f24aa8
Binary files /dev/null and b/finn_examples/notebooks/images/mfcc_py.png differ
diff --git a/setup.py b/setup.py
index aa38a35..cc741fb 100644
--- a/setup.py
+++ b/setup.py
@@ -86,11 +86,12 @@ def extend_package(path):
readme_lines = fh.readlines()[4:]
long_description = "".join(readme_lines)
extend_package(os.path.join(module_name, "bitfiles"))
+extend_package(os.path.join(module_name, "data"))
extend_package(os.path.join(module_name, "notebooks"))
setup(
name=module_name,
- version="0.0.3b",
+ version="0.0.4",
description="FINN Examples on PYNQ for Zynq and Alveo",
long_description=long_description,
long_description_content_type="text/markdown",
@@ -108,7 +109,7 @@ def extend_package(path):
setup_requires=["pynq>=2.5.1"],
install_requires=[
"pynq>=2.5.1",
- "finn-base==0.0.2b0",
+ "finn-base==0.0.3",
"finn-dataset_loading==0.0.5", # noqa
],
extras_require={