Files
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

277 lines
10 KiB
Python

#
# SPDX-FileCopyrightText: Copyright (c) 1993-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# 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 argparse
import numpy as np
import tensorrt as trt
import pycuda.driver as cuda
# If you face the following issue:
# "pycuda._driver.LogicError: explicit_context_dependent failed: invalid device context - no currently active context?"
# Add "import pycuda.autoinit", this is needed to initialize cuda!
import pycuda.autoinit
import tensorflow as tf
from examples.data.data_loader import load_data_tfrecord_tf, load_image_np, _SUPPORTED_MODEL_NAMES
TRT_DYNAMIC_DIM = -1
class HostDeviceMem(object):
"""Simple helper data class to store Host and Device memory."""
def __init__(self, host_mem, device_mem):
self.host = host_mem
self.device = device_mem
def __str__(self):
return "Host:\n" + str(self.host) + "\nDevice:\n" + str(self.device)
def __repr__(self):
return self.__str__()
def allocate_buffers(engine: trt.ICudaEngine, batch_size: int) -> [list, list, list]:
"""
Function to allocate buffers and bindings for TensorRT inference.
Args:
engine (trt.ICudaEngine):
batch_size (int): batch size to be used during inference.
Returns:
inputs (List): list of input buffers.
outputs (List): list of output buffers.
dbindings (List): list of device bindings.
"""
inputs = []
outputs = []
dbindings = []
for binding in engine:
binding_shape = engine.get_binding_shape(binding)
if binding_shape[0] == TRT_DYNAMIC_DIM: # dynamic shape
size = batch_size * abs(trt.volume(binding_shape))
else:
size = abs(trt.volume(binding_shape))
dtype = trt.nptype(engine.get_binding_dtype(binding))
# Allocate host and device buffers
host_mem = cuda.pagelocked_empty(size, dtype)
device_mem = cuda.mem_alloc(host_mem.nbytes)
# Append the device buffer to device bindings
dbindings.append(int(device_mem))
# Append to the appropriate list (input/output)
if engine.binding_is_input(binding):
inputs.append(HostDeviceMem(host_mem, device_mem))
else:
outputs.append(HostDeviceMem(host_mem, device_mem))
return inputs, outputs, dbindings
def infer(
engine_path: str,
val_batches,
batch_size: int = 8,
top_k_value: int = 1,
) -> None:
"""
Performs inference in TensorRT engine.
Args:
engine_path (str): path to the TensorRT engine.
val_batches (tf.data.Dataset): validation dataset (batches).
batch_size (int): batch size used for inference and dataset batch splitting.
top_k_value (int): value of `K` for the top K predictions used in the accuracy calculation.
Raises:
RuntimeError: raised when loading images in the host fails.
"""
def override_shape(shape: tuple) -> tuple:
"""Overrides batch dimension if dynamic."""
if TRT_DYNAMIC_DIM in shape:
shape = tuple(
[batch_size if dim == TRT_DYNAMIC_DIM else dim for dim in shape]
)
return shape
# Open engine as runtime
with open(engine_path, "rb") as f, trt.Runtime(
trt.Logger(trt.Logger.ERROR)
) as runtime:
engine = runtime.deserialize_cuda_engine(f.read())
# Allocate buffers and create a CUDA stream.
inputs, outputs, dbindings = allocate_buffers(engine, batch_size)
# Initiate test_accuracy
test_accuracy = tf.keras.metrics.SparseTopKCategoricalAccuracy(
k=top_k_value, name="top_k_accuracy", dtype=tf.float32
)
test_accuracy.reset_states()
# Contexts are used to perform inference.
with engine.create_execution_context() as context:
# Resolves dynamic shapes in the context
for binding in engine:
binding_idx = engine.get_binding_index(binding)
binding_shape = engine.get_binding_shape(binding_idx)
if engine.binding_is_input(binding_idx):
binding_shape = override_shape(binding_shape)
context.set_binding_shape(binding_idx, binding_shape)
if isinstance(val_batches, tf.Tensor):
# Load images in Host (flatten and copy to page-locked buffer in Host)
data = val_batches.numpy().astype(np.float32).ravel()
pagelocked_buffer = inputs[0].host
np.copyto(pagelocked_buffer, data)
inp = inputs[0]
# Transfer input data from Host to Device (GPU)
cuda.memcpy_htod(inp.device, inp.host)
# Run inference
context.execute_v2(dbindings)
# Transfer predictions back to Host from GPU
out = outputs[0]
cuda.memcpy_dtoh(out.host, out.device)
softmax_output = np.array(out.host)
top1_idx = np.argmax(softmax_output)
output_confidence = softmax_output[top1_idx]
print("Top-1 Index of the image : {} Confidence: {}".format(top1_idx, output_confidence))
elif isinstance(val_batches, tf.data.Dataset):
# Loop over number of steps to evaluate entire validation dataset
for step, example in enumerate(val_batches):
images, labels = example
if step % 100 == 0 and step != 0:
print(
"Evaluating batch {}: {:.4f}".format(
step, test_accuracy.result()
)
)
try:
# Load images in Host (flatten and copy to page-locked buffer in Host)
data = images.numpy().astype(np.float32).ravel()
pagelocked_buffer = inputs[0].host
np.copyto(pagelocked_buffer, data)
except RuntimeError:
raise RuntimeError(
"Failed to load images in Host at step {}".format(step)
)
inp = inputs[0]
# Transfer input data from Host to Device (GPU)
cuda.memcpy_htod(inp.device, inp.host)
# Run inference
context.execute_v2(dbindings)
# Transfer predictions back to Host from GPU
out = outputs[0]
cuda.memcpy_dtoh(out.host, out.device)
# Split 1-D output of length N*labels into 2-D array of (N, labels)
batch_outs = np.array(np.split(np.array(out.host), batch_size))
# Update test accuracy
test_accuracy.update_state(labels, batch_outs)
# Print final accuracy and save to log file
print("\n======================================\n")
result_str = "Top-{} accuracy: {:.4f}\n".format(
top_k_value, test_accuracy.result()
)
print(result_str)
# Save logs to file
results_dir = "/".join(args.engine.split("/")[:-1])
with open(os.path.join(results_dir, args.log_file), "w") as log_file:
log_file.write(result_str)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Run inference on TensorRT engines for Imagenet-based Classification models."
)
parser.add_argument(
"-e", "--engine", type=str, required=True, help="Path to TensorRT engine"
)
parser.add_argument(
"--image", type=str, help="Path to an image to perform single image inference"
)
parser.add_argument(
"-m",
"--model_name",
type=str,
default="resnet_v1",
help="Name of the model, needed to choose the appropriate input pre-processing."
"Options include {}".format(_SUPPORTED_MODEL_NAMES),
)
parser.add_argument(
"-d",
"--data_dir",
default="/media/Data/ImageNet/train-val-tfrecord",
type=str,
help="Path to directory of input images in tfrecord format (val data).",
)
parser.add_argument(
"-k",
"--top_k_value",
default=1,
type=int,
help="Value of `K` for the top-K predictions used in the accuracy calculation.",
)
parser.add_argument(
"-b",
"--batch_size",
default=1,
type=int,
help="Number of inputs to send in parallel (up to max batch size of engine).",
)
parser.add_argument(
"--log_file",
type=str,
default="engine_accuracy.log",
help="Filename to save logs.",
)
args = parser.parse_args()
if args.model_name not in _SUPPORTED_MODEL_NAMES:
raise ValueError(
"Invalid model name ",
args.model_name,
" provided. Please select among {}".format(_SUPPORTED_MODEL_NAMES),
)
# Load the test data and pre-process input
val_batches = None
if args.image:
val_batches = load_image_np(args.image, args.model_name)
else:
data_batches = load_data_tfrecord_tf(
data_dir=args.data_dir, batch_size=args.batch_size, model_name=args.model_name
)
val_batches = data_batches["validation"]
# Perform inference
infer(
args.engine,
val_batches,
batch_size=args.batch_size,
top_k_value=args.top_k_value,
)