1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""Tests for convolution related functionality in tensorflow.ops.nn.""" 16 17import numpy as np 18 19from tensorflow.python.framework import constant_op 20from tensorflow.python.framework import dtypes 21from tensorflow.python.framework import test_util 22from tensorflow.python.ops import array_ops 23from tensorflow.python.ops import gradient_checker 24from tensorflow.python.ops import nn_ops 25from tensorflow.python.ops import random_ops 26from tensorflow.python.ops import variable_scope 27from tensorflow.python.ops import variables 28import tensorflow.python.ops.nn_grad # pylint: disable=unused-import 29from tensorflow.python.platform import test 30 31 32class Conv2DTransposeTest(test.TestCase): 33 34 def testConv2DTransposeSingleStride(self): 35 with self.cached_session(): 36 for dtype in (dtypes.float32, dtypes.int32): 37 strides = [1, 1, 1, 1] 38 39 # Input, output: [batch, height, width, depth] 40 x_shape = [2, 6, 4, 3] 41 y_shape = [2, 6, 4, 2] 42 43 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 44 f_shape = [3, 3, 2, 3] 45 46 x = constant_op.constant(1, shape=x_shape, name="x", dtype=dtype) 47 f = constant_op.constant(1, shape=f_shape, name="filter", dtype=dtype) 48 output = nn_ops.conv2d_transpose( 49 x, f, y_shape, strides=strides, padding="SAME") 50 value = self.evaluate(output) 51 52 # We count the number of cells being added at the locations in the 53 # output. 54 # At the center, #cells=kernel_height * kernel_width 55 # At the corners, #cells=ceil(kernel_height/2) * ceil(kernel_width/2) 56 # At the borders, #cells=ceil(kernel_height/2)*kernel_width or 57 # kernel_height * ceil(kernel_width/2) 58 59 for n in range(x_shape[0]): 60 for k in range(f_shape[2]): 61 for w in range(y_shape[2]): 62 for h in range(y_shape[1]): 63 target = 4 * 3 64 h_in = h > 0 and h < y_shape[1] - 1 65 w_in = w > 0 and w < y_shape[2] - 1 66 if h_in and w_in: 67 target += 5 * 3 68 elif h_in or w_in: 69 target += 2 * 3 70 if dtype.is_integer: 71 self.assertAllEqual(target, value[n, h, w, k]) 72 else: 73 self.assertAllClose(target, value[n, h, w, k]) 74 75 def testConv2DTransposeSame(self): 76 with self.cached_session(): 77 for dtype in (dtypes.float32, dtypes.int32): 78 strides = [1, 2, 2, 1] 79 80 # Input, output: [batch, height, width, depth] 81 x_shape = [2, 6, 4, 3] 82 y_shape = [2, 12, 8, 2] 83 84 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 85 f_shape = [3, 3, 2, 3] 86 87 x = constant_op.constant(1, shape=x_shape, name="x", dtype=dtype) 88 f = constant_op.constant(1, shape=f_shape, name="filter", dtype=dtype) 89 output = nn_ops.conv2d_transpose( 90 x, f, y_shape, strides=strides, padding="SAME") 91 value = self.evaluate(output) 92 93 for n in range(x_shape[0]): 94 for k in range(f_shape[2]): 95 for w in range(y_shape[2]): 96 for h in range(y_shape[1]): 97 target = 3 98 # We add a case for locations divisible by the stride. 99 h_in = h % strides[1] == 0 and h > 0 and h < y_shape[1] - 1 100 w_in = w % strides[2] == 0 and w > 0 and w < y_shape[2] - 1 101 if h_in and w_in: 102 target += 9 103 elif h_in or w_in: 104 target += 3 105 106 if dtype.is_integer: 107 self.assertAllEqual(target, value[n, h, w, k]) 108 else: 109 self.assertAllClose(target, value[n, h, w, k]) 110 111 def testConv2DTransposeValid(self): 112 with self.cached_session(): 113 for dtype in (dtypes.float32, dtypes.int32): 114 strides = [1, 2, 2, 1] 115 116 # Input, output: [batch, height, width, depth] 117 x_shape = [2, 6, 4, 3] 118 y_shape = [2, 13, 9, 2] 119 120 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 121 f_shape = [3, 3, 2, 3] 122 123 x = constant_op.constant(1, shape=x_shape, name="x", dtype=dtype) 124 f = constant_op.constant(1, shape=f_shape, name="filter", dtype=dtype) 125 output = nn_ops.conv2d_transpose( 126 x, f, y_shape, strides=strides, padding="VALID") 127 value = self.evaluate(output) 128 129 cache_values = np.zeros(y_shape, dtype=np.float32) 130 131 # The amount of padding added 132 pad = 1 133 134 for n in range(x_shape[0]): 135 for k in range(f_shape[2]): 136 for w in range(pad, y_shape[2] - pad): 137 for h in range(pad, y_shape[1] - pad): 138 target = 3 139 # We add a case for locations divisible by the stride. 140 h_in = h % strides[1] == 0 and h > pad and h < y_shape[ 141 1] - 1 - pad 142 w_in = w % strides[2] == 0 and w > pad and w < y_shape[ 143 2] - 1 - pad 144 if h_in and w_in: 145 target += 9 146 elif h_in or w_in: 147 target += 3 148 cache_values[n, h, w, k] = target 149 150 # copy values in the border 151 cache_values[n, :, 0, k] = cache_values[n, :, 1, k] 152 cache_values[n, :, -1, k] = cache_values[n, :, -2, k] 153 cache_values[n, 0, :, k] = cache_values[n, 1, :, k] 154 cache_values[n, -1, :, k] = cache_values[n, -2, :, k] 155 156 if dtype.is_integer: 157 self.assertAllEqual(cache_values, value) 158 else: 159 self.assertAllClose(cache_values, value) 160 161 @test_util.run_deprecated_v1 162 def testGradient(self): 163 x_shape = [2, 6, 4, 3] 164 f_shape = [3, 3, 2, 3] 165 y_shape = [2, 12, 8, 2] 166 strides = [1, 2, 2, 1] 167 np.random.seed(1) # Make it reproducible. 168 x_val = np.random.random_sample(x_shape).astype(np.float64) 169 f_val = np.random.random_sample(f_shape).astype(np.float64) 170 with self.cached_session(): 171 x = constant_op.constant(x_val, name="x", dtype=dtypes.float32) 172 f = constant_op.constant(f_val, name="f", dtype=dtypes.float32) 173 output = nn_ops.conv2d_transpose( 174 x, f, y_shape, strides=strides, padding="SAME") 175 err = gradient_checker.compute_gradient_error([x, f], [x_shape, f_shape], 176 output, y_shape) 177 print("conv2d_transpose gradient err = %g " % err) 178 err_tolerance = 0.0006 179 self.assertLess(err, err_tolerance) 180 181 def testConv2DTransposeSingleStrideNCHW(self): 182 # `NCHW` data format is only supported for CUDA device. 183 if test.is_gpu_available(cuda_only=True): 184 with self.session(): 185 strides = [1, 1, 1, 1] 186 187 # Input, output: [batch, depth, height, width, depth] 188 x_shape = [2, 3, 6, 4] 189 y_shape = [2, 2, 6, 4] 190 191 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 192 f_shape = [3, 3, 2, 3] 193 194 x = constant_op.constant( 195 1.0, shape=x_shape, name="x", dtype=dtypes.float32) 196 f = constant_op.constant( 197 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) 198 199 output = nn_ops.conv2d_transpose( 200 x, f, y_shape, strides=strides, padding="SAME", data_format="NCHW") 201 202 value = self.evaluate(output) 203 for n in range(x_shape[0]): 204 for k in range(f_shape[2]): 205 for w in range(y_shape[3]): 206 for h in range(y_shape[2]): 207 target = 4 * 3.0 208 h_in = h > 0 and h < y_shape[2] - 1 209 w_in = w > 0 and w < y_shape[3] - 1 210 if h_in and w_in: 211 target += 5 * 3.0 212 elif h_in or w_in: 213 target += 2 * 3.0 214 self.assertAllClose(target, value[n, k, h, w]) 215 216 def testConv2DTransposeSameNCHW(self): 217 # `NCHW` data format is only supported for CUDA device. 218 if test.is_gpu_available(cuda_only=True): 219 with self.session(): 220 strides = [1, 1, 2, 2] 221 222 # Input, output: [batch, depth, height, width] 223 x_shape = [2, 3, 6, 4] 224 y_shape = [2, 2, 12, 8] 225 226 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 227 f_shape = [3, 3, 2, 3] 228 229 x = constant_op.constant( 230 1.0, shape=x_shape, name="x", dtype=dtypes.float32) 231 f = constant_op.constant( 232 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) 233 234 output = nn_ops.conv2d_transpose( 235 x, f, y_shape, strides=strides, padding="SAME", data_format="NCHW") 236 237 value = self.evaluate(output) 238 for n in range(x_shape[0]): 239 for k in range(f_shape[2]): 240 for w in range(y_shape[3]): 241 for h in range(y_shape[2]): 242 target = 3.0 243 # We add a case for locations divisible by the stride. 244 h_in = h % strides[2] == 0 and h > 0 and h < y_shape[2] - 1 245 w_in = w % strides[3] == 0 and w > 0 and w < y_shape[3] - 1 246 if h_in and w_in: 247 target += 9.0 248 elif h_in or w_in: 249 target += 3.0 250 self.assertAllClose(target, value[n, k, h, w]) 251 252 def testConv2DTransposeValidNCHW(self): 253 # `NCHW` data format is only supported for CUDA device. 254 if test.is_gpu_available(cuda_only=True): 255 with self.session(): 256 strides = [1, 1, 2, 2] 257 258 # Input, output: [batch, depth, height, width] 259 x_shape = [2, 3, 6, 4] 260 y_shape = [2, 2, 13, 9] 261 262 # Filter: [kernel_height, kernel_width, output_depth, input_depth] 263 f_shape = [3, 3, 2, 3] 264 265 x = constant_op.constant( 266 1.0, shape=x_shape, name="x", dtype=dtypes.float32) 267 f = constant_op.constant( 268 1.0, shape=f_shape, name="filter", dtype=dtypes.float32) 269 output = nn_ops.conv2d_transpose( 270 x, f, y_shape, strides=strides, padding="VALID", data_format="NCHW") 271 272 value = self.evaluate(output) 273 cache_values = np.zeros(y_shape, dtype=np.float32) 274 # The amount of padding added 275 pad = 1 276 for n in range(x_shape[0]): 277 for k in range(f_shape[2]): 278 for w in range(pad, y_shape[3] - pad): 279 for h in range(pad, y_shape[2] - pad): 280 target = 3.0 281 # We add a case for locations divisible by the stride. 282 h_in = h % strides[2] == 0 and h > pad and h < y_shape[ 283 2] - 1 - pad 284 w_in = w % strides[3] == 0 and w > pad and w < y_shape[ 285 3] - 1 - pad 286 if h_in and w_in: 287 target += 9.0 288 elif h_in or w_in: 289 target += 3.0 290 cache_values[n, k, h, w] = target 291 292 # copy values in the border 293 cache_values[n, k, :, 0] = cache_values[n, k, :, 1] 294 cache_values[n, k, :, -1] = cache_values[n, k, :, -2] 295 cache_values[n, k, 0, :] = cache_values[n, k, 1, :] 296 cache_values[n, k, -1, :] = cache_values[n, k, -2, :] 297 298 self.assertAllClose(cache_values, value) 299 300 def testConv2DTransposeShapeInference(self): 301 # Test case for 8972 302 initializer = random_ops.truncated_normal( 303 [3, 3, 5, 1], mean=0.0, stddev=0.01, dtype=dtypes.float32) 304 x = variables.Variable(random_ops.random_normal([3, 10, 5, 1])) 305 f = variable_scope.get_variable("f", initializer=initializer) 306 f_shape = array_ops.stack([array_ops.shape(x)[0], 10, 5, 5]) 307 output = nn_ops.conv2d_transpose( 308 x, f, f_shape, strides=[1, 1, 1, 1], padding="SAME") 309 self.assertEqual(output.get_shape().as_list(), [3, 10, 5, 5]) 310 311 312if __name__ == "__main__": 313 test.main() 314