def extract(cls, node): pb = node.parameters try: collect_until_token(pb, b'<InputDim>') except Error: raise Error("<InputDim> was not found") in_dim = read_binary_integer32_token(pb) try: collect_until_token(pb, b'<OutputDim>') except Error: raise Error("<OutputDim> was not found") out_dim = read_binary_integer32_token(pb) assert in_dim % out_dim == 0 group = in_dim / out_dim try: collect_until_token(pb, b'<P>') except Error: raise Error("<P> was not found") p = read_binary_float_token(pb) attrs = { 'group': group, 'p': p, } PNormOp.update_node_stat(node, attrs) return cls.enabled
def guess_source_layouts_by_mean_scale(ov_function: Model, layout_values, mean_scale_values: dict): """ Internal function. Try to guess source layout for input by its shape and/or framework :param: ov_function Original model :param: layout_values Existing source/target layout items specified by user :param: mean_scale_values Dictionary with mean/scale values defined for each argument :return: updated layout items with guessed layouts """ for ms_name, mean_scale in mean_scale_values.items(): num_channels_mean = len( mean_scale['mean']) if mean_scale['mean'] is not None else 0 num_channels_scale = len(mean_scale['scale']) if hasattr( mean_scale['scale'], '__len__') else 0 if num_channels_mean > 1 and \ num_channels_scale > 1 and \ num_channels_mean is not num_channels_scale: raise Error( 'Mean/Scale values for {} have different sizes: {} {}'.format( ms_name, num_channels_mean, num_channels_scale)) need_guess_channels = num_channels_mean > 1 or num_channels_scale > 1 if not need_guess_channels: # Mean/scale is complex and needs 'channels' specified in layout continue num_channels = num_channels_mean if num_channels_mean > 1 else num_channels_scale for i in range(0, len(ov_function.inputs)): ov_input = ov_function.input(i) if not ov_function.get_parameters()[i].layout.empty: continue if ms_name not in ov_input.get_tensor().get_names(): continue layout_item = None for name in ov_input.get_tensor().get_names(): if name in layout_values: layout_item = layout_values[name] break if layout_item is not None: # User specified some layout, skip guessing continue # Guess layout is applicable only when number of channels is '3' if num_channels != 3: raise Error('Can\'t determine channels dimension for {}. ' 'When number of mean/scale values is {} (not 3), ' 'please specify layout for input manually'.format( ms_name, num_channels)) layout_values = find_channels_dimension( shape=ov_input.get_partial_shape(), num_channels=num_channels, name=ms_name, layout_values=layout_values) return layout_values
def check_keys_valid(ov_function: Model, keys: list, search_outputs: bool): """ Internal function: checks if keys from cmd line arguments correspond to ov_function's inputs/outputs Throws if some key is not found Throws if some different keys point to the same actual input/output """ nodes_used = {} nodes = ov_function.inputs if search_outputs: nodes += ov_function.outputs for name in keys: node_found = False for ov_node in nodes: if name in ov_node.get_tensor().get_names(): if ov_node in nodes_used: raise Error( 'Key for {} and {} point to same model input/output.'. format(name, nodes_used[ov_node])) nodes_used[ov_node] = name node_found = True break if not node_found: if not search_outputs: raise Error('Input with name {} wasn\'t found! {}'.format( name, refer_to_faq_msg(83))) else: raise Error( 'Input/Output with name {} wasn\'t found! {}'.format( name, refer_to_faq_msg(83)))
def parse_custom_replacement_config_file(file_name: str): """ Reads custom replacement configuration file file_name. :param file_name: name of the file to read from. :return: The dictionary where key is the layer id and value is an instance of the CustomLayerDescriptor object. """ if not os.path.exists(file_name): raise Error("Custom replacements configuration file '{}' does not exist. ".format(file_name) + refer_to_faq_msg(69)) data = load_and_validate_json_config(file_name) result = list() validation_errors = list() for attrs in data: if 'id' not in attrs: raise Error('One of the custom replacements in the configuration file "{}" does not contain attribute ' '"id". '.format(file_name) + refer_to_faq_msg(71)) if 'match_kind' not in attrs: raise Error('One of the custom replacements in the configuration file "{}" does not contain attribute ' '"match_kind". Possible values are "points", "scope" and "general". '.format(file_name) + refer_to_faq_msg(71)) desc = CustomReplacementDescriptor.create_instance(attrs['match_kind'], attrs['id'], attrs) validation_errors.extend(desc.validate_data()) result.append(desc) if len(validation_errors) > 0: raise Error("File '{}' validation failed:\n{}. ".format(file_name, "\n".join(validation_errors)) + refer_to_faq_msg(72)) return result
def _update_param_using_rule(self, params: dict, rule: [str, tuple]): if isinstance(rule, str): if rule in params: self._model_params[rule] = params[rule] log.debug('Found value "{}" for path "{}"'.format( params[rule], rule)) elif isinstance(rule, tuple): if len(rule) != 2 and len(rule) != 3: raise Error( 'Invalid rule length. Rule must be a tuple with two elements: key and path, or three ' 'elements: key, path, default_value.') value = __class__._get_value_by_path(params, rule[1].split('/')) if value is not None: log.debug('Found value "{}" for path "{}"'.format( value, rule[1])) self._model_params[rule[0]] = value elif len(rule) == 3: self._model_params[rule[0]] = rule[2] log.debug( 'There is no value path "{}". Set default value "{}"'. format(value, rule[2])) else: raise Error( 'Invalid rule type. Rule can be either string or tuple')
def extract_port_from_string(node_name: str): """ Extracts port and node name from string Raises if node name was not provided in the expected format: NODE:OUT_PORT or IN_PORT:NODE :param node_name: string value provided by user :return: node name, input port and output port """ parts = node_name.split(':') if len(parts) > 2: raise Error( "Please provide only one port number for {}. Expected format is NODE:OUT_PORT or IN_PORT:NODE, " "where IN_PORT and OUTPUT_PORT are integers".format(node_name)) if len(parts) == 1: return node_name, None, None else: in_port, out_port, name = None, None, None try: in_port, name = int(parts[0]), parts[1] except ValueError: try: out_port, name = int(parts[1]), parts[0] except ValueError: raise Error( "Non integer port number in {}. Expected format is NODE:OUT_PORT or IN_PORT:NODE, where " "IN_PORT and OUTPUT_PORT are integers".format(node_name)) return name, in_port, out_port
def load_symbol_def(input_model_name, input_symbol, input_names: str = '', nd_prefix_name: str = '', pretrained_model_name: str = '', legacy_mxnet_model: bool = False): if not nd_prefix_name and not pretrained_model_name: # model name always has extension 'param' try: model_name, iteration_number = parse_input_model(input_model_name) except ValueError as err: raise Error( 'Input model name {} is not in an expected format, cannot extract iteration number. ' + refer_to_faq_msg(48), input_model_name) if input_names: model_params = load_params(input_model_name, data_names=input_names.split(',')) else: model_params = load_params(input_model_name) elif nd_prefix_name and pretrained_model_name and input_symbol: model_name, iteration_number = parse_input_model(pretrained_model_name) model_name = '-'.join(input_symbol.split('-')[:-1]) model_params = build_params_file(nd_prefix_name, pretrained_model_name, input_names) else: raise Error( "Arguments --nd_prefix_name, --pretrained_model_name and --input_symbol should be provided. Please provide all or do not use any. " + refer_to_faq_msg(81)) model_nodes = load_symbol_nodes(model_name, input_symbol, legacy_mxnet_model) return model_nodes, model_params, model_name, iteration_number
def infer(node: Node): in_shape = node.in_node().shape if in_shape.size != 4: raise Error('TensorFlow SpaceToDepth operation is supported for 4D \'NHWC\' input layout only. ' 'Current input shape is \'{}\''.format(in_shape)) layout = node.graph.graph['layout'] N = in_shape[get_batch_dim(layout, 4)] H = in_shape[get_height_dim(layout, 4)] W = in_shape[get_width_dim(layout, 4)] C = in_shape[get_features_dim(layout, 4)] block_size = node['block_size'] if (H is not dynamic_dimension and H % block_size) or (W is not dynamic_dimension and W % block_size): raise Error('Spatial dimensions of input tensor of SpaceToDepth operation have to be divisible by ' 'SpaceToDepth \'block_size\' parameter. Input tensor shape = {}. Spatial dimensions = {},{}. ' 'block_size = {}'.format(in_shape, H, W, block_size)) out_shape = shape_for_layout(layout, batch=N, features=C * (block_size ** 2), height=H // block_size, width=W // block_size) node.out_port(0).data.set_shape(out_shape)
def add_destination(self, port): # In this method we are adding destination port with type 'in' for a connection. # See detailed example below. # # SOURCE - Op1(out_port:0) # # DESTINATIONS - Op2(in_port:0) # # NEW PORT - Op3(in_port:0) # # CONNECTION # Op1(out_port:0)--->Op2(in_port:0) # # When we set destination for connection we disconnect destination port if exists and connect source to # the new given port with type='in'. # # UPDATED CONNECTION # ,-->Op3(in_port:0) # Op1(out_port:0)--->Op2(in_port:0) # if self.control_flow is True: raise Error("Cannot operate with connection with control_flow=True") if self.source is None: raise Error("Can not add destination for connection without source port!") if self.graph.stage == 'front': node = self.source.node self.graph.create_edge(node, port.node, out_port=self.source.idx, in_port=port.idx) else: data_node = self.source._create_data_if_necessary() self.graph.add_edge(data_node.id, port.node.id, **{'in': port.idx}) self.destinations.append(port)
def replace_pattern(graph: Graph, match: dict): nodes = [ ('input_unsqueezed'), ('squeeze', dict(op='Reshape')), ('input_squeezed'), ('input_hidden'), ('input_cell'), ('weights'), ('biases'), ('lstm', dict(op='LSTMCell')), ('output_hidden'), ('output_cell'), ('unsqueeze', dict(op='Reshape')), ('output_unsqueezed'), ] edges = [ ('input_unsqueezed', 'squeeze'), ('squeeze', 'input_squeezed'), ('input_squeezed', 'lstm', { 'in': 0 }), ('input_hidden', 'lstm', { 'in': 1 }), ('input_cell', 'lstm', { 'in': 2 }), ('weights', 'lstm', { 'in': 3 }), ('biases', 'lstm', { 'in': 4 }), ('lstm', 'output_hidden', { 'out': 0 }), ('lstm', 'output_cell', { 'out': 1 }), ('output_hidden', 'unsqueeze'), ('unsqueeze', 'output_unsqueezed'), ] ti = match['ti'] isomorphisms = find_isomorphisms(ti.body, nodes, edges) if len(list(isomorphisms)) != 1: raise Error( 'Unsupported TensorIterator layer {} was found: either its body, ports or ' 'edges are not supported by Inference Engine. ' 'Only TensorIterator with LSTMCell in a body of strict form is supported. ' 'Please modify the original network ' 'to meet the requirements.'.format(ti.soft_get('name'))) body_match = isomorphisms[0] if body_match['input_hidden'].has_valid( 'value') or body_match['input_cell'].has_valid('value'): raise Error( 'Unsupported TensorIterator layer {} was found: initial hidden and/or cell states ' 'for LSTMCell are constants. This is not supported. ' 'Only TensorIterator with LSTMCell in a body of strict form is supported. ' 'Please modify the original network ' 'to meet the requirements.'.format(ti.soft_get('name')))
def deducing_metagraph_path(meta_graph_file: str): match = re.search(r'^(.*)\.(data-\d*-of-\d*|index|meta)$', meta_graph_file) if match is not None: deduced_meta_graph_file = match.group(1) + '.meta' if not os.path.isfile(deduced_meta_graph_file): raise Error( '\n\nMetaGraph freezing mechanism was enabled. ' '\n{} file does not represent MetaGraph. ' '\n{} path to MetaGraph was deduced, but it does not exist' '\n\nModel with MetaGraph consists of 3-4 files:' '\n1. model_name.meta' '\n2. model_name.index' '\n3. model_name.data-00000-of-00001 (digit part may vary)' '\n4. checkpoint (optional)'.format(meta_graph_file, deduced_meta_graph_file)) else: meta_graph_file = deduced_meta_graph_file else: raise Error('\n\nMetaGraph freezing mechanism was enabled. ' '\n{} file does not represent MetaGraph. ' '\n\nModel with MetaGraph consists of 3-4 files:' '\n1. model_name.meta' '\n2. model_name.index' '\n3. model_name.data-00000-of-00001 (digit part may vary)' '\n4. checkpoint (optional)' '\n\nTo load this model, simply run:' '\npython3 mo_tf.py --input_meta_graph model_name.meta' ''.format(meta_graph_file)) return meta_graph_file
def infer(node: Node): in_shape = node.in_port(0).data.get_shape() if in_shape.size != 4: raise Error('TensorFlow DepthToSpace operation is supported for 4D \'NHWC\' input layout only. ' 'Current input shape is \'{}\''.format(in_shape)) layout = node.graph.graph['layout'] N = in_shape[get_batch_dim(layout, 4)] H = in_shape[get_height_dim(layout, 4)] W = in_shape[get_width_dim(layout, 4)] C = in_shape[get_features_dim(layout, 4)] block_size = node['block_size'] if C is not dynamic_dimension and C % (block_size ** 2): raise Error('Feature dimensions of input tensor of DepthToSpace operation have to be divisible by square ' 'of DepthToSpace \'block_size\' parameter. Input tensor shape = {}. Feature dimension = {}. ' 'block_size = {}'.format(in_shape, C, block_size)) out_shape = shape_for_layout(layout, batch=N, features=C // (block_size * block_size), height=H * block_size, width=W * block_size) if is_fully_defined(in_shape) and is_fully_defined(out_shape) and np.prod(in_shape) != np.prod(out_shape): raise Error('Number of input elements "{}" is not equal to number of output elements "" for node "{}"' ''.format(in_shape, out_shape, node.soft_get('name', node.id))) node.out_port(0).data.set_shape(out_shape)
def _one_input_infer(node: Node): input_shape = node.in_port(0).data.get_shape() node_name = node.soft_get('name', node.id) if input_shape is None: raise Error('input_shape is none for {} node'.format(node_name)) if not node.has_valid('axis'): raise Error('axis attribute is missing for {} node. should be set in crop extractor'.format(node_name)) output_shape = input_shape.copy() if node.has_valid('dim'): if len(node.dim) != len(node.axis): raise Error('Number of axis "{}" should match number of dim "{}" for node "{}"' ''.format(node.axis, node.dim, node_name)) output_shape[node.axis] = node.dim elif node.has_valid('crop_begin') and node.has_valid('crop_end'): if len(node.crop_begin) != len(node.axis) or len(node.crop_end) != len(node.axis): raise Error('number of crop_begin({})/crop_end({}) should match number of axis "{}" for node "{}"' ''.format(node.crop_begin, node.crop_end, node.axis, node_name)) if type(node.axis) in [list, tuple]: for i in range(len(node.axis)): output_shape[node.axis[i]] = output_shape[node.axis[i]] - node.crop_begin[i] - node.crop_end[i] else: output_shape[node.axis] = output_shape[node.axis] - node.crop_begin - node.crop_end else: raise Error('Crop node {} should have either dim or crop_begin and crop_end attributes'.format(node_name)) node.out_port(0).data.set_shape(output_shape) PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')])
def shape_for_layout(layout: str, **kwargs): """ Creates 4D or 5D tensor with the layout with specified dimension sizes. :param layout: layout string. :param kwargs: dictionary that contains the dimension sizes using the following keys: 'batch', 'features', 'depth', 'height', 'width'. :return: shape_array of type np.int64 with 4 or 5 elements. """ assert layout in supported_layouts for required_key in ('batch', 'features', 'height', 'width'): if required_key not in kwargs: raise Error( 'Required parameter "{}" is missing.'.format(required_key)) for key in kwargs.keys(): if key not in ('batch', 'features', 'height', 'width', 'depth'): raise Error('Parameter "{}" is not supported.'.format(key)) depth = kwargs.get('depth', None) shape_len = 4 + (depth is not None) output_shape = np.ma.ones(shape=[shape_len], dtype=np.int64, fill_value=dynamic_dimension_value) output_shape[get_batch_dim(layout, shape_len)] = kwargs['batch'] output_shape[get_height_dim(layout, shape_len)] = kwargs['height'] output_shape[get_width_dim(layout, shape_len)] = kwargs['width'] output_shape[get_features_dim(layout, shape_len)] = kwargs['features'] if depth is not None: output_shape[get_depth_dim(layout, shape_len)] = depth return output_shape
def infer(node): if len(node.in_nodes()) <= 1: raise Error('There is no input with unsqueeze dims for the node {}'.format(node.soft_get('name'))) unsqueeze_dims = node.in_port(1).data.get_value() if unsqueeze_dims is None: raise Error('The dimensions to unsqueeze are not defined for the node {}'.format(node.soft_get('name'))) unsqueeze_dims = int64_array(unsqueeze_dims) input_value = node.in_port(0).data.get_value() input_shape = node.in_port(0).data.get_shape() # TODO remove the following line when the Inference Engine plugins support 0D tensors if unsqueeze_dims.ndim == 0: unsqueeze_dims = int64_array([unsqueeze_dims.item()]) # make dimensions positive to correctly translate from NHWC to NCHW layout unsqueeze_dims = int64_array([dim + len(node.in_port(0).data.get_shape()) + 1 if dim < 0 else dim for dim in unsqueeze_dims]) if node.in_port(1).get_source().node.op == 'Const': node.in_port(1).data.set_value(unsqueeze_dims) output_shape = input_shape.copy() for dim in unsqueeze_dims: output_shape = shape_insert(output_shape, dim, 1) if input_value is not None and is_fully_defined(output_shape): node.out_port(0).data.set_value(input_value.reshape(output_shape)) else: node.out_port(0).data.set_shape(output_shape) PermuteInputs().set_input_permutation(node.in_node(1), node, 'input:0', 'axis')
def extract(cls, node): pb = node.parameters mapping_rule = { 'context': list() } tag = find_next_tag(pb) if tag == '<LeftContext>': read_placeholder(pb, 1) l_context = read_binary_integer32_token(pb) tag = find_next_tag(pb) if tag != '<RightContext>': raise Error('Unknown token {} in SpliceComponent node {}'.format(tag, node.id)) read_placeholder(pb, 1) r_context = read_binary_integer32_token(pb) for i in range(-l_context, r_context + 1): mapping_rule['context'].append(i) elif tag == '<Context>': collect_until_whitespace(pb) mapping_rule['context'] = read_binary_vector(pb, False, dtype=np.int32) else: raise Error('Unknown token {} in SpliceComponent node {}'.format(tag, node.id)) tag = find_next_tag(pb) if tag == '<ConstComponentDim>': read_placeholder(pb, 1) const_dim = read_binary_integer32_token(pb) mapping_rule['const_dim'] = const_dim Splice.update_node_stat(node, mapping_rule) return cls.enabled
def infer(node: Node): node_name = node.soft_get('name', node.id) input_shape = node.in_port(0).data.get_shape() input_value = node.in_port(0).data.get_value() target_shape = node.in_port(1).data.get_value() assert target_shape is not None, 'Output shape is not defined for node "{}"'.format( node_name) assert node.has_and_set( 'mode'), 'Broadcasting mode is not defined for node "{}"'.format( node_name) PermuteInputs().set_input_permutation(node.in_node(1), node, 'output:0', 'shape') if input_value is not None and not node.has_and_set('stop_value_propagation') and \ is_fully_defined(target_shape): if node.mode == 'numpy': node.out_port(0).data.set_value( uni_directional_broadcasting(input_value, target_shape)) elif node.mode == 'bidirectional': node.out_port(0).data.set_value( bi_directional_broadcasting(input_value, target_shape)) elif node.mode == 'explicit': axes_mapping = node.in_port(2).data.get_value() assert axes_mapping is not None, 'Broadcast(mode="explicit") with dynamic axes_mapping input ' \ 'is not supported. Node: `{}`'.format(node_name) PermuteInputs().set_input_permutation(node.in_node(2), node, 'output:0', 'axis') axes_mapping = node.in_port(2).data.get_value() node.out_port(0).data.set_value( explicit_broadcasting(input_value, target_shape, axes_mapping)) else: raise Error('The node "{}" has unsupported mode "{}"'.format( node_name, node.mode)) else: if node.mode == 'numpy': node.out_port(0).data.set_shape( uni_directional_shape_broadcasting(input_shape, target_shape)) elif node.mode == 'bidirectional': node.out_port(0).data.set_shape( bi_directional_shape_broadcasting(input_shape, target_shape)) elif node.mode == 'explicit': axes_mapping = node.in_port(2).data.get_value() assert axes_mapping is not None, 'Broadcast(mode="explicit") with dynamic axes_mapping input ' \ 'is not supported. Node: `{}`'.format(node_name) PermuteInputs().set_input_permutation(node.in_node(2), node, 'output:0', 'axis') axes_mapping = node.in_port(2).data.get_value() new_shape, _ = explicit_shape_broadcasting( input_shape, target_shape, axes_mapping) node.out_port(0).data.set_shape(new_shape) else: raise Error('The node "{}" has unsupported mode "{}"'.format( node_name, node.mode))
def extract(cls, node): # some Dropout flavors doesn't have is_test attribute; when it is missing, interpret it as 1 is_test = onnx_attr(node, 'is_test', 'i', 1) if len(node.out_nodes()) > 1: raise Error('Dropout node {} has more than one consumer. Unsupported.', node.name) if not is_test: raise Error('Dropout node {} has is_test: 0. This means training mode which is not supported.', node.name) Identity.update_node_stat(node) return cls.enabled
def check_keys_valid(ov_function: Model, dict_to_validate: dict, search_outputs: bool): """ Internal function: checks if keys from cmd line arguments correspond to ov_function's inputs/outputs Throws if some key is not found Throws if some different keys point to the same actual input/output """ nodes_used = {} nodes = ov_function.inputs if search_outputs: nodes += ov_function.outputs # We need to replace all node names from dict to tensor names rename_dict = {} # Find names for replacing for name in dict_to_validate.keys(): for ov_node in nodes: if name in ov_node.get_tensor().get_names(): break elif name == ov_node.get_node().get_friendly_name(): assert len(ov_node.get_tensor().get_names() ) > 0, 'Node must have at least one tensor name' new_name = list(ov_node.get_tensor().get_names())[0] rename_dict[name] = new_name break # Replace found node names with tensor names for name, new_name in rename_dict.items(): assert name in dict_to_validate, 'Key {} is not in initial dict'.format( name) assert new_name not in dict_to_validate, 'Key {} is already in initial dict'.format( new_name) dict_to_validate[new_name] = dict_to_validate[name] del dict_to_validate[name] # validate the dict for name in dict_to_validate.keys(): node_found = False for ov_node in nodes: if name in ov_node.get_tensor().get_names(): if ov_node in nodes_used: raise Error( 'Key for {} and {} point to same model input/output.'. format(name, nodes_used[ov_node])) nodes_used[ov_node] = name node_found = True break if not node_found: if not search_outputs: raise Error('Input with name {} wasn\'t found! {}'.format( name, refer_to_faq_msg(83))) else: raise Error( 'Input/Output with name {} wasn\'t found! {}'.format( name, refer_to_faq_msg(83)))
def apply_biases_to_last_layer(graph, counts): r""" When user provides counts file, it is a file that contains log-apriory probabilities, technically it should be subtracted from the bias of the last layer unless it is a SoftMax. Case 1: weights ---\ biases ---\ some layer ---> AffineTransform ---> SoftMax Then, counts are applied to biases of Affine Transform: weights ---\ (biases - counts) ---\ some layer ---> AffineTransform ---> SoftMax Case 2: weights ---\ biases ---\ some layer ---> AffineTransform Just takes the last layer and updates biases: weights ---\ (biases - counts) ---\ some layer ---> AffineTransform Parameters ---------- graph counts Returns ------- """ "" outputs_ids = find_outputs(graph) for output in outputs_ids.copy(): node = Node(graph, output) if node.op != 'Assign' and node.op != "MemoryOffset": continue outputs_ids.remove(output) if len(outputs_ids) > 1: raise Error('Ambiguity in applying counts to several outputs.') elif len(outputs_ids) == 0: raise Error('No outputs were found') target_node = Node(graph, outputs_ids[0]) if target_node.op == 'SoftMax': target_node = target_node.in_port(0).get_source().node sub_node = create_op_node_with_second_input(graph, Add, -counts, {'name': 'sub_counts'}) target_node.out_port(0).get_connection().set_source(sub_node.out_port(0)) sub_node.in_port(0).connect(target_node.out_port(0))
def extract(cls, node: Node) -> bool: """ Extract conv parameters from node.parameters. node.parameters like file descriptor object. :param node: Convolution node :return: """ pb = node.parameters kernel = read_token_value(pb, b'<PatchDim>') stride = read_token_value(pb, b'<PatchStep>') patch_stride = read_token_value(pb, b'<PatchStride>') read_learning_info(pb) collect_until_whitespace(pb) weights, weights_shape = read_binary_matrix(pb) collect_until_whitespace(pb) biases = read_binary_vector(pb) if (patch_stride - kernel) % stride != 0: raise Error( 'Kernel size and stride does not correspond to `patch_stride` attribute of Convolution layer. ' + refer_to_faq_msg(93)) output = biases.shape[0] if weights_shape[0] != output: raise Error('Weights shape does not correspond to the `output` attribute of Convolution layer. ' + refer_to_faq_msg(93)) mapping_rule = { 'output': output, 'patch_stride': patch_stride, 'bias_term': None, 'pad': np.array([[0, 0], [0, 0], [0, 0], [0, 0]], dtype=np.int64), 'pad_spatial_shape': np.array([[0, 0], [0, 0]], dtype=np.int64), 'dilation': np.array([1, 1, 1, 1], dtype=np.int64), 'kernel': np.array([1, 1, 1, kernel], dtype=np.int64), 'stride': np.array([1, 1, 1, stride], dtype=np.int64), 'kernel_spatial': np.array([1, kernel], dtype=np.int64), 'input_feature_channel': 1, 'output_feature_channel': 0, 'kernel_spatial_idx': [2, 3], 'group': 1, 'reshape_kernel': True, } mapping_rule.update(layout_attrs()) embed_input(mapping_rule, 1, 'weights', weights) embed_input(mapping_rule, 2, 'biases', biases) mapping_rule['bias_addable'] = len(biases) > 0 Convolution.update_node_stat(node, mapping_rule) return cls.enabled
def extract(cls, node): onnx_opset_version = get_onnx_opset_version(node) if onnx_opset_version is not None and onnx_opset_version >= 9: mode = onnx_attr(node, 'mode', 's', default='nearest', dst_type=lambda x: x.decode()) ONNXResize10.update_node_stat(node, {'mode': mode}) else: mode = onnx_attr(node, 'mode', 's', default='nearest', dst_type=lambda x: x.decode()) scales = onnx_attr( node, 'scales', 'floats', dst_type=lambda x: np.array(x, dtype=np.float32)) width_scale = onnx_attr(node, 'width_scale', 'f') height_scale = onnx_attr(node, 'height_scale', 'f') supported_modes = ['nearest', 'linear'] if mode not in supported_modes: raise Error( 'Error decoding Upsample node {}, mode = {} is not in the list of supported modes {}.', node.name, mode, supported_modes) if scales is not None: if scales.shape != (4, ): raise Error( 'Upsample scales attribute is wrong for node {}. Only 4D scales are supported.', node.name) if math.fabs(scales[0] - 1) > 1e-5 or math.fabs(scales[1] - 1) > 1e-5: raise Error( 'Upsampling of batch and feature dimensions is not supported for node {}.', node.name) height_scale = scales[2] width_scale = scales[3] if (width_scale is None or height_scale is None) and len(node.in_nodes()) != 2: raise Error( 'One/both of widths_scale = {} and height_scale = {} is not defined for Upsample node {}.', width_scale, height_scale, node.name) UpsampleOp.update_node_stat( node, { 'mode': mode, 'height_scale': height_scale, 'width_scale': width_scale }) return cls.enabled
def _create_data_if_necessary(self): if self.node.graph.stage == 'front': raise Error("_create_data_if_necessary method is not applicable for front Graph phase!") if self.type == 'in': raise Error("_create_data_if_necessary method is not applicable for 'in' Port type!") if self.idx not in self.node.out_nodes(control_flow=self.control_flow): from openvino.tools.mo.ops.op import Op Op.create_data_node(self.node.graph, self.node, out_port=self.idx) self.node['need_shape_inference'] = True return self.node.out_node(self.idx, control_flow=self.control_flow)
def find_and_replace_pattern(self, graph: Graph): values = graph.graph['cmd_params'].mean_scale_values input_nodes = graph.get_op_nodes(op='Parameter') if not isinstance(values, dict): # The case when input names to apply mean/scales weren't specified if len(values) != len(input_nodes): raise Error('Numbers of inputs and mean/scale values do not match. ' + refer_to_faq_msg(61)) data = np.copy(values) values = {} for idx, node in enumerate(input_nodes): values.update( { node.soft_get('name', node.id): { 'mean': data[idx][0], 'scale': data[idx][1] } } ) for node_name, node_mean_scale_values in values.items(): node_id = None node_name = get_node_name_with_port_from_input_value(node_name) try: node_id, direction, port = get_node_id_with_ports(graph, node_name, skip_if_no_port=False) assert direction != 'out', 'Only input port can be specified for mean/scale application' except Error as e: log.warning('node_name {} is not found in graph'.format(node_name)) if Node(graph, node_id) not in input_nodes: # if the user cutted-off input of the network then input node name specified in the --scale_values # or --mean_values doesn't correspond to a real input node generated by Model Optimizer. But # the information about initial input node name is stored in Placeholder's attribute 'initial_node_name' new_node_id = None for placeholder in input_nodes: try: placeholder_port = int(placeholder.id.split("_")[-1]) except Exception as ex: log.debug('Can not get the port number from the node {}'.format(placeholder.id)) log.debug('Port will be defined as None') port = None if placeholder.has('initial_node_name') and placeholder.initial_node_name == node_id and ( port is None or placeholder_port == port): new_node_id = placeholder.id break if new_node_id is None: raise Error('Input with name {} wasn\'t found!'.format(node_name) + refer_to_faq_msg(83)) node_id = new_node_id input_node = Node(graph, node_id) AddMeanScaleValues.apply_scale(graph, input_node, node_mean_scale_values) AddMeanScaleValues.apply_mean_value(graph, input_node, node_mean_scale_values)
def infer(node: Node): node_name = node.soft_get('name', node.id) loc_shape = node.in_port(0).data.get_shape() conf_shape = node.in_port(1).data.get_shape() prior_boxes_shape = node.in_port(2).data.get_shape() if loc_shape is None or conf_shape is None or prior_boxes_shape is None: raise Error( 'Shapes for the Detection Output node "{}" are not defined'. format(node_name)) prior_size = 4 if node.has('normalized') and not node.normalized: prior_size = 5 if is_fully_defined(prior_boxes_shape[-1] ) and prior_boxes_shape[-1] % prior_size != 0: raise Error( 'Amount of confidences "{}" is not divisible by {} for node "{}"' ''.format(prior_boxes_shape[-1], prior_size, node_name)) num_priors = prior_boxes_shape[-1] // prior_size if not node.has_valid('keep_top_k') or node.keep_top_k == -1: node['keep_top_k'] = num_priors num_classes = conf_shape[-1] // num_priors num_loc_classes = num_classes if node.has_and_set('share_location') and node.share_location: num_loc_classes = 1 if not compatible_dims(num_priors * num_loc_classes * 4, loc_shape[-1]): raise Error( 'Locations and prior boxes shapes mismatch: "{}" vs "{}" for node "{}"' ''.format(loc_shape, prior_boxes_shape, node_name)) if not node.variance_encoded_in_target and not compatible_dims( prior_boxes_shape[-2], 2): raise Error( 'The "-2" dimension of the prior boxes must be 2 but it is "{}" for node "{}".' ''.format(prior_boxes_shape[-2], node_name)) if is_fully_defined(conf_shape[-1]) and is_fully_defined( num_priors) and conf_shape[-1] % num_priors != 0: raise Error( 'Amount of confidences "{}" is not divisible by amount of priors "{}" for node "{}".' ''.format(conf_shape[-1], num_priors, node_name)) node.out_port(0).data.set_shape( [1, 1, conf_shape[0] * node.keep_top_k, 7]) # the line below is needed for the TF framework so the MO will not change the layout node.graph.node[node.out_node(0).id]['nchw_layout'] = True
def infer(node: Node): real_squeeze_dims = int64_array([]) input_shape = node.in_port(0).data.get_shape() node_name = node.soft_get('name', node.id) if input_shape is None: raise Error( 'Input shape is not defined for node {}'.format(node_name)) output_shape = input_shape.copy() assert len(node.in_nodes( )) == 2, 'The Squeeze node {} must have 2 inputs'.format(node_name) # TODO remove the following 'if' statement when IE start support 0D tensors squeeze_dims = node.in_port(1).data.get_value() if squeeze_dims.ndim == 0: squeeze_dims = squeeze_dims.reshape([1]) for dim in squeeze_dims: if output_shape[dim] == 1 or output_shape[dim] is dynamic_dimension: real_squeeze_dims = np.ma.append( real_squeeze_dims, get_canonical_axis_index(output_shape, dim)) else: raise Error( 'Trying to squeeze dimension not equal to 1 for node "{}"'. format(node_name)) # if squeeze_dims empty then all 1s should be removed (tf specification of Squeeze op) if squeeze_dims.size == 0: for i in range(output_shape.size): if output_shape[i] == 1: real_squeeze_dims = np.ma.append( real_squeeze_dims, get_canonical_axis_index(output_shape, i)) assert is_fully_defined( real_squeeze_dims ), 'Squeeze dimension(s) is not defined for op "{}"'.format(node_name) output_shape = shape_delete(output_shape, real_squeeze_dims) node.out_port(0).data.set_shape(output_shape) # make dimensions positive to correctly translate from NHWC to NCHW layout if node.in_port(1).get_source().node.op == 'Const': node.in_port(1).data.set_value(real_squeeze_dims) if node.in_port(0).data.get_value() is not None: node.out_port(0).data.set_value( node.in_port(0).data.get_value().reshape(output_shape)) # the squeeze_dim attribute will be converted to the second input in the end of the Middle phase PermuteInputs().set_input_permutation(node.in_node(1), node, 'input:0', 'axis')
def build_net(graph: Graph): try: if not hasattr(os.environ, 'GLOG_minloglevel'): os.environ['GLOG_minloglevel'] = '2' import caffe log.info('Partial inference via the framework is available') except ImportError: log.warning( 'pyCaffe is not available. Partial inference via the framework is not ' + 'possible') return try: net = caffe.Net(graph.proto_path, graph.caffemodel_path, caffe.TEST) except Exception as err: raise Error( 'Error happened while constructing caffe.Net in the Caffe fallback function: {}. ' + refer_to_faq_msg(12), str(err)) from err inputs_node_name = find_inputs(graph) reshape_flag = False for i in inputs_node_name: new_input_shape = graph.node[i]['shape'].astype(int) top_node = get_node_top(graph, i) caffe_shape = list(net.blobs[top_node].shape) if not np.all(caffe_shape == new_input_shape): net.blobs[top_node].reshape(*[int(x) for x in new_input_shape]) reshape_flag = True if reshape_flag: net.reshape() try: net.forward() except KeyError as err: log.error('Error happened in Caffe net.forward: {}.'.format(str(err))) log.error( 'It may point to the known bug in pycaffe when top and name of the layer do not match.' ) log.error('Please make sure that the latest pycaffe is used.') raise Error( 'Cannot infer shapes due to exception in Caffe: {}. ' + refer_to_faq_msg(13), str(err)) from err except Exception as err: raise Error( 'Cannot infer shapes in Caffe net.forward due to exception: {}.' + refer_to_faq_msg(13), str(err)) from err graph.__setattr__('caffe_net', net)
def check_and_update_ports(node, edges_data: list, in_port: bool = True): key = 'in' if in_port else 'out' key_in_edges = [key in edge_data for edge_data in edges_data] if all(key_in_edges): ports = [edge_data[key] for edge_data in edges_data] if len(ports) != len(set(ports)): raise Error("Please, provide unique {} ports for nodes".format(key)) elif not any(key_in_edges): if node.has_valid('kind') and node.kind == 'data': return for i, edge_data in enumerate(edges_data): edge_data[key] = i else: raise Error("Please, provide all {} ports for nodes".format(key))
def parse_mean(file_path: str, in_shape: np.ndarray, mean_file_offsets: [tuple, None], caffe_pb2): blob = caffe_pb2.BlobProto() with open(file_path, 'rb') as file: data = file.read() if not data: raise Error('Mean file "{}" is empty.' + refer_to_faq_msg(5), file_path) try: blob.ParseFromString(data) data = np.array(blob.data) # pylint: disable=no-member if blob.HasField('channels') or blob.HasField('height') or blob.HasField('width'): data = data.reshape(blob.channels, blob.height, blob.width) # pylint: disable=no-member else: data = data.reshape(blob.shape.dim) # pylint: disable=no-member # crop mean image according to input size if in_shape[2] > data.shape[1] or in_shape[3] > data.shape[2]: raise Error( 'Input image of shape {} is larger than mean image {} from file "{}". ' + refer_to_faq_msg(4), in_shape, data.shape, file_path ) if mean_file_offsets is not None and len(mean_file_offsets) == 2: offset_x = mean_file_offsets[0] offset_y = mean_file_offsets[1] else: offset_x = int((data.shape[1] - in_shape[2]) / 2) offset_y = int((data.shape[2] - in_shape[3]) / 2) mean = [] for i in range(in_shape[1]): data_channel = np.zeros(in_shape[2] * in_shape[3], dtype=np.float32) for x in range(in_shape[2]): for y in range(in_shape[3]): data_channel[x * in_shape[3] + y] = data[i, x + offset_x, y + offset_y] mean.append(data_channel) return mean except Exception as err: raise Error( 'While processing mean file "{}": {}. Probably mean file has incorrect format. ' + refer_to_faq_msg(6), file_path, str(err)) from err
def replace_sub_graph(self, graph: Graph, match: dict): ph = match['placeholder'] if ph.name in graph.graph['freeze_placeholder']: name = ph.name if ph.has_and_set('data_type'): data_type = ph.data_type else: data_type = SUPPORTED_DATA_TYPES[ graph.graph['cmd_params'].data_type][0] string_value = graph.graph['freeze_placeholder'][name] try: if data_type != np.bool: value = mo_array(string_value, dtype=data_type) # TODO: investigate why boolean type is allowed only for TensorFlow elif data_type == np.bool and graph.graph['fw'] == 'tf': from openvino.tools.mo.front.tf.common import tf_data_type_cast if isinstance(string_value, list): casted_list = list() for v in mo_array(string_value): casted_list.append( tf_data_type_cast[ph.data_type](v)) value = mo_array(string_value, dtype=data_type) else: value = tf_data_type_cast[ph.data_type](string_value) else: raise Error("Cannot cast value {} to {} data_type".format( string_value, data_type)) except: raise Error("Cannot cast value {} to {} data_type".format( string_value, data_type)) try: value = np.reshape(a=value, newshape=ph.shape) except: raise Error("Can not reshape value {} to shape {}".format( value, ph.shape)) out_edges = list(graph.out_edges(ph.id, data=True)) new_node = Const(graph).create_node( attrs={ 'value': value, 'data_type': type(value), 'name': name + '/const_placeholder', 'shape': ph.shape }) graph.erase_node(ph) graph.add_edges_from([(new_node.id, v, attrs) for u, v, attrs in out_edges]) log.info( "Placeholder node \"{}\" was replaced with Const node \"{}\" with value \"{}\"" .format(name, new_node.name, value))