예제 #1
0
def inference_cost_conv(model, node, discount_sparsity):
    # extract info about the conv kernel attributes
    k = get_by_name(node.attribute, "kernel_shape").ints
    k_prod = np.prod(k)
    group = get_by_name(node.attribute, "group")
    if group is None:
        group = 1
    else:
        group = group.i
    # extract info from tensor shapes and datatypes
    (i_dtype, w_dtype, o_dtype) = get_node_tensor_dtypes(model, node)
    (i_shape, w_shape, o_shape) = get_node_tensor_shapes(model, node)
    bsize = i_shape[0]
    ifm_ch = i_shape[1]
    ofm_ch = o_shape[1]
    assert ofm_ch == w_shape[0], "Mismatch in output channels"
    assert ofm_ch % group == 0, "Invalid group setting: " + str(node)
    ofm_pix_total = np.prod(o_shape[2:])
    n_macs = bsize * (ofm_ch // group) * ifm_ch * k_prod * ofm_pix_total
    w_mem = np.prod(w_shape)
    o_mem = np.prod(o_shape)
    if discount_sparsity:
        wname = node.input[1]
        density = get_node_weight_density(model, wname)
        n_macs *= density
        w_mem *= density
    idt_name = i_dtype.name
    wdt_name = w_dtype.name
    odt_name = o_dtype.name
    mac_op_type_str = "op_mac_%s_%s" % (idt_name, wdt_name)
    w_mem_type_str = "mem_w_%s" % (wdt_name)
    o_mem_type_str = "mem_o_%s" % (odt_name)
    ret = {mac_op_type_str: n_macs, w_mem_type_str: w_mem, o_mem_type_str: o_mem}
    return ret
예제 #2
0
 def set_tensor_datatype(self, tensor_name, datatype):
     """Sets the FINN DataType of tensor with given name."""
     graph = self._model_proto.graph
     qnt_annotations = graph.quantization_annotation
     ret = util.get_by_name(qnt_annotations, tensor_name, "tensor_name")
     if ret is not None:
         ret_dt = util.get_by_name(ret.quant_parameter_tensor_names,
                                   "finn_datatype", "key")
         if ret_dt is not None:
             if datatype is None:
                 ret_dt.Clear()
             else:
                 ret_dt.value = datatype.name
         elif datatype is not None:
             dt = onnx.StringStringEntryProto()
             dt.key = "finn_datatype"
             dt.value = datatype.name
             ret.quant_parameter_tensor_names.append(dt)
     elif datatype is not None:
         qa = onnx.TensorAnnotation()
         dt = onnx.StringStringEntryProto()
         dt.key = "finn_datatype"
         dt.value = datatype.name
         qa.tensor_name = tensor_name
         qa.quant_parameter_tensor_names.append(dt)
         qnt_annotations.append(qa)
예제 #3
0
 def set_tensor_layout(self, tensor_name, data_layout):
     """Sets the data layout annotation of tensor with given name. See
     get_tensor_layout for examples."""
     tensor_shape = self.get_tensor_shape(tensor_name)
     assert type(data_layout) == list, "data_layout must be a list"
     if tensor_shape is not None:
         assert len(tensor_shape) == len(
             data_layout
         ), """Mismatch between number
         of dimensions of tensor shape and data layout annotation."""
     graph = self._model_proto.graph
     qnt_annotations = graph.quantization_annotation
     ret = util.get_by_name(qnt_annotations, tensor_name, "tensor_name")
     if ret is not None:
         ret_tl = util.get_by_name(
             ret.quant_parameter_tensor_names, "tensor_layout", "key"
         )
         if ret_tl is not None:
             ret_tl.value = str(data_layout)
         else:
             tl = onnx.StringStringEntryProto()
             tl.key = "tensor_layout"
             tl.value = str(data_layout)
             ret.quant_parameter_tensor_names.append(tl)
     else:
         qa = onnx.TensorAnnotation()
         dt = onnx.StringStringEntryProto()
         dt.key = "tensor_layout"
         dt.value = str(data_layout)
         qa.tensor_name = tensor_name
         qa.quant_parameter_tensor_names.append(dt)
         qnt_annotations.append(qa)
예제 #4
0
 def rename_tensor(self, old_name, new_name):
     """Renames a tensor from old_name to new_name."""
     graph = self.graph
     # sweep over inputs
     if util.get_by_name(graph.input, old_name) is not None:
         util.get_by_name(graph.input, old_name).name = new_name
     # sweep over outputs
     if util.get_by_name(graph.output, old_name) is not None:
         util.get_by_name(graph.output, old_name).name = new_name
     # sweep over value_info
     if util.get_by_name(graph.value_info, old_name) is not None:
         util.get_by_name(graph.value_info, old_name).name = new_name
     # sweep over initializers
     if util.get_by_name(graph.initializer, old_name) is not None:
         util.get_by_name(graph.initializer, old_name).name = new_name
     # sweep over quantization annotations
     if (
         util.get_by_name(graph.quantization_annotation, old_name, "tensor_name")
         is not None
     ):
         util.get_by_name(
             graph.quantization_annotation, old_name, "tensor_name"
         ).tensor_name = new_name
     # sweep over node i/o
     for n in graph.node:
         if old_name in n.input:
             n.input[list(n.input).index(old_name)] = new_name
         if old_name in n.output:
             n.output[list(n.output).index(old_name)] = new_name
예제 #5
0
    def apply(self, model):
        # TODO we currently assume that all dataflow nodes are connected to
        # each other, forming a single partition. check the assumption and/or
        # improve this.
        all_nodes = list(model.graph.node)
        df_nodes = filter(
            lambda x: get_by_name(x.attribute, "backend") is not None,
            all_nodes)
        df_nodes = filter(
            lambda x: get_by_name(x.attribute, "backend").s.decode("UTF-8") ==
            "fpgadataflow",
            df_nodes,
        )
        df_nodes = list(df_nodes)
        non_df_nodes = filter(lambda x: x not in df_nodes, all_nodes)
        non_df_nodes = list(non_df_nodes)

        if len(df_nodes) == 0:
            # no changes if no dataflow nodes are present
            return (model, False)
        else:
            # partition the model into two models
            df_model = copy.deepcopy(model)
            non_df_model = model
            # remove all non-dataflow nodes from the dataflow model
            for node_to_remove in non_df_nodes:
                df_model.graph.node.remove(node_to_remove)
            # identify the entry and exit points for the dataflow part
            df_in = df_model.graph.node[0].input[0]
            df_out = df_model.graph.node[-1].output[0]
            df_in_vi = df_model.get_tensor_valueinfo(df_in)
            df_out_vi = df_model.get_tensor_valueinfo(df_out)
            # set df graph in/out to be df_in/df_out
            df_model.graph.input.remove(df_model.graph.input[0])
            df_model.graph.input.insert(0, df_in_vi)
            df_model.graph.output.remove(df_model.graph.output[0])
            df_model.graph.output.insert(0, df_out_vi)
            df_model_dir = make_build_dir("dataflow_partition_")
            df_model_filename = df_model_dir + "/df_model.onnx"
            df_model.save(df_model_filename)
            # remove all dataflow nodes from the non-dataflow model
            # keep track of where the dataflow part starts
            df_start_ind = all_nodes.index(df_nodes[0])
            for node_to_remove in df_nodes:
                non_df_model.graph.node.remove(node_to_remove)
            # create StreamingDataflow node with df_in/df_out io
            df_node = helper.make_node(
                "StreamingDataflowPartition",
                [df_in],
                [df_out],
                # use the model attribute to mark the df model
                model=df_model_filename,
            )
            non_df_model.graph.node.insert(df_start_ind, df_node)

        return (non_df_model, False)
예제 #6
0
 def get_tensor_sparsity(self, tensor_name):
     """Returns the sparsity of a given tensor as dictionary."""
     graph = self._model_proto.graph
     qnt_annotations = graph.quantization_annotation
     ret = util.get_by_name(qnt_annotations, tensor_name, "tensor_name")
     if ret is not None:
         ret = util.get_by_name(ret.quant_parameter_tensor_names,
                                "tensor_sparsity", "key")
         if ret is not None:
             return eval(ret.value)
     return None
예제 #7
0
 def get_tensor_datatype(self, tensor_name):
     """Returns the FINN DataType of tensor with given name."""
     graph = self._model_proto.graph
     qnt_annotations = graph.quantization_annotation
     ret = util.get_by_name(qnt_annotations, tensor_name, "tensor_name")
     if ret is not None:
         ret = util.get_by_name(ret.quant_parameter_tensor_names,
                                "finn_datatype", "key")
         if ret is not None:
             return DataType[ret.value]
     # TODO maybe use native ONNX tensor type instead of assuming fp32?
     return DataType["FLOAT32"]
예제 #8
0
 def set_tensor_shape(self, tensor_name, tensor_shape, dtype=TensorProto.FLOAT):
     """Assigns shape in ValueInfoProto for tensor with given name."""
     new_vi = oh.make_tensor_value_info(tensor_name, dtype, tensor_shape)
     # find what container tis tensor's ValueInfo lives in
     # if not found anywhere, we assume it's a new value_info
     target_container = self.graph.value_info
     if util.get_by_name(self.graph.input, tensor_name) is not None:
         target_container = self.graph.input
     if util.get_by_name(self.graph.output, tensor_name) is not None:
         target_container = self.graph.output
     # remove from target container and add new
     util.remove_by_name(target_container, tensor_name)
     target_container.append(new_vi)
예제 #9
0
파일: absorb.py 프로젝트: pete-lennart/finn
 def apply(self, model):
     graph = model.graph
     node_ind = 0
     graph_modified = False
     for n in graph.node:
         node_ind += 1
         if n.op_type == "Transpose" and not model.is_fork_node(n):
             perms = list(get_by_name(n.attribute, "perm").ints)
             if perms == [0, 3, 1, 2]:
                 mt_cand = model.find_consumer(n.output[0])
                 if mt_cand.op_type == "MultiThreshold" and not model.is_fork_node(
                     mt_cand
                 ):
                     final_t_cand = model.find_consumer(mt_cand.output[0])
                     if final_t_cand.op_type == "Transpose":
                         perms = list(
                             get_by_name(final_t_cand.attribute, "perm").ints
                         )
                         if perms == [0, 2, 3, 1]:
                             mt = getCustomOp(mt_cand)
                             mt.set_nodeattr("data_layout", "NHWC")
                             # get rid of tranpose nodes, wire MT directly
                             mt_cand.input[0] = n.input[0]
                             mt_cand.output[0] = final_t_cand.output[0]
                             graph.node.remove(n)
                             graph.node.remove(final_t_cand)
                             graph_modified = True
                     else:
                         mt = getCustomOp(mt_cand)
                         mt.set_nodeattr("data_layout", "NHWC")
                         # get rid of first tranpose node
                         mt_cand.input[0] = n.input[0]
                         graph.node.remove(n)
                         # fix output shape for MultiThreshold
                         mt_ishape = model.get_tensor_shape(mt_cand.input[0])
                         model.set_tensor_shape(mt_cand.output[0], mt_ishape)
                         # re-insert Transpose behind MultiThreshold
                         transpose_output = model.make_new_valueinfo_name()
                         new_transpose = oh.make_node(
                             "Transpose",
                             [mt_cand.output[0]],
                             [transpose_output],
                             perm=[0, 3, 1, 2],
                         )
                         graph.node.insert(node_ind + 1, new_transpose)
                         final_t_cand.input[0] = transpose_output
                         graph_modified = True
     if graph_modified:
         model = model.transform(InferDataTypes())
     return (model, graph_modified)
예제 #10
0
 def apply(self, model):
     graph = model.graph
     node_ind = 0
     graph_modified = False
     for n in graph.node:
         node_ind += 1
         if n.op_type == "Transpose" and not model.is_fork_node(n):
             perms = list(get_by_name(n.attribute, "perm").ints)
             if perms == [0, 3, 1, 2]:
                 mt_cand = model.find_consumer(n.output[0])
                 if mt_cand.op_type == "MultiThreshold" and not model.is_fork_node(
                         mt_cand):
                     final_t_cand = model.find_consumer(mt_cand.output[0])
                     if final_t_cand.op_type == "Transpose":
                         perms = list(
                             get_by_name(final_t_cand.attribute,
                                         "perm").ints)
                         if perms == [0, 2, 3, 1]:
                             mt = getCustomOp(mt_cand)
                             mt.set_nodeattr("data_layout", "NHWC")
                             # get rid of tranpose nodes, wire MT directly
                             mt_cand.input[0] = n.input[0]
                             mt_cand.output[0] = final_t_cand.output[0]
                             graph.node.remove(n)
                             graph.node.remove(final_t_cand)
                             graph_modified = True
                     elif final_t_cand.op_type == "Reshape":
                         oshape = model.get_tensor_shape(
                             final_t_cand.output[0])
                         if len(oshape) == 2:
                             # transition to FC part, can still use NHWC
                             mt = getCustomOp(mt_cand)
                             mt.set_nodeattr("data_layout", "NHWC")
                             # get rid of first tranpose node
                             mt_cand.input[0] = n.input[0]
                             # fix output shape for MultiThreshold
                             mt_ishape = model.get_tensor_shape(
                                 mt_cand.input[0])
                             (b, h, w, c) = mt_ishape
                             assert (h == 1
                                     and w == 1), """Untested spatial dim
                             in conv->fc transition, proceed with caution!"""
                             model.set_tensor_shape(mt_cand.output[0],
                                                    mt_ishape)
                             graph.node.remove(n)
                             graph_modified = True
     if graph_modified:
         model = model.transform(InferDataTypes())
     return (model, graph_modified)
예제 #11
0
파일: reorder.py 프로젝트: Xilinx/finn
 def apply(self, model):
     graph = model.graph
     node_ind = 0
     graph_modified = False
     for n in graph.node:
         node_ind += 1
         if n.op_type == "MaxPool":
             consumer = model.find_consumer(n.output[0])
             producer = model.find_producer(n.input[0])
             if consumer is not None and consumer.op_type == "Transpose":
                 perms = list(get_by_name(consumer.attribute, "perm").ints)
                 if perms == [0, 2, 3, 1]:
                     n.op_type = "MaxPoolNHWC"
                     n.domain = "finn.custom_op.general"
                     start_name = n.input[0]
                     mid_name = consumer.input[0]
                     end_name = consumer.output[0]
                     (b, c, hi, wi) = model.get_tensor_shape(start_name)
                     (b, c, ho, wo) = model.get_tensor_shape(mid_name)
                     consumer.input[0] = start_name
                     consumer.output[0] = mid_name
                     n.input[0] = mid_name
                     n.output[0] = end_name
                     model.set_tensor_shape(mid_name, (b, hi, wi, c))
                     model.set_tensor_shape(end_name, (b, ho, wo, c))
                     graph.node.remove(consumer)
                     graph.node.insert(node_ind - 1, consumer)
                     graph_modified = True
             elif producer is not None and producer.op_type == "Transpose":
                 perms = list(get_by_name(producer.attribute, "perm").ints)
                 if perms == [0, 3, 1, 2]:
                     n.op_type = "MaxPoolNHWC"
                     n.domain = "finn.custom_op.general"
                     start_name = producer.input[0]
                     mid_name = n.input[0]
                     end_name = n.output[0]
                     (b, hi, wi, c) = model.get_tensor_shape(start_name)
                     (b, c, ho, wo) = model.get_tensor_shape(end_name)
                     producer.input[0] = mid_name
                     producer.output[0] = end_name
                     n.input[0] = start_name
                     n.output[0] = mid_name
                     model.set_tensor_shape(mid_name, (b, ho, wo, c))
                     model.set_tensor_shape(end_name, (b, c, ho, wo))
                     graph.node.remove(producer)
                     graph.node.insert(node_ind, producer)
                     graph_modified = True
     return (model, graph_modified)
예제 #12
0
def test_change_datalayout_quantavgpool(s, k, ibits, obits, signed, c, idim):
    n = 1
    odim = compute_pool_output_dim(idim, k, s)
    # determine input FINN datatype
    if signed is True:
        prefix = "INT"
    else:
        prefix = "UINT"
    dt_name = prefix + str(ibits)
    dtype = DataType[dt_name]

    inp = helper.make_tensor_value_info("inp", TensorProto.FLOAT,
                                        [n, c, idim, idim])
    outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT,
                                         [n, c, odim, odim])

    node = helper.make_node(
        "QuantAvgPool2d",
        ["inp"],
        ["outp"],
        domain="finn",
        stride=s,
        kernel=k,
        ibits=ibits,
        obits=obits,
        signed=signed,
        data_layout="NCHW",
    )
    graph = helper.make_graph(nodes=[node],
                              name="single-quantavgpool",
                              inputs=[inp],
                              outputs=[outp])

    model = helper.make_model(graph)
    model = ModelWrapper(model)
    model = model.transform(InferShapes())
    model = model.transform(InferDataTypes())
    model = model.transform(InferDataLayouts())
    model = model.transform(GiveUniqueNodeNames())
    model = model.transform(GiveReadableTensorNames())
    model_transformed = model.transform(ChangeDataLayoutQuantAvgPool2d())
    model_transformed = model_transformed.transform(InferShapes())
    model_transformed = model_transformed.transform(InferDataTypes())
    model_transformed = model_transformed.transform(InferDataLayouts())
    model_transformed = model_transformed.transform(GiveUniqueNodeNames())
    model_transformed = model_transformed.transform(GiveReadableTensorNames())
    inp_values = gen_finn_dt_tensor(dtype, [n, c, idim, idim])
    idict = {"inp": inp_values}
    assert oxe.compare_execution(model, model_transformed, idict)
    assert len(model.graph.node) + 2 == len(model_transformed.graph.node)
    assert model_transformed.graph.node[-1].op_type == "Transpose"
    assert model_transformed.graph.node[0].op_type == "Transpose"
    # check if QuantAvgPool2d node has datalayout set correctly
    node = model_transformed.graph.node[1]
    d_layout = get_by_name(node.attribute, "data_layout").s.decode("UTF-8")
    assert d_layout == "NHWC"
    assert model_transformed.get_tensor_layout(
        node.input[0]) == DataLayout.NHWC
    assert model_transformed.get_tensor_layout(
        node.output[0]) == DataLayout.NHWC
