model_lib_test.py 19.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
15
"""Tests for object detection model library."""
16
17
18
19
20
21
22
23
24
25
26

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import functools
import os

import numpy as np
import tensorflow as tf

27
28
29
from tensorflow.contrib.tpu.python.tpu import tpu_config
from tensorflow.contrib.tpu.python.tpu import tpu_estimator

30
31
from object_detection import inputs
from object_detection import model_hparams
32
from object_detection import model_lib
33
34
35
36
37
from object_detection.builders import model_builder
from object_detection.core import standard_fields as fields
from object_detection.utils import config_util


38
39
40
# Model for test. Options are:
# 'ssd_inception_v2_pets', 'faster_rcnn_resnet50_pets'
MODEL_NAME_FOR_TEST = 'ssd_inception_v2_pets'
41

42
43
44
# Model for testing keypoints.
MODEL_NAME_FOR_KEYPOINTS_TEST = 'ssd_mobilenet_v1_fpp'

45
46
47

def _get_data_path():
  """Returns an absolute path to TFRecord file."""
48
  return os.path.join(tf.resource_loader.get_data_files_path(), 'test_data',
49
50
51
                      'pets_examples.record')


52
53
def get_pipeline_config_path(model_name):
  """Returns path to the local pipeline config file."""
54
55
56
57
58
59
  if model_name == MODEL_NAME_FOR_KEYPOINTS_TEST:
    return os.path.join(tf.resource_loader.get_data_files_path(), 'test_data',
                        model_name + '.config')
  else:
    return os.path.join(tf.resource_loader.get_data_files_path(), 'samples',
                        'configs', model_name + '.config')
60
61


62
63
def _get_labelmap_path():
  """Returns an absolute path to label map file."""
64
  return os.path.join(tf.resource_loader.get_data_files_path(), 'data',
65
66
67
                      'pet_label_map.pbtxt')


68
69
70
71
72
73
def _get_keypoints_labelmap_path():
  """Returns an absolute path to label map file."""
  return os.path.join(tf.resource_loader.get_data_files_path(), 'data',
                      'face_person_with_keypoints_label_map.pbtxt')


74
75
def _get_configs_for_model(model_name):
  """Returns configurations for model."""
76
  filename = get_pipeline_config_path(model_name)
77
  data_path = _get_data_path()
78
79
80
81
  if model_name == MODEL_NAME_FOR_KEYPOINTS_TEST:
    label_map_path = _get_keypoints_labelmap_path()
  else:
    label_map_path = _get_labelmap_path()
82
  configs = config_util.get_configs_from_pipeline_file(filename)
83
84
85
86
87
  override_dict = {
      'train_input_path': data_path,
      'eval_input_path': data_path,
      'label_map_path': label_map_path
  }
88
  configs = config_util.merge_external_params_with_configs(
89
      configs, kwargs_dict=override_dict)
90
91
92
  return configs


93
94
95
96
97
98
99
100
101
102
103
104
105
106
def _make_initializable_iterator(dataset):
  """Creates an iterator, and initializes tables.

  Args:
    dataset: A `tf.data.Dataset` object.

  Returns:
    A `tf.data.Iterator`.
  """
  iterator = dataset.make_initializable_iterator()
  tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer)
  return iterator


107
class ModelLibTest(tf.test.TestCase):
108
109
110
111
112

  @classmethod
  def setUpClass(cls):
    tf.reset_default_graph()

113
114
  def _assert_model_fn_for_train_eval(self, configs, mode,
                                      class_agnostic=False):
115
116
117
    model_config = configs['model']
    train_config = configs['train_config']
    with tf.Graph().as_default():
118
      if mode == 'train':
119
120
121
122
        features, labels = _make_initializable_iterator(
            inputs.create_train_input_fn(configs['train_config'],
                                         configs['train_input_config'],
                                         configs['model'])()).get_next()
123
        model_mode = tf.estimator.ModeKeys.TRAIN
124
        batch_size = train_config.batch_size
125
      elif mode == 'eval':
