1 # Copyright 2016 The TensorFlow Authors. All Rights Reserved.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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 slim.slim_nets.resnet_v1."""
17 from __future__ import absolute_import
18 from __future__ import division
19 from __future__ import print_function
22 import tensorflow as tf
24 from nets import resnet_utils
25 from nets import resnet_v1
27 slim = tf.contrib.slim
30 def create_test_input(batch_size, height, width, channels):
31 """Create test input tensor.
34 batch_size: The number of images per batch or `None` if unknown.
35 height: The height of each image or `None` if unknown.
36 width: The width of each image or `None` if unknown.
37 channels: The number of channels per image or `None` if unknown.
40 Either a placeholder `Tensor` of dimension
41 [batch_size, height, width, channels] if any of the inputs are `None` or a
42 constant `Tensor` with the mesh grid values along the spatial dimensions.
44 if None in [batch_size, height, width, channels]:
45 return tf.placeholder(tf.float32, (batch_size, height, width, channels))
50 np.reshape(np.arange(height), [height, 1]) +
51 np.reshape(np.arange(width), [1, width]),
52 [1, height, width, 1]),
53 [batch_size, 1, 1, channels]))
56 class ResnetUtilsTest(tf.test.TestCase):
58 def testSubsampleThreeByThree(self):
59 x = tf.reshape(tf.to_float(tf.range(9)), [1, 3, 3, 1])
60 x = resnet_utils.subsample(x, 2)
61 expected = tf.reshape(tf.constant([0, 2, 6, 8]), [1, 2, 2, 1])
62 with self.test_session():
63 self.assertAllClose(x.eval(), expected.eval())
65 def testSubsampleFourByFour(self):
66 x = tf.reshape(tf.to_float(tf.range(16)), [1, 4, 4, 1])
67 x = resnet_utils.subsample(x, 2)
68 expected = tf.reshape(tf.constant([0, 2, 8, 10]), [1, 2, 2, 1])
69 with self.test_session():
70 self.assertAllClose(x.eval(), expected.eval())
72 def testConv2DSameEven(self):
76 x = create_test_input(1, n, n, 1)
79 w = create_test_input(1, 3, 3, 1)
80 w = tf.reshape(w, [3, 3, 1, 1])
82 tf.get_variable('Conv/weights', initializer=w)
83 tf.get_variable('Conv/biases', initializer=tf.zeros([1]))
84 tf.get_variable_scope().reuse_variables()
86 y1 = slim.conv2d(x, 1, [3, 3], stride=1, scope='Conv')
87 y1_expected = tf.to_float([[14, 28, 43, 26],
91 y1_expected = tf.reshape(y1_expected, [1, n, n, 1])
93 y2 = resnet_utils.subsample(y1, 2)
94 y2_expected = tf.to_float([[14, 43],
96 y2_expected = tf.reshape(y2_expected, [1, n2, n2, 1])
98 y3 = resnet_utils.conv2d_same(x, 1, 3, stride=2, scope='Conv')
99 y3_expected = y2_expected
101 y4 = slim.conv2d(x, 1, [3, 3], stride=2, scope='Conv')
102 y4_expected = tf.to_float([[48, 37],
104 y4_expected = tf.reshape(y4_expected, [1, n2, n2, 1])
106 with self.test_session() as sess:
107 sess.run(tf.global_variables_initializer())
108 self.assertAllClose(y1.eval(), y1_expected.eval())
109 self.assertAllClose(y2.eval(), y2_expected.eval())
110 self.assertAllClose(y3.eval(), y3_expected.eval())
111 self.assertAllClose(y4.eval(), y4_expected.eval())
113 def testConv2DSameOdd(self):
117 x = create_test_input(1, n, n, 1)
119 # Convolution kernel.
120 w = create_test_input(1, 3, 3, 1)
121 w = tf.reshape(w, [3, 3, 1, 1])
123 tf.get_variable('Conv/weights', initializer=w)
124 tf.get_variable('Conv/biases', initializer=tf.zeros([1]))
125 tf.get_variable_scope().reuse_variables()
127 y1 = slim.conv2d(x, 1, [3, 3], stride=1, scope='Conv')
128 y1_expected = tf.to_float([[14, 28, 43, 58, 34],
129 [28, 48, 66, 84, 46],
130 [43, 66, 84, 102, 55],
131 [58, 84, 102, 120, 64],
132 [34, 46, 55, 64, 30]])
133 y1_expected = tf.reshape(y1_expected, [1, n, n, 1])
135 y2 = resnet_utils.subsample(y1, 2)
136 y2_expected = tf.to_float([[14, 43, 34],
139 y2_expected = tf.reshape(y2_expected, [1, n2, n2, 1])
141 y3 = resnet_utils.conv2d_same(x, 1, 3, stride=2, scope='Conv')
142 y3_expected = y2_expected
144 y4 = slim.conv2d(x, 1, [3, 3], stride=2, scope='Conv')
145 y4_expected = y2_expected
147 with self.test_session() as sess:
148 sess.run(tf.global_variables_initializer())
149 self.assertAllClose(y1.eval(), y1_expected.eval())
150 self.assertAllClose(y2.eval(), y2_expected.eval())
151 self.assertAllClose(y3.eval(), y3_expected.eval())
152 self.assertAllClose(y4.eval(), y4_expected.eval())
154 def _resnet_plain(self, inputs, blocks, output_stride=None, scope=None):
155 """A plain ResNet without extra layers before or after the ResNet blocks."""
156 with tf.variable_scope(scope, values=[inputs]):
157 with slim.arg_scope([slim.conv2d], outputs_collections='end_points'):
158 net = resnet_utils.stack_blocks_dense(inputs, blocks, output_stride)
159 end_points = slim.utils.convert_collection_to_dict('end_points')
160 return net, end_points
162 def testEndPointsV1(self):
163 """Test the end points of a tiny v1 bottleneck network."""
165 resnet_v1.resnet_v1_block(
166 'block1', base_depth=1, num_units=2, stride=2),
167 resnet_v1.resnet_v1_block(
168 'block2', base_depth=2, num_units=2, stride=1),
170 inputs = create_test_input(2, 32, 16, 3)
171 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
172 _, end_points = self._resnet_plain(inputs, blocks, scope='tiny')
174 'tiny/block1/unit_1/bottleneck_v1/shortcut',
175 'tiny/block1/unit_1/bottleneck_v1/conv1',
176 'tiny/block1/unit_1/bottleneck_v1/conv2',
177 'tiny/block1/unit_1/bottleneck_v1/conv3',
178 'tiny/block1/unit_2/bottleneck_v1/conv1',
179 'tiny/block1/unit_2/bottleneck_v1/conv2',
180 'tiny/block1/unit_2/bottleneck_v1/conv3',
181 'tiny/block2/unit_1/bottleneck_v1/shortcut',
182 'tiny/block2/unit_1/bottleneck_v1/conv1',
183 'tiny/block2/unit_1/bottleneck_v1/conv2',
184 'tiny/block2/unit_1/bottleneck_v1/conv3',
185 'tiny/block2/unit_2/bottleneck_v1/conv1',
186 'tiny/block2/unit_2/bottleneck_v1/conv2',
187 'tiny/block2/unit_2/bottleneck_v1/conv3']
188 self.assertItemsEqual(expected, end_points)
190 def _stack_blocks_nondense(self, net, blocks):
191 """A simplified ResNet Block stacker without output stride control."""
193 with tf.variable_scope(block.scope, 'block', [net]):
194 for i, unit in enumerate(block.args):
195 with tf.variable_scope('unit_%d' % (i + 1), values=[net]):
196 net = block.unit_fn(net, rate=1, **unit)
199 def testAtrousValuesBottleneck(self):
200 """Verify the values of dense feature extraction by atrous convolution.
202 Make sure that dense feature extraction by stack_blocks_dense() followed by
203 subsampling gives identical results to feature extraction at the nominal
204 network output stride using the simple self._stack_blocks_nondense() above.
206 block = resnet_v1.resnet_v1_block
208 block('block1', base_depth=1, num_units=2, stride=2),
209 block('block2', base_depth=2, num_units=2, stride=2),
210 block('block3', base_depth=4, num_units=2, stride=2),
211 block('block4', base_depth=8, num_units=2, stride=1),
215 # Test both odd and even input dimensions.
218 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
219 with slim.arg_scope([slim.batch_norm], is_training=False):
220 for output_stride in [1, 2, 4, 8, None]:
221 with tf.Graph().as_default():
222 with self.test_session() as sess:
223 tf.set_random_seed(0)
224 inputs = create_test_input(1, height, width, 3)
225 # Dense feature extraction followed by subsampling.
226 output = resnet_utils.stack_blocks_dense(inputs,
229 if output_stride is None:
232 factor = nominal_stride // output_stride
234 output = resnet_utils.subsample(output, factor)
235 # Make the two networks use the same weights.
236 tf.get_variable_scope().reuse_variables()
237 # Feature extraction at the nominal network rate.
238 expected = self._stack_blocks_nondense(inputs, blocks)
239 sess.run(tf.global_variables_initializer())
240 output, expected = sess.run([output, expected])
241 self.assertAllClose(output, expected, atol=1e-4, rtol=1e-4)
244 class ResnetCompleteNetworkTest(tf.test.TestCase):
245 """Tests with complete small ResNet v1 networks."""
247 def _resnet_small(self,
253 include_root_block=True,
255 scope='resnet_v1_small'):
256 """A shallow and thin ResNet v1 for faster tests."""
257 block = resnet_v1.resnet_v1_block
259 block('block1', base_depth=1, num_units=3, stride=2),
260 block('block2', base_depth=2, num_units=3, stride=2),
261 block('block3', base_depth=4, num_units=3, stride=2),
262 block('block4', base_depth=8, num_units=2, stride=1),
264 return resnet_v1.resnet_v1(inputs, blocks, num_classes,
265 is_training=is_training,
266 global_pool=global_pool,
267 output_stride=output_stride,
268 include_root_block=include_root_block,
272 def testClassificationEndPoints(self):
275 inputs = create_test_input(2, 224, 224, 3)
276 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
277 logits, end_points = self._resnet_small(inputs, num_classes,
278 global_pool=global_pool,
280 self.assertTrue(logits.op.name.startswith('resnet/logits'))
281 self.assertListEqual(logits.get_shape().as_list(), [2, 1, 1, num_classes])
282 self.assertTrue('predictions' in end_points)
283 self.assertListEqual(end_points['predictions'].get_shape().as_list(),
284 [2, 1, 1, num_classes])
286 def testClassificationShapes(self):
289 inputs = create_test_input(2, 224, 224, 3)
290 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
291 _, end_points = self._resnet_small(inputs, num_classes,
292 global_pool=global_pool,
294 endpoint_to_shape = {
295 'resnet/block1': [2, 28, 28, 4],
296 'resnet/block2': [2, 14, 14, 8],
297 'resnet/block3': [2, 7, 7, 16],
298 'resnet/block4': [2, 7, 7, 32]}
299 for endpoint in endpoint_to_shape:
300 shape = endpoint_to_shape[endpoint]
301 self.assertListEqual(end_points[endpoint].get_shape().as_list(), shape)
303 def testFullyConvolutionalEndpointShapes(self):
306 inputs = create_test_input(2, 321, 321, 3)
307 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
308 _, end_points = self._resnet_small(inputs, num_classes,
309 global_pool=global_pool,
311 endpoint_to_shape = {
312 'resnet/block1': [2, 41, 41, 4],
313 'resnet/block2': [2, 21, 21, 8],
314 'resnet/block3': [2, 11, 11, 16],
315 'resnet/block4': [2, 11, 11, 32]}
316 for endpoint in endpoint_to_shape:
317 shape = endpoint_to_shape[endpoint]
318 self.assertListEqual(end_points[endpoint].get_shape().as_list(), shape)
320 def testRootlessFullyConvolutionalEndpointShapes(self):
323 inputs = create_test_input(2, 128, 128, 3)
324 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
325 _, end_points = self._resnet_small(inputs, num_classes,
326 global_pool=global_pool,
327 include_root_block=False,
329 endpoint_to_shape = {
330 'resnet/block1': [2, 64, 64, 4],
331 'resnet/block2': [2, 32, 32, 8],
332 'resnet/block3': [2, 16, 16, 16],
333 'resnet/block4': [2, 16, 16, 32]}
334 for endpoint in endpoint_to_shape:
335 shape = endpoint_to_shape[endpoint]
336 self.assertListEqual(end_points[endpoint].get_shape().as_list(), shape)
338 def testAtrousFullyConvolutionalEndpointShapes(self):
342 inputs = create_test_input(2, 321, 321, 3)
343 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
344 _, end_points = self._resnet_small(inputs,
346 global_pool=global_pool,
347 output_stride=output_stride,
349 endpoint_to_shape = {
350 'resnet/block1': [2, 41, 41, 4],
351 'resnet/block2': [2, 41, 41, 8],
352 'resnet/block3': [2, 41, 41, 16],
353 'resnet/block4': [2, 41, 41, 32]}
354 for endpoint in endpoint_to_shape:
355 shape = endpoint_to_shape[endpoint]
356 self.assertListEqual(end_points[endpoint].get_shape().as_list(), shape)
358 def testAtrousFullyConvolutionalValues(self):
359 """Verify dense feature extraction with atrous convolution."""
361 for output_stride in [4, 8, 16, 32, None]:
362 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
363 with tf.Graph().as_default():
364 with self.test_session() as sess:
365 tf.set_random_seed(0)
366 inputs = create_test_input(2, 81, 81, 3)
367 # Dense feature extraction followed by subsampling.
368 output, _ = self._resnet_small(inputs, None, is_training=False,
370 output_stride=output_stride)
371 if output_stride is None:
374 factor = nominal_stride // output_stride
375 output = resnet_utils.subsample(output, factor)
376 # Make the two networks use the same weights.
377 tf.get_variable_scope().reuse_variables()
378 # Feature extraction at the nominal network rate.
379 expected, _ = self._resnet_small(inputs, None, is_training=False,
381 sess.run(tf.global_variables_initializer())
382 self.assertAllClose(output.eval(), expected.eval(),
383 atol=1e-4, rtol=1e-4)
385 def testUnknownBatchSize(self):
387 height, width = 65, 65
390 inputs = create_test_input(None, height, width, 3)
391 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
392 logits, _ = self._resnet_small(inputs, num_classes,
393 global_pool=global_pool,
395 self.assertTrue(logits.op.name.startswith('resnet/logits'))
396 self.assertListEqual(logits.get_shape().as_list(),
397 [None, 1, 1, num_classes])
398 images = create_test_input(batch, height, width, 3)
399 with self.test_session() as sess:
400 sess.run(tf.global_variables_initializer())
401 output = sess.run(logits, {inputs: images.eval()})
402 self.assertEqual(output.shape, (batch, 1, 1, num_classes))
404 def testFullyConvolutionalUnknownHeightWidth(self):
406 height, width = 65, 65
408 inputs = create_test_input(batch, None, None, 3)
409 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
410 output, _ = self._resnet_small(inputs, None, global_pool=global_pool)
411 self.assertListEqual(output.get_shape().as_list(),
412 [batch, None, None, 32])
413 images = create_test_input(batch, height, width, 3)
414 with self.test_session() as sess:
415 sess.run(tf.global_variables_initializer())
416 output = sess.run(output, {inputs: images.eval()})
417 self.assertEqual(output.shape, (batch, 3, 3, 32))
419 def testAtrousFullyConvolutionalUnknownHeightWidth(self):
421 height, width = 65, 65
424 inputs = create_test_input(batch, None, None, 3)
425 with slim.arg_scope(resnet_utils.resnet_arg_scope()):
426 output, _ = self._resnet_small(inputs,
428 global_pool=global_pool,
429 output_stride=output_stride)
430 self.assertListEqual(output.get_shape().as_list(),
431 [batch, None, None, 32])
432 images = create_test_input(batch, height, width, 3)
433 with self.test_session() as sess:
434 sess.run(tf.global_variables_initializer())
435 output = sess.run(output, {inputs: images.eval()})
436 self.assertEqual(output.shape, (batch, 9, 9, 32))
439 if __name__ == '__main__':