在开发者手册中搜索了一下torch,主要在下面三个部分提到:
这个demo是在线训练了mnist的网络,然后直接用torch的nn.Module.state_dict()方法把weights取出来,填充给builder创建的trt格式的network,然后利用这个被填充完weights的network创建engine,进行推断。
这个demo没有涉及到pytorch从文件中读取weights的问题。
# 只是返回了/mnist目录的绝对路径
data_path, _ = common.find_sample_data(description="Runs an MNIST network using a PyTorch model", subfolder="mnist")
# Train the PyTorch model
mnist_model = model.MnistModel()
mnist_model.learn()
# weights调用了nn.Module.state_dict()方法
weights = mnist_model.get_weights()
# Do inference with TensorRT.
with build_engine(weights) as engine:
# 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)
with engine.create_execution_context() as context:
case_num = load_random_test_case(mnist_model, 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)
print("Test Case: " + str(case_num))
print("Prediction: " + str(pred))
mnist_model
的参数,利用nn.Module.state_dict()
方法创建的。build_engine()
方法,封装了builder
和trt格式的network
的创建过程以及利用weights填充networks的过程,最后在build_engine()
方法中调用builder.build_cuda_engine(network)
来实现engine的创建。看demo的代码发现,确实如开发者手册3.2.6所说的,pytorch在这个demo上的实现,就是用”tensorRT语法“重新把网络结构写了一遍,也就是方法populate_network()的实现。
input_tensor = network.add_input(name=ModelData.INPUT_NAME, dtype=ModelData.DTYPE, shape=ModelData.INPUT_SHAPE)
conv1_w = weights['conv1.weight'].numpy()
conv1_b = weights['conv1.bias'].numpy()
conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b)
conv1.stride = (1, 1)
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)
# ModelData是上面自定义的一个数据结构
fc2.get_output(0).name = ModelData.OUTPUT_NAME
network.mark_output(tensor=fc2.get_output(0))
传入的network是TensorRT由builder创建的network,add_xxx()方法都是network的内置函数,看c++版本的别的demo,看到network是INetworkDefinition类型的指针。
INetworkDefinition* network = builder->createNetwork();
然后去Nvinfer.h中去找INetworkDefinition,发现这类下面果然有很多add_xxx()方法:
class INetworkDefinition
{
public:
virtual ITensor* addInput(const char* name, DataType type, Dims dimensions) = 0;
virtual void markOutput(ITensor& tensor) = 0;
virtual IConvolutionLayer* addConvolution(ITensor& input, int nbOutputMaps, DimsHW kernelSize, Weights kernelWeights, Weights biasWeights) = 0;
virtual IFullyConnectedLayer* addFullyConnected(ITensor& input, int nbOutputs, Weights kernelWeights, Weights biasWeights) = 0;
...
所以,其实这个demo就是利用tensorRT自带的构建网络的功能重建了torch构建的网络,没有读任何网络结构。所以还是想要用pytorch转onnx,再使用tensorRT。
由上面的结论,想用onnx格式进行尝试。
同样在Developer Guide中搜索一下onnx,发现有75处提到了,这里简略记录:
The ONNX Parser shipped with TensorRT 5.1.x supports ONNX IR (Intermediate Representation) version 0.0.3, opset version 9.
Export the trained network to a format such as UFF or ONNX which can be imported into TensorRT (see Working With Deep Learning Frameworks for more details).
.
这个sample将在mnixt数据集上训练好的model转化成onnx(Open Neural Network Exchange)格式。
C++代码除了多了个报错机制,基本和读取caffe模型一样
if (!onnxToTRTModel("mnist.onnx", 1, trtModelStream))
gLogger.reportFail(sampleTest);
gLogger.reportFail的作用是查看有关网络的其他信息(包括图层信息和单个图层维度)。
.
这个部分提到ONNX是有严格的版本限制的,TensorRT5.1.x支持的是ONNX IR 0.03,opset 9。
一般来说,ONNX解析器是具备向后兼容性的,因此之前版本的ONNXmodel一般不会遇到问题,但是不后兼容的功能可能会有例外,这时就需要把ONNX的模型文件转到支持的版本。
可能,有些人的model是通过更高版本的opsets生成的,这时可以看一下Github上TensorRT的ONNX模块是不是更新了,查看适合的版本。具体的位置是onnx_trt_backend.cpp的BACK+OPSET_VERSION变量的值。如果需要的话要下载最新版本的解析器,README写了build的过程
之后就又链接到了“Hello World” For TensorRT From ONNX”这个sample。
Python接口的onnx使用,这里涉及两个sample。
第一个sample只是介绍python中创建engine和执行inference的过程。[Introduction To Importing ONNX Models Into TensorRT Using Python](Introduction To Importing Caffe, TensorFlow And ONNX Models Into TensorRT Using Python).
基于yolov3-608的目标检测sample,不支持Ubuntu14.04及以下,不支持Python3.
第二个sample包含了yolov3转onnx,onnx转TensorRT的过程。Object Detection With The ONNX TensorRT Backend In Python.
ImportError: libnvinfer.so.5: cannot open shared object file: Nosuch file or directory
缺少什么文件就去/TensorRT5.x.x.x/targets/x86_64-linux-gnu/lib/目录下找对应的文件,复制到usr/lib
sudo cp /TensorRT5.x.x.x/targets/x86_64-linux-gnu/lib/libnvinfer.so.5 /usr/lib/
如果发现这个目录下没有报错的.so文件,就是误删了或者安装的不是TensorRT5.x,TensorRT4.x就会出现这个问题缺少libnvonnxparser.so.
问题出现原因是python加载动态库方面是默认从系统lib库上查找库文件,所以把需要的.so复制上去就行了。
File "/media/boyun/6a2d0d8c-27e4-4ad2-975c-b24087659438/pycharm/self_pytoch_tensorRT/test.py", line 54, in
batch_x, batch_y = Variable(batch_x.cuda(), volatile=True), Variable(batch_y.cuda(), volatile=True)
TypeError: 'module' object is not callable
查了之后告诉我,采用import...方式导入模块后,调用模块中的方法应该是模块名.方法(),如果忘记写模块名就会这样报错
,后来检查了,确实Variable方法应该是用from导入
即From torch.autograd import Variable
我的导入方法出了错误。