MSAdapter场景精度数据采集

简介

msProbe工具通过在MSAdapter模型训练脚本中添加PrecisionDebugger接口并启动训练的方式,采集模型运行过程中的精度数据。该工具支持MSAdapter场景不同level等级的精度数据采集。

采集精度数据时,"statistics"模式的性能开销与"tensor"模式采集的数据量大小与MindSpore场景类似,可以参考MindSpore场景的精度数据采集基线

注意

  • 为了正确识别MSAdapter场景,在导入msProbe工具前,需完成torch模块的导入。

  • 因MSAdapter模型底层自动微分机制的限制,dump数据中可能会缺少原地操作模块/API及其上一个模块/API的反向数据。

  • 使用msProbe工具后模型训练的loss或gnorm值可能发生变化,详见《模型计算结果改变原因分析》。

基本概念

  • MSAdapter:一款MindSpore生态适配工具,可以将PyTorch训练脚本高效迁移至MindSpore框架执行,以实现在不改变原有PyTorch用户开发习惯的情况下,使得PyTorch代码能在昇腾环境上获得高效性能。

  • dump:采集精度数据,并完成数据持久化的过程。

使用前准备

环境准备

安装msProbe工具,详情请参见《msProbe安装指南》。

约束

仅支持MSAdapter框架。

快速入门

  1. 配置文件创建

    在当前目录下创建config.json文件,用于配置dump参数。文件内容如下:

    {
        "task": "statistics",
        "dump_path": "./output",
        "rank": [],
        "step": [0, 1],
        "level": "L0",
        "statistics": {
            "scope": [],
            "list": [],
            "data_mode": ["all"],
            "summary_mode": "statistics"
        }
    }
    

    配置参数详细介绍请参见《config.json配置文件介绍》。

  2. 模型训练脚本编写

    在当前目录下创建一个Python脚本文件,例如net.py。脚本内容如下:

    import mindspore as ms
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    # 导入工具的数据采集接口
    from msprobe.mindspore import PrecisionDebugger
    
    # 在模型训练开始前实例化PrecisionDebugger
    debugger = PrecisionDebugger(config_path='./config.json')
    
    
    # 定义网络
    class Net(nn.Module):
        def __init__(self) -> None:
            super().__init__()
            self.linear1 = nn.Linear(in_features=8, out_features=4)
            self.linear2 = nn.Linear(in_features=4, out_features=2)
    
        def forward(self, x):
            x1 = self.linear1(x)
            x2 = self.linear2(x1)
            logits = F.relu(x2)
            return logits
    
    
    net = Net()
    
    
    def train_step(inputs):
        return net(inputs)
    
    
    if __name__ == "__main__":
        data = (torch.randn(10, 8), torch.randn(10, 8), torch.randn(10, 8))
        grad_fn = ms.value_and_grad(train_step, grad_position=0)
    
        for inputs in data:
            # 开启数据 dump
            debugger.start(model=net)
    
            out, grad = grad_fn(inputs)
    
            # 停止数据 dump
            debugger.stop()
            # 更新 step 信息
            debugger.step()
    
  3. 模型训练

    在命令行中执行以下命令:

    python net.py
    

    工具将会采集模型训练过程中的精度数据。

  4. 查看采集结果

    出现以下回显信息后,说明数据采集完成,即可手动停止模型训练查看采集数据。

    ****************************************************************************
    *                        msprobe ends successfully.                        *
    ****************************************************************************
    

MSAdapter dump功能介绍

功能说明

在MSAdapter场景下,msProbe工具支持L0L1mix三个level等级的精度数据采集。

  • L0 level:采集模块(nn.Module)对象的输入输出数据,适用于需要分析特定网络模块的场景。

  • L1 level:采集API的输入输出数据,适用于需要定位API层面精度问题的场景。

  • mix level:同时采集模块级和API级数据,适用于需要分析模块和API层面精度问题的场景。

因MSAdapter模型底层使用MindSpore框架,所以采集精度数据时使用的msProbe工具接口与MindSpore动态图场景下使用的dump接口一致,分别为:

  • msprobe.mindspore.seed_all:用于固定网络中的随机性和开启确定性计算。

  • msprobe.mindspore.PrecisionDebugger:通过加载dump配置文件的方式来确定dump操作的详细配置。

  • msprobe.mindspore.PrecisionDebugger.start:启动精度数据采集。

  • msprobe.mindspore.PrecisionDebugger.stop:停止精度数据采集。

  • msprobe.mindspore.PrecisionDebugger.step:结束一个step的数据采集,完成所有数据落盘并更新dump参数。