126
127
128
129
        features, labels = _make_initializable_iterator(
            inputs.create_eval_input_fn(configs['eval_config'],
                                        configs['eval_input_config'],
                                        configs['model'])()).get_next()
130
131
132
        model_mode = tf.estimator.ModeKeys.EVAL
        batch_size = 1
      elif mode == 'eval_on_train':
133
134
135
136
        features, labels = _make_initializable_iterator(
            inputs.create_eval_input_fn(configs['eval_config'],
                                        configs['train_input_config'],
                                        configs['model'])()).get_next()
137
        model_mode = tf.estimator.ModeKeys.EVAL
138
139
140
141
142
143
144
145
        batch_size = 1

      detection_model_fn = functools.partial(
          model_builder.build, model_config=model_config, is_training=True)

      hparams = model_hparams.create_hparams(
          hparams_overrides='load_pretrained=false')

146
      model_fn = model_lib.create_model_fn(detection_model_fn, configs, hparams)
147
      estimator_spec = model_fn(features, labels, model_mode)
148
149
150

      self.assertIsNotNone(estimator_spec.loss)
      self.assertIsNotNone(estimator_spec.predictions)
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
      if mode == 'eval' or mode == 'eval_on_train':
        if class_agnostic:
          self.assertNotIn('detection_classes', estimator_spec.predictions)
        else:
          detection_classes = estimator_spec.predictions['detection_classes']
          self.assertEqual(batch_size, detection_classes.shape.as_list()[0])
          self.assertEqual(tf.float32, detection_classes.dtype)
        detection_boxes = estimator_spec.predictions['detection_boxes']
        detection_scores = estimator_spec.predictions['detection_scores']
        num_detections = estimator_spec.predictions['num_detections']
        self.assertEqual(batch_size, detection_boxes.shape.as_list()[0])
        self.assertEqual(tf.float32, detection_boxes.dtype)
        self.assertEqual(batch_size, detection_scores.shape.as_list()[0])
        self.assertEqual(tf.float32, detection_scores.dtype)
        self.assertEqual(tf.float32, num_detections.dtype)
166
167
168
        if mode == 'eval':
          self.assertIn('Detections_Left_Groundtruth_Right/0',
                        estimator_spec.eval_metric_ops)
169
      if model_mode == tf.estimator.ModeKeys.TRAIN:
170
171
172
        self.assertIsNotNone(estimator_spec.train_op)
      return estimator_spec

173
  def _assert_model_fn_for_predict(self, configs):
174
175
176
    model_config = configs['model']

    with tf.Graph().as_default():
177
178
179
180
      features, _ = _make_initializable_iterator(
          inputs.create_eval_input_fn(configs['eval_config'],
                                      configs['eval_input_config'],
                                      configs['model'])()).get_next()
181
182
183
184
185
186
      detection_model_fn = functools.partial(
          model_builder.build, model_config=model_config, is_training=False)

      hparams = model_hparams.create_hparams(
          hparams_overrides='load_pretrained=false')

187
      model_fn = model_lib.create_model_fn(detection_model_fn, configs, hparams)
188
189
190
191
192
193
194
195
196
      estimator_spec = model_fn(features, None, tf.estimator.ModeKeys.PREDICT)

      self.assertIsNone(estimator_spec.loss)
      self.assertIsNone(estimator_spec.train_op)
      self.assertIsNotNone(estimator_spec.predictions)
      self.assertIsNotNone(estimator_spec.export_outputs)
      self.assertIn(tf.saved_model.signature_constants.PREDICT_METHOD_NAME,
                    estimator_spec.export_outputs)

197
  def test_model_fn_in_train_mode(self):
