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