
# msprobe 工具 MindSpore场景精度数据采集指南


## 1. 专业名词解释

* **静态图**：在编译时就确定网络结构，静态图模式拥有较高的训练性能，但难以调试。
* **动态图**：运行时动态构建网络，相较于静态图模式虽然易于调试，但难以高效执行。
* **高阶 API**：如 `mindspore.train.Model`，封装了训练过程的高级接口。
* **JIT（Just-In-Time 编译）**：MindSpore提供JIT（just-in-time）技术进一步进行性能优化。JIT模式会通过AST树解析的方式或者Python字节码解析的方式，将代码解析为一张中间表示图（IR，intermediate representation）。IR图作为该代码的唯一表示，编译器通过对该IR图的优化，来达到对代码的优化，提高运行性能。与动态图模式相对应，这种JIT的编译模式被称为静态图模式。
* **Primitive op**：MindSpore 中的基本算子，通常由 `mindspore.ops.Primitive` 定义，提供底层的算子操作接口。


## 2. 工具安装

请参见[《msprobe 工具安装指南》](./01.installation.md)。


## 3. 快速入门

以下通过一个简单的示例，展示如何在 MindSpore 中使用 msprobe 工具进行精度数据采集。

您可以参考 [动态图快速入门示例](./data_dump_Mindspore/dynamic_graph_quick_start_example.md)  了解详细步骤。

## 4. 概述

msprobe 工具通过在训练脚本中添加 `PrecisionDebugger` 接口并启动训练的方式，采集模型在运行过程中的精度数据。该工具支持对MindSpore的静态图和动态图场景进行不同Level等级的精度数据采集。


## 5. 场景介绍 

### 5.1 静态图场景 
在静态图场景下，msprobe 仅支持 **L2 Level**  的数据采集。 
- **L2 Level（Kernel 级）** ：采集底层算子的输入输出数据，适用于深入分析算子级别的精度问题。

