def build_quant_conv_layer( self, W=None, quantization_type="linear", nbits=8, quant_scale=None, quant_bias=None, quant_lut=None, ): input_features = [("data", datatypes.Array(1, 2, 2))] output_features = [("out", datatypes.Array(2, 1, 1))] builder = NeuralNetworkBuilder(input_features, output_features) builder.add_convolution( name="conv", kernel_channels=1, output_channels=2, height=2, width=2, stride_height=1, stride_width=1, border_mode="valid", groups=1, W=W, b=None, has_bias=False, input_name="data", output_name="out", quantization_type=quantization_type, nbits=nbits, quant_scale=quant_scale, quant_bias=quant_bias, quant_lut=quant_lut, ) return MLModel(builder.spec)
def test_convolution_converter(self): input_dim = (1, 1, 4, 2) output_dim = (1, 1, 4, 2) input = [('input', datatypes.Array(*input_dim))] output = [('output', datatypes.Array(*output_dim))] builder = NeuralNetworkBuilder(input, output) weights = numpy.zeros(shape=(1, 1, 2, 2)) weights[:] = [[1, 1], [-1, -1]] bias = numpy.zeros(shape=(1, )) bias[:] = 100 builder.add_convolution(name='Conv', kernel_channels=1, output_channels=1, height=2, width=2, stride_height=1, stride_width=1, border_mode='same', groups=1, W=weights, b=bias, has_bias=True, input_name='input', output_name='output', is_deconv=True, output_shape=(1, 1, 4, 2)) model_onnx = convert_coreml(builder.spec) self.assertTrue(model_onnx is not None)
def verify_convolution(input_dim, filter, padding): dtype = "float32" N, C, H, W = input_dim OC, _, KH, KW = filter a_np = np.random.uniform(size=input_dim).astype(dtype) w_np = np.random.uniform(size=(OC, C, KH, KW)).astype(dtype) w_np_cm = np.transpose(w_np, axes=(2, 3, 1, 0)) b_np = conv2d_nchw_python(a_np, w_np, [1, 1], padding) inputs = [("input1", datatypes.Array(C, H, W))] output = [("output", datatypes.Array(*b_np.shape))] builder = NeuralNetworkBuilder(inputs, output) builder.add_convolution( name="conv", kernel_channels=3, output_channels=OC, height=KH, width=KW, stride_height=1, stride_width=1, border_mode=padding.lower(), groups=1, W=w_np_cm, b=None, has_bias=False, is_deconv=False, input_name="input1", output_name="output", ) model = cm.models.MLModel(builder.spec) for target, dev in tvm.testing.enabled_targets(): out = run_tvm_graph(model, target, dev, [a_np], ["input1"], output_shape=None) tvm.testing.assert_allclose(out, b_np, rtol=1e-5)
def make_mlmodel(variables): # Specify the inputs and outputs (there can be multiple). # Each name corresponds to the input_name/output_name of a layer in the network so # that Core ML knows where to insert and extract data. input_features = [('image', datatypes.Array(1, IMAGE_HEIGHT, IMAGE_WIDTH))] output_features = [('labelValues', datatypes.Array(NUM_LABEL_INDEXES))] builder = NeuralNetworkBuilder(input_features, output_features, mode=None) # The "name" parameter has no effect on the function of the network. As far as I know # it's only used when Xcode fails to load your mlmodel and gives you an error telling # you what the problem is. # The input_names and output_name are used to link layers to each other and to the # inputs and outputs of the model. When adding or removing layers, or renaming their # outputs, always make sure you correct the input and output names of the layers # before and after them. builder.add_elementwise(name='add_layer', input_names=['image'], output_name='add_layer', mode='ADD', alpha=-0.5) # Although Core ML internally uses weight matrices of shape # (outputChannels, inputChannels, height, width) (as can be found by looking at the # protobuf specification comments), add_convolution takes the shape # (height, width, inputChannels, outputChannels) (as can be found in the coremltools # documentation). The latter shape matches what TensorFlow uses so we don't need to # reorder the matrix axes ourselves. builder.add_convolution(name='conv2d_1', kernel_channels=1, output_channels=32, height=3, width=3, stride_height=1, stride_width=1, border_mode='same', groups=0, W=variables['W_conv1'].eval(), b=variables['b_conv1'].eval(), has_bias=True, is_deconv=False, output_shape=None, input_name='add_layer', output_name='conv2d_1') builder.add_activation(name='relu_1', non_linearity='RELU', input_name='conv2d_1', output_name='relu_1', params=None) builder.add_pooling(name='maxpool_1', height=2, width=2, stride_height=2, stride_width=2, layer_type='MAX', padding_type='SAME', input_name='relu_1', output_name='maxpool_1') # ... builder.add_flatten(name='maxpool_3_flat', mode=1, input_name='maxpool_3', output_name='maxpool_3_flat') # We must swap the axes of the weight matrix because add_inner_product takes the shape # (outputChannels, inputChannels) whereas TensorFlow uses # (inputChannels, outputChannels). Unlike with add_convolution (see the comment # above), the shape add_inner_product expects matches what the protobuf specification # requires for inner products. builder.add_inner_product(name='fc1', W=tf_fc_weights_order_to_mlmodel( variables['W_fc1'].eval()).flatten(), b=variables['b_fc1'].eval().flatten(), input_channels=6 * 6 * 64, output_channels=1024, has_bias=True, input_name='maxpool_3_flat', output_name='fc1') # ... builder.add_softmax(name='softmax', input_name='fc2', output_name='labelValues') model = MLModel(builder.spec) model.short_description = 'Model for recognizing a variety of images drawn on screen with one\'s finger' model.input_description['image'] = 'A gesture image to classify' model.output_description[ 'labelValues'] = 'The "probability" of each label, in a dense array' return model