TensorRT程序分析

文章目录


##写在前面
TensorRT 本质上是C++ library, 它是帮助我们在产品边缘部署的时候,优化训练好的网络,充分利用资源有限的GPU来做高性能的推理。
我们可以利用 TensorRT提供的C++/Python API,提升用Caffe 、Mxnet、TensorFlow训练好的网络模型。

首先,部署TensorRT进行推理,我们需要有一个训练好的网络,
需要它的网络结构文件*.prototxt
需要它的网络权值文件*caffemodel

我们要用一个cuda程序来部署,那么这个在TensorRT中的cuda程序如何编写呢,我们还是从TensorRT给的实例来分析


##simpleMNIST.cpp

###Part1 caffe model 到 TensorRT model的转换 -->gieModelStream

// create a GIE model from the caffe model and serialize it to a stream
std::stringstream gieModelStream;
caffeToGIEModel("mnist.prototxt", "mnist.caffemodel", std::vector < std::string > { OUTPUT_BLOB_NAME }, 1, gieModelStream);

main函数 上来调用caffeToGIEModel函数 把caffe网络模型转化为GIE(即TensorRT)网络模型,并将其序列化,那么到底是如何操作的呢

//从入口参数也可以看得到,它需要caffe网络模型的 网络结构文件和网络权值文件,  
//指定batchsize 最后是转换后的TensorRT model
void caffeToGIEModel(const std::string& deployFile,				// name for caffe prototxt
					 const std::string& modelFile,				// name for model 
					 const std::vector& outputs,   // network outputs
					 unsigned int maxBatchSize,					// batch size - NB must be at least as large as the batch we want to run with)
					 std::ostream& gieModelStream)				// output stream for the GIE model
{
	// create the builder
	IBuilder* builder = createInferBuilder(gLogger);

	// parse the caffe model to populate the network, then set the outputs
	INetworkDefinition* network = builder->createNetwork();
	ICaffeParser* parser = createCaffeParser();
	const IBlobNameToTensor* blobNameToTensor = parser->parse(locateFile(deployFile).c_str(),
															  locateFile(modelFile).c_str(),
															  *network,
															  DataType::kFLOAT);

	// specify which tensors are outputs
	for (auto& s : outputs)
		network->markOutput(*blobNameToTensor->find(s.c_str()));

	// Build the engine
	builder->setMaxBatchSize(maxBatchSize);
	builder->setMaxWorkspaceSize(1 << 20);

	ICudaEngine* engine = builder->buildCudaEngine(*network);
	assert(engine);

	// we don't need the network any more, and we can destroy the parser
	network->destroy();
	parser->destroy();

	// serialize the engine, then close everything down
	engine->serialize(gieModelStream);
	engine->destroy();
	builder->destroy();
	shutdownProtobufLibrary();
}
//创建了一个builder,并利用builder创建一个createNetwork
//解析caffe model
//指明TensorRT的输出
//创建engine
//序列化engine

###Part2 输入 输入文件与均值文件作差 ---->data[INPUT_H*INPUT_W]

//因为这个实例是手写数字的识别,所以它随机产生了一个十以内的数字,并且print出来
//获取均值文件
//把均值文件与输入的文件作差
	// read a random digit file
	srand(unsigned(time(nullptr)));
	uint8_t fileData[INPUT_H*INPUT_W];
	readPGMFile(std::to_string(rand() % 10) + ".pgm", fileData);

	// print an ascii representation
	std::cout << "\n\n\n---------------------------" << "\n\n\n" << std::endl;
	for (int i = 0; i < INPUT_H*INPUT_W; i++)
		std::cout << (" .:-=+*#%@"[fileData[i] / 26]) << (((i + 1) % INPUT_W) ? "" : "\n");

	// parse the mean file and 	subtract it from the image
	ICaffeParser* parser = createCaffeParser();
	IBinaryProtoBlob* meanBlob = parser->parseBinaryProto(locateFile("mnist_mean.binaryproto").c_str());
	parser->destroy();

	const float *meanData = reinterpret_cast(meanBlob->getData());

	float data[INPUT_H*INPUT_W];
	for (int i = 0; i < INPUT_H*INPUT_W; i++)
		data[i] = float(fileData[i])-meanData[i];

	meanBlob->destroy();


###Part3 反序列化引擎 得到---->IExecutionContext *context

	// deserialize the engine 
	gieModelStream.seekg(0, gieModelStream.beg);

	IRuntime* runtime = createInferRuntime(gLogger);
	ICudaEngine* engine = runtime->deserializeCudaEngine(gieModelStream);

	IExecutionContext *context = engine->createExecutionContext();
	//创建runtime
	//反序列化引擎
	//创建可执行上下文


###Part4 执行推理,输出结果----->doInference【context, data, prob, 1】—>prob

	// run inference
	float prob[OUTPUT_SIZE];
	doInference(*context, data, prob, 1);

	// destroy the engine
	context->destroy();
	engine->destroy();
	runtime->destroy();

	// print a histogram of the output distribution
	std::cout << "\n\n";
	for (unsigned int i = 0; i < 10; i++)
		std::cout << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5f)), '*') << "\n";
	std::cout << std::endl;
//执行推理
//输出结果

所以着重看一下怎么执行推理的, doInference(*context, data, prob, 1);
从函数的入口参数也能看出,*context, data分别是Part3/2的结果 prob是最终的推理结果,batchsize

创建GPU buffer CHECK(cudaMalloc(&buffers[inputIndex], batchSize *
INPUT_H * INPUT_W * sizeof(float)));
CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE *
sizeof(float)));

执行推理 cudaStream_t stream; CHECK(cudaStreamCreate(&stream));

// DMA the input to the GPU, execute the batch asynchronously, and
DMA it back: CHECK(cudaMemcpyAsync(buffers[inputIndex], input,
batchSize * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice,
stream)); context.enqueue(batchSize, buffers, stream, nullptr);
CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize *
OUTPUT_SIZE*sizeof(float), cudaMemcpyDeviceToHost, stream));
cudaStreamSynchronize(stream);


void doInference(IExecutionContext& context, float* input, float* output, int batchSize)
{
	const ICudaEngine& engine = context.getEngine();
	// input and output buffer pointers that we pass to the engine - the engine requires exactly IEngine::getNbBindings(),
	// of these, but in this case we know that there is exactly one input and one output.
	assert(engine.getNbBindings() == 2);
	void* buffers[2];

	// In order to bind the buffers, we need to know the names of the input and output tensors.
	// note that indices are guaranteed to be less than IEngine::getNbBindings()
	int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME), 
		outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);

	// create GPU buffers and a stream
	CHECK(cudaMalloc(&buffers[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));
	CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));

	cudaStream_t stream;
	CHECK(cudaStreamCreate(&stream));

	// DMA the input to the GPU,  execute the batch asynchronously, and DMA it back:
	CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));
	context.enqueue(batchSize, buffers, stream, nullptr);
	CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE*sizeof(float), cudaMemcpyDeviceToHost, stream));
	cudaStreamSynchronize(stream);

	// release the stream and the buffers
	cudaStreamDestroy(stream);
	CHECK(cudaFree(buffers[inputIndex]));
	CHECK(cudaFree(buffers[outputIndex]));
}

你可能感兴趣的:(机器学习,NVIDIA,开发工具链)