def testSeparableConvQuantize_(self, kwargs): kwargs['filters'] = 2 kwargs['kernel_size'] = 3 num_samples = 2 stack_size = 3 num_row = 7 num_col = 6 sepconv_model = tf.keras.Sequential([ tf.keras.Input(shape=(num_row, num_col, stack_size), batch_size=num_samples), tf.keras.layers.SeparableConv2D(**kwargs) ]) transformed_model, updated_metadata = ModelTransformer( sepconv_model, [default_8bit_transforms.SeparableConvQuantize()], ).transform() self.assertContainsSubset(updated_metadata.keys(), {'depthwise_conv2d', 'conv2d'}) # Transformed model should have the same output shape self.assertEqual(sepconv_model.output_shape, transformed_model.output_shape) x = np.random.rand(*sepconv_model.input_shape) y = np.random.rand(*sepconv_model.output_shape) # Ensure model is equivalent, and forward pass results are the same. self.assertAllClose(sepconv_model.predict(x), transformed_model.predict(x)) # Ensure model is equivalent, and training results are the same. sepconv_model.compile(loss='categorical_crossentropy', optimizer='sgd') transformed_model.compile(loss='categorical_crossentropy', optimizer='sgd') epochs = 100 for _ in range(epochs): sepconv_model.fit(x, y, epochs=1, verbose=2) transformed_model.fit(x, y, epochs=1, verbose=2) self.assertAllClose(sepconv_model.get_weights(), transformed_model.get_weights()) # To prevent accumulated numerical errors. transformed_model.set_weights(sepconv_model.get_weights()) self.assertAllClose(sepconv_model.predict(x), transformed_model.predict(x))
def testSeparableConvQuantize_(self, kwargs): kwargs['filters'] = 2 kwargs['kernel_size'] = 3 num_samples = 2 stack_size = 3 num_row = 7 num_col = 6 sepconv_model = tf.keras.Sequential([ tf.keras.Input(shape=(num_row, num_col, stack_size), batch_size=num_samples), tf.keras.layers.SeparableConv2D(**kwargs) ]) transformed_model, updated_metadata = ModelTransformer( sepconv_model, [default_8bit_transforms.SeparableConvQuantize()], ).transform() self.assertContainsSubset(updated_metadata.keys(), {'depthwise_conv2d', 'conv2d'}) # Transformed model should have the same output shape self.assertEqual(sepconv_model.output_shape, transformed_model.output_shape) x = np.random.rand(*sepconv_model.input_shape) y = np.random.rand(*sepconv_model.output_shape) # Ensure model is equivalent, and forward pass results are the same. self.assertAllClose(sepconv_model.predict(x), transformed_model.predict(x)) # Ensure model is equivalent, and training results are the same. sepconv_model.compile(loss='categorical_crossentropy', optimizer='sgd') sepconv_model.fit(x, y, epochs=100) transformed_model.compile(loss='categorical_crossentropy', optimizer='sgd') transformed_model.fit(x, y, epochs=100) # Over a long training cycle with constraints and regularizers, the model # can build very minute differences. Hence reducing tol to 1e-5. self.assertAllClose(sepconv_model.predict(x), transformed_model.predict(x), atol=1e-5, rtol=1e-5)
def apply(self, model, layer_quantize_map): """Implement default 8-bit transforms. Currently this means the following. 1. Pull activations into layers, and apply fuse activations. (TODO) 2. Modify range in incoming layers for Concat. (TODO) 3. Fuse Conv2D/DepthwiseConv2D + BN into single layer. Args: model: Keras model to be quantized. layer_quantize_map: Map with keys as layer names, and values as dicts containing custom `QuantizeConfig`s which may have been passed with layers. Returns: (Transformed Keras model to better match TensorFlow Lite backend, updated layer quantize map.) """ transforms = [ default_8bit_transforms.InputLayerQuantize(), default_8bit_transforms.SeparableConv1DQuantize(), default_8bit_transforms.SeparableConvQuantize(), default_8bit_transforms.Conv2DReshapeBatchNormReLUQuantize(), default_8bit_transforms.Conv2DReshapeBatchNormActivationQuantize(), default_8bit_transforms.Conv2DBatchNormReLUQuantize(), default_8bit_transforms.Conv2DBatchNormActivationQuantize(), default_8bit_transforms.Conv2DReshapeBatchNormQuantize(), default_8bit_transforms.Conv2DBatchNormQuantize(), default_8bit_transforms.ConcatTransform6Inputs(), default_8bit_transforms.ConcatTransform5Inputs(), default_8bit_transforms.ConcatTransform4Inputs(), default_8bit_transforms.ConcatTransform3Inputs(), default_8bit_transforms.ConcatTransform(), default_8bit_transforms.AddReLUQuantize(), default_8bit_transforms.AddActivationQuantize(), OptFlowQuantize(), FlowQuantize() ] return model_transformer.ModelTransformer( model, transforms, set(layer_quantize_map.keys()), layer_quantize_map).transform()