# Copyright 2022 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. """Contains a collection of util functions for training and evaluating.""" from absl import logging import numpy as np import tensorflow as tf from official.vision.dataloaders import tfexample_utils def Dequantize(feat_vector, max_quantized_value=2, min_quantized_value=-2): """Dequantize the feature from the byte format to the float format. Args: feat_vector: the input 1-d vector. max_quantized_value: the maximum of the quantized value. min_quantized_value: the minimum of the quantized value. Returns: A float vector which has the same shape as feat_vector. """ assert max_quantized_value > min_quantized_value quantized_range = max_quantized_value - min_quantized_value scalar = quantized_range / 255.0 bias = (quantized_range / 512.0) + min_quantized_value return feat_vector * scalar + bias def MakeSummary(name, value): """Creates a tf.Summary proto with the given name and value.""" summary = tf.Summary() val = summary.value.add() val.tag = str(name) val.simple_value = float(value) return summary def AddGlobalStepSummary(summary_writer, global_step_val, global_step_info_dict, summary_scope="Eval"): """Add the global_step summary to the Tensorboard. Args: summary_writer: Tensorflow summary_writer. global_step_val: a int value of the global step. global_step_info_dict: a dictionary of the evaluation metrics calculated for a mini-batch. summary_scope: Train or Eval. Returns: A string of this global_step summary """ this_hit_at_one = global_step_info_dict["hit_at_one"] this_perr = global_step_info_dict["perr"] this_loss = global_step_info_dict["loss"] examples_per_second = global_step_info_dict.get("examples_per_second", -1) summary_writer.add_summary( MakeSummary("GlobalStep/" + summary_scope + "_Hit@1", this_hit_at_one), global_step_val) summary_writer.add_summary( MakeSummary("GlobalStep/" + summary_scope + "_Perr", this_perr), global_step_val) summary_writer.add_summary( MakeSummary("GlobalStep/" + summary_scope + "_Loss", this_loss), global_step_val) if examples_per_second != -1: summary_writer.add_summary( MakeSummary("GlobalStep/" + summary_scope + "_Example_Second", examples_per_second), global_step_val) summary_writer.flush() info = ( "global_step {0} | Batch Hit@1: {1:.3f} | Batch PERR: {2:.3f} | Batch " "Loss: {3:.3f} | Examples_per_sec: {4:.3f}").format( global_step_val, this_hit_at_one, this_perr, this_loss, examples_per_second) return info def AddEpochSummary(summary_writer, global_step_val, epoch_info_dict, summary_scope="Eval"): """Add the epoch summary to the Tensorboard. Args: summary_writer: Tensorflow summary_writer. global_step_val: a int value of the global step. epoch_info_dict: a dictionary of the evaluation metrics calculated for the whole epoch. summary_scope: Train or Eval. Returns: A string of this global_step summary """ epoch_id = epoch_info_dict["epoch_id"] avg_hit_at_one = epoch_info_dict["avg_hit_at_one"] avg_perr = epoch_info_dict["avg_perr"] avg_loss = epoch_info_dict["avg_loss"] aps = epoch_info_dict["aps"] gap = epoch_info_dict["gap"] mean_ap = np.mean(aps) summary_writer.add_summary( MakeSummary("Epoch/" + summary_scope + "_Avg_Hit@1", avg_hit_at_one), global_step_val) summary_writer.add_summary( MakeSummary("Epoch/" + summary_scope + "_Avg_Perr", avg_perr), global_step_val) summary_writer.add_summary( MakeSummary("Epoch/" + summary_scope + "_Avg_Loss", avg_loss), global_step_val) summary_writer.add_summary( MakeSummary("Epoch/" + summary_scope + "_MAP", mean_ap), global_step_val) summary_writer.add_summary( MakeSummary("Epoch/" + summary_scope + "_GAP", gap), global_step_val) summary_writer.flush() info = ("epoch/eval number {0} | Avg_Hit@1: {1:.3f} | Avg_PERR: {2:.3f} " "| MAP: {3:.3f} | GAP: {4:.3f} | Avg_Loss: {5:3f} | num_classes: {6}" ).format(epoch_id, avg_hit_at_one, avg_perr, mean_ap, gap, avg_loss, len(aps)) return info def GetListOfFeatureNamesAndSizes(feature_names, feature_sizes): """Extract the list of feature names and the dimensionality. Args: feature_names: string containing comma separated list of feature names feature_sizes: string containing comma separated list of feature sizes Returns: List of the feature names and list of the dimensionality of each feature. Elements in the first/second list are strings/integers. """ list_of_feature_names = [ feature_names.strip() for feature_names in feature_names.split(",") ] list_of_feature_sizes = [ int(feature_sizes) for feature_sizes in feature_sizes.split(",") ] if len(list_of_feature_names) != len(list_of_feature_sizes): logging.error( "length of the feature names (=%r) != length of feature " "sizes (=%r)", str(len(list_of_feature_names)), str(len(list_of_feature_sizes))) return list_of_feature_names, list_of_feature_sizes def ClipGradientNorms(gradients_to_variables, max_norm): """Clips the gradients by the given value. Args: gradients_to_variables: A list of gradient to variable pairs (tuples). max_norm: the maximum norm value. Returns: A list of clipped gradient to variable pairs. """ clipped_grads_and_vars = [] for grad, var in gradients_to_variables: if grad is not None: if isinstance(grad, tf.IndexedSlices): tmp = tf.clip_by_norm(grad.values, max_norm) grad = tf.IndexedSlices(tmp, grad.indices, grad.dense_shape) else: grad = tf.clip_by_norm(grad, max_norm) clipped_grads_and_vars.append((grad, var)) return clipped_grads_and_vars def CombineGradients(tower_grads): """Calculate the combined gradient for each shared variable across all towers. Note that this function provides a synchronization point across all towers. Args: tower_grads: List of lists of (gradient, variable) tuples. The outer list is over individual gradients. The inner list is over the gradient calculation for each tower. Returns: List of pairs of (gradient, variable) where the gradient has been summed across all towers. """ filtered_grads = [ [x for x in grad_list if x[0] is not None] for grad_list in tower_grads ] final_grads = [] for i in range(len(filtered_grads[0])): grads = [filtered_grads[t][i] for t in range(len(filtered_grads))] grad = tf.stack([x[0] for x in grads], 0) grad = tf.reduce_sum(grad, 0) final_grads.append(( grad, filtered_grads[0][i][1], )) return final_grads def MakeYt8mExample(num_segment: int = 5) -> tf.train.SequenceExample: """Generate fake data for unit tests.""" rgb = np.random.randint(low=256, size=1024, dtype=np.uint8) audio = np.random.randint(low=256, size=128, dtype=np.uint8) seq_example = tf.train.SequenceExample() seq_example.context.feature["id"].bytes_list.value[:] = [b"id001"] seq_example.context.feature["labels"].int64_list.value[:] = [1, 2, 3, 4] seq_example.context.feature["segment_labels"].int64_list.value[:] = ( [4] * num_segment) seq_example.context.feature["segment_start_times"].int64_list.value[:] = [ i * 5 for i in range(num_segment) ] seq_example.context.feature["segment_scores"].float_list.value[:] = ( [0.] * num_segment) tfexample_utils.put_bytes_list_to_feature( seq_example, rgb.tobytes(), key="rgb", repeat_num=120) tfexample_utils.put_bytes_list_to_feature( seq_example, audio.tobytes(), key="audio", repeat_num=120) return seq_example # TODO(yeqing): Move the test related functions to test_utils. def MakeExampleWithFloatFeatures( num_segment: int = 5) -> tf.train.SequenceExample: """Generate fake data for unit tests.""" rgb = np.random.rand(1, 2048).astype(np.float32) audio = np.random.rand(256).astype(np.float32) seq_example = tf.train.SequenceExample() seq_example.context.feature["id"].bytes_list.value[:] = [b"id001"] seq_example.context.feature["labels"].int64_list.value[:] = [1, 2, 3, 4] seq_example.context.feature["segment_labels"].int64_list.value[:] = ( [4] * num_segment) seq_example.context.feature["segment_start_times"].int64_list.value[:] = [ i * 5 for i in range(num_segment) ] seq_example.context.feature["segment_scores"].float_list.value[:] = ( [0.] * num_segment) seq_example.context.feature[ "VIDEO_EMBEDDING/context_feature/floats"].float_list.value[:] = ( audio.tolist()) tfexample_utils.put_float_list_to_feature( seq_example, rgb.tolist(), key="FEATURE/feature/floats") return seq_example