PyPTO-Gym 外积 kernel 参考:unsqueeze + mul 组合实现与按行 loop 切分实践
2026/9/18 5:02:24 网站建设 项目流程

PyPTO-Gym 外积 kernel 参考:unsqueeze + mul 组合实现与按行 loop 切分实践

【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym

本文围绕 PyPTO-Gym 仓库中pypto-api-explore技能包下的 outer kernel 参考骨架,讲解如何用 PyPTO 的unsqueeze+mul组合方案实现torch.outer(外积)算子,并深入剖析其“沿第一输入元素(输出行)loop 切分、第二输入轴整块广播”的 Tiling 策略。读完本篇,你可以理解 PyPTO 组合算子的 kernel 骨架写法、占位符约定与 view→compute→assemble 的切分范式,并能将同一模式迁移到 RoPE 频率矩阵构建等真实模型算子场景。

1. 算子映射:outer 在 PyPTO 中的组合方案

PyPTO 没有提供与torch.outer同名的原子接口。在仓库的算子对标手册 torch-pypto-op-mapping.md 中,outer被明确归入「数学运算」类的组合方案

Torch 算子Pypto 组合方案参考实现
outerunsqueeze+mulouter.md

从映射逻辑看,外积out[i, j] = a[i] * b[j]本质上是一次逐元素广播乘法:把两个一维向量各自扩展出一个新轴(a变成列向量、b变成行向量),再用mul做广播相乘即可得到二维结果。这也决定了它属于纯 Vector 类算子(仅逐元素运算,不涉及 matmul),实现上无需 Cube 侧的set_cube_tile_shapes,重点在于 loop 切分与广播轴的处理。

该骨架的取用方式由技能工作流定义:pypto-api-explore/SKILL.md 的「步骤 2.5: 本地映射优先」要求——算子命中「命名映射」或「组合方案」条目时,按表取用对应的 Pypto API 组合方案,并阅读 examples/ 下对应的 kernel 参考骨架。

2. outer kernel 完整参考骨架

outer.md 给出的完整骨架如下(占位符约定见下一节):

Note: 沿第一输入元素(输出行)loop 切分;每行 = a[i] × b 向量,第二输入轴整块。

@pypto.frontend.jit(runtime_options={"run_mode": pypto.RunMode.NPU}) def outer_kernel(a: pypto.Tensor(al, pypto_dtype), b: pypto.Tensor(bl, pypto_dtype), out: pypto.Tensor(ol, pypto_dtype)): b_row = pypto.unsqueeze(b, 0) for i in pypto.loop(batch, name="row", unroll_list=[1]): a_i = pypto.view(a, [1, 1], [i, 0]) r = pypto.mul(a_i, b_row) pypto.assemble(r, [i, 0], out)

逐行拆解:

  1. @pypto.frontend.jit(runtime_options={"run_mode": pypto.RunMode.NPU}):以 PyPTO 前端 JIT 编译入口声明 kernel,runtime_options指定 NPU 运行模式;
  2. pypto.Tensor(al, pypto_dtype):kernel 参数按(shape 列表, dtype)声明,al/bl/ol分别是输入a、输入b与输出out的 shape 占位符,pypto_dtype为元素 dtype(如pypto.DT_FP32);
  3. b_row = pypto.unsqueeze(b, 0):在 loop 之外对b提一次级,补出新首轴使其成为行向量,供循环内逐行广播复用;
  4. pypto.loop(batch, name="row", unroll_list=[1]):沿输出行轴(长度由占位符batch给出)建立切分 loop,每次迭代处理一行,unroll_list=[1]表示按 1 份展开;
  5. a_i = pypto.view(a, [1, 1], [i, 0]):用viewa中按偏移[i, 0]取出一个1×1的 tile,即标量a[i]
  6. r = pypto.mul(a_i, b_row)1×1tile 与整块b行向量广播相乘,得到输出的第i行;
  7. pypto.assemble(r, [i, 0], out):把计算得到的行 tile 按偏移[i, 0]组装回输出张量。

这套「view取 tile → 逐元素计算 →assemble写回」的三段式,是 PyPTO loop 切分算子的通用数据流。仓库中的生产实现也采用同一模式,例如 mla_prolog.py 中沿 tiling 轴view出子块、处理后assemble回输出;sum_lstm.py 同样以view取 tile、assemble写回。

3. 切分策略解析:为什么按“行” loop、第二输入轴整块

