Skip to content

[Operator Mechanism] Align interpolate GPU kernel with torch 2.12 - #79895

Open
omoYang wants to merge 6 commits into
PaddlePaddle:developfrom
omoYang:align-interpolate
Open

omoYang wants to merge 6 commits into
PaddlePaddle:developfrom
omoYang:align-interpolate

Conversation

@omoYang

@omoYang omoYang commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

PR Category

Operator Mechanism

PR Types

Improvements

Description

在 FLAGS_use_accuracy_compatible_kernel 下扩展 CUDA bilinear 对齐范围并新增 trilinear 对齐,目标为与 torch 2.12 在 torch.use_deterministic_algorithms(True) 下的 _upsample_linear decomposition 逐位一致;同时将原生 trilinear 反向的精度对齐到 torch upsample_trilinear3d_backward。

1. bilinear compat kernel 扩展(interpolate_bilinear_compat.cuh)

  • 新增支持:NHWC、align_corners=True(此时忽略 align_mode)、未 recompute 的 scale_factor(attr 与 Scale Tensor 两种输入)、FP16/BF16(按 FP32 计算,存储时舍入一次,与 torch pw_cast_for_opmath 一致)。
  • 带 scale 时按 torch 规则用 1/scale 映射坐标;即使输出尺寸等于输入尺寸也不走 Copy 快路径(如 scale=1.1)。
  • 仍回退原 kernel 的情况:align_corners=False 且 align_mode=1(torch 线性插值无对应模式)、同时给定 scale 与 size(torch 无法表达)、FP64。

2. 新增 trilinear compat kernel(interpolate_trilinear_compat.cuh)

  • 分发规则与 bilinear 相同,复用其坐标、lerp 与索引 helper;支持 NCDHW/NDHWC、align_corners、scale/Scale Tensor、FP32/FP16/BF16。
  • 反向按 torch index_put 的稳定排序求和:上采样不超过 2x 时由单线程回放 warp 求和顺序,超过 2x 时用 warp 归约。

3. 原生 KeTrilinearInterpBw 精度对齐(flag 关闭的默认路径)

  • 源坐标与 lambda 改用 AreaPixelComputeSourceIndex / PreCalculatorForLinearInterpInputIndex 在 MT 中计算,与前向 KeTrilinearInterpFw 及 torch 一致;每个贡献在 MT 中算完后只转换为 T 一次,再做 atomicAdd。
  • 影响:FP16/BF16 trilinear 反向不再先把 lambda 转成 T 再在 T 中连乘,精度提升;FP32 的坐标计算从 double 改为 float(与 torch 的 accscalar_t 一致),可能存在末位差异。前向不受影响。

4. 测试覆盖

  • test_bilinear_interp_accuracy_compatible.py:删除旧的「NHWC / align_corners / scale 保持原路径」断言,改为与 torch 等价的 NumPy 参考逐位比较;新增 align_corners、FP16/BF16、非 recompute scale(含 1.1、2.05、>2x 边界)、PIR Scale Tensor、NHWC 用例。
  • test_trilinear_interp_accuracy_compatible.py(新增):覆盖随机梯度与重复执行、边界与 warp 归约、align_corners、scale、PIR Scale Tensor、NDHWC、FP16/BF16,以及回退路径。
  • test_trilinear_interp_v2_op.py:新增 TestTrilinearInterpGradLowPrecision,在 flag 关闭时逐位验证 FP16/BF16 原生反向等于 FP32 梯度舍入一次(选取每个输入至多一个贡献的下采样 shape,使 atomicAdd 结果确定)。

验证

  • 上述三个测试文件在 B30Z(sm_103)上全部通过。
  • 两份 NumPy 参考实现与 torch 2.12.0+cu132(deterministic)逐位比对一致,覆盖 2D/3D、align_corners、scale、>2x 与 2.05 边界。
  • 原生 FP16/BF16 trilinear 反向与 torch 原生 kernel 在新增用例的输入上逐位一致;按旧实现模拟,1296 个非零梯度中有 521 个不同,说明该用例能拦截回退。

是否引起精度变化

是

  • flag 开启时 bilinear 新增路径与 trilinear 改为与 torch 逐位一致;
  • flag 关闭时 trilinear 反向 FP16/BF16 精度提升至FP32,可能有末位差异。

…cale

