CANN Execution Provider

Huawei Compute Architecture for Neural Networks (CANN) ist eine heterogene Computerarchitektur für KI-Szenarien und bietet mehrschichtige Programmierschnittstellen, um Benutzern zu helfen, KI-Anwendungen und -Dienste schnell auf Basis der Ascend-Plattform zu erstellen.

Die Verwendung des CANN Execution Provider für ONNX Runtime kann Ihnen helfen, ONNX-Modelle auf Huawei Ascend-Hardware zu beschleunigen.

Der CANN Execution Provider (EP) für ONNX Runtime wird von Huawei entwickelt.

Inhalt

Installieren

Vorkompilierte Binärdateien von ONNX Runtime mit CANN EP werden veröffentlicht, aber derzeit nur für Python. Bitte beachten Sie onnxruntime-cann.

Voraussetzungen

Bitte beachten Sie die folgende Tabelle für die offiziellen CANN-Paketabhängigkeiten für das ONNX Runtime-Inferenzpaket.

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

Build

Anweisungen zum Erstellen finden Sie auf der BUILD-Seite.

Konfigurationsoptionen

Der CANN Execution Provider unterstützt die folgenden Konfigurationsoptionen.

device_id

Die Geräte-ID.

Standardwert: 0

npu_mem_limit

Die Grössenbeschränkung des Gerätespeicher-Arenas in Bytes. Diese Grössenbeschränkung gilt nur für den Arena des Execution Providers. Die gesamte Gerätespeichernutzung kann höher sein.

arena_extend_strategy

Die Strategie zur Erweiterung des Gerätespeicher-Arenas.

Wert Beschreibung
kNextPowerOfTwo nachfolgende Erweiterungen erfolgen in grösseren Schritten (multipliziert mit Zweierpotenzen)
kSameAsRequested Erweiterung um den angeforderten Betrag

Standardwert: kNextPowerOfTwo

enable_cann_graph

Ob die Graph-Inferenzmaschine zur Beschleunigung der Leistung verwendet werden soll. Die empfohlene Einstellung ist true. Wenn false, wird auf die Single-Operator-Inferenzmaschine zurückgegriffen.

Standardwert: true

dump_graphs

Ob der Subgraph im ONNX-Format für die Analyse der Subgraph-Segmentierung ausgegeben werden soll.

Standardwert: false

dump_om_model

Ob das Offline-Modell für den Ascend AI Processor in eine .om-Datei ausgegeben werden soll.

Standardwert: true

precision_mode

Der Präzisionsmodus des Operators.

Wert Beschreibung
force_fp32/cube_fp16in_fp32out Konvertierung in float32 gemäss Operator-Implementierung
force_fp16 Konvertierung in float16, wenn float16 und float32 beide unterstützt werden
allow_fp32_to_fp16 Konvertierung in float16, wenn float32 nicht unterstützt wird
must_keep_origin_dtype beibehalten wie es ist
allow_mix_precision/allow_mix_precision_fp16 Mix-Präzisionsmodus

Standardwert: force_fp16

op_select_impl_mode

Einige integrierte Operatoren in CANN verfügen über hochpräzise und hochleistungsfähige Implementierungen.

Wert Beschreibung
high_precision Ziel: hohe Präzision
high_performance Ziel: hohe Leistung

Standardwert: high_performance

optypelist_for_implmode

Enumerieren Sie die Liste der Operatoren, die den Modus verwenden, der durch den Parameter op_select_impl_mode angegeben ist.

Die unterstützten Operatoren sind wie folgt:

  • Pooling
  • SoftmaxV2
  • LRN
  • ROIAlign

Standardwert: None

Leistungsoptimierung

IO Binding

Die I/O Binding-Funktion sollte verwendet werden, um Overhead durch Kopien von Ein- und Ausgaben zu vermeiden.

  • 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++ (zukünftig)

Beispiele

Derzeit können Benutzer die C/C++ und Python API auf CANN EP nutzen.

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++

Hinweis: Dieses Beispiel zeigt die Modellinferenz unter Verwendung von resnet50_Opset16.onnx als Beispiel. Sie müssen den model_path sowie die Funktionen input_prepare() und output_postprocess() nach Ihren Bedürfnissen anpassen.

#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;
}

Unterstützte Ops

Folgende Ops werden vom CANN Execution Provider im Single-Operator-Inferenzmodus unterstützt.

Operator Hinweis
ai.onnx:Abs  
ai.onnx:Add  
ai.onnx:AveragePool Nur 2D-Pooling wird unterstützt.
ai.onnx:BatchNormalization  
ai.onnx:Cast  
ai.onnx:Ceil  
ai.onnx:Conv Nur 2D Conv wird unterstützt.
Gewichte und Bias müssen konstant sein.
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 Nur 2D-Pooling wird unterstützt.
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  

Zusätzliche Ressourcen

Zusätzliche Operatorunterstützung und Leistungsoptimierung werden bald hinzugefügt.