骨架开头的 Note 一句话点明了 Tiling 决策,值得展开:

  • loop 轴 = 输出行(即第一输入a的轴):外积输出是二维的[len(a), len(b)],行与行之间相互独立(第i行只依赖a[i]与整个b),因此沿行轴 loop 切分是天然的并行切分方向,每轮迭代的计算量为len(b)个元素的逐元素乘法;
  • 整块轴 = 第二输入b的轴b对每一行都是完整参与的,且是纯广播(不需要逐元素索引变化),无需再切,整块参与可避免额外的切片开销;
  • a每次只取一个元素view(a, [1, 1], [i, 0])a[i]变成1×1tile 后广播,等价于把标量乘到整行。

从源码结构看,这种“一个 loop 轴 + 若干整块广播轴”的骨架形式与同目录其他参考(如 rope.md 的 batch 轴 loop、cos/sin 同行整块参与乘加)保持一致:每篇骨架只用一句话说明“哪些轴 loop、哪些轴整块、为什么”。

4. 占位符约定与最小可运行 setup

examples/README.md 定义了所有骨架共用的占位符体系,阅读outer.md时须对照理解:

占位符含义
sl输入 shape 列表,如[B, S, D]
ol输出 shape 列表
pypto_dtype元素 dtype,如pypto.DT_FP32
batch被 loop 的外层轴长度(通常sl[0]
inner单次迭代处理的内层 shape,如sl[1:]
inner_out单次迭代的输出内层 shape(末轴长度可能变化)

outer 骨架在此基础上额外使用了al/bl/ol三个 shape 列表占位符,分别对应输入a、输入b与输出。结合骨架中view(a, [1, 1], [i, 0])assemble(r, [i, 0], out)的偏移写法,可以推断其心智模型为:batch = al[0](loop 的行数),输出ol为二维外积形状,b的第二轴整块参与。

README 同时给出各骨架的最小可运行 setup 示例(examples/README.md#L20-L29):

import pypto B, D = 8, 128 sl, ol = [B, D], [B, 1] pypto_dtype = pypto.DT_FP32 batch, inner, inner_out = B, [D], [1]

BD具体化为 outer 场景时,B对应a的长度(loop 轴长度),D对应b的长度(整块广播轴长度)。

5. outer 的真实应用场景:RoPE 频率矩阵

外积不是抽象练习——仓库中多个模型的前向路径都在使用torch.outer构建 RoPE(旋转位置编码)的频率矩阵。以 Qwen3.5 视觉端 RoPE 实现 为例:

def forward(self, seqlen: int) -> torch.Tensor: seq = torch.arange(seqlen, device=self.inv_freq.device, dtype=self.inv_freq.dtype) freqs = torch.outer(seq, self.inv_freq) return freqs

这里seq是长度为seqlen的位置索引向量,inv_freq是长度为dim/2的逆频率向量,外积得到[seqlen, dim/2]的频率矩阵。同一模式也出现在 OpenPangu Dense 模型、GutenOCR 的 Qwen2.5-VL 建模文件 等多处,且测试代码 test_pangu_fused_layer.py 中同样以torch.outer(t, inv_freq)作为参考行为。

这个用法与 outer 骨架的切分策略严丝合缝:seqlen轴即 loop 的“输出行”轴,每一行等于seq[i](标量)×inv_freq(整块向量)。若在 NPU 侧将该步骤下沉为 PyPTO kernel,outer kernel 参考 的行 loop 切分正是可直接对号入座的骨架。

6. 使用边界与注意事项

引用该骨架时,务必注意 examples/README.md 的三条约定:

  1. 骨架非标准模板:每个<op>.md仅为 kernel 参考骨架,只展示接口组合与轴切分模式(哪些轴 loop、哪些轴整块);loop 轴、unroll_list、tile shape、动态轴处理等需按实际 shape / dtype 与平台约束确定并调优;
  2. 未经逐一经 NPU 编译验证:所有骨架未逐一经 NPU 编译验证,落地前需要走完整验证流程;
  3. Note 约定:每篇骨架以一句话说明切分方式(loop 的轴、整块的轴及原因),outer 的 Note 即“沿第一输入元素(输出行)loop 切分;每行 = a[i] × b 向量,第二输入轴整块”。

7. 延伸阅读

  • 完整的 API 探索工作流(输入解析、公式分解、并行探索、报告生成)见 pypto-api-explore/SKILL.md;
  • Torch ↔ Pypto 全量对标(同名映射、命名映射、组合方案三大类)见 torch-pypto-op-mapping.md;
  • 切分模式相近的参考骨架:rope.md(batch 轴 loop + last-dim 折半旋转)、norm.md(mul+sum+sqrt组合);
  • 生产侧同范式的 view/assemble 切分实现:mla_prolog.py、sum_lstm.py。

综上,outer在 PyPTO 中的落地路径是标准的“组合方案”:unsqueeze构造广播轴 +mul完成逐元素外积,配合沿输出行轴的loop切分与view/assemble数据流,即可覆盖 RoPE 频率矩阵构建等高频真实场景。

【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询