aff45dd565
Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com>
151 lines
6.2 KiB
Python
151 lines
6.2 KiB
Python
#
|
|
# Copyright (c) 2021, NVIDIA CORPORATION. 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.
|
|
#
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
|
|
import numpy as np
|
|
import tensorflow as tf
|
|
|
|
from infer import TensorRTInfer
|
|
from image_batcher import ImageBatcher
|
|
|
|
|
|
class TensorFlowInfer:
|
|
"""
|
|
Implements TensorFlow inference of a saved model, following the same API as the TensorRTInfer class.
|
|
"""
|
|
|
|
def __init__(self, saved_model_path):
|
|
gpus = tf.config.experimental.list_physical_devices('GPU')
|
|
for gpu in gpus:
|
|
tf.config.experimental.set_memory_growth(gpu, True)
|
|
|
|
self.model = tf.saved_model.load(saved_model_path)
|
|
self.pred_fn = self.model.signatures['serving_default']
|
|
|
|
# Setup I/O bindings
|
|
self.inputs = []
|
|
fn_inputs = self.pred_fn.structured_input_signature[1]
|
|
for i, input in enumerate(list(fn_inputs.values())):
|
|
self.inputs.append({
|
|
'index': i,
|
|
'name': input.name,
|
|
'dtype': np.dtype(input.dtype.as_numpy_dtype()),
|
|
'shape': input.shape.as_list(),
|
|
})
|
|
self.outputs = []
|
|
fn_outputs = self.pred_fn.structured_outputs
|
|
for i, output in enumerate(list(fn_outputs.values())):
|
|
self.outputs.append({
|
|
'index': i,
|
|
'name': output.name,
|
|
'dtype': np.dtype(output.dtype.as_numpy_dtype()),
|
|
'shape': output.shape.as_list(),
|
|
})
|
|
|
|
def input_spec(self):
|
|
return self.inputs[0]['shape'], self.inputs[0]['dtype']
|
|
|
|
def output_spec(self):
|
|
return self.outputs[0]['shape'], self.outputs[0]['dtype']
|
|
|
|
def infer(self, batch, top=1):
|
|
# Process I/O and execute the network
|
|
input = {self.inputs[0]['name']: tf.convert_to_tensor(batch)}
|
|
output = self.pred_fn(**input)
|
|
output = output[self.outputs[0]['name']].numpy()
|
|
|
|
# Read and process the results
|
|
classes = np.argmax(output, axis=1)
|
|
scores = np.max(output, axis=1)
|
|
top = max(top, output.shape[1])
|
|
top_classes = np.flip(np.argsort(output, axis=1), axis=1)[:, 0:top]
|
|
top_scores = np.flip(np.sort(output, axis=1), axis=1)[:, 0:top]
|
|
|
|
return classes, scores, [top_classes, top_scores]
|
|
|
|
|
|
def main(args):
|
|
# Initialize TRT and TF infer objects.
|
|
tf_infer = TensorFlowInfer(args.saved_model)
|
|
trt_infer = TensorRTInfer(args.engine)
|
|
|
|
batcher = ImageBatcher(args.input, *trt_infer.input_spec(), max_num_images=args.num_images,
|
|
preprocessor=args.preprocessor)
|
|
|
|
# Make sure both systems use the same input spec, so we can use the exact same image batches with both
|
|
tf_shape, tf_dtype = tf_infer.input_spec()
|
|
trt_shape, trt_dtype = trt_infer.input_spec()
|
|
if trt_dtype != tf_dtype:
|
|
print("Input datatype does not match")
|
|
print("TRT Engine Input Dtype: {} {}".format(trt_dtype))
|
|
print("TF Saved Model Input Dtype: {} {}".format(tf_dtype))
|
|
print("Please use the same TensorFlow saved model that the TensorRT engine was built with")
|
|
sys.exit(1)
|
|
|
|
if (tf_shape[1] and trt_shape[1] != tf_shape[1]) or (tf_shape[2] and trt_shape[2] != tf_shape[2]):
|
|
print("Input shapes do not match")
|
|
print("TRT Engine Input Shape: {} {}".format(trt_shape[1:]))
|
|
print("TF Saved Model Input Shape: {} {}".format(tf_shape[1:]))
|
|
print("Please use the same TensorFlow saved model that the TensorRT engine was built with")
|
|
sys.exit(1)
|
|
|
|
match = 0
|
|
error = 0
|
|
for batch, images in batcher.get_batch():
|
|
# Run inference on the same batch with both inference systems
|
|
tf_classes, tf_scores, _ = tf_infer.infer(batch)
|
|
trt_classes, trt_scores, _ = trt_infer.infer(batch)
|
|
|
|
# The last batch may not have all image slots filled, so limit the results to only the amount of actual images
|
|
tf_classes = tf_classes[0:len(images)]
|
|
tf_scores = tf_scores[0:len(images)]
|
|
trt_classes = trt_classes[0:len(images)]
|
|
trt_scores = trt_scores[0:len(images)]
|
|
|
|
# Track how many images match on top-1 class id predictions
|
|
match += np.sum(trt_classes == tf_classes)
|
|
# Track the mean square error in confidence score
|
|
error += np.sum((trt_scores - tf_scores) * (trt_scores - tf_scores))
|
|
|
|
print("Processing {} / {} images: {:.2f}% match ".format(batcher.image_index, batcher.num_images,
|
|
(100 * (match / batcher.image_index))), end="\r")
|
|
|
|
print()
|
|
pc = 100 * (match / batcher.num_images)
|
|
print("Matching Top-1 class predictions for {} out of {} images: {:.2f}%".format(match, batcher.num_images, pc))
|
|
avgerror = np.sqrt(error / batcher.num_images)
|
|
print("RMSE between TensorFlow and TensorRT confidence scores: {:.3f}".format(avgerror))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-e", "--engine", help="The TensorRT engine to infer with")
|
|
parser.add_argument("-m", "--saved_model", help="The TensorFlow saved model path to validate against")
|
|
parser.add_argument("-i", "--input",
|
|
help="The input to infer, either a single image path, or a directory of images")
|
|
parser.add_argument("-n", "--num_images", default=5000, type=int,
|
|
help="The maximum number of images to use for validation, default: 5000")
|
|
parser.add_argument("-p", "--preprocessor", default="V2", choices=["V1", "V1MS", "V2"],
|
|
help="Select the image preprocessor to use, either 'V2', 'V1' or 'V1MS', default: V2")
|
|
args = parser.parse_args()
|
|
if not all([args.engine, args.saved_model, args.input]):
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
main(args)
|