Unverified Commit fe748d4a authored by pkulzc's avatar pkulzc Committed by GitHub
Browse files

Object detection changes: (#7208)

257914648  by lzc:

    Internal changes

--
257525973  by Zhichao Lu:

    Fixes bug that silently prevents checkpoints from loading when training w/ eager + functions. Also sets up scripts to run training.

--
257296614  by Zhichao Lu:

    Adding detection_features to model outputs

--
257234565  by Zhichao Lu:

    Fix wrong order of `classes_with_max_scores` in class-agnostic NMS caused by
    sorting in partitioned-NMS.

--
257232002  by ronnyvotel:

    Supporting `filter_nonoverlapping` option in np_box_list_ops.clip_to_window().

--
257198282  by Zhichao Lu:

    Adding the focal loss and l1 loss from the Objects as Points paper.

--
257089535  by Zhichao Lu:

    Create Keras based ssd + resnetv1 + fpn.

--
257087407  by Zhichao Lu:

    Make object_detection/data_decoders Python3-compatible.

--
257004582  by Zhichao Lu:

    Updates _decode_raw_data_into_masks_and_boxes to the latest binary masks-to-string encoding fo...
parent 81123ebf
......@@ -78,7 +78,7 @@ Extras:
* <a href='g3doc/instance_segmentation.md'>
Run an instance segmentation model</a><br>
* <a href='g3doc/challenge_evaluation.md'>
Run the evaluation for the Open Images Challenge 2018</a><br>
Run the evaluation for the Open Images Challenge 2018/2019</a><br>
* <a href='g3doc/tpu_compatibility.md'>
TPU compatible detection pipelines</a><br>
* <a href='g3doc/running_on_mobile_tensorflowlite.md'>
......@@ -101,9 +101,23 @@ reporting an issue.
## Release information
### July 1st, 2019
We have released an updated set of utils and an updated
[tutorial](g3doc/challenge_evaluation.md) for all three tracks of the
[Open Images Challenge 2019](https://storage.googleapis.com/openimages/web/challenge2019.html)!
The Instance Segmentation metric for
[Open Images V5](https://storage.googleapis.com/openimages/web/index.html)
and [Challenge 2019](https://storage.googleapis.com/openimages/web/challenge2019.html)
is part of this release. Check out [the metric description](https://storage.googleapis.com/openimages/web/evaluation.html#instance_segmentation_eval)
on the Open Images website.
<b>Thanks to contributors</b>: Alina Kuznetsova, Rodrigo Benenson
### Feb 11, 2019
We have released detection models trained on the [Open Images Dataset V4](https://storage.googleapis.com/openimages/web/challenge.html)
We have released detection models trained on the Open Images Dataset V4
in our detection model zoo, including
* Faster R-CNN detector with Inception Resnet V2 feature extractor
......
......@@ -79,6 +79,27 @@ def _function_approximation_proto_to_tf_tensors(x_y_pairs_message):
return tf_x, tf_y
def _get_class_id_function_dict(calibration_config):
"""Create a dictionary mapping class id to function approximations.
Args:
calibration_config: calibration_pb2 proto containing
id_function_approximations.
Returns:
Dictionary mapping a class id to a tuple of TF tensors to be used for
function approximation.
"""
class_id_function_dict = {}
class_id_xy_pairs_map = (
calibration_config.class_id_function_approximations.class_id_xy_pairs_map)
for class_id in class_id_xy_pairs_map:
class_id_function_dict[class_id] = (
_function_approximation_proto_to_tf_tensors(
class_id_xy_pairs_map[class_id]))
return class_id_function_dict
def build(calibration_config):
"""Returns a function that calibrates Tensorflow model scores.
......@@ -107,9 +128,9 @@ def build(calibration_config):
def calibration_fn(class_predictions_with_background):
"""Calibrate predictions via 1-d linear interpolation.
Predictions scores are linearly interpolated based on class-agnostic
function approximations. Note that the 0-indexed background class may
also transformed.
Predictions scores are linearly interpolated based on a class-agnostic
function approximation. Note that the 0-indexed background class is also
transformed.
Args:
class_predictions_with_background: tf.float32 tensor of shape
......@@ -118,9 +139,8 @@ def build(calibration_config):
and the result of calling the `predict` method of a detection model.
Returns:
tf.float32 tensor of shape [batch_size, num_anchors, num_classes] if
background class is not present (else shape is
[batch_size, num_anchors, num_classes + 1]) on the interval [0, 1].
tf.float32 tensor of the same shape as the input with values on the
interval [0, 1].
"""
# Flattening Tensors and then reshaping at the end.
flat_class_predictions_with_background = tf.reshape(
......@@ -139,7 +159,61 @@ def build(calibration_config):
name='calibrate_scores')
return calibrated_class_predictions_with_background
# TODO(zbeaver): Add sigmoid calibration and per-class isotonic regression.
elif (calibration_config.WhichOneof('calibrator') ==
'class_id_function_approximations'):
def calibration_fn(class_predictions_with_background):
"""Calibrate predictions per class via 1-d linear interpolation.
Prediction scores are linearly interpolated with class-specific function
approximations. Note that after calibration, an anchor's class scores will
not necessarily sum to 1, and score ordering may change, depending on each
class' calibration parameters.
Args:
class_predictions_with_background: tf.float32 tensor of shape
[batch_size, num_anchors, num_classes + 1] containing scores on the
interval [0,1]. This is usually produced by a sigmoid or softmax layer
and the result of calling the `predict` method of a detection model.
Returns:
tf.float32 tensor of the same shape as the input with values on the
interval [0, 1].
Raises:
KeyError: Calibration parameters are not present for a class.
"""
class_id_function_dict = _get_class_id_function_dict(calibration_config)
# Tensors are split by class and then recombined at the end to recover
# the input's original shape. If a class id does not have calibration
# parameters, it is left unchanged.
class_tensors = tf.unstack(class_predictions_with_background, axis=-1)
calibrated_class_tensors = []
for class_id, class_tensor in enumerate(class_tensors):
flat_class_tensor = tf.reshape(class_tensor, shape=[-1])
if class_id in class_id_function_dict:
output_tensor = _tf_linear_interp1d(
x_to_interpolate=flat_class_tensor,
fn_x=class_id_function_dict[class_id][0],
fn_y=class_id_function_dict[class_id][1])
else:
tf.logging.info(
'Calibration parameters for class id `%d` not not found',
class_id)
output_tensor = flat_class_tensor
calibrated_class_tensors.append(output_tensor)
combined_calibrated_tensor = tf.stack(calibrated_class_tensors, axis=1)
input_shape = shape_utils.combined_static_and_dynamic_shape(
class_predictions_with_background)
calibrated_class_predictions_with_background = tf.reshape(
combined_calibrated_tensor,
shape=input_shape,
name='calibrate_scores')
return calibrated_class_predictions_with_background
# TODO(zbeaver): Add sigmoid calibration.
else:
raise ValueError('No calibration builder defined for "Oneof" in '
'calibration_config.')
......
......@@ -94,37 +94,34 @@ class CalibrationBuilderTest(tf.test.TestCase):
@staticmethod
def _add_function_approximation_to_calibration_proto(calibration_proto,
x_array,
y_array,
class_label):
"""Adds a function approximation to calibration proto for a class label."""
x_array, y_array,
class_id):
"""Adds a function approximation to calibration proto for a class id."""
# Per-class calibration.
if class_label:
label_function_approximation = (calibration_proto
.label_function_approximations
.label_xy_pairs_map[class_label])
if class_id is not None:
function_approximation = (
calibration_proto.class_id_function_approximations
.class_id_xy_pairs_map[class_id])
# Class-agnostic calibration.
else:
label_function_approximation = (calibration_proto
.function_approximation
.x_y_pairs)
function_approximation = (
calibration_proto.function_approximation.x_y_pairs)
for x, y in zip(x_array, y_array):
x_y_pair_message = label_function_approximation.x_y_pair.add()
x_y_pair_message = function_approximation.x_y_pair.add()
x_y_pair_message.x = x
x_y_pair_message.y = y
def test_class_agnostic_function_approximation(self):
"""Ensures that calibration appropriate values, regardless of class."""
"""Tests that calibration produces correct class-agnostic values."""
# Generate fake calibration proto. For this interpolation, any input on
# [0.0, 0.5] should be divided by 2 and any input on (0.5, 1.0] should have
# 0.25 subtracted from it.
class_agnostic_x = np.asarray([0.0, 0.5, 1.0])
class_agnostic_y = np.asarray([0.0, 0.25, 0.75])
calibration_config = calibration_pb2.CalibrationConfig()
self._add_function_approximation_to_calibration_proto(calibration_config,
class_agnostic_x,
class_agnostic_y,
class_label=None)
self._add_function_approximation_to_calibration_proto(
calibration_config, class_agnostic_x, class_agnostic_y, class_id=None)
od_graph = tf.Graph()
with self.test_session(graph=od_graph) as sess:
......@@ -144,5 +141,55 @@ class CalibrationBuilderTest(tf.test.TestCase):
[[0.35, 0.45, 0.55],
[0.65, 0.75, 0.75]]])
def test_multiclass_function_approximations(self):
"""Tests that calibration produces correct multiclass values."""
# Background class (0-index) maps all predictions to 0.5.
class_0_x = np.asarray([0.0, 0.5, 1.0])
class_0_y = np.asarray([0.5, 0.5, 0.5])
calibration_config = calibration_pb2.CalibrationConfig()
self._add_function_approximation_to_calibration_proto(
calibration_config, class_0_x, class_0_y, class_id=0)
# Class id 1 will interpolate using these values.
class_1_x = np.asarray([0.0, 0.2, 1.0])
class_1_y = np.asarray([0.0, 0.6, 1.0])
self._add_function_approximation_to_calibration_proto(
calibration_config, class_1_x, class_1_y, class_id=1)
od_graph = tf.Graph()
with self.test_session(graph=od_graph) as sess:
calibration_fn = calibration_builder.build(calibration_config)
# batch_size = 2, num_classes = 2, num_anchors = 2.
class_predictions_with_background = tf.constant(
[[[0.1, 0.2], [0.9, 0.1]],
[[0.6, 0.4], [0.08, 0.92]]],
dtype=tf.float32)
calibrated_scores = calibration_fn(class_predictions_with_background)
calibrated_scores_np = sess.run(calibrated_scores)
self.assertAllClose(calibrated_scores_np, [[[0.5, 0.6], [0.5, 0.3]],
[[0.5, 0.7], [0.5, 0.96]]])
def test_skips_class_when_calibration_parameters_not_present(self):
"""Tests that graph fails when parameters not present for all classes."""
# Only adding calibration parameters for class id = 0, even though class id
# 1 is present in the data.
class_0_x = np.asarray([0.0, 0.5, 1.0])
class_0_y = np.asarray([0.5, 0.5, 0.5])
calibration_config = calibration_pb2.CalibrationConfig()
self._add_function_approximation_to_calibration_proto(
calibration_config, class_0_x, class_0_y, class_id=0)
od_graph = tf.Graph()
with self.test_session(graph=od_graph) as sess:
calibration_fn = calibration_builder.build(calibration_config)
# batch_size = 2, num_classes = 2, num_anchors = 2.
class_predictions_with_background = tf.constant(
[[[0.1, 0.2], [0.9, 0.1]],
[[0.6, 0.4], [0.08, 0.92]]],
dtype=tf.float32)
calibrated_scores = calibration_fn(class_predictions_with_background)
calibrated_scores_np = sess.run(calibrated_scores)
self.assertAllClose(calibrated_scores_np, [[[0.5, 0.2], [0.5, 0.1]],
[[0.5, 0.4], [0.5, 0.92]]])
if __name__ == '__main__':
tf.test.main()
......@@ -206,6 +206,9 @@ def _build_ssd_feature_extractor(feature_extractor_config,
feature_extractor_config.replace_preprocessor_with_placeholder
})
if feature_extractor_config.HasField('num_layers'):
kwargs.update({'num_layers': feature_extractor_config.num_layers})
if is_keras_extractor:
kwargs.update({
'conv_hyperparams': conv_hyperparams,
......
......@@ -78,13 +78,16 @@ def _build_non_max_suppressor(nms_config):
Raises:
ValueError: On incorrect iou_threshold or on incompatible values of
max_total_detections and max_detections_per_class.
max_total_detections and max_detections_per_class or on negative
soft_nms_sigma.
"""
if nms_config.iou_threshold < 0 or nms_config.iou_threshold > 1.0:
raise ValueError('iou_threshold not in [0, 1.0].')
if nms_config.max_detections_per_class > nms_config.max_total_detections:
raise ValueError('max_detections_per_class should be no greater than '
'max_total_detections.')
if nms_config.soft_nms_sigma < 0.0:
raise ValueError('soft_nms_sigma should be non-negative.')
non_max_suppressor_fn = functools.partial(
post_processing.batch_multiclass_non_max_suppression,
score_thresh=nms_config.score_threshold,
......@@ -93,7 +96,8 @@ def _build_non_max_suppressor(nms_config):
max_total_size=nms_config.max_total_detections,
use_static_shapes=nms_config.use_static_shapes,
use_class_agnostic_nms=nms_config.use_class_agnostic_nms,
max_classes_per_detection=nms_config.max_classes_per_detection)
max_classes_per_detection=nms_config.max_classes_per_detection,
soft_nms_sigma=nms_config.soft_nms_sigma)
return non_max_suppressor_fn
......
......@@ -30,6 +30,7 @@ class PostProcessingBuilderTest(tf.test.TestCase):
iou_threshold: 0.6
max_detections_per_class: 100
max_total_detections: 300
soft_nms_sigma: 0.4
}
"""
post_processing_config = post_processing_pb2.PostProcessing()
......@@ -40,6 +41,7 @@ class PostProcessingBuilderTest(tf.test.TestCase):
self.assertEqual(non_max_suppressor.keywords['max_total_size'], 300)
self.assertAlmostEqual(non_max_suppressor.keywords['score_thresh'], 0.7)
self.assertAlmostEqual(non_max_suppressor.keywords['iou_thresh'], 0.6)
self.assertAlmostEqual(non_max_suppressor.keywords['soft_nms_sigma'], 0.4)
def test_build_non_max_suppressor_with_correct_parameters_classagnostic_nms(
self):
......
......@@ -297,6 +297,26 @@ def build(preprocessor_step_config):
})
return (preprocessor.ssd_random_crop, {})
if step_type == 'autoaugment_image':
config = preprocessor_step_config.autoaugment_image
return (preprocessor.autoaugment_image, {
'policy_name': config.policy_name,
})
if step_type == 'drop_label_probabilistically':
config = preprocessor_step_config.drop_label_probabilistically
return (preprocessor.drop_label_probabilistically, {
'dropped_label': config.label,
'drop_probability': config.drop_probability,
})
if step_type == 'remap_labels':
config = preprocessor_step_config.remap_labels
return (preprocessor.remap_labels, {
'original_labels': config.original_labels,
'new_label': config.new_label
})
if step_type == 'ssd_random_crop_pad':
config = preprocessor_step_config.ssd_random_crop_pad
if config.operations:
......
......@@ -363,6 +363,51 @@ class PreprocessorBuilderTest(tf.test.TestCase):
'probability': 0.95,
'size_to_image_ratio': 0.12})
def test_auto_augment_image(self):
preprocessor_text_proto = """
autoaugment_image {
policy_name: 'v0'
}
"""
preprocessor_proto = preprocessor_pb2.PreprocessingStep()
text_format.Merge(preprocessor_text_proto, preprocessor_proto)
function, args = preprocessor_builder.build(preprocessor_proto)
self.assertEqual(function, preprocessor.autoaugment_image)
self.assert_dictionary_close(args, {'policy_name': 'v0'})
def test_drop_label_probabilistically(self):
preprocessor_text_proto = """
drop_label_probabilistically{
label: 2
drop_probability: 0.5
}
"""
preprocessor_proto = preprocessor_pb2.PreprocessingStep()
text_format.Merge(preprocessor_text_proto, preprocessor_proto)
function, args = preprocessor_builder.build(preprocessor_proto)
self.assertEqual(function, preprocessor.drop_label_probabilistically)
self.assert_dictionary_close(args, {
'dropped_label': 2,
'drop_probability': 0.5
})
def test_remap_labels(self):
preprocessor_text_proto = """
remap_labels{
original_labels: 1
original_labels: 2
new_label: 3
}
"""
preprocessor_proto = preprocessor_pb2.PreprocessingStep()
text_format.Merge(preprocessor_text_proto, preprocessor_proto)
function, args = preprocessor_builder.build(preprocessor_proto)
self.assertEqual(function, preprocessor.remap_labels)
self.assert_dictionary_close(args, {
'original_labels': [1, 2],
'new_label': 3
})
def test_build_random_resize_method(self):
preprocessor_text_proto = """
random_resize_method {
......
......@@ -29,15 +29,20 @@ dynamically at generation time. The number of anchors to place at each location
is static --- implementations of AnchorGenerator must always be able return
the number of anchors that it uses per location for each feature map.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from abc import ABCMeta
from abc import abstractmethod
import six
from six.moves import zip
import tensorflow as tf
class AnchorGenerator(object):
class AnchorGenerator(six.with_metaclass(ABCMeta, object)):
"""Abstract base class for anchor generators."""
__metaclass__ = ABCMeta
@abstractmethod
def name_scope(self):
......@@ -147,4 +152,3 @@ class AnchorGenerator(object):
* feature_map_shape[1])
actual_num_anchors += anchors.num_boxes()
return tf.assert_equal(expected_num_anchors, actual_num_anchors)
......@@ -13,15 +13,22 @@
# limitations under the License.
# ==============================================================================
"""Tests for google3.third_party.tensorflow_models.object_detection.core.batch_multiclass_nms."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from absl.testing import parameterized
import numpy as np
from six.moves import range
import tensorflow as tf
from object_detection.core import post_processing
from object_detection.utils import test_case
class BatchMulticlassNonMaxSuppressionTest(test_case.TestCase):
class BatchMulticlassNonMaxSuppressionTest(test_case.TestCase,
parameterized.TestCase):
def test_batch_multiclass_nms_with_batch_size_1(self):
@parameterized.named_parameters(('', False), ('_use_static_shapes', True))
def test_batch_multiclass_nms_with_batch_size_1(self, use_static_shapes):
boxes = tf.constant([[[[0, 0, 1, 1], [0, 0, 4, 5]],
[[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]],
[[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]],
......@@ -47,10 +54,15 @@ class BatchMulticlassNonMaxSuppressionTest(test_case.TestCase):
exp_nms_classes = [[0, 0, 1, 0]]
(nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks,
nmsed_additional_fields, num_detections
) = post_processing.batch_multiclass_non_max_suppression(
boxes, scores, score_thresh, iou_thresh,
max_size_per_class=max_output_size, max_total_size=max_output_size)
nmsed_additional_fields,
num_detections) = post_processing.batch_multiclass_non_max_suppression(
boxes,
scores,
score_thresh,
iou_thresh,
max_size_per_class=max_output_size,
max_total_size=max_output_size,
use_static_shapes=use_static_shapes)
self.assertIsNone(nmsed_masks)
self.assertIsNone(nmsed_additional_fields)
......@@ -64,6 +76,17 @@ class BatchMulticlassNonMaxSuppressionTest(test_case.TestCase):
self.assertAllClose(nmsed_classes, exp_nms_classes)
self.assertEqual(num_detections, [4])
def test_batch_iou_with_negative_data(self):
boxes = tf.constant([[[0, -0.01, 0.1, 1.1], [0, 0.2, 0.2, 5.0],
[0, -0.01, 0.1, 1.], [-1, -1, -1, -1]]], tf.float32)
iou = post_processing.batch_iou(boxes, boxes)
expected_iou = [[[0.99999994, 0.0917431, 0.9099099, -1.],
[0.0917431, 1., 0.08154944, -1.],
[0.9099099, 0.08154944, 1., -1.], [-1., -1., -1., -1.]]]
with self.test_session() as sess:
iou = sess.run(iou)
self.assertAllClose(iou, expected_iou)
def test_batch_multiclass_nms_with_batch_size_2(self):
boxes = tf.constant([[[[0, 0, 1, 1], [0, 0, 4, 5]],
[[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]],
......
......@@ -14,8 +14,13 @@
# ==============================================================================
"""Provides functions to batch a dictionary of input tensors."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import collections
from six.moves import range
import tensorflow as tf
from object_detection.core import prefetcher
......
......@@ -15,7 +15,12 @@
"""Tests for object_detection.core.batcher."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
from six.moves import range
import tensorflow as tf
from object_detection.core import batcher
......
......@@ -26,10 +26,15 @@ Users of a BoxCoder can call two methods:
In both cases, the arguments are assumed to be in 1-1 correspondence already;
it is not the job of a BoxCoder to perform matching.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from abc import ABCMeta
from abc import abstractmethod
from abc import abstractproperty
import six
import tensorflow as tf
from object_detection.utils import shape_utils
......@@ -42,9 +47,8 @@ MEAN_STDDEV = 'mean_stddev'
SQUARE = 'square'
class BoxCoder(object):
class BoxCoder(six.with_metaclass(ABCMeta, object)):
"""Abstract base class for box coder."""
__metaclass__ = ABCMeta
@abstractproperty
def code_size(self):
......
......@@ -23,6 +23,11 @@ Example box operations that are supported:
Whenever box_list_ops functions output a BoxList, the fields of the incoming
BoxList are retained unless documented otherwise.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from six.moves import range
import tensorflow as tf
from object_detection.core import box_list
......
......@@ -13,13 +13,15 @@
# limitations under the License.
# ==============================================================================
"""Tests for google3.third_party.tensorflow_models.object_detection.core.class_agnostic_nms."""
from absl.testing import parameterized
import tensorflow as tf
from object_detection.core import post_processing
from object_detection.core import standard_fields as fields
from object_detection.utils import test_case
class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase):
class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase,
parameterized.TestCase):
def test_class_agnostic_nms_select_with_shared_boxes(self):
boxes = tf.constant(
......@@ -53,6 +55,7 @@ class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase):
self.assertAllClose(nms_scores_output, exp_nms_scores)
self.assertAllClose(nms_classes_output, exp_nms_classes)
def test_class_agnostic_nms_select_with_per_class_boxes(self):
boxes = tf.constant(
[[[4, 5, 9, 10], [0, 0, 1, 1]],
......@@ -98,7 +101,14 @@ class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase):
self.assertAllClose(nms_scores_output, exp_nms_scores)
self.assertAllClose(nms_classes_output, exp_nms_classes)
def test_batch_classagnostic_nms_with_batch_size_1(self):
# Two cases will be tested here: using / not using static shapes.
# Named the two test cases for easier control during testing, with a flag of
# '--test_filter=ClassAgnosticNonMaxSuppressionTest.test_batch_classagnostic_nms_with_batch_size_1'
# or
# '--test_filter=ClassAgnosticNonMaxSuppressionTest.test_batch_classagnostic_nms_with_batch_size_1_use_static_shapes'.
@parameterized.named_parameters(('', False), ('_use_static_shapes', True))
def test_batch_classagnostic_nms_with_batch_size_1(self,
use_static_shapes=False):
boxes = tf.constant(
[[[[0, 0, 1, 1]], [[0, 0.1, 1, 1.1]], [[0, -0.1, 1, 0.9]],
[[0, 10, 1, 11]], [[0, 10.1, 1, 11.1]], [[0, 100, 1, 101]],
......@@ -126,6 +136,7 @@ class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase):
max_size_per_class=max_output_size,
max_total_size=max_output_size,
use_class_agnostic_nms=use_class_agnostic_nms,
use_static_shapes=use_static_shapes,
max_classes_per_detection=max_classes_per_detection)
self.assertIsNone(nmsed_masks)
......
......@@ -18,13 +18,16 @@
Data decoders decode the input data and return a dictionary of tensors keyed by
the entries in core.reader.Fields.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from abc import ABCMeta
from abc import abstractmethod
import six
class DataDecoder(object):
class DataDecoder(six.with_metaclass(ABCMeta, object)):
"""Interface for data decoders."""
__metaclass__ = ABCMeta
@abstractmethod
def decode(self, data):
......
......@@ -20,12 +20,16 @@ to numpy arrays (materialized tensors) directly, it is used to read data for
evaluation/visualization; to parse the data during training, DataDecoder should
be used.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from abc import ABCMeta
from abc import abstractmethod
import six
class DataToNumpyParser(object):
__metaclass__ = ABCMeta
class DataToNumpyParser(six.with_metaclass(ABCMeta, object)):
"""Abstract interface for data parser that produces numpy arrays."""
@abstractmethod
def parse(self, input_data):
......
......@@ -14,7 +14,12 @@
# ==============================================================================
"""Tests for object_detection.core.freezable_batch_norm."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
from six.moves import zip
import tensorflow as tf
from object_detection.core import freezable_batch_norm
......
......@@ -26,7 +26,12 @@ Classification losses:
* WeightedSoftmaxClassificationAgainstLogitsLoss
* BootstrappedSigmoidClassificationLoss
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import abc
import six
import tensorflow as tf
from object_detection.core import box_list
......@@ -36,9 +41,8 @@ from object_detection.utils import ops
slim = tf.contrib.slim
class Loss(object):
class Loss(six.with_metaclass(abc.ABCMeta, object)):
"""Abstract base class for loss functions."""
__metaclass__ = abc.ABCMeta
def __call__(self,
prediction_tensor,
......@@ -153,6 +157,7 @@ class WeightedSmoothL1LocalizationLoss(Loss):
Args:
delta: delta for smooth L1 loss.
"""
super(WeightedSmoothL1LocalizationLoss, self).__init__()
self._delta = delta
def _compute_loss(self, prediction_tensor, target_tensor, weights):
......@@ -257,6 +262,7 @@ class SigmoidFocalClassificationLoss(Loss):
gamma: exponent of the modulating factor (1 - p_t) ^ gamma.
alpha: optional alpha weighting factor to balance positives vs negatives.
"""
super(SigmoidFocalClassificationLoss, self).__init__()
self._alpha = alpha
self._gamma = gamma
......@@ -316,6 +322,7 @@ class WeightedSoftmaxClassificationLoss(Loss):
(default 1.0)
"""
super(WeightedSoftmaxClassificationLoss, self).__init__()
self._logit_scale = logit_scale
def _compute_loss(self, prediction_tensor, target_tensor, weights):
......@@ -360,6 +367,7 @@ class WeightedSoftmaxClassificationAgainstLogitsLoss(Loss):
(default 1.0)
"""
super(WeightedSoftmaxClassificationAgainstLogitsLoss, self).__init__()
self._logit_scale = logit_scale
def _scale_and_softmax_logits(self, logits):
......@@ -423,6 +431,7 @@ class BootstrappedSigmoidClassificationLoss(Loss):
Raises:
ValueError: if bootstrap_type is not either 'hard' or 'soft'
"""
super(BootstrappedSigmoidClassificationLoss, self).__init__()
if bootstrap_type != 'hard' and bootstrap_type != 'soft':
raise ValueError('Unrecognized bootstrap_type: must be one of '
'\'hard\' or \'soft.\'')
......@@ -673,3 +682,5 @@ class HardExampleMiner(object):
num_negatives = tf.size(subsampled_selection_indices) - num_positives
return (tf.reshape(tf.gather(indices, subsampled_selection_indices), [-1]),
num_positives, num_negatives)
......@@ -14,9 +14,14 @@
# ==============================================================================
"""Tests for google3.research.vale.object_detection.losses."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import math
import numpy as np
from six.moves import zip
import tensorflow as tf
from object_detection.core import box_list
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment