Beispiel #1
0
 def test_add_pre_and_postproc(self, topology, wbits, abits):
     prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "import_and_tidy")
     model = load_test_checkpoint_or_skip(prev_chkpt_name)
     global_inp_name = model.graph.input[0].name
     ishape = model.get_tensor_shape(global_inp_name)
     # preprocessing: torchvision's ToTensor divides uint8 inputs by 255
     totensor_pyt = ToTensor()
     chkpt_preproc_name = get_checkpoint_name(topology, wbits, abits, "preproc")
     bo.export_finn_onnx(totensor_pyt, ishape, chkpt_preproc_name)
     assert os.path.isfile(chkpt_preproc_name)
     # join preprocessing and core model
     pre_model = ModelWrapper(chkpt_preproc_name)
     model = model.transform(MergeONNXModels(pre_model))
     # add input quantization annotation: UINT8 for all BNN-PYNQ models
     global_inp_name = model.graph.input[0].name
     model.set_tensor_datatype(global_inp_name, DataType.UINT8)
     # postprocessing: insert Top-1 node at the end
     model = model.transform(InsertTopK(k=1))
     chkpt_name = get_checkpoint_name(topology, wbits, abits, "pre_post")
     # tidy-up again
     model = model.transform(InferShapes())
     model = model.transform(FoldConstants())
     model = model.transform(GiveUniqueNodeNames())
     model = model.transform(GiveReadableTensorNames())
     model = model.transform(InferDataTypes())
     model = model.transform(RemoveStaticGraphInputs())
     model.save(chkpt_name)
     assert os.path.isfile(chkpt_name)
Beispiel #2
0
def pre_processing(model):
    log("Starting Pre Processing")
    global_inp_name = model.graph.input[0].name
    ishape = model.get_tensor_shape(global_inp_name)
    # preprocessing: torchvision's ToTensor divides uint8 inputs by 255
    totensor_pyt = ToTensor()
    chkpt_preproc_name = build_dir + "tfc_preproc.onnx"
    bo.export_finn_onnx(totensor_pyt, ishape, chkpt_preproc_name)
    # join preprocessing and core model
    pre_model = ModelWrapper(chkpt_preproc_name)
    model = model.transform(MergeONNXModels(pre_model))
    # add input quantization annotation: UINT8 for all BNN-PYNQ models
    global_inp_name = model.graph.input[0].name
    model.set_tensor_datatype(global_inp_name, DataType.UINT8)
    log("Finished Pre Processing!")
    save(model, "1_with_preproc")
    return model
def test_end2end_mobilenet_tidy_and_merge_with_preproc():
    preproc_model = load_test_checkpoint_or_skip(
        build_dir + "/end2end_mobilenet_preproc.onnx")
    model = load_test_checkpoint_or_skip(build_dir +
                                         "/end2end_mobilenet_export.onnx")
    model = model.transform(InferShapes())
    model = model.transform(FoldConstants())
    model = model.transform(InsertTopK())
    # get initializer from Mul that will be absorbed into topk
    a0 = model.get_initializer(model.graph.node[-2].input[1])
    np.save(build_dir + "/end2end_mobilenet_topk_scale.npy", a0)
    model = model.transform(absorb.AbsorbScalarMulAddIntoTopK())
    model = model.transform(InferShapes())
    model = model.transform(InferDataTypes())
    model = model.transform(InferDataLayouts())
    model = model.transform(GiveUniqueNodeNames())
    model = model.transform(GiveUniqueParameterTensors())
    model = model.transform(GiveReadableTensorNames())
    model = model.transform(MergeONNXModels(preproc_model))
    model.save(build_dir + "/end2end_mobilenet_tidy.onnx")
