飞桨 x 昇腾生态适配方案:06_ 算子适配举例
本节介绍 aclnn 算子的三种适配场景。
Paddle-API 与 CANN-Kernel 差异剖析及适配策略
对于 Paddle-API 与 CANN-Kernel 两者中常见的差别与适配方法如下:
Paddle 参数缺失或者参数无法直接对应
如果 Paddle 算子只需要 CANN 提供的某个参数为默认值的功能,则可通过默认赋值的方式完成
考虑通过计算取得需要参数
CANN 参数缺失
CANN 算子没有某个 Paddle 有的参数,一般是此算子 CANN 支持的模式少于 Paddle
可通过多个算子分别完成算子的部分功能(如 max_pool + avg_pool)
如果 CANN 只能支持部分功能,则可以在调用处抛出参数值判断异常
数据类型不支持
输入数据类型不匹配时需要在计算前插入
Cast
操作,并且要更改输出的数据类型,在计算后对输出数据进行Cast
操作返回原数据类型
layout 转换
NPU 算子基本不支持 NHWC,但是部分 Paddle 算子支持,如果遇到这样的情况需要在计算前后插入
Transpose
小算子拼接
部分 Paddle-API 的功能在 NPU 中没法直接完成,但可通过多个小算子拼接完成,一般会少许影响性能
加入缺少的参数
以 ReluGrad 算子为例,通过计算或者默认赋值方式加入缺少的参数:

在进行参数对齐时,需要检查是否存在需要默认参数的情况。以 Paddle 的 relugrad 算子为例,其对应的 aclnn 的 ThresholdBackward 算子包含额外参数 threshold。在实际操作中,可通过默认赋值的方式实现参数对齐,如图所示,代码为phi::Scalar threshold = 0.0
。完成参数对齐后,即可直接调用 NPU 的 aclnnThresholdBackward 算子 。
数据类型转换
以 nll_loss 算子为例,Paddle API 与 CANN API 所支持的数据类型存在差异。Paddle API 中,输入 x 的数据类型为 double,而 CANN 的对应算子仅支持 float32 这一特定数据类型,具体情况如下图所示:Paddle 侧:

CAAN 侧:

此情形需进行数据类型转换:
首先,对输入数据执行 cast 操作,将其转换为 CANN 算子支持的数据类型;
完成转换后,执行 NPU 的 aclnn 算子;
算子运算结束后,再将计算结果的数据类型由 float32 转换回输入 x 原本的数据类型。具体流程如下图所示:

数据类型转换需要 Cast_kernel 算子,下面为 Cast_kernel 算子的声明:

以下介绍将变量 x 的数据类型从 double 转换为 float32(转换后的变量记为 x_cast)的流程:
对象声明
phi::DenseTensor
:深度学习框架中表示多维数组的核心数据结构,包含数据和元信息(如形状、数据类型)。phi::DenseTensorMeta
:用于存储张量的元信息(metadata)。
元数据初始化
phi::DataType::FLOAT32
:明确将目标张量的数据类型设为 float32。x.dims()
:继承输入张量 x 的维度信息(如 [batch_size, channels, height, width])
绑定元数据
作用:将初始化后的元数据绑定到目标张量 x_cast
。
执行类型转换
核心参数:dev_ctx
: 设备上下文(如 CPU/GPU 资源管理)x
: 输入张量phi::DataType::FLOAT32
: 目标数据类型&x_cast
: 输出的目标张量指针功能:将输入张量 x
的数据类型转换为 float32,结果写入 x_cast
。
执行 NPU aclnn 算子计算
把所有需要进行数据类型转换的参数转换完成后,使用算子执行宏 EXEC_NPU_CMD
执行 aclnn 算子:

aclnn 算子输出结果原类型恢复

将 out_cast(NPU 计算结果)转换为 paddle api 的
out
张量类型(如 float32 → double)。将损失计算中累计的权重值
total_weight_cast
数据类型转换为目标类型后写入total_weight
。
转置操作
在 Pool2dGradKernel 算子中,若输入数据格式 data_format 为NHWC
,即高度、宽度、通道数位于最后,鉴于 NPU 的操作要求,需将数据转换为NCHW
格式。此转换通过Transpose
操作达成。Transpose 操作的核心功能是实现张量维度重排,旨在适配 NPU 计算特性所规定的数据布局需求。在本场景中,其主要作用是完成从NHWC
(Channel Last)到NCHW
(Channel First)这两种内存布局的转换 。

流程图如下:

变量声明
phi::DenseTensor transformed_out_grad;
通过临时变量隔离布局转换过程,保证原始数据的完整性
布局判断逻辑
接下来,程序执行条件判断if (channel_last)
。该判断旨在检查输入数据是否采用NHWC
格式。若if (channel_last)
条件成立,即NHWC
(Channel Last)格式时,程序将执行转置操作,把数据格式转换为通道优先 Channel First 的NCHW
格式。
维度置换规则
std::vector<int> perm = {0, 3, 1, 2};
数学原理:对应张量维度[N,H,W,C]->[N,C,H,W]定义了一个 perm 向量{0, 3, 1, 2},这应该是用来重新排列维度的顺序。原来的维度假设是 NHWC(0,1,2,3),转置后变为 NCHW(0,3,1,2)。
新形状构建
接着构造了 out_grad_tensor_shape,调整形状以匹配新的维度顺序。调整后的形状是通过重新排列 out_grad 的维度得到的,例如将原维度[0]、[3]、[1]、[2]组合成新的形状。
通过维度复制而非引用保证形状独立性。
内存分配
然后将 transformed_out_grad 调整大小,分配内存,并通过 TransposeKernel 进行转置操作。
转置运算
custom_kernel::TransposeKernel<T, Context>(dev_ctx, out_grad, perm, &transformed_out_grad);
custom_kernel::TransposeKernel 是调用 NPU 的转置内核函数,需在算子开发代码中进行函数声明,如图所示:

评论