blob: c54969c144225cc5b65c6d25d43deb89071fe717 [file] [log] [blame]
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for fused_batch_norm related functionality in tensorflow.ops.nn."""
import numpy as np
from tensorflow.python.eager import context
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gen_nn_ops
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import nn_grad
from tensorflow.python.ops import nn_impl
from tensorflow.python.ops import nn_ops
from tensorflow.python.platform import test
class BatchNormalizationTest(test.TestCase):
def _batch_norm(self, x, mean, var, offset, scale, epsilon):
# We compute the batch norm manually in this function because
# nn_impl.batch_normalization does not support float16 yet.
# TODO(reedwm): Add float16 support to nn_impl.batch_normalization.
inv = math_ops.rsqrt(var + epsilon) * scale
y = math_ops.cast(x, scale.dtype) * inv + (offset - mean * inv)
return math_ops.cast(y, x.dtype)
def _inference_ref(self, x, scale, offset, mean, var, epsilon, data_format):
if data_format not in ['NHWC', 'NCHW', 'NDHWC', 'NCDHW']:
raise ValueError('data_format must be NCHW or NHWC for 4D tensors or'
'NCDHW or NDHWC for 5D tensors, got %s.' % data_format)
if data_format == 'NCHW':
x = array_ops.transpose(x, [0, 2, 3, 1])
elif data_format == 'NCDHW':
x = array_ops.transpose(x, [0, 2, 3, 4, 1])
y = self._batch_norm(x, mean, var, offset, scale, epsilon)
if data_format == 'NCHW':
y = array_ops.transpose(y, [0, 3, 1, 2])
elif data_format == 'NCDHW':
y = array_ops.transpose(y, [0, 4, 1, 2, 3])
return self.evaluate(y)
def _test_inference(self,
x_shape,
x_dtype,
scale_shape,
scale_dtype,
use_gpu=True,
exponential_avg_factor=1.0,
data_format='NHWC'):
np.random.seed(1)
x_val = np.random.random_sample(x_shape).astype(x_dtype)
scale_val = np.random.random_sample(scale_shape).astype(scale_dtype)
offset_val = np.random.random_sample(scale_shape).astype(scale_dtype)
mean_val = np.random.random_sample(scale_shape).astype(scale_dtype)
var_val = np.random.random_sample(scale_shape).astype(scale_dtype)
with self.cached_session(use_gpu=use_gpu) as sess:
x = constant_op.constant(x_val, name='x')
scale = constant_op.constant(scale_val, name='scale')
offset = constant_op.constant(offset_val, name='offset')
mean = constant_op.constant(mean_val, name='mean')
var = constant_op.constant(var_val, name='variance')
epsilon = 0.001
y, _, _ = nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=mean,
variance=var,
epsilon=epsilon,
exponential_avg_factor=exponential_avg_factor,
data_format=data_format,
is_training=False)
y_val = self.evaluate(y)
y_ref = self._inference_ref(x, scale, offset, mean, var, epsilon,
data_format)
# An atol value of 1e-3 is too small for float16's, because some adjacent
# float16 values that y_val can take are greater than 1e-3 apart, e.g.
# 2.16602 and 2.16797.
atol = 2e-3 if x_dtype in [np.float16, dtypes.bfloat16.as_numpy_dtype
] else 1e-3
self.assertAllClose(y_ref, y_val, atol=atol)
def _running_mean(self, old_mean, new_val, factor):
if factor == 1.0:
return new_val
else:
return (1.0 - factor) * old_mean + factor * new_val
def _training_ref(self, x, scale, offset, old_mean, old_var,
exponential_avg_factor, epsilon, data_format):
if data_format not in ['NHWC', 'NCHW', 'NDHWC', 'NCDHW']:
raise ValueError('data_format must be NCHW or NHWC for 4D tensors or'
'NCDHW or NDHWC for 5D tensors, got %s.' % data_format)
use_4d_tensor = (x.shape.ndims == 4)
if data_format == 'NCHW':
x = array_ops.transpose(x, [0, 2, 3, 1])
elif data_format == 'NCDHW':
x = array_ops.transpose(x, [0, 2, 3, 4, 1])
mean_axis = [0, 1, 2] if use_4d_tensor else [0, 1, 2, 3]
batch_mean, batch_var = nn_impl.moments(
math_ops.cast(x, scale.dtype), mean_axis, keep_dims=False)
y = self._batch_norm(x, batch_mean, batch_var, offset, scale, epsilon)
if data_format == 'NCHW':
y = array_ops.transpose(y, [0, 3, 1, 2])
elif data_format == 'NCDHW':
y = array_ops.transpose(y, [0, 4, 1, 2, 3])
# This is for Bessel's correction. tf.nn.moments uses n, instead of n-1, as
# the denominator in the formula to calculate variance, while
# tf.compat.v1.nn.fused_batch_norm has Bessel's correction built in.
sample_size = math_ops.cast(
array_ops.size(x) / array_ops.size(scale), scale.dtype)
batch_var_corrected = batch_var * sample_size / (
math_ops.maximum(sample_size - 1.0, 1.0))
mean = self._running_mean(old_mean, batch_mean, exponential_avg_factor)
var = self._running_mean(old_var, batch_var_corrected,
exponential_avg_factor)
return self.evaluate(y), self.evaluate(mean), self.evaluate(var)
def _test_training(self,
x_shape,
x_dtype,
scale_shape,
scale_dtype,
use_gpu=True,
exponential_avg_factor=1.0,
data_format='NHWC'):
np.random.seed(1)
x_val = np.random.random_sample(x_shape).astype(x_dtype)
scale_val = np.random.random_sample(scale_shape).astype(scale_dtype)
offset_val = np.random.random_sample(scale_shape).astype(scale_dtype)
if exponential_avg_factor == 1.0:
old_mean_val = None
old_var_val = None
else:
old_mean_val = np.random.random_sample(scale_shape).astype(scale_dtype)
old_var_val = np.random.random_sample(scale_shape).astype(scale_dtype)
with self.cached_session(use_gpu=use_gpu) as sess:
x = constant_op.constant(x_val, name='x')
scale = constant_op.constant(scale_val, name='scale')
offset = constant_op.constant(offset_val, name='offset')
epsilon = 0.001
y, mean, var = nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=old_mean_val,
variance=old_var_val,
epsilon=epsilon,
exponential_avg_factor=exponential_avg_factor,
data_format=data_format,
is_training=True)
y_val, mean_val, var_val = self.evaluate([y, mean, var])
y_ref, mean_ref, var_ref = self._training_ref(x, scale, offset,
old_mean_val, old_var_val,
exponential_avg_factor,
epsilon, data_format)
y_atol = 1e-3
if x_dtype == np.float16:
y_atol = 2e-3
elif x_dtype == dtypes.bfloat16.as_numpy_dtype:
y_atol = 1e-2
self.assertAllClose(y_ref, y_val, atol=y_atol)
self.assertAllClose(mean_ref, mean_val, atol=1e-3)
self.assertAllClose(var_ref, var_val, atol=1e-3)
def _compute_gradient_error_float16(self, x, x32, x_shape, y, y32, y_shape,
x_dtype):
"""Computes the gradient error for float16 inputs and/or outputs.
This returns the same value as gradient_checker.compute_gradient_error. The
difference is that gradient_checker.compute_gradient_error does not
numerically compute the gradients in a numerically stable way for float16
tensors. To fix this, this function requires float32 versions of x and y to
numerically compute the gradients, to compare with the float16 symbolically
computed gradients.
Args:
x: The input tensor.
x32: A float32 version of x.
x_shape: The shape of x.
y: The output tensor.
y32: A float32 version of y. Must be calculated based on x32, not x.
y_shape: The shape of y.
x_dtype: The type of x, float16 or bfloat16.
Returns:
The maximum error in between the two Jacobians, as in
gradient_checker.compute_gradient_error.
"""
x_init_val = np.random.random_sample(x_shape).astype(x_dtype)
x32_init_val = x_init_val.astype(np.float32)
# TODO(reedwm): Do not perform the unnecessary computations in
# compute_gradient, since they double the computation time of this function.
theoretical_grad, _ = gradient_checker.compute_gradient(
x, x_shape, y, y_shape, delta=1e-3, x_init_value=x_init_val)
_, numerical_grad = gradient_checker.compute_gradient(
x32, x_shape, y32, y_shape, delta=1e-3, x_init_value=x32_init_val)
# If grad is empty, no error.
if theoretical_grad.size == 0 and numerical_grad.size == 0:
return 0
return np.fabs(theoretical_grad - numerical_grad).max()
def _test_gradient(self,
x_shape,
x_dtype,
scale_shape,
scale_dtype,
use_gpu=True,
exponential_avg_factor=1.0,
data_format='NHWC',
is_training=True):
np.random.seed(1)
x_val = np.random.random_sample(x_shape).astype(x_dtype)
scale_val = np.random.random_sample(scale_shape).astype(scale_dtype)
offset_val = np.random.random_sample(scale_shape).astype(scale_dtype)
with self.cached_session(use_gpu=use_gpu):
x = constant_op.constant(x_val, name='x')
scale = constant_op.constant(scale_val, name='scale')
offset = constant_op.constant(offset_val, name='offset')
if is_training and exponential_avg_factor == 1.0:
pop_mean = None
pop_var = None
else:
pop_mean = np.random.random_sample(scale_shape).astype(scale_dtype)
pop_var = np.random.random_sample(scale_shape).astype(scale_dtype)
y, _, _ = nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=pop_mean,
variance=pop_var,
exponential_avg_factor=exponential_avg_factor,
data_format=data_format,
is_training=is_training)
if x_dtype not in [np.float16, dtypes.bfloat16.as_numpy_dtype]:
err_x = gradient_checker.compute_gradient_error(x, x_shape, y, x_shape)
err_scale = gradient_checker.compute_gradient_error(
scale, scale_shape, y, x_shape)
err_offset = gradient_checker.compute_gradient_error(
offset, scale_shape, y, x_shape)
else:
x32 = constant_op.constant(x_val, name='x32', dtype=dtypes.float32)
y32, _, _ = nn_impl.fused_batch_norm(
x32,
scale,
offset,
mean=pop_mean,
variance=pop_var,
data_format=data_format,
exponential_avg_factor=exponential_avg_factor,
is_training=is_training)
err_x = self._compute_gradient_error_float16(x, x32, x_shape, y, y32,
x_shape, x_dtype)
err_scale = self._compute_gradient_error_float16(
scale, scale, scale_shape, y, y32, x_shape, x_dtype)
err_offset = self._compute_gradient_error_float16(
offset, offset, scale_shape, y, y32, x_shape, x_dtype)
x_err_tolerance = 1e-3
if x_dtype == np.float16:
x_err_tolerance = 2e-3
elif dtypes.bfloat16.as_numpy_dtype:
x_err_tolerance = 2e-2
scale_err_tolerance = 1e-3
self.assertLess(err_x, x_err_tolerance)
self.assertLess(err_scale, scale_err_tolerance)
self.assertLess(err_offset, scale_err_tolerance)
def _test_grad_grad(self,
x_shape,
x_dtype,
scale_shape,
scale_dtype,
use_gpu=True,
exponential_avg_factor=1.0,
data_format='NHWC',
is_training=True,
err_tolerance=1e-3):
np.random.seed(1)
x_val = np.random.random_sample(x_shape).astype(x_dtype)
grad_y_val = np.random.random_sample(x_shape).astype(x_dtype)
scale_val = np.random.random_sample(scale_shape).astype(scale_dtype)
offset_val = np.random.random_sample(scale_shape).astype(scale_dtype)
with self.cached_session(use_gpu=use_gpu) as sess:
x = constant_op.constant(x_val, name='x')
grad_y = constant_op.constant(grad_y_val, name='grad_y')
scale = constant_op.constant(scale_val, name='scale')
offset = constant_op.constant(offset_val, name='offset')
if is_training and exponential_avg_factor == 1.0:
pop_mean = None
pop_var = None
else:
pop_mean = np.random.random_sample(scale_shape).astype(scale_dtype)
pop_var = np.random.random_sample(scale_shape).astype(scale_dtype)
y, _, _ = nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=pop_mean,
variance=pop_var,
exponential_avg_factor=exponential_avg_factor,
data_format=data_format,
is_training=is_training)
grad_x, grad_scale, grad_offset = gradients_impl.gradients(
y, [x, scale, offset], grad_y)
if is_training:
epsilon = y.op.get_attr('epsilon')
data_format = y.op.get_attr('data_format')
grad_vals = self.evaluate([grad_x, grad_scale, grad_offset])
grad_internal = nn_grad._BatchNormGrad(grad_y, x, scale, pop_mean,
pop_var, epsilon, data_format)
grad_internal_vals = self.evaluate(list(grad_internal))
for grad_val, grad_internal_val in zip(grad_vals, grad_internal_vals):
self.assertAllClose(grad_val, grad_internal_val, atol=err_tolerance)
if x_dtype not in [np.float16, dtypes.bfloat16.as_numpy_dtype]:
err_grad_grad_y_1 = gradient_checker.compute_gradient_error(
grad_y, x_shape, grad_x, x_shape)
err_grad_grad_y_2 = gradient_checker.compute_gradient_error(
grad_y, x_shape, grad_scale, scale_shape)
err_grad_grad_y_3 = gradient_checker.compute_gradient_error(
grad_y, x_shape, grad_offset, scale_shape)
# In freeze mode, grad_x is not a function of x.
if is_training:
err_grad_x_1 = gradient_checker.compute_gradient_error(
x, x_shape, grad_x, x_shape)
err_grad_x_2 = gradient_checker.compute_gradient_error(
x, x_shape, grad_scale, scale_shape)
err_grad_scale = gradient_checker.compute_gradient_error(
scale, scale_shape, grad_x, x_shape)
else:
x32 = constant_op.constant(x_val, dtype=dtypes.float32, name='x32')
grad_y32 = constant_op.constant(
grad_y_val, dtype=dtypes.float32, name='grad_y32')
y32, _, _ = nn_impl.fused_batch_norm(
x32,
scale,
offset,
mean=pop_mean,
variance=pop_var,
exponential_avg_factor=exponential_avg_factor,
data_format=data_format,
is_training=is_training)
grad_x32, grad_scale32, grad_offset32 = gradients_impl.gradients(
y32, [x32, scale, offset], grad_y32)
err_grad_grad_y_1 = self._compute_gradient_error_float16(
grad_y, grad_y32, x_shape, grad_x, grad_x32, x_shape, x_dtype)
err_grad_grad_y_2 = self._compute_gradient_error_float16(
grad_y, grad_y32, x_shape, grad_scale, grad_scale32, scale_shape,
x_dtype)
err_grad_grad_y_3 = self._compute_gradient_error_float16(
grad_y, grad_y32, x_shape, grad_offset, grad_offset32, scale_shape,
x_dtype)
# In freeze mode, grad_x is not a function of x.
if is_training:
err_grad_x_1 = self._compute_gradient_error_float16(
x, x32, x_shape, grad_x, grad_x32, x_shape, x_dtype)
err_grad_x_2 = self._compute_gradient_error_float16(
x, x32, x_shape, grad_scale, grad_scale32, scale_shape, x_dtype)
err_grad_scale = self._compute_gradient_error_float16(
scale, scale, scale_shape, grad_x, grad_x32, x_shape, x_dtype)
self.assertLess(err_grad_grad_y_1, err_tolerance)
self.assertLess(err_grad_grad_y_2, err_tolerance)
self.assertLess(err_grad_grad_y_3, err_tolerance)
if is_training:
self.assertLess(err_grad_x_1, err_tolerance)
self.assertLess(err_grad_x_2, err_tolerance)
self.assertLess(err_grad_scale, err_tolerance)
def _runtests(self, x_shape, is_training, gradient_test=False,
cpu_only=False):
if len(x_shape) == 4:
data_format_list = ['NHWC', 'NCHW']
else:
data_format_list = ['NCDHW', 'NDHWC']
use_gpu_vals = [False]
if test.is_gpu_available(cuda_only=True) and not cpu_only:
use_gpu_vals += [True]
factors = [1.0, 0.6]
for dtype in [np.float16, np.float32, dtypes.bfloat16.as_numpy_dtype]:
for use_gpu in use_gpu_vals:
if dtype == dtypes.bfloat16.as_numpy_dtype and not use_gpu:
continue
for data_format in data_format_list:
if data_format == 'NHWC' or data_format == 'NDHWC':
scale_shape = x_shape[-1:]
else:
scale_shape = x_shape[1:2]
for exponential_avg_factor in factors:
if gradient_test:
self._test_gradient(
x_shape,
dtype,
scale_shape,
np.float32,
use_gpu=use_gpu,
data_format=data_format,
is_training=is_training,
exponential_avg_factor=exponential_avg_factor)
else:
if is_training:
self._test_training(
x_shape,
dtype,
scale_shape,
np.float32,
use_gpu=use_gpu,
data_format=data_format,
exponential_avg_factor=exponential_avg_factor)
else:
self._test_inference(
x_shape,
dtype,
scale_shape,
np.float32,
use_gpu=use_gpu,
data_format=data_format,
exponential_avg_factor=exponential_avg_factor)
def testInferenceShape1(self):
x_shape = [1, 1, 6, 1]
self._runtests(x_shape, False)
def testInferenceShape2(self):
x_shape = [1, 1, 6, 2]
self._runtests(x_shape, False)
def testInferenceShape3(self):
x_shape = [1, 2, 1, 6]
self._runtests(x_shape, False)
def testInferenceShape4(self):
x_shape = [27, 131, 127, 6]
self._runtests(x_shape, False)
def testInferenceShape5(self):
x_shape = [0, 131, 127, 6]
self._runtests(x_shape, False)
def testInferenceShape6(self):
x_shape = [1, 1, 1, 1]
# GPU kernel doesn't properly handle case where non-channel dimensions are 1
self._runtests(x_shape, False, cpu_only=True)
def testInferenceShape7(self):
x_shape = [1, 2, 6, 1, 3]
self._runtests(x_shape, False)
def testTrainingShape1(self):
x_shape = [1, 1, 6, 1]
self._runtests(x_shape, True)
def testTrainingShape2(self):
x_shape = [1, 1, 6, 2]
self._runtests(x_shape, True)
def testTrainingShape3(self):
x_shape = [1, 2, 1, 6]
self._runtests(x_shape, True)
def testTrainingShape4(self):
x_shape = [27, 131, 127, 6]
self._runtests(x_shape, True)
@test_util.disable_xla('b/141236973: Empty inputs wrong on CPU.')
def testTrainingShape5(self):
x_shape = [0, 131, 127, 6]
self._runtests(x_shape, True)
@test_util.run_deprecated_v1
def testTrainingShape6(self):
x_shape = [1, 1, 1, 1]
# GPU kernel doesn't properly handle case where non-channel dimensions are 1
self._runtests(x_shape, True, cpu_only=True)
def testTrainingShape7(self):
x_shape = [1, 2, 6, 1, 3]
self._runtests(x_shape, True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape1(self):
x_shape = [1, 1, 6, 1]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape2(self):
x_shape = [1, 1, 6, 2]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape3(self):
x_shape = [1, 2, 1, 6]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape4(self):
x_shape = [5, 7, 11, 4]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
@test_util.disable_xla('This test never passed for XLA')
def testBatchNormGradInferenceShape5(self):
x_shape = [0, 7, 11, 4]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape6(self):
x_shape = [1, 1, 1, 1]
# GPU kernel doesn't properly handle case where non-channel dimensions are 1
self._runtests(x_shape, is_training=False, gradient_test=True,
cpu_only=True)
@test_util.run_deprecated_v1
def testBatchNormGradInferenceShape7(self):
x_shape = [1, 2, 6, 1, 3]
self._runtests(x_shape, is_training=False, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape1(self):
x_shape = [1, 1, 6, 1]
self._runtests(x_shape, is_training=True, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape2(self):
x_shape = [1, 1, 6, 2]
self._runtests(x_shape, is_training=True, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape3(self):
x_shape = [1, 2, 1, 6]
self._runtests(x_shape, is_training=True, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape4(self):
x_shape = [5, 7, 11, 4]
self._runtests(x_shape, is_training=True, gradient_test=True)
@test_util.run_deprecated_v1
@test_util.disable_xla('This test never passed for XLA')
def testBatchNormGradTrainingShape5(self):
x_shape = [0, 7, 11, 4]
self._runtests(x_shape, is_training=True, gradient_test=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape6(self):
x_shape = [1, 1, 1, 1]
# GPU kernel doesn't properly handle case where non-channel dimensions are 1
self._runtests(x_shape, is_training=True, gradient_test=True, cpu_only=True)
@test_util.run_deprecated_v1
def testBatchNormGradTrainingShape7(self):
x_shape = [1, 2, 6, 1, 3]
self._runtests(x_shape, is_training=True, gradient_test=True)
def _testBatchNormGradGrad(self, config):
shape = config['shape']
err_tolerance = config['err_tolerance']
dtype = config['dtype']
rank = len(shape)
if rank == 4:
data_format_nhwc, features_nhwc = 'NHWC', shape[3]
data_format_nchw, features_nchw = 'NCHW', shape[1]
else:
data_format_nhwc, features_nhwc = 'NDHWC', shape[4]
data_format_nchw, features_nchw = 'NCDHW', shape[1]
for is_training in [True, False]:
if test.is_gpu_available(cuda_only=True):
self._test_grad_grad(
shape,
dtype, [features_nhwc],
np.float32,
use_gpu=True,
data_format=data_format_nhwc,
is_training=is_training,
err_tolerance=err_tolerance)
self._test_grad_grad(
shape,
dtype, [features_nchw],
np.float32,
use_gpu=True,
data_format=data_format_nchw,
is_training=is_training,
err_tolerance=err_tolerance)
self._test_grad_grad(
shape,
dtype, [features_nhwc],
np.float32,
use_gpu=False,
data_format=data_format_nhwc,
is_training=is_training,
err_tolerance=err_tolerance)
self._test_grad_grad(
shape,
dtype, [features_nchw],
np.float32,
use_gpu=False,
data_format=data_format_nchw,
is_training=is_training,
err_tolerance=err_tolerance)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig1(self):
config = {
'shape': [2, 3, 4, 5],
'err_tolerance': 1e-2,
'dtype': np.float32,
}
self._testBatchNormGradGrad(config)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig2(self):
config = {
'shape': [2, 3, 2, 2],
'err_tolerance': 1e-3,
'dtype': np.float32,
}
self._testBatchNormGradGrad(config)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig3(self):
config = {
'shape': [2, 3, 4, 5],
'err_tolerance': 2e-2,
'dtype': np.float16,
}
self._testBatchNormGradGrad(config)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig4(self):
config = {
'shape': [2, 3, 2, 2],
'err_tolerance': 2e-3,
'dtype': np.float16,
}
self._testBatchNormGradGrad(config)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig5(self):
config = {
'shape': [2, 3, 2, 2, 2],
'err_tolerance': 2e-3,
'dtype': np.float32,
}
self._testBatchNormGradGrad(config)
@test_util.run_deprecated_v1
def testBatchNormGradGradConfig6(self):
config = {
'shape': [2, 3, 2, 2, 2],
'err_tolerance': 3e-3,
'dtype': np.float16,
}
self._testBatchNormGradGrad(config)
def test5dBatchNormFollowedByRelu(self):
# The remapper grappler pass previously did not properly handle a 5D
# inference FusedBatchNorm followed by Relu. This asserts that this case is
# correctly handled.
np.random.seed(1)
x = np.random.random_sample((2, 3, 2, 2, 3)).astype(np.float32)
scale = np.random.random_sample((3,)).astype(np.float32)
offset = np.random.random_sample((3,)).astype(np.float32)
mean = np.random.random_sample((3,)).astype(np.float32)
var = np.random.random_sample((3,)).astype(np.float32)
epsilon = 0.001
y, _, _ = nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=mean,
variance=var,
epsilon=epsilon,
data_format='NCDHW',
is_training=False)
y = nn_ops.relu(y)
y_val = self.evaluate(y)
y_ref = self._inference_ref(x, scale, offset, mean, var, epsilon,
'NCDHW')
y_ref = np.maximum(y_ref, 0.)
self.assertAllClose(y_ref, y_val, atol=1e-3)
def testEagerShapeErrors(self):
with context.eager_mode():
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((3,))
offset = array_ops.ones((2,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'scale must have the same number of elements'):
nn_impl.fused_batch_norm(x, scale, offset)
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
offset = array_ops.ones((3,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'offset must have the same number of elements'):
nn_impl.fused_batch_norm(x, scale, offset)
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
offset = array_ops.ones((2,))
mean = array_ops.ones((0,))
variance = array_ops.ones((2,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'When is_training=false, mean must have the same number of elements'):
nn_impl.fused_batch_norm(
x, scale, offset, mean=mean, variance=variance, is_training=False)
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
offset = array_ops.ones((2,))
mean = array_ops.ones((2,))
variance = array_ops.ones((0,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'When is_training=false, variance must have the same number of '
'elements'):
nn_impl.fused_batch_norm(
x, scale, offset, mean=mean, variance=variance, is_training=False)
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
offset = array_ops.ones((2,))
mean = array_ops.ones((0,))
variance = array_ops.ones((2,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'When exponential_avg_factor != 1, mean must have the same number of '
'elements'):
nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=mean,
variance=variance,
exponential_avg_factor=0.5)
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
offset = array_ops.ones((2,))
mean = array_ops.ones((2,))
variance = array_ops.ones((0,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'When exponential_avg_factor != 1, variance must have the same '
'number of elements'):
nn_impl.fused_batch_norm(
x,
scale,
offset,
mean=mean,
variance=variance,
exponential_avg_factor=0.5)
def testEagerShapeGradErrors(self):
with context.eager_mode():
y_backprop = array_ops.ones((2, 2, 2, 3))
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
reserve_space_1 = array_ops.ones((2,))
reserve_space_2 = array_ops.ones((2,))
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
'x and y_backprop must have same shape,'):
gen_nn_ops.fused_batch_norm_grad_v2(y_backprop, x, scale,
reserve_space_1, reserve_space_2)
y_backprop = array_ops.ones((2, 2, 2, 2))
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((3,))
reserve_space_1 = array_ops.ones((2,))
reserve_space_2 = array_ops.ones((2,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'scale must have the same number of elements'):
gen_nn_ops.fused_batch_norm_grad_v2(y_backprop, x, scale,
reserve_space_1, reserve_space_2)
y_backprop = array_ops.ones((2, 2, 2, 2))
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
reserve_space_1 = array_ops.ones((3,))
reserve_space_2 = array_ops.ones((2,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'reserve_space_1 must have the same number of elements'):
gen_nn_ops.fused_batch_norm_grad_v2(y_backprop, x, scale,
reserve_space_1, reserve_space_2)
y_backprop = array_ops.ones((2, 2, 2, 2))
x = array_ops.ones((2, 2, 2, 2))
scale = array_ops.ones((2,))
reserve_space_1 = array_ops.ones((2,))
reserve_space_2 = array_ops.ones((3,))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
'reserve_space_2 must have the same number of elements'):
gen_nn_ops.fused_batch_norm_grad_v2(y_backprop, x, scale,
reserve_space_1, reserve_space_2)
if __name__ == '__main__':
test.main()