| feat(optim): add InplaceApplyProximalAdagrad operator Co-authored-by: handsomeRobot_SK<suke13@huawei.com> # message auto-generated for no-merge-commit merge: !8332 merge inplace_apply_proximal_adagrad into master feat(optim): add InplaceApplyProximalAdagrad operator Created-by: handsomeRobot_SK Commit-by: handsomeRobot_SK Merged-by: cann-robot Description: ## PR: feat(optim): add InplaceApplyProximalAdagrad operator ### 概述 新增 InplaceApplyProximalAdagrad 算子,实现 Proximal Adagrad 优化器的参数更新(FOBOS 近端算法),结合 Adagrad 自适应学习率与 L1/L2 近端正则化,用于深度学习训练中反向传播后的参数更新阶段。算子基于 Ascend C 开发,支持 Ascend950 (arch35/DAV_3510) 平台,仅支持 GEIR 图模式调用。 基于 ApplyProximalAdagrad V1 演进,将单输出 algorithmic inplace 设计升级为**双显式 inplace 输出**(var / accum),完整反映 Proximal Adagrad 算法的 inplace 更新语义,对齐弃用版 ApplyProximalAdagradD 的双输出端口设计。 ### 算子功能 实现 Proximal Adagrad 单步参数更新(FOBOS 五步公式): $$ \begin{aligned} \text{accum}_t &= \text{accum}_{t-1} + \text{grad}_t^2 \\ \eta_t &= \frac{\text{lr}}{\sqrt{\text{accum}_t}} \\ \text{prox}_t &= \text{var}_{t-1} - \eta_t \cdot \text{grad}_t \\ \text{var}_t &= \frac{\text{sign}(\text{prox}_t)}{1 + \eta_t \cdot \text{l2}} \cdot \max\!\left(|\text{prox}_t| - \eta_t \cdot \text{l1},\ 0\right) \end{aligned} $$ 当 L1 = 0 时简化为: $$ \text{var}_t = \frac{\text{prox}_t}{1 + \eta_t \cdot \text{l2}} $$ 其中 $var$ 为模型参数,$accum$ 为梯度平方累积量,$lr$ 为学习率,$l1/l2$ 为正则化强度,$grad$ 为当前梯度。 **关键特性**: - **L1 软阈值产生稀疏性**:当 $|prox| \leq \eta \cdot l1$ 时权重直接置零,适用于需要稀疏模型的场景 - **Adagrad 自适应学习率**:对低频特征使用更大学习率,适合稀疏特征训练 - **Inplace 语义**:var/accum 均原地更新,V2 将两者显式暴露为输出端口 - **L1=0 双层快速路径**:编译期 HAS_L1 + 运行时 l1==0 双层优化,跳过 sign/软阈值计算 - **FP16/BF16 Cast up**:半精度输入内部 Cast 到 FP32 计算,结果 Cast 回原始精度 ### 对标竞品 | 竞品 | 接口 | 说明 | |------|------|------| | TensorFlow | tf.raw_ops.ApplyProximalAdagrad | 功能对标,CANN 显式暴露 var/accum 双输出 | | PyTorch | 无原生实现 | PyTorch 优化器在 Python 层实现,不在 ATen 注册 | ### 支持规格 | 项目 | 支持情况 | |------|---------| | **数据类型** | float16, float32, bfloat16 | | **数据格式** | ND | | **平台** | Ascend950 (arch35) | | **调用模式** | GEIR 图模式 | | **输出端口** | 2 个显式 inplace 输出 (var + accum) | | **TensorFlow 兼容** | 对标 ApplyProximalAdagrad / ResourceApplyProximalAdagrad | ### 交付件清单 optim/inplace_apply_proximal_adagrad/ ├── CMakeLists.txt # 构建配置 ├── README.md # 算子文档 ├── examples/ │ ├── test_geir_inplace_apply_proximal_adagrad.cpp # GEIR 示例 │ └── arch35/ │ └── test_geir_inplace_apply_proximal_adagrad.cpp # arch35 GEIR 示例 ├── op_graph/ │ ├── CMakeLists.txt │ ├── inplace_apply_proximal_adagrad_proto.h # GEIR 原型定义 │ └── inplace_apply_proximal_adagrad_graph_infer.cpp # 图模式 InferDataType ├── op_host/ │ ├── inplace_apply_proximal_adagrad_def.cpp # 算子定义(6输入2输出) │ ├── inplace_apply_proximal_adagrad_infershape.cpp # 形状推导(含accum输出) │ └── arch35/ │ ├── inplace_apply_proximal_adagrad_tiling_arch35.h # Tiling 类声明 │ └── inplace_apply_proximal_adagrad_tiling_arch35.cpp # Tiling 计算 ├── op_kernel/ │ ├── inplace_apply_proximal_adagrad.cpp # Kernel 入口(8参数含accum_out) │ └── arch35/ │ ├── inplace_apply_proximal_adagrad.h # Kernel 实现(计算逻辑零改动复用V1) │ ├── inplace_apply_proximal_adagrad_struct.h # 模板参数声明 │ └── inplace_apply_proximal_adagrad_tiling_data.h # Tiling 数据结构 └── tests/ └── ut/ └── op_host/ ├── test_inplace_apply_proximal_adagrad_infershape.cpp # InferShape UT └── arch35/ └── test_inplace_apply_proximal_adagrad_tiling_arch35.cpp # Tiling UT ### 测试验证 #### 单元测试 (UT) - **覆盖范围**:InferShape + Tiling - **通过率**:100% #### GEIR 端对端验证 - **状态**:✅ 通过 - **验证内容**:图编译 + 图执行 + 输出正确性 - **精度标准**:商用标准(双标杆 Ratio),L0 级(MARE Ratio≤10) - **Golden 标杆**:TensorFlow tf.raw_ops.ResourceApplyProximalAdagrad - **覆盖范围**: - 数据类型:FP32 / FP16 / BF16 - L1 强度:0(快速路径)/ 非0(稀疏路径) - 特殊场景:空 Tensor、大 shape、标量输入 ### 代码质量 - ✅ 文件头格式统一(Copyright + Generated By CANNBot) - ✅ Tiling 校验使用 OP_LOGE 统一日志格式 - ✅ 算子定义属性添加 AttrType(OPTIONAL) 与原型对齐 - ✅ dtype 校验改用 OP_CHECK_IF + OP_LOGE 格式 - ✅ 魔鬼数字常量化(kVecRegBytes, kAlignBytes, kPadVarGrad, kPadAccum 等) - ✅ Kernel 计算逻辑 100% 复用 ApplyProximalAdagrad V1,零改动 - ✅ Tiling 策略 100% 复用 ApplyProximalAdagrad V1,零改动 - ✅ clang-format 格式化 ### 编译验证 bash cd ops-nn bash build.sh --soc=ascend950 --pkg --ops=inplace_apply_proximal_adagrad -j16 # 输出 ✅ 编译成功 ✅ 生成算子包:cann-ops-nn-custom_linux-x86_64.run ### 关键实现细节 #### 1. 版本关系 | 版本 | 算子名 | 输出端口 | Kernel 实现 | 状态 | |------|--------|---------|------------|------| | 弃用版 | ApplyProximalAdagradD | 2 | TBE (Python) | DEPRECATED | | V1 | ApplyProximalAdagrad | 1 | Ascend C (C++) | 推荐使用 | | **V2(本次)** | **InplaceApplyProximalAdagrad** | **2** | **Ascend C(复用V1)** | **本次交付** | #### 2. Kernel 实现 - **架构**:TQue 双缓冲 + TBuf scratch,完全复用 ApplyProximalAdagrad V1 - **模板参数**:(D_T_VAR, PAD_TAIL, HAS_L1),3 dtype × 2 pad × 2 L1 = 12 变体 - **五步公式**: 1. accum' = accum + grad² 2. eta = lr * rsqrt(accum') 3. prox = var - eta * grad 4. (l1>0) 软阈值: sign(prox) * max(|prox|-eta*l1, 0); (l1=0) 跳过 5. var' = prox_hat / (1 + eta*l2) #### 3. 精度策略 - **FP32**:原生计算,无转换 - **FP16**:Cast→FP32 计算→Cast 回(CAST_RINT) - **BF16**:Cast→FP32 计算→Cast 回(CAST_ROUND),标量加载走 Vector Cast workaround #### 4. Tiling 实现 - **多核切分**:blockFactor = CeilAlign(CeilDiv(total, coreNum), ubBlockSize) - **UB 切分**:按 co-resident buffer 占用计算 ubFactor - **TilingKey**:(dType, padTail, hasL1),默认 hasL1=1(标量在 GM 不可 host 读取) #### 5. 尾部 Padding - **var / grad**:pad 0,grad=0 保持 accum 不变 - **accum**:pad 1,Rsqrt(1)=1 避免 +Inf→NaN ### 依赖与限制 - **依赖**:CANN 9.0.0 - **限制**: - 仅支持 GEIR 图模式(无 ACLNN 接口) - 仅支持 Ascend950 (arch35) - var、accum、grad 三者 shape/dtype 必须完全一致 - lr、l1、l2 为 0-D 或 1-element 标量 - 不支持空 Tensor(0 元素) - use_locking 属性为 TensorFlow 兼容保留 ### Checklist - [x] 代码符合 ops-nn 内置算子标准 - [x] 文件头格式统一 - [x] Tiling 校验规范化(OP_LOGE + OP_CHECK_IF) - [x] 单元测试通过 - [x] GEIR 端对端验证通过 - [x] 编译安装验证通过 - [x] 文档完整(README + 示例) - [x] clang-format 格式化 - [x] Kernel 计算逻辑复用 V1(零改动) - [x] Tiling 策略复用 V1(零改动) See merge request: cann/ops-nn!8332 | 1 个月前 |