• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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