1 # Copyright 2016 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 """Functional tests for 3d pooling operations.""" 16 17 from __future__ import absolute_import 18 from __future__ import division 19 from __future__ import print_function 20 21 import numpy as np 22 23 from tensorflow.python.framework import constant_op 24 from tensorflow.python.framework import test_util 25 from tensorflow.python.ops import gradient_checker 26 from tensorflow.python.ops import gradients_impl 27 from tensorflow.python.ops import nn_ops 28 import tensorflow.python.ops.nn_grad # pylint: disable=unused-import 29 from tensorflow.python.platform import test 30 31 32 def GetTestConfigs(): 33 """Get all the valid tests configs to run. 34 35 Returns: 36 all the valid test configs as tuples of data_format and use_gpu. 37 """ 38 test_configs = [("NDHWC", False), ("NDHWC", True)] 39 if test.is_gpu_available(cuda_only=True): 40 # "NCHW" format is currently supported exclusively on CUDA GPUs. 41 test_configs += [("NCDHW", True)] 42 return test_configs 43 44 45 # TODO(mjanusz): Add microbenchmarks for 3d pooling. 46 class PoolingTest(test.TestCase): 47 48 def _VerifyOneTest(self, pool_func, input_sizes, window, strides, padding, 49 data_format, expected, use_gpu): 50 """Verifies the output values of the pooling function. 51 52 Args: 53 pool_func: Function to be called: co.MaxPool, co.AvgPool. 54 input_sizes: Input tensor dimensions. 55 window: Tuple of kernel dims: planes, rows, cols. 56 strides: Tuple of strides for dims: planes, rows, cols. 57 padding: Padding type. 58 data_format: The data format we use to run the pooling operation. 59 expected: An array containing the expected operation outputs. 60 use_gpu: Whether to run ops on GPU. 61 """ 62 total_size = 1 63 for s in input_sizes: 64 total_size *= s 65 # Initializes the input tensor with array containing incrementing 66 # numbers from 1. 67 x = [f * 1.0 for f in range(1, total_size + 1)] 68 with self.test_session(use_gpu=use_gpu) as sess: 69 t = constant_op.constant(x, shape=input_sizes) 70 window = [1] + list(window) + [1] 71 strides = [1] + list(strides) + [1] 72 if data_format == "NCDHW": 73 t = test_util.NHWCToNCHW(t) 74 window = test_util.NHWCToNCHW(window) 75 strides = test_util.NHWCToNCHW(strides) 76 t = pool_func( 77 t, 78 ksize=window, 79 strides=strides, 80 padding=padding, 81 data_format=data_format) 82 if data_format == "NCDHW": 83 t = test_util.NCHWToNHWC(t) 84 vals = sess.run(t) 85 # Verifies values. 86 actual = vals.flatten() 87 self.assertAllClose(expected, actual) 88 89 def _VerifyValues(self, pool_func, input_sizes, window, strides, 90 padding, expected): 91 for data_format, use_gpu in GetTestConfigs(): 92 self._VerifyOneTest(pool_func, input_sizes, window, strides, padding, 93 data_format, expected, use_gpu) 94 95 def testAvgPool3dValidPadding(self): 96 expected_output = [20.5, 21.5, 22.5] 97 self._VerifyValues( 98 nn_ops.avg_pool3d, 99 input_sizes=[1, 3, 3, 3, 3], 100 window=(2, 2, 2), 101 strides=(2, 2, 2), 102 padding="VALID", 103 expected=expected_output) 104 105 def testAvgPool3dSamePadding(self): 106 expected_output = [20.5, 21.5, 22.5, 26.5, 27.5, 28.5] 107 self._VerifyValues( 108 nn_ops.avg_pool3d, 109 input_sizes=[1, 2, 2, 4, 3], 110 window=(2, 2, 2), 111 strides=(2, 2, 2), 112 padding="SAME", 113 expected=expected_output) 114 115 def testAvgPool3dSamePaddingDifferentStrides(self): 116 expected_output = [1.5, 4.5, 7.5, 17.5, 20.5, 23.5, 33.5, 36.5, 39.5] 117 self._VerifyValues( 118 nn_ops.avg_pool3d, 119 input_sizes=[1, 5, 8, 1, 1], 120 window=(1, 2, 3), 121 strides=(2, 3, 1), 122 padding="SAME", 123 expected=expected_output) 124 125 def testMaxPool3dValidPadding(self): 126 expected_output = [40.0, 41.0, 42.0] 127 self._VerifyValues( 128 nn_ops.max_pool3d, 129 input_sizes=[1, 3, 3, 3, 3], 130 window=(2, 2, 2), 131 strides=(2, 2, 2), 132 padding="VALID", 133 expected=expected_output) 134 135 def testMaxPool3dSamePadding(self): 136 expected_output = [31., 32., 33., 34., 35., 36.] 137 self._VerifyValues( 138 nn_ops.max_pool3d, 139 input_sizes=[1, 2, 2, 3, 3], 140 window=(2, 2, 2), 141 strides=(2, 2, 2), 142 padding="SAME", 143 expected=expected_output) 144 145 def testMaxPool3dSamePaddingDifferentStrides(self): 146 expected_output = [2., 5., 8., 18., 21., 24., 34., 37., 40.] 147 self._VerifyValues( 148 nn_ops.max_pool3d, 149 input_sizes=[1, 5, 8, 1, 1], 150 window=(1, 2, 3), 151 strides=(2, 3, 1), 152 padding="SAME", 153 expected=expected_output) 154 155 # Test pooling on a larger input, with different stride and kernel 156 # size for the 'z' dimension. 157 158 # Simulate max pooling in numpy to get the expected output. 159 input_data = np.arange(1, 5 * 27 * 27 * 64 + 1).reshape((5, 27, 27, 64)) 160 input_data = np.pad(input_data, [[0, 0], [0, 1], [0, 1], [0, 0]], 161 mode="constant") 162 expected_output = input_data[:, 1::2, 1::2, :] 163 expected_output[:, -1, :, :] = input_data[:, -2, 1::2, :] 164 expected_output[:, :, -1, :] = input_data[:, 1::2, -2, :] 165 expected_output[:, -1, -1, :] = input_data[:, -2, -2, :] 166 167 self._VerifyValues( 168 nn_ops.max_pool3d, 169 input_sizes=[1, 5, 27, 27, 64], 170 window=(1, 2, 2), 171 strides=(1, 2, 2), 172 padding="SAME", 173 expected=expected_output.flatten()) 174 175 def testKernelSmallerThanStride(self): 176 self._VerifyValues( 177 nn_ops.max_pool3d, 178 input_sizes=[1, 3, 3, 3, 1], 179 window=[1, 1, 1], 180 strides=[2, 2, 2], 181 padding="SAME", 182 expected=[1, 3, 7, 9, 19, 21, 25, 27]) 183 184 self._VerifyValues( 185 nn_ops.max_pool3d, 186 input_sizes=[1, 7, 7, 7, 1], 187 window=[2, 2, 2], 188 strides=[3, 3, 3], 189 padding="VALID", 190 expected=[58, 61, 79, 82, 205, 208, 226, 229]) 191 192 self._VerifyValues( 193 nn_ops.avg_pool3d, 194 input_sizes=[1, 3, 3, 3, 1], 195 window=[1, 1, 1], 196 strides=[2, 2, 2], 197 padding="SAME", 198 expected=[1, 3, 7, 9, 19, 21, 25, 27]) 199 200 self._VerifyValues( 201 nn_ops.avg_pool3d, 202 input_sizes=[1, 7, 7, 7, 1], 203 window=[2, 2, 2], 204 strides=[3, 3, 3], 205 padding="VALID", 206 expected=[29.5, 32.5, 50.5, 53.5, 176.5, 179.5, 197.5, 200.5]) 207 208 def _ConstructAndTestGradientForConfig(self, 209 pool_func, 210 input_sizes, 211 output_sizes, 212 window, 213 strides, 214 padding, 215 data_format, 216 use_gpu): 217 """Verifies the gradients of a pooling function. 218 219 Args: 220 pool_func: Function to be called, co.MaxPool, co.AvgPool, 221 or the Lua version. 222 input_sizes: Input tensor dimensions. 223 output_sizes: Output tensor dimensions. 224 window: Tuple of kernel dims: planes, rows, cols. 225 strides: Tuple of strides for dims: planes, rows, cols. 226 padding: Padding type. 227 data_format: Data format string. 228 use_gpu: Whether to run on GPU. 229 """ 230 total_size = 1 231 for s in input_sizes: 232 total_size *= s 233 # Initializes the input tensor with array containing incrementing 234 # numbers from 1. 235 x = np.arange(1, total_size + 1, dtype=np.float32) 236 with self.test_session(use_gpu=use_gpu): 237 input_tensor = constant_op.constant(x, shape=input_sizes, name="input") 238 err_g_margin = 1e-3 239 err_gg_margin = 1.5e-2 240 if pool_func == nn_ops.avg_pool3d: 241 func_name = "avg_pool3d" 242 x_init_value = None 243 else: 244 x_init_value = np.asfarray(np.arange(1, total_size + 1), 245 dtype=np.float32).reshape(input_sizes) 246 func_name = "max_pool3d" 247 248 ksize = [1, window[0], window[1], window[2], 1] 249 strides = [1, strides[0], strides[1], strides[2], 1] 250 t = input_tensor 251 252 if data_format == "NCDHW": 253 ksize = test_util.NHWCToNCHW(ksize) 254 strides = test_util.NHWCToNCHW(strides) 255 t = test_util.NHWCToNCHW(t) 256 257 t = pool_func( 258 t, 259 ksize=ksize, 260 strides=strides, 261 padding=padding, 262 data_format=data_format, 263 name=func_name) 264 t_g = gradients_impl.gradients(t**2, input_tensor)[0] 265 266 err_g = gradient_checker.compute_gradient_error( 267 input_tensor, 268 input_sizes, 269 t, 270 output_sizes, 271 x_init_value=x_init_value, 272 delta=1e-2) 273 err_gg = gradient_checker.compute_gradient_error( 274 input_tensor, 275 input_sizes, 276 t_g, 277 input_sizes, 278 x_init_value=x_init_value, 279 delta=1e-2) 280 281 print("%s gradient error = " % func_name, err_g) 282 self.assertLess(err_g, err_g_margin) 283 print("%s second-order gradient error = " % func_name, err_gg) 284 self.assertLess(err_gg, err_gg_margin) 285 286 def _ConstructAndTestGradient(self, 287 pool_func, 288 **kwargs): 289 """Runs _ConstructAndTestGradientForConfig for all tests configurations.""" 290 291 for data_format, use_gpu in GetTestConfigs(): 292 self._ConstructAndTestGradientForConfig(pool_func, 293 data_format=data_format, 294 use_gpu=use_gpu, 295 **kwargs) 296 297 def testMaxPoolGradValidPadding1_1_3d(self): 298 self._ConstructAndTestGradient( 299 nn_ops.max_pool3d, 300 input_sizes=[1, 3, 3, 3, 1], 301 output_sizes=[1, 3, 3, 3, 1], 302 window=(1, 1, 1), 303 strides=(1, 1, 1), 304 padding="VALID") 305 306 def testMaxPoolGradValidPadding2_1_6_3d(self): 307 self._ConstructAndTestGradient( 308 nn_ops.max_pool3d, 309 input_sizes=[1, 2, 3, 4, 2], 310 output_sizes=[1, 1, 2, 3, 2], 311 window=(2, 2, 2), 312 strides=(1, 1, 1), 313 padding="VALID") 314 315 def testMaxPoolGradValidPadding2_1_7_3d(self): 316 self._ConstructAndTestGradient( 317 nn_ops.max_pool3d, 318 input_sizes=[1, 3, 2, 7, 1], 319 output_sizes=[1, 2, 1, 6, 1], 320 window=(2, 2, 2), 321 strides=(1, 1, 1), 322 padding="VALID") 323 324 def testMaxPoolGradValidPadding1_2_3d(self): 325 self._ConstructAndTestGradient( 326 nn_ops.max_pool3d, 327 input_sizes=[1, 3, 3, 3, 1], 328 output_sizes=[1, 2, 2, 2, 1], 329 window=(1, 1, 1), 330 strides=(2, 2, 2), 331 padding="VALID") 332 333 def testMaxPoolGradValidPadding2_2_3d(self): 334 self._ConstructAndTestGradient( 335 nn_ops.max_pool3d, 336 input_sizes=[2, 2, 2, 2, 1], 337 output_sizes=[2, 1, 1, 1, 1], 338 window=(2, 2, 2), 339 strides=(2, 2, 2), 340 padding="VALID") 341 342 def testMaxPoolGradSamePadding1_1_3d(self): 343 self._ConstructAndTestGradient( 344 nn_ops.max_pool3d, 345 input_sizes=[1, 3, 2, 4, 1], 346 output_sizes=[1, 3, 2, 4, 1], 347 window=(1, 1, 1), 348 strides=(1, 1, 1), 349 padding="SAME") 350 351 def testMaxPoolGradSamePadding1_2_3d(self): 352 self._ConstructAndTestGradient( 353 nn_ops.max_pool3d, 354 input_sizes=[1, 3, 2, 4, 1], 355 output_sizes=[1, 2, 1, 2, 1], 356 window=(1, 1, 1), 357 strides=(2, 2, 2), 358 padding="SAME") 359 360 def testMaxPoolGradSamePadding2_1_3d(self): 361 self._ConstructAndTestGradient( 362 nn_ops.max_pool3d, 363 input_sizes=[1, 3, 2, 4, 1], 364 output_sizes=[1, 3, 2, 4, 1], 365 window=(2, 2, 2), 366 strides=(1, 1, 1), 367 padding="SAME") 368 369 def testMaxPoolGradSamePadding2_2_3d(self): 370 self._ConstructAndTestGradient( 371 nn_ops.max_pool3d, 372 input_sizes=[1, 5, 2, 4, 2], 373 output_sizes=[1, 3, 1, 2, 2], 374 window=(2, 2, 2), 375 strides=(2, 2, 2), 376 padding="SAME") 377 378 def testMaxPoolGradSamePadding3_1_3d(self): 379 self._ConstructAndTestGradient( 380 nn_ops.max_pool3d, 381 input_sizes=[1, 3, 4, 2, 1], 382 output_sizes=[1, 3, 4, 2, 1], 383 window=(3, 3, 3), 384 strides=(1, 1, 1), 385 padding="SAME") 386 387 def testAvgPoolGradValidPadding1_1_3d(self): 388 self._ConstructAndTestGradient( 389 nn_ops.avg_pool3d, 390 input_sizes=[1, 3, 3, 3, 1], 391 output_sizes=[1, 3, 3, 3, 1], 392 window=(1, 1, 1), 393 strides=(1, 1, 1), 394 padding="VALID") 395 396 def testAvgPoolGradValidPadding1_2_3d(self): 397 self._ConstructAndTestGradient( 398 nn_ops.avg_pool3d, 399 input_sizes=[1, 3, 3, 3, 1], 400 output_sizes=[1, 2, 2, 2, 1], 401 window=(1, 1, 1), 402 strides=(2, 2, 2), 403 padding="VALID") 404 405 def testAvgPoolGradValidPadding2_1_3d(self): 406 self._ConstructAndTestGradient( 407 nn_ops.avg_pool3d, 408 input_sizes=[1, 3, 3, 3, 2], 409 output_sizes=[1, 2, 2, 2, 2], 410 window=(2, 2, 2), 411 strides=(1, 1, 1), 412 padding="VALID") 413 414 def testAvgPoolGradValidPadding2_2_3d(self): 415 self._ConstructAndTestGradient( 416 nn_ops.avg_pool3d, 417 input_sizes=[2, 2, 2, 2, 2], 418 output_sizes=[2, 1, 1, 1, 2], 419 window=(2, 2, 2), 420 strides=(2, 2, 2), 421 padding="VALID") 422 423 def testAvgPoolGradSamePadding1_1_3d(self): 424 self._ConstructAndTestGradient( 425 nn_ops.avg_pool3d, 426 input_sizes=[1, 3, 2, 4, 2], 427 output_sizes=[1, 3, 2, 4, 2], 428 window=(1, 1, 1), 429 strides=(1, 1, 1), 430 padding="SAME") 431 432 def testAvgPoolGradSamePadding1_2_3d(self): 433 self._ConstructAndTestGradient( 434 nn_ops.avg_pool3d, 435 input_sizes=[1, 3, 2, 4, 2], 436 output_sizes=[1, 2, 1, 2, 2], 437 window=(1, 1, 1), 438 strides=(2, 2, 2), 439 padding="SAME") 440 441 def testAvgPoolGradSamePadding2_1_3d(self): 442 self._ConstructAndTestGradient( 443 nn_ops.avg_pool3d, 444 input_sizes=[1, 2, 2, 2, 1], 445 output_sizes=[1, 2, 2, 2, 1], 446 window=(2, 2, 2), 447 strides=(1, 1, 1), 448 padding="SAME") 449 450 def testAvgPoolGradSamePadding2_2_3d(self): 451 self._ConstructAndTestGradient( 452 nn_ops.avg_pool3d, 453 input_sizes=[1, 5, 2, 4, 1], 454 output_sizes=[1, 3, 1, 2, 1], 455 window=(2, 2, 2), 456 strides=(2, 2, 2), 457 padding="SAME") 458 459 def testAvgPoolGradSamePadding3_1_3d(self): 460 self._ConstructAndTestGradient( 461 nn_ops.avg_pool3d, 462 input_sizes=[1, 3, 6, 2, 1], 463 output_sizes=[1, 3, 6, 2, 1], 464 window=(3, 3, 3), 465 strides=(1, 1, 1), 466 padding="SAME") 467 468 469 if __name__ == "__main__": 470 test.main() 471