FAQ

模型计算结果改变原因分析

问题现象

在模型训练场景下,使用seed_all接口同时固定随机性和开启计算,通信确定性计算,能够保证模型执行两次得到的loss和gnorm结果完全一样。如果出现使能工具后loss或者gnorm出现偏差,可能是以下原因导致。

原因分析

工具引入同步导致计算结果变化:

工具采集统计量数据时,会涉及到将device上的tensor计算后的统计量信息通过item的时候传到CPU侧,再落盘到json文件中,item操作是一个同步的操作,可能会导致模型的计算结果出现变化。一般的现象是模型计算出现NaN,且在未使用工具时问题会复现,使用工具后问题不再出现。

ASCEND_LAUNCH_BLOCKING是一个环境变量,用于控制在PyTorch训练或在线推理场景中算子的执行模式。当设置为“1”时,算子将采用同步模式运行。因此如果出现加工具计算结果变化,可以设置ASCEND_LAUNCH_BLOCKING为1,如果结果仍然发生变化,则说明是由于同步引起的结果改变。这个时候需要复现问题现象完成问题定位,推荐使用msProbe工具的异步dump功能,具体使用方式可查看config配置中的async_dump字段。

解决方案

通过Hook机制改变计算结果:

PyTorch或MindSpore的hook机制会导致某些特殊场景下梯度计算的累加顺序产生变化,从而影响模型反向计算的gnorm结果。具体代码示例如下:

import random, os
import numpy as np
import torch
from torch import nn


