def _common(cls, node, scales, sizes, nearest_mode='round_prefer_ceil', **kwargs): all_nodes = kwargs['all_nodes'] G = kwargs['G'] valid_name = kwargs['valid_name'] inputs = [all_nodes[inp] if inp else None for inp in node.input] x = inputs[0] x_shape = x[2].shape x_rank = len(x_shape) spatial_size = x_rank - 2 in_c = x_shape[1] in_w = x_shape[-1] if scales is not None: sizes = np.array(x_shape) * np.array(scales) sizes = [None if x_shape[idx] is None else dim for idx, dim in enumerate(sizes)] if spatial_size == 1: sizes.insert(-1, 1) if nearest_mode != 'round_prefer_ceil': logger.warning('only round_prefer_ceil is supported for nearest mode') if spatial_size != 2 and spatial_size != 1: raise ValueError('resize only supports 4D tensor in NCHW mode or 3D tensor in NCF mode' f' - input shape is {x_shape} sizes is {sizes}') if not all(x_dim == size_dim for x_dim, size_dim in zip(x_shape[:2:], sizes[:2:])): raise ValueError('resize only supports 4D tensor in NCHW mode or 3D tensor in NCF mode' f' - input shape is {x_shape} sizes is {sizes}') mode = node.attrs.get('mode', 'nearest') if mode != 'nearest' and mode != 'linear': raise ValueError('resize only supports nearest and linear modes') params_class = BilinearResizerParameters if mode == 'linear' else NearestNeighborResizerParameters params = params_class(valid_name, new_shape=tuple(sizes[2::]), align_corners=False, halfpixel_centers=False, in_dims_hint=[['c', 'h', 'w']], out_dims_hint=[['c', 'h', 'w']]) if spatial_size == 1: r1_params = ReshapeParameters(f'{valid_name}_reshape2d', old_shape=Dim.unnamed([in_c, in_w]), shape=Dim.unnamed([in_c, 1, in_w])) r2_params = ReshapeParameters(f'{valid_name}_reshape1d', old_shape=Dim.unnamed([in_c, 1, sizes[-1]]), shape=Dim.unnamed([in_c, sizes[-1]])) G.add_edge(NNEdge(from_node=x[0], to_node=r1_params, from_idx=x[1], to_idx=0)) G.add_edge(NNEdge(from_node=r1_params, to_node=params, from_idx=0, to_idx=0)) G.add_edge(NNEdge(from_node=params, to_node=r2_params, from_idx=0, to_idx=0)) pout_dims = ProvisionalDim(sizes[:-2:] + sizes[-1::]) params = r2_params else: pout_dims = ProvisionalDim(sizes) G.add_edge(NNEdge(from_node=x[0], to_node=params, from_idx=x[1], to_idx=0)) all_nodes[node.output[0]] = (params, 0, pout_dims) return params
def common_11_13(cls, node, **kwargs): all_nodes = kwargs['all_nodes'] inputs = [all_nodes[inp] if inp else None for inp in node.input] x = inputs[0] x_shape = x[2].shape nearest_mode = node.attrs.get('nearest_mode', 'round_prefer_floor') coord_transmode = node.attrs.get( 'coordinate_transformation_mode', 'half_pixel') if coord_transmode != 'align_corners': logger.warning( 'only align_corners is supported as coordinate_transformation_mode') scales_inp = inputs[2] scales_shape = scales_inp[2].shape if scales_inp else None if scales_inp and len(scales_shape) > 0 and scales_shape[0] == len(x_shape): scales = cls.get_constant(inputs[2]) return cls._common(node, scales, None, nearest_mode=nearest_mode, **kwargs) else: sizes_inp = inputs[2] sizes_shape = sizes_inp[2].shape if sizes_inp else None if not sizes_inp or len(sizes_shape) < 1 or sizes_shape[0] == len(x_shape): raise ValueError( 'neither sizes nor scales input is valid for reshape') sizes = cls.get_constant(inputs[3]) return cls._common(node, None, sizes, nearest_mode=nearest_mode, **kwargs)
def _common(cls, node, **kwargs): all_nodes = kwargs['all_nodes'] G = kwargs['G'] valid_name = kwargs['valid_name'] used_tensors = kwargs['used_tensors'] inputs = [all_nodes[inp] if inp else None for inp in node.input] input_shapes = [inp[2].shape if inp else None for inp in inputs] x = inputs[0] seq_len = input_shapes[0][0] if seq_len is None: logger.warning("sequence length is variable in size. forcing to 20. " "reexport your graph with sequence length set to the maxmimum sequence size") seq_len = 20 input_size = input_shapes[0][2] hidden_size = node.attrs["hidden_size"] direction = node.attrs.get("direction", "forward") num_directions = 2 if direction == "bidirectional" else 1 linear_before_reset = bool(node.attrs.get("linear_before_reset", 0)) if not linear_before_reset: logger.warning("In {} linear_before_reset == 0 not supported by the Autotiler kernels".fromat(valid_name)) # output_sequence = node.attrs.get("output_sequence", 0) i_weights = cls.get_constant(inputs[1]) r_weights = cls.get_constant(inputs[2]) biases = cls.get_constant(inputs[3]) if inputs[3] else np.zeros( [num_directions, 6 * hidden_size]) tensors = {'forward': {}, 'backward': {}} # extract all the tensors in approriate format cls.deep_update(tensors, cls.extract_weights( i_weights, hidden_size, ('w_2_z_w', 'w_2_r_w', 'w_2_h_w'), num_directions)) cls.deep_update(tensors, cls.extract_weights( r_weights, hidden_size, ('r_2_z_w', 'r_2_r_w', 'r_2_h_w'), num_directions)) cls.deep_update(tensors, cls.extract_biases( biases, hidden_size, ("w_z_b", "w_r_b", "w_h_b", "r_z_b", "r_r_b", "r_h_b"), num_directions)) cls.deep_update(tensors, cls.get_state(inputs, 5, 'h_state', hidden_size, num_directions)) cls.combine_biases_gru(tensors, ['z', 'r'], num_directions) extra_args = { 'linear_before_reset': linear_before_reset } return cls.attach_rnn(G, x, GRUParameters, extra_args, valid_name, tensors, used_tensors, hidden_size, input_size, all_nodes, node, seq_len, num_directions)
def _common(cls, node, mode='constant', pads=None, constant_value=0, **kwargs): all_nodes = kwargs['all_nodes'] G = kwargs['G'] valid_name = kwargs['valid_name'] inputs = [all_nodes[inp] for inp in node.input] x = inputs[0] x_shape = x[2].shape ndim = len(x_shape) npad = len(pads) // 2 if npad != ndim: if all(not pad for pad in pads): logger.warning( f'Pad {valid_name} has {npad} pad values and {ndim} input rank. ' 'Since pad is zero this is ignored but it probably indicates a bug in the ONNX graph.' ) else: raise ValueError( f'Eroor in ONNX graph - pad {valid_name} has {npad} pad values and {ndim} input rank.' ) apads = np.array([[pads[idx], pads[idx + ndim]] for idx in range(ndim)]) # apads = np.array(pads).reshape((-1, 2)) if cls.is_constant(x): logger.info("reducing %s to a constant", valid_name) val = cls.get_constant(x) if mode == 'constant': val = np.pad(val, apads, mode=mode, constant_values=constant_value) else: val = np.pad(val, apads, mode=mode) params = ConstantInputParameters(valid_name, value=val) pshape = [ dim + sum(apads[idx]) if dim is not None else None for idx, dim in enumerate(x_shape) ] all_nodes[node.output[0]] = (params, 0, ProvisionalDim(pshape), x[3]) return params if mode != 'constant': raise ValueError('%s - pad mode %s is not supported' % (valid_name, mode)) if any( sum(pad) > 0 and x_shape[idx] is None for idx, pad in enumerate(apads)): raise ValueError( f'unknown/batch axis is being padded in {valid_name}. Manipulation of ' 'unknown/batch axis is not supported') trimmed_pads = tuple( [pad for idx, pad in enumerate(apads) if x_shape[idx] is not None]) if all(sum(trimmed_pad) == 0 for trimmed_pad in trimmed_pads): params = NoOPParameters(valid_name, desc="eliminated pad of 0") pshape = x_shape else: pshape = [ dim + sum(apads[idx]) if dim is not None else None for idx, dim in enumerate(x_shape) ] # pshape = [None if dim is None else dim + sum(apads[idx]) for idx, dim in enumerate(x_shape)] padvals = [(constant_value, constant_value)] * len(trimmed_pads) params = PadParameters(valid_name, padding=trimmed_pads, pad_vals=padvals) G.add_edge( NNEdge(from_node=x[0], to_node=params, from_idx=x[1], to_idx=0)) all_nodes[node.output[0]] = (params, 0, ProvisionalDim(pshape), x[3]) return params
def conv(cls, node, quantized=False, **kwargs): all_nodes = kwargs['all_nodes'] G = kwargs['G'] valid_name = kwargs['valid_name'] inputs = [all_nodes[inp] for inp in node.input] # input N x C x H x W x = inputs[0] x_rank = len(x[2].shape) x_shape = x[2].shape if x_shape[0] is not None: real_in_shape = tuple(x_shape.copy()) if x_shape[0] > 1: # support for multi batch is very limited batch = x_shape[0] logger.warning( f"{valid_name} has a non 1 batch dimension of {batch} -" " this is not supported by nntool or autotiler kernels") else: # if the batch is specified but is 1 then the input will be reshaped # and the output will have the batch dim set as unknown. batch = None else: real_in_shape = tuple(x_shape[1:]) batch = None spatial_size = x_rank - 2 assert spatial_size == 2 or spatial_size == 1, "only 1D and 2D convolutions supported" # Input error checking undefined = [] if x_shape[1] is None: # cope with swapped batch and channel due to bad initial reshape if x_shape[0] == 1: batch = None x_shape = [x_shape[1], x_shape[0]] + list(x_shape[2:]) real_in_shape = x_shape[1:] else: undefined.append(f"input channel size of filter {valid_name} must be defined.") if not all(dim is not None for dim in x_shape[-spatial_size:]): undefined.append(f"input spatial size {x_shape} of filter {valid_name} must be defined.") if undefined: raise ValueError(f"{' '.join(undefined)}. You may need to override input dimensions.") # M x C/group x kH x kW weights_idx = 3 if quantized else 1 weights_node = inputs[weights_idx][0] weights_node.name = f'{valid_name}_weights' weights = cls.get_constant(inputs[weights_idx]) out_c = weights.shape[0] group = node.attrs.get("group", 1) in_c = x_shape[1] filt_in_c = in_c // group if in_c != weights.shape[1] * group: raise ValueError(f'node {valid_name} has incorrect input channel ' f'dimension {in_c} expecting {weights.shape[1] * group}') if spatial_size == 1: filt_w = weights.shape[-1] filt_h = h = 1 w = x_shape[-1] # create a new constant node since we are changing the shape weights = np.reshape(weights, (out_c, filt_in_c, filt_h, filt_w)) weights_node = ConstantInputParameters(f'{valid_name}_weights', value=weights, dims=Dim.unnamed( weights.shape)) cls.record_constant_qrec(inputs[1], weights_node, **kwargs) else: filt_h = weights.shape[-2] filt_w = weights.shape[-1] h = x_shape[-2] w = x_shape[-1] conv_in_shape = (in_c, h, w) # h = 1 if spatial_size == 1 else ( # x_shape[-2] if x_shape[-2] is not None else 1) # w = x_shape[-1] if x_shape[-1] is not None else 1 filt_dim = Conv2DFilterDim(filt_h, filt_w, out_c, in_c=filt_in_c) filt_dim = filt_dim.impose_order(cls.ONNX_FILTER_ORDER) biases_idx = 8 if quantized else 2 if len(inputs) > biases_idx: biases_node = inputs[biases_idx][0] biases = cls.get_constant(inputs[biases_idx]) else: biases = np.zeros([out_c], dtype=np.float32) biases_node = ConstantInputParameters(f'{valid_name}_biases', value=biases, dims=Dim.unnamed( biases.shape)) dilations = cls.pad_start_with(node.attrs.get("dilations", []), [1], 2) strides = cls.pad_start_with(node.attrs.get("strides", []), [1], 2) pad_dim = cls.calc_pad_dim(node, 4) if batch is not None: in_hint = ['n', 'c', 'h', 'w'] out_hint = ['n', 'c', 'h', 'w'] in_dim = Dim.named_ordered(n=batch, c=in_c, h=h, w=w) ker_in_order = [ ['n', 'c', 'h', 'w'], ['out_c', 'in_c', 'h', 'w'], ['out_c']] ker_out_order = [['n', 'c', 'h', 'w']] else: in_hint = ['c', 'h', 'w'] out_hint = ['c', 'h', 'w'] in_dim = Dim.named_ordered(c=in_c, h=h, w=w) ker_in_order = [ ['c', 'h', 'w'], ['out_c', 'in_c', 'h', 'w'], ['out_c']] ker_out_order = [['c', 'h', 'w']] params = Conv2DParameters(valid_name, filt=filt_dim, stride=StrideDim(strides[0], strides[1]), dilation=DilationDim(dilations[0], dilations[1]), batch=batch, groups=group, padding=pad_dim, ker_in_order=ker_in_order, ker_out_order=ker_out_order, has_bias=True, in_dims_hint=[in_hint, cls.ONNX_FILTER_ORDER, ['c']], out_dims_hint=[out_hint]) if quantized: qrecs = kwargs['qrecs'] x_zp = cls.get_constant(inputs[2]) x_scale = cls.get_constant(inputs[1]) x_qtype = QType(dtype=x_zp.dtype, scale=x_scale, zero_point=x_zp) w_zp = cls.get_constant(inputs[5]) w_scale = cls.get_constant(inputs[4]) weights_node.qtype = w_qtype = QType( dtype=w_zp.dtype, scale=w_scale, zero_point=w_zp, quantized_dimension=0 if len(w_scale) > 1 else None) o_zp = cls.get_constant(inputs[7]) o_scale = cls.get_constant(inputs[6]) o_qtype = QType(dtype=o_zp.dtype, scale=o_scale, zero_point=o_zp) biases_node.qtype = b_qtype = QType( dtype=biases.dtype, scale=w_scale*x_scale) qrecs[NodeId(params)] = QRec.scaled( in_qs=[x_qtype, w_qtype, b_qtype], out_qs=[o_qtype], ) else: o_qtype = None w_dim = Dim.named_ordered( out_c=out_c, in_c=filt_in_c, h=filt_h, w=filt_w) b_dim = Dim.named_ordered(c=out_c) out_dims = params.get_output_size([in_dim, w_dim, b_dim]) G.add_edge(NNEdge(from_node=weights_node, to_node=params, from_idx=0, to_idx=1)) G.add_edge(NNEdge(from_node=biases_node, to_node=params, from_idx=0, to_idx=2)) # check if input needs a reshape if conv_in_shape != real_in_shape: r1_params = ReshapeParameters(f'{valid_name}_reshape_in', old_shape=Dim.unnamed(real_in_shape), shape=Dim.unnamed(conv_in_shape)) G.add_edge( NNEdge(from_node=x[0], to_node=r1_params, from_idx=x[1], to_idx=0)) G.add_edge(NNEdge(from_node=r1_params, to_node=params, from_idx=0, to_idx=0)) else: G.add_edge( NNEdge(from_node=x[0], to_node=params, from_idx=x[1], to_idx=0)) # check if output needs a reshape if spatial_size == 1: if batch is not None: oned_out_shape = [batch, out_dims[0].c, out_dims[0].w] pout_dims = ProvisionalDim(oned_out_shape) else: oned_out_shape = [out_dims[0].c, out_dims[0].w] pout_dims = ProvisionalDim([None] + oned_out_shape) r2_params = ReshapeParameters(f'{valid_name}_reshape_out', old_shape=out_dims[0], shape=Dim.unnamed(oned_out_shape)) G.add_edge(NNEdge(from_node=params, to_node=r2_params, from_idx=0, to_idx=0)) params = r2_params else: pout_dims = ProvisionalDim([batch] + out_dims[0].shape) all_nodes[node.output[0]] = (params, 0, pout_dims, o_qtype) return params