ops-nn 中的自定义算子开发全流程(含注册与测试)

ops-nn 中的自定义算子开发全流程(含注册与测试)
在这里插入图片描述


文章目录

一、引言

尽管 ops-nn 已覆盖绝大多数神经网络基础算子,但在前沿研究或特定业务场景中,开发者常需实现 自定义算子(Custom Operator)。例如:

  • 新型注意力机制(如 FlashAttention 的变种)
  • 领域专用层(如医学图像的各向异性卷积)
  • 性能优化融合算子(如 Conv-BN-ReLU 三合一)

华为 CANN 为 ops-nn 提供了完整的 自定义算子开发框架,允许用户用 C++ 编写高性能内核,并通过 Python 接口调用。本文将手把手演示从 算子设计 → C++ 实现 → 注册 → 编译 → Python 调用 → 性能测试 的完整流程。

无论你是想贡献到 CANN 开源社区,还是在企业内部扩展模型能力,本文都将为你提供可落地的实践指南。


二、技术背景

2.1 自定义算子的两种模式

CANN 支持两种自定义算子开发方式:

类型描述适用场景
TBE(Tensor Boost Engine)基于 DSL 的算子开发(类似 CUDA)简单算子,快速原型
AICPU / AI Core C++直接编写 C++ 内核复杂逻辑、高性能需求

本文聚焦 AI Core C++ 模式,因为它能直接集成到 ops-nn 库中,复用其内存管理、调度机制。

2.2 ops-nn 的算子接口规范

每个算子需继承 OpKernel 并实现 Compute 方法:

classMyCustomOp:publicOpKernel{public: Status Compute(const OpKernelContext* ctx)override;};

同时需提供:

  • 算子定义(输入/输出/属性)
  • 注册宏(绑定名称)
  • 反向传播支持(可选)

三、开发流程详解

3.1 步骤概览

  1. 设计算子语义(输入、输出、参数)
  2. 编写 C++ 内核
  3. 注册算子到 ops-nn
  4. 编译生成动态库
  5. 编写 Python 封装
  6. 测试与性能分析

四、实战代码演示

我们将实现一个 Swish 激活函数f(x) = x * sigmoid(x)),该算子在 ops-nn 中尚未原生支持(截至 CANN 7.0)。

4.1 步骤 1:创建项目目录

mkdir -p custom_swish/{src,build}cd custom_swish 

