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