---
title: 使用接口全新构建Graph
description: "GE的C++图引擎接口提供了两种构图方式："
url: https://www.hiascend.com/document/detail/zh/canncommercial/latest/programug/graphdevg/atlasag_25_0077.html
sourcePath: /source/zh/canncommercial/900/programug/graphdevg/atlasag_25_0077.html
indexId: 0cb5ef404270e1411aaa0037b2fdf260927d2895ef181d30e27b2bca109767ad70
---
# 使用接口全新构建Graph

GE的C++图引擎接口提供了两种构图方式：

- 使用图引擎接口全新构建Graph：用户通过图引擎接口，逐个将算子添加到计算图中，构建成Ascend IR表示的计算图。
- 使用Parser接口将原始模型解析为Graph：用户通过Parser接口，将框架模型文件（如*.onnx和*.pb等格式）解析成Ascend IR表示的计算图。

本章节综合上述两种场景，为您介绍如何构建一个Graph，构建完Graph后，可以根据自身需要修改Graph，然后将该Graph编译为适配AI处理器的离线模型。整体流程如下：


流程说明如下：

1. 进行构建Graph操作之前，先进行环境搭建，安装相应的CANN软件包，并进行网络结构分析；如果有不支持的算子，则开发对应算子，并部署到硬件环境。

  a. 如果使用图引擎接口全新构建Graph，需要根据原始网络，明确如下信息：

    1. 网络中包含哪些算子，以及这些算子的输入、输出、属性等信息。
    2. 网络中算子之间的关联关系。
    3. 确认原始网络中的算子在AI处理器是否支持，当前支持的算子请参见“Ascend IR算子规格说明(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/aolapi/operatorlist_00093.html)”章节。如果不支持或不满足实际需要，开发者可以参见如下方式自定义算子：
      参见《Ascend C算子开发(https://www.hiascend.com/document/detail/zh/canncommercial/900/programug/Ascendcopdevg/atlas_ascendc_map_10_0002.html)》自定义Ascend C算子，并将算子部署到硬件环境。

  b. 如果使用Parser接口将原始模型解析为Graph，需要确认原始网络中的算子在AI处理器是否支持，当前支持的算子请参见“Ascend IR算子规格说明(https://www.hiascend.comdocument/detail/zh/canncommercial/900/API/aolapi/operatorlist_00093.html)”章节。如果不支持或不满足实际需要，可参见如下方式自定义算子：
    参见《Ascend C算子开发(https://www.hiascend.com/document/detail/zh/canncommercial/900/programug/Ascendcopdevg/atlas_ascendc_map_10_0002.html)》自定义Ascend C算子，并将算子部署到硬件环境。

2. 构建Graph，开发者可以使用图引擎接口全新构建Graph，也可以使用Parser接口将原始模型解析为Graph。
3. 修改Graph，如果开发者想要优化Graph结构，则可以基于构建好的Graph直接将Graph修改为期望的结构。
4. 编译和运行Graph，开发者可以将修改后的Graph编译成适配AI处理器的离线模型，然后通过模型加载接口加载后上板推理；也可以直接运行Graph，得到Graph的运行结果。

#### 获取样例

单击Link(https://gitee.com/ascend/samples/tree/master/cplusplus/level1_single_api/3_ir/IRBuild)获取样例代码，目录结构如下：

```
├── src
│   ├──main.cpp                // 实现文件
├── Makefile                    // 编译脚本
├── CMakeLists.txt              // 编译脚本
├── data
│   ├──data_generate.py        // 使用图开发接口全新构建Graph时，用于生成Graph所需要的数据信息，例如权重、偏置等数据
│   ├──tensorflow_generate.py  // 使用Parser接口将TensorFlow原始模型解析为Graph时，用于生成.pb格式的TensorFlow模型
│   ├──caffe_generate.py       // 使用Parser接口将Caffe原始模型解析为Graph时，用于生成.pbtxt格式的Caffe模型与.caffemodel格式的权重文件
├── scripts
│   ├──host_version.conf       // version配置
│   ├──testcase_300.sh         // 测试脚本
```

该样例主要包括如下功能：

1. 构建Graph。

  - 使用图引擎接口全新构建Graph
    本样例构造的Graph结构如下：

  - 使用Parser接口将原始模型解析为Graph，此处以TensorFlow为例进行介绍。
    本样例给出的TensorFlow原始模型结构为：

    解析后的Graph结构为：

2. 修改Graph：此处以使用Parser接口将TensorFlow模型解析得到的Graph为例进行说明。
  修改后的Graph结构如下，在Add算子和Const算子之间插入Abs算子：

3. 编译Graph：将上述最终的Graph编译为离线模型。


#### 编译运行

1. 准备环境。

  - 参见    环境准备
搭建环境，并配置环境变量。
  - 如果将TensorFlow框架原始模型解析为Graph，需要搭建TensorFlow1.15.0环境，如下为root用户在x86架构安装示例：

```
pip3 install tensorflow-cpu==1.15
```


    如果为非root用户安装，需要在上述命令末尾增加--user参数；如果为aarch64架构，安装方法请参见《TensorFlow 1.15模型迁移(https://www.hiascend.com/document/detail/zh/TensorFlowCommercial/900/migration/tfmigr1/tfmigr1_000001.html)》>环境准备 > 安装开源框架TensorFlow 1.15。

2. 准备构图数据。

  - 使用图引擎接口全新构建Graph。
    在data目录执行数据生成脚本，命令如下：

```
python3 data_generate.py
```


    执行完成后，在data目录下生成.bin格式的数据，后续模型构建时会从该文件读取权重、偏置等数据。

  - 使用Parser接口将原始模型解析为Graph。
    在data目录执行原始模型生成脚本，命令如下：

```
python3 tensorflow_generate.py
```


    执行完成后，在data目录下生成.pb格式的模型文件，后续将原始模型解析时会使用该模型，名称为tf_test.pb。

3. 编译代码。

  a. （可选）打开Makefile文件，修改如下ASCEND_PATH路径，修改为CANN软件安装目录/cann，例如root用户修改为：/usr/local/Ascend/cann。

```
ifeq
(${ASCEND_INSTALL_PATH},)
ASCEND_PATH
:= /usr/local/Ascend/cann
```


  b. 执行编译。
    在Makefile文件同级目录执行如下命令进行编译：

```
make clean && make ir_build
```


    编译完成后，在当前路径out文件夹下生成可执行文件ir_build。

4. 运行代码。

  - 使用图引擎接口全新构建Graph。
    在out文件夹下执行如下命令：

```
./ir_build
<soc_version>
gen
```


    若提示如下信息，则说明运行成功，在同级目录生成*.om离线模型文件。

    1 2 3 ========== Generate Graph1 Success!========== Build Model1 SUCCESS! Save Offline Model1 SUCCESS!

  - 使用Parser接口将原始模型解析为Graph。
    在out文件夹下执行如下命令：

```
./ir_build
<soc_version>
tf
```


    若提示如下信息，则说明运行成功，在同级目录生成*.om离线模型文件。

    1 2 3 4 5 6 7 8 ========== Generate graph from tensorflow origin model success.========== Modify Graph Start. Find src node: const. Find dst node: add. Modify Graph Success. ========== Modify tensorflow origin graph success.========== Build Model1 SUCCESS! Save Offline Model1 SUCCESS!

<soc_version>取值查询方法如下：- 针对如下产品：在安装AI处理器的服务器执行npu-smi info命令进行查询，获取Name信息。实际配置值为AscendName，例如Name取值为xxxyy，实际配置值为Ascendxxxyy。
  Atlas A2 训练系列产品 / Atlas A2 推理系列产品

  Atlas 200I/500 A2 推理产品

  Atlas 推理系列产品

  Atlas 训练系列产品

- 针对Atlas A3 训练系列产品 / Atlas A3 推理系列产品，在安装AI处理器的服务器执行npu-smi info -t board -iid-cchip_id命令进行查询，获取Chip Name和NPU Name信息，实际配置值为Chip Name_NPU Name。例如Chip Name取值为Ascendxxx，NPU Name取值为1234，实际配置值为Ascendxxx_1234。其中：

  - id：设备id，通过npu-smi info -l命令查出的NPU ID即为设备id。
  - chip_id：芯片id，通过npu-smi info -m命令查出的Chip ID即为芯片id。
- 针对Atlas 350 加速卡，在安装AI处理器的服务器执行npu-smi info -t board -iid命令进行查询，获取Chip Name和NPU Name信息，实际配置值为Chip Name_NPU Name。例如Chip Name取值为Ascendxxx，NPU Name取值为1234，实际配置值为Ascendxxx_1234。
  其中，id为设备id，通过npu-smi info -l命令查出的NPU ID即为设备id。


5. 后续操作。
  转换成*.om离线模型后，可以调用模型加载接口加载转换好的*.om离线模型，再调用模型执行接口进行推理。详细的模型加载与推理的方法可参见《应用开发 (C&C++)(https://www.hiascend.com/document/detail/zh/canncommercial/900/programug/acldevg/aclcppdevg_000006.html)》中的“模型推理(https://www.hiascend.comdocument/detail/zh/canncommercial/900/programug/acldevg/aclcppdevg_000025.html)”。


#### 代码解析

体验快速入门样例的编译运行后，若还想了解代码的实现逻辑，可查看下文的代码解析。此处按各函数的功能来介绍代码逻辑，为了便于阅读、理解代码，将各函数相关的变量与函数放在一起介绍。

打开“src/main.cpp”文件，先从main函数开始，了解整个样例的代码串接逻辑，再分别了解各自定义函数的实现。

1. main函数用来串联整个构建Graph的代码逻辑。
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int main(int argc, char* argv[]) { // 1.构建Graph Graph graph1("IrGraph1"); // 1.1使用图引擎接口全新构建Graph ret = GenGraph(graph1); // 1.2使用Parser接口将原始模型解析为Graph std::string tfPath = "../data/tf_test.pb"; std::map<AscendString, AscendString> parser_options; tfStatus = ge::aclgrphParseTensorFlow(tfPath.c_str(), parser_options, graph1); // 2.修改Graph：此处以从TensorFlow模型解析得到的Graph为例进行修改 ModifyGraph(graph1); // 3.系统初始化，并申请资源 std::map<AscendString, AscendString> global_options = { {AscendString(ge::ir_option::SOC_VERSION), AscendString(argv[kSocVersion])} , }; auto status = aclgrphBuildInitialize(global_options); // 4.编译Graph ModelBufferData model1; std::map<AscendString, AscendString> options; PrepareOptions(options); status = aclgrphBuildModel(graph1, options, model1); // 5.将内存缓冲区中的模型保存为离线模型文件 status = aclgrphSaveModel("ir_build_sample1", model1); // 6.释放资源 aclgrphBuildFinalize(); }

2. include依赖的头文件。
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <string.h> #include "tensorflow_parser.h" #include "graph.h" #include "types.h" #include "tensor.h" #include "attr_value.h" #include "ge_error_codes.h" #include "ge_api_types.h" #include "ge_ir_build.h" #include "all_ops.h" ... ... using namespace std; using namespace ge; using ge::Operator;

3. 构建Graph。
  使用算子原型衍生接口定义算子，并连接算子。详细代码示例为： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 bool GenGraph(Graph& graph) { auto shape_data = vector<int64_t>({ 1,1,28,28 }); TensorDesc desc_data(ge::Shape(shape_data), FORMAT_ND, DT_FLOAT16); // 创建Data算子实例 auto data = op::Data("data"); data.update_input_desc_x(desc_data); data.update_output_desc_y(desc_data); // 创建Add算子实例，并与Data算子连接 auto add = op::Add("add") .set_input_x1(data) .set_input_x2(data); // 创建AscendQuant算子实例，并与Data算子连接 auto quant = op::AscendQuant("quant") .set_input_x(data) .set_attr_scale(1.0) .set_attr_offset(0.0); auto conv_weight = op::Const("Conv2D/weight") .set_attr_value(weight_tensor); // 创建Conv2D算子实例，并与AscendQuant算子连接 auto conv2d = op::Conv2D("Conv2d1") .set_input_x(quant) .set_input_filter(conv_weight) .set_attr_strides({ 1, 1, 1, 1 }) .set_attr_pads({ 0, 1, 0, 1 }) .set_attr_dilations({ 1, 1, 1, 1 }); ... ... auto dequant_scale = op::Const("dequant_scale") .set_attr_value(dequant_tensor); // 创建AscendDequant算子实例，并与Conv2D算子连接 auto dequant = op::AscendDequant("dequant") .set_input_x(conv2d) .set_input_deq_scale(dequant_scale); ... ... auto bias_weight_1 = op::Const("Bias/weight_1") .set_attr_value(weight_bias_add_tensor_1); // 创建BiasAdd算子实例，并与AscendDequant算子连接 auto bias_add_1 = op::BiasAdd("bias_add_1") .set_input_x(dequant) .set_input_bias(bias_weight_1) .set_attr_data_format("NCHW"); ... ... auto dynamic_const = op::Const("dynamic_const").set_attr_value(dynamic_const_tensor); // 创建Reshape算子实例，并与BiasAdd算子连接 auto reshape = op::Reshape("Reshape") .set_input_x(bias_add_1) .set_input_shape(dynamic_const); ... ... auto matmul_weight_1 = op::Const("dense/kernel") .set_attr_value(matmul_weight_tensor_1); // 创建MatMul算子实例，并与Reshape算子连接 auto matmul_1 = op::MatMul("MatMul_1") .set_input_x1(reshape) .set_input_x2(matmul_weight_1); ... ... auto bias_add_const_1 = op::Const("dense/bias") .set_attr_value(bias_add_const_tensor_1); // 创建BiasAdd算子实例，并与MatMul算子连接 auto bias_add_2 = op::BiasAdd("bias_add_2") .set_input_x(matmul_1) .set_input_bias(bias_add_const_1) .set_attr_data_format("NCHW"); // 创建Relu6算子实例，并与BiasAdd算子连接 auto relu6 = op::Relu6("relu6") .set_input_x(bias_add_2); ... ... auto matmul_weight_2 = op::Const("OutputLayer/kernel") .set_attr_value(matmul_weight_tensor_2); // 创建MatMul算子实例，并与Relu6算子连接 auto matmul_2 = op::MatMul("MatMul_2") .set_input_x1(relu6) .set_input_x2(matmul_weight_2); ... ... auto bias_add_const_3 = op::Const("OutputLayer/bias") .set_attr_value(bias_add_const_tensor_3); // 创建BiasAdd算子实例，并与MatMul算子连接 auto bias_add_3 = op::BiasAdd("bias_add_3") .set_input_x_by_name(matmul_2, "y") .set_input_bias_by_name(bias_add_const_3, "y") .set_attr_data_format("NCHW"); // 创建SoftmaxV2算子实例，并与BiasAdd算子连接 auto softmax = op::SoftmaxV2("Softmax") .set_input_x_by_name(bias_add_3, "y"); std::vector<Operator> inputs{ data }; std::vector<Operator> outputs{ softmax, add }; std::vector<std::pair<ge::Operator, std::string>> outputs_with_name = {{softmax, "y"}}; // 调用接口，设置Graph的输入输出算子 graph.SetInputs(inputs).SetOutputs(outputs); return true; }

4. 修改Graph：此处以从TensorFlow模型解析得到的Graph为例进行修改。
  在Add算子和Const算子之间插入Abs算子： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 bool ModifyGraph(Graph &graph) { const std::string CONST = "const1"; const std::string ADD = "input3_add"; GNode src_node; GNode dst_node; // 获取图中的所有节点 std::vector<GNode> nodes = graph.GetAllNodes(); graphStatus ret = GRAPH_FAILED; for (auto &node : nodes) { ge::AscendString name; // 获取图中算子的名称 ret = node.GetName(name); std::string node_name(name.GetString()); } // 删除图中的指定连接边 ret = graph.RemoveEdge(src_node, 0, dst_node, 1); auto abs = op::Abs("input3_abs"); // 新增abs节点到图上 GNode node_abs = graph.AddNodeByOp(abs); //新增数据边，连接新增的节点 ret = graph.AddDataEdge(src_node, 0, node_abs, 0); ret = graph.AddDataEdge(node_abs, 0, dst_node, 1); return true; }

5. 编译Graph。
  构建和修改完Graph之后，调用接口进行Graph编译操作，接口调用流程如下图所示，详细流程请参见main()函数。