예제 #13
0
 def apply(self, model):
     for node in model.graph.node:
         op_type = node.op_type
         if node.domain == "finn":
             backend_attribute = util.get_by_name(node.attribute, "backend")
             if backend_attribute is None:
                 continue
             backend_value = backend_attribute.s.decode("UTF-8")
             if backend_value == "fpgadataflow":
                 try:
                     # lookup op_type in registry of CustomOps
                     inst = registry.custom_op[op_type](node)
                     # ensure that code is generated
                     assert (
                         inst.get_nodeattr("code_gen_dir_ipgen") != ""
                     ), """Node
                     attribute "code_gen_dir_ipgen" is empty. Please run
                     transformation CodeGen_ipgen first."""
                     # call the compilation function for this node
                     inst.ipgen_singlenode_code()
                     # ensure that executable path is now set
                     assert (
                         inst.get_nodeattr("ipgen_path") != ""
                     ), """Transformation
                     HLSSynth_IPGen was not successful. Node attribute "ipgen_path"
                     is empty."""
                 except KeyError:
                     # exception if op_type is not supported
                     raise Exception(
                         "Custom op_type %s is currently not supported." % op_type
                     )
     return (model, False)
예제 #14
0
 def apply(self, model):
     for node in model.graph.node:
         op_type = node.op_type
         if node.domain == "finn":
             backend_attribute = util.get_by_name(node.attribute, "backend")
             if backend_attribute is None:
                 continue
             backend_value = backend_attribute.s.decode("UTF-8")
             if backend_value == "fpgadataflow":
                 try:
                     # lookup op_type in registry of CustomOps
                     inst = registry.custom_op[op_type](node)
                     # find the IP gen dir
                     ipgen_path = inst.get_nodeattr("ipgen_path")
                     if ipgen_path is not None and os.path.isdir(
                             ipgen_path):
                         for dname, dirs, files in os.walk(ipgen_path):
                             for fname in files:
                                 if fname.endswith(".v"):
                                     fpath = os.path.join(dname, fname)
                                     with open(fpath, "r") as f:
                                         s = f.read()
                                     old = '$readmemh(".'
                                     new = '$readmemh("%s' % dname
                                     s = s.replace(old, new)
                                     with open(fpath, "w") as f:
                                         f.write(s)
                 except KeyError:
                     pass
     return (model, False)
예제 #15
0
 def find_prod_mt(x):
     is_mt = x.op_type == "MultiThreshold"
     is_bp = False
     if is_mt:
         dt = get_by_name(x.attribute, "out_dtype").s
         is_bp = dt.decode("utf-8") == "BIPOLAR"
     return is_mt and is_bp
예제 #16
0
def inference_cost_upsample(model, node, discount_sparsity):
    # extract info about the upsampling kernel attributes
    mode = get_by_name(node.attribute, "mode").s.decode("utf-8")
    scales_tensor = node.input[1]
    scales_initializer = model.get_initializer(scales_tensor)

    # extract info from tensor shapes and datatypes
    (i_dtype, scale_dtype, o_dtype) = get_node_tensor_dtypes(model, node)
    (i_shape, scale_shape, o_shape) = get_node_tensor_shapes(model, node)
    bsize = i_shape[0]
    ifm_ch = i_shape[1]
    ofm_pix_total = np.prod(o_shape[2:])

    # MAC calculation
    if mode == "nearest":
        # No calculation involved, since data is just copied over multiple times
        n_macs = 0
    elif mode == "linear":
        # Data gets linearly interpolated in each dimension
        # Two MACs per dimension and output pixel assumed
        n_dim_scaling = np.sum(scales_initializer > 1)
        n_macs = 2 * n_dim_scaling * ofm_pix_total * ifm_ch * bsize
    else:
        raise ValueError(f"Upsampling mode {mode} not supported for estimation.")

    # Mem calculation
    o_mem = np.prod(o_shape)
    idt_name = i_dtype.name
    odt_name = o_dtype.name
    mac_op_type_str = "op_mac_%s_%s" % (idt_name, idt_name)
    o_mem_type_str = "mem_o_%s" % (odt_name)

    ret = {mac_op_type_str: n_macs, o_mem_type_str: o_mem}
    return ret