def test_merge_onnx_models():
    # load pre model
    raw_m = get_data("finn", "data/onnx/mnist-conv/model.onnx")
    model1 = ModelWrapper(raw_m)
    # the input for model1 comes from a uint8 vector so we set the finn datatype
    # of the input tensor to DataType.UINT8 to verify that the datatypes are correctly
    # preserved in the transformed model
    model1.set_tensor_datatype(model1.graph.input[0].name, DataType.UINT8)
    model1 = model1.transform(InferShapes())
    model1 = model1.transform(GiveUniqueNodeNames())
    model1 = model1.transform(GiveReadableTensorNames())

    # set up post model
    shape = [1, 10]
    inp = helper.make_tensor_value_info("inp", TensorProto.FLOAT, shape)
    a0 = helper.make_tensor_value_info("a0", TensorProto.FLOAT, [])
    a1 = helper.make_tensor_value_info("a1", TensorProto.FLOAT, [])
    outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT, shape)

    mul_node = helper.make_node("Mul", ["inp", "a0"], ["mul_out"])
    div_node = helper.make_node("Div", ["mul_out", "a1"], ["outp"])

    graph = helper.make_graph(
        nodes=[mul_node, div_node],
        name="model2-graph",
        inputs=[inp],
        outputs=[outp],
        value_info=[a0, a1],
    )

    model2 = helper.make_model(graph, producer_name="model2")
    model2 = ModelWrapper(model2)
    # initialize model2
    a0_value = np.random.uniform(low=0, high=1, size=(1)).astype(np.float32)
    model2.set_initializer("a0", a0_value)
    a1_value = np.random.uniform(low=0.1, high=1, size=(1)).astype(np.float32)
    model2.set_initializer("a1", a1_value)
    # set a dummy sparsity annotation to check if it gets correctly transferred
    # to the merged model
    sparsity = {"dw": {"kernel_shape": 0}}
    model2.set_tensor_sparsity("a1", sparsity)
    model2 = model2.transform(InferShapes())
    model2 = model2.transform(InferDataTypes())
    model2 = model2.transform(InferDataLayouts())
    model2 = model2.transform(GiveUniqueNodeNames())
    model2 = model2.transform(GiveReadableTensorNames())

    # simulate the models before the merging and pass the output of model1 to model2
    # load one of the test vectors
    raw_i = get_data("finn", "data/onnx/mnist-conv/test_data_set_0/input_0.pb")
    inp_values = onnx.load_tensor_from_string(raw_i)
    inp_values = np_helper.to_array(inp_values)
    idict = {model1.graph.input[0].name: inp_values}
    odict = oxe.execute_onnx(model1, idict)
    temp = odict[model1.graph.output[0].name]

    idict = {model2.graph.input[0].name: temp}
    odict = oxe.execute_onnx(model2, idict)
    outp = odict[model2.graph.output[0].name]
    # merge models
    model_transformed = model2.transform(MergeONNXModels(model1))

    idict = {model_transformed.graph.input[0].name: inp_values}
    odict = oxe.execute_onnx(model_transformed, idict)
    outp_transformed = odict[model_transformed.graph.output[0].name]

    assert (outp == outp_transformed).all()
    assert len(model_transformed.graph.node) == len(model1.graph.node) + len(
        model2.graph.node
    )
    # to test if the value is preserved we set the sparsity annotation of input[1]
    # of the division block to a dummy value, we can now look for the division block
    # and check if the sparsity annotation is still the same
    for n in model_transformed.graph.node:
        if n.op_type == "Div":
            tensor_name = n.input[1]
            set_sparsity = model_transformed.get_tensor_sparsity(tensor_name)
            assert sparsity == set_sparsity

    # check if finn datatype of graph.input[0] is still set to UINT8
    assert model_transformed.get_tensor_datatype("global_in") == DataType.UINT8
