4.6.3. 单模型单设备样例

下面样例介绍如何在单个后摩设备上推理一个ResNet50网络模型。样例使用单线程、单个stream(默认stream)、1个batch对模型进行推理。示例展示关键步骤代码,仅供参考,不可以直接拷贝运行。有关TCIM API详情,参看 《TCIM开发者手册》

4.6.3.1. C++ API

使用C++ API推理模型,主要步骤如下:

  1. 调用 tcim::Module::LoadFromFile 加载编译模型 TCIM_PATH/inferences/resnet50/resnet50.hmm

    auto module = tcim::Module::LoadFromFile("resnet50.hmm");
    
  2. 准备模型输入。调用 tcim::Module::GetInputNum 获取输入数据的数量。对每个输入:

    1. 调用 tcim::Module::GetInputName 获取输入名称。

    2. 调用 tcim::Module::GetInputInfo 获取输入信息,并调用 TensorInfo::AsContiguous 创建 TensorInfo 对象,将张量的内存布局更改为连续。

    3. 调用 tcim::Tensor::CreateHostTensor 为输入数据分配CPU内存。

    4. 定义一个 input_map,存入输入名称与输入数据的对应关系。

    std::map<std::string, tcim::Tensor> input_map;
    // Get the total number of inputs
    int input_num = module.GetInputNum();
    std::cout << "Count of Input: " << input_num << std::endl;
    // For each input:
    for (int idx = 0; idx < input_num; idx++) {
      // Get the name of the input
      auto input_name = module.GetInputName(idx);
      // Get input data information
      auto input_info = module.GetInputInfo(input_name).AsContiguous();
      std::cout << "Input[" << input_name << "] " << input_info << std::endl;
      // Allocate memory on host CPU for storing input data
      auto input_tensor = tcim::Tensor::CreateHostTensor(input_info);
      // Create a map between input name and input tensor
      input_map.insert(std::pair<std::string, tcim::Tensor>(input_name, input_tensor));
    }
    
  3. 图像预处理。示例将 snake.png 图片调整为:

    • 图像格式从BGR转为YUV420SP。

    • 图像大小调整为 224 x 224 x 3。

    cv::Mat img_rgb;
    cv::Mat img_yuv;
    // Load the image
    img_rgb = cv::imread("../../data/snake.png");
    // Convert BGR to RGB
    ImageProc::BgrToRgb((int8_t *)(img_rgb.data), img_rgb.rows, img_rgb.cols);
    // Resize the RGB image to (224, 224)
    cv::resize(img_rgb, img_rgb, {224, 224});
    // Convert RGB to YUV420P
    cv::cvtColor(img_rgb, img_yuv, cv::COLOR_RGB2YUV_I420);
    // Calculate the size of the YUV image
    int size = 224*224*3;
    // Convert image format to YUV420SP and store the result in the input_map at "input.1"
    ImageProc::I420To420sp((uint8_t *)input_map.at("input.1").Data(), (uint8_t *)img_yuv.data, size);
    
  4. 准备模型输出。调用 tcim::Module::GetOutputNum 获取输出数据数量。对每个输出:

    1. 调用 tcim::Module::GetOutputName 获取输出名称。

    2. 调用 tcim::Module::GetOutputInfo 获取输出信息,并调用 TensorInfo::AsContiguous 创建 TensorInfo 对象,将张量的内存布局更改为连续。

    3. 调用 tcim::Tensor::CreateHostTensor 为输出数据分配CPU内存。

    4. 定义 output_map,插入输出名称与输出数据的对应关系。

    // Create a map to store output data
    std::map<std::string, tcim::Tensor> output_map;
    // Get total number of outputs
    int output_num = module.GetOutputNum();
    std::cout << "Count of Output: " << output_num << std::endl;
    //For each output:
    for (int idx = 0; idx < output_num; idx++) {
      // Get the name of the output
      auto output_name = module.GetOutputName(idx);
      // Get the information of the output
      auto output_info = module.GetOutputInfo(output_name).AsContiguous();
      std::cout << "Output[" << output_name << "] " << output_info << std::endl;
      // Allocate memory on host CPU for storing output data
      auto output_tensor = tcim::Tensor::CreateHostTensor(output_info);
      // Insert the output name and tensor into the output map
      output_map.insert(std::pair<std::string, tcim::Tensor>(output_name, output_tensor));
    }
    
  5. 调用 tcim::Module::SetInput 设置输入。

    // Loop through each key-value pair in the input_map
    for (const auto& input : input_map) {
      // Set each input with the key-value pair from the input_map
      module.SetInput(input.first, input.second);
    }
    
  6. 分别调用 tcim::Module::Runtcim::Module::Sync 推理和同步模型。

    module.Run();
    module.Sync();
    
  7. 调用 tcim::Module::GetOutput 获取推理输出数据。

    // Loop through each key-value pair in the output_map
    for (auto& output : output_map) {
      // Get each output with the key-value pair from the output_map
      module.GetOutput(output.first, output.second);
    }
    
  8. 预测结果展示,包括conf(置信度)和label(目标类别)等。

    // Initialize top1 to store the index of the top-ranked result
    int top1 = 0;
    // Iterate through each output in the output_map
    for (auto& output : output_map) {
      // Create a vector to store pairs of confidence scores and their corresponding indices
      std::vector<std::pair<float, int>> sort_pairs;
      // Set sort_pairs with confidence scores and the corresponding indices
      for (int i = 0; i < 1000; ++i) {
        // Extract confidence score at index i and pair it with its index
        sort_pairs.emplace_back(static_cast<float*>(output.second.Data())[i], i);
      }
      // Sort sort_pairs in descending order based on confidence scores
      std::sort(sort_pairs.begin(), sort_pairs.end(),
                [](const std::pair<float, int>& a, const std::pair<float, int>& b) {
                return a.first > b.first;
                });
      // Specify the topk (top k) results to display
      const int topk = 5;
      // Return the topk results with their index, confidence score, and corresponding label
      for (int i = 0; i < topk; ++i) {
        std::cout << "top" << (i + 1) << ": Index="
                  << sort_pairs[i].second << " Conf=" << sort_pairs[i].first
                  << ", Label=[" << Imagenet::GetLabel(sort_pairs[i].second) << "]" << std::endl;
      }
      // Check result, modify it when you change model or data
      if (sort_pairs[0].second != 65) {
        std::cout << "top1 != 65" << std::endl;
        exit(-1);
      }
    }
    