dump接口详细介绍请参见接口介绍章节。

注意事项

使用示例

  1. 创建config.json dump配置文件,用于配置dump参数。文件内容如下:

    {
        "task": "statistics",
        "dump_path": "./output",
        "rank": [],
        "step": [],
        "level": "L0",
        "statistics": {
            "scope": [],
            "list": [],
            "data_mode": ["all"],
            "summary_mode": "statistics"
        }
    }
    

    dump参数详细介绍请参见《config.json配置文件介绍》。

  2. 编写模型训练脚本net.py,并在脚本中插入msprobe工具dump接口。脚本内容如下:

    import mindspore as ms
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    # 导入工具的数据采集接口
    from msprobe.mindspore import PrecisionDebugger, seed_all
    
    # 在模型训练开始前固定随机性
    seed_all()
    # 在模型训练开始前实例化PrecisionDebugger
    debugger = PrecisionDebugger(config_path='./config.json')
    
    
    # 定义网络
    class Net(nn.Module):
        def __init__(self) -> None:
            super().__init__()
            self.linear1 = nn.Linear(in_features=8, out_features=4)
            self.linear2 = nn.Linear(in_features=4, out_features=2)
    
        def forward(self, x):
            x1 = self.linear1(x)
            x2 = self.linear2(x1)
            logits = F.relu(x2)
            return logits
    
    
    net = Net()
    
    
    def train_step(inputs):
        return net(inputs)
    
    
    if __name__ == "__main__":
        data = (torch.randn(10, 8), torch.randn(10, 8), torch.randn(10, 8))
        grad_fn = ms.value_and_grad(train_step, grad_position=0)
    
        for inputs in data:
            # 开启数据 dump
            debugger.start(model=net)
    
            out, grad = grad_fn(inputs)
    
            # 停止数据 dump
            debugger.stop()
            # 更新 step 信息
            debugger.step()
    
  3. 执行模型训练脚本,开始模型训练。执行命令如下:

    python net.py
    

    工具将会采集模型训练过程中的精度数据。

输出说明

完成精度数据采集后,将打印dump数据文件生成路径。

dump.json is at <dump_path>/step<N>
  • <dump_path>:dump配置文件中“dump_path”参数设置的dump数据输出路径。

  • step<N>:第N+1个step,例如step0,表示该目录存放第1个step的所有dump数据。

dump数据的具体介绍请参见dump输出文件说明章节。

dump输出文件说明

输出文件目录结构

MSAdapter场景下,dump输出文件的目录结构示例如下:

├── dump_path
│   ├── step0
│   |   ├── rank0
│   |   │   ├── dump_tensor_data
|   |   |   |    ├── Tensor.permute.1.forward.npy
|   |   |   |    ├── Functional.linear.5.backward.output.npy    # 命名格式为{api_type}.{api_name}.{API调用次数}.{forward/backward}.{input/output}.{参数序号}, 其中,“参数序号”表示该API的第n个输入或输出,例如1,则为第一个参数,若该参数为list格式,则根据list继续排序,例如1.1,表示该API的第1个参数的第1个元素。
|   |   |   |    ...
|   |   |   |    ├── Module.conv1.Conv2d.forward.0.input.0.npy          # 命名格式为{Module}.{module_name}.{class_name}.{forward/backward}.{调用次数}.{input/output}.{参数序号}, 其中,“参数序号”表示该Module的第n个参数,例如1,则为第一个参数,若该参数为list格式,则根据list继续排序,例如1.1,表示该Module的第1个参数的第1个元素。
|   |   |   |    ├── Module.conv1.Conv2d.forward.0.parameters.bias.npy  # 模块参数数据:命名格式为{Module}.{module_name}.{class_name}.forward.{调用次数}.parameters.{parameter_name}。
|   |   |   |    └── Module.conv1.Conv2d.parameters_grad.0.weight.npy     # 模块参数梯度数据:命名格式为{Module}.{module_name}.{class_name}.parameters_grad.{parameter_name}, 其中,参数梯度中的计数是参数梯度的计算次数,不是模块的调用次数。
|   |   |   |                                                          # 当dump时传入的model参数为List[torch.nn.Module]或Tuple[torch.nn.Module]时,模块级数据的命名中包含该模块在列表中的索引index,命名格式为{Module}.{index}.*,*表示以上三种模块级数据的命名格式,例如:Module.0.conv1.Conv2d.forward.0.input.0.npy。
│   |   |   ├── dump.json
│   |   |   ├── stack.json
│   |   |   └── construct.json
│   |   ├── rank1
|   |   |   ├── dump_tensor_data
|   |   |   |   └── ...
│   |   |   ├── dump.json
│   |   |   ├── stack.json
|   |   |   └── construct.json
│   |   ├── ...
│   |   |
|   |   └── rank7
│   ├── step1
│   |   ├── ...
│   ├── step2
  • rank:设备ID,每张卡的数据保存在对应的rank{ID}目录下。非分布式场景下没有rank ID,目录名称为proc{pid},pid为进程ID。

  • dump_tensor_data:保存采集到的张量数据。

  • dump.json: 保存API或Module输入输出数据的统计量信息。包含dump数据的API名称或Module名称,各数据的dtype、 shape、max、min、mean、L2norm(L2范数,平方根)统计信息以及当配置 summary_mode="md5" 时的CRC-32 数据。

  • dump_error_info.log:仅在dump工具报错时拥有此记录日志,用于记录dump错误日志。

  • stack.json:API/Module的调用栈信息。

  • construct.json:分层分级结构,level为L1时,construct.json内容为空。