예제 #17
0
파일: __init__.py 프로젝트: umav1511/finn
 def get_nodeattr(self, name):
     """Get a node attribute by name. Data is stored inside the ONNX node's
     AttributeProto container. Attribute must be part of get_nodeattr_types.
     Default value is returned if attribute is not set."""
     try:
         (dtype, req, def_val) = self.get_nodeattr_types()[name]
         attr = get_by_name(self.onnx_node.attribute, name)
         if attr is not None:
             # dtype indicates which ONNX Attribute member to use
             # (such as i, f, s...)
             ret = attr.__getattribute__(dtype)
             if dtype == "s":
                 # decode string attributes
                 ret = ret.decode("utf-8")
             return ret
         else:
             if req:
                 raise Exception(
                     """Required attribute %s unspecified in
                 a %s node"""
                     % (name, self.onnx_node.op_type)
                 )
             else:
                 # not set, return default value
                 return def_val
     except KeyError:
         raise AttributeError("Op has no such attribute: " + name)
예제 #18
0
    def apply(self, model):
        graph = model.graph
        node_ind = 0
        graph_modified = False
        for n in graph.node:
            node_ind += 1
            if (n.op_type in ["Add", "Sub"] and not model.is_fork_node(n)
                    and not model.is_join_node(n)):
                A = model.get_initializer(n.input[1])
                if (A is not None and np.isclose(
                        A, np.zeros_like(A), atol=self.atol).all()):
                    remove_node_and_rewire(model, n)
                    graph_modified = True
                    break

            elif (n.op_type in ["Mul", "Div"] and not model.is_fork_node(n)
                  and not model.is_join_node(n)):
                A = model.get_initializer(n.input[1])
                if (A is not None and np.isclose(
                        A, np.ones_like(A), atol=self.atol).all()):
                    remove_node_and_rewire(model, n)
                    graph_modified = True
                    break
            elif (n.op_type == "Pad" and not model.is_fork_node(n)
                  and not model.is_join_node(n)):
                pads = get_by_name(n.attribute, "pads")
                pads = np.asarray(pads.ints)
                if (pads == 0).all():
                    remove_node_and_rewire(model, n)
                    graph_modified = True
                    break
        model = model.transform(InferShapes())
        return (model, graph_modified)
