# Copyright 2018 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. # ============================================================================== """Tests for lstm_object_detection.tensorflow.model_builder.""" import tensorflow.compat.v1 as tf from google.protobuf import text_format from lstm_object_detection import model_builder from lstm_object_detection.meta_architectures import lstm_ssd_meta_arch from lstm_object_detection.protos import pipeline_pb2 as internal_pipeline_pb2 from object_detection.protos import pipeline_pb2 class ModelBuilderTest(tf.test.TestCase): def create_train_model(self, model_config, lstm_config): """Builds a DetectionModel based on the model config. Args: model_config: A model.proto object containing the config for the desired DetectionModel. lstm_config: LstmModel config proto that specifies LSTM train/eval configs. Returns: DetectionModel based on the config. """ return model_builder.build(model_config, lstm_config, is_training=True) def create_eval_model(self, model_config, lstm_config): """Builds a DetectionModel based on the model config. Args: model_config: A model.proto object containing the config for the desired DetectionModel. lstm_config: LstmModel config proto that specifies LSTM train/eval configs. Returns: DetectionModel based on the config. """ return model_builder.build(model_config, lstm_config, is_training=False) def get_model_configs_from_proto(self): """Creates a model text proto for testing. Returns: A dictionary of model configs. """ model_text_proto = """ [lstm_object_detection.protos.lstm_model] { train_unroll_length: 4 eval_unroll_length: 4 } model { ssd { feature_extractor { type: 'lstm_ssd_mobilenet_v1' conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } negative_class_weight: 2.0 box_coder { faster_rcnn_box_coder { } } matcher { argmax_matcher { } } similarity_calculator { iou_similarity { } } anchor_generator { ssd_anchor_generator { aspect_ratios: 1.0 } } image_resizer { fixed_shape_resizer { height: 320 width: 320 } } box_predictor { convolutional_box_predictor { conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } } normalize_loc_loss_by_codesize: true loss { classification_loss { weighted_softmax { } } localization_loss { weighted_smooth_l1 { } } } } }""" pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() text_format.Merge(model_text_proto, pipeline_config) configs = {} configs['model'] = pipeline_config.model configs['lstm_model'] = pipeline_config.Extensions[ internal_pipeline_pb2.lstm_model] return configs def get_interleaved_model_configs_from_proto(self): """Creates an interleaved model text proto for testing. Returns: A dictionary of model configs. """ model_text_proto = """ [lstm_object_detection.protos.lstm_model] { train_unroll_length: 4 eval_unroll_length: 10 lstm_state_depth: 320 depth_multipliers: 1.4 depth_multipliers: 0.35 pre_bottleneck: true low_res: true train_interleave_method: 'RANDOM_SKIP_SMALL' eval_interleave_method: 'SKIP3' } model { ssd { feature_extractor { type: 'lstm_ssd_interleaved_mobilenet_v2' conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } negative_class_weight: 2.0 box_coder { faster_rcnn_box_coder { } } matcher { argmax_matcher { } } similarity_calculator { iou_similarity { } } anchor_generator { ssd_anchor_generator { aspect_ratios: 1.0 } } image_resizer { fixed_shape_resizer { height: 320 width: 320 } } box_predictor { convolutional_box_predictor { conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } } normalize_loc_loss_by_codesize: true loss { classification_loss { weighted_softmax { } } localization_loss { weighted_smooth_l1 { } } } } }""" pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() text_format.Merge(model_text_proto, pipeline_config) configs = {} configs['model'] = pipeline_config.model configs['lstm_model'] = pipeline_config.Extensions[ internal_pipeline_pb2.lstm_model] return configs def test_model_creation_from_valid_configs(self): configs = self.get_model_configs_from_proto() # Test model properties. self.assertEqual(configs['model'].ssd.negative_class_weight, 2.0) self.assertTrue(configs['model'].ssd.normalize_loc_loss_by_codesize) self.assertEqual(configs['model'].ssd.feature_extractor.type, 'lstm_ssd_mobilenet_v1') model = self.create_train_model(configs['model'], configs['lstm_model']) # Test architechture type. self.assertIsInstance(model, lstm_ssd_meta_arch.LSTMSSDMetaArch) # Test LSTM unroll length. self.assertEqual(model.unroll_length, 4) model = self.create_eval_model(configs['model'], configs['lstm_model']) # Test architechture type. self.assertIsInstance(model, lstm_ssd_meta_arch.LSTMSSDMetaArch) # Test LSTM configs. self.assertEqual(model.unroll_length, 4) def test_interleaved_model_creation_from_valid_configs(self): configs = self.get_interleaved_model_configs_from_proto() # Test model properties. self.assertEqual(configs['model'].ssd.negative_class_weight, 2.0) self.assertTrue(configs['model'].ssd.normalize_loc_loss_by_codesize) self.assertEqual(configs['model'].ssd.feature_extractor.type, 'lstm_ssd_interleaved_mobilenet_v2') model = self.create_train_model(configs['model'], configs['lstm_model']) # Test architechture type. self.assertIsInstance(model, lstm_ssd_meta_arch.LSTMSSDMetaArch) # Test LSTM configs. self.assertEqual(model.unroll_length, 4) self.assertEqual(model._feature_extractor.lstm_state_depth, 320) self.assertAllClose(model._feature_extractor.depth_multipliers, (1.4, 0.35)) self.assertTrue(model._feature_extractor.pre_bottleneck) self.assertTrue(model._feature_extractor.low_res) self.assertEqual(model._feature_extractor.interleave_method, 'RANDOM_SKIP_SMALL') model = self.create_eval_model(configs['model'], configs['lstm_model']) # Test architechture type. self.assertIsInstance(model, lstm_ssd_meta_arch.LSTMSSDMetaArch) # Test LSTM configs. self.assertEqual(model.unroll_length, 10) self.assertEqual(model._feature_extractor.lstm_state_depth, 320) self.assertAllClose(model._feature_extractor.depth_multipliers, (1.4, 0.35)) self.assertTrue(model._feature_extractor.pre_bottleneck) self.assertTrue(model._feature_extractor.low_res) self.assertEqual(model._feature_extractor.interleave_method, 'SKIP3') def test_model_creation_from_invalid_configs(self): configs = self.get_model_configs_from_proto() # Test model build failure with wrong input configs. with self.assertRaises(AttributeError): _ = self.create_train_model(configs['model'], configs['model']) with self.assertRaises(AttributeError): _ = self.create_eval_model(configs['model'], configs['model']) if __name__ == '__main__': tf.test.main()