当 dump配置文件中的“task”参数为“tensor”时,dump过程中,npy文件在对应算子或者模块被执行后就会落盘,而json文件则需要在正常执行PrecisionDebugger.stop()后才会写入完整数据。因此如果程序异常终止,终止前被执行算子的相关npy文件得以保存,但json文件中的数据可能丢失。

npy 文件名的前缀含义如下:

前缀 含义
Tensor torch.Tensor API数据。
Torch torch API数据。
Functional torch.nn.functional API数据。
NPU NPU亲和API数据。
Distributed torch.distributed API数据。
Module torch.nn.Module 类(模块)数据。
Jit 被 "jit" 装饰的模块或函数数据。
Primitive mindspore.ops.Primitive API数据。

dump.json文件说明

L0级别

L0级别的dump.json文件中包含了模块的输入输出、参数以及参数梯度数据。以 Conv2d 模块为例,网络中模块调用代码为: output = self.conv2(input) # self.conv2 = torch.nn.Conv2d(64, 128, 5, padding=2, bias=True)

dump.json文件中包含以下数据名称:

  • Module.conv2.Conv2d.forward.0:模块的前向数据,其中input_args为模块的输入数据(位置参数),input_kwargs为模块的输入数据(关键字参数),output为模块的输出数据,parameters为模块的参数数据,包括权重(weight)和偏置(bias)。

  • Module.conv2.Conv2d.parameters_grad.0:模块的参数梯度数据,包括权重(weight)和偏置(bias)的梯度。

  • Module.conv2.Conv2d.backward.0:模块的反向数据,其中input为模块反向的输入梯度(对应前向输出的梯度),output为模块的反向输出梯度(对应前向输入的梯度)。

说明:当dump时传入的model参数为List[torch.nn.Module]或Tuple[torch.nn.Module]时,模块级数据的命名中包含该模块在列表中的索引index,命名格式为{Module}.{index}.*,*表示以上三种模块级数据的命名格式,例如:Module.0.conv1.Conv2d.forward.0

