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 |
額外資源
更多運算子支援和效能調優將很快新增。