AdvaitBERT-AI_Explanability
/
models
/research
/object_detection
/builders
/calibration_builder_test.py
# Lint as: python2, python3 | |
# Copyright 2019 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 calibration_builder.""" | |
from __future__ import absolute_import | |
from __future__ import division | |
from __future__ import print_function | |
import numpy as np | |
from scipy import interpolate | |
from six.moves import zip | |
import tensorflow.compat.v1 as tf | |
from object_detection.builders import calibration_builder | |
from object_detection.protos import calibration_pb2 | |
from object_detection.utils import test_case | |
class CalibrationBuilderTest(test_case.TestCase): | |
def test_tf_linear_interp1d_map(self): | |
"""Tests TF linear interpolation mapping to a single number.""" | |
def graph_fn(): | |
tf_x = tf.constant([0., 0.5, 1.]) | |
tf_y = tf.constant([0.5, 0.5, 0.5]) | |
new_x = tf.constant([0., 0.25, 0.5, 0.75, 1.]) | |
tf_map_outputs = calibration_builder._tf_linear_interp1d( | |
new_x, tf_x, tf_y) | |
return tf_map_outputs | |
tf_map_outputs_np = self.execute(graph_fn, []) | |
self.assertAllClose(tf_map_outputs_np, [0.5, 0.5, 0.5, 0.5, 0.5]) | |
def test_tf_linear_interp1d_interpolate(self): | |
"""Tests TF 1d linear interpolation not mapping to a single number.""" | |
def graph_fn(): | |
tf_x = tf.constant([0., 0.5, 1.]) | |
tf_y = tf.constant([0.6, 0.7, 1.0]) | |
new_x = tf.constant([0., 0.25, 0.5, 0.75, 1.]) | |
tf_interpolate_outputs = calibration_builder._tf_linear_interp1d( | |
new_x, tf_x, tf_y) | |
return tf_interpolate_outputs | |
tf_interpolate_outputs_np = self.execute(graph_fn, []) | |
self.assertAllClose(tf_interpolate_outputs_np, [0.6, 0.65, 0.7, 0.85, 1.]) | |
def _get_scipy_interp1d(new_x, x, y): | |
"""Helper performing 1d linear interpolation using SciPy.""" | |
interpolation1d_fn = interpolate.interp1d(x, y) | |
return interpolation1d_fn(new_x) | |
def _get_tf_interp1d(self, new_x, x, y): | |
"""Helper performing 1d linear interpolation using Tensorflow.""" | |
def graph_fn(): | |
tf_interp_outputs = calibration_builder._tf_linear_interp1d( | |
tf.convert_to_tensor(new_x, dtype=tf.float32), | |
tf.convert_to_tensor(x, dtype=tf.float32), | |
tf.convert_to_tensor(y, dtype=tf.float32)) | |
return tf_interp_outputs | |
np_tf_interp_outputs = self.execute(graph_fn, []) | |
return np_tf_interp_outputs | |
def test_tf_linear_interp1d_against_scipy_map(self): | |
"""Tests parity of TF linear interpolation with SciPy for simple mapping.""" | |
length = 10 | |
np_x = np.linspace(0, 1, length) | |
# Mapping all numbers to 0.5 | |
np_y_map = np.repeat(0.5, length) | |
# Scipy and TF interpolations | |
test_data_np = np.linspace(0, 1, length * 10) | |
scipy_map_outputs = self._get_scipy_interp1d(test_data_np, np_x, np_y_map) | |
np_tf_map_outputs = self._get_tf_interp1d(test_data_np, np_x, np_y_map) | |
self.assertAllClose(scipy_map_outputs, np_tf_map_outputs) | |
def test_tf_linear_interp1d_against_scipy_interpolate(self): | |
"""Tests parity of TF linear interpolation with SciPy.""" | |
length = 10 | |
np_x = np.linspace(0, 1, length) | |
# Requires interpolation over 0.5 to 1 domain | |
np_y_interp = np.linspace(0.5, 1, length) | |
# Scipy interpolation for comparison | |
test_data_np = np.linspace(0, 1, length * 10) | |
scipy_interp_outputs = self._get_scipy_interp1d(test_data_np, np_x, | |
np_y_interp) | |
np_tf_interp_outputs = self._get_tf_interp1d(test_data_np, np_x, | |
np_y_interp) | |
self.assertAllClose(scipy_interp_outputs, np_tf_interp_outputs) | |
def _add_function_approximation_to_calibration_proto(calibration_proto, | |
x_array, y_array, | |
class_id): | |
"""Adds a function approximation to calibration proto for a class id.""" | |
# Per-class calibration. | |
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: | |
function_approximation = ( | |
calibration_proto.function_approximation.x_y_pairs) | |
for x, y in zip(x_array, y_array): | |
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): | |
"""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_id=None) | |
def graph_fn(): | |
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.3], | |
[0.4, 0.5, 0.0]], | |
[[0.6, 0.7, 0.8], | |
[0.9, 1.0, 1.0]]], dtype=tf.float32) | |
# Everything should map to 0.5 if classes are ignored. | |
calibrated_scores = calibration_fn(class_predictions_with_background) | |
return calibrated_scores | |
calibrated_scores_np = self.execute(graph_fn, []) | |
self.assertAllClose(calibrated_scores_np, [[[0.05, 0.1, 0.15], | |
[0.2, 0.25, 0.0]], | |
[[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) | |
def graph_fn(): | |
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) | |
return calibrated_scores | |
calibrated_scores_np = self.execute(graph_fn, []) | |
self.assertAllClose(calibrated_scores_np, [[[0.5, 0.6], [0.5, 0.3]], | |
[[0.5, 0.7], [0.5, 0.96]]]) | |
def test_temperature_scaling(self): | |
"""Tests that calibration produces correct temperature scaling values.""" | |
calibration_config = calibration_pb2.CalibrationConfig() | |
calibration_config.temperature_scaling_calibration.scaler = 2.0 | |
def graph_fn(): | |
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.3], [0.4, 0.5, 0.0]], | |
[[0.6, 0.7, 0.8], [0.9, 1.0, 1.0]]], | |
dtype=tf.float32) | |
calibrated_scores = calibration_fn(class_predictions_with_background) | |
return calibrated_scores | |
calibrated_scores_np = self.execute(graph_fn, []) | |
self.assertAllClose(calibrated_scores_np, | |
[[[0.05, 0.1, 0.15], [0.2, 0.25, 0.0]], | |
[[0.3, 0.35, 0.4], [0.45, 0.5, 0.5]]]) | |
def test_temperature_scaling_incorrect_value_error(self): | |
calibration_config = calibration_pb2.CalibrationConfig() | |
calibration_config.temperature_scaling_calibration.scaler = 0 | |
calibration_fn = calibration_builder.build(calibration_config) | |
class_predictions_with_background = tf.constant( | |
[[[0.1, 0.2, 0.3]]], dtype=tf.float32) | |
with self.assertRaises(ValueError): | |
calibration_fn(class_predictions_with_background) | |
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) | |
def graph_fn(): | |
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) | |
return calibrated_scores | |
calibrated_scores_np = self.execute(graph_fn, []) | |
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() | |