Skip to content
Snippets Groups Projects
Commit 09165f8b authored by Yaman Umuroglu's avatar Yaman Umuroglu
Browse files

Merge branch 'feature/notebook_brevitas_import' into dev

parents 30f5ea08 70f2abed
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,9 @@ COPY requirements.txt .
RUN pip install -r requirements.txt
RUN rm requirements.txt
RUN apt update; apt install nano
RUN pip install jupyter
RUN pip install netron
RUN pip install matplotlib
# Note that we expect the cloned finn directory on the host to be
# mounted on /workspace/finn -- see run-docker.sh for an example
......@@ -35,4 +38,3 @@ RUN chown -R $UNAME:$GNAME /home/$UNAME
USER $UNAME
WORKDIR /home/$UNAME/finn
ENTRYPOINT /bin/bash
%% Cell type:markdown id: tags:
# FINN - Analysis passes
--------------------------------------
* <font size="3">traverses the graph structure and produces information about certain properties</font>
* <font size="3">input: ModelWrapper</font>
* <font size="3">returns dictionary of named properties that the analysis extracts</font>
%% Cell type:markdown id: tags:
## Example - Quantity analysis of nodes in onnx graph
----------------------------------------------------------------------
<font size="3">Purpose of this analysis pass is to return the number of similar nodes in a dictionary. So first an onnx model is loaded. In this example a trained brevitas model is used. It was exported from brevitas and saved as .onnx file. With the help of `import onnx` the load function can be accessed. As argument it takes the model path.</font>
%% Cell type:code id: tags:
``` python
import onnx
onnx_model = onnx.load('LFCW1A1.onnx')
```
%% Cell type:markdown id: tags:
**Here a picture or a call for onnx_model in netron**
%% Cell type:markdown id: tags:
<font size="3">The onnx model has to be converted to a format that can be processed by FINN. This is done with ModelWrapper. As described in the short introduction, this is the format an analysis pass takes as input.</font>
%% Cell type:code id: tags:
``` python
from finn.core.modelwrapper import ModelWrapper
onnx_model = ModelWrapper(onnx_model)
```
%% Cell type:markdown id: tags:
<font size="3">The idea is to count all nodes that have the same operation type. The result should contain the operation types and the corresponding number of nodes that occur in the model. At the beginning an empty dictionary is created which is filled by the function and returned as result to the user at the end of the analysis.</font>
%% Cell type:code id: tags:
``` python
def count_equal_nodes(model):
count_dict = {}
for node in model.graph.node:
if node.op_type in count_dict:
count_dict[node.op_type] +=1
else:
count_dict[node.op_type] = 1
return count_dict
```
%% Cell type:markdown id: tags:
<font size="3">The function takes the model as input and iterates over the nodes. Then it is checked whether there is already an entry for the operation type in the dictionary. If this is not the case, an entry is created and set to `1`. If there is already an entry, it is incremented. If all nodes in the model have been iterated, the filled dictionary is returned.</font>
%% Cell type:code id: tags:
``` python
print(count_equal_nodes(onnx_model))
```
%% Output
{'Shape': 1, 'Gather': 1, 'Unsqueeze': 5, 'Concat': 1, 'Reshape': 1, 'Mul': 5, 'Sub': 1, 'Sign': 4, 'MatMul': 4, 'BatchNormalization': 3, 'Squeeze': 3}
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
# FINN - Transformation passes
--------------------------------------
* <font size="3">changes (transforms) the given graph</font>
* <font size="3">input: ModelWrapper</font>
* <font size="3">returns the changed model (ModelWrapper) and flag `model_was_changed`</font>
%% Cell type:markdown id: tags:
## General Information
-----------------------------
<font size="3">Transformation passes have a base class and must inherit from that. Transformations are meant to be applied using .transform function from the ModelWrapper. This function makes a deep copy of the input model by default. The next cell shows .transform of ModelWrapper. </font>
%% Cell type:markdown id: tags:
### .transform() from ModelWrapper
`def transform(self, transformation, make_deepcopy=True):
transformed_model = self
if make_deepcopy:
transformed_model = copy.deepcopy(self)
model_was_changed = True
while model_was_changed:
(transformed_model, model_was_changed) = transformation.apply(
transformed_model
)
return transformed_model`
%% Cell type:markdown id: tags:
<font size="3">When the function is called, the model, the name of the transformation and, if required, the flag make_deepcopy are passed. It is also possible not to make a copy of the model. In this case `make_deepcopy` must be set to False. Then the branch `if make_deepcopy:` would not be taken and no copy of the model would be made.
The unchanged model is first passed to the variable `transformed_model` to pass this variable on to the transformation later.
`model_was_changed` indicates whether the transformation needs to be applied more then once. Because it needs to be applied at least one time `model_was_changed` is first set to True and then depending on the return values of the transformation function the transformation can be applied more then once.
**Important**: Due to the structure of this function, `model_was_changed` must be set to False at some point. Otherwise the loop is infinite.
Each new transformation must correspond to the scheme of the base class and contain at least the function `apply(model)`, which returns the changed model and a bool value for `model_was_changed`.
</font>
%% Cell type:markdown id: tags:
### transformation base class
`from abc import ABC, abstractmethod`
`class Transformation(ABC):
def __init__(self):
super().__init__()`
` @abstractmethod
def apply(self, model):
pass`
<font size="3"> Base class is abstract class (`import ABC`) with only one abstract method (`apply()`) which gets the model as input. To show what a transformation should look like, the following example is taken from FINN. </font>
%% Cell type:markdown id: tags:
## Example - ConvertSubToAdd
-----------------------------
<font size="3">The transformation replaces all subtraction nodes in a model with addition nodes with appropriate sign.</font>
%% Cell type:markdown id: tags:
`from finn.transformation import Transformation`
`class ConvertSubToAdd(Transformation):
def apply(self, model):
graph = model.graph
for n in graph.node:
if n.op_type == "Sub":
A = model.get_initializer(n.input[1])
if A is not None:
n.op_type = "Add"
model.set_initializer(n.input[1], -A)
return (model, False)`
%% Cell type:markdown id: tags:
<font size="3">First the transformation class must be imported. Then a class can be created for the new transformation, which is derived from the base class. In this case the transformation has only the `apply()` function.
All nodes are checked by first extracting the graph from the model and then iterating over the node list. With the help of .op_type the operation type of the node can be checked, if the node is a subtraction node the condition `if n.op_type == "Sub"` is true. It may be that the subtraction input of the node has no value, this is checked with `model.get_initializer(n.input[1])`.
**Important:** FINN always assumes a certain order of inputs, this is especially important if you want to create additional custom operation nodes.
When the input is initialized, the operation type of the node is converted to `"Add"`, this can simply be done by using the equal sign. Then the sign of the initial value must be changed. For this the ModelWrapper function `.set_initializer` can be used.
At the end the changed model is returned and `model_was_changed` is set to False, because the transformation has to be executed only once.</font>
%% Cell type:code id: tags:
``` python
```
File added
This diff is collapsed.
#!/bin/sh
#!/bin/bash
if [ -z "$VIVADO_PATH" ];then
echo "For correct implementation please set an environment variable VIVADO_PATH that contains the path to your vivado installation directory"
......@@ -41,6 +41,17 @@ echo "Mounting $SCRIPTPATH/cnpy into /workspace/cnpy"
echo "Mounting $SCRIPTPATH/finn-hlslib into /workspace/finn-hlslib"
echo "Mounting $VIVADO_PATH/include into /workspace/vivado-hlslib"
if [ "$1" = "test" ]; then
echo "Running test suite"
DOCKER_CMD="python setup.py test"
elif [ "$1" = "notebook" ]; then
echo "Running Jupyter notebook server"
DOCKER_CMD="jupyter notebook --ip=0.0.0.0 notebooks"
else
echo "Running container only"
DOCKER_CMD="bash"
fi
# Build the FINN Docker image
docker build --tag=$DOCKER_TAG \
--build-arg GID=$DOCKER_GID \
......@@ -57,4 +68,5 @@ docker run --rm --name finn_dev -it \
-v $SCRIPTPATH/cnpy:/workspace/cnpy \
-v $SCRIPTPATH/finn-hlslib:/workspace/finn-hlslib \
-v $VIVADO_PATH/include:/workspace/vivado-hlslib \
$DOCKER_TAG bash
-p 8888:8888 -p 8081:8081 \
$DOCKER_TAG $DOCKER_CMD
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment