# MindSpore 场景的精度数据采集

msprobe 工具主要通过在训练脚本内添加 dump 接口、启动训练的方式采集精度数据。目前，静态图场景仅支持 kernel 级数据采集，对应 config.json 配置中的 "L2" level；动态图场景支持cell、API、kernel级数据采集，对应 config.json 配置中的 "L0"、"L1" 、"L2"、"mix" level。

需要注意，**动态图 kernel 级**（"L2" level）精度数据采集对象为被 PSJit 或 PIJit 装饰的 Cell 或 function 内的算子，其被装饰部分实际以**静态图**模式执行，所以此场景下的工具使用方式与静态图场景完全相同。下文无特殊说明时，介绍的动态图场景不包括 kernel 级（"L2" level）dump 情形。

精度数据采集功能的配置示例见[MindSpore 静态图场景下 task 配置为 statistics](https://gitee.com/ascend/mstt/blob/master/debug/accuracy_tools/msprobe/docs/03.config_examples.md#21-task-%E9%85%8D%E7%BD%AE%E4%B8%BA-statistics)、[MindSpore 静态图场景下 task 配置为 tensor](https://gitee.com/ascend/mstt/blob/master/debug/accuracy_tools/msprobe/docs/03.config_examples.md#22-task-%E9%85%8D%E7%BD%AE%E4%B8%BA-tensor)、[MindSpore 动态图场景下 task 配置为 statistics](https://gitee.com/ascend/mstt/blob/master/debug/accuracy_tools/msprobe/docs/03.config_examples.md#31-task-%E9%85%8D%E7%BD%AE%E4%B8%BA-statistics)、[MindSpore 动态图场景下 task 配置为 tensor](https://gitee.com/ascend/mstt/blob/master/debug/accuracy_tools/msprobe/docs/03.config_examples.md#32-task-%E9%85%8D%E7%BD%AE%E4%B8%BA-tensor)。

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

```yaml
ops:  # ops为算子类别，找到对应的类别，在该类别下按照下列格式删除或添加API
  - adaptive_avg_pool1d
  - adaptive_avg_pool2d
  - adaptive_avg_pool3d
```

## 1 接口介绍

### 1.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)。

#### 1.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 时，传入 model 可以采集 model 内的所有Cell 对象数据。API级别（"L1" level）dump 时，传入 model 可以采集 model 内包含 primitive op 对象在内的所有 API 数据，若不传入 model 参数，则只采集非 primitive op 的 API 数据。

#### 1.1.2 stop

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

**原型**：

```Python
stop()
```

#### 1.1.3 step

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

**原型**：

```Python
step()
```

#### 1.1.4 forward_backward_dump_end

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

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

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

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

**原型**：

```Python
forward_backward_dump_end()
```

### 1.2 msprobe.mindspore.common.utils.MsprobeStep

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

**原型**：

```Python
MsprobeStep(debugger)
```

**参数说明**:

1. debugger：PrecisionDebugger对象。

### 1.3 msprobe.mindspore.seed_all

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

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

**参数说明**:

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执行性能降低，建议在发现模型多次执行结果不同的情况下开启。

## 2 示例代码

### 2.1 MindSpore 静态图场景

```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调用后
# ...
```

### 2.2 MindSpore 动态图场景

#### 2.2.1 未使用 Model 高阶 API(非 L2 级别)

```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级别或L1级别下primitive op的数据采集时调用
    # 如下是模型每个step执行的逻辑
    grad_net = ms.grad(model)(data)
    # ...
    debugger.stop()         # 关闭数据dump
    debugger.step()         # 结束一个step的dump
```

#### 2.2.2 未使用 Model 高阶 API(L2 级别)

```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)
    # ...
```

#### 2.2.3 使用 Model 高阶 API(非 L2 级别)

```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对象或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)])
```

#### 2.2.4 使用 Model 高阶 API(L2 级别)

```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)
```

## 3 dump 结果文件介绍

### 3.1 MindSpore 静态图场景

训练结束后，工具将 dump 的数据保存在 dump_path 参数指定的目录下。

- jit_level 为O0/O1时：

  dump 结果目录请参见 MindSpore 官网中的[同步 Dump 数据对象目录](https://www.mindspore.cn/tutorials/experts/zh-CN/r2.3.1/debug/dump.html#%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1%E7%9B%AE%E5%BD%95%E5%92%8C%E6%95%B0%E6%8D%AE%E6%96%87%E4%BB%B6%E4%BB%8B%E7%BB%8D)。

- jit_level 为O2时：

  dump 结果目录请参见 MindSpore 官网中的[异步 Dump 数据对象目录](https://www.mindspore.cn/tutorials/experts/zh-CN/r2.3.1/debug/dump.html#%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1%E7%9B%AE%E5%BD%95%E5%92%8C%E6%95%B0%E6%8D%AE%E6%96%87%E4%BB%B6%E4%BB%8B%E7%BB%8D-1)。

jit_level 请参见 [mindspore.set_context](https://www.mindspore.cn/docs/zh-CN/r2.3.1/api_python/mindspore/mindspore.set_context.html) 中的 jit_config 参数。

### 3.2 MindSpore 动态图场景

训练结束后，工具将 dump 的数据保存在dump_path参数指定的目录下。

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

```bash
├── 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
|   |   |   |    └── Cell.relu.ReLU.forward.0.input.0.npy        # config.json文件配置level为L0时dump的cell模块级数据，命名格式为{Cell}_{cell_name}_{class_name}_{前向反向}.{index}.{input/output}.{参数序号}
│   |   |   ├── dump.json        # 保存前反向算子、算子的统计量信息或溢出算子信息。包含dump数据的API名称（命名格式为：{api_type}_{api_name}_{API调用次数}_{前向反向}_{input/output}.{参数序号}）、dtype、 shape、各数据的max、min、mean、L2norm统计信息以及当配置summary_mode="md5"时的md5数据。其中，“参数序号”表示该API下的第n个参数，例如1，则为第一个参数，若该参数为list格式，则根据list继续排序，例如1.1，表示该API的第1个参数的第1个子参数；L2norm表示L2范数（平方根）
│   |   |   ├── stack.json        # 算子调用栈信息
│   |   |   └── construct.json        # 分层分级结构，level为L1时，construct.json内容为空
│   |   ├── rank1
|   |   |   ├── dump_tensor_data
|   |   |   |   └── ...
│   |   |   ├── dump.json
│   |   |   ├── stack.json
|   |   |   └── construct.json
│   |   ├── ...
│   |   |
|   |   └── rank7
│   ├── step1
│   |   ├── ...
│   ├── step2
```

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

其中 rank 为设备上各卡的 ID，每张卡上 dump 的数据会生成对应 dump 目录。非分布式场景下没有 rank ID，目录名称为 rank。

动态图场景下使能 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 文件保存的前缀和 MindSpore 对应关系如下：

| 前缀           | MindSpore 模块                |
| -------------- | ---------------------------- |
| Tensor         | mindspore.Tensor             |
| Functional     | mindspore.ops                |
| Primitive      | mindspore.ops.Primitive      |
| Mint           | mindspore.mint               |
| MintFunctional | mindspore.mint.nn.functional |
| Jit            | mindspore.jit                |
| Cell           | mindspore.nn.Cell            |