Beispiel #5
0
def test_brevitas_compare_exported_mobilenet():
    if "IMAGENET_VAL_PATH" not in os.environ.keys():
        pytest.skip("Can't do validation without IMAGENET_VAL_PATH")
    n_images = 10
    debug_mode = False
    export_onnx_path = make_build_dir("test_brevitas_mobilenet-v1_")
    # export preprocessing
    preproc_onnx = export_onnx_path + "/quant_mobilenet_v1_4b_preproc.onnx"
    preproc = NormalizePreProc(mean, std, ch)
    bo.export_finn_onnx(preproc, (1, 3, 224, 224), preproc_onnx)
    preproc_model = ModelWrapper(preproc_onnx)
    preproc_model = preproc_model.transform(InferShapes())
    preproc_model = preproc_model.transform(GiveUniqueNodeNames())
    preproc_model = preproc_model.transform(GiveUniqueParameterTensors())
    preproc_model = preproc_model.transform(GiveReadableTensorNames())
    # export the actual MobileNet-v1
    finn_onnx = export_onnx_path + "/quant_mobilenet_v1_4b.onnx"
    mobilenet = get_test_model_trained("mobilenet", 4, 4)
    if debug_mode:
        dbg_hook = bo.enable_debug(mobilenet)
    bo.export_finn_onnx(mobilenet, (1, 3, 224, 224), finn_onnx)
    model = ModelWrapper(finn_onnx)
    model = model.transform(InferShapes())
    model = model.transform(FoldConstants())
    model = model.transform(RemoveStaticGraphInputs())
    model = model.transform(InsertTopK())
    # get initializer from Mul that will be absorbed into topk

    a0 = model.get_initializer(model.get_nodes_by_op_type("Mul")[-1].input[1])
    model = model.transform(absorb.AbsorbScalarMulAddIntoTopK())
    model = model.transform(InferShapes())
    model = model.transform(InferDataTypes())
    model = model.transform(InferDataLayouts())
    model = model.transform(GiveUniqueNodeNames())
    model = model.transform(GiveUniqueParameterTensors())
    model = model.transform(GiveReadableTensorNames())
    model.save(export_onnx_path + "/quant_mobilenet_v1_4b_wo_preproc.onnx")
    # create merged preprocessing + MobileNet-v1 model
    model = model.transform(MergeONNXModels(preproc_model))
    model.save(export_onnx_path + "/quant_mobilenet_v1_4b.onnx")

    with open(
        export_onnx_path + "/mobilenet_validation.csv", "w", newline=""
    ) as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(
            [
                "goldenID",
                "brevitasTop5",
                "brevitasTop5[%]",
                "finnTop5",
                "finnTop5[%]",
                "top5equal",
                "top5%equal",
            ]
        )
        csvfile.flush()
        workload = imagenet_util.get_val_images(n_images, interleave_classes=True)
        all_inds_ok = True
        all_probs_ok = True
        for (img_path, target_id) in workload:
            img_np = imagenet_util.load_resize_crop(img_path)
            img_torch = torch.from_numpy(img_np).float()
            # do forward pass in PyTorch/Brevitas
            input_tensor = preproc.forward(img_torch)
            expected = mobilenet.forward(input_tensor).detach().numpy()
            expected_topk = expected.flatten()
            expected_top5 = np.argsort(expected_topk)[-5:]
            expected_top5 = np.flip(expected_top5)
            expected_top5_prob = []
            for index in expected_top5:
                expected_top5_prob.append(expected_topk[index])
            idict = {model.graph.input[0].name: img_np}
            odict = oxe.execute_onnx(model, idict, return_full_exec_context=True)
            produced = odict[model.graph.output[0].name]
            produced_prob = odict["TopK_0_out0"] * a0
            inds_ok = (produced.flatten() == expected_top5).all()
            probs_ok = np.isclose(produced_prob.flatten(), expected_top5_prob).all()
            all_inds_ok = all_inds_ok and inds_ok
            all_probs_ok = all_probs_ok and probs_ok
            writer.writerow(
                [
                    str(target_id),
                    str(expected_top5),
                    str(expected_top5_prob),
                    str(produced.flatten()),
                    str(produced_prob.flatten()),
                    str(inds_ok),
                    str(probs_ok),
                ]
            )
            csvfile.flush()
            if ((not inds_ok) or (not probs_ok)) and debug_mode:
                print("Results differ for %s" % img_path)
                # check all tensors at debug markers
                names_brevitas = set(dbg_hook.values.keys())
                names_finn = set(odict.keys())
                names_common = names_brevitas.intersection(names_finn)
                for dbg_name in names_common:
                    if not np.isclose(
                        dbg_hook.values[dbg_name].detach().numpy(),
                        odict[dbg_name],
                        atol=1e-3,
                    ).all():
                        print("Tensor %s differs between Brevitas and FINN" % dbg_name)
        assert all_inds_ok and all_probs_ok
