194 lines
7.0 KiB
Python
194 lines
7.0 KiB
Python
#
|
|
# SPDX-FileCopyrightText: Copyright (c) 1993-2026 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 sys
|
|
|
|
import tensorrt as trt
|
|
|
|
from model import TRT_MODEL_PATH
|
|
from load_plugin_lib import load_plugin_lib
|
|
|
|
# ../common.py
|
|
parent_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)
|
|
sys.path.insert(1, parent_dir)
|
|
import common
|
|
|
|
# Reuse some BiDAF-specific methods
|
|
# ../engine_refit_onnx_bidaf/data_processing.py
|
|
sys.path.insert(1, os.path.join(parent_dir, "engine_refit_onnx_bidaf"))
|
|
from engine_refit_onnx_bidaf.data_processing import preprocess, get_inputs
|
|
|
|
# Maxmimum number of words in context or query text.
|
|
# Used in optimization profile when building engine.
|
|
# Adjustable.
|
|
MAX_TEXT_LENGTH = 64
|
|
|
|
WORKING_DIR = os.environ.get("TRT_WORKING_DIR") or os.path.dirname(
|
|
os.path.realpath(__file__)
|
|
)
|
|
|
|
# Path to which trained model will be saved (check README.md)
|
|
ENGINE_FILE_PATH = os.path.join(WORKING_DIR, "bidaf.trt")
|
|
|
|
# Define global logger object (it should be a singleton,
|
|
# available for TensorRT from anywhere in code).
|
|
# You can set the logger severity higher to suppress messages
|
|
# (or lower to display more messages)
|
|
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
|
|
|
|
|
|
# Builds TensorRT Engine
|
|
def build_engine(model_path):
|
|
|
|
builder = trt.Builder(TRT_LOGGER)
|
|
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.STRONGLY_TYPED))
|
|
config = builder.create_builder_config()
|
|
parser = trt.OnnxParser(network, TRT_LOGGER)
|
|
runtime = trt.Runtime(TRT_LOGGER)
|
|
|
|
# Parse model file
|
|
print("Loading ONNX file from path {}...".format(model_path))
|
|
with open(model_path, "rb") as model:
|
|
print("Beginning ONNX file parsing")
|
|
if not parser.parse(model.read()):
|
|
print("ERROR: Failed to parse the ONNX file.")
|
|
for error in range(parser.num_errors):
|
|
print(parser.get_error(error))
|
|
return None
|
|
print("Completed parsing of ONNX file")
|
|
|
|
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, common.GiB(1))
|
|
|
|
# The input text length is variable, so we need to specify an optimization profile.
|
|
profile = builder.create_optimization_profile()
|
|
for i in range(network.num_inputs):
|
|
input = network.get_input(i)
|
|
assert input.shape[0] == -1
|
|
min_shape = [1] + list(input.shape[1:])
|
|
opt_shape = [8] + list(input.shape[1:])
|
|
max_shape = [MAX_TEXT_LENGTH] + list(input.shape[1:])
|
|
profile.set_shape(input.name, min_shape, opt_shape, max_shape)
|
|
config.add_optimization_profile(profile)
|
|
|
|
print("Building TensorRT engine. This may take a few minutes.")
|
|
plan = builder.build_serialized_network(network, config)
|
|
engine = runtime.deserialize_cuda_engine(plan)
|
|
with open(ENGINE_FILE_PATH, "wb") as f:
|
|
f.write(plan)
|
|
return engine
|
|
|
|
|
|
def load_test_case(inputs, context_text, query_text, trt_context):
|
|
# Part 1: Specify Input shapes
|
|
cw, cc = preprocess(context_text)
|
|
qw, qc = preprocess(query_text)
|
|
for arr in (cw, cc, qw, qc):
|
|
assert arr.shape[0] <= MAX_TEXT_LENGTH, (
|
|
"Input context or query is too long! "
|
|
+ "Either decrease the input length or increase MAX_TEXT_LENGTH"
|
|
)
|
|
trt_context.set_input_shape("CategoryMapper_4", cw.shape)
|
|
trt_context.set_input_shape("CategoryMapper_5", cc.shape)
|
|
trt_context.set_input_shape("CategoryMapper_6", qw.shape)
|
|
trt_context.set_input_shape("CategoryMapper_7", qc.shape)
|
|
|
|
# Part 2: load input data
|
|
cw_flat, cc_flat, qw_flat, qc_flat = get_inputs(context_text, query_text)
|
|
for i, arr in enumerate([cw_flat, cc_flat, qw_flat, qc_flat]):
|
|
inputs[i].host = arr
|
|
|
|
|
|
def main():
|
|
# Load the shared object file containing the Hardmax plugin implementation.
|
|
# By doing this, you will also register the Hardmax plugin with the TensorRT
|
|
# PluginRegistry through use of the macro REGISTER_TENSORRT_PLUGIN present
|
|
# in the plugin implementation. Refer to plugin/customHardmaxPlugin.cpp for more details.
|
|
load_plugin_lib()
|
|
|
|
# Load pretrained model
|
|
if not os.path.isfile(TRT_MODEL_PATH):
|
|
raise IOError(
|
|
"\n{}\n{}\n{}\n".format(
|
|
"Failed to load model file ({}).".format(TRT_MODEL_PATH),
|
|
"Please use 'python3 model.py' to generate the ONNX model.",
|
|
"For more information, see README.md",
|
|
)
|
|
)
|
|
|
|
if os.path.exists(ENGINE_FILE_PATH):
|
|
print(f"Loading saved TRT engine from {ENGINE_FILE_PATH}")
|
|
with open(ENGINE_FILE_PATH, "rb") as f:
|
|
runtime = trt.Runtime(TRT_LOGGER)
|
|
runtime.max_threads = 10
|
|
engine = runtime.deserialize_cuda_engine(f.read())
|
|
else:
|
|
print("Engine plan not saved. Building new engine...")
|
|
engine = build_engine(TRT_MODEL_PATH)
|
|
|
|
inputs, outputs, bindings = common.allocate_buffers(engine, profile_idx=0)
|
|
|
|
testcases = [
|
|
(
|
|
"Garry the lion is 5 years old. He lives in the savanna.",
|
|
"Where does the lion live?",
|
|
),
|
|
("A quick brown fox jumps over the lazy dog.", "What color is the fox?"),
|
|
]
|
|
|
|
print("\n=== Testing ===")
|
|
|
|
interactive = "--interactive" in sys.argv
|
|
if interactive:
|
|
context_text = input("Enter context: ")
|
|
query_text = input("Enter query: ")
|
|
testcases = [(context_text, query_text)]
|
|
|
|
trt_context = engine.create_execution_context()
|
|
|
|
# Use context manager for proper stream lifecycle management
|
|
with common.CudaStreamContext() as stream:
|
|
for context_text, query_text in testcases:
|
|
|
|
context_words, _ = preprocess(context_text)
|
|
|
|
load_test_case(inputs, context_text, query_text, trt_context)
|
|
if not interactive:
|
|
print(f"Input context: {context_text}")
|
|
print(f"Input query: {query_text}")
|
|
trt_outputs = common.do_inference(
|
|
trt_context,
|
|
engine=engine,
|
|
bindings=bindings,
|
|
inputs=inputs,
|
|
outputs=outputs,
|
|
stream=stream,
|
|
)
|
|
start = trt_outputs[1].item()
|
|
end = trt_outputs[0].item()
|
|
answer = context_words[start : end + 1].flatten()
|
|
print(f"Model prediction: ", " ".join(answer))
|
|
print()
|
|
|
|
# Note: free_buffers no longer needs stream parameter
|
|
common.free_buffers(inputs, outputs)
|
|
print("Passed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|