Tensorrt 优化加速Tensorflow 生成的模型,详细流程,可以正常使用

  1. 查看linux上安装的cuda版本和cudnn版本,linux 的版本
    1. cuda:cat /usr/local/cuda-10/version.txt 这里使用的是cuda10,其他的具体分析
    2. cudnn:cat /usr/local/cuda-10/include/cudnn.h | grep CUDNN_MAJOR -A 2
    3. linux版本: cat /etc/issue
  2. 根据对应的cuda和cudnn版本下载对应的tensorrt版本
    1. 下载地址:https://developer.nvidia.com/tensorrt
    2. 对于nvidia提供的nano上tensorrt,在https://developer.nvidia.com/embedded/jetpack 中有详细的官方介绍,这个tensorrt集成在了jetpack中
  3. 将 TensorRTlib 添加进环境变量
    1. 在本地环境中的.profile中添加环境变量:LD_LIBRARY_PATH=*/TensorRT-6.0.1.5/lib:$LD_LIBRARY_PATH
    2. 注意使用的tensorrt的版本,我这里使用的是6
  4. 安装TensorRT wheelfile 这个根据python的版本来确定,我用的是6.0.1.5
    1. 在*/TensorRT-6.0.1.5/python 中执行
    2. pip3 install tensorrt-6.0.1.5-cp35-none-linux_x86_64.whl
  5. 赚点外快:本人出售中高端面膜,需要的联系vx:18600043898
  6. 安装uff文件
    1. cd */TensorRT-6.0.1.5/uff
    2. pip3 install uff-0.6.5-py2.py3-none-any.whl
    3. 检查安装情况 which convert-to-uff
  7. 安装graphsurgeon 
    1. cd */TensorRT-6.0.1.5/graphsurgeon
    2. pip3 install graphsurgeon-0.4.1-py2.py3-none-any.whl
  8. 验证安装情况:
    1. Build and run one of the shipped samples, for example, sampleMNIST in the installed directory. You should be able to compile and execute the sample without additional settings. For more information, see the “Hello World” For TensorRT (sampleMNIST).
      1. 运行案例的时候,有cuda_install_dir的配置,注意将配置文件中的cuda_install_dir的路径更改一下
      2. 将cuda的库放进本地环境变量中
      3. 编译sampleMNIST 查看reademe文件
      4. 下载需要的数据
        1. TensorRT-6.0.1.5/data/mnist$
        2. wget http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
        3. wget http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
        4. 解压文件gunzip train-images-idx3-ubyte.gz
        5. gunzip train-labels-idx1-ubyte.gz
        6. 解析数据:python3 generate_pgms.py
        7. ./sample_mnist [-h] [--datadir=/path/to/data/dir/] [--useDLA=N] [--fp16 or --int8]
        8. 结果和readme中的结果一样,证明安装成功
  9. 在自己的环境中:检查tensorrt的情况,
    1. python3 回车
    2. import tensorrt
    3. tensorrt.__version__ 显示tensorrt的版本信息,证明安装成功
  10. 安装python版本的tensorflow
    1. 查看tensorrt需要的tensorflow的版本 https://docs.nvidia.com/deeplearning/sdk/tensorrt-install-guide/index.html#overview 2. Getting Started 中介绍需要1.14.0的tensorflow
    2. pip3 install tensorflow==1.14.0
    3. 在使用中出现了版本的问题,这里先将tensorflow降低版本值1.12.0;降低后发现,模型可以正常使用了
    4. 注意:使用tensorrt进行模型的优化,这里要注意tensorflow的版本,这个版本要和训练模型的版本一致。tensorflow在1.13之前,基本都是兼容的;1.14后改变较大,编译时注意一下。
  11. 模型转换的函数(1,2对应的使用的tensorrt的编译,是python版本的,我使用的c++类型的 3)
    1. def TF_TRT_frozen_graph_12():
          frozen_graph_path = ‘*.pb’
          save_frozen_graph_path = ''  #保存路径
          if not os.path.isdir(save_frozen_graph_path):
              os.mkdir(save_frozen_graph_path)
          save_frozen_graph_path+='*.pb'
          # 将模型读取
          graph_def = tf.GraphDef()
          with tf.gfile.GFile(frozen_graph_path, 'rb') as f:
              graph_def.ParseFromString(f.read())
      
          calib_graph = trt.create_inference_graph(
              # 要转换的模型
              input_graph_def=graph_def,
              #
              outputs=[' '],  # 输出的节点的名字,这个是在模型训练的时候有这个参数,看看你们自己写的是什么名字
              # 控制精度
              precision_mode='FP16'
          )
      
          #将optimized graph保存起来
          with open(save_frozen_graph_path,'wb') as wh:
              wh.write(calib_graph.SerializeToString())
    2. 1.14以后的转换函数
      1. def TF_TRT_frozen_graph_14():
            frozen_graph_path = '*.pb'
            save_frozen_graph_path = ‘’  #保存路径
            if not os.path.isdir(save_frozen_graph_path):
                os.mkdir(save_frozen_graph_path)
            save_frozen_graph_path += '*.pb'
            # 将模型读取
            graph_def = tf.compat.v1.GraphDef()
            with tf.io.gfile.GFile(frozen_graph_path, 'rb') as f:
                graph_def.ParseFromString(f.read())
        
            converter = trt.TrtGraphConverter(
                # 要转换的模型
                input_graph_def=graph_def,
                #
                nodes_blacklist=[' '],  # 输出的节点的名字
                # 控制精度
                precision_mode='FP16'
            )
        
            frozen_graph = converter.convert()
        
            #savedModel 的保存形式
            # converter.save(save_frozen_graph_path)
        
            #将optimized graph保存起来
            with open(save_frozen_graph_path,'wb') as wh:
                wh.write(frozen_graph.SerializeToString())​​​​​​​​
    3. ​​​​​​​使用c++编译的流程:​​​​​​​

          IBuilder* builder = nvinfer1::createInferBuilder(gLogger.getTRTLogger());
          assert(builder != nullptr);

          //这个根据外部输入决定
          builder->setMaxBatchSize(batch_size);
          //控制编译时占用的内存的空间的大小,这个大小根据自己的需要调控
          int MAX_WORKSPACE = 300000000;
          builder->setMaxWorkspaceSize(MAX_WORKSPACE);
          nvinfer1::INetworkDefinition* network = builder->createNetwork();
          
          //创建解析对象
          auto parser = nvuffparser::createUffParser();
          //将模型的输入输出在uff文件中记录,这里输入的数据维度等信息,注意根据自己的需要进行修正
          parser->registerInput("input_node", nvinfer1::Dims3(channel_size, 1, width), nvuffparser::UffInputOrder::kNCHW);
          parser->registerOutput("myoutputnode"); //myoutputnode 是输出节点的名字
          
          if (!parser->parseBuffer(model,,model_Len, *network, nvinfer1::DataType::kFLOAT))  //kFLOAT :32位   kHALF :16位
          {
              printf("Failure while parsing UFF file\n");
              return 0;
          }
          
          ICudaEngine* engine = builder->buildCudaEngine(*network);

          if (!engine)
          {
              printf("Unable to create engine\n");
              return 0;
          }
          //将引擎序列化保存起来
          IHostMemory *serializedModel = engine->serialize();
          ofstream ofs(save_path, ios::out | ios::binary);
          ofs.write((char*)(serializedModel->data()), serializedModel->size());
          ofs.close();
          serializedModel->destroy();

      1. ​​​​​​​将序列化的模型进行加载和使用:

        1. ​​​​​​​IRuntime* runtime = nvinfer1::createInferRuntime(gLogger.getTRTLogger());

          ​​​​​​​​​​​​​ICudaEngine* engine = runtime->deserializeCudaEngine(model,model_len, nullptr)
        2. const ICudaEngine& engine = context.getEngine();
        3. // 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];
        4. // 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_layer);                                                                           int outputIndex = engine.getBindingIndex(output_layer);
        5. std::unique_ptr output(new float[batch_size * num_class * sizeof(float)]);
        6. create GPU buffers and a stream
           CHECK(cudaMalloc(&buffers[inputIndex], batch_size * 1 * width * sizeof(float)));
           CHECK(cudaMalloc(&buffers[outputIndex], batch_size * num_class * sizeof(float)));
           cudaStream_t stream;
           CHECK(cudaStreamCreate(&stream));
        7. DMA the input to the GPU,  execute the batch asynchronously, and DMA it back:​​​​​​​​​​​​​CHECK(cudaMemcpyAsync(buffers[inputIndex], input + data_offset , batch_size * 1 * width * sizeof(float), cudaMemcpyHostToDevice, stream));
        8. context.enqueue(batch_size, buffers, stream, nullptr);
        9. CHECK(cudaMemcpyAsync(output.get(), buffers[outputIndex], batch_size * num_class * sizeof(float), cudaMemcpyDeviceToHost, stream));
        10. release the stream and the buffers                                                                              cudaStreamDestroy(stream);
            CHECK(cudaFree(buffers[inputIndex]))                                                CHECK(cudaFree(buffers[outputIndex]));
        11. output label​​​​​​​
        12. float * out_buf = output.get();
        13.     for (int sample_ind = 0; sample_ind < batch_size; sample_ind++)
              {
                  float score{ 0.0f };
                  int label_idx{ 0 };
                  for (unsigned int i = 0; i < num_class; i++)
                  {
                      float cur_score = out_buf[sample_ind*num_class + i];
                      score = std::max(score, cur_score);
                      if (score == cur_score) label_idx = i;
                  }

                  out_label[label_offset + sample_ind] = label_idx;
                  out_score[label_offset + sample_ind] = score;
              }

  12. 注意,编译推理使用的引擎,转换模型的引擎,设备使用的引擎要一致,否则会出现性能上的问题(使用rtx2080和nano出现过这种问题。)

你可能感兴趣的:(Tensorrt 优化加速Tensorflow 生成的模型,详细流程,可以正常使用)