def _convert_concat_table(builder, name, layer, input_names, output_names): layers = layer.modules result_outputs = [] for l in layers: l_name = _gen_layer_name(l) l_outputs = _convert_layer(builder, l_name, l, input_names, [l_name]) result_outputs += l_outputs return result_outputs
def _convert_full_convolution(builder, name, layer, input_names, output_names): input_name = input_names[0] output_name = output_names[0] k_h, k_w = layer.kH, layer.kW pad_h, pad_w = layer.padH, layer.padW weight = layer.weight.numpy().transpose((2, 3, 0, 1)) bias = None if layer.bias is not None: bias = layer.bias.numpy() add_crop = False output_ = layer.output.numpy() output_shape = ( output_.shape[-2] + 2 * pad_h, output_.shape[-1] + 2 * pad_w ) if pad_h > 0 or pad_w > 0: crop_padding_name = _gen_layer_name('padding') output_name = name + '_output' add_crop = True builder.add_convolution( name=name, kernel_channels=layer.nInputPlane, output_channels=layer.nOutputPlane, height=k_h, width=k_w, stride_height=layer.dH, stride_width=layer.dW, border_mode='valid', groups=1, W=weight, b=bias, has_bias=bias is not None, is_deconv=True, output_shape=output_shape, input_name=input_name, output_name=output_name, dilation_factors=[1, 1] ) if add_crop: builder.add_crop( name=crop_padding_name, left=pad_w, right=pad_w, top=pad_h, bottom=pad_h, offset=0, input_names=[output_name], output_name=output_names[0] ) return output_names
def _convert_parallel_table(builder, name, layer, input_names, output_names): layers = layer.modules assert len(input_names) == len(layers) result_outputs = [] for i in range(len(layers)): l_ = layers[i] l_name = _gen_layer_name(l_) l_outputs = _convert_layer(builder, l_name, l_, [input_names[i]], [l_name]) result_outputs.append(l_outputs[0]) return result_outputs
def _convert_cdiv_table(builder, name, layer, input_names, output_names): assert len(input_names) == 2 assert len(output_names) == 1 inverse_layer_name = _gen_layer_name('inverse') inverse_layer_output_name = inverse_layer_name + '_output' builder.add_unary(name=inverse_layer_name, input_name=input_names[1], output_name=inverse_layer_output_name, mode='inverse') builder.add_elementwise( name=name, input_names=[input_names[0], inverse_layer_output_name], output_name=output_names[0], mode='MULTIPLY') return output_names
def _convert_sequential(builder, name, layer, input_names, output_names): layers = layer.modules n = len(layers) inputs = input_names for i in range(n): l = layers[i] l_outputs = None l_name = _gen_layer_name(l) if i != (n - 1): l_outputs = [l_name] else: l_outputs = output_names l_outputs = _convert_layer(builder, l_name, l, inputs, l_outputs) inputs = l_outputs return output_names
def _convert_sequential(builder, name, layer, input_names, output_names): layers = layer.modules n = len(layers) inputs = input_names for i in range(n): l_ = layers[i] l_outputs = None l_name = _gen_layer_name(l_) if i != (n - 1): if isinstance(l_.output, list): l_outputs = [ "{}_{}".format(l_name, i) for i in range(len(l_.output)) ] else: l_outputs = [l_name] else: l_outputs = output_names l_outputs = _convert_layer(builder, l_name, l_, inputs, l_outputs) inputs = l_outputs return output_names
def convert(model, input_shapes, input_names=['input'], output_names=['output'], mode=None, image_input_names=[], preprocessing_args={}, image_output_names=[], deprocessing_args={}, class_labels=None, predicted_feature_name='classLabel', unknown_layer_converter_fn=None): """ Convert Torch7 model to CoreML. Parameters ---------- model: Torch7 model (loaded with PyTorch) | str A trained Torch7 model loaded in python using PyTorch or path to file with model (*.t7). input_shapes: list of tuples Shapes of the input tensors. mode: str ('classifier', 'regressor' or None) Mode of the converted coreml model: 'classifier', a NeuralNetworkClassifier spec will be constructed. 'regressor', a NeuralNetworkRegressor spec will be constructed. preprocessing_args: dict 'is_bgr', 'red_bias', 'green_bias', 'blue_bias', 'gray_bias', 'image_scale' keys with the same meaning as https://apple.github.io/coremltools/generated/coremltools.models.neural_network.html#coremltools.models.neural_network.NeuralNetworkBuilder.set_pre_processing_parameters deprocessing_args: dict Same as 'preprocessing_args' but for deprocessing. class_labels: A string or list of strings. As a string it represents the name of the file which contains the classification labels (one per line). As a list of strings it represents a list of categories that map the index of the output of a neural network to labels in a classifier. predicted_feature_name: str Name of the output feature for the class labels exposed in the Core ML model (applies to classifiers only). Defaults to 'classLabel' unknown_layer_converter_fn: function with signature: (builder, name, layer, input_names, output_names) builder: object - instance of NeuralNetworkBuilder class name: str - generated layer name layer: object - pytorch object for corresponding layer input_names: list of strings output_names: list of strings Returns: list of strings for layer output names Callback function to handle unknown for torch2coreml layers Returns ------- model: A coreml model. """ _gen_layer_name.called = 0 _get_layer_converter_fn.unknown_converter_fn = unknown_layer_converter_fn if isinstance(model, basestring): torch_model = load_lua(model) elif isinstance(model, torch.legacy.nn.Sequential): torch_model = model else: raise TypeError( "Model must be file path to .t7 file or pytorch loaded model \ with torch.legacy.nn.Sequential module as root") torch_model.evaluate() if not isinstance(input_shapes, list): raise TypeError("Input shapes should be a list of tuples.") for shape in input_shapes: if not isinstance(shape, tuple): raise TypeError("Input shape should be a tuple.") if len(input_names) != len(input_shapes): raise ValueError( "Input names count must be equal to input shapes count") output_shapes = _infer_torch_output_shapes(torch_model, input_shapes) if len(output_shapes) != len(output_names): raise ValueError( "Model has {} outputs, but you set output_names for {}.".format( len(output_shapes), len(output_names))) # create input/output features input_features = [] for i in range(len(input_names)): input_features.append( (input_names[i], datatypes.Array(*input_shapes[i]))) output_features = [] for i in range(len(output_names)): output_features.append( (output_names[i], datatypes.Array(*output_shapes[i]))) builder = NeuralNetworkBuilder(input_features, output_features, mode) # build model layer_name = _gen_layer_name(torch_model) _output_names = output_names[:] if len(image_output_names) > 0: for i in range(len(_output_names)): if _output_names[i] in image_output_names: _output_names[i] = _gen_layer_name(_DEPROCESS_LAYER_NAME) model_output_names = _layers._convert_layer(builder, layer_name, torch_model, input_names, _output_names) # set preprocessing parameters if len(image_input_names) > 0: builder.set_pre_processing_parameters( image_input_names=image_input_names, is_bgr=preprocessing_args.get('is_bgr', False), red_bias=preprocessing_args.get('red_bias', 0.0), green_bias=preprocessing_args.get('green_bias', 0.0), blue_bias=preprocessing_args.get('blue_bias', 0.0), gray_bias=preprocessing_args.get('gray_bias', 0.0), image_scale=preprocessing_args.get('image_scale', 1.0)) # set deprocessing parameters if len(image_output_names) > 0: for i in range(len(output_names)): output_name = output_names[i] if output_name in image_output_names: output_shape = output_shapes[i] if len(output_shape) == 2 or output_shape[0] == 1: is_grayscale = True elif output_shape[0] == 3: is_grayscale = False else: raise ValueError('Output must be RGB image or Grayscale') _set_deprocessing(is_grayscale, builder, deprocessing_args, model_output_names[i], output_name) if class_labels is not None: if type(class_labels) is str: labels = [l.strip() for l in open(class_labels).readlines()] elif type(class_labels) is list: labels = class_labels else: raise TypeError("synset variable of unknown type. Type found: {}. \ Expected either string or list of strings.".format( type(class_labels), )) builder.set_class_labels(class_labels=labels, predicted_feature_name=predicted_feature_name) return MLModel(builder.spec)