2d517d270e
Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com>
165 lines
7.0 KiB
Python
165 lines
7.0 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
|
|
|
|
# This sample uses an MNIST PyTorch model to create a TensorRT Inference Engine
|
|
import model
|
|
import numpy as np
|
|
import pycuda.autoinit
|
|
import tensorrt as trt
|
|
|
|
sys.path.insert(1, os.path.join(sys.path[0], os.path.pardir))
|
|
|
|
import common
|
|
|
|
# You can set the logger severity higher to suppress messages (or lower to display more messages).
|
|
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
|
|
|
|
class ModelData(object):
|
|
INPUT_NAME = "data"
|
|
INPUT_SHAPE = (1, 28, 28)
|
|
OUTPUT_NAME = "prob"
|
|
OUTPUT_SIZE = 10
|
|
DTYPE = trt.float32
|
|
|
|
# Populate the TRT network, injecting some dummy weights
|
|
def populate_network_with_some_dummy_weights(network, weights):
|
|
# Configure the network layers based on the weights provided.
|
|
input_tensor = network.add_input(name=ModelData.INPUT_NAME, dtype=ModelData.DTYPE, shape=ModelData.INPUT_SHAPE)
|
|
|
|
# Set dummy weights for the kernel and bias weights in the conv1 layer. We
|
|
# will refit the engine with the actual weights later.
|
|
conv1_w = np.zeros((20,5,5), dtype=np.float32)
|
|
conv1_b = np.zeros(20, dtype=np.float32)
|
|
|
|
conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b)
|
|
conv1.name = "conv_1"
|
|
conv1.stride = (1, 1)
|
|
# Associate weights with name and refit weights via name later in refitter.
|
|
network.set_weights_name(conv1_w, 'conv1.weight')
|
|
|
|
pool1 = network.add_pooling(input=conv1.get_output(0), type=trt.PoolingType.MAX, window_size=(2, 2))
|
|
pool1.stride = (2, 2)
|
|
|
|
conv2_w = weights['conv2.weight'].numpy()
|
|
conv2_b = weights['conv2.bias'].numpy()
|
|
conv2 = network.add_convolution(pool1.get_output(0), 50, (5, 5), conv2_w, conv2_b)
|
|
conv2.stride = (1, 1)
|
|
|
|
pool2 = network.add_pooling(conv2.get_output(0), trt.PoolingType.MAX, (2, 2))
|
|
pool2.stride = (2, 2)
|
|
|
|
fc1_w = weights['fc1.weight'].numpy()
|
|
fc1_b = weights['fc1.bias'].numpy()
|
|
fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=500, kernel=fc1_w, bias=fc1_b)
|
|
|
|
relu1 = network.add_activation(input=fc1.get_output(0), type=trt.ActivationType.RELU)
|
|
|
|
fc2_w = weights['fc2.weight'].numpy()
|
|
fc2_b = weights['fc2.bias'].numpy()
|
|
fc2 = network.add_fully_connected(relu1.get_output(0), ModelData.OUTPUT_SIZE, fc2_w, fc2_b)
|
|
|
|
fc2.get_output(0).name = ModelData.OUTPUT_NAME
|
|
network.mark_output(tensor=fc2.get_output(0))
|
|
|
|
# Build a TRT engine, but leave out some weights
|
|
def build_engine_with_some_missing_weights(weights):
|
|
# For more information on TRT basics, refer to the introductory samples.
|
|
builder = trt.Builder(TRT_LOGGER)
|
|
network = builder.create_network()
|
|
config = builder.create_builder_config()
|
|
runtime = trt.Runtime(TRT_LOGGER)
|
|
|
|
config.max_workspace_size = common.GiB(1)
|
|
# Set the refit flag in the builder
|
|
config.set_flag(trt.BuilderFlag.REFIT)
|
|
# Populate the network using weights from the PyTorch model.
|
|
populate_network_with_some_dummy_weights(network, weights)
|
|
# Build and return an engine.
|
|
plan = builder.build_serialized_network(network, config)
|
|
return runtime.deserialize_cuda_engine(plan)
|
|
|
|
# Copy an image to the pagelocked input buffer
|
|
def load_img_to_input_buffer(img, pagelocked_buffer):
|
|
np.copyto(pagelocked_buffer, img)
|
|
|
|
# Get the accuracy on the test set using TensorRT
|
|
def get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model):
|
|
context = engine.create_execution_context()
|
|
correct = 0
|
|
total = 0
|
|
# Run inference on every sample.
|
|
# Technically this could be batched, however this only comprises a fraction of total
|
|
# time spent in the test.
|
|
for test_img, test_name in mnist_model.get_all_test_samples():
|
|
load_img_to_input_buffer(test_img, pagelocked_buffer=inputs[0].host)
|
|
# For more information on performing inference, refer to the introductory samples.
|
|
# The common.do_inference function will return a list of outputs - we only have one in this case.
|
|
[output] = common.do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)
|
|
pred = np.argmax(output)
|
|
correct += (test_name == pred)
|
|
total += 1
|
|
|
|
accuracy = float(correct)/total
|
|
print("Got {} correct predictions out of {} ({:.1f}%)".format(correct, total, 100 * accuracy))
|
|
|
|
return accuracy
|
|
|
|
|
|
def main():
|
|
common.add_help(description="Runs an MNIST network using a PyTorch model")
|
|
# Train the PyTorch model
|
|
mnist_model = model.MnistModel()
|
|
mnist_model.learn()
|
|
weights = mnist_model.get_weights()
|
|
# Do inference with TensorRT.
|
|
engine = build_engine_with_some_missing_weights(weights)
|
|
# Build an engine, allocate buffers and create a stream.
|
|
# For more information on buffer allocation, refer to the introductory samples.
|
|
inputs, outputs, bindings, stream = common.allocate_buffers(engine)
|
|
print("Accuracy Before Engine Refit")
|
|
get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model)
|
|
|
|
# Refit the engine with the actual trained weights for the conv_1 layer.
|
|
refitter = trt.Refitter(engine, TRT_LOGGER)
|
|
|
|
# To get a list of all refittable layers and associated weightRoles
|
|
# in the network, use refitter.get_all()
|
|
# Set the actual weights for the conv_1 layer. Since it consists of
|
|
# kernel weights and bias weights, set each of them by specifying
|
|
# the WeightsRole.
|
|
# Prefer to refit named weights via set_named_weights
|
|
refitter.set_named_weights('conv1.weight', weights['conv1.weight'].numpy())
|
|
# set_named_weights is not available for unnamed weights. Call set_weights instead.
|
|
refitter.set_weights("conv_1", trt.WeightsRole.BIAS,
|
|
weights['conv1.bias'].numpy())
|
|
# Get missing weights names. This should return empty
|
|
# lists in this case.
|
|
missing_weights = refitter.get_missing_weights()
|
|
assert len(missing_weights) == 0, "Refitter found missing weights. Call set_named_weights() or set_weights() for all missing weights"
|
|
# Refit the engine with the new weights. This will return True if
|
|
# the refit operation succeeded.
|
|
assert refitter.refit_cuda_engine()
|
|
|
|
expected_correct_predictions = mnist_model.get_latest_test_set_accuracy()
|
|
print("Accuracy After Engine Refit (expecting {:.1f}% correct predictions)".format(100 * expected_correct_predictions))
|
|
assert get_trt_test_accuracy(engine, inputs, outputs, bindings, stream, mnist_model) >= expected_correct_predictions
|
|
|
|
if __name__ == '__main__':
|
|
main()
|