Predictor 类

Paddle Inference 的预测器,由 CreatePredictor 根据 Config 进行创建。用户可以根据Predictor提供的接口设置输入数据、执行模型预测、获取输出等。

注意事项: 一个 Config 对象只能用于调用一次 CreatePredictor 生成一个 Predictor,需要通过 CreatePredictor 创建多个 Predictor 时请分别创建 Config 对象。

获取输入输出

API 定义如下:

// 获取所有输入 paddle_infer::Tensor 的名称
// 参数:None
// 返回:std::vector<std::string> - 所有输入 paddle_infer::Tensor 的名称
std::vector<std::string> GetInputNames();

// 根据名称获取输入 paddle_infer::Tensor 的句柄
// 参数:name - paddle_infer::Tensor 的名称
// 返回:std::unique_ptr<Tensor> - 指向 paddle_infer::Tensor 的指针
std::unique_ptr<paddle_infer::Tensor> GetInputHandle(const std::string& name);

// 获取所有输出 paddle_infer::Tensor 的名称
// 参数:None
// 返回:std::vector<std::string> - 所有输出 paddle_infer::Tensor 的名称
std::vector<std::string> GetOutputNames();

// 根据名称获取输出 paddle_infer::Tensor 的句柄
// 参数:name - paddle_infer::Tensor 的名称
// 返回:std::unique_ptr<Tensor> - 指向 paddle_infer::Tensor 的指针
std::unique_ptr<paddle_infer::Tensor> GetOutputHandle(const std::string& name);

代码示例:

// 构造 Config 对象
paddle_infer::Config config("./resnet.pdmodel", "./resnet.pdiparams");

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 准备输入 paddle_infer::Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape({1, 3, 224, 224});
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output paddle_infer::Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);

运行和生成

API 定义如下:

// 执行模型预测,需要在设置输入数据后调用
// 参数:None
// 返回:bool - 是否执行成功
// 备注:此接口对应于 paddle_infer::Tensor
bool Run();

// 执行模型预测(推荐使用)
// 参数:inputs - 输入数据,对应模型输入的 paddle::Tensor 向量
//      outputs - 输出数据,对应模型输出的 paddle::Tenosr 向量
// 返回:bool - 是否执行成功
// 备注:此接口对应于 paddle::Tensor
bool Run(const std::vector<paddle::Tensor>& inputs, std::vector<paddle::Tensor> * outputs);

// 根据该 Predictor,克隆一个新的 Predictor,两个 Predictor 之间共享权重
// 参数:None
// 返回:std::unique_ptr<Predictor> - 新的 Predictor
std::unique_ptr<Predictor> Clone();

// 释放中间Tensor
// 参数:None
// 返回:None
void ClearIntermediateTensor();

// 释放内存池中的所有临时 Tensor
// 参数:None
// 返回:uint64_t - 释放的内存字节数
uint64_t TryShrinkMemory();

代码示例:

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 准备输入 paddle_infer::Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape({1, 3, 224, 224});
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output paddle_infer::Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_tensor->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 
                              1, std::multiplies<int>());
// 获取 Output 数据
std::vector<float> out_data;
out_data.resize(out_num);
output_tensor->CopyToCpu(out_data.data());

// 释放中间Tensor
predictor->ClearIntermediateTensor();

// 释放内存池中的所有临时 Tensor
predictor->TryShrinkMemory();

获取 OP 中间输出 paddle::Tensor

API 定义如下:

// 获取中间 op 的输出 paddle::Tensor
// 参数:OutputHookFunc    - hook 函数签名为 void(const std::string&, const std::string&, const paddle::Tensor&)
//                              第一个参数是 op type(name)
//                              第二个参数是输出 paddle::Tensor‘s name
//                              第三个参数是输出 paddle::Tensor
// 返回:None
void RegisterOutputHook(const OutputHookFunc& hookfunc);

代码示例:

示例一: 该示例输出所有 tensor 的均值和方差。用户可根据需要单独输出某一特定 op 的输出 tensor 的信息。

(下面是针对跑 fp32 的模型给出的示例。跑混合精度的话,需要做些修改,具体见注释部分)

// 使用 paddle::Tensor 需要包含这个头文件
#include "paddle/extension.h"

void get_output_tensor(const std::string &op_type,
                       const std::string &tensor_name,
                       const paddle::Tensor &tensor) {
  if(tensor.dtype() != paddle::DataType::FLOAT32) return;
  auto cpu_tensor = tensor.copy_to(paddle::CPUPlace{}, true);
  // using TYPE = phi::dtype::float16;
  using TYPE = float;

  std::stringstream ss;

  // op type and tensor name
  ss << std::left << std::setw(20) << op_type << std::setw(40) << tensor_name;

  // tensor shape
  std::string shape_str;
  shape_str += "[" + std::to_string(cpu_tensor.shape()[0]);
  for (size_t i = 1; i < cpu_tensor.shape().size(); i++) {
    shape_str += "," + std::to_string(cpu_tensor.shape()[i]);
  }
  shape_str += "]";
  ss << std::setw(20) << shape_str;

  // tensor data mean and variance
  TYPE sum{0};
  for (size_t i = 0; i < cpu_tensor.numel(); i++) {
    sum += cpu_tensor.data<TYPE>()[i];
  }
  TYPE mean = sum / TYPE(cpu_tensor.numel());
  TYPE accum{0};
  for (size_t i = 0; i < cpu_tensor.numel(); i++) {
    accum += (cpu_tensor.data<TYPE>()[i] - mean) *
             (cpu_tensor.data<TYPE>()[i] - mean);
  }
  TYPE variance = accum / TYPE(cpu_tensor.numel());
  ss << std::setw(20) << mean << std::setw(20) << variance;

  std::cout << ss.str() << std::endl;
}

// 通过该接口注册的 hook 函数,在每个 op run 完都会被执行一次
predictor->RegisterOutputHook(get_output_tensor);

输出结果(op typeoutput tensor nametensor shapemean of tensorvariance of tensor):

image

示例二: 该示例输出每个 op run 前后当前 device 上的显存占用信息。

void get_current_memory(const std::string &op_type,
                        const std::string &tensor_name, const paddle::Tensor &tensor) {
  // parameters tensor_name and tensor are not used
  std::stringstream ss;

  int device_id = 0;
  if (auto p = getenv("CUDA_VISIBLE_DEVICES")) {
    device_id = atoi(p);
  }
  size_t avail;
  size_t total;
  cudaMemGetInfo(&avail, &total);
  ss << std::left << std::setw(20) << op_type << std::setw(5) << device_id
     << std::setw(10) << (total - avail) / 1024.0 / 1024.0 << std::setw(5)
     << 1.0 * (total - avail) / total;
  LOG(INFO) << ss.str();
}

// 通过该接口注册的 hook 函数,在每个 op run 完都会被执行一次
predictor->RegisterOutputHook(get_current_memory);

输出结果(op typedevice idmemory usage(MiB)memory usage(%)):

image

获取 OP 中间输入 paddle::Tensor

API定义如下,示例参考上一节《获取 OP 中间输出 paddle::Tensor》。

// 获取中间 op 的输入 paddle::Tensor
// 参数:OutputHookFunc    - hook 函数签名为 void(const std::string&, const std::string&, const paddle::Tensor&)
//                              第一个参数是 op type(name)
//                              第二个参数是输出 paddle::Tensor‘s name
//                              第三个参数是输出 paddle::Tensor
// 返回:None
void RegisterInputHook(const OutputHookFunc& hookfunc);