{
 "task": "tensor",
 "level": "L0",
 "framework": "mindtorch",
 "dump_data_dir": "/dump/path",
 "data": {
  "Module.conv2.Conv2d.forward.0": {
   "input_args": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      8,
      16,
      14,
      14
     ],
     "Max": 1.638758659362793,
     "Min": 0.0,
     "Mean": 0.2544615864753723,
     "Norm": 70.50277709960938,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.input.0.npy"
    }
   ],
   "input_kwargs": {},
   "output": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      8,
      32,
      10,
      10
     ],
     "Max": 1.6815717220306396,
     "Min": -1.5120246410369873,
     "Mean": -0.025344856083393097,
     "Norm": 149.65576171875,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.output.0.npy"
    }
   ],
   "parameters": {
    "weight": {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      5,
      5
     ],
     "Max": 0.05992485210299492,
     "Min": -0.05999220535159111,
     "Mean": -0.0006165213999338448,
     "Norm": 3.421217441558838,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.parameters.weight.npy"
    },
    "bias": {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32
     ],
     "Max": 0.05744686722755432,
     "Min": -0.04894155263900757,
     "Mean": 0.006410328671336174,
     "Norm": 0.17263513803482056,
     "requires_grad": true,
     "data_name": "Module.conv2.Conv2d.forward.0.parameters.bias.npy"
    }
   }
  },
  "Module.conv2.Conv2d.parameters_grad.0": {
   "weight": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      5,
      5
     ],
     "Max": 0.018550323322415352,
     "Min": -0.008627401664853096,
     "Mean": 0.0006675920449197292,
     "Norm": 0.26084786653518677,
     "requires_grad": false,
     "data_name": "Module.conv2.Conv2d.parameters_grad.0.weight.npy"
    }
   ],
   "bias": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32
     ],
     "Max": 0.014914230443537235,
     "Min": -0.006656786892563105,
     "Mean": 0.002657240955159068,
     "Norm": 0.029451673850417137,
     "requires_grad": false,
     "data_name": "Module.conv2.Conv2d.parameters_grad.0.bias.npy"
    }
   ]
  },
  "Module.conv2.Conv2d.backward.0": {
   "input": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      8,
      32,
      10,
      10
     ],
     "Max": 0.0015069986693561077,
     "Min": -0.001139344065450132,
     "Mean": 3.3215508210560074e-06,
     "Norm": 0.020567523315548897,
     "requires_grad": false,
     "data_name": "Module.conv2.Conv2d.backward.0.input.0.npy"
    }
   ],
   "output": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      8,
      16,
      14,
      14
     ],
     "Max": 0.0007466732058674097,
     "Min": -0.00044813455315306783,
     "Mean": 6.814070275140693e-06,
     "Norm": 0.01474067009985447,
     "requires_grad": false,
     "data_name": "Module.conv2.Conv2d.backward.0.output.0.npy"
    }
   ]
  }
 }
}

L1级别

L1级别的dump.json文件中包含了API的输入输出数据。以relu API为例,网络中API调用代码为: output = torch.nn.functional.relu(input)

dump.json文件中包含以下数据名称:

  • Functional.relu.0.forward:API的前向数据,其中input_args为API的输入数据(位置参数),input_kwargs为API的输入数据(关键字参数),output为API的输出数据。

  • Functional.relu.0.backward:API的反向数据,其中input为API的反向输入梯度(对应前向输出的梯度),output为API的反向输出梯度(对应前向输入的梯度)。

{
 "task": "tensor",
 "level": "L1",
 "framework": "mindtorch",
 "dump_data_dir":"/dump/path",
 "data": {
  "Functional.relu.0.forward": {
   "input_args": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 1.3864083290100098,
     "Min": -1.3364859819412231,
     "Mean": 0.03711778670549393,
     "Norm": 236.20692443847656,
     "requires_grad": true,
     "data_name": "Functional.relu.0.forward.input.0.npy"
    }
   ],
   "input_kwargs": {},
   "output": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 1.3864083290100098,
     "Min": 0.0,
     "Mean": 0.16849493980407715,
     "Norm": 175.23345947265625,
     "requires_grad": true,
     "data_name": "Functional.relu.0.forward.output.0.npy"
    }
   ]
  },
  "Functional.relu.0.backward": {
   "input": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 0.0001815402356442064,
     "Min": -0.00013352684618439525,
     "Mean": 0.00011915402356442064,
     "Norm": 0.007598237134516239,
     "requires_grad": false,
     "data_name": "Functional.relu.0.backward.input.0.npy"
    }
   ],
   "output": [
    {
     "type": "mindspore.Tensor",
     "dtype": "Float32",
     "shape": [
      32,
      16,
      28,
      28
     ],
     "Max": 0.0001815402356442064,
     "Min": -0.00012117840378778055,
     "Mean": 2.0098118724831693e-08,
     "Norm": 0.006532244384288788,
     "requires_grad": false,
     "data_name": "Functional.relu.0.backward.output.0.npy"
    }
   ]
  }
 }
}  

mix级别

mix级别的dump.json文件同时包括L0和L1级别的dump数据,文件格式与上述示例相同。

附录

接口介绍

msprobe.mindspore.seed_all

功能说明

用于固定网络中的随机性和开启确定性计算。

函数原型

msprobe.mindspore.seed_all(seed=1234, mode=False, rm_dropout=False)

