Skip to content

Commit

Permalink
small fixes (#52)
Browse files Browse the repository at this point in the history
* small fixes

* split

* toc

* update

* update

* update

* executable

* 2019.1->2019.2
  • Loading branch information
jmduarte authored Jun 24, 2023
1 parent 2f9193c commit dd18adb
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 139 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ _build
model_1
model_2
model_3
.DS_Store
4 changes: 3 additions & 1 deletion _toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ chapters:
- file: part4_quantization.ipynb
- file: part5_bdt.ipynb
- file: part6_cnns.ipynb
- file: part7_deployment.ipynb
- file: part7a_bitstream.ipynb
- file: part7b_deployment.ipynb
- file: part7c_validation.ipynb
3 changes: 3 additions & 0 deletions docker/Dockerfile.vivado
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ RUN mamba clean --all -f -y && \
fix-permissions "/home/${NB_USER}"

LABEL org.opencontainers.image.source https://github.com/fastmachinelearning/hls4ml-tutorial

# ENV XILINX_VIVADO /opt/Xilinx/Vivado/2019.2
COPY docker/start-notebook.sh /usr/local/bin/
25 changes: 25 additions & 0 deletions docker/start-notebook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

set -e

# setup vivado 2019.2
source /opt/Xilinx/Vivado/2019.2/settings64.sh

# The Jupyter command to launch
# JupyterLab by default
DOCKER_STACKS_JUPYTER_CMD="${DOCKER_STACKS_JUPYTER_CMD:=lab}"

if [[ -n "${JUPYTERHUB_API_TOKEN}" ]]; then
echo "WARNING: using start-singleuser.sh instead of start-notebook.sh to start a server associated with JupyterHub."
exec /usr/local/bin/start-singleuser.sh "$@"
fi

wrapper=""
if [[ "${RESTARTABLE}" == "yes" ]]; then
wrapper="run-one-constantly"
fi

# shellcheck disable=SC1091,SC2086
exec /usr/local/bin/start.sh ${wrapper} jupyter ${DOCKER_STACKS_JUPYTER_CMD} ${NOTEBOOK_ARGS} "$@"
2 changes: 1 addition & 1 deletion part1_getting_started.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"tf.random.set_seed(seed)\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']"
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion part2_advanced_config.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"import plotting\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']"
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion part3_compression.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"tf.random.set_seed(seed)\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']"
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']"
]
},
{
Expand Down
6 changes: 4 additions & 2 deletions part4_quantization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"tf.random.set_seed(seed)\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']"
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']"
]
},
{
Expand Down Expand Up @@ -251,7 +251,9 @@
"hls_model.compile()\n",
"\n",
"y_qkeras = model.predict(np.ascontiguousarray(X_test))\n",
"y_hls = hls_model.predict(np.ascontiguousarray(X_test))"
"y_hls = hls_model.predict(np.ascontiguousarray(X_test))\n",
"np.save('model_3/y_qkeras.npy', y_qkeras)\n",
"np.save('model_3/y_hls.npy', y_hls)"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion part5_bdt.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"import matplotlib.pyplot as plt\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']\n",
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']\n",
"np.random.seed(0)"
]
},
Expand Down
4 changes: 4 additions & 0 deletions part6_cnns.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,10 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']\n",
"\n",
"synth = False # Only if you want to synthesize the models yourself (>1h per model) rather than look at the provided reports.\n",
"if synth:\n",
" hls_model.build(csim=False, synth=True, vsynth=True)\n",
Expand Down
164 changes: 32 additions & 132 deletions part7_deployment.ipynb → part7a_bitstream.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "6ee8630c",
"metadata": {},
"source": [
"# Part 7: Deployment\n",
"# Part 7a: Bitstream Generation\n",
"\n",
"In the previous sections we've seen how to train a Neural Network with a small resource footprint using QKeras, then to convert it to `hls4ml` and create an IP. That IP can be interfaced into a larger design to deploy on an FPGA device. In this section, we introduce the `VivadoAccelerator` backend of `hls4ml`, where we can easily target some supported devices to get up and running quickly. Specifically, we'll deploy the model on a [pynq-z2 board](http://www.pynq.io/)."
]
Expand All @@ -26,7 +26,7 @@
"_add_supported_quantized_objects(co)\n",
"import os\n",
"\n",
"os.environ['PATH'] = '/opt/Xilinx/Vivado/2019.2/bin:' + os.environ['PATH']"
"os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']"
]
},
{
Expand Down Expand Up @@ -136,27 +136,6 @@
"np.save('model_3/y_hls.npy', y_hls)"
]
},
{
"cell_type": "markdown",
"id": "9ca4f0e2",
"metadata": {},
"source": [
"## Synthesize\n",
"Now synthesize the model, and also export the IP."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ef6c817f",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"hls_model.build(csim=False, export=True)"
]
},
{
"attachments": {
"part7_block_design.png": {
Expand All @@ -167,8 +146,9 @@
"id": "3412fa7c",
"metadata": {},
"source": [
"## Make bitfile\n",
"Now we've exported the NN IP, let's create a bitfile! The `VivadoAccelerator` backend design scripts create a Block Design in Vivado IPI containing our Neural Network IP, as well as the other necessary IPs to create a complete system.\n",
"## Synthesize and make bitfile\n",
"\n",
"Now we will synthesize the model, export the IP, and create a bitfile! The `VivadoAccelerator` backend design scripts create a Block Design in Vivado IPI containing our Neural Network IP, as well as the other necessary IPs to create a complete system.\n",
"\n",
"In the case of our `pynq-z2`, we add a DMA IP to transfer data between the PS and PL containg the Neural Network. If you want to create a different design, for example to connect your NN to a sensor, you can use our block designs as a starting point and add in relevant IP for your use case.\n",
"\n",
Expand All @@ -184,7 +164,7 @@
"metadata": {},
"outputs": [],
"source": [
"hls4ml.templates.VivadoAcceleratorBackend.make_bitfile(hls_model)"
"hls_model.build(csim=False, export=True, bitfile=True)"
]
},
{
Expand Down Expand Up @@ -222,148 +202,68 @@
},
{
"cell_type": "markdown",
"id": "033cc4d9",
"id": "aac3541d",
"metadata": {},
"source": [
"## Part 7b: running on a pynq-z2\n",
"The following section is the code to execute in the pynq-z2 jupyter notebook to execute NN inference. \n",
"## Preparations for deployment\n",
"First, you'll need to follow the [setup instructions for the pynq-z2 board](https://pynq.readthedocs.io/en/latest/getting_started/pynq_z2_setup.html).\n",
"Typically, this includes connecting the board to your host via ethernet and setting up a static IP address for your host (192.168.2.1). \n",
"The IP address for the pynq-z2 is 192.168.2.99.\n",
"The default username and password is xilinx.\n",
"\n",
"First, you'll need to follow the setup instructions for the pynq-z2 board, then transfer the following files from the earlier part of this notebook into a directory on the pynq-z2:\n",
"Next you'll transfer the following files from the earlier part of this notebook into a directory on the pynq-z2:\n",
"- bitfile: `model_3/hls4ml_prj_pynq/myproject_vivado_accelerator/project_1.runs/impl_1/design_1_wrapper.bit` -> `hls4ml_nn.bit`\n",
"- hardware handoff: `model_3/hls4ml_prj_pynq/myproject_vivado_accelerator/project_1.srcs/sources_1/bd/design_1/hw_handoff/design_1.hwh` -> `hls4ml_nn.hwh`\n",
"- driver: `model_3/hls4ml_prj_pynq/axi_stream_driver.py` -> `axi_stream_driver.py`\n",
"- data: `X_test.npy`, `y_test.npy`\n",
"- notebook: `part7b_deployment.ipynb`\n",
"\n",
"The following commands archive these files into `model_3/hls4ml_prj_pynq/package.tar.gz` that can be copied over to the pynq-z2 and extracted."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "22892f4b",
"id": "3ca20d05",
"metadata": {},
"outputs": [],
"source": [
"!mkdir model_3/hls4ml_prj_pynq/package/\n",
"!mkdir -p model_3/hls4ml_prj_pynq/package\n",
"!cp model_3/hls4ml_prj_pynq/myproject_vivado_accelerator/project_1.runs/impl_1/design_1_wrapper.bit model_3/hls4ml_prj_pynq/package/hls4ml_nn.bit\n",
"!cp model_3/hls4ml_prj_pynq/myproject_vivado_accelerator/project_1.srcs/sources_1/bd/design_1/hw_handoff/design_1.hwh model_3/hls4ml_prj_pynq/package/hls4ml_nn.hwh\n",
"!cp model_3/hls4ml_prj_pynq/axi_stream_driver.py model_3/hls4ml_prj_pynq/package/\n",
"!cp X_test.npy y_test.npy model_3/hls4ml_prj_pynq/package\n",
"!cp part7b_deployment.ipynb model_3/hls4ml_prj_pynq/package\n",
"!tar -czvf model_3/hls4ml_prj_pynq/package.tar.gz -C model_3/hls4ml_prj_pynq/package/ ."
]
},
{
"cell_type": "markdown",
"id": "a9a52cfb",
"id": "06d6bd96",
"metadata": {},
"source": [
"The following cells are intended to run on a pynq-z2, they will not run on the server used to train and synthesize models!\n",
"To copy it to the pynq-z2 with the default settings, the command would be:\n",
"\n",
"First, import our driver `Overlay` class. We'll also load the test data."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89c67e4f",
"metadata": {},
"outputs": [],
"source": [
"from axi_stream_driver import NeuralNetworkOverlay\n",
"import numpy as np\n",
"```bash\n",
"scp model_3/hls4ml_prj_pynq/package.tar.gz [email protected]:~/jupyter_notebooks\n",
"```\n",
"\n",
"X_test = np.load('X_test.npy')\n",
"y_test = np.load('y_test.npy')"
]
},
{
"cell_type": "markdown",
"id": "551c5cd6",
"metadata": {},
"source": [
"Create a `NeuralNetworkOverlay` object. This will download the `Overlay` (bitfile) onto the PL of the pynq-z2. We provide the `X_test.shape` and `y_test.shape` to allocate some buffers for the data transfer."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cfb786f3",
"metadata": {},
"outputs": [],
"source": [
"nn = NeuralNetworkOverlay('hls4ml_nn.bit', X_test.shape, y_test.shape)"
]
},
{
"cell_type": "markdown",
"id": "5fde9b2d",
"metadata": {},
"source": [
"Now run the prediction! When we set `profile=True` the function times the inference, and prints out a summary as well as returning the profiling information. We also save the output to a file so we can do some validation."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1fd6dee7",
"metadata": {},
"outputs": [],
"source": [
"y_hw, latency, throughput = nn.predict(X_test, profile=True)"
]
},
{
"cell_type": "markdown",
"id": "1983e7d7",
"metadata": {},
"source": [
"An example print out looks like:\n",
"Then you can navigate your web browser to http://192.168.2.99.\n",
"You will see the JupyterHub running on the pynq-z2. Open a terminal and extract the tarball.\n",
"\n",
"```bash\n",
"cd ~/jupyter_notebooks\n",
"tar xvzf package.tar.gz\n",
"```\n",
"\n",
"Classified 166000 samples in 0.402568 seconds (412352.6956936468 inferences / s)"
"Now open the notebook `part7b_deployment.ipynb` on the pynq-z2 JupyterHub"
]
},
{
"cell_type": "markdown",
"id": "005ae126",
"metadata": {},
"source": [
"## Part 7c: final validation\n",
"We executed NN inference on the pynq-z2! Now we can copy the `y_hw.npy` back to the host we've been using for the training and synthesis, and make a final plot to check that the output we took on the board is as expected."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fee790be",
"id": "f876eff5",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline\n",
"from sklearn.metrics import accuracy_score\n",
"\n",
"y_hw = np.load('y_hw.npy')\n",
"y_test = np.load('y_test.npy')\n",
"classes = np.load('classes.npy', allow_pickle=True)\n",
"y_qkeras = model.predict(X_test)\n",
"\n",
"print(\"Accuracy QKeras, CPU: {}\".format(accuracy_score(np.argmax(y_test, axis=1), np.argmax(y_qkeras, axis=1))))\n",
"print(\"Accuracy hls4ml, pynq-z2: {}\".format(accuracy_score(np.argmax(y_test, axis=1), np.argmax(y_hw, axis=1))))\n",
"\n",
"fig, ax = plt.subplots(figsize=(9, 9))\n",
"_ = plotting.makeRoc(y_test, y_qkeras, classes, linestyle='-')\n",
"plt.gca().set_prop_cycle(None) # reset the colors\n",
"_ = plotting.makeRoc(y_test, y_hw, classes, linestyle='--')\n",
"\n",
"from matplotlib.lines import Line2D\n",
"\n",
"lines = [Line2D([0], [0], ls='-'), Line2D([0], [0], ls='--')]\n",
"from matplotlib.legend import Legend\n",
"\n",
"leg = Legend(ax, lines, labels=['QKeras, CPU', 'hls4ml, pynq-z2'], loc='lower right', frameon=False)\n",
"ax.add_artist(leg)"
]
"source": []
}
],
"metadata": {
Expand Down
Loading

0 comments on commit dd18adb

Please sign in to comment.