class Net(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.ln1 = nn.Linear(32, 32)
        self.bn1 = nn.BatchNorm1d(32)
        self.ln2 = nn.Linear(32, 32)

    def forward(self, x):
        x1 = self.ln1(x)

        x2 = self.bn1(x)
        x2 = self.ln2(x2)
        return x1 + x2


class BigNet(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.net1 = Net()
        self.net2 = Net()

    def forward(self, x):
        out1 = self.net1(x)
        out2 = self.net2(out1)
        return out1, out2


def my_backward_hook(module, grad_input, grad_output):
    pass


if __name__ == "__main__":
    os.environ["HCCL_DETERMINISTIC"] = 'true'

    seed = 1234
    os.environ["PYTHONHASHSEED"] = str(seed)
    torch.manual_seed(seed)
    random.seed(seed)
    np.random.seed(seed)

    model = BigNet()
    model.net2.register_full_backward_hook(my_backward_hook)
    inputs = torch.randn(3, 32)

    out1, out2 = model(inputs)
    loss = out1.sum() + out2.sum()
    loss.backward()

    for name, param in model.named_parameters():
        print(f"{name}: {param.grad.mean()}")

执行一遍以上脚本,可以打印得到模型中各层的权重梯度,注释掉model.net2.register_full_backward_hook(my_backward_hook)后再执行一次,可以看出bn层的权重梯度已经发生了变化。

如果在L0,mix级别采集出现gnorm发生变化,可以尝试将采集级别改为L1,若L1级别gnorm不发生变化,则大概率是hook机制导致的梯度计算结果变化。

数据采集

  1. dump.json中API或Module统计信息里出现null或None值的原因是什么?

    dump.json里出现null或None值的可能性较多,常见的场景有:

    • 输入或者输出参数本身是一个None值。
    • 输入参数或输出参数类型当前工具不支持,会有日志打印提醒。
    • 输入或者输出tensor的dtype为bool时,Mean和Norm等字段为null。
  2. 如果存在namedtuple类型的数据作为nn.Module的输出,工具会将各字段数据dump下来,但是输出数据类型会被转成tuple,原因是什么?

    • 这是由于PyTorch框架自身,在注册module的backward hook时,会将namedtuple类型转成tuple类型。
  3. 如果某个API在dump支持列表support_wrap_ops.yaml中,但没有dump该API的数据,原因是什么?

    • 首先确认API调用是否在采集范围内,即需要在start和stop接口涵盖的范围内。
    • 其次,由于工具只在被调用时才对API进行patch,从而使得数据可以被dump下来。因此当API是被直接import进行调用时,由于该API的地址已经确定, 工具无法再对其进行patch,故而该API数据无法被dump下来。如下示例,relu将无法被dump:
    import torch
    from torch import relu    # 此时relu地址已经确定,无法修改
    
    from msprobe.pytorch import PrecisionDebugger
    
    debugger = PrecisionDebugger(dump_path="./dump_data")
    x = torch.randn(10)
    debugger.start()    # 此时会对torch下面的API进行patch,但已无法对import进来的API进行patch
    x = relu(x)          
    debugger.stop()
    

    在上述场景中,若希望采集relu数据,只需要将relu(x)修改为torch.relu(x)即可。

  4. 在使用L0 dump时,发现有些module的数据没有采集下来,原因是什么?

    • 确认日志打印中是否存在The {module_name} has registered deprecated register_backward_hook信息, 该信息说明module挂载了被PyTorch框架废弃的register_backward_hook,这与工具使用的register_full_backward_hook接口会产生冲突,故工具会跳过该module的反向数据采集。
    • 如果您希望所有module数据都能采集下来,可以将模型中使用的register_backward_hook接口改为PyTorch框架推荐的register_full_backward_pre_hook或register_full_backward_hook接口。
  5. 在vllm场景下进行数据dump时,发现报错:RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cpu and npu:0!

    • 这是因为工具的debugger实例化早于LLM实例化导致的,解决方法就需要将debugger的实例化移至LLM实例化之后进行,可参考下方示例:
    from vllm import LLM, SamplingParams
    from msprobe.pytorch import PrecisionDebugger
    prompts = [
       "Hello, my name is",
       "The president of the United States is",
       "The capital of France is",
       "The future of AI is",
    ]
    
    sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
    llm = LLM(model="Qwen/Qwen2.5-0.5B-Instruct")
    
    debugger = PrecisionDebugger("./config.json")    # debugger实例化晚于LLM实例化
    
    debugger.start()
    outputs = llm.generate(prompts, sampling_params)
    debugger.stop()
    
  6. 在使用msProbe进行PyTorch框架的数据采集功能时,请注意确认环境变量NPU_ASD_ENABLE=0,即关闭特征值检测功能。由于工具冲突,在该功能开启的情况下可能导致某些API数据采集的缺失。

精度预检(PyTorch)

  1. 预检工具在dump和acc_check的过程中,是否需要同时开启或关闭jit编译(jit_compile)?

    答:是。

  2. 预检工具对于type_as这类涉及数据类型转换操作的API,是否具有参考性?

    由于这类API在CPU侧存在精度先提升后下降的操作,因此这类API的有效性的参考价值有限。

  3. acc_check过程中出现报错:ERROR: Got unsupported ScalarType BFloat16。

    答:请使用最新版本的工具。

  4. Dropout算子,CPU和NPU的随机应该不一样,为什么结果比对是一致的?

    答:这个结果是正常的,工具对该算子有特殊处理,只判定位置为0的位置比例大约和设定p值相当。

  5. 为什么浮点型数据bench和CPU的dtype不一致?

    答:对于fp16的数据,CPU会上升一个精度fp32去计算,这是和算子那边对齐的精度结论,CPU用更高精度去计算会更接近真实值。

  6. Tensor魔法函数具体对应什么操作?

    答:

    Tensor魔法函数 具体操作
    __add__ +
    __and__ &
    __bool__ 返回Tensor布尔值
    __div__ /
    __eq__ ==
    __ge__ >=
    __gt__ >
    __iadd__ +=
    __iand__ &=
    __idiv__ /=
    __ifloordiv__ //=
    __ilshift__ <<=
    __imod__ %=
    __imul__ *=
    __ior__ |=
    __irshift__ >>=
    __isub__ -=
    __ixor__ ^=
    __lshift__ <<
    __matmul__ 矩阵乘法
    __mod__ %
    __mul__ *
    __nonzero__ 返回Tensor布尔值
    __or__ |
    __radd__ +(反向)
    __rmul__ *(反向)
    __rshift__ >>
    __sub__ -
    __truediv__ /
    __xor__ ^

精度比对(PyTorch)

工具使用

dump指定融合算子

数据采集当前支持融合算子的输入输出,需要在support_wrap_ops.yaml中添加,比如以下代码段调用的softmax融合算子。

def npu_forward_fused_softmax(self, input_, mask):
    resl = torch_npu.npu_scaled_masked_softmax(input_, mask, self.scale, False)
    return resl

如果需要dump其中调用的npu_scaled_masked_softmax算子的输入输出信息,需要在support_wrap_ops.yaml中的torch_npu:中自行添加该融合算子:

- npu_scaled_masked_softmax

npu_scaled_masked_softmax融合算子工具已支持dump,本例仅供参考。

常见问题

  1. 在同一个目录多次执行dump会冲突吗?

    答:会,同一个目录多次dump,会覆盖上一次结果,可以使用dump_path参数修改dump目录。

  2. 如何dump算子级的数据?

    答:需要配置level为L2模式。

  3. 工具比对发现NPU和标杆数据的API无法完全对齐?

    答:torch版本和硬件差异属于正常情况。

异常情况

  1. HCCL报错: error code: EI0006。

    CANN软件版本较低导致不兼容。升级新版CANN软件版本即可。

  2. torch_npu._C._clear_overflow_npu() RuntimeError NPU error,error code is 107002。

    如果运行溢出检测功能遇到这个报错,采取以下解决方法:

    如果是单卡运行,添加如下代码,0是卡号,选择自己空闲的卡号。

    torch.npu.set_device('npu:0')
    

    如果多卡运行,请在代码中修改对应卡号,比如进程使用卡号为{rank}时可以添加如下代码:

    torch.npu.set_device(f'npu:{rank}')
    

    如果运行精度比对功能遇到这个报错,尝试安装最新版本的msProbe。

  3. dump得到的VF_lstm_99_forward_input.1.0.npyVF_lstm_99_forward_input.1.1.npy类似的数据是否正常?

    带1.0/1.1/1.2后缀的npy是正常现象,例如,当输入数据为[[tensor1, tensor2, tensor3]]会生成这样的后缀。

  4. dump指定反向API的kernel级别的数据报错:NameError:name 'torch_npu' is not defined。

    答:如果是NPU环境,请安装torch_npu;如果是GPU环境,暂不支持dump指定API的kernel级别的数据。

  5. 配置dump_path后,使用工具报错:[ERROR] The file path /home/xxx/dump contains special characters。

    答:请检查你设置的dump绝对路径是否包含特殊字符,确保路径名只包含大小写字母、数字、下划线、斜杠、点和短横线;注意,如果执行脚本的路径为/home/abc++/,设置的dump_path="./dump",工具实际校验的路径为绝对路径/home/abc++/dump,++为特殊字符,会引发本条报错。

  6. 无法dump matmul权重的反向梯度数据。

    答:matmul期望的输入是二维,当输入不是二维时,会将输入通过view操作展成二维,再进行matmul运算,因此在反向求导时,backward_hook能拿到的是UnsafeViewBackward这步操作里面数据的梯度信息,取不到MmBackward这步操作里面数据的梯度信息,即权重的反向梯度数据。典型的例子有,当linear的输入不是二维,且无bias时,会调用output = input.matmul(weight.t()),因此拿不到linear层的weight的反向梯度数据。

  7. dump.json文件中的某些API的dtype类型为float16,但是读取此API的npy文件显示的dtype类型为float32。

    答:msProbe工具在dump数据时需要将原始数据从NPU to CPU上再转换为numpy类型,NPU to CPU的逻辑和GPU to CPU是保持一致的,都存在dtype可能从float16变为float32类型的情况,如果出现dtype不一致的问题,最终采集数据的dtype以pkl文件为准。

  8. 使用dataloader后raise异常Exception("msprobe: exit after iteration {}". format(max(self.config.step)))。

    答:正常现象,dataloader通过raise结束程序,堆栈信息可忽略。

  9. 使用msProbe工具数据采集功能后,模型出现报错,报错信息为:activation_func must be F.geluValueError(Only support fusion of gelu and swiglu)

    答:这一类报错常见于Megatron/MindSpeed/ModelLink等加速库或模型仓中,原因是工具本身会封装torch的API(API类型和地址会发生改变),而有些API在工具使能前类型和地址就已经确定,此时工具无法对这类API再进行封装,而加速库中会对某些API进行类型检查,即会把工具无法封装的原始的API和工具封装之后的API进行判断,所以会报错。 规避方式包含如下三种:

    • 将PrecisionDebugger的实例化放在文件的开始位置,即导包后的位置,确保所有API都被封装。

    • 注释MindStudio-Probe/python/msprobe/pytorch/dump/api_dump/support_wrap_ops.yaml文件中的-gelu或者-silu,工具会跳过采集该API。

    • 可以考虑根据报错堆栈信息注释引发报错的类型检查。

  10. 添加msProbe工具后触发与AsStrided算子相关、或者编译相关的报错,如:Failed to compile Op [AsStrided]

    答:注释工具目录MindStudio-Probe/python/msprobe/pytorch/dump/api_dump/support_wrap_ops.yaml文件中Tensor:下的- t- transpose