S100P 目前onnx还不支持isnan,isinf,模型中目前有这两种算子,请问都是有什么办法来替换或者避免这两个算子的

我来帮你分析这个问题。先搜索一下社区是否有类似的解决方案。

问题分析

你的模型在 S100P 上量化失败,核心问题是:

  1. ONNX 模型包含 isnanisinf 算子 - S100P 工具链不支持这两个算子
  2. 强行移除后校准出现 NaN - 说明模型本身存在数值不稳定问题

从日志看,错误发生在 Transformer 结构的 Attention 层和 Where 算子处,这是典型的数值溢出/下溢问题。

解决方案

方案一:模型层面修复(推荐)

在导出 ONNX 之前,在 PyTorch 模型中替换这两个算子:

import torch
import torch.nn as nn

# 替换 isnan
def safe_isnan(x):
    return torch.isnan(x.clamp(-1e10, 1e10))

# 替换 isinf  
def safe_isinf(x):
    return torch.isinf(x.clamp(-1e10, 1e10))

# 或者更彻底的方案:在 Attention 机制中添加数值稳定处理
class StableAttention(nn.Module):
    def forward(self, q, k, v, mask=None):
        # 添加 clamp 防止数值溢出
        scale = 1.0 / (q.size(-1) ** 0.5)
        attn_weights = torch.matmul(q, k.transpose(-2, -1)) * scale
        
        # 关键:在 softmax 前 clamp 数值
        attn_weights = attn_weights.clamp(-1e4, 1e4)
        
        if mask is not None:
            # 用大负数替代 -inf,避免 isinf
            attn_weights = attn_weights.masked_fill(mask == 0, -1e4)
        
        attn_probs = torch.softmax(attn_weights, dim=-1)
        return torch.matmul(attn_probs, v)

方案二:ONNX 层面替换

使用 ONNX Graph Surge 或 onnxruntime 替换算子:

import onnx
import onnx_graphsurgeon as gs

graph = gs.import_onnx(onnx.load("your_model.onnx"))

# 查找并替换 isnan/isinf 节点
for node in graph.nodes:
    if node.op in ["IsNaN", "IsInf"]:
        # 替换为 Constant + Equal 组合
        # 或者替换为合理的数值判断逻辑
        print(f"Found unsupported op: {node.op} at {node.name}")

# 保存修改后的模型
onnx.save(gs.export_onnx(graph), "fixed_model.onnx")

方案三:量化配置优化

在量化 YAML 配置中添加数值保护:

model_parameters:
  onnx_model: 'your_model.onnx'
  march: nash-m  # S100P 使用 nash-m
  
calibration_parameters:
  cal_data_dir: './calibration_data'
  cal_data_type: 'float32'
  calibration_type: 'default'
  
quant_config:
  op_config:
    # 对 Attention 相关算子特殊处理
    "Softmax":
      qtype: "fp16"  # 保持 FP16 精度
    "Where":
      qtype: "fp16"
    "Add":
      qtype: "int8"
      clamp_range: [-1e4, 1e4]  # 添加数值范围限制

方案四:校准数据检查

你的日志显示 find non positive thresholdfind inf threshold,说明校准数据有问题

# 检查校准数据
import numpy as np
import os

cal_dir = './calibration_data'
for file in os.listdir(cal_dir):
    data = np.load(os.path.join(cal_dir, file))
    print(f"{file}: min={data.min()}, max={data.max()}, "
          f"has_nan={np.isnan(data).any()}, has_inf={np.isinf(data).any()}")
    
    # 如果存在 NaN/Inf,需要清理
    if np.isnan(data).any() or np.isinf(data).any():
        data = np.nan_to_num(data, nan=0.0, posinf=1e4, neginf=-1e4)
        np.save(os.path.join(cal_dir, file), data)

排查步骤

  1. 先用 Netron 查看模型,定位 isnan/isinf 具体在哪个层
  2. 检查训练时的 loss 曲线,确认模型是否完全收敛
  3. 验证校准数据质量,确保没有异常值
  4. 尝试 FP16 量化而非 INT8,看是否能通过

社区资源

推荐参考这篇详细的 S100 量化教程:万字长文,学妹吵着要学的 RDKS100 模型量化及部署

如果问题仍未解决,建议:

  • 提供模型结构图(Netron 截图)
  • 分享校准数据统计信息
  • 说明模型训练框架和导出方式

这样社区能更精准地帮你定位问题。