198
199
    """Tests the model function in TRAIN mode."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
200
    self._assert_model_fn_for_train_eval(configs, 'train')
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  def test_model_fn_in_train_mode_freeze_all_variables(self):
    """Tests model_fn TRAIN mode with all variables frozen."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
    configs['train_config'].freeze_variables.append('.*')
    with self.assertRaisesRegexp(ValueError, 'No variables to optimize'):
      self._assert_model_fn_for_train_eval(configs, 'train')

  def test_model_fn_in_train_mode_freeze_all_included_variables(self):
    """Tests model_fn TRAIN mode with all included variables frozen."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
    train_config = configs['train_config']
    train_config.update_trainable_variables.append('FeatureExtractor')
    train_config.freeze_variables.append('.*')
    with self.assertRaisesRegexp(ValueError, 'No variables to optimize'):
      self._assert_model_fn_for_train_eval(configs, 'train')

  def test_model_fn_in_train_mode_freeze_box_predictor(self):
    """Tests model_fn TRAIN mode with FeatureExtractor variables frozen."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
    train_config = configs['train_config']
    train_config.update_trainable_variables.append('FeatureExtractor')
    train_config.update_trainable_variables.append('BoxPredictor')
    train_config.freeze_variables.append('FeatureExtractor')
    self._assert_model_fn_for_train_eval(configs, 'train')

227
  def test_model_fn_in_eval_mode(self):
228
229
    """Tests the model function in EVAL mode."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
230
231
    self._assert_model_fn_for_train_eval(configs, 'eval')

232
233
234
235
236
237
238
239
240
241
242
  def test_model_fn_in_keypoints_eval_mode(self):
    """Tests the model function in EVAL mode with keypoints config."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_KEYPOINTS_TEST)
    estimator_spec = self._assert_model_fn_for_train_eval(configs, 'eval')
    metric_ops = estimator_spec.eval_metric_ops
    self.assertIn('Keypoints_Precision/mAP ByCategory/face', metric_ops)
    self.assertIn('Keypoints_Precision/mAP ByCategory/PERSON', metric_ops)
    detection_keypoints = estimator_spec.predictions['detection_keypoints']
    self.assertEqual(1, detection_keypoints.shape.as_list()[0])
    self.assertEqual(tf.float32, detection_keypoints.dtype)

