# PyTorch 场景的精度比对

## 1 简介

- 本节主要介绍通过命令行和比对函数的方式进行 CPU 或 GPU 与 NPU 的精度数据比对，执行精度比对操作前需要先完成 CPU 或 GPU 与 NPU 的精度数据 dump，参见 [PyTorch 场景下的数据采集](./05.data_dump_PyTorch.md)章节。

- msprobe 使用子命令 compare 进行比对，可支持单卡和多卡场景的精度数据比对。

- 比对函数均通过单独创建精度比对脚本执行，可支持单卡和多卡场景的精度数据比对。

- 工具性能：比对数据量较小时（单份文件小于 10 GB），比对速度 0.1 GB/s；比对数据量较大时，比对速度 0.3 GB/s。 推荐环境配置：独占环境，CPU 核心数 192，固态硬盘（IO 速度参考：固态硬盘 > 500 MB/s，机械硬盘 60 ~ 170 MB/s）。用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。比对速度的计算方式：两份比对文件大小/比对耗时。

**使用场景**：

- 同一模型，从 CPU 或 GPU 移植到 NPU 中存在精度下降问题，对比 NPU 芯片中的 API 计算数值与 CPU 或 GPU 芯片中的 API 计算数值，进行问题定位。
- 同一模型，进行迭代（模型、框架版本升级或设备硬件升级）时存在的精度下降问题，对比相同模型在迭代前后版本的 API 计算数值，进行问题定位。
- 以上两个场景下，当存在无法自动匹配的API和模块时，则通过用户手动指定可以比对的API或模块来自定义映射关系，进行比对。

**注意事项**：

- NPU 自研 API，在 CPU 或 GPU 侧若没有对应的 API，该 API 的 dump 数据不比对。
- NPU 与 CPU 或 GPU 的计算结果误差可能会随着模型的执行不断累积，最终会出现同一个 API 因为输入的数据差异较大而无法比对的情况。
- CPU 或 GPU 与 NPU 中两个相同的 API 会因为调用次数不同导致无法比对或比对到错误的 API，不影响整体运行，该 API 忽略。

**API 匹配条件**：

进行精度比对时，需要判断 CPU 或 GPU 的 API 与 NPU 的 API 是否可以比对，须满足以下匹配条件：

- 两个 API 的名称相同，API 命名规则：`{api_type}.{api_name}.{api调用次数}.{正反向}.{输入输出}.{index}`，如：Functional.conv2d.1.backward.input.0。
- 两个 API 输入输出的 Tensor 数量相同且各个 Tensor 的 Shape 相同。

通常满足以上两个条件，工具就认为是同一个 API，成功进行 API 的匹配，后续进行相应的计算。

## 2 精度比对操作指导

### 2.1 命令行方式

#### 2.1.1 比对命令说明

命令示例如下：

```shell
msprobe -f pytorch compare -i ./compare.json -o ./output -s
```

**完整参数说明**：