예제 #19
0
파일: reorder.py 프로젝트: rbcarlos/finn
    def apply(self, model):
        graph = model.graph
        node_ind = 0
        graph_modified = False
        nodes = [n for n in graph.node]
        for n in nodes:
            node_ind += 1
            if n.op_type == "MaxPool" and not model.is_fork_node(n):
                consumer = model.find_consumer(n.output[0])
                pads = get_by_name(n.attribute, "pads")
                has_padding = False
                if pads is not None:
                    pads = list(pads.ints)
                    has_padding = np.prod(pads) != 0
                if consumer is not None and consumer.op_type == "MultiThreshold":
                    mt_out = consumer.output[0]
                    mt_odt = model.get_tensor_datatype(mt_out)
                    if mt_odt.signed() and has_padding:
                        warnings.warn(
                            "Skipping padded MaxPool + signed-output MultiThreshold"
                        )
                        continue
                    # check for non-decreasing thresholds and nonnegative
                    # scale factor in MultiThreshold
                    # otherwise we cannot do the reordering
                    T = model.get_initializer(consumer.input[1])
                    T_sorted = np.sort(T, axis=1)
                    assert (T == T_sorted).all(
                    ), "MultiThreshold must have non-decreasing thresholds"
                    mt_inst = getCustomOp(consumer)
                    if mt_inst.get_nodeattr("out_scale") < 0:
                        warnings.warn(
                            "Skipping MultiThreshold with negative out_scale")
                        continue

                    # remove old nodes
                    graph.node.remove(n)
                    graph.node.remove(consumer)

                    # swap conections
                    group_in = n.input[0]
                    # new tensor because dims change
                    group_middle = model.make_new_valueinfo_name()
                    group_out = consumer.output[0]

                    consumer.input[0] = group_in
                    consumer.output[0] = group_middle

                    n.input[0] = group_middle
                    n.output[0] = group_out

                    # insert them back in
                    graph.node.insert(node_ind - 1, consumer)
                    graph.node.insert(node_ind, n)

                    graph_modified = True

        model = model.transform(InferShapes())
        return (model, graph_modified)