采集方式请参见[示例代码 > 静态图场景](#71-静态图场景)。详细介绍请参见[《config.json 配置文件介绍》](./02.config_introduction.md#11-通用配置)中的“level 参数”和[《config.json 配置示例》](./03.config_examples.md#2-mindspore-静态图场景) 中的“MindSpore 静态图场景”。

### 5.2 动态图场景 
在动态图场景下，msprobe 支持 **L0** 、**L1** 、**mix** 、**L2 Level**  的数据采集，具体分为以下几种情况： 
- **使用高阶 API（如 `Model 高阶API`）** ： 
  - 需要使用 `MsprobeStep` 回调类来控制数据采集的启停，适用于 **L0** 、**L1** 、**mix** 、**L2** 数据采集。
 
- **未使用高阶 API** ： 
  - 手动在训练循环中调用 `start`、`stop`、`step` 等接口，适用于 **L0** 、**L1** 、**mix** 、**L2** 数据采集。

采集方式请参见[示例代码 > 动态图场景](#72-动态图场景-)。

> **注意** ：动态图模式下，使用 `PSJit` 或 `PIJit` 装饰的部分实际以静态图模式执行，此时的 **Kernel 级（L2 Level）**  数据采集方式与静态图场景相同。 
- **L0 Level（Cell 级）** ：采集 `Cell` 对象的数据，适用于需要分析特定网络模块的情况。
 
- **L1 Level（API 级）** ：采集 MindSpore API 的输入输出数据，适用于定位 API 层面的精度问题。

- **mix（模块级 + API 级）** ：在 `L0` 和 `L1` 级别的基础上同时采集模块级和 API 级数据，适用于需要分析模块和 API 层面精度问题的场景。


详细介绍请参见[《config.json 配置文件介绍》](./02.config_introduction.md#11-通用配置)中的“level 参数”和[《config.json 配置示例》](./03.config_examples.md#3-mindspore-动态图场景) 中的“MindSpore 动态图场景”。


## 6 接口介绍

### 6.1 msprobe.mindspore.PrecisionDebugger

**功能说明**：通过加载 dump 配置文件的方式来确定 dump 操作的详细配置。

**原型**：

```Python
PrecisionDebugger(config_path=None)
```

**参数说明**:

1. config_path：指定 dump 配置文件路径，string 类型。参数示例："./config.json"。未配置该路径时，默认使用 [config.json](../config.json) 文件的默认配置，配置选项含义可见 [config.json 介绍](./02.config_introduction.md)。

#### 6.1.1 start

**功能说明**：启动精度数据采集。需在模型执行模式（静态图/动态图、O0/O1/O2编译等级）设置后调用。静态图场景下，必须在模型初始化及 mindspore.communication.init 调用前添加；动态图场景下，如果没有使用 [Model](https://gitee.com/link?target=https%3A%2F%2Fwww.mindspore.cn%2Ftutorials%2Fzh-CN%2Fr2.3.1%2Fadvanced%2Fmodel.html) 高阶 API 进行训练，则需要与 stop 函数一起添加在 for 循环内，否则只有需要传入model参数时，才使用该接口。

**原型**：

```Python
start(model=None)
```

**参数说明**:

1. model：指具体的 mindspore.nn.Cell对象，默认不配置。Cell级别（"L0" level）dump 与 "mix" level dump 时，必须传入 model 才可以采集 model 内的所有Cell 对象数据。API级别（"L1" level）dump 时，传入 model 可以采集 model 内包含 primitive op 对象在内的所有 API 数据，若不传入 model 参数，则只采集非 primitive op 的 API 数据。

#### 6.1.2 stop

**功能说明**：停止数据采集。在 **start** 函数之后的任意位置添加。需要与 start 函数一起添加在 for 循环内。若需要 dump 反向数据，则需要添加在反向计算代码之后。**仅未使用 Model 高阶 API 的动态图场景支持。**

**原型**：

```Python
stop()
```

#### 6.1.3 step

**功能说明**：在最后一个 **stop** 函数后或一个 step 训练结束的位置添加。**仅未使用 Model 高阶 API 的动态图场景支持。**

**原型**：

```Python
step()
```

#### 6.1.4 forward_backward_dump_end

**功能说明**：在 **start** 函数和在 **stop** 函数之间调用，表示采集 **start** 到 **forward_backward_dump_end**之间的L1级别的正反向数据。

**仅支持L1级别数据采集场景。**

**L1级别数据中的jit数据采集行为不受此接口影响。**

**仅未使用 Model 高阶 API 的动态图场景支持。**

**原型**：

```Python
forward_backward_dump_end()
```

### 6.2 msprobe.mindspore.common.utils.MsprobeStep

**功能说明**：MindSpore Callback类，自动在每个step开始时调用start()接口，在每个step结束时调用stop()、step()接口。实现使用 Model 高阶 API 的动态图场景下 L0、L1、mix 级别的精度数据采集控制，控制粒度为单个 **Step** ，而 PrecisionDebugger.start, PrecisionDebugger.stop 接口的控制粒度任意训练代码段。

**原型**：

```Python
MsprobeStep(debugger)
```

**参数说明**:

1. debugger：PrecisionDebugger对象。

### 6.3 msprobe.mindspore.seed_all

**功能说明**：用于固定网络中的随机性和开启确定性计算。

**原型**：
```python
seed_all(seed=1234, mode=False, rm_dropout=True)
```

**参数说明**:

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

2. mode：确定性计算使能，可配置 True 或 False，默认值：False，非必选。参数示例：mode=True。该参数设置为 True 后，将会开启算子确定性运行模式与归约类通信算子（AllReduce、ReduceScatter、Reduce）的确定性计算。注意：确定性计算会导致API执行性能降低，建议在发现模型多次执行结果不同的情况下开启。

3. rm_dropout：控制dropout失效的开关。可配置 True 或 False，默认值：True，非必选。参数示例：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实例前调用才能生效。




## 7. 示例代码

### 7.1 静态图场景

```python
import mindspore as ms
ms.set_context(mode=ms.GRAPH_MODE, device_target="Ascend")

from msprobe.mindspore import PrecisionDebugger
debugger = PrecisionDebugger(config_path="./config.json")
debugger.start()
# 请勿将以上初始化流程置于模型实例化或 mindspore.communication.init 调用后
# 模型定义和训练代码
# ...

```

### 7.2 动态图场景 

#### 7.2.1 L0 ,L1, mix 级别

##### 7.2.1.1 未使用 Model 高阶 API 


```python
import mindspore as ms
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="Ascend")

from msprobe.mindspore import PrecisionDebugger
debugger = PrecisionDebugger(config_path="./config.json")

# 模型、损失函数的定义以及初始化等操作
# ...
model = Network()
# 数据集迭代的地方往往是模型开始训练的地方
for data, label in data_loader:
    debugger.start()        # 进行 L1 级别下非 primitive op 采集时调用
    # debugger.start(model) # 进行 L0, mix 级别或 L1 级别下 primitive op 的数据采集时调用
    # 如下是模型每个 step 执行的逻辑
    grad_net = ms.grad(model)(data)
    # ...
    debugger.stop()         # 关闭数据 dump
    debugger.step()         # 更新迭代数
```

##### 7.2.1.2 使用 Model 高阶 API 


```python
import mindspore as ms
from mindspore.train import Model
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="Ascend")

from msprobe.mindspore import PrecisionDebugger
from msprobe.mindspore.common.utils import MsprobeStep
debugger = PrecisionDebugger(config_path="./config.json")

# 模型、损失函数的定义以及初始化等操作
# ...

model = Network()
# 只有进行 L0 级别下 Cell 对象，mix 级别，L1 级别下 primitive op 的数据采集时才需要调用
# debugger.start(model)
trainer = Model(model, loss_fn=loss_fn, optimizer=optimizer, metrics={'accuracy'})
trainer.train(1, train_dataset, callbacks=[MsprobeStep(debugger)])
```

#### 7.2.2 L2 级别

##### 7.2.2.1 未使用 Model 高阶 API 


```python
import mindspore as ms
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="Ascend")

from msprobe.mindspore import PrecisionDebugger
debugger = PrecisionDebugger(config_path="./config.json")
debugger.start()
# 请勿将以上初始化流程置于模型实例化或 mindspore.communication.init 调用后

# 模型、损失函数的定义以及初始化等操作
# ...
model = Network()
# 数据集迭代的地方往往是模型开始训练的地方
for data, label in data_loader:
    # 如下是模型每个 step 执行的逻辑
    grad_net = ms.grad(model)(data)
    # ...
```


##### 7.2.2.2 使用 Model 高阶 API 


```python
import mindspore as ms
from mindspore.train import Model
ms.set_context(mode=ms.PYNATIVE_MODE, device_target="Ascend")

from msprobe.mindspore import PrecisionDebugger
debugger = PrecisionDebugger(config_path="./config.json")
debugger.start()
# 请勿将以上初始化流程置于模型实例化或 mindspore.communication.init 调用后

# 模型、损失函数的定义以及初始化等操作
# ...

model = Network()
trainer = Model(model, loss_fn=loss_fn, optimizer=optimizer, metrics={'accuracy'})
trainer.train(1, train_dataset)
```

## 8. dump 结果文件介绍

### 8.1 静态图场景

训练结束后，数据将保存在 `dump_path` 指定的目录下，目录结构请参见 MindSpore 官方文档中的[数据对象目录](https://www.mindspore.cn/docs/zh-CN/r2.4.0/model_train/debug/dump.html)。

### 8.2 动态图场景

dump 结果目录结构示例如下：

```lua
├── dump_path
│   ├── step0
│   |   ├── rank0
│   |   │   ├── dump_tensor_data
|   |   |   |    ├── MintFunctional.relu.0.backward.input.0.npy
|   |   |   |    ├── Mint.abs.0.forward.input.0.npy
|   |   |   |    ├── Functional.split.0.forward.input.0.npy
|   |   |   |    ├── Tensor.__add__.0.forward.output.0.npy
|   |   |   |    ...
|   |   |   |    ├── Jit.AlexNet.0.forward.input.0.npy
|   |   |   |    ├── Primitive.conv2d.Conv2D.0.forward.input.0.npy
|   |   |   |    └── Cell.relu.ReLU.forward.0.input.0.npy        
│   |   |   ├── dump.json        # 保存前反向API、API的统计量信息或溢出API信息。包含dump数据的API名称或Cell名称（Api命名格式为：{api_type}_{api_name}_{API调用次数}_{前向反向}_{input/output}.{参数序号}，Cell命名格式为：{Cell}_{cell_name}_{class_name}_{前向反向}.{index}.{input/output}.{参数序号}）、dtype、 shape、各数据的max、min、mean、L2norm统计信息以及当配置summary_mode="md5"时的CRC-32数据。其中，“参数序号”表示该API或Cell下的第n个参数，例如1，则为第一个参数，若该参数为list格式，则根据list继续排序，例如1.1，表示该API或Cell的第1个参数的第1个子参数；L2norm表示L2范数（平方根）。
│   |   |   ├── stack.json        # API 调用栈信息
│   |   |   └── construct.json        # 分层分级结构，level为L1时，construct.json内容为空
│   |   ├── rank1
|   |   |   ├── dump_tensor_data
|   |   |   |   └── ...
│   |   |   ├── dump.json
│   |   |   ├── stack.json
|   |   |   └── construct.json
│   |   ├── ...
│   |   |
|   |   └── rank7
│   ├── step1
│   |   ├── ...
│   ├── step2
```

* `rank`：设备 ID，每张卡的数据保存在对应的 `rank{ID}` 目录下。非分布式场景下没有 rank ID，目录名称为 rank。
* `dump_tensor_data`：保存采集到的张量数据。
* `dump.json`：记录模块或 API 的统计信息。
* `stack.json`：API调用栈信息。
* `construct.json`：分层分级结构信息。

dump 过程中，npy 文件在对应API或者模块被执行后就会落盘，而 json 文件则需要在正常执行 PrecisionDebugger.stop() 后才会写入完整数据，因此，程序异常终止时，被执行API对应的 npy 文件已被保存，但 json 文件中的数据可能丢失。

动态图场景下使能 PSJit 或 PIJit，装饰特定 Cell 或 function，被装饰的部分会全部/部分使能**静态图**流程。

- PSJit 场景下 config.json 文件配置 level 为 L1 时，被 PSJit 装饰的部分也作为 API 被 dump 到对应目录；配置 level 为 L2 时，则只会 dump 用户网络中静态图流程下的相关 kernel，其结果目录同jit_level 为 O0/O1 时的静态图 dump 相同。
- PIJit 场景下 config.json 文件配置 level 为 L1 时，会被还原为动态图，按 API 粒度进行 dump；配置 level 为 L2 时，则只会 dump 用户网络中静态图流程下的相关 kernel。


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

| 前缀           | 含义                           |
| -------------- |------------------------------|
| Tensor         | mindspore.Tensor API数据             |
| Functional     | mindspore.ops API数据                |
| Primitive      | mindspore.ops.Primitive API数据      |
| Mint           | mindspore.mint API数据              |
| MintFunctional | mindspore.mint.nn.functional API数据 |
| Jit            | 被"jit"装饰的模块或函数数据                |
| Cell           | mindspore.nn.Cell 类（模块）数据           |



## 9.补充说明

### 9.1 修改 API 支持列表

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

```yaml
ops:
  - adaptive_avg_pool1d
  - adaptive_avg_pool2d
  - adaptive_avg_pool3d
```