| 参数名               | 说明     | 是否必选 |
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------- |
| -i 或 --input_path   | 指定[比对文件](#214-比对文件)，str 类型。             | 是       |
| -o 或 --output_path  | 配置比对结果文件存盘目录，str 类型，默认在当前目录创建output目录。文件名称基于时间戳自动生成，格式为：`compare_result_{timestamp}.xlsx`。     | 否       |
| -s 或 --stack_mode   | 比对结果展示调用栈信息（NPU_Stack_Info）的开关，bool 类型。单卡场景开启时，需要使用[比对文件](#214-比对文件)的单卡场景配置stack_path指定stack.json文件，才能生成详细调用栈信息，否则在比对时会报错；多卡场景开启时，npu_dump目录下需要存在stack.json文件，才能生成详细调用栈信息，否则在比对时会报错。通过直接配置该参数开启，默认未配置，表示关闭。 | 否       |
| -c 或 --compare_only | 仅比对开关，bool 类型。该参数默认未配置，会启用自动精度分析，工具自动针对比对结果进行分析，识别到第一个精度可能不达标节点（在比对结果文件中的 Accuracy Reached or Not 列显示为 No），并给出问题可能产生的原因（打屏展示并生成 `advisor_{timestamp}.txt` 文件）。通过配置该参数取消自动精度分析，仅输出比对结果表格。 | 否       |
| -f 或 --fuzzy_match  | 模糊匹配，bool 类型。开启后，对于网络中同一层级且命名仅调用次数不同的 API，可匹配并进行比对。通过直接配置该参数开启，默认未配置，表示关闭。 | 否       |
| -dm或--data_mapping | 自定义映射关系比对。需要指定自定义映射文件*.yaml。自定义映射文件的格式请参见[自定义映射文件](#215-自定义映射文件)。仅[API和模块无法自动匹配场景](#213-api和模块无法自动匹配场景)需要配置。仅支持逐卡比对，即使用[比对文件](#214-比对文件)的单卡场景示例。 | 否 |

#### 2.1.2 整网比对场景

整网比对场景是包含：CPU 或 GPU 与 NPU环境的 API 计算数值的整网数据比对；相同模型不同迭代版本的 API 计算数值的整网数据比对。

支持单卡和多卡，可同时比对多卡的 dump 数据。多机场景需要每个设备单独执行比对操作。

1. 配置[config.json](../config.json)文件。

2. 参见 [PyTorch 场景下的数据采集](./05.data_dump_PyTorch.md)章节完成 CPU 或 GPU 与 NPU 的精度数据 dump。

3. 创建[比对文件](#214-比对文件)。

4. 运行命令：

   ```shell
   msprobe -f pytorch compare -i ./compare.json -o ./output -s
   ```

5. 查看比对结果，请参见 [3 精度比对结果分析](#3-精度比对结果分析)。

#### 2.1.3 API和模块无法自动匹配场景

当存在无法自动匹配的API和模块时，则用户可以通过提供自定义映射关系的配置文件来告知工具可匹配的API或模块，进行比对。

1. [config.json](../config.json)文件level配置为L0或L1、task配置为tensor或statistics并指定需要dump的API或模块名。

2. 参见[PyTorch 场景下的数据采集](./05.data_dump_PyTorch.md)章节完成 CPU 或 GPU 与 NPU 的精度数据 dump。

3. 创建[比对文件](#214-比对文件)（单卡场景示例）。

4. 运行命令：

   ```shell
   msprobe -f pytorch compare -i ./compare.json -o ./output -s -dm data_mapping.yaml
   ```

   data_mapping.yaml文件配置请参见[自定义映射文件](#215-自定义映射文件)。

   该场景不支持-f模糊匹配。

5. 查看比对结果，请参见 [3 精度比对结果分析](#3-精度比对结果分析)。

#### 2.1.4 比对文件

   以在当前目录创建 ./compare.json 为例。

   - 单卡场景示例：

     ```json
     {
     "npu_path": "./npu_dump/dump.json",
     "bench_path": "./bench_dump/dump.json",
     "stack_path": "./npu_dump/stack.json",
     "is_print_compare_log": true
     }
     ```

   - 多卡场景示例：

     ```json
     {
     "npu_path": "./npu_dump/step0",
     "bench_path": "./bench_dump/step0",
     "is_print_compare_log": true
     }
     ```

   **参数说明**：

| 参数名               | 说明                                                         | 是否必选           |
| -------------------- | ------------------------------------------------------------ | ------------------ |
| npu_path             | 配置 NPU 环境下的 dump.json 文件（单卡场景）或真实数据目录（多卡场景），str 类型。 | 是                 |
| bench_path           | 配置 CPU、GPU 或 NPU 环境下的 dump.json 文件（单卡场景）或真实数据目录（多卡场景），str 类型。 | 是                 |
| stack_path           | 配置 NPU dump 目录下的 stack.json 文件，str 类型。      | 单卡必选，多卡不选 |
| is_print_compare_log | 配置是否开启单个算子的日志打屏。可取值 true 或 false，默认为 true。关闭后则只输出常规日志，bool 类型。 | 否                 |

#### 2.1.5 自定义映射文件

文件名格式：*.yaml，*为文件名，可自定义。

文件内容格式：

```yaml
# API
{api_type}.{api_name}.{API调用次数}.{前向反向}.{input/output}.{参数序号}: {api_type}.{api_name}.{API调用次数}.{前向反向}.{input/output}.{参数序号}
# 模块
{Module}.{module_name}.{前向反向}.{index}.{input/output}.{参数序号}: {Module}.{module_name}.{前向反向}.{index}.{input/output}.{参数序号}
```

冒号左侧和右侧分别为PyTorch框架不同版本或不同芯片环境的API的名称和module模块名称。

API和模块名称请从《[PyTorch 场景的精度数据采集](05.data_dump_PyTorch.md)》中的dump.json文件获取。

文件内容示例：

```yaml
# API
NPU.npu_fusion_attention.4.forward.input.0: NPU.npu_fusion_attention.4.forward.input.0
# 模块
Module.module.language_model.embedding.word_embedding.VocabParallelEmbedding.forward.0.input.0: Module.module.language_model.embedding.word_embedding.VocabParallelEmbedding.forward.0.input.0
```

API和模块名称在dump.json文件中的“data_name”字段展示，如下图红框处所示：

![pt_dump](./img/pt_dump.png)

### 2.2 比对函数方式

#### 2.2.1 compare 函数

此函数将 CPU 或 GPU 与 NPU 的 dump 文件进行比对，仅支持单机单卡。

**函数原型**：

```Python
compare(input_param, output_path, stack_mode=False, auto_analyze=True, fuzzy_match=False)
```

**参数说明**：

| 参数名       | 说明                                                         | 是否必选 |
| ------------ | ------------------------------------------------------------ | -------- |
| input_param  | 配置 dump 数据文件及目录，dict 类型。配置参数包括：<br>        "npu_json_path"：指定 NPU dump 目录下的 dump.json 文件。<br/>**配置示例**："npu_json_path": "./npu_dump/dump.json"。<br/>        "bench_json_path"：指定 CPU、GPU 或 NPU dump 目录下的 dump.json 文件。<br/>**配置示例**："bench_json_path": "./bench_dump/dump.json"。<br/>        "stack_json_path"：指定 NPU dump 目录下的 stack.json 文件。<br/>**配置示例**："stack_json_path": "./npu_dump/stack.json"。<br/>        "is_print_compare_log"：配置是否开启单个算子的日志打屏。<br/>**配置示例**：True 或 False。 | 是       |
| output_path  | 配置比对结果文件存盘目录，str 类型。<br/>**配置示例**：'./output'。文件名称基于时间戳自动生成，格式为：`compare_result_{timestamp}.xlsx`。 | 是       |
| stack_mode   | 配置 stack_mode 的开关，bool 类型。仅当配置 stack_json_path 时需要开启。<br/>**配置示例**：stack_mode=True，默认为 False。 | 否       |
| auto_analyze | 自动精度分析，bool 类型。开启后工具自动针对比对结果进行分析，识别到第一个精度可能不达标节点（在比对结果文件中的 Accuracy Reached or Not 列显示为 No），并给出问题可能产生的原因（打屏展示并生成 advisor_{timestamp}.txt 文件）。<br/>**配置示例**：auto_analyze=False，默认为 True。 | 否       |
| fuzzy_match  | 模糊匹配，bool 类型。开启后，对于网络中同一层级且命名仅调用次数不同的 API，可匹配并进行比对。<br/>**配置示例**：fuzzy_match=True，默认为 False。 | 否       |

**函数示例**：

单机单卡场景下创建比对脚本，例如 compare.py，拷贝如下代码，具体参数请根据实际环境修改。

```Python
from msprobe.pytorch import compare
input_param={
"npu_json_path": "./npu_dump/dump.json",
"bench_json_path": "./bench_dump/dump.json",
"stack_json_path": "./npu_dump/stack.json",
"is_print_compare_log": True
}
compare(input_param, output_path="./output", stack_mode=True)
```

#### 2.2.2 compare_distributed 函数

此函数将 CPU 或 GPU 与 NPU 的 dump 文件进行比对，支持单卡和多卡，可同时比对多卡的 dump 数据。多机场景需要每个设备单独执行比对操作。可自动检索和匹配对应卡和进程所 dump 的数据文件，再调用 compare 进行比对。单机单卡时与 compare 函数二选一。

**函数原型**：

```Python
compare_distributed(npu_dump_dir, bench_dump_dir, output_path, **kwargs)
```

**参数说明**：

| 参数名         | 说明                                                         | 是否必选 |
| -------------- | ------------------------------------------------------------ | -------- |
| npu_dump_dir   | 配置 NPU 环境下的 dump 目录。str 类型。dump 数据目录须指定到 step 级。<br/>**配置示例**：'./npu_dump/step0'。 | 是       |
| bench_dump_dir | 配置 CPU、GPU 或 NPU 环境下的 dump 目录。str 类型。<br/>**配置示例**：'./gpu_dump/step0'。 | 是       |
| output_path    | 配置比对结果文件存盘目录。需要预先创建 output_path 目录。str 类型。<br/>**配置示例**：'./output'。文件名称基于时间戳自动生成，格式为：`compare_result_rank{npu_ID}-rank{cpu/gpu/npu_ID}_{timestamp}.xlsx`。 | 是       |
| **kwargs       | 支持 compare 的所有可选参数。                                  | 否       |

**函数示例**：

```Python
from msprobe.pytorch import *
compare_distributed('./npu_dump/step0', './gpu_dump/step0', './output')
```

## 3 精度比对结果分析

PyTorch 精度比对是以 CPU 或 GPU 的计算结果为标杆，通过计算精度评价指标判断 API 在运行时是否存在精度问题。

- `advisor_{timestamp}.txt` 文件中给出了可能存在精度问题的 API 的专家建议。

- `compare_result_{timestamp}.xlsx` 文件列出了所有执行精度比对的 API 详细信息和比对结果，示例如下：

  ![compare_result](./img/compare_result.png)

- **提示**：比对结果通过颜色标记、比对结果（Result）、计算精度达标情况（Accuracy Reached no Not）、错误信息提示（Err_Message）定位可疑算子，但鉴于每种指标都有对应的判定标准，还需要结合实际情况进行判断。

### 3.1 指标说明

精度比对从三个层面评估 API 的精度，依次是：真实数据模式、统计数据模式和 MD5 模式。比对结果分别有不同的表头。

**公共表头**：

|dump 数据模式|NPU Name (NPU 的 API 名)|Bench Name (bench 的 API 名)|NPU Dtype (NPU 数据类型)|Bench Dtype (bench 数据类型)|NPU Tensor Shape (NPU 张量形状)|Bench Tensor Shape (bench 张量形状)|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|真实数据模式|√|√|√|√|√|√|
|统计数据模式|√|√|√|√|√|√|
|MD5 模式|√|√|√|√|√|√|

**个性表头**：

统计量有 4 种：最大值（max）、最小值（min）、平均值（mean）和 L2-范数（L2 norm）。

|dump 数据模式|Cosine (tensor 余弦相似度)|MaxAbsErr (tensor 最大绝对误差)|MaxRelativeErr (tensor 最大相对误差)|One Thousandth Err Ratio (tensor 相对误差小于千分之一的比例)|Five Thousandth Err Ratio (tensor 相对误差小于千分之五的比例)|NPU 和 bench 的统计量绝对误差 (max, min, mean, L2 norm) diff| NPU 和 bench 的统计量相对误差 (max, min, mean, L2 norm) RelativeErr |NPU 和 bench 的统计量 (max, min, mean, L2 norm)|NPU MD5 (NPU 数据 CRC-32 值)|BENCH MD5 (bench 数据 CRC-32 值)|Result (比对结果)|Accuracy Reached or Not (计算精度是否达标)|Err_message (错误信息提示)|NPU_Stack_Info (堆栈信息)|Data_Name (NPU 真实数据名)|
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|真实数据模式|√|√|√|√|√|||√||||√|√|√|√|
|统计数据模式||||||√|√|√|||√||√|√||
|MD5 模式|||||||||√|√|√|||√||

上表中NPU_Stack_Info字段需要配置-s参数生成。

### 3.2 颜色标记——真实数据模式、统计数据模式

在比对结果中的Err_message列呈现比对结果颜色标记的原因，具体含义如下：

红色标记情况：
1. 一个 API 或模块的 One Thousandth Err Ratio 的 input > 0.9 同时 output < 0.6（真实数据模式）；
2. 一个 API 或模块的 output 的最大值相对误差 (Max diff 除以 max(0.01, Bench max)) > 0.5（统计数据模式）；
3. 一个 API 或模块的 NPU 的最大值或最小值中存在 nan/inf/-inf（真实数据模式、统计数据模式）；
4. 一个 API 或模块的最大值绝对误差大于 1e+10（真实数据模式，统计数据模式）。

黄色标记情况：
1. 一个 API 或模块的 One Thousandth Err Ratio 的 input - output > 0.1（真实数据模式）；
2. 一个 API 或模块的 Cosine 的 input - output > 0.1（真实数据模式）；
3. 一个 API 或模块的 output 的最大值相对误差 > 0.1 同时 input < 0.01（真实数据模式，统计数据模式）；
4. 一个 API 或模块的 input 与 output 的最大值绝对误差都大于 1，同时 output 比 input 大一个数量级以上（真实数据模式、统计数据模式）。

### 3.3 比对结果（Result）——统计数据模式、MD5 模式

统计数据模式：
1. Warning 情况：4种统计值至少一种相对误差 > 0.5，要重点检查该 API；
2. 空情况：相对误差 ≤ 0.5，可以不需要重点关注，但不代表不存在精度问题；
3. N/A 情况：API 没有匹配上。

MD5 模式：
1. Pass 情况：NPU 与标杆的 CRC-32 值一致，即 API 数据完全一致；
2. Different 情况：NPU 与标杆的 CRC-32 值不一致，即 API 数据不完全一致；
3. N/A 情况：API 没有匹配上。

### 3.4 判断计算精度达标情况（Accuracy Reached or Not）——真实数据模式

标记为 `No`，表示精度不达标：
1. Cosine < 0.99 且 MaxAbsError > 0.001；
2. Cosine < 0.9；
3. MaxAbsError > 1；
4. Cosine 或 MaxAbsError 计算结果为 N/A（NPU 和 Bench 的数据有 nan/inf）。

标记为 `None`：
1. Cosine 或 MaxAbsError 计算结果为 None（读取到的数据为空）；
2. Cosine 或 MaxAbsError 计算结果无法转换为浮点型。

标记为 `Unmatched`：
1. Cosine 计算结果为 `shape unmatched`（NPU 和 Bench 的数据结构不一致）。

标记为 `Yes`，表示精度达标：
1. 除以上情况外的其余情况。

### 3.5 错误信息提示（Err_message）——真实数据模式、统计数据模式

1. "Need double check api accuracy."：四个统计值中至少 1 个相对误差 > 0.5（统计数据模式）；
2. "Fuzzy matching data, the comparison arruracy may be affected."：NPU 或 Bench 的真实数据名没有匹配上（真实数据模式）；
3. "Dump file: {} not found."：NPU 真实数据不存在或者读取出错（真实数据模式）；
4. "No bench data matched."：Bench 的 API 没有匹配上、Bench 真实数据不存在或读取出错（真实数据模式）；
5. "This is empty data, can not compare."：读取到的数据为空（真实数据模式）；
6. "Shape of NPU and bench Tensor do not match. Skipped."：NPU 和 Bench 的数据结构不一致（真实数据模式）；
7. "The Position of inf or nan in NPU and bench Tensor do not match."：NPU 和 Bench 的数据有 nan/inf（真实数据模式）；
8. "This is type of scalar data, can not compare."：NPU 为标量（真实数据模式）；
9.  "Dtype of NPU and bench Tensor do not match."：NPU 和 Bench 数据的数据类型不同（真实数据模式）；
10. ""：除以上情况的其余情况（真实数据模式、统计数据模式）。

除以上错误信息提示外，异常数据颜色高亮标记的原因叠加呈现于此列。

### 3.6 计算精度评价指标分析

1. Cosine：通过计算两个向量的余弦值来判断其相似度，数值越接近于 1 说明计算出的两个张量越相似，实际可接受阈值为大于 0.99。在计算中可能会存在 nan，主要由于可能会出现其中一个向量为 0。

2. MaxAbsErr：当最大绝对误差越接近 0 表示其计算的误差越小，实际可接受阈值为小于 0.001。

3. MaxRelativeErr：当最大相对误差越接近 0 表示其计算的误差越小。

   当 dump 数据中存在 0 或 Nan 时，比对结果中最大相对误差则出现 inf 或 Nan 的情况，属于正常现象。

4. One Thousandth Err Ratio（双千分之一）、Five Thousandths Err Ratio（双千分之五）精度指标：是指 NPU 的 Tensor 中的元素逐个与对应的标杆数据对比，相对误差大于千分之一、千分之五的比例占总元素个数的比例小于千分之一、千分之五。该数据仅作为精度下降趋势的参考，并不参与计算精度是否通过的判定。