예제 #20
0
파일: reorder.py 프로젝트: Xilinx/finn
 def apply(self, model):
     graph = model.graph
     node_ind = 0
     graph_modified = False
     for n in graph.node:
         node_ind += 1
         if (
             n.op_type == "Mul"
             and not model.is_fork_node(n)
             and not model.is_join_node(n)
         ):
             consumer = model.find_consumer(n.output[0])
             if (
                 consumer is not None
                 and consumer.op_type == "Conv"
                 and not model.is_join_node(consumer)
             ):
                 mul_weight_name = n.input[1]
                 A = model.get_initializer(mul_weight_name)
                 if A is None:
                     warnings.warn(
                         """Mul weight tensor is not set. If it is a constant,
                             please use set_initializer to set the tensor."""
                     )
                     continue
                 conv_node = consumer
                 mul_node = n
                 start_name = mul_node.input[0]
                 conv_in_name = conv_node.input[0]
                 conv_in_shape = model.get_tensor_shape(conv_in_name)
                 ifm_ch = conv_in_shape[1]
                 group_attribute = get_by_name(consumer.attribute, "group")
                 if group_attribute is None:
                     continue
                 group_attribute = group_attribute.i
                 conv_out_name = conv_node.output[0]
                 conv_out_shape = model.get_tensor_shape(conv_out_name)
                 if A.shape == (1, ifm_ch, 1, 1) and ifm_ch == group_attribute:
                     # if the mul is channelwise and conv is depthwise,
                     # we can simply swap the order of ops
                     # rewire mul input to be conv input
                     conv_node.input[0] = start_name
                     model.set_tensor_shape(start_name, conv_in_shape)
                     model.set_tensor_datatype(start_name, DataType["FLOAT32"])
                     # use old conv input tensor as conv output
                     conv_node.output[0] = conv_in_name
                     model.set_tensor_shape(conv_in_name, conv_out_shape)
                     model.set_tensor_datatype(conv_in_name, DataType["FLOAT32"])
                     # use new conv output as new mul node input
                     mul_node.input[0] = conv_in_name
                     # use old conv output as new mul node output
                     mul_node.output[0] = conv_out_name
                     model.set_tensor_datatype(conv_out_name, DataType["FLOAT32"])
                     # move mul node past conv node
                     graph.node.remove(mul_node)
                     graph.node.insert(node_ind, mul_node)
                     graph_modified = True
     model = model.transform(InferShapes())
     return (model, graph_modified)
예제 #21
0
 def get_metadata_prop(self, key):
     """Returns the value associated with metadata_prop with given key,
     or None otherwise."""
     metadata_prop = util.get_by_name(self.model.metadata_props, key, "key")
     if metadata_prop is None:
         return None
     else:
         return metadata_prop.value
예제 #22
0
 def assign_partition_id(node):
     if node.op_type in [
             "GenericPartition", "StreamingDataflowPartition"
     ]:
         return -1
     else:
         backend = get_by_name(node.attribute, "backend")
         if backend is not None and backend.s.decode(
                 "UTF-8") == "fpgadataflow":
             assigned_partition = get_by_name(node.attribute,
                                              "partition_id")
             if assigned_partition is not None:
                 return assigned_partition.i
             else:
                 return 0
         else:
             return -1