参数说明

  • seed (int):可选参数,随机性种子,默认值:1234。参数示例:seed=1000。该参数用于random、numpy.random、mindspore.common.Initializer、mindspore.nn.probability.distribution的随机数生成以及Python中str、bytes、datetime对象的hash算法。

  • mode (bool):可选参数,确定性计算使能,可配置True或False,默认值:False。参数示例:mode=True。该参数设置为True后,将会开启算子确定性运行模式与归约类通信算子(AllReduce、ReduceScatter、Reduce)的确定性计算。

    注意:确定性计算会导致API执行性能降低,建议在发现模型多次执行结果不同的情况下开启。

  • rm_dropout (bool):可选参数,控制dropout失效的开关。可配置True或False,默认值:False。参数示例:rm_dropout=True。该参数设置为True后,将会使mindspore.ops.Dropout,mindspore.ops.Dropout2D,mindspore.ops.Dropout3D,mindspore.mint.nn.Dropout和mindspore.mint.nn.functional.dropout失效,以避免因随机dropout造成的网络随机性。建议在采集mindspore数据前开启。

    注意:通过rm_dropout控制dropout失效或生效需要在初始化Dropout实例前调用才能生效。

返回值说明

调用示例

请参见MSAdapter dump功能介绍中的“使用示例”。

msprobe.mindspore.PrecisionDebugger

功能说明

通过加载dump配置文件的方式来确定dump操作的详细配置。

函数原型

class msprobe.mindspore.PrecisionDebugger(config_path=None, task=None, dump_path=None, level=None, step=None)

参数说明

  • config_path (str):可选参数,指定dump配置文件路径。参数示例:"./config.json"。未配置该路径时,默认使用config.json文件的默认配置,配置选项含义可见config.json介绍

  • 其他参数均在config.json文件中可配,非必选。详细配置可见config.json介绍。当参数值非None时,优先级高于config.json文件中的同名配置。

返回值说明

调用示例

请参见MSAdapter dump功能介绍中的“使用示例”。

msprobe.mindspore.PrecisionDebugger.start

功能说明

启动精度数据采集。需要与stop接口一起添加在训练迭代的for循环内。

函数原型

PrecisionDebugger.start(model=None, token_range=None)

参数说明

  • model:可选参数,指定需要采集数据的实例化模型,支持传入torch.nn.Module、list[torch.nn.Module]或Tuple[torch.nn.Module]类型,默认未配置。模块级别("L0" level)dump与"mix" level dump时,必须传入model才可以采集model内的所有Module对象数据,且若存在会进行图编译的Module对象(例如被mindspore.jit装饰的Module),则必须在第一个step训练开始前调用start接口。API级别("L1" level)dump时,传入model可以采集model内包含primitive op对象在内的所有API数据,若不传入model参数,则只采集非primitive op的API数据。token_range不为None时,必须传入model参数。

    对于复杂模型,如果仅需要监测一部分(如model.A,model.A extends torch.nn.Module),传入需要监测的部分(如model.A)即可。

    注意:传入的当前层不会被dump,工具只会dump传入层的子层级。如传入了model.A,A本身不会被dump,而是会dump A.x, A.x.xx等。

  • token_range (Tuple[int, int]):可选参数,指定推理模型采集时的token循环始末范围,支持传入[int, int]类型,代表[start, end],范围包含边界,默认未配置。

返回值说明

调用示例

请参见MSAdapter dump功能介绍中的“使用示例”。

msprobe.mindspore.PrecisionDebugger.stop

功能说明

停止精度数据采集。在start接口调用之后的任意位置添加。若stop接口添加在反向计算代码之后,则会采集start和该接口之间的前向和反向数据。 若stop接口添加在反向计算代码之前,则需要将step添加到反向计算代码之后,才能采集start和该接口之间的前向和反向数据。

stop接口必须调用,否则可能导致精度数据落盘不全。

函数原型

PrecisionDebugger.stop()

参数说明

返回值说明

调用示例

请参见MSAdapter dump功能介绍中的“使用示例”。

msprobe.mindspore.PrecisionDebugger.step

功能说明

进行训练step数的自增,完成当前step所有数据的落盘并更新dump参数。在一个step训练结束的位置添加,且必须在stop接口之后的位置调用。该接口需要配合startstop函数使用,尽量添加在反向计算代码之后,否则可能会导致反向数据丢失。

函数原型

PrecisionDebugger.step()

参数说明

返回值说明

调用示例

请参见MSAdapter dump功能介绍中的“使用示例”。

API支持列表

dump API级精度数据时,本工具提供固定的API支持列表,仅支持对列表中的API进行精度数据采集。一般情况下,无需修改该列表,而是通过config.json中的scope/list字段进行dump API指定。若需要改变API支持列表,可以在support_wrap_ops.yaml文件内手动修改,如下示例:

functional:  # functional为算子类别,找到对应的类别,在该类别下按照下列格式删除或添加API
  - conv1d
  - conv2d
  - conv3d