CANN 執行提供程式

華為昇騰計算架構(CANN)是面向 AI 場景的異構計算架構,提供多層程式設計介面,幫助使用者在 Ascend 平臺上快速構建 AI 應用和服務。

使用 ONNX Runtime 的 CANN 執行提供程式可以幫助您在華為昇騰硬體上加速 ONNX 模型。

ONNX Runtime 的 CANN 執行提供程式(EP)由華為開發。

目錄

安裝

包含 CANN EP 的 ONNX Runtime 預編譯二進位制檔案已釋出,但目前僅限 Python,請參考 onnxruntime-cann

要求

請參考下表瞭解 ONNX Runtime 推理包的官方 CANN 軟體包依賴項。

ONNX Runtime CANN
v1.18.0 8.0.0
v1.19.0 8.0.0
v1.20.0 8.0.0

構建

有關構建說明,請參閱構建頁面

配置選項

CANN 執行提供程式支援以下配置選項。

device_id

裝置 ID。

預設值:0

npu_mem_limit

裝置記憶體區域的大小限制(以位元組為單位)。此大小限制僅適用於執行提供程式的區域。總裝置記憶體使用量可能更高。

arena_extend_strategy

擴充套件裝置記憶體區域的策略。

描述
kNextPowerOfTwo 後續擴充套件以更大的量(乘以 2 的冪)進行擴充套件
kSameAsRequested 按請求量擴充套件

預設值:kNextPowerOfTwo

enable_cann_graph

是否使用圖推理引擎來加速效能。建議設定為 true。如果為 false,則將回退到單運算子推理引擎。

預設值:true

dump_graphs

是否將子圖轉儲為 ONNX 格式,以便分析子圖分割。

預設值:false

dump_om_model

是否將昇騰 AI 處理器離線模型轉儲為 .om 檔案。

預設值:true

precision_mode

運算子的精度模式。

描述
force_fp32/cube_fp16in_fp32out 首先根據運算子實現轉換為 float32
force_fp16 當 float16 和 float32 都受支援時轉換為 float16
allow_fp32_to_fp16 當 float32 不受支援時轉換為 float16
must_keep_origin_dtype 保持原樣
allow_mix_precision/allow_mix_precision_fp16 混合精度模式

預設值:force_fp16

op_select_impl_mode

CANN 中的一些內建運算子具有高精度和高效能實現。

描述
high_precision 旨在實現高精度
high_performance 旨在實現高效能

預設值:high_performance

optypelist_for_implmode

列舉使用 op_select_impl_mode 引數指定的模式的運算子列表。

支援的運算子如下

  • Pooling
  • SoftmaxV2
  • LRN
  • ROIAlign

預設值:無

效能調優

IO 繫結

應利用I/O 繫結功能,以避免輸入和輸出複製造成的開銷。

  • Python
import numpy as np
import onnxruntime as ort

providers = [
    (
        "CANNExecutionProvider",
        {
            "device_id": 0,
            "arena_extend_strategy": "kNextPowerOfTwo",
            "npu_mem_limit": 2 * 1024 * 1024 * 1024,
            "enable_cann_graph": True,
        },
    ),
    "CPUExecutionProvider",
]

model_path = '<path to model>'

options = ort.SessionOptions()
options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL
options.execution_mode = ort.ExecutionMode.ORT_PARALLEL

session = ort.InferenceSession(model_path, sess_options=options, providers=providers)

x = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], dtype=np.int64)
x_ortvalue = ort.OrtValue.ortvalue_from_numpy(x, "cann", 0)

io_binding = sess.io_binding()
io_binding.bind_ortvalue_input(name="input", ortvalue=x_ortvalue)
io_binding.bind_output("output", "cann")

sess.run_with_iobinding(io_binding)

return io_binding.get_outputs()[0].numpy()
  • C/C++(未來)

示例

目前,使用者可以在 CANN EP 上使用 C/C++ 和 Python API。

Python

import onnxruntime as ort

model_path = '<path to model>'

options = ort.SessionOptions()

providers = [
    (
        "CANNExecutionProvider",
        {
            "device_id": 0,
            "arena_extend_strategy": "kNextPowerOfTwo",
            "npu_mem_limit": 2 * 1024 * 1024 * 1024,
            "op_select_impl_mode": "high_performance",
            "optypelist_for_implmode": "Gelu",
            "enable_cann_graph": True
        },
    ),
    "CPUExecutionProvider",
]

session = ort.InferenceSession(model_path, sess_options=options, providers=providers)

C/C++

注意:此示例以 resnet50_Opset16.onnx 為例展示了模型推理。您需要根據自己的需要修改 model_path 以及 input_prepare() 和 output_postprocess() 函式。

#include <iostream>
#include <vector>

#include "onnxruntime_cxx_api.h"

// path of model, Change to user's own model path
const char* model_path = "./onnx/resnet50_Opset16.onnx";

/**
 * @brief Input data preparation provided by user.
 *
 * @param num_input_nodes The number of model input nodes.
 * @return  A collection of input data.
 */
std::vector<std::vector<float>> input_prepare(size_t num_input_nodes) {
  std::vector<std::vector<float>> input_datas;
  input_datas.reserve(num_input_nodes);

  constexpr size_t input_data_size = 3 * 224 * 224;
  std::vector<float> input_data(input_data_size);
  // initialize input data with values in [0.0, 1.0]
  for (unsigned int i = 0; i < input_data_size; i++)
    input_data[i] = (float)i / (input_data_size + 1);
  input_datas.push_back(input_data);

  return input_datas;
}

