aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/contrib/quantize
diff options
context:
space:
mode:
authorGravatar Suharsh Sivakumar <suharshs@google.com>2018-07-06 13:08:50 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-07-06 13:11:59 -0700
commitff06d6783078c205610d21cdea1d740ca168faa3 (patch)
tree7f54e9aee2eaa5b137cae9eb6770ec3b0fccfec6 /tensorflow/contrib/quantize
parent0e616c112249eae4a2981b06ef8627cf41d6c0b1 (diff)
Support extra identity operation added if batch_norms updates are forced.
PiperOrigin-RevId: 203515353
Diffstat (limited to 'tensorflow/contrib/quantize')
-rw-r--r--tensorflow/contrib/quantize/python/quantize.py13
-rw-r--r--tensorflow/contrib/quantize/python/quantize_parameterized_test.py76
2 files changed, 85 insertions, 4 deletions
diff --git a/tensorflow/contrib/quantize/python/quantize.py b/tensorflow/contrib/quantize/python/quantize.py
index 19e5bef1ea..4fc315d901 100644
--- a/tensorflow/contrib/quantize/python/quantize.py
+++ b/tensorflow/contrib/quantize/python/quantize.py
@@ -278,6 +278,13 @@ def _FindLayersToQuantize(graph):
],
ordered_inputs=False)
+ # batch_norms with forced updates have an Identity operation at the end.
+ # TODO(suharshs): Find a way to easily skip extra Identity operations. The
+ # current issue is that doing so can often match patterns across many layers
+ # incorrectly.
+ batch_norm_identity = graph_matcher.OpTypePattern(
+ 'Identity', inputs=[folded_bias_add_pattern])
+
bias_add_pattern = graph_matcher.OpTypePattern(
'Add|BiasAdd', inputs=[layer_output_pattern, '*'], ordered_inputs=False)
@@ -286,20 +293,22 @@ def _FindLayersToQuantize(graph):
'Add',
inputs=[
graph_matcher.OneofPattern(
- [bias_add_pattern, folded_bias_add_pattern]), '*'
+ [bias_add_pattern, folded_bias_add_pattern, batch_norm_identity]),
+ '*'
],
ordered_inputs=False)
# The input to the activation can come from bias add, fold bias add, the
# bypasses.
# TODO(suharshs): We should ideally skip Identity operations instead of
- # treating them as an activation.
+ # treating them as activations.
activation_pattern = graph_matcher.OpTypePattern(
'|'.join(_ACTIVATION_TYPES) + '|Identity',
inputs=[
graph_matcher.OneofPattern([
bias_add_pattern,
folded_bias_add_pattern,
+ batch_norm_identity,
bypass_pattern,
])
])
diff --git a/tensorflow/contrib/quantize/python/quantize_parameterized_test.py b/tensorflow/contrib/quantize/python/quantize_parameterized_test.py
index 5e3af0a567..31a2955ddb 100644
--- a/tensorflow/contrib/quantize/python/quantize_parameterized_test.py
+++ b/tensorflow/contrib/quantize/python/quantize_parameterized_test.py
@@ -654,8 +654,80 @@ class QuantizeTest(test_util.TensorFlowTestCase):
graph_def_after = str(graph.as_graph_def())
self.assertEqual(graph_def_before, graph_def_after)
- def _BatchNormParams(self, fused=False):
- return {'center': True, 'scale': True, 'decay': 1.0 - 0.003, 'fused': fused}
+ def testBatchNormForcedUpdates(self):
+ parameter_list = [
+ # (activation, activation_op_name, fused_batch_norm)
+ (nn_ops.relu6, 'Relu6', False),
+ (nn_ops.relu, 'Relu', False),
+ (array_ops.identity, 'Identity', False),
+ (nn_ops.relu6, 'Relu6', True),
+ (nn_ops.relu, 'Relu', True),
+ (array_ops.identity, 'Identity', True),
+ ]
+ for params in parameter_list:
+ self._TestBatchNormForcedUpdates(params[0], params[1], params[2], False)
+ self._TestBatchNormForcedUpdates(params[0], params[1], params[2], True)
+
+ def _TestBatchNormForcedUpdates(self, activation, activation_op_name,
+ fused_batch_norm, use_resource):
+ """post_activation bypass quantization should happen with forced updates."""
+ graph = ops.Graph()
+ with graph.as_default():
+ variable_scope.get_variable_scope().set_use_resource(use_resource)
+ batch_size, height, width, depth = 5, 128, 128, 3
+ input1 = array_ops.zeros((batch_size, height, width, depth))
+ input2 = array_ops.zeros((batch_size, height / 2, width / 2, 32))
+ # Setting updates_collections to None forces updates adding an extra
+ # identity operation following batch norms.
+ bn_params = self._BatchNormParams(
+ fused=fused_batch_norm, force_updates=True)
+ conv = conv2d(
+ input1,
+ 32, [5, 5],
+ stride=2,
+ padding='SAME',
+ weights_initializer=self._WeightInit(0.09),
+ activation_fn=activation,
+ normalizer_fn=batch_norm,
+ normalizer_params=bn_params,
+ scope='test/test')
+ bypass_tensor = math_ops.add(conv, input2, name='test/add')
+ # The output of the post_activation bypass will be another layer.
+ _ = conv2d(
+ bypass_tensor,
+ 32, [5, 5],
+ stride=2,
+ padding='SAME',
+ weights_initializer=self._WeightInit(0.09),
+ normalizer_fn=batch_norm,
+ normalizer_params=bn_params,
+ activation_fn=activation,
+ scope='test/unused')
+
+ fold_batch_norms.FoldBatchNorms(graph, is_training=True)
+ quantize.Quantize(graph, is_training=True)
+
+ # Ensure that the bypass node is preceded by and followed by a
+ # FakeQuantWithMinMaxVar operation, since the output of the Add isn't an
+ # activation.
+ self.assertTrue('FakeQuantWithMinMaxVars' in
+ [c.type for c in bypass_tensor.consumers()])
+ self.assertTrue('FakeQuantWithMinMaxVars' in
+ [i.op.type for i in bypass_tensor.op.inputs])
+
+ with open('/tmp/bn_quant_test.pbtxt', 'w') as f:
+ f.write(str(graph.as_graph_def()))
+
+ def _BatchNormParams(self, fused=False, force_updates=False):
+ params = {
+ 'center': True,
+ 'scale': True,
+ 'decay': 1.0 - 0.003,
+ 'fused': fused
+ }
+ if force_updates:
+ params['updates_collections'] = None
+ return params
def _WeightInit(self, stddev):
"""Returns truncated normal variable initializer.