代码详情,参看 TCIM_PATH/inferences/resnet50/resnet50.cc

4.6.3.2. Python API

使用Python API推理模型,主要步骤如下:

注意

引入外部库时,必须先引入PyTorch库(import torch)再引入TCIM(import tcim_lite as tcim),否则会导致报错。

  1. 调用 load 加载已编译的模型文件 resnet50.hmm

    module = tcim.runtime.load("resnet50.hmm")
    
  2. 模型预处理。示例将 snake.png 图像调整为:

    • 图像格式从BGR转为YUV420SP。

    • 图像大小调整为 224 x 224 x 3。

    # Load the image
    input_data = cv2.imread("../../data/snake.png")
    # Resize the image to (224, 224)
    input_data = cv2.resize(input_data, (224, 224))
    # Transpose the image dimensions to channel first
    input_data = np.transpose(input_data, (2, 0, 1))
    # Add a batch dimension
    input_data = np.expand_dims(input_data, axis=0)
    # Convert to torch tensor and ensure data type is float32
    input_data = torch.tensor(input_data.astype(np.float32))
    # Remove the batch dimension added earlier
    input_data = torch.squeeze(input_data, 0)
    # Conver image format from BGR to YUV420
    from transform import BGR2YUV
    rgb2yuv_func = BGR2YUV(fmt='YUV420')
    # Apply the RGB to YUV transformation and convert back to numpy array
    input_data = torch.unsqueeze(rgb2yuv_func(input_data), 0).numpy()
    # Convert data type to uint8
    input_data = input_data.astype(np.uint8)
    
  3. 准备模型输入并设置输入数据。调用 get_num_inputs 获取输入数据的数量。对每个输入:

    1. 调用 get_input_name 获取输入名称。

    2. 调用 get_input_info 获取输入信息。

    3. 调用 set_input 设置输入。

    # Get the total number of inputs
    input_num = module.get_num_inputs()
    # For each input
    for id in range(0, input_num):
        # Get the input name
        input_name = module.get_input_name(id)
        # Get the information about input data
        input_info = module.get_input_info(input_name).ascontiguous()
        print("input[{}] shape = {}, dtype = {}, format = {}".format(input_name, input_info.shape, input_info.dtype, input_info.format.name))
        # Set input data to the module with the given input name
        module.set_input(input_name, input_data)
    
  4. 分别调用 runsync 推理和同步模型。

    module.run()
    module.sync()
    
  5. 准备模型输出,并获取输出数据。调用 get_num_outputs 获取输出数据的数量。对每个输出:

    1. 调用 get_output_name 获取输出名称。

    2. 调用 get_output_info 获取输出信息。

    3. 调用 get_output 获取输出数据。

    4. 调用 astype 将输出数据转为float32类型tensor。

    5. 调用 numpy 获取反量化后tensor数据,并设置为输出数据。

    result_check = True
    # Get the total number of outputs
    output_num = module.get_num_outputs()
    # For each output:
    for id in range(0, output_num):
        # Get the output name
        output_name = module.get_output_name(id)
        # Get the information about output data
        output_info = module.get_output_info(output_name).ascontiguous().astype(np.float32)
        print("output[{}] shape = {}, dtype = {}, format = {}".format(output_name, output_info.shape, output_info.dtype,
                                                                    output_info.format.name))
        # Get the output data
        output_data = module.get_output(output_name).astype(np.float32).numpy()
    
  6. 预测结果展示,包括prob(置信度)和cls(目标类别ID)等。

    from postprocess import softmax
    # Convert output data into probabilities
    output_data = softmax(output_data)
    topk = 5
    # Sort the output data in descending order and select the top topk predictions
    pred_list = np.argsort(-output_data, axis=1, kind="quicksort").flatten()[0:topk]
    prob_list = output_data.flatten()
    # Iterate through the top predictions
    for i, id in enumerate(pred_list):
        print("top{}: predict cls = {}, prob = {:.6f}".format(i+1, id, prob_list[id]))
    
    # Check result, modify it when you change model or data
    assert(pred_list[0] == 65)
    

代码详情参看 TCIM_PATH/inferences/resnet50/resnet50.py