From 80bda772037717856d15fb05d3dbd3a2090d177a Mon Sep 17 00:00:00 2001 From: KeDengMS Date: Wed, 18 Sep 2019 14:18:32 -0700 Subject: [PATCH] Fix symbolic shape inference for faster_rcnn, mask_rcnn, yolov3 (#1867) * Fix symbolic shape inference for faster_rcnn, mask_rcnn, yolov3 Force merge when --auto_merge, on symbolic dims which sympy cannot simplify Add symbolic inference for Resize opset 10 Add support for step != 1 in Slice Add support for computed dim in TopK Bug fixes in passing symbolic dims from subgraph Fix an outdate comment in Nuphar provider header --- cmake/external/tvm | 2 +- .../nuphar/nuphar_provider_factory.h | 2 +- .../nuphar/scripts/symbolic_shape_infer.py | 40 ++++++++++++++----- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/cmake/external/tvm b/cmake/external/tvm index 638d7d2407..2ad9c02cdc 160000 --- a/cmake/external/tvm +++ b/cmake/external/tvm @@ -1 +1 @@ -Subproject commit 638d7d2407de27f98f542f61a37a33c90a2e75a9 +Subproject commit 2ad9c02cdcca2d78237e0a90f1f324383452511e diff --git a/include/onnxruntime/core/providers/nuphar/nuphar_provider_factory.h b/include/onnxruntime/core/providers/nuphar/nuphar_provider_factory.h index 58c82a0e1f..ec574f74c7 100644 --- a/include/onnxruntime/core/providers/nuphar/nuphar_provider_factory.h +++ b/include/onnxruntime/core/providers/nuphar/nuphar_provider_factory.h @@ -8,7 +8,7 @@ extern "C" { #endif /** * \param device_id nuphar device id, starts from zero. - * \param target_str TVM target string. + * \param settings_str Nuphar settings string. */ ORT_API_STATUS(OrtSessionOptionsAppendExecutionProvider_Nuphar, _In_ OrtSessionOptions* options, int allow_unaligned_buffers, _In_ const char* settings_str); diff --git a/onnxruntime/core/providers/nuphar/scripts/symbolic_shape_infer.py b/onnxruntime/core/providers/nuphar/scripts/symbolic_shape_infer.py index 47a19566d1..6c181ce161 100644 --- a/onnxruntime/core/providers/nuphar/scripts/symbolic_shape_infer.py +++ b/onnxruntime/core/providers/nuphar/scripts/symbolic_shape_infer.py @@ -84,6 +84,7 @@ class SymbolicShapeInference: 'Range' : self._infer_Range, 'ReduceProd' : self._infer_ReduceProd, 'Reshape' : self._infer_Reshape, + 'Resize' : self._infer_Resize, 'Round' : self._pass_on_shape_and_type, 'Scan' : self._infer_Scan, 'Shape' : self._infer_Shape, @@ -113,7 +114,9 @@ class SymbolicShapeInference: if type(self.symbolic_dims_[s]) == sympy.Symbol: map_to = s if not map_to: - raise Exception('Cannot merge between symbolic expressions: ({}), please modify model input dims to avoid this!'.format(','.join(symbols))) + if self.verbose_ > 0: + print('Potential unsafe merge between symbolic expressions: ({})'.format(','.join(symbols))) + map_to = symbols.pop() # force merge when unable to determine for s in symbols: if s == map_to: continue @@ -307,7 +310,7 @@ class SymbolicShapeInference: # for new symbolic dims from subgraph output, add to main graph symbolic dims subgraph_shapes = [get_shape_from_type_proto(o.type) for o in symbolic_shape_inference.out_mp_.graph.output] subgraph_new_symbolic_dims = set([d for s in subgraph_shapes if s for d in s if type(d) == str and not d in self.symbolic_dims_]) - self.symbolic_dims_.update({d:symbolic_shape_inference[d] for d in subgraph_new_symbolic_dims}) + self.symbolic_dims_.update({d:symbolic_shape_inference.symbolic_dims_[d] for d in subgraph_new_symbolic_dims}) def _get_int_values(self, node, broadcast=False): values = [self._try_get_value(node, i) for i in range(len(node.input))] @@ -510,7 +513,7 @@ class SymbolicShapeInference: vi.type.tensor_type.elem_type, data_shape[:axis] + indices_shape + data_shape[axis+1:])) if node.input[0] in self.sympy_data_: - assert 0 == get_attribute(node, 'axis') # only handle 1D sympy compute + assert 0 == get_attribute(node, 'axis', 0) # only handle 1D sympy compute idx = self._get_value(node, 1) data = self.sympy_data_[node.input[0]] if type(data) == list: @@ -650,6 +653,16 @@ class SymbolicShapeInference: get_shape_from_sympy_shape(new_sympy_shape))) self._pass_on_sympy_data(node) + def _infer_Resize(self, node): + assert get_opset(self.out_mp_) <= 10 # only support opset 10 Resize for now + scales = self._try_get_value(node, 1) + if scales is not None: + input_sympy_shape = self._get_sympy_shape(node, 0) + new_sympy_shape = [sympy.simplify(sympy.floor(d*s)) for d,s in zip(input_sympy_shape, scales)] + self._update_computed_dims(new_sympy_shape) + vi = self.known_vi_[node.output[0]] + vi.CopyFrom(helper.make_tensor_value_info(node.output[0], self.known_vi_[node.input[0]].type.tensor_type.elem_type, get_shape_from_sympy_shape(new_sympy_shape))) + def _infer_Scan(self, node): subgraph = get_attribute(node, 'body') num_scan_inputs = get_attribute(node, 'num_scan_inputs') @@ -713,12 +726,12 @@ class SymbolicShapeInference: new_shape[i] = self._new_symbolic_dim_from_output(node,0,i) else: for i,s,e,t in zip(axes, starts, ends, steps): - # TODO: handle step - assert t == 1 idx = handle_negative_axis(i, len(new_shape)) if is_literal(e): if e >= int(2 ** 31 - 1): # max value of int32 e = new_shape[i] + elif e <= -int(2 ** 31): # min value of int32 + e = 0 elif is_literal(new_shape[i]): e = min(e, new_shape[i]) else: @@ -740,7 +753,7 @@ class SymbolicShapeInference: if is_literal(s) and int(s) < 0: s = new_shape[i] + s - new_shape[idx] = e - s + new_shape[idx] = (e - s + (-1 if t > 0 else 1)) // t + 1 self._update_computed_dims(new_shape) new_shape = get_shape_from_sympy_shape(new_shape) @@ -787,18 +800,26 @@ class SymbolicShapeInference: def _infer_TopK(self, node): rank = self._get_shape_rank(node, 0) - axis = handle_negative_axis(get_attribute(node, 'axis'), rank) + axis = handle_negative_axis(get_attribute(node, 'axis', -1), rank) new_shape = self._get_shape(node, 0) if get_opset(self.out_mp_) <= 9: k = get_attribute(node, 'k') else: - k = self._try_get_value(node, 1) + k = self._get_int_values(node)[1] if k == None: k = self._new_symbolic_dim_from_output(node) + else: + k = as_scalar(k) - new_shape[axis] = k + if type(k) in [int, str]: + new_shape[axis] = k + else: + new_sympy_shape = self._get_sympy_shape(node, 0) + new_sympy_shape[axis] = k + self._update_computed_dims(new_sympy_shape) # note that TopK dim could be computed in sympy_data, so need to update computed_dims when it enters shape + new_shape = get_shape_from_sympy_shape(new_sympy_shape) for i_o in range(len(node.output)): vi = self.known_vi_[node.output[i_o]] @@ -874,6 +895,7 @@ class SymbolicShapeInference: if self.verbose_ > 0 or not self.auto_merge_ or out_type_undefined: print('Stopping at incomplete shape inference at ' + node.op_type + ': ' + node.name) + print(node) print('node inputs:') for i in node.input: print(self.known_vi_[i])