Under FLAGS_use_accuracy_compatible_kernel, the CUDA bilinear compat path
now also covers NHWC layout, align_corners=True, explicit scale / Scale
tensor without recompute, and FP16/BF16, matching torch's deterministic
_upsample_linear decomposition. Scales reuse funcs::AreaPixelComputeScale.
Under FLAGS_use_accuracy_compatible_kernel, CUDA trilinear interpolation
now matches torch's deterministic _upsample_linear decomposition for
FP32/FP16/BF16, NCDHW/NDHWC, align_corners and explicit scales, using the
same dispatch rules as the bilinear compat kernel and reusing its
coordinate, lerp and index helpers.
KeTrilinearInterpBw now computes source indices and lambdas with
AreaPixelComputeSourceIndex / PreCalculatorForLinearInterpInputIndex in
MT, like KeTrilinearInterpFw and torch's upsample_trilinear3d_backward,
and forms each contribution in MT before casting to T for the atomic add.
@Paddle-Review-Bot

Paddle-Review-Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Paddle-Bot Review Board (review完成)

序号 位置 优先级 规则来源 状态
1 兼容路径扩展与现有单测冲突 P1 仓库规则:测试质量与验证 ✅
2 三线性反向默认路径数值变化 P2 仓库规则:功能正确性与兼容性 ✅
3 三线性兼容测试与 PR 描述 P3 仓库规则:PR 信息与评审评论 ✅
4 PR 描述精度变化声明 P3 仓库规则:PR 信息与评审评论 ✅

可维护性建议(总分 88.5 / 100,只给建议,不影响评审结论)

序号 位置 维度 状态
1 兼容 kernel 分发条件重复四处 设计一致性 ✅
2 三线性 Launch 函数位置参数过多 抽象适配度 💡
3 channel-last 布局判断方式 类型安全 ✅
4 三线性反向归约路径判据 底层优雅度 💡
5 双线性同尺寸 Copy 回退用例 测试策略 ✅
各维度得分(仅评本 PR 改动,点击展开)
83 抽象适配度
2
85 高层优雅度
86 API 表面一致性
86 底层优雅度
4
86 中层优雅度
86 命名质量
87 包组织结构
88 AI 生成债务
88 跨模块架构
88 设计一致性
1
90 依赖健康度
90 约定偏离
90 契约一致性
90 逻辑清晰度
90 类型安全
3
92 错误处理一致性
92 未完成迁移
92 测试策略
5
93 初始化耦合
— 鉴权一致性

Powered by Nyanpasu claude with Opus 5.5 默认推理级别, please check the suggestions carefully.

