finn icon indicating copy to clipboard operation
finn copied to clipboard

Exception: Found multiple get_by_name matches, undefined behavior

Open jacopoabramo opened this issue 3 years ago • 2 comments

Greetings,

I'm currently trying to compile a convolutional model generated via Brevitas and structured as follows:

deeptrack_model_preproc onnx

When coming to the streamline step, I get the following Exception:

---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-7-c3ce74697ba8> in <module>
      8 # model = model.transform(MoveScalarLinearPastInvariants())
      9 # streamline
---> 10 model = model.transform(Streamline())
     11 
     12 chkpt_name = build_dir + model_name + streamline + ".onnx"

/home/jacopo/git/finn/deps/qonnx/src/qonnx/core/modelwrapper.py in transform(self, transformation, make_deepcopy, cleanup)
    138         model_was_changed = True
    139         while model_was_changed:
--> 140             (transformed_model, model_was_changed) = transformation.apply(transformed_model)
    141         if cleanup:
    142             transformed_model.cleanup()

/home/jacopo/git/finn/src/finn/transformation/streamline/__init__.py in apply(self, model)
     99             model = model.transform(RemoveIdentityOps())
    100             model = model.transform(GiveUniqueNodeNames())
--> 101             model = model.transform(GiveReadableTensorNames())
    102             model = model.transform(InferDataTypes())
    103         return (model, False)

/home/jacopo/git/finn/deps/qonnx/src/qonnx/core/modelwrapper.py in transform(self, transformation, make_deepcopy, cleanup)
    138         model_was_changed = True
    139         while model_was_changed:
--> 140             (transformed_model, model_was_changed) = transformation.apply(transformed_model)
    141         if cleanup:
    142             transformed_model.cleanup()

/home/jacopo/git/finn/deps/qonnx/src/qonnx/transformation/general.py in apply(self, model)
    128             out_num = 0
    129             for o in n.output:
--> 130                 model.rename_tensor(o, "%s_out%d" % (n.name, out_num))
    131                 out_num += 1
    132             init_in_num = 0

/home/jacopo/git/finn/deps/qonnx/src/qonnx/core/modelwrapper.py in rename_tensor(self, old_name, new_name)
    302             util.get_by_name(graph.initializer, old_name).name = new_name
    303         # sweep over quantization annotations
--> 304         if util.get_by_name(graph.quantization_annotation, old_name, "tensor_name") is not None:
    305             util.get_by_name(graph.quantization_annotation, old_name, "tensor_name").tensor_name = new_name
    306         # sweep over node i/o

/home/jacopo/git/finn/deps/qonnx/src/qonnx/util/basic.py in get_by_name(container, name, name_field)
     80     inds = [i for i, e in enumerate(names) if e == name]
     81     if len(inds) > 1:
---> 82         raise Exception("Found multiple get_by_name matches, undefined behavior")
     83     elif len(inds) == 0:
     84         return None

Exception: Found multiple get_by_name matches, undefined behavior

FINN branch: main (v0.8.1) OS: Windows 10 (WSL2 Ubuntu 18.04) Python: 3.8

The model is a custom-made model made with Brevitas and exported in the ONNX format.

EDIT: I'm also copying the original Brevitas model I used.

import brevitas.nn as qnn
import torch.nn as nn
from brevitas.quant import (
    Int8ActPerTensorFixedPoint as ActQuant,
    Int8WeightPerTensorFixedPoint as WeightQuant,
)

class ExtendedActQuant(ActQuant):
    bit_width = 16

class ExtendedWeightQuant(WeightQuant):
    bit_width = 16

class QSPTModel(nn.Module):
    def __init__(self, in_channels: int, image_size: int, convolution_sizes: list, dense_sizes: list, output_size: int) -> None:
        super(QSPTModel, self).__init__()
        self.identity = qnn.QuantIdentity(
            act_quant=ExtendedActQuant,
            return_quant_tensor=True
        )
        self.layers = nn.Sequential()
        in_size = in_channels
        for idx, size in enumerate(convolution_sizes, 1):
            self.layers.add_module(
                name=f"conv_{idx}",
                module=qnn.QuantConv2d(
                    in_size, 
                    size, 
                    kernel_size=(3, 3), 
                    padding_type="same",
                    weight_quant=ExtendedWeightQuant,
                    return_quant_tensor=True
                )
            )
            self.layers.add_module(
                name=f"conv_{idx}_relu",
                module=qnn.QuantReLU(
                    act_quant=ExtendedActQuant,
                    return_quant_tensor=True
                )
            )
            self.layers.add_module(
                name=f"maxpool_{idx}",
                module=qnn.QuantMaxPool2d(
                    (2, 2),
                    return_quant_tensor=True
                )
            )
            in_size = size
        self.layers.add_module(name="flatten", module=nn.Flatten())

        # the convolution layers squeeze the image of a factor 2
        # after len(convolution_sizes) layers, the final size will be 
        # convolution_sizes[-1]*((image_size / (2**len(convolution_sizes)))**2)

        maxpool_final_size = image_size // (2**len(convolution_sizes))
        in_size = convolution_sizes[-1]*(maxpool_final_size*maxpool_final_size)
        for idx, size in enumerate(dense_sizes, 1):
            self.layers.add_module(
                name=f"linear_{idx}",
                module=qnn.QuantLinear(
                    in_size, 
                    size,
                    bias=True,
                    weight_quant=ExtendedWeightQuant,
                    return_quant_tensor=True
                )
            )
            self.layers.add_module(
                name=f"linear_{idx}_relu",
                module=qnn.QuantReLU(
                    act_quant=ExtendedActQuant,
                    return_quant_tensor=True
                )
            )
            in_size = size
        self.layers.add_module(
            name=f"linear_output",
            module=qnn.QuantLinear(
                in_size, 
                output_size,
                bias=True,
                weight_quant=ExtendedWeightQuant,
                return_quant_tensor=False
            )
        )

    def forward(self, x):
        x = self.identity(x)
        x = self.layers(x)
        return x

jacopoabramo avatar Sep 16 '22 13:09 jacopoabramo

Quick update: oddly enough, but I really cannot give an explanation to this, now the Streamline call does not cause Exception and works fine. I'll leave the issue open in case something new comes up.

jacopoabramo avatar Sep 16 '22 14:09 jacopoabramo

I tried "hacking" the workflow a bit by copy-pasting the original Streamline class source code and removing the line of code which causes the exception as follows:

def local_streamline(model):
    from qonnx.transformation.base import Transformation
    from qonnx.transformation.batchnorm_to_affine import BatchNormToAffine
    from qonnx.transformation.general import (
        ConvertDivToMul,
        ConvertSubToAdd,
        GiveReadableTensorNames,
        GiveUniqueNodeNames,
    )
    from qonnx.transformation.infer_datatypes import InferDataTypes
    from qonnx.transformation.remove import RemoveIdentityOps

    from finn.transformation.streamline.absorb import (
        Absorb1BitMulIntoConv,
        Absorb1BitMulIntoMatMul,
        AbsorbAddIntoMultiThreshold,
        AbsorbMulIntoMultiThreshold,
        AbsorbSignBiasIntoMultiThreshold,
        FactorOutMulSignMagnitude,
    )
    from finn.transformation.streamline.collapse_repeated import (
        CollapseRepeatedAdd,
        CollapseRepeatedMul,
    )
    from finn.transformation.streamline.reorder import (
        MoveAddPastConv,
        MoveAddPastMul,
        MoveMulPastMaxPool,
        MoveScalarAddPastMatMul,
        MoveScalarLinearPastInvariants,
        MoveScalarMulPastConv,
        MoveScalarMulPastMatMul,
    )
    from finn.transformation.streamline.round_thresholds import RoundAndClipThresholds
    from finn.transformation.streamline.sign_to_thres import ConvertSignToThres

    streamline_transformations = [
                ConvertSubToAdd(),
                ConvertDivToMul(),
                BatchNormToAffine(),
                ConvertSignToThres(),
                MoveMulPastMaxPool(),
                MoveScalarLinearPastInvariants(),
                AbsorbSignBiasIntoMultiThreshold(),
                MoveAddPastMul(),
                MoveScalarAddPastMatMul(),
                MoveAddPastConv(),
                MoveScalarMulPastMatMul(),
                MoveScalarMulPastConv(),
                MoveAddPastMul(),
                CollapseRepeatedAdd(),
                CollapseRepeatedMul(),
                MoveMulPastMaxPool(),
                AbsorbAddIntoMultiThreshold(),
                FactorOutMulSignMagnitude(),
                AbsorbMulIntoMultiThreshold(),
                Absorb1BitMulIntoMatMul(),
                Absorb1BitMulIntoConv(),
                RoundAndClipThresholds(),
            ]
    for trn in streamline_transformations:
        model = model.transform(trn)
        model = model.transform(RemoveIdentityOps())
        model = model.transform(GiveUniqueNodeNames())
        model = model.transform(InferDataTypes())
    
    model = model.transform(GiveReadableTensorNames())
    return model

This fixes the problem, but still no clue why calling GiveReadableTensorNames() causes that exception.

jacopoabramo avatar Sep 16 '22 16:09 jacopoabramo