def test_brevitas_mobilenet():
    # get single image as input and prepare image
    img = Image.open("/workspace/finn/tests/brevitas/king_charles.jpg")
    # resize smallest side of the image to 256 pixels and resize larger side
    # with same ratio
    img = resize_smaller_side(256, img)
    # crop central 224*224 window
    img = crop_center(224, img)
    # save image as numpy array and as torch tensor to enable testing in
    # brevitas/pytorch and finn and transpose from (H, W, C) to (C, H, W)
    img_np = np.asarray(img).copy().astype(np.float32).transpose(2, 0, 1)
    img_np = img_np.reshape(1, 3, 224, 224)
    img_torch = torch.from_numpy(img_np).float()

    # export preprocess
    export_onnx_path = make_build_dir("test_brevitas_mobilenet-v1_")
    preproc_onnx = export_onnx_path + "/quant_mobilenet_v1_4b_preproc.onnx"
    mean = [0.485, 0.456, 0.406]
    std = 0.226
    ch = 3
    preproc = NormalizePreProc(mean, std, ch)
    bo.export_finn_onnx(preproc, (1, 3, 224, 224), preproc_onnx)
    preproc_model = ModelWrapper(preproc_onnx)
    # set input finn datatype to UINT8
    preproc_model.set_tensor_datatype(preproc_model.graph.input[0].name, DataType.UINT8)
    preproc_model = preproc_model.transform(InferShapes())
    preproc_model = preproc_model.transform(GiveUniqueNodeNames())
    preproc_model = preproc_model.transform(GiveUniqueParameterTensors())
    preproc_model = preproc_model.transform(GiveReadableTensorNames())

    finn_onnx = export_onnx_path + "/quant_mobilenet_v1_4b_exported.onnx"
    mobilenet = get_test_model_trained("mobilenet", 4, 4)
    bo.export_finn_onnx(mobilenet, (1, 3, 224, 224), finn_onnx)

    # do forward pass in PyTorch/Brevitas
    input_tensor = preproc.forward(img_torch)
    expected = mobilenet.forward(input_tensor).detach().numpy()
    expected_topk = expected.flatten()
    expected_top5 = np.argsort(expected_topk)[-5:]
    expected_top5 = np.flip(expected_top5)
    expected_top5_prob = []
    for index in expected_top5:
        expected_top5_prob.append(expected_topk[index])
    model = ModelWrapper(finn_onnx)
    model = model.transform(InferShapes())
    model = model.transform(FoldConstants())
    model = model.transform(InsertTopK())
    # get initializer from Mul that will be absorbed into topk
    a0 = model.get_initializer(model.graph.node[-2].input[1])
    model = model.transform(absorb.AbsorbScalarMulAddIntoTopK())
    model = model.transform(InferShapes())
    model = model.transform(InferDataTypes())
    model = model.transform(InferDataLayouts())
    model = model.transform(GiveUniqueNodeNames())
    model = model.transform(GiveUniqueParameterTensors())
    model = model.transform(GiveReadableTensorNames())
    model.save(export_onnx_path + "/quant_mobilenet_v1_4b_wo_preproc.onnx")
    model = model.transform(MergeONNXModels(preproc_model))
    model.save(export_onnx_path + "/quant_mobilenet_v1_4b.onnx")
    idict = {model.graph.input[0].name: img_np}
    odict = oxe.execute_onnx(model, idict, True)
    produced = odict[model.graph.output[0].name]
    produced_prob = odict["TopK_0_out0"] * a0
    assert (produced.flatten() == expected_top5).all()
    assert np.isclose(produced_prob.flatten(), expected_top5_prob).all()