if (FLAGS_use_accuracy_compatible_kernel && interp_method == "bilinear" &&
data_layout == DataLayout::NCHW && !align_corners && align_mode == 0 &&
scale.empty() && !scale_tensor) {
(align_corners || align_mode == 0) && !(has_scale && has_size) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 这里把 align_corners=True、带 scale_factor(非 recompute)以及 NHWC 都路由进了 compat kernel,但仓库现有的 test/legacy_test/test_bilinear_interp_accuracy_compatible.py 仍按旧契约断言这些场景「保持原 kernel 路径」:

  • test_other_modes_keep_existing_path 对 align_corners=True、scale_factor=0.75、data_format='NHWC' 三个子用例做 flag 开/关 tobytes() 完全相等断言;
  • test_nonrecomputed_scale_keeps_existing_path 对 scale_factor 非 recompute 的前向做逐位相等断言。

compat kernel 采用 left + (right - left) * w 的逐步 FP32 舍入,原 kernel 是 h2*(w2*a+w1*b)+h1*(...) 加权和,两者结果按位不同。我用 NumPy 按两种公式模拟(7x9 输入):align_corners 3x4 有 4/24、scale=0.75 5x6 有 29/60、半像素 3x4 有 12/24 个元素不一致,因此上述子用例在 CUDA 环境下预计会失败(本地无 GPU,未实跑)。

建议同步更新该测试文件:删除/改写这些「保持原路径」的断言,改为对 NHWC、align_corners、scale 路径给出与 torch 对齐的参考值(可复用文件内 reference_forward/reference_backward 并扩展参数)。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修复:test_other_modes_keep_existing_path 删除了 align_corners / scale_factor / NHWC 子用例,test_nonrecomputed_scale_keeps_existing_path 改为 test_nonrecomputed_scale,并新增 test_align_corners、test_nhwc、test_low_precision、test_scale_tensor_pir,均与扩展后的 NumPy 参考逐位比较。CPU 上用参考实现回放各用例自洽通过;GPU 未实跑。

T w2lambda = static_cast<T>(w2lambda_mt);
int64_t in_img_idt, in_img_idy, in_img_idx, d_id, h_id, w_id;
MT d1lambda, d2lambda, h1lambda, h2lambda, w1lambda, w2lambda;
MT src_d = funcs::AreaPixelComputeSourceIndex<MT>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 KeTrilinearInterpBw 的这处改动不在 FLAGS_use_accuracy_compatible_kernel 之后,会改变默认路径(flag 关闭)以及 flag 开启但仍回退到原 kernel(如 align_mode=1、scale 与 size 同时给出)时的反向结果。

逐项对比旧实现:坐标与 lambda 的取值在 FP32 下与旧逻辑等价,FP32 结果应不变;但 FP16/BF16 下旧代码先把 6 个 lambda 转成 T,再在 T 中做 d*h*w*grad 连乘,新代码在 MT(float) 中计算后只舍入一次,结果会变化(精度提升,与 2D KeBilinearInterpBw 一致)。

这属于默认路径的数值行为变化,建议:要么挂到兼容 flag 之后,要么在 PR 描述中明确说明该默认路径变化及影响范围(仅 FP16/BF16 trilinear 反向),并补一个 FP16/BF16 trilinear 反向与 FP32 参考对比的用例。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新增的 TestTrilinearInterpGradHalfPrecision 在默认 flag 下覆盖了 FP16/BF16 trilinear 反向(NCDHW/NDHWC),确认这是有意的默认路径精度提升,旧实现在 T 中连乘无法通过该用例。按已处理关闭;PR 描述中的说明并入「PR 描述为空」一项跟踪。

const bool has_scale = scale_tensor || !scale.empty();
const bool has_size = out_size || (size_tensor && size_tensor->size() > 0);
if (FLAGS_use_accuracy_compatible_kernel && interp_method == "trilinear" &&
(align_corners || align_mode == 0) && !(has_scale && has_size) &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 新增的 trilinear_compat 前向/反向(含 NDHWC、align_corners、scale、warp 规约与单线程 32-lane 回放两条路径),以及 bilinear compat 新开放的 FP16/BF16、NHWC、align_corners、scale 路径,本 PR 都没有新增或修改任何测试;PR 描述的 Description 也为空,未说明与 torch 2.12 对齐的验证方式。

这些 kernel 的目标是与 torch 逐位一致,回归风险集中在索引/布局与求和顺序,靠现有 test_trilinear_interp_v2_op 的容差比较无法发现。建议参照 test_bilinear_interp_accuracy_compatible.py 补一组 trilinear 用例:覆盖 NCDHW/NDHWC、align_corners 真/假、scale_factor、下采样与 >2x 上采样(走 WarpReduce 分支)、FP16/BF16,并给出 torch 生成的 md5 或独立 NumPy 参考;同时在 PR 描述中补充动机与验证结果。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试部分已补齐:新增 test_trilinear_interp_accuracy_compatible.py 覆盖 NCDHW/NDHWC、align_corners、scale_factor(含 >2x WarpReduce 分支)、Scale Tensor + PIR、FP16/BF16,bilinear 侧同步补齐。剩余项降为 P3:PR 描述的 Description 仍为空,请补充动机、与 torch 2.12 对齐的验证方式,并注明 flag 关闭时 FP16/BF16 trilinear 反向数值也会变化。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 描述已补充动机、分发与回退规则、默认路径 FP16/BF16 trilinear 反向的数值变化及验证结果,此项已解决。

Cover the bilinear compat kernel's NHWC, align_corners, scale_factor,
Scale tensor and FP16/BF16 paths, add bitwise tests for the trilinear
compat kernel against an independent torch-equivalent NumPy reference,
and check that the native trilinear backward forms FP16/BF16
contributions in FP32 and rounds them once, as torch does.

@Paddle-Review-Bot Paddle-Review-Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试问题已修复;剩余 P3:请补充 PR 描述(动机、验证方式及默认路径 FP16/BF16 trilinear 反向的数值变化说明)。

@omoYang

omoYang commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

2 similar comments
@omoYang

omoYang commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

@omoYang

omoYang commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

std::is_same<T, phi::bfloat16>::value) {
const bool has_scale = scale_tensor || !scale.empty();
const bool has_size = out_size || (size_tensor && size_tensor->size() > 0);
if (FLAGS_use_accuracy_compatible_kernel && interp_method == "trilinear" &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(设计一致性,不阻塞合入):compat kernel 的启用条件(dtype 白名单、flag、interp_method、align_corners || align_mode == 0、!(has_scale && has_size)、尺寸是否变化)在 interpolate_kernel.cu 2D/3D 前向和 interpolate_grad_kernel.cu 2D/3D 反向共复制了 4 份,3D 处只靠「Same dispatch rules as ...」注释维持同步。前向与反向一旦条件分叉,会出现前向走 compat、反向走原 kernel 的情况。建议在 compat 头文件中提供一个 host 端谓词(如 UseAccuracyCompatibleKernel(align_corners, align_mode, has_scale, has_size, size_changed)),四处统一调用。

}

template <typename T>
inline void LaunchForward(const T* input,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(抽象适配度,不阻塞合入):文件内已有 Geometry,但 LaunchForward/LaunchBackward 仍接收 16 个位置参数(6 个相邻的 int64_t 尺寸 + bool + 3 个 double),并各自重复一遍 10 参数的 MakeGeometry(...);调用方交换 in/out 或 d/h/w 时编译期无法发现。建议让两个 Launch 函数直接接收 const Geometry&,由调用方构造一次。

out_h,
out_w,
c,
data_layout == DataLayout::NHWC,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(类型安全,不阻塞合入):这里用 data_layout == DataLayout::NHWC 判断 channel-last,而同函数的 ExtractNCDWH、dim_out 及原 kernel 都按 data_layout == DataLayout::NCHW 否则视为 channel-last。若 layout 字符串为 "NDHWC"(StringToDataLayout 返回独立的 DataLayout::NDHWC),原路径按 channel-last 计算,compat 路径却按 NCDHW 寻址,结果静默错误。Python API 目前会把 NDHWC 映射为 "NHWC",所以只是防御性问题;建议此处及 interpolate_kernel.cu 2D、interpolate_grad_kernel.cu 两处统一改为 data_layout != DataLayout::NCHW。

// Both paths are exact. Up to 2x upsampling each input sees at most 64
// contributions per corner, cheap enough for one thread; larger factors
// spread the reduction over a warp as torch does.
bool small_factor =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(底层优雅度,不阻塞合入):注释已说明两条路径都逐位精确,out_* <= 2 * in_* 本身已限制每个输入的贡献数,small_factor 额外的 scale_factor_* <= 2. 只会把 (2.05, 2.05, 2.1) 这类仍在 2 倍以内的情况送去 warp 路径,且与 bilinear_compat::LaunchBackward 的判据不一致,名字也不反映它决定的是归约方式。建议删除该条件,与 bilinear 使用同一判据;若需保留,请改名并注明原因。

@@ -360,15 +568,9 @@ def test_other_modes_keep_existing_path(self):
.astype(np.float32)
)
cases = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(测试策略,不阻塞合入):本 PR 把 in_h == out_h && in_w == out_w 的 Copy 快路径移到 compat 分发之后,并依赖 (has_scale || in_h != out_h || in_w != out_w) 保证不带 scale 的同尺寸调用仍走 Copy。trilinear 测试已有对应回退用例,bilinear 这里没有。建议补 (data, {"size": [7, 9], "mode": 'bilinear'}),沿用 flag 开/关逐字节比较。

…l-last check

- Add funcs::UseAccuracyCompatibleKernel host predicate shared by the
  bilinear/trilinear forward and backward compat dispatch so the four call
  sites can no longer drift apart.
- Use data_layout != DataLayout::NCHW for channel-last at the compat dispatch,
  matching dim_out/ExtractNCDWH and the original kernel (fixes a silent NDHWC
  mismatch).
- Add a same-size, no-scale bilinear case to test_other_modes_keep_existing_path,
  mirroring the trilinear test's Copy fast-path guard.
@omoYang

omoYang commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

1 similar comment
@omoYang

omoYang commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

// trilinear interpolation grad
CudaAtomicAdd(&in_pos1[0], d2lambda * h2lambda * w2lambda * out_pos[0]);
CudaAtomicAdd(&in_pos1[0],
static_cast<T>(d2lambda * h2lambda * w2lambda * grad));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 PR 描述最新一次编辑把「是否引起精度变化」从「是」改成了「否」,但这与实现和描述正文不一致。

这里在 flag 关闭的默认路径上,KeTrilinearInterpBw 改为在 MT 中计算每个贡献,再只转换为 T 一次。描述第 3 节和该字段下方的说明也写明:FP16/BF16 trilinear 反向结果会变化,FP32 坐标计算从 double 改为 float,可能有末位差异。默认路径的数值结果确实会变,建议把该字段改回「是」,下方说明保持不变。

@omoYang

omoYang commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

/re-run all-failed

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants