From 3b1578e44b4c6a8c8c9a8e0891d3866a89bd66af Mon Sep 17 00:00:00 2001 From: Tim Hall Date: Fri, 13 Jan 2023 17:57:25 +0000 Subject: MLBEDSW-7151: MLCE: Difference in model output between x86 & aarch64 - The issue is due to undefined behaviour when casting a NumPy float to a NumPy unsigned integer which occurs in create_const_tensor() - The fix is to make sure that the values are first cast to a Python float - In addition, the values datatype argument has been removed from create_const_tensor() to stop the tensor and values datatypes getting out of sync Change-Id: I134b9be8c941b361929a5ae7db8cb35f2e9728f2 Signed-off-by: Tim Hall --- ethosu/vela/tensor.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'ethosu/vela/tensor.py') diff --git a/ethosu/vela/tensor.py b/ethosu/vela/tensor.py index 899b1bed..6a95bad4 100644 --- a/ethosu/vela/tensor.py +++ b/ethosu/vela/tensor.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2020-2022 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2020-2023 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 # @@ -300,17 +300,31 @@ class QuantizationParameters: def create_const_tensor( name: str, shape: Shape, - dtype: DataType, - values: np.ndarray, - value_dtype: np.dtype = None, + dtype: DataType, # datatype of the tensor + values: Optional[Union[np.ndarray, list]], # list-like data of some type, or scalar (skip mypy), or None purpose: TensorPurpose = TensorPurpose.Unknown, - quantization: QuantizationParameters = None, + quantization: Optional[QuantizationParameters] = None, ): + assert isinstance(dtype, DataType) + # Tensor const_tensor = Tensor(shape, dtype, name + "_0") const_tensor.purpose = purpose const_tensor.quantization = quantization - const_tensor.values = np.array(values, dtype=value_dtype) + + # if the tensor datatype does not match that of the values then np.array() will perform a cast operation. this can + # result in undefined behaviour if casting from a numpy float to a numpy unsigned integer. therefore, we need to + # avoid this undefined behaviour by converting the numpy floats to python floats as these give the desired behaviour + # when casting to unsigned integers + if ( + values is not None + and shape != [] # values are not a scalar + and isinstance(values[0], np.floating) + and dtype.type == BaseType.Unsigned + ): + values = [float(v) for v in values] + + const_tensor.values = np.array(values, dtype=dtype.as_numpy_type()) # Operator const_op = Operation(Op.Const, name) const_op.set_output_tensor(const_tensor) -- cgit v1.2.1