diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..9087804 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,31 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: | + python setup.py sdist + twine upload dist/* diff --git a/AUTHORS.rst b/AUTHORS.rst index 4832a4c..14346eb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,10 +2,12 @@ Contributors ============ -* Yaman Umuroglu (maintainer) -* Sambhav Jain (maintainer) +* Yaman Umuroglu (@maltanar) (maintainer) +* Sambhav Jain (@sjain-stanford) * Jakoba Petri-Koenig (@auphelia) * Lucian Petrica (@quetric) * Tobias Alonso (@Tobi-Alonso) * Hendrik Borras (@HenniOVP) * Mirza Mrahorovic (@mmrahorovic) +* Felix Paul Jentzsch (@felixpj) +* Jon Ander Lezeta (@jalezeta) diff --git a/src/finn/analysis/inference_cost.py b/src/finn/analysis/inference_cost.py index 85c1765..1dc6a17 100644 --- a/src/finn/analysis/inference_cost.py +++ b/src/finn/analysis/inference_cost.py @@ -165,6 +165,42 @@ def inference_cost_matmul(model, node, discount_sparsity): return ret +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 + + def inference_cost(model, discount_sparsity=True): "Ensure all nodes have unique names prior to calling this analysis pass." @@ -193,6 +229,7 @@ def inference_cost(model, discount_sparsity=True): "Conv": inference_cost_conv, "MatMul": inference_cost_matmul, "Gemm": inference_cost_matmul, + "Upsample": inference_cost_upsample, } for node in model.graph.node: if node.op_type in inference_cost_fxn_map.keys(): diff --git a/src/finn/util/basic.py b/src/finn/util/basic.py index 3781470..cacb5d4 100644 --- a/src/finn/util/basic.py +++ b/src/finn/util/basic.py @@ -44,6 +44,7 @@ pynq_part_map["Pynq-Z2"] = "xc7z020clg400-1" pynq_part_map["ZCU102"] = "xczu9eg-ffvb1156-2-e" pynq_part_map["ZCU104"] = "xczu7ev-ffvc1156-2-e" +pynq_part_map["ZCU111"] = "xczu28dr-ffvg1517-2-e" # native AXI HP port width (in bits) for PYNQ boards pynq_native_port_width = dict() @@ -52,6 +53,7 @@ pynq_native_port_width["Ultra96"] = 128 pynq_native_port_width["ZCU102"] = 128 pynq_native_port_width["ZCU104"] = 128 +pynq_native_port_width["ZCU111"] = 128 # Alveo device and platform mappings alveo_part_map = dict() diff --git a/src/finn/util/inference_cost.py b/src/finn/util/inference_cost.py index 7c3628b..0ebcec9 100644 --- a/src/finn/util/inference_cost.py +++ b/src/finn/util/inference_cost.py @@ -68,20 +68,20 @@ def compute_mem_bits(inf_cost_dict, filter_string="mem_w"): def inference_cost( model_filename, *, - output_json="inference_cost.json", + output_json=None, + output_onnx=None, preprocess=True, - save_final=True, discount_sparsity=True ): """Print the inference cost estimate metric for given ONNX model. Supports the Quant op for weight/activation quantization. :param model_filename: Filename for ONNX model - :param output_json: Filename for output JSON with detailed inference cost dict + :param output_json: Optional JSON filename to save the inference cost dict + :param output_onnx: Optional ONNX filename to save the final model after any + preprocessing :param preprocess: If set, run preprocessing steps such as shape inference, datatype inference and constant folding. Strongly recommended. - :param save_final: If set, save the final ONNX model after any preprocessing - as final.onnx :param discount_sparsity: If set, will discount op cost of MAC ops with a constant zero weight, and the mem cost of constant zero weights. """ @@ -100,8 +100,8 @@ def inference_cost( model = model.transform(InferDataTypes()) model = model.transform(GiveUniqueNodeNames()) model = model.transform(GiveReadableTensorNames()) - if save_final: - model.save("final.onnx") + if output_onnx is not None: + model.save(output_onnx) ret = model.analysis(lambda x: infca.inference_cost(x, discount_sparsity)) bops = compute_bops(ret) mem_w_bits = compute_mem_bits(ret, "mem_w") @@ -114,6 +114,10 @@ def inference_cost( ret["unsupported"] = str(ret["unsupported"]) print(json.dumps(ret, sort_keys=True, indent=2)) + if output_json is not None: + with open(output_json, "w") as f: + json.dump(ret, f, sort_keys=True, indent=2) + def main(): clize.run(inference_cost)