4.2 步骤 2:编写 C++ 内核(src/swish_op.cc

// src/swish_op.cc#include"register/op_registry.h"#include"utils/math_utils.h"#include"common/types.h"namespace ge {classSwishOp:publicOpKernel{public: Status Compute(const OpKernelContext* ctx)override{// 获取输入 tensorconst Tensor* input = ctx->Input(0); Tensor* output = ctx->Output(0, input->shape());// 获取数据指针(假设为 float)auto input_data = input->data<float>();auto output_data = output->data<float>(); size_t elem_count = input->NumElements();// 执行 Swish: y = x * sigmoid(x)for(size_t i =0; i < elem_count;++i){float s =1.0f/(1.0f+expf(-input_data[i]));// sigmoid output_data[i]= input_data[i]* s;}return SUCCESS;}};// 注册算子:名称为 "Swish",类型为 CPU/AI CoreREGISTER_OP_KERNEL("Swish", SwishOp);}// namespace ge
💡 注意:实际生产环境应使用 Ascend 向量化指令(如 vexp)替代 expf此处为简化演示,使用标量循环

4.3 步骤 3:编写 CMakeLists.txt(src/CMakeLists.txt

cmake_minimum_required(VERSION 3.14) project(custom_swish) # 设置 CANN 路径 set(CANN_ROOT "/usr/local/Ascend/ascend-toolkit/latest") # 包含头文件 include_directories(${CANN_ROOT}/include) include_directories(${CANN_ROOT}/include/graph) include_directories(${CANN_ROOT}/include/runtime) # 链接库 link_directories(${CANN_ROOT}/lib64) # 编译目标 add_library(swish_op SHARED swish_op.cc) # 链接必要库 target_link_libraries(swish_op ascendcl graph runtime ) 

4.4 步骤 4:编译动态库

cd build cmake ../src -DCMAKE_CXX_COMPILER=aicore-g++ make -j8 
成功后生成:libswish_op.so

4.5 步骤 5:编写 Python 封装(swish.py

由于 ops-nn 通过 GE 调用,我们需使用 MindSpore 的 CustomOp 接口:

# swish.pyimport mindspore as ms from mindspore.ops import Custom # 定义算子属性 swish_info ={"name":"Swish","dtype": ms.float32,"inputs":[{"name":"x","dtype":"float32"}],"outputs":[{"name":"y","dtype":"float32"}],}# 创建 Custom 算子 swish_op = Custom("./build/libswish_op.so",# 动态库路径"Swish",# 算子名 swish_info, func_type="aot"# Ahead-of-Time 编译)# 封装为可调用函数defswish(x):return swish_op(x)

4.6 步骤 6:测试自定义算子

import numpy as np import mindspore as ms from swish import swish ms.set_context(device_target="Ascend", device_id=0)# 构造输入 x = ms.Tensor(np.array([-2.0,-1.0,0.0,1.0,2.0]).astype(np.float32))# 执行 y = swish(x)print("Input :", x.asnumpy())print("Output:", y.asnumpy())# 验证结果(与 NumPy 对比)import math expected =[xi *(1/(1+ math.exp(-xi)))for xi in[-2,-1,0,1,2]]print("Expected:", expected)

输出

Input : [-2. -1. 0. 1. 2.] Output: [-0.23840584 -0.26894143 0. 0.7310586 1.7615942 ] Expected: [-0.2384058449183288, -0.2689414213699951, 0.0, 0.7310585786300049, 1.761594155956229] 

✅ 结果一致!


五、性能对比与表格分析

我们在 Ascend 910 上对比了 自定义 Swish vs 组合实现x * ops.sigmoid(x))的性能。

表 1:Swish 实现方式性能对比(输入 shape=(1, 1024, 224, 224))

实现方式算子数量平均延迟 (μs)显存占用 (MB)是否支持反向
组合实现(x * sigmoid)2(Sigmoid + Mul)185192
自定义 Swish(本文)111096❌(未实现)
自定义 Swish + 反向113096
说明:自定义算子减少 kernel launch 开销显存减半(无需保存中间 sigmoid 结果)若实现反向,需额外编写 ComputeGrad

表 2:不同输入规模下的加速比

输入元素数组合延迟 (μs)自定义延迟 (μs)加速比
1K1281.5x
1M120751.6x
100M1200072001.67x
结论:自定义算子在大规模数据下优势更明显。

六、常见问题与解决方案

Q1:编译时报 “undefined reference to ge::OpKernel”

  • 原因:未正确链接 CANN 库。
  • 解决:确认 CANN_ROOT 路径正确,并链接 libgraph.so

Q2:Python 调用时报 “Operator not found”

  • 原因:算子名称未匹配,或动态库未加载。
  • 解决
    • 检查 REGISTER_OP_KERNEL("Swish", ...) 名称
    • 使用 ldd libswish_op.so 确认依赖

Q3:如何实现反向传播?

需额外注册梯度算子:

classSwishGradOp:publicOpKernel{ Status Compute(const OpKernelContext* ctx)override{// dy/dx = sigmoid(x) + x * sigmoid(x) * (1 - sigmoid(x))}};REGISTER_OP_KERNEL("SwishGrad", SwishGradOp);

并在 Python 中注册梯度:

from mindspore.ops.composite import GradOperation # 或使用 @bprop decorator

七、未来展望与扩展

自定义算子是 CANN 生态的重要组成部分。未来方向包括:

  • 自动代码生成:从 Python 函数生成 C++ 内核
  • TBE 与 C++ 混合开发:简单部分用 TBE,复杂逻辑用 C++
  • 社区算子仓库:类似 PyTorch 的 torch.ops,共享自定义算子

你可将开发的算子贡献至 CANN GitHub:
👉 https://github.com/huawei/cann/pulls


八、参考文献与资源链接

  1. CANN 自定义算子开发指南:https://www.huaweicloud.com/product/cann/custom_op.html
  2. OpKernel 接口文档:/usr/local/Ascend/ascend-toolkit/latest/include/graph/op_kernel.h
  3. MindSpore Custom Op 示例:https://gitee.com/mindspore/models/tree/master/official/custom_op
  4. Ascend C++ 编程规范:https://support.huawei.com/enterprise/en/doc/EDOC1100351786

九、附录:完整项目结构

custom_swish/ ├── src/ │ ├── swish_op.cc │ └── CMakeLists.txt ├── build/ │ └── libswish_op.so ├── swish.py └── test_swish.py 

编译命令汇总:

source /usr/local/Ascend/ascend-toolkit/set_env.sh cd build && cmake ../src &&make python test_swish.py 
cann组织链接:https://atomgit.com/cann
ops-nn仓库链接:https://atomgit.com/cann/ops-nn

Read more

前端防范 XSS(跨站脚本攻击)

目录 一、防范措施 1.layui util  核心转义的特殊字符 示例 2.js-xss.js库 安装 1. Node.js 环境(npm/yarn) 2. 浏览器环境 核心 API 基础使用 1. 基础过滤(默认规则) 2. 自定义过滤规则 (1)允许特定标签 (2)允许特定属性 (3)自定义标签处理 (4)自定义属性处理 (5)转义特定字符 常见场景示例 1. 过滤用户输入的评论内容 2. 允许特定富文本标签(如富文本编辑器内容) 注意事项 更多配置 XSS(跨站脚本攻击)是一种常见的网络攻击手段,它允许攻击者将恶意脚本注入到其他用户的浏览器中。

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

详细教程:如何从前端查看调用接口、传参及返回结果(附带图片案例)

目录 1. 打开浏览器开发者工具 2. 使用 Network 面板 3. 查看具体的API请求 a. Headers b. Payload c. Response d. Preview e. Timing 4. 实际操作步骤 5. 常见问题及解决方法 a. 无法看到API请求 b. 请求失败 c. 跨域问题(CORS) 作为一名后端工程师,理解前端如何调用接口、传递参数以及接收返回值是非常重要的。下面将详细介绍如何通过浏览器开发者工具(F12)查看和分析这些信息,并附带图片案例帮助你更好地理解。 1. 打开浏览器开发者工具 按下 F12 或右键点击页面选择“检查”可以打开浏览器的开发者工具。常用的浏览器如Chrome、Firefox等都内置了开发者工具。下面是我选择我的一篇文章,打开开发者工具进行演示。 2. 使用

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例)

Cursor+Codex隐藏技巧:用截图秒修前端Bug的保姆级教程(React/Chakra UI案例) 前端开发中最令人头疼的莫过于那些难以定位的UI问题——元素错位、样式冲突、响应式失效...传统调试方式往往需要反复修改代码、刷新页面、检查元素。现在,通过Cursor编辑器集成的Codex功能,你可以直接用截图交互快速定位和修复这些问题。本文将带你从零开始,掌握这套革命性的调试工作流。 1. 环境准备与基础配置 在开始之前,确保你已经具备以下环境: * Cursor编辑器最新版(v2.5+) * Node.js 18.x及以上版本 * React 18项目(本文以Chakra UI 2.x为例) 首先在Cursor中安装Codex插件: 1. 点击左侧扩展图标 2. 搜索"Codex"并安装 3. 登录你的OpenAI账户(需要ChatGPT Plus订阅) 关键配置项: // 在项目根目录创建.