예제 #23
0
파일: absorb.py 프로젝트: pete-lennart/finn
 def apply(self, model):
     graph = model.graph
     graph_modified = False
     node_ind = 0
     for n in graph.node:
         node_ind += 1
         if (
             n.op_type == "Reshape"
             and (model.get_initializer(n.input[1]) == [1, -1]).all()
         ) or n.op_type == "Flatten":
             prod = model.find_producer(n.input[0])
             if (
                 prod is not None
                 and prod.op_type == "Transpose"
                 # we ensure that the first dimension is not changed from the
                 # transpose operation
                 and get_by_name(prod.attribute, "perm").ints[0] == 0
             ):
                 data_layout = model.get_tensor_layout(prod.input[0])
                 # check for the data layout to interpret input shape correctly
                 if data_layout is None:
                     warnings.warn(
                         """Data layout for input tensor of Transpose node is not set.
                             To use AbsorbTransposeIntoFlatten transformation
                             please set tensor data layout."""
                     )
                     continue
                 elif data_layout == DataLayout.NCHW:
                     (b, c, h, w) = model.get_tensor_shape(prod.input[0])
                     # if h=w=1 the transposition can be absorbed, otherwise
                     # the absorption would lead to an error in the behavior
                     if h != 1 or w != 1:
                         continue
                     # the flatten node from onnx keeps by default the first
                     # dim and flattens the rest, that is why this transformation
                     # can only work with b != 1 if the model contains already a
                     # flatten node and not a reshape node with shape = [1, -1].
                     # If the first  dim of the input tensor is not 1, flatten and
                     # reshape (with shape = [1, -1]) would lead to different results
                     if n.op_type == "Reshape" and b != 1:
                         continue
                 elif data_layout == DataLayout.NHWC:
                     (b, h, w, c) = model.get_tensor_shape(prod.input[0])
                     if h != 1 or w != 1:
                         continue
                     if n.op_type == "Reshape" and b != 1:
                         continue
                 # create single flatten node and remove obsolete nodes
                 node = oh.make_node("Flatten", [prod.input[0]], [n.output[0]])
                 graph.node.remove(n)
                 graph.node.remove(prod)
                 graph.node.insert(node_ind, node)
                 graph_modified = True
     if graph_modified:
         model = model.transform(InferDataTypes())
     return (model, graph_modified)
예제 #24
0
 def set_metadata_prop(self, key, value):
     """Sets metadata property with given key to the given value."""
     metadata_prop = util.get_by_name(self.model.metadata_props, key, "key")
     if metadata_prop is None:
         metadata_prop = onnx.StringStringEntryProto()
         metadata_prop.key = key
         metadata_prop.value = value
         self.model.metadata_props.append(metadata_prop)
     else:
         metadata_prop.value = value
예제 #25
0
 def apply(self, model):
     for node in model.graph.node:
         if node.domain == "finn":
             backend_attribute = get_by_name(node.attribute, "backend")
             if backend_attribute is None:
                 continue
             backend_value = backend_attribute.s.decode("UTF-8")
             if backend_value == "fpgadataflow":
                 _codegen_single_node(node, model, self.fpgapart, self.clk)
     return (model, False)
예제 #26
0
파일: reorder.py 프로젝트: Xilinx/finn
    def apply(self, model):
        graph = model.graph
        node_ind = 0
        graph_modified = False
        for n in graph.node:
            node_ind += 1
            if n.op_type == "Flatten":
                consumer = model.find_consumer(n.output[0])
                if consumer is not None and consumer.op_type == "TopK":
                    axis = get_by_name(consumer.attribute, "axis")
                    if axis is None or axis.i != -1:
                        continue
                    start_name = n.input[0]
                    data_layout = model.get_tensor_layout(start_name)
                    if data_layout != DataLayout.NHWC:
                        warnings.warn(
                            """Transformation can't be applied. The input
                            to flatten has to have DataLayout.NHWC"""
                        )
                        continue
                    (b, h, w, c) = model.get_tensor_shape(start_name)
                    if h != 1 or w != 1:
                        continue
                    # get parameter k from topk
                    k = model.get_tensor_shape(consumer.output[1])[-1]

                    # swap conections
                    # new tensor because dims change
                    middle_name = model.make_new_valueinfo_name()
                    topk_indices = oh.make_tensor_value_info(
                        middle_name, TensorProto.INT64, [b, h, w, k]
                    )
                    end_name = consumer.output[1]
                    graph.value_info.append(topk_indices)

                    # remove old nodes
                    graph.node.remove(n)
                    graph.node.remove(consumer)

                    # set inputs and outputs correctly
                    consumer.input[0] = start_name
                    consumer.output[1] = middle_name
                    model.set_tensor_shape(consumer.output[0], (b, h, w, k))

                    n.input[0] = middle_name
                    n.output[0] = end_name

                    # insert them back in
                    graph.node.insert(node_ind - 1, consumer)
                    graph.node.insert(node_ind, n)

                    graph_modified = True

        model = model.transform(InferShapes())
        return (model, graph_modified)
