From 2ade533b7ffefc516573282d4b5776ad54ac8234 Mon Sep 17 00:00:00 2001 From: Henry Shi Date: Sun, 20 Feb 2022 23:39:38 +0800 Subject: [PATCH] dynamic shape feed mode --- .../common/session/anf_runtime_algorithm.cc | 20 ++-- .../kernel/tbe/tbe_dynaminc_shape_util.cc | 6 ++ .../ascend/kernel/tbe/tbe_kernel_build.cc | 8 +- .../actor/data_prepare_actor.cc | 1 + .../tbe_compiler/tbe_helper.py | 5 + mindspore/python/mindspore/common/tensor.py | 24 +++-- mindspore/python/mindspore/nn/cell.py | 94 ++++++++++++++++++- mindspore/python/mindspore/train/model.py | 2 + 8 files changed, 138 insertions(+), 22 deletions(-) diff --git a/mindspore/ccsrc/backend/common/session/anf_runtime_algorithm.cc b/mindspore/ccsrc/backend/common/session/anf_runtime_algorithm.cc index 87854dc868a..1397f3a3922 100644 --- a/mindspore/ccsrc/backend/common/session/anf_runtime_algorithm.cc +++ b/mindspore/ccsrc/backend/common/session/anf_runtime_algorithm.cc @@ -927,15 +927,19 @@ bool AnfRuntimeAlgorithm::IsIndependentNode(const CNodePtr &node) { } static inline void GetMaxOrDefaultShape(const std::vector &max_shape, std::vector *device_shape) { + constexpr size_t kDefaultValueForDynamicDim = 16; + auto ConvertNegOneToDefault = [&kDefaultValueForDynamicDim](size_t size) { + return static_cast(size) < 0 ? kDefaultValueForDynamicDim : size; + }; if (!max_shape.empty()) { - (void)std::transform(max_shape.begin(), max_shape.end(), device_shape->begin(), IntToSize); + if (device_shape->empty()) { + std::transform(max_shape.begin(), max_shape.end(), std::back_inserter(*device_shape), ConvertNegOneToDefault); + } else { + std::transform(max_shape.begin(), max_shape.end(), device_shape->begin(), IntToSize); + } } else { - constexpr size_t kDefaultValueForDynamicDim = 16; auto tmp_shape = *device_shape; - auto ConvertNegOneToDefalut = [&kDefaultValueForDynamicDim](size_t size) { - return static_cast(size) < 0 ? kDefaultValueForDynamicDim : size; - }; - (void)std::transform(tmp_shape.begin(), tmp_shape.end(), device_shape->begin(), ConvertNegOneToDefalut); + (void)std::transform(tmp_shape.begin(), tmp_shape.end(), device_shape->begin(), ConvertNegOneToDefault); } } @@ -948,7 +952,7 @@ static inline void GetMaxOrDefaultShape(const std::vector &max_shape, s std::vector AnfRuntimeAlgorithm::GetInputDeviceShapeAdaptively(const AnfNodePtr &anf_node, size_t index) { auto device_shape = GetInputDeviceShape(anf_node, index); // Initialize GPUKernel with max shape to fit 'InitDynamicOutputKernelRef()' for memory reuse. - if (AnfUtils::IsShapeDynamic(device_shape)) { + if (AnfUtils::IsShapeDynamic(device_shape) || device_shape.empty()) { auto max_shape = common::AnfAlgo::GetInputMaxShape(anf_node, index); GetMaxOrDefaultShape(max_shape, &device_shape); auto format = GetInputFormat(anf_node, index); @@ -962,7 +966,7 @@ std::vector AnfRuntimeAlgorithm::GetInputDeviceShapeAdaptively(const Anf std::vector AnfRuntimeAlgorithm::GetOutputDeviceShapeAdaptively(const AnfNodePtr &anf_node, size_t index) { auto device_shape = GetOutputDeviceShape(anf_node, index); // Initialize GPUKernel with max shape to fit 'InitDynamicOutputKernelRef()' for memory reuse. - if (AnfUtils::IsShapeDynamic(device_shape)) { + if (AnfUtils::IsShapeDynamic(device_shape) || device_shape.empty()) { auto max_shape = common::AnfAlgo::GetOutputMaxShape(anf_node, index); GetMaxOrDefaultShape(max_shape, &device_shape); auto format = GetOutputFormat(anf_node, index); diff --git a/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_dynaminc_shape_util.cc b/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_dynaminc_shape_util.cc index 5bf394b917c..845b10359e8 100644 --- a/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_dynaminc_shape_util.cc +++ b/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_dynaminc_shape_util.cc @@ -81,6 +81,9 @@ RangePair TbeDynamicShapeUtil::GetInputDynamicRange(const AnfNodePtr &anf_node, } RangePair ret; for (size_t i = 0; i < input_range_min.size(); ++i) { + if (input_range_min[i] < 0) { + input_range_min[i] = 1; + } ret.emplace_back(input_range_min[i], input_range_max[i]); } return shapeRangeTransfer.GetRealRange(ret, format, data_type); @@ -108,6 +111,9 @@ RangePair TbeDynamicShapeUtil::GetOutputDynamicRange(const AnfNodePtr &anf_node, } RangePair ret; for (size_t i = 0; i < output_range_min.size(); ++i) { + if (output_range_min[i] < 0) { + output_range_min[i] = 1; + } ret.emplace_back(output_range_min[i], output_range_max[i]); } return shapeRangeTransfer.GetRealRange(ret, format, data_type); diff --git a/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_kernel_build.cc b/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_kernel_build.cc index 7731dc2cd1a..efc149b830c 100644 --- a/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_kernel_build.cc +++ b/mindspore/ccsrc/plugin/device/ascend/kernel/tbe/tbe_kernel_build.cc @@ -42,8 +42,8 @@ void GetRealInputSize(const nlohmann::json &input_json, std::vector *inp if (j >= input_max_shape.size()) { MS_LOG(EXCEPTION) << "Invalid Dynamic Shape Max Shape"; } - MS_LOG(INFO) << "Change -1 Shape to Max Shape:" << input_max_shape[j][1]; - (*size_i) = SizetMulWithOverflowCheck((*size_i), LongToSize(input_max_shape[j][1])); + MS_LOG(INFO) << "Change -1 Shape to 1:" << input_max_shape[j][1]; + (*size_i) = SizetMulWithOverflowCheck((*size_i), 1); continue; } (*size_i) = SizetMulWithOverflowCheck((*size_i), static_cast(input_json[kJShape][j])); @@ -89,8 +89,8 @@ void GetRealOutputSize(const nlohmann::json &output_json, std::vector *o if (j >= output_max_shape.size()) { MS_LOG(EXCEPTION) << "Invalid Dynamic Shape Max Shape"; } - MS_LOG(INFO) << "Change -1 Shape to Max Shape:" << output_max_shape[j][1]; - (*size_i) = SizetMulWithOverflowCheck(*size_i, LongToSize(output_max_shape[j][1])); + MS_LOG(INFO) << "Change -1 Shape to 1:" << output_max_shape[j][1]; + (*size_i) = SizetMulWithOverflowCheck(*size_i, 1); continue; } (*size_i) = SizetMulWithOverflowCheck(*size_i, static_cast(output_json[kJShape][j])); diff --git a/mindspore/ccsrc/runtime/graph_scheduler/actor/data_prepare_actor.cc b/mindspore/ccsrc/runtime/graph_scheduler/actor/data_prepare_actor.cc index 3f506d0e602..350483afefa 100644 --- a/mindspore/ccsrc/runtime/graph_scheduler/actor/data_prepare_actor.cc +++ b/mindspore/ccsrc/runtime/graph_scheduler/actor/data_prepare_actor.cc @@ -415,6 +415,7 @@ void DataPrepareActor::PrepareDataForHostTensorQueue(const std::vectorSetNodeIndex(input_node, 0); } + device_address->SetSize(host_tensors[tensor_position]->data().nbytes()); } } diff --git a/mindspore/python/mindspore/_extends/parallel_compile/tbe_compiler/tbe_helper.py b/mindspore/python/mindspore/_extends/parallel_compile/tbe_compiler/tbe_helper.py index 949aa82ac60..67d09351cf7 100644 --- a/mindspore/python/mindspore/_extends/parallel_compile/tbe_compiler/tbe_helper.py +++ b/mindspore/python/mindspore/_extends/parallel_compile/tbe_compiler/tbe_helper.py @@ -149,6 +149,11 @@ def get_single_io_arg(info): res = info else: res = None + if 'range' in info: + for i in range(len(info['range'])): + if info['range'][i][1] == -1: + info['range'][i][1] = None + res = info return res diff --git a/mindspore/python/mindspore/common/tensor.py b/mindspore/python/mindspore/common/tensor.py index 26bdf750e85..432f67bb3af 100644 --- a/mindspore/python/mindspore/common/tensor.py +++ b/mindspore/python/mindspore/common/tensor.py @@ -125,7 +125,10 @@ class Tensor(Tensor_): _check_tensor_input(input_data, dtype, shape, init) # If input_data is tuple/list/numpy.ndarray, it's support in check_type method. - if init is None: + if (isinstance(shape, (list, tuple)) and None in shape) or init is not None: + shape = _check_tensor_dynamic_shape(dtype, shape, init) + Tensor_.__init__(self, dtype, shape) + else: validator.check_value_type('input_data', input_data, (Tensor_, np.ndarray, np.str_, list, tuple, float, int, bool, complex), 'Tensor') @@ -153,8 +156,6 @@ class Tensor(Tensor_): Tensor_.__init__(self, input_data, dtype) else: Tensor_.__init__(self, input_data) - else: - Tensor_.__init__(self, dtype, shape) self.virtual_flag = False self.init = init @@ -2886,9 +2887,6 @@ def _check_tensor_input(input_data=None, dtype=None, shape=None, init=None): if init is not None and (shape is None or dtype is None): raise ValueError("init, dtype and shape must have values at the same time.") - if (int(input_data is None) + int(init is None)) != 1: - raise TypeError("input_data and init can not be None at the same time.") - if input_data is not None: if isinstance(input_data, np.ndarray) and input_data.ndim > 1 and input_data.size == 0: raise ValueError("input_data can not contain zero dimension.") @@ -2900,4 +2898,18 @@ def _check_tensor_input(input_data=None, dtype=None, shape=None, init=None): raise ValueError("Shape can not contain zero value.") +def _check_tensor_dynamic_shape(dtype=None, shape=None, init=None): + """Check if the tensor has dynamic shape.""" + shape_list = list(shape) + if len(shape_list) >= 1: + shape_replaced_list = [-1 if i is None else i for i in shape_list] + if isinstance(shape, tuple): + shape = tuple(shape_replaced_list) + if isinstance(shape, list): + shape = shape_replaced_list + if -1 in shape and (dtype is None or init is not None): + raise ValueError("If setting dynamic shape, dtype must not be None, init must be None") + return shape + + tensor_operator_registry.register('vm_compare', _vm_compare) diff --git a/mindspore/python/mindspore/nn/cell.py b/mindspore/python/mindspore/nn/cell.py index df108aa57ee..9873ffc9e98 100755 --- a/mindspore/python/mindspore/nn/cell.py +++ b/mindspore/python/mindspore/nn/cell.py @@ -138,6 +138,8 @@ class Cell(Cell_): self.cast = Cast() self._has_config_recompute = False self._user_parameters = [] + self._dynamic_shape_inputs = None + self.saved_dynamic_shape = None def __getstate__(self): base = Cell_.__getstate__(self) @@ -660,7 +662,7 @@ class Cell(Cell_): exist_objs.add(item) if item.name == PARAMETER_NAME_DEFAULT: logger.warning("The parameter definition is deprecated.\n" - "Please set a unique name for the parameter in ParameterTuple '{}'.". format(value)) + "Please set a unique name for the parameter in ParameterTuple '{}'.".format(value)) item.name = item.name + "$" + str(self._id) self._id += 1 self.insert_param_to_cell(item.name, item, check_name_contain_dot=False) @@ -875,6 +877,40 @@ class Cell(Cell_): self._construct_inputs_names = self._construct_inputs_names[1:self._construct_inputs_num] self._construct_inputs_num = self._construct_inputs_num - 1 + def set_inputs(self, *inputs): + """ + Save set inputs for computation graph. + + Args: + inputs (tuple): Inputs of the Cell object. + + Examples: + >>> n = Net() + >>> input_dyn = Tensor(shape = [3, None], dtype=type) + >>> net.set_inputs(input_dyn) + >>> input1 = Tensor(np.random.random([3, 10]), dtype=type) + >>> output = net(input1) + + NOTE: + This is an experimental interface that is subject to change or deletion. + """ + + self._dynamic_shape_inputs = inputs + if isinstance(self._dynamic_shape_inputs[0], (str, int, dict)): + raise TypeError(f"For 'set_inputs, the type must be tuple, but got {type(self._dynamic_shape_inputs[0])}.") + + def get_inputs(self): + """ + Returns the dynamic_inputs of a cell object in one network. + + Returns: + inputs (tuple): Inputs of the Cell object. + NOTE: + This is an experimental interface that is subject to change or deletion. + """ + + return self._dynamic_shape_inputs + def compile(self, *inputs): """ Compile Cell as a computation graph, the input must be consistent with the input defined in construct. @@ -882,7 +918,20 @@ class Cell(Cell_): Args: inputs (tuple): Inputs of the Cell object. """ - _cell_graph_executor.compile(self, *inputs, phase=self.phase, auto_parallel_mode=self._auto_parallel_mode) + if self._dynamic_shape_inputs is None or self._dynamic_shape_inputs[0] is None: + _cell_graph_executor.compile(self, *inputs, phase=self.phase, auto_parallel_mode=self._auto_parallel_mode) + else: + self._check_compile_dynamic_shape(*inputs) + if self.saved_dynamic_shape: + for i in range(len(self.saved_dynamic_shape)): + if self.saved_dynamic_shape[i].shape != self._dynamic_shape_inputs[i].shape \ + and self.saved_dynamic_shape[i].shape != self._dynamic_shape_inputs[i].shape: + break + return + self.saved_dynamic_shape = self._dynamic_shape_inputs + _cell_graph_executor.compile(self, *self._dynamic_shape_inputs, phase=self.phase, + auto_parallel_mode=self._auto_parallel_mode) + logger.debug("Compiled Graph with dynamic shape") def compile_and_run(self, *inputs): """ @@ -911,7 +960,7 @@ class Cell(Cell_): new_inputs.append(i) elif context.get_context("grad_for_scalar") and isinstance(i, (int, float)): new_inputs.append(i) - elif hasattr(self, "enable_tuple_broaden") and self.enable_tuple_broaden and isinstance(i, tuple) and\ + elif hasattr(self, "enable_tuple_broaden") and self.enable_tuple_broaden and isinstance(i, tuple) and \ _check_all_tensor(i): new_inputs.append(i) @@ -1239,7 +1288,7 @@ class Cell(Cell_): for value, param in self.parameters_and_names(): if param.name in names: raise ValueError("The value of {} is {}, its name '{}' already exists. " - "Please set a unique name for the parameter.". format(value, param, param.name)) + "Please set a unique name for the parameter.".format(value, param, param.name)) names.add(param.name) def parameters_and_names(self, name_prefix='', expand=True): @@ -2067,6 +2116,43 @@ class Cell(Cell_): params.append(param) return params + def _check_compile_dynamic_shape(self, *inputs): + """ + Check if graph has been compiled with dynamic shape. + + Args: + inputs (tuple): Inputs of the Cell object. + """ + len_inputs = len(inputs) + len_dynamic_shape_inputs = len(self._dynamic_shape_inputs) + if len_dynamic_shape_inputs != len_inputs: + raise ValueError( + f"For 'set_inputs', the Length of Tensor should be {len_inputs}, but got {len_dynamic_shape_inputs}." + ) + for tensor_index in range(len_dynamic_shape_inputs): + i_dynamic_shape_inputs = self._dynamic_shape_inputs[tensor_index] + i_inputs = inputs[tensor_index] + if i_dynamic_shape_inputs.dtype is not i_inputs.dtype: + raise TypeError( + f"For 'set_inputs', the DataType of Tensor should be {i_inputs.dtype}, but got " + f"{i_dynamic_shape_inputs.dtype}." + ) + set_inputs_shape = list(i_dynamic_shape_inputs.shape) + inputs_shape = list(i_inputs.shape) + if len(inputs_shape) != len(set_inputs_shape): + raise ValueError( + f"For 'set_inputs' the Dimension of Tensor shape must be {len(inputs_shape)}, but got " + f"{len(set_inputs_shape)}." + ) + for shape_index in i_dynamic_shape_inputs.shape: + if shape_index != -1: + dynamic_index = i_dynamic_shape_inputs.shape.index(shape_index) + if set_inputs_shape[dynamic_index] != inputs_shape[dynamic_index]: + raise ValueError( + f"For 'Length of Tensor shape', the value must be the same with that of inputs, but" + f" got {i_dynamic_shape_inputs.shape}." + ) + class GraphCell(Cell): """ diff --git a/mindspore/python/mindspore/train/model.py b/mindspore/python/mindspore/train/model.py index 2bd69e98c81..a28d07d36ff 100644 --- a/mindspore/python/mindspore/train/model.py +++ b/mindspore/python/mindspore/train/model.py @@ -294,6 +294,8 @@ class Model: if self._optimizer is None: # In this case, multiple optimizer(s) is supposed to be included in 'self._network' _set_multi_subgraphs() + if self._network.get_inputs() is not None: + network.set_inputs(*self._network.get_inputs()) return network def _build_eval_network(self, metrics, eval_network, eval_indexes):