243
244
245
246
  def test_model_fn_in_eval_on_train_mode(self):
    """Tests the model function in EVAL mode with train data."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
    self._assert_model_fn_for_train_eval(configs, 'eval_on_train')
247

248
  def test_model_fn_in_predict_mode(self):
249
250
    """Tests the model function in PREDICT mode."""
    configs = _get_configs_for_model(MODEL_NAME_FOR_TEST)
251
252
253
254
255
256
257
258
259
260
261
262
263
    self._assert_model_fn_for_predict(configs)

  def test_create_estimator_and_inputs(self):
    """Tests that Estimator and input function are constructed correctly."""
    run_config = tf.estimator.RunConfig()
    hparams = model_hparams.create_hparams(
        hparams_overrides='load_pretrained=false')
    pipeline_config_path = get_pipeline_config_path(MODEL_NAME_FOR_TEST)
    train_steps = 20
    train_and_eval_dict = model_lib.create_estimator_and_inputs(
        run_config,
        hparams,
        pipeline_config_path,
264
        train_steps=train_steps)
265
266
267
268
    estimator = train_and_eval_dict['estimator']
    train_steps = train_and_eval_dict['train_steps']
    self.assertIsInstance(estimator, tf.estimator.Estimator)
    self.assertEqual(20, train_steps)
269
    self.assertIn('train_input_fn', train_and_eval_dict)
270
    self.assertIn('eval_input_fns', train_and_eval_dict)
271
    self.assertIn('eval_on_train_input_fn', train_and_eval_dict)
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

  def test_create_estimator_with_default_train_eval_steps(self):
    """Tests that number of train/eval defaults to config values."""
    run_config = tf.estimator.RunConfig()
    hparams = model_hparams.create_hparams(
        hparams_overrides='load_pretrained=false')
    pipeline_config_path = get_pipeline_config_path(MODEL_NAME_FOR_TEST)
    configs = config_util.get_configs_from_pipeline_file(pipeline_config_path)
    config_train_steps = configs['train_config'].num_steps
    train_and_eval_dict = model_lib.create_estimator_and_inputs(
        run_config, hparams, pipeline_config_path)
    estimator = train_and_eval_dict['estimator']
    train_steps = train_and_eval_dict['train_steps']

    self.assertIsInstance(estimator, tf.estimator.Estimator)
    self.assertEqual(config_train_steps, train_steps)

  def test_create_tpu_estimator_and_inputs(self):
    """Tests that number of train/eval defaults to config values."""

    run_config = tpu_config.RunConfig()
    hparams = model_hparams.create_hparams(
        hparams_overrides='load_pretrained=false')
    pipeline_config_path = get_pipeline_config_path(MODEL_NAME_FOR_TEST)
    train_steps = 20
    train_and_eval_dict = model_lib.create_estimator_and_inputs(
        run_config,
        hparams,
        pipeline_config_path,
        train_steps=train_steps,
        use_tpu_estimator=True)
    estimator = train_and_eval_dict['estimator']
    train_steps = train_and_eval_dict['train_steps']

    self.assertIsInstance(estimator, tpu_estimator.TPUEstimator)
    self.assertEqual(20, train_steps)

  def test_create_train_and_eval_specs(self):
    """Tests that `TrainSpec` and `EvalSpec` is created correctly."""
    run_config = tf.estimator.RunConfig()
    hparams = model_hparams.create_hparams(
        hparams_overrides='load_pretrained=false')
    pipeline_config_path = get_pipeline_config_path(MODEL_NAME_FOR_TEST)
    train_steps = 20
    train_and_eval_dict = model_lib.create_estimator_and_inputs(
        run_config,
        hparams,
        pipeline_config_path,
320
        train_steps=train_steps)
321
    train_input_fn = train_and_eval_dict['train_input_fn']
322
    eval_input_fns = train_and_eval_dict['eval_input_fns']
323
    eval_on_train_input_fn = train_and_eval_dict['eval_on_train_input_fn']
324
325
326
327
328
    predict_input_fn = train_and_eval_dict['predict_input_fn']
    train_steps = train_and_eval_dict['train_steps']

    train_spec, eval_specs = model_lib.create_train_and_eval_specs(
        train_input_fn,
329
        eval_input_fns,
330
        eval_on_train_input_fn,
331
332
333
334
        predict_input_fn,
        train_steps,
        eval_on_train_data=True,
        final_exporter_name='exporter',
335
        eval_spec_names=['holdout'])
336
337
    self.assertEqual(train_steps, train_spec.max_steps)
    self.assertEqual(2, len(eval_specs))
338
    self.assertEqual(None, eval_specs[0].steps)
339
    self.assertEqual('holdout', eval_specs[0].name)
340
    self.assertEqual('exporter', eval_specs[0].exporters[0].name)
341
    self.assertEqual(None, eval_specs[1].steps)
342
343
344
    self.assertEqual('eval_on_train', eval_specs[1].name)

  def test_experiment(self):
345
    """Tests that the `Experiment` object is constructed correctly."""
346
347
348
349
350
351
352
353
354
355
356
    run_config = tf.estimator.RunConfig()
    hparams = model_hparams.create_hparams(
        hparams_overrides='load_pretrained=false')
    pipeline_config_path = get_pipeline_config_path(MODEL_NAME_FOR_TEST)
    experiment = model_lib.populate_experiment(
        run_config,
        hparams,
        pipeline_config_path,
        train_steps=10,
        eval_steps=20)
    self.assertEqual(10, experiment.train_steps)
357
    self.assertEqual(None, experiment.eval_steps)
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378


class UnbatchTensorsTest(tf.test.TestCase):

  def test_unbatch_without_unpadding(self):
    image_placeholder = tf.placeholder(tf.float32, [2, None, None, None])
    groundtruth_boxes_placeholder = tf.placeholder(tf.float32, [2, None, None])
    groundtruth_classes_placeholder = tf.placeholder(tf.float32,
                                                     [2, None, None])
    groundtruth_weights_placeholder = tf.placeholder(tf.float32, [2, None])

    tensor_dict = {
        fields.InputDataFields.image:
            image_placeholder,
        fields.InputDataFields.groundtruth_boxes:
            groundtruth_boxes_placeholder,
        fields.InputDataFields.groundtruth_classes:
            groundtruth_classes_placeholder,
        fields.InputDataFields.groundtruth_weights:
            groundtruth_weights_placeholder
    }
379
    unbatched_tensor_dict = model_lib.unstack_batch(
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
        tensor_dict, unpad_groundtruth_tensors=False)

    with self.test_session() as sess:
      unbatched_tensor_dict_out = sess.run(
          unbatched_tensor_dict,
          feed_dict={
              image_placeholder:
                  np.random.rand(2, 4, 4, 3).astype(np.float32),
              groundtruth_boxes_placeholder:
                  np.random.rand(2, 5, 4).astype(np.float32),
              groundtruth_classes_placeholder:
                  np.random.rand(2, 5, 6).astype(np.float32),
              groundtruth_weights_placeholder:
                  np.random.rand(2, 5).astype(np.float32)
          })
    for image_out in unbatched_tensor_dict_out[fields.InputDataFields.image]:
      self.assertAllEqual(image_out.shape, [4, 4, 3])
    for groundtruth_boxes_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_boxes]:
      self.assertAllEqual(groundtruth_boxes_out.shape, [5, 4])
    for groundtruth_classes_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_classes]:
      self.assertAllEqual(groundtruth_classes_out.shape, [5, 6])
    for groundtruth_weights_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_weights]:
      self.assertAllEqual(groundtruth_weights_out.shape, [5])

  def test_unbatch_and_unpad_groundtruth_tensors(self):
    image_placeholder = tf.placeholder(tf.float32, [2, None, None, None])
    groundtruth_boxes_placeholder = tf.placeholder(tf.float32, [2, 5, None])
    groundtruth_classes_placeholder = tf.placeholder(tf.float32, [2, 5, None])
    groundtruth_weights_placeholder = tf.placeholder(tf.float32, [2, 5])
    num_groundtruth_placeholder = tf.placeholder(tf.int32, [2])

    tensor_dict = {
        fields.InputDataFields.image:
            image_placeholder,
        fields.InputDataFields.groundtruth_boxes:
            groundtruth_boxes_placeholder,
        fields.InputDataFields.groundtruth_classes:
            groundtruth_classes_placeholder,
        fields.InputDataFields.groundtruth_weights:
            groundtruth_weights_placeholder,
        fields.InputDataFields.num_groundtruth_boxes:
            num_groundtruth_placeholder
    }
426
    unbatched_tensor_dict = model_lib.unstack_batch(
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
        tensor_dict, unpad_groundtruth_tensors=True)
    with self.test_session() as sess:
      unbatched_tensor_dict_out = sess.run(
          unbatched_tensor_dict,
          feed_dict={
              image_placeholder:
                  np.random.rand(2, 4, 4, 3).astype(np.float32),
              groundtruth_boxes_placeholder:
                  np.random.rand(2, 5, 4).astype(np.float32),
              groundtruth_classes_placeholder:
                  np.random.rand(2, 5, 6).astype(np.float32),
              groundtruth_weights_placeholder:
                  np.random.rand(2, 5).astype(np.float32),
              num_groundtruth_placeholder:
                  np.array([3, 3], np.int32)
          })
    for image_out in unbatched_tensor_dict_out[fields.InputDataFields.image]:
      self.assertAllEqual(image_out.shape, [4, 4, 3])
    for groundtruth_boxes_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_boxes]:
      self.assertAllEqual(groundtruth_boxes_out.shape, [3, 4])
    for groundtruth_classes_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_classes]:
      self.assertAllEqual(groundtruth_classes_out.shape, [3, 6])
    for groundtruth_weights_out in unbatched_tensor_dict_out[
        fields.InputDataFields.groundtruth_weights]:
      self.assertAllEqual(groundtruth_weights_out.shape, [3])


if __name__ == '__main__':
  tf.test.main()