예제 #27
0
def inference_cost_matmul(model, node, discount_sparsity):
    # extract info from tensor shapes and datatypes
    (i_dtype, w_dtype, o_dtype) = get_node_tensor_dtypes(model, node)
    (i_shape, w_shape, o_shape) = get_node_tensor_shapes(model, node)
    if node.op_type == "Gemm":
        assert len(i_shape) == 2 and len(w_shape) == 2
        tA = get_by_name(node.attribute, "transA")
        tB = get_by_name(node.attribute, "transB")
        if tA is not None and tA.i == 1:
            i_shape = i_shape[::-1]
        if tB is not None and tB.i == 1:
            w_shape = w_shape[::-1]
    # exclude common dim (last axis) from one side to avoid duplication
    n_macs = np.prod(i_shape[:-1]) * np.prod(w_shape)
    # deal with both dyn,param and dyn,dyn cases for weight memory
    inp0_is_const = model.get_initializer(node.input[0]) is not None
    inp1_is_const = model.get_initializer(node.input[1]) is not None
    if inp0_is_const and (not inp1_is_const):
        # inp 0 is static
        w_mem = np.prod(i_shape)
        wname = node.input[0]
    elif (not inp0_is_const) and inp1_is_const:
        # inp 1 is static
        w_mem = np.prod(w_shape)
        wname = node.input[1]
    elif (not inp0_is_const) and (not inp1_is_const):
        # both inputs dynamic
        w_mem = 0
        wname = None
    if discount_sparsity and wname is not None:
        density = get_node_weight_density(model, wname)
        n_macs *= density
        w_mem *= density
    o_mem = np.prod(o_shape)
    idt_name = i_dtype.name
    wdt_name = w_dtype.name
    odt_name = o_dtype.name
    mac_op_type_str = "op_mac_%s_%s" % (idt_name, wdt_name)
    w_mem_type_str = "mem_w_%s" % (wdt_name)
    o_mem_type_str = "mem_o_%s" % (odt_name)
    ret = {mac_op_type_str: n_macs, w_mem_type_str: w_mem, o_mem_type_str: o_mem}
    return ret
예제 #28
0
 def get_tensor_layout(self, tensor_name):
     """Returns the data layout annotation of tensor with given name.
     The data layout is expressed as a list of strings with as many
     elements as the number of dimensions in the tensor shape. Each
     string annotates what is contained in that dimension. If there is no
     data layout annotation, None will be returned.
     Examples of data layout annotations:
     ["N", "C"] is tensor[batch][channel]
     ["N", "C", "H", "W"] is tensor[batch][channel][height][width]
     ["N", "H", "W", "C"] is tensor[batch][height][width][channel]
     """
     graph = self._model_proto.graph
     qnt_annotations = graph.quantization_annotation
     ret = util.get_by_name(qnt_annotations, tensor_name, "tensor_name")
     if ret is not None:
         ret = util.get_by_name(ret.quant_parameter_tensor_names,
                                "tensor_layout", "key")
         if ret is not None:
             return eval(ret.value)
     return None
예제 #29
0
def is_fpgadataflow_node(node):
    """Returns True if given node is fpgadataflow node. Otherwise False."""
    is_node = False
    if node is not None:
        if node.domain == "finn":
            n_backend = get_by_name(node.attribute, "backend")
            if n_backend is not None:
                backend_value = n_backend.s.decode("UTF-8")
                if backend_value == "fpgadataflow":
                    is_node = True

    return is_node
예제 #30
0
def test_code_gen_trafo():
    idt = wdt = odt = DataType.BIPOLAR
    mw = 8
    mh = 8
    pe = 4
    simd = 4

    inp = helper.make_tensor_value_info("inp", TensorProto.FLOAT, [1, mw])
    outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT, [1, mh])
    node_inp_list = ["inp", "weights", "thresh"]
    FCLayer_node = helper.make_node(
        "StreamingFCLayer_Batch",
        node_inp_list,
        ["outp"],
        domain="finn",
        backend="fpgadataflow",
        code_gen_dir="",
        executable_path="",
        resType="ap_resource_lut()",
        MW=mw,
        MH=mh,
        SIMD=simd,
        PE=pe,
        inputDataType=idt.name,
        weightDataType=wdt.name,
        outputDataType=odt.name,
        noActivation=1,
    )
    graph = helper.make_graph(nodes=[FCLayer_node],
                              name="fclayer_graph",
                              inputs=[inp],
                              outputs=[outp])

    model = helper.make_model(graph, producer_name="fclayer-model")
    model = ModelWrapper(model)

    model.set_tensor_datatype("inp", idt)
    model.set_tensor_datatype("outp", odt)
    model.set_tensor_datatype("weights", wdt)
    W = util.gen_finn_dt_tensor(wdt, (mw, mh))
    model.set_initializer("weights", W)

    model = model.transform(CodeGen_npysim())
    for node in model.graph.node:
        code_gen_attribute = util.get_by_name(node.attribute,
                                              "code_gen_dir_npysim")
        tmp_dir = code_gen_attribute.s.decode("UTF-8")
        assert os.path.isdir(
            tmp_dir), """Code generation directory of node with
            op type {} does not exist!""".format(node.op_type)
        assert (len(os.listdir(tmp_dir)) !=
                0), """Code generation directory of node with
            op type {} is empty!""".format(node.op_type)