本节介绍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 x_cast; // 声明目标张量 x_cast
phi::DenseTensorMeta x_cast_meta; // 声明张量的元数据对象
phi::DenseTensor
:深度学习框架中表示多维数组的核心数据结构,包含数据和元信息(如形状、数据类型)。phi::DenseTensorMeta
:用于存储张量的元信息(metadata)。
元数据初始化
x_cast_meta = {phi::DataType::FLOAT32, x.dims();
phi::DataType::FLOAT32
:明确将目标张量的数据类型设为 float32。x.dims()
:继承输入张量 x 的维度信息(如 [batch_size, channels, height, width])
绑定元数据
x_cast.set_meta(x_cast_meta);
作用:将初始化后的元数据绑定到目标张量 x_cast
。
执行类型转换
custom_kernel::CastKernel<T, Context>(dev_ctx, x, phi::DataType::FLOAT32, &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]组合成新的形状。
std::vector<int> out_grad_tensor_shape = {
out_grad.dims()[0],
out_grad.dims()[3],
out_grad.dims()[1],
out_grad.dims()[2],
};
通过维度复制而非引用保证形状独立性。
内存分配
然后将transformed_out_grad调整大小,分配内存,并通过TransposeKernel进行转置操作。
transformed_out_grad.Resize(phi::make_ddim(out_grad_tensor_shape));
dev_ctx.template Alloc<T>(&transformed_out_grad);
转置运算
custom_kernel::TransposeKernel<T, Context>(dev_ctx, out_grad, perm, &transformed_out_grad);
custom_kernel::TransposeKernel是调用NPU的转置内核函数,需在算子开发代码中进行函数声明,如图所示:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。