/**
 * @brief Model output data processing logic(For User updates).
 *
 * @param output_tensors The results of the model output.
 */
void output_postprocess(std::vector<Ort::Value>& output_tensors) {
  auto floatarr = output_tensors.front().GetTensorMutableData<float>();

  for (int i = 0; i < 5; i++) {
    std::cout << "Score for class [" << i << "] =  " << floatarr[i] << '\n';
  }
  
  std::cout << "Done!" << std::endl;
}

/**
 * @brief The main functions for model inference.
 *
 *  The complete model inference process, which generally does not need to be
 * changed here
 */
void inference() {
  const auto& api = Ort::GetApi();
  Ort::Env env(ORT_LOGGING_LEVEL_WARNING);

  // Enable cann graph in cann provider option.
  OrtCANNProviderOptions* cann_options = nullptr;
  api.CreateCANNProviderOptions(&cann_options);

  // Configurations of EP
  std::vector<const char*> keys{
      "device_id",
      "npu_mem_limit",
      "arena_extend_strategy",
      "enable_cann_graph"};
  std::vector<const char*> values{"0", "4294967296", "kNextPowerOfTwo", "1"};
  api.UpdateCANNProviderOptions(
      cann_options, keys.data(), values.data(), keys.size());

  // Convert to general session options
  Ort::SessionOptions session_options;
  api.SessionOptionsAppendExecutionProvider_CANN(
      static_cast<OrtSessionOptions*>(session_options), cann_options);

  Ort::Session session(env, model_path, session_options);

  Ort::AllocatorWithDefaultOptions allocator;

  // Input Process
  const size_t num_input_nodes = session.GetInputCount();
  std::vector<const char*> input_node_names;
  std::vector<Ort::AllocatedStringPtr> input_names_ptr;
  input_node_names.reserve(num_input_nodes);
  input_names_ptr.reserve(num_input_nodes);
  std::vector<std::vector<int64_t>> input_node_shapes;
  std::cout << num_input_nodes << std::endl;
  for (size_t i = 0; i < num_input_nodes; i++) {
    auto input_name = session.GetInputNameAllocated(i, allocator);
    input_node_names.push_back(input_name.get());
    input_names_ptr.push_back(std::move(input_name));
    auto type_info = session.GetInputTypeInfo(i);
    auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
    input_node_shapes.push_back(tensor_info.GetShape());
  }

  // Output Process
  const size_t num_output_nodes = session.GetOutputCount();
  std::vector<const char*> output_node_names;
  std::vector<Ort::AllocatedStringPtr> output_names_ptr;
  output_names_ptr.reserve(num_input_nodes);
  output_node_names.reserve(num_output_nodes);
  for (size_t i = 0; i < num_output_nodes; i++) {
    auto output_name = session.GetOutputNameAllocated(i, allocator);
    output_node_names.push_back(output_name.get());
    output_names_ptr.push_back(std::move(output_name));
  }

  //  User need to generate input date according to real situation.
  std::vector<std::vector<float>> input_datas = input_prepare(num_input_nodes);

  auto memory_info = Ort::MemoryInfo::CreateCpu(
      OrtAllocatorType::OrtArenaAllocator, OrtMemTypeDefault);

  std::vector<Ort::Value> input_tensors;
  input_tensors.reserve(num_input_nodes);
  for (size_t i = 0; i < input_node_shapes.size(); i++) {
    auto input_tensor = Ort::Value::CreateTensor<float>(
        memory_info,
        input_datas[i].data(),
        input_datas[i].size(),
        input_node_shapes[i].data(),
        input_node_shapes[i].size());
    input_tensors.push_back(std::move(input_tensor));
  }

  auto output_tensors = session.Run(
      Ort::RunOptions{nullptr},
      input_node_names.data(),
      input_tensors.data(),
      num_input_nodes,
      output_node_names.data(),
      output_node_names.size());

  // Processing of out_tensor
  output_postprocess(output_tensors);
}

int main(int argc, char* argv[]) {
  inference();
  return 0;
}

支援的運算子

CANN 執行提供程式在單運算子推理模式下支援以下運算子。

運算子 說明
ai.onnx:Abs  
ai.onnx:Add  
ai.onnx:AveragePool 僅支援 2D 池化。
ai.onnx:BatchNormalization  
ai.onnx:Cast  
ai.onnx:Ceil  
ai.onnx:Conv 僅支援 2D 卷積。
權重和偏置應為常數。
ai.onnx:Cos  
ai.onnx:Div  
ai.onnx:Dropout  
ai.onnx:Exp  
ai.onnx:Erf  
ai.onnx:Flatten  
ai.onnx:Floor  
ai.onnx:Gemm  
ai.onnx:GlobalAveragePool  
ai.onnx:GlobalMaxPool  
ai.onnx:Identity  
ai.onnx:Log  
ai.onnx:MatMul  
ai.onnx:MaxPool 僅支援 2D 池化。
ai.onnx:Mul  
ai.onnx:Neg  
ai.onnx:Reciprocal  
ai.onnx:Relu  
ai.onnx:Reshape  
ai.onnx:Round  
ai.onnx:Sin  
ai.onnx:Sqrt  
ai.onnx:Sub  
ai.onnx:Transpose  

額外資源

更多運算子支援和效能調優將很快新增。