pcb defect detetcion application
[ealt-edge.git] / example-apps / PDD / pcb-defect-detection / libs / networks / slim_nets / resnet_v2_test.py
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 """Tests for slim.slim_nets.resnet_v2."""
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 import tensorflow as tf
23
24 from nets import resnet_utils
25 from nets import resnet_v2
26
27 slim = tf.contrib.slim
28
29
30 def create_test_input(batch_size, height, width, channels):
31   """Create test input tensor.
32
33   Args:
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.
38
39   Returns:
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.
43   """
44   if None in [batch_size, height, width, channels]:
45     return tf.placeholder(tf.float32, (batch_size, height, width, channels))
46   else:
47     return tf.to_float(
48         np.tile(
49             np.reshape(
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]))
54
55
56 class ResnetUtilsTest(tf.test.TestCase):
57
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())
64
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())
71
72   def testConv2DSameEven(self):
73     n, n2 = 4, 2
74
75     # Input image.
76     x = create_test_input(1, n, n, 1)
77
78     # Convolution kernel.
79     w = create_test_input(1, 3, 3, 1)
80     w = tf.reshape(w, [3, 3, 1, 1])
81
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()
85
86     y1 = slim.conv2d(x, 1, [3, 3], stride=1, scope='Conv')
87     y1_expected = tf.to_float([[14, 28, 43, 26],
88                                [28, 48, 66, 37],
89                                [43, 66, 84, 46],
90                                [26, 37, 46, 22]])
91     y1_expected = tf.reshape(y1_expected, [1, n, n, 1])
92
93     y2 = resnet_utils.subsample(y1, 2)
94     y2_expected = tf.to_float([[14, 43],
95                                [43, 84]])
96     y2_expected = tf.reshape(y2_expected, [1, n2, n2, 1])
97
98     y3 = resnet_utils.conv2d_same(x, 1, 3, stride=2, scope='Conv')
99     y3_expected = y2_expected
100
101     y4 = slim.conv2d(x, 1, [3, 3], stride=2, scope='Conv')
102     y4_expected = tf.to_float([[48, 37],
103                                [37, 22]])
104     y4_expected = tf.reshape(y4_expected, [1, n2, n2, 1])
105
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())
112
113   def testConv2DSameOdd(self):
114     n, n2 = 5, 3
115
116     # Input image.
117     x = create_test_input(1, n, n, 1)
118
119     # Convolution kernel.
120     w = create_test_input(1, 3, 3, 1)
121     w = tf.reshape(w, [3, 3, 1, 1])
122
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()
126
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])
134
135     y2 = resnet_utils.subsample(y1, 2)
136     y2_expected = tf.to_float([[14, 43, 34],
137                                [43, 84, 55],
138                                [34, 55, 30]])
139     y2_expected = tf.reshape(y2_expected, [1, n2, n2, 1])
140
141     y3 = resnet_utils.conv2d_same(x, 1, 3, stride=2, scope='Conv')
142     y3_expected = y2_expected
143
144     y4 = slim.conv2d(x, 1, [3, 3], stride=2, scope='Conv')
145     y4_expected = y2_expected
146
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())
153
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
161
162   def testEndPointsV2(self):
163     """Test the end points of a tiny v2 bottleneck network."""
164     blocks = [
165         resnet_v2.resnet_v2_block(
166             'block1', base_depth=1, num_units=2, stride=2),
167         resnet_v2.resnet_v2_block(
168             'block2', base_depth=2, num_units=2, stride=1),
169     ]
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')
173     expected = [
174         'tiny/block1/unit_1/bottleneck_v2/shortcut',
175         'tiny/block1/unit_1/bottleneck_v2/conv1',
176         'tiny/block1/unit_1/bottleneck_v2/conv2',
177         'tiny/block1/unit_1/bottleneck_v2/conv3',
178         'tiny/block1/unit_2/bottleneck_v2/conv1',
179         'tiny/block1/unit_2/bottleneck_v2/conv2',
180         'tiny/block1/unit_2/bottleneck_v2/conv3',
181         'tiny/block2/unit_1/bottleneck_v2/shortcut',
182         'tiny/block2/unit_1/bottleneck_v2/conv1',
183         'tiny/block2/unit_1/bottleneck_v2/conv2',
184         'tiny/block2/unit_1/bottleneck_v2/conv3',
185         'tiny/block2/unit_2/bottleneck_v2/conv1',
186         'tiny/block2/unit_2/bottleneck_v2/conv2',
187         'tiny/block2/unit_2/bottleneck_v2/conv3']
188     self.assertItemsEqual(expected, end_points)
189
190   def _stack_blocks_nondense(self, net, blocks):
191     """A simplified ResNet Block stacker without output stride control."""
192     for block in blocks:
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)
197     return net
198
199   def testAtrousValuesBottleneck(self):
200     """Verify the values of dense feature extraction by atrous convolution.
201
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.
205     """
206     block = resnet_v2.resnet_v2_block
207     blocks = [
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),
212     ]
213     nominal_stride = 8
214
215     # Test both odd and even input dimensions.
216     height = 30
217     width = 31
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,
227                                                        blocks,
228                                                        output_stride)
229               if output_stride is None:
230                 factor = 1
231               else:
232                 factor = nominal_stride // output_stride
233
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)
242
243
244 class ResnetCompleteNetworkTest(tf.test.TestCase):
245   """Tests with complete small ResNet v2 networks."""
246
247   def _resnet_small(self,
248                     inputs,
249                     num_classes=None,
250                     is_training=True,
251                     global_pool=True,
252                     output_stride=None,
253                     include_root_block=True,
254                     reuse=None,
255                     scope='resnet_v2_small'):
256     """A shallow and thin ResNet v2 for faster tests."""
257     block = resnet_v2.resnet_v2_block
258     blocks = [
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),
263     ]
264     return resnet_v2.resnet_v2(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,
269                                reuse=reuse,
270                                scope=scope)
271
272   def testClassificationEndPoints(self):
273     global_pool = True
274     num_classes = 10
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,
279                                               scope='resnet')
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])
285
286   def testClassificationShapes(self):
287     global_pool = True
288     num_classes = 10
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,
293                                          scope='resnet')
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)
302
303   def testFullyConvolutionalEndpointShapes(self):
304     global_pool = False
305     num_classes = 10
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,
310                                          scope='resnet')
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)
319
320   def testRootlessFullyConvolutionalEndpointShapes(self):
321     global_pool = False
322     num_classes = 10
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,
328                                          scope='resnet')
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)
337
338   def testAtrousFullyConvolutionalEndpointShapes(self):
339     global_pool = False
340     num_classes = 10
341     output_stride = 8
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,
345                                          num_classes,
346                                          global_pool=global_pool,
347                                          output_stride=output_stride,
348                                          scope='resnet')
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)
357
358   def testAtrousFullyConvolutionalValues(self):
359     """Verify dense feature extraction with atrous convolution."""
360     nominal_stride = 32
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,
369                                            is_training=False,
370                                            global_pool=False,
371                                            output_stride=output_stride)
372             if output_stride is None:
373               factor = 1
374             else:
375               factor = nominal_stride // output_stride
376             output = resnet_utils.subsample(output, factor)
377             # Make the two networks use the same weights.
378             tf.get_variable_scope().reuse_variables()
379             # Feature extraction at the nominal network rate.
380             expected, _ = self._resnet_small(inputs, None,
381                                              is_training=False,
382                                              global_pool=False)
383             sess.run(tf.global_variables_initializer())
384             self.assertAllClose(output.eval(), expected.eval(),
385                                 atol=1e-4, rtol=1e-4)
386
387   def testUnknownBatchSize(self):
388     batch = 2
389     height, width = 65, 65
390     global_pool = True
391     num_classes = 10
392     inputs = create_test_input(None, height, width, 3)
393     with slim.arg_scope(resnet_utils.resnet_arg_scope()):
394       logits, _ = self._resnet_small(inputs, num_classes,
395                                      global_pool=global_pool,
396                                      scope='resnet')
397     self.assertTrue(logits.op.name.startswith('resnet/logits'))
398     self.assertListEqual(logits.get_shape().as_list(),
399                          [None, 1, 1, num_classes])
400     images = create_test_input(batch, height, width, 3)
401     with self.test_session() as sess:
402       sess.run(tf.global_variables_initializer())
403       output = sess.run(logits, {inputs: images.eval()})
404       self.assertEqual(output.shape, (batch, 1, 1, num_classes))
405
406   def testFullyConvolutionalUnknownHeightWidth(self):
407     batch = 2
408     height, width = 65, 65
409     global_pool = False
410     inputs = create_test_input(batch, None, None, 3)
411     with slim.arg_scope(resnet_utils.resnet_arg_scope()):
412       output, _ = self._resnet_small(inputs, None,
413                                      global_pool=global_pool)
414     self.assertListEqual(output.get_shape().as_list(),
415                          [batch, None, None, 32])
416     images = create_test_input(batch, height, width, 3)
417     with self.test_session() as sess:
418       sess.run(tf.global_variables_initializer())
419       output = sess.run(output, {inputs: images.eval()})
420       self.assertEqual(output.shape, (batch, 3, 3, 32))
421
422   def testAtrousFullyConvolutionalUnknownHeightWidth(self):
423     batch = 2
424     height, width = 65, 65
425     global_pool = False
426     output_stride = 8
427     inputs = create_test_input(batch, None, None, 3)
428     with slim.arg_scope(resnet_utils.resnet_arg_scope()):
429       output, _ = self._resnet_small(inputs,
430                                      None,
431                                      global_pool=global_pool,
432                                      output_stride=output_stride)
433     self.assertListEqual(output.get_shape().as_list(),
434                          [batch, None, None, 32])
435     images = create_test_input(batch, height, width, 3)
436     with self.test_session() as sess:
437       sess.run(tf.global_variables_initializer())
438       output = sess.run(output, {inputs: images.eval()})
439       self.assertEqual(output.shape, (batch, 9, 9, 32))
440
441
442 if __name__ == '__main__':
443   tf.test.main()