diff --git a/debug/accuracy_tools/api_accuracy_checker/common/utils.py b/debug/accuracy_tools/api_accuracy_checker/common/utils.py index eee58ef7ae5e278993ff8eade41052de7b9deec1..f6f4d26f594a386b9c95771d753171cc4648f44e 100644 --- a/debug/accuracy_tools/api_accuracy_checker/common/utils.py +++ b/debug/accuracy_tools/api_accuracy_checker/common/utils.py @@ -39,7 +39,7 @@ else: from ptdbg_ascend.src.python.ptdbg_ascend.common.file_check_util import FileCheckConst, FileChecker, FileOpen from ptdbg_ascend.src.python.ptdbg_ascend.common import file_check_util -torch_without_guard_version_list = ['2.1'] +torch_without_guard_version_list = ['2.1', '2.2'] for version in torch_without_guard_version_list: if torch.__version__.startswith(version): torch_without_guard_version = True diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/algorithm.py b/debug/accuracy_tools/api_accuracy_checker/compare/algorithm.py index c92eff25a701c5f0c228d3225fbbb22959d5f929..a0c0f5aa108e90ddbe9455ca281e6ab73c9a08b1 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/algorithm.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/algorithm.py @@ -1,7 +1,7 @@ # 定义比对算法及比对标准 import torch import numpy as np -from api_accuracy_checker.compare.compare_utils import CompareConst, check_dtype_comparable +from api_accuracy_checker.compare.compare_utils import CompareConst, check_dtype_comparable, ULP_PARAMETERS from api_accuracy_checker.common.utils import Const @@ -189,3 +189,19 @@ def check_norm_value(normal_value_mask, rel_err, rtol): err_mask = np.logical_and(err_mask, normal_value_mask) err_cnt = np.sum(err_mask) return 0 if np.sum(normal_value_mask) == 0 else err_cnt / np.sum(normal_value_mask) + + +def get_ulp_err(bench_output, device_output, dtype): + parameters = ULP_PARAMETERS.get(dtype) + min_eb = (parameters.get('min_eb'))[0] + exponent_num = (parameters.get('exponent_num'))[0] + abs_bench = np.abs(bench_output) + eb = np.where(abs_bench == 0, 0, np.floor(np.log2(abs_bench))) + eb = np.maximum(eb, min_eb) + + if dtype == torch.float32: + ulp_err = (device_output.astype(np.float64) - bench_output).astype(np.float64) * np.exp2(-eb + exponent_num) + else: + ulp_err = (device_output.astype(np.float32) - bench_output).astype(np.float32) * np.exp2(-eb + exponent_num) + ulp_err = np.abs(ulp_err) + return ulp_err diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_compare.py b/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_compare.py index ad38cb9561e3e14dc332790b3c06c9cb78716c4e..a68cdce4a27f6e77bdd90a3e820c456eb6330818 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_compare.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_compare.py @@ -4,6 +4,7 @@ import sys import csv import math from collections import namedtuple +import torch import pandas as pd from api_accuracy_checker.common.utils import print_info_log, print_warn_log, print_error_log, write_csv, \ @@ -11,8 +12,8 @@ from api_accuracy_checker.common.utils import print_info_log, print_warn_log, pr from api_accuracy_checker.common.config import msCheckerConfig from api_accuracy_checker.compare.compare_utils import CompareConst, API_PRECISION_COMPARE_RESULT_FILE_NAME, \ API_PRECISION_COMPARE_DETAILS_FILE_NAME, BENCHMARK_COMPARE_SUPPORT_LIST, API_PRECISION_COMPARE_UNSUPPORT_LIST, \ - ApiPrecisionCompareColumn, AbsoluteStandardApi, BinaryStandardApi, BINARY_COMPARE_UNSUPPORT_LIST, \ - convert_str_to_float, CompareMessage + ApiPrecisionCompareColumn, AbsoluteStandardApi, BinaryStandardApi, ULPStandardApi, BINARY_COMPARE_UNSUPPORT_LIST, \ + ULP_COMPARE_SUPPORT_LIST, convert_str_to_float, CompareMessage from api_accuracy_checker.compare.compare_column import ApiPrecisionOutputColumn from api_accuracy_checker.run_ut.run_ut import get_validated_result_csv_path from ptdbg_ascend.src.python.ptdbg_ascend.common.file_check_util import FileCheckConst, FileChecker, change_mode @@ -67,7 +68,17 @@ benchmark_message = { } -class BenchmarkStandard: +class Standard: + @staticmethod + def _calc_ratio(x, y, default_value=1.0): + x, y = convert_str_to_float(x), convert_str_to_float(y) + if math.isclose(y, 0.0): + return 1.0 if math.isclose(x, 0.0) else default_value + else: + return abs(x / y) + + +class BenchmarkStandard(Standard): def __init__(self, api_name, npu_precision, gpu_precision): self.api_name = api_name self.npu_precision = npu_precision @@ -86,7 +97,7 @@ class BenchmarkStandard: self.final_result = CompareConst.PASS def __str__(self): - return "%s" % (self.api_name) + return f"{self.api_name}" def get_result(self): self._compare_ratio() @@ -121,7 +132,7 @@ class BenchmarkStandard: return [self.small_value_err_ratio, self.small_value_err_status, self.rmse_ratio, self.rmse_status, self.max_rel_err_ratio, self.max_rel_err_status, self.mean_rel_err_ratio, self.mean_rel_err_status, self.eb_ratio, self.eb_status] - + @staticmethod def _get_status(ratio, algorithm): error_threshold = benchmark_algorithms_thresholds.get(algorithm).get('error_threshold') @@ -132,13 +143,44 @@ class BenchmarkStandard: return CompareConst.WARNING return CompareConst.PASS - @staticmethod - def _calc_ratio(x, y, default_value=1.0): - x, y = convert_str_to_float(x), convert_str_to_float(y) - if math.isclose(y, 0.0): - return 1.0 if math.isclose(x, 0.0) else default_value + +class ULPStandard(Standard): + def __init__(self, api_name, npu_precision, gpu_precision): + self.api_name = api_name + self.npu_precision = npu_precision + self.gpu_precision = gpu_precision + self.mean_ulp_err = 0 + self.ulp_err_proportion = 0 + self.ulp_err_proportion_ratio = 1 + self.ulp_err_status = CompareConst.PASS + + def __str__(self): + return f"{self.api_name}" + + def get_result(self): + self.mean_ulp_err = convert_str_to_float(self.npu_precision.get(ApiPrecisionCompareColumn.MEAN_ULP_ERR)) + self.ulp_err_proportion = convert_str_to_float(self.npu_precision.get(ApiPrecisionCompareColumn.ULP_ERR_PROPORTION)) + self.ulp_err_proportion_ratio = self._calc_ratio(self.npu_precision.get(ApiPrecisionCompareColumn.ULP_ERR_PROPORTION), + self.gpu_precision.get(ApiPrecisionCompareColumn.ULP_ERR_PROPORTION)) + self.ulp_err_status = self.get_ulp_status(self.npu_precision.get(ApiPrecisionCompareColumn.DEVICE_DTYPE)) + + def get_ulp_status(self, dtype): + if dtype == torch.float32: + if self.mean_ulp_err < 64: + return CompareConst.PASS + elif self.ulp_err_proportion < 0.05: + return CompareConst.PASS + elif self.ulp_err_proportion_ratio < 1: + return CompareConst.PASS + else: + return CompareConst.ERROR else: - return abs(x / y) + if self.ulp_err_proportion < 0.001: + return CompareConst.PASS + elif self.ulp_err_proportion_ratio < 1: + return CompareConst.PASS + else: + return CompareConst.ERROR def write_detail_csv(content, save_path): @@ -191,20 +233,28 @@ def analyse_csv(npu_data, gpu_data, config): msg = f'This API : {full_api_name_with_direction_status} has multiple records in the GPU data.' raise CompareException(CompareException.INVALID_DATA_ERROR, msg) row_gpu = row_gpu.iloc[0] + new_status = CompareConst.SPACE #当前API的输出为空(例如反向过程中requires_grad=False),跳过比对 if row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE].isspace(): - continue - _, api_name, _ = full_api_name.split("*") - new_status = CompareConst.SPACE - compare_column.api_name = full_api_name_with_direction_status - if row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE] not in BINARY_COMPARE_UNSUPPORT_LIST or api_name in BinaryStandardApi: - new_status = record_binary_consistency_result(api_name, compare_column, row_npu) - elif api_name in AbsoluteStandardApi: - new_status = record_absolute_threshold_result(compare_column, row_npu) - elif row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE] in BENCHMARK_COMPARE_SUPPORT_LIST: - bs = BenchmarkStandard(full_api_name_with_direction_status, row_npu, row_gpu) - new_status = record_benchmark_compare_result(compare_column, bs) - write_detail_csv(compare_column.to_column_value(), config.details_csv_path) + compare_column.api_name = full_api_name_with_direction_status + compare_column.compare_result = CompareConst.SKIP + compare_column.compare_message = row_npu[ApiPrecisionCompareColumn.MESSAGE] + new_status = CompareConst.SKIP + write_detail_csv(compare_column.to_column_value(), config.details_csv_path) + else: + _, api_name, _ = full_api_name.split("*") + compare_column.api_name = full_api_name_with_direction_status + if row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE] not in BINARY_COMPARE_UNSUPPORT_LIST or api_name in BinaryStandardApi: + new_status = record_binary_consistency_result(api_name, compare_column, row_npu) + elif api_name in AbsoluteStandardApi: + new_status = record_absolute_threshold_result(compare_column, row_npu) + elif api_name in ULPStandardApi and row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE] in ULP_COMPARE_SUPPORT_LIST: + us = ULPStandard(full_api_name_with_direction_status, row_npu, row_gpu) + new_status = record_ulp_compare_result(compare_column, us) + elif row_npu[ApiPrecisionCompareColumn.DEVICE_DTYPE] in BENCHMARK_COMPARE_SUPPORT_LIST: + bs = BenchmarkStandard(full_api_name_with_direction_status, row_npu, row_gpu) + new_status = record_benchmark_compare_result(compare_column, bs) + write_detail_csv(compare_column.to_column_value(), config.details_csv_path) if full_last_api_name is not None and full_api_name != full_last_api_name: if last_api_dtype in API_PRECISION_COMPARE_UNSUPPORT_LIST: @@ -279,6 +329,8 @@ def get_absolute_threshold_result(row_npu): def get_api_checker_result(status): if not status: return CompareConst.SPACE + if all(item == CompareConst.SKIP for item in status): + return CompareConst.SKIP for const in (CompareConst.ERROR, CompareConst.WARNING): if const in status: return const @@ -289,7 +341,7 @@ def check_csv_columns(columns, csv_type): required_columns = ApiPrecisionCompareColumn.to_required_columns() missing_columns = [column for column in required_columns if column not in columns] if missing_columns: - msg = f"The followint columns {','.join(missing_columns)} are missing in{csv_type}" + msg = f"The following columns {','.join(missing_columns)} are missing in{csv_type}" raise CompareException(CompareException.INVALID_DATA_ERROR, msg) @@ -351,6 +403,21 @@ def record_benchmark_compare_result(compare_column, bs): return compare_column.compare_result +def record_ulp_compare_result(compare_column, us): + us.get_result() + compare_column.mean_ulp_err = us.mean_ulp_err + compare_column.ulp_err_proportion = us.ulp_err_proportion + compare_column.ulp_err_proportion_ratio = us.ulp_err_proportion_ratio + compare_column.ulp_err_status = us.ulp_err_status + compare_column.compare_result = us.ulp_err_status + compare_column.compare_algorithm = "ULP误差比对法" + message = '' + if compare_column.ulp_err_status == CompareConst.ERROR: + message += "ERROR: ULP误差不满足标准\n" + compare_column.compare_message = message + return compare_column.compare_result + + def _api_precision_compare(parser=None): if not parser: parser = argparse.ArgumentParser() diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_standard.yaml b/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_standard.yaml index 4033538b73e9b6a094bbf2c05e0c02bbed607c24..84d0675874b0016aaeb6955c42fb6b3a34a747d8 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_standard.yaml +++ b/debug/accuracy_tools/api_accuracy_checker/compare/api_precision_standard.yaml @@ -106,3 +106,21 @@ BinaryCompareStandard: - tril_ - triu - triu_ + - type_as + +ULPStandard: + - __matmul__ + - addbmm + - addbmm_ + - addmm + - addmm_ + - baddbmm + - baddbmm_ + - bmm + - chain_matmul + - hspmm + - matmul + - mm + - mv + - smm + - sspaddmm \ No newline at end of file diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/compare.py b/debug/accuracy_tools/api_accuracy_checker/compare/compare.py index bd10f77976642331fa8e7bce28a703f0922c1411..db549ec1d54060cb8a9f96ceeec39f90566b05ca 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/compare.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/compare.py @@ -7,12 +7,13 @@ from rich.table import Table from rich.console import Console from api_accuracy_checker.common.utils import get_json_contents, write_csv, print_warn_log from api_accuracy_checker.compare.compare_utils import CompareConst, check_dtype_comparable, DETAIL_TEST_ROWS, \ - precision_configs, BENCHMARK_COMPARE_SUPPORT_LIST, AbsoluteStandardApi, BinaryStandardApi, apis_threshold + precision_configs, BENCHMARK_COMPARE_SUPPORT_LIST, AbsoluteStandardApi, BinaryStandardApi, ULPStandardApi, \ + apis_threshold from api_accuracy_checker.compare.compare_column import CompareColumn from api_accuracy_checker.compare.algorithm import get_rmse, get_error_balance, get_max_rel_err, get_mean_rel_err, \ get_rel_err, get_abs_err, get_max_abs_err, get_rel_err_ratio, cosine_sim, get_rel_err_origin, \ get_small_value_err_ratio, get_finite_and_infinite_mask, get_small_value_mask, check_inf_nan_value, \ - check_small_value, check_norm_value, get_abs_bench_with_eps + check_small_value, check_norm_value, get_abs_bench_with_eps, get_ulp_err from api_accuracy_checker.common.config import msCheckerConfig from ptdbg_ascend.src.python.ptdbg_ascend.common.file_check_util import FileOpen @@ -97,7 +98,7 @@ class Comparator: self.test_result_cnt["total_skip_num"] += 1 continue self.test_result_cnt["total_num"] += 1 - if column1 == CompareConst.PASS and column2 in [CompareConst.PASS, CompareConst.SPACE]: + if column1 == CompareConst.PASS and column2 in [CompareConst.PASS, CompareConst.SPACE, CompareConst.SKIP]: self.test_result_cnt['success_num'] += 1 elif column1 == CompareConst.ERROR and column2 == CompareConst.ERROR: self.test_result_cnt['forward_and_backward_fail_num'] += 1 @@ -125,8 +126,8 @@ class Comparator: name = test_result[0] df_row = list(test_result[:3]) - if test_result[1] == "SKIP" or test_result[2] == "SKIP": - df_row.append(test_result[3]) + if test_result[1] == "SKIP": + df_row.append(test_result[3][0][-1]) if self.stack_info: stack_info = "\n".join(self.stack_info[name]) df_row.append(stack_info) @@ -158,18 +159,28 @@ class Comparator: self.write_summary_csv(args) self.write_detail_csv(args) - def compare_output(self, full_api_name, bench_output, device_output, bench_grad=None, npu_grad=None): + def compare_output(self, full_api_name, data_info): _, api_name, _ = full_api_name.split("*") + bench_output = data_info.bench_output + device_output = data_info.device_output + bench_grad = data_info.bench_grad + device_grad = data_info.device_grad + backward_message = data_info.backward_message compare_func = self._compare_dropout if "dropout" in full_api_name else self._compare_core_wrapper fwd_success_status, fwd_compare_alg_results = compare_func(api_name, bench_output, device_output) - if not (bench_grad and npu_grad): + if not (bench_grad and device_grad): bwd_success_status, bwd_compare_alg_results = (CompareConst.SPACE, []) else: if "dropout" in full_api_name: - bwd_success_status, bwd_compare_alg_results = compare_func(api_name, bench_grad[0], npu_grad[0]) + bwd_success_status, bwd_compare_alg_results = compare_func(api_name, bench_grad[0], device_grad[0]) else: - bwd_success_status, bwd_compare_alg_results = compare_func(api_name, bench_grad, npu_grad) - self.record_results(full_api_name, fwd_success_status, bwd_success_status if bwd_compare_alg_results is not None else CompareConst.SPACE, fwd_compare_alg_results, bwd_compare_alg_results) + bwd_success_status, bwd_compare_alg_results = compare_func(api_name, bench_grad, device_grad) + if backward_message: + backward_column = CompareColumn() + bwd_compare_alg_results = backward_column.to_column_value(CompareConst.SKIP, backward_message) + self.record_results(full_api_name, fwd_success_status, CompareConst.SKIP, fwd_compare_alg_results, [bwd_compare_alg_results]) + else: + self.record_results(full_api_name, fwd_success_status, bwd_success_status if bwd_compare_alg_results is not None else CompareConst.SPACE, fwd_compare_alg_results, bwd_compare_alg_results) return fwd_success_status == CompareConst.PASS, bwd_success_status == CompareConst.PASS \ or bwd_success_status == CompareConst.SPACE @@ -282,6 +293,19 @@ class Comparator: compare_column.inf_nan_error_ratio = check_inf_nan_value(inf_nan_mask, bench_output, device_output, dtype, rtol) compare_column.rel_err_ratio = check_norm_value(normal_value_mask, rel_err, rtol) compare_column.abs_err_ratio = check_small_value(abs_err, small_value_mask, small_value_atol) + elif api_name in ULPStandardApi: + if bench_output.size == 0: + compare_column.max_ulp_error = 0 + compare_column.mean_ulp_error = 0 + compare_column.ulp_error_proportion = 0 + else: + ulp_err = get_ulp_err(bench_output, device_output, dtype) + compare_column.max_ulp_error = np.max(ulp_err) + compare_column.mean_ulp_error = np.mean(ulp_err) + if dtype == torch.float32: + compare_column.ulp_error_proportion = np.sum(ulp_err > 32) / bench_output.size + else: + compare_column.ulp_error_proportion = np.sum(ulp_err > 1) / bench_output.size else: dtype_config = precision_configs.get(dtype) small_value_mask = get_small_value_mask(abs_bench, both_finite_mask, dtype_config['small_value'][0]) diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/compare_column.py b/debug/accuracy_tools/api_accuracy_checker/compare/compare_column.py index 961fce6811efd34789cb06f19d894244da681c33..cbaa3cb512397522d41423492641179bfd72f5db 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/compare_column.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/compare_column.py @@ -20,12 +20,16 @@ class CompareColumn: self.inf_nan_error_ratio = CompareConst.SPACE self.rel_err_ratio = CompareConst.SPACE self.abs_err_ratio = CompareConst.SPACE + self.max_ulp_error = CompareConst.SPACE + self.mean_ulp_error = CompareConst.SPACE + self.ulp_error_proportion = CompareConst.SPACE def to_column_value(self, is_pass, message): return [self.bench_type, self.npu_type, self.shape, self.cosine_sim, self.max_abs_err, self.rel_err_hundredth, self.rel_err_thousandth, self.rel_err_ten_thousandth, self.error_rate, self.EB, self.RMSE, self.small_value_err_ratio, self.Max_rel_error, self.Mean_rel_error, self.inf_nan_error_ratio, - self.rel_err_ratio, self.abs_err_ratio, is_pass, message] + self.rel_err_ratio, self.abs_err_ratio, self.max_ulp_error, self.mean_ulp_error, + self.ulp_error_proportion, is_pass, message] class ApiPrecisionOutputColumn: @@ -49,6 +53,10 @@ class ApiPrecisionOutputColumn: self.abs_err_ratio_status = CompareConst.SPACE self.error_rate = CompareConst.SPACE self.error_rate_status = CompareConst.SPACE + self.mean_ulp_err = CompareConst.SPACE + self.ulp_err_proportion = CompareConst.SPACE + self.ulp_err_proportion_ratio = CompareConst.SPACE + self.ulp_err_status = CompareConst.SPACE self.compare_result = CompareConst.SPACE self.compare_algorithm = CompareConst.SPACE self.compare_message = CompareConst.SPACE @@ -58,6 +66,6 @@ class ApiPrecisionOutputColumn: self.rmse_status, self.max_rel_err_ratio, self.max_rel_err_status, self.mean_rel_err_ratio, self.mean_rel_err_status, self.eb_ratio, self.eb_status, self.inf_nan_error_ratio, self.inf_nan_error_ratio_status, self.rel_err_ratio, self.rel_err_ratio_status, self.abs_err_ratio, - self.abs_err_ratio_status, self.error_rate, self.error_rate_status, self.compare_result, + self.abs_err_ratio_status, self.error_rate, self.error_rate_status, self.mean_ulp_err, + self.ulp_err_proportion, self.ulp_err_proportion_ratio, self.ulp_err_status, self.compare_result, self.compare_algorithm, self.compare_message] - \ No newline at end of file diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/compare_utils.py b/debug/accuracy_tools/api_accuracy_checker/compare/compare_utils.py index bce185a9cb9427610022443bfb48dc2e9803089a..a1cc8caf8851ba13a313b16f049827e8397a8314 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/compare_utils.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/compare_utils.py @@ -12,6 +12,7 @@ API_PRECISION_COMPARE_RESULT_FILE_NAME = "api_precision_compare_result_" + curre API_PRECISION_COMPARE_DETAILS_FILE_NAME = "api_precision_compare_details_" + current_time + ".csv" BENCHMARK_COMPARE_SUPPORT_LIST = ['torch.float16', 'torch.bfloat16', 'torch.float32'] API_PRECISION_COMPARE_UNSUPPORT_LIST = ['torch.float64', 'torch.complex64', 'torch.complex128'] +ULP_COMPARE_SUPPORT_LIST = ['torch.float16', 'torch.bfloat16', 'torch.float32'] BINARY_COMPARE_UNSUPPORT_LIST = BENCHMARK_COMPARE_SUPPORT_LIST + API_PRECISION_COMPARE_UNSUPPORT_LIST @@ -21,6 +22,7 @@ with FileOpen(standard_yaml_path, 'r') as f: Apis = yaml.safe_load(f) AbsoluteStandardApi = Apis.get('AbsoluteThreshStandard') BinaryStandardApi = Apis.get('BinaryCompareStandard') + ULPStandardApi = Apis.get('ULPStandard') threshold_yaml_path = os.path.join(cur_path, "api_precision_threshold.yaml") @@ -44,6 +46,9 @@ DETAIL_TEST_ROWS = [[ "inf/nan错误率", "相对误差错误率", "绝对误差错误率", + "ULP误差最大值", + "ULP误差平均值", + "ULP误差大于阈值占比", "Status", "Message" ]] @@ -77,6 +82,34 @@ precision_configs = { } +ULP_PARAMETERS = { + torch.float16 : { + 'min_eb' : [ + -14 + ], + 'exponent_num' : [ + 10 + ] + }, + torch.bfloat16 : { + 'min_eb' : [ + -126 + ], + 'exponent_num' : [ + 7 + ] + }, + torch.float32 : { + 'min_eb' : [ + -126 + ], + 'exponent_num' : [ + 23 + ] + } +} + + class CompareConst: NAN = np.nan NA = "N/A" @@ -118,6 +151,10 @@ class ApiPrecisionCompareColumn: REL_ERR_RATIO_STATUS = '相对误差判定结果' ABS_ERR_RATIO = '绝对误差错误率' ABS_ERR_RATIO_STATUS = '绝对误差判定结果' + MEAN_ULP_ERR = 'ULP误差平均值' + ULP_ERR_PROPORTION = 'ULP误差大于阈值占比' + ULP_ERR_PROPORTION_RATIO = 'ULP误差大于阈值占比比值' + ULP_ERR_STATUS = 'ULP误差判定结果' FINAL_RESULT = '比对结果' ALGORITHM = '比对算法' FORWWARD_STATUS = 'Forward Test Success' @@ -130,11 +167,12 @@ class ApiPrecisionCompareColumn: ApiPrecisionCompareColumn.SMALL_VALUE_ERROR_RATE, ApiPrecisionCompareColumn.RMSE, ApiPrecisionCompareColumn.MAX_REL_ERR, ApiPrecisionCompareColumn.MEAN_REL_ERR, ApiPrecisionCompareColumn.EB, ApiPrecisionCompareColumn.ERROR_RATE, ApiPrecisionCompareColumn.INF_NAN_ERROR_RATIO, - ApiPrecisionCompareColumn.REL_ERR_RATIO, ApiPrecisionCompareColumn.ABS_ERR_RATIO] + ApiPrecisionCompareColumn.REL_ERR_RATIO, ApiPrecisionCompareColumn.ABS_ERR_RATIO, + ApiPrecisionCompareColumn.MEAN_ULP_ERR, ApiPrecisionCompareColumn.ULP_ERR_PROPORTION] @staticmethod def get_detail_csv_title(): - return [ApiPrecisionCompareColumn.API_NAME, + return [ApiPrecisionCompareColumn.API_NAME, ApiPrecisionCompareColumn.SMALL_VALUE_ERROR_RATIO, ApiPrecisionCompareColumn.SMALL_VALUE_ERROR_STATUS, ApiPrecisionCompareColumn.RMSE_RATIO, ApiPrecisionCompareColumn.RMSE_STATUS, ApiPrecisionCompareColumn.MAX_REL_ERR_RATIO, ApiPrecisionCompareColumn.MAX_REL_ERR_STATUS, @@ -144,6 +182,8 @@ class ApiPrecisionCompareColumn: ApiPrecisionCompareColumn.REL_ERR_RATIO, ApiPrecisionCompareColumn.REL_ERR_RATIO_STATUS, ApiPrecisionCompareColumn.ABS_ERR_RATIO, ApiPrecisionCompareColumn.ABS_ERR_RATIO_STATUS, ApiPrecisionCompareColumn.ERROR_RATE, ApiPrecisionCompareColumn.ERROR_RATE_STATUS, + ApiPrecisionCompareColumn.MEAN_ULP_ERR, ApiPrecisionCompareColumn.ULP_ERR_PROPORTION, + ApiPrecisionCompareColumn.ULP_ERR_PROPORTION_RATIO, ApiPrecisionCompareColumn.ULP_ERR_STATUS, ApiPrecisionCompareColumn.FINAL_RESULT, ApiPrecisionCompareColumn.ALGORITHM, ApiPrecisionCompareColumn.MESSAGE] @staticmethod diff --git a/debug/accuracy_tools/api_accuracy_checker/dump/dump.py b/debug/accuracy_tools/api_accuracy_checker/dump/dump.py index d8b317aa282bb74f7b35c6a4b6216446959fb30e..f5a4620e1ad2044a3ba0259b5ef533cb569ed102 100644 --- a/debug/accuracy_tools/api_accuracy_checker/dump/dump.py +++ b/debug/accuracy_tools/api_accuracy_checker/dump/dump.py @@ -87,6 +87,7 @@ class DumpConst: def pretest_info_dump(name, out_feat, module, phase): if not DumpUtil.get_dump_switch(): return + name = name.replace('*', '.') if phase == DumpConst.forward: api_info = ForwardAPIInfo(name, module.input_args, module.input_kwargs) elif phase == DumpConst.backward: diff --git a/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut.py b/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut.py index 856cb237ca7e1ce71da45938be2284c2e6133a2b..71018a86d8e7adaa1e1d0f9eec57046d4fbb7811 100644 --- a/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut.py +++ b/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut.py @@ -17,9 +17,12 @@ else: import torch from tqdm import tqdm from api_accuracy_checker.run_ut.data_generate import gen_api_params, gen_args +from api_accuracy_checker.run_ut.run_ut_utils import Backward_Message from api_accuracy_checker.common.utils import print_info_log, print_warn_log, get_json_contents, api_info_preprocess, \ print_error_log, initialize_save_path, Const, create_directory from api_accuracy_checker.compare.compare import Comparator +from api_accuracy_checker.compare.compare_column import CompareColumn +from api_accuracy_checker.compare.compare_utils import CompareConst from api_accuracy_checker.hook_module.wrap_tensor import TensorOPTemplate from api_accuracy_checker.hook_module.wrap_functional import FunctionalOPTemplate from api_accuracy_checker.hook_module.wrap_torch import TorchOPTemplate @@ -39,6 +42,7 @@ RunUTConfig = namedtuple('RunUTConfig', ['forward_content', 'backward_content', 'save_error_data', 'is_continue_run_ut', 'real_data_path']) not_backward_list = ['repeat_interleave'] not_detach_set = {'resize_', 'resize_as_', 'set_', 'transpose_', 't_', 'squeeze_', 'unsqueeze_'} +not_raise_dtype_set = {'type_as'} tqdm_params = { 'smoothing': 0, # 平滑进度条的预计剩余时间,取值范围0到1 @@ -142,6 +146,7 @@ def generate_cpu_params(input_args, input_kwargs, need_backward, api_name): elif len(need_raise_dtypes) >= 2: raise_dtype = torch.float32 + raise_dtype = None if api_name in not_raise_dtype_set else raise_dtype is_detach = api_name not in not_detach_set cpu_args = recursive_arg_to_cpu(input_args, is_detach, raise_dtype=raise_dtype) cpu_kwargs = {key: recursive_arg_to_cpu(value, key != "out" and is_detach, raise_dtype=raise_dtype) for key, value in input_kwargs.items()} @@ -165,25 +170,24 @@ def run_ut(config): continue try: if msCheckerConfig.white_list: - [_, api_name, _] = api_full_name.split("*") + [_, api_name, _] = api_full_name.split(".") if api_name not in set(msCheckerConfig.white_list): continue data_info = run_torch_api(api_full_name, config.real_data_path, config.backward_content, api_info_dict) is_fwd_success, is_bwd_success = compare.compare_output(api_full_name, - data_info.bench_out, - data_info.device_out, - data_info.bench_grad_out, - data_info.device_grad_out) + data_info) if config.save_error_data: do_save_error_data(api_full_name, data_info, is_fwd_success, is_bwd_success) except Exception as err: - [_, api_name, _] = api_full_name.split("*") + [_, api_name, _] = api_full_name.split(".") if "expected scalar type Long" in str(err): print_warn_log(f"API {api_name} not support int32 tensor in CPU, please add {api_name} to CONVERT_API " f"'int32_to_int64' list in accuracy_tools/api_accuracy_check/common/utils.py file.") else: print_error_log(f"Run {api_full_name} UT Error: %s" % str(err)) - compare.write_summary_csv((api_full_name, "SKIP", "SKIP", str(err))) + err_column = CompareColumn() + fwd_compare_alg_results = err_column.to_column_value(CompareConst.SKIP, str(err)) + compare.record_results(api_full_name, CompareConst.SKIP, CompareConst.SKIP, [fwd_compare_alg_results], None) finally: if is_gpu: torch.cuda.empty_cache() @@ -197,7 +201,6 @@ def run_ut(config): def do_save_error_data(api_full_name, data_info, is_fwd_success, is_bwd_success): if not is_fwd_success or not is_bwd_success: - api_full_name = api_full_name.replace("*", ".") for element in data_info.in_fwd_data_list: UtAPIInfo(api_full_name + '.forward.input', element) UtAPIInfo(api_full_name + '.forward.output.bench', data_info.bench_out) @@ -209,18 +212,19 @@ def do_save_error_data(api_full_name, data_info, is_fwd_success, is_bwd_success) def run_torch_api(api_full_name, real_data_path, backward_content, api_info_dict): in_fwd_data_list = [] - [api_type, api_name, _] = api_full_name.split("*") + backward_message = '' + [api_type, api_name, _] = api_full_name.split(".") args, kwargs, need_grad = get_api_info(api_info_dict, api_name, real_data_path) in_fwd_data_list.append(args) in_fwd_data_list.append(kwargs) need_backward = api_full_name in backward_content if not need_grad: - print_warn_log("%s function with out=... arguments don't support automatic differentiation, skip backward." - % api_full_name) + print_warn_log(f"{api_full_name} {Backward_Message.UNSUPPORT_BACKWARD_MESSAGE.format(api_full_name)}") + backward_message += Backward_Message.UNSUPPORT_BACKWARD_MESSAGE if api_name in not_backward_list: need_grad = False - print_warn_log( - "%s function backward result is None, skip backward." % api_full_name) + print_warn_log(f"{api_full_name} {Backward_Message.NO_BACKWARD_RESULT_MESSAGE.format(api_full_name)}") + backward_message += Backward_Message.NO_BACKWARD_RESULT_MESSAGE need_backward = need_backward and need_grad if kwargs.get("device"): del kwargs["device"] @@ -239,17 +243,17 @@ def run_torch_api(api_full_name, real_data_path, backward_content, api_info_dict grad_index = grad_input_index.get('grad_index') if need_backward: - backward_args = backward_content[api_full_name] - grad = gen_args(backward_args, real_data_path=real_data_path)[0] - bench_grad, _ = generate_cpu_params(grad, {}, False, api_name) - bench_grad_out = run_backward(cpu_args, bench_grad, grad_index, out) - device_grad = grad.clone().detach().to(current_device) - device_grad_out = run_backward(device_args, device_grad, grad_index, device_out) + if need_to_backward(grad_index, out): + backward_args = backward_content[api_full_name] + grad = gen_args(backward_args, real_data_path=real_data_path)[0] + bench_grad, _ = generate_cpu_params(grad, {}, False, api_name) + bench_grad_out = run_backward(cpu_args, bench_grad, grad_index, out) + device_grad = grad.clone().detach().to(current_device) + device_grad_out = run_backward(device_args, device_grad, grad_index, device_out) + else: + backward_message += Backward_Message.MULTIPLE_BACKWARD_MESSAGE - if grad_index is not None: - return UtDataInfo(bench_grad_out, device_grad_out, device_out[grad_index], out[grad_index], bench_grad, - in_fwd_data_list) - return UtDataInfo(bench_grad_out, device_grad_out, device_out, out, bench_grad, in_fwd_data_list) + return UtDataInfo(bench_grad_out, device_grad_out, device_out, out, bench_grad, in_fwd_data_list, backward_message) def get_api_info(api_info_dict, api_name, real_data_path): @@ -261,12 +265,16 @@ def get_api_info(api_info_dict, api_name, real_data_path): return args, kwargs, need_grad +def need_to_backward(grad_index, out): + if grad_index is None and isinstance(out, (list, tuple)): + return False + return True + + def run_backward(args, grad, grad_index, out): if grad_index is not None: out[grad_index].backward(grad) - elif isinstance(out, (list, tuple)): - raise NotImplementedError("Multiple backward is not supported.") else: out.backward(grad) args_grad = [] @@ -438,13 +446,14 @@ def run_ut_command(args): class UtDataInfo: - def __init__(self, bench_grad_out, device_grad_out, device_out, bench_out, grad_in, in_fwd_data_list): - self.bench_grad_out = bench_grad_out - self.device_grad_out = device_grad_out - self.device_out = device_out - self.bench_out = bench_out + def __init__(self, bench_grad, device_grad, device_output, bench_output, grad_in, in_fwd_data_list, backward_message): + self.bench_grad = bench_grad + self.device_grad = device_grad + self.device_output = device_output + self.bench_output = bench_output self.grad_in = grad_in self.in_fwd_data_list = in_fwd_data_list + self.backward_message = backward_message class UtAPIInfo(APIInfo): diff --git a/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut_utils.py b/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..490d8a5db3d13678c4b86d042f2681322a88620d --- /dev/null +++ b/debug/accuracy_tools/api_accuracy_checker/run_ut/run_ut_utils.py @@ -0,0 +1,4 @@ +class Backward_Message: + MULTIPLE_BACKWARD_MESSAGE = "Multiple backward is not supported." + UNSUPPORT_BACKWARD_MESSAGE = "function with out=... arguments don't support automatic differentiation, skip backward." + NO_BACKWARD_RESULT_MESSAGE = "function backward result is None, skip backward." \ No newline at end of file diff --git a/debug/accuracy_tools/api_accuracy_checker/test/ut/compare/test_compare.py b/debug/accuracy_tools/api_accuracy_checker/test/ut/compare/test_compare.py index 4ce73ce550dfc5d5cd21246dbc2756a6024f6fea..2c9b13c4d9c2a69169801a5af4c5de58598cec3b 100644 --- a/debug/accuracy_tools/api_accuracy_checker/test/ut/compare/test_compare.py +++ b/debug/accuracy_tools/api_accuracy_checker/test/ut/compare/test_compare.py @@ -9,6 +9,7 @@ import torch.nn.functional from api_accuracy_checker.compare.compare import Comparator from api_accuracy_checker.compare.compare_column import CompareColumn +from api_accuracy_checker.run_ut.run_ut import UtDataInfo current_time = time.strftime("%Y%m%d%H%M%S") RESULT_FILE_NAME = "accuracy_checking_result_" + current_time + ".csv" @@ -47,7 +48,7 @@ class TestCompare(unittest.TestCase): # 对其他值进行比较,确保它们符合预期 detailed_result_total[0][3] = 1.0 self.assertEqual(detailed_result_total, [['torch.float32', 'torch.float32', (100, 100), 1.0, 0.0, ' ', ' ', ' ', - ' ', 0.0, 0.0, 0, 0.0, 0.0, ' ', ' ', ' ', 'pass', + ' ', 0.0, 0.0, 0, 0.0, 0.0, ' ', ' ', ' ', ' ', ' ', ' ', 'pass', '\nMax abs error is less than 0.001, consider as pass, skip other check and set to SPACE.\n']]) self.assertTrue(test_final_success) @@ -61,22 +62,24 @@ class TestCompare(unittest.TestCase): detailed_result_total[1][3] = 1.0 self.assertTrue(test_final_success) self.assertEqual(detailed_result_total, [['torch.float32', 'torch.float32', (100, 100), 1.0, 0.0, ' ', ' ', ' ', - ' ', 0.0, 0.0, 0, 0.0, 0.0, ' ', ' ', ' ', 'pass', + ' ', 0.0, 0.0, 0, 0.0, 0.0, ' ', ' ', ' ', ' ', ' ', ' ', 'pass', '\nMax abs error is less than 0.001, consider as pass, skip other check and set to SPACE.\n'], ['torch.float32', 'torch.float32', (100, 100), 1.0, 0.0, ' ', ' ', ' ', ' ', 0.0, 0.0, 0, 0.0, 0.0, ' ', ' ', - ' ', 'pass', '\nMax abs error is less than 0.001, consider as pass, skip other check and set to SPACE.\n']]) + ' ', ' ', ' ', ' ', 'pass', '\nMax abs error is less than 0.001, consider as pass, skip other check and set to SPACE.\n']]) def test_compare_output(self): bench_out, npu_out = torch.randn(100, 100), torch.randn(100, 100) bench_grad, npu_grad = [torch.randn(100, 100)], [torch.randn(100, 100)] api_name = 'Functional*conv2d*0' - is_fwd_success, is_bwd_success = self.compare.compare_output(api_name, bench_out, npu_out, bench_grad, npu_grad) + data_info = UtDataInfo(bench_grad, npu_grad, bench_out, npu_out, None, None, None) + is_fwd_success, is_bwd_success = self.compare.compare_output(api_name, data_info) self.assertFalse(is_fwd_success) self.assertFalse(is_bwd_success) dummy_input = torch.randn(100, 100) bench_out, npu_out = dummy_input, dummy_input - is_fwd_success, is_bwd_success = self.compare.compare_output(api_name, bench_out, npu_out) + data_info = UtDataInfo(None, None, bench_out, npu_out, None, None, None) + is_fwd_success, is_bwd_success = self.compare.compare_output(api_name, data_info) self.assertTrue(is_fwd_success) self.assertTrue(is_bwd_success) diff --git a/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_run_ut.py b/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_run_ut.py index fdcc1cfddeb38d4fca0d2a67a09147b571b35def..3c180fa23b3e27bdd2ccd91698320684f007e47a 100644 --- a/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_run_ut.py +++ b/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_run_ut.py @@ -61,10 +61,11 @@ class TestRunUtMethods(unittest.TestCase): self.assertEqual(cpu_kwargs, {'inplace': False}) def test_UtDataInfo(self): - data_info = UtDataInfo(None, None, None, None, None, None) - self.assertIsNone(data_info.bench_grad_out) - self.assertIsNone(data_info.device_grad_out) - self.assertIsNone(data_info.device_out) - self.assertIsNone(data_info.bench_out) + data_info = UtDataInfo(None, None, None, None, None, None, None) + self.assertIsNone(data_info.bench_grad) + self.assertIsNone(data_info.device_grad) + self.assertIsNone(data_info.device_output) + self.assertIsNone(data_info.bench_output) self.assertIsNone(data_info.grad_in) self.assertIsNone(data_info.in_fwd_data_list) + self.assertIsNone(data_info.backward_message) diff --git a/debug/accuracy_tools/grad_tool/README.md b/debug/accuracy_tools/grad_tool/README.md index 78acffbe685f4b724dd1cff82a48236276e8b16f..b5c11bb2045661e5df2d47c6897205eea452d775 100644 --- a/debug/accuracy_tools/grad_tool/README.md +++ b/debug/accuracy_tools/grad_tool/README.md @@ -77,27 +77,18 @@ -2. 在训练流程执行之前传入 config.yaml 的路径实例化一个 GradientDumper 对象。实例代码如下: +2. 在模型构造完成时插入如下代码: ```python from grad_tool.grad_monitor import GradientMonitor gm = GradientMonitor("config_path") + gm.monitor(model) ``` + config_path: 传入config.yaml 的路径实例化一个 GradientMonitor 对象 + + model: 传入刚构造好的模型进行监控 -3. 插入代码监控模型,有两种方法,选择其一即可: - - 推荐:在训练流程中,反向执行之后梯度清零之前的位置,调用 gm.save_grad 并将模型作为参数传入 - - ```python - gm.save_grad(model) - ``` - - 另一种:在训练开始前,调用 gm.monitor 并将模型作为参数传入。这种方式目前不稳定。 - - ```python - gm.monitor(model) - ``` ### 输出结果 **输出目录结构**(level 为 L2) diff --git a/debug/accuracy_tools/grad_tool/grad_comparator.py b/debug/accuracy_tools/grad_tool/grad_comparator.py index ff6302d4fc29d76796cdb533dc6ba3622bcc25a5..f2daf988cc4b45c48373f9f00a03d826da802513 100644 --- a/debug/accuracy_tools/grad_tool/grad_comparator.py +++ b/debug/accuracy_tools/grad_tool/grad_comparator.py @@ -114,8 +114,8 @@ class GradComparator: @staticmethod def _calculate_similarity(pt_file1: str, pt_file2: str): - tensor1 = torch.load(pt_file1) - tensor2 = torch.load(pt_file2) + tensor1 = torch.load(pt_file1, map_location=torch.device("cpu")) + tensor2 = torch.load(pt_file2, map_location=torch.device("cpu")) if tensor1.shape != tensor2.shape: raise Exception(f"tensor shape is not equal: {pt_file1}, {pt_file2}") if tensor1.dtype != torch.bool: diff --git a/debug/accuracy_tools/grad_tool/grad_monitor.py b/debug/accuracy_tools/grad_tool/grad_monitor.py index bf1a6da0ba31af41d6a7e43b6b3b4bacba75b4b8..9324d60932e80d2aa2c2063f15c41ee26b6b2529 100644 --- a/debug/accuracy_tools/grad_tool/grad_monitor.py +++ b/debug/accuracy_tools/grad_tool/grad_monitor.py @@ -1,9 +1,11 @@ import os +from collections import defaultdict import torch +from torch.optim.optimizer import register_optimizer_step_pre_hook from grad_tool.level_adapter import Level, LevelAdapter from grad_tool.grad_stat_csv import GradStatCsv -from grad_tool.utils import get_config, check_numeral_list_ascend, ListCache, data_in_list_target,\ - write_csv, get_rank_id, print_info_log, create_directory, print_warn_log +from grad_tool.utils import get_config, check_numeral_list_ascend, data_in_list_target,\ + write_csv, get_rank_id, print_info_log, create_directory, print_warn_log, print_rank_0 class GradientMonitor: @@ -27,81 +29,40 @@ class GradientMonitor: else: print_warn_log(f"the file in {self._output_path} will be recoverd") self._step = -1 - self._list_cache = ListCache() - - @staticmethod - def _hook_fun(param_name, f): - def backward_hook(grad): - f(param_name, grad) - return backward_hook + self._param2name = defaultdict(str) def _rank_in_targets(self): if not hasattr(self, "_rank"): raise AttributeError("grad monitor need attribute {_rank}") return not torch.distributed.is_initialized() or data_in_list_target(getattr(self, "_rank"), self._target_ranks) - - def _model_backward_hook(self, module, gin, gout): - self._step += 1 - if not hasattr(self, "_rank"): - setattr(self, "_rank", get_rank_id(gout)) - print_info_log(f"rank_{self._rank} exists") - if not self._rank_in_targets(): - return - self._list_cache.flush() - if not data_in_list_target(self._step, self._target_step): - return - print_info_log(f"result generate: rank_{self._rank} step_{self._step}") - output_path = os.path.join(self._output_path, f"rank_{getattr(self, '_rank')}", f"grad_summary_{self._step}.csv") - write_csv(output_path, [], GradStatCsv.generate_csv_header(level=self._level_adp, bounds=self._bounds)) - self._list_cache.set_output_file(output_path) - - def _save_grad_stat(self, param_name, grad): - if not self._rank_in_targets(): - return - if not data_in_list_target(self._step, self._target_step): - return - print_info_log(f"param result: rank{self._rank} step{self._step} {param_name}") - grad_info = GradStatCsv.generate_csv_line( - level=self._level_adp, - param_name=param_name, - grad=grad, - bounds=self._bounds) - self._list_cache.append(grad_info) - self._level_adp.save_grad_direction(param_name, grad, f'{self._output_path}/rank_{self._rank}/step_{self._step}') - def monitor(self, model): - last_module = None - for name, module in model.named_modules(): - last_module = module - last_module.register_backward_hook(self._model_backward_hook) - for param_name, param in model.named_parameters(): - if not data_in_list_target(param_name, self._param_list): - continue - if param is None or param.requires_grad == False: - continue - param.register_hook(GradientMonitor._hook_fun(param_name, self._save_grad_stat)) + def _hook_optimizer(self): + def optimizer_pre_step_hook(optimizer, args, kargs): + self._step += 1 + if not data_in_list_target(self._step, self._target_step): + return + output_lines = [] + for param, param_name in self._param2name.items(): + if not data_in_list_target(param_name, self._param_list): + continue + grad = param.main_grad if hasattr(param, "main_grad") else param.grad + grad_info = GradStatCsv.generate_csv_line( + level=self._level_adp, + param_name=param_name, + grad=grad, + bounds=self._bounds) + output_lines.append(grad_info) + self._level_adp.save_grad_direction(param_name, grad, f'{self._output_path}/rank_{self._rank}/step_{self._step}') + output_path = os.path.join(self._output_path, f"rank_{getattr(self, '_rank')}", f"grad_summary_{self._step}.csv") + write_csv(output_path, output_lines, GradStatCsv.generate_csv_header(level=self._level_adp, bounds=self._bounds)) + register_optimizer_step_pre_hook(optimizer_pre_step_hook) - def save_grad(self, model): - self._step += 1 - if not hasattr(self, "_rank"): - setattr(self, "_rank", get_rank_id(next(model.parameters()))) - if not self._rank_in_targets(): - return - if not data_in_list_target(self._step, self._target_step): + def monitor(self, model): + print_rank_0("> parameter names:") + for name, param in model.named_parameters(): + self._param2name[param] = name + print_rank_0(f"\t{name}") + setattr(self, "_rank", get_rank_id()) + if torch.distributed.is_initialized() and not data_in_list_target(getattr(self, "_rank"), self._target_ranks): return - print_info_log(f"save grad rank_{getattr(self, '_rank')} step_{self._step}") - output_path = os.path.join(self._output_path, f"rank_{getattr(self, '_rank')}", f"grad_summary_{self._step}.csv") - write_csv(output_path, [], GradStatCsv.generate_csv_header(level=self._level_adp, bounds=self._bounds)) - self._list_cache.set_output_file(output_path) - for param_name, param in model.named_parameters(): - if not data_in_list_target(param_name, self._param_list): - continue - if param.grad is not None: - grad = param.grad - elif hasattr(param, "main_grad") and param.main_grad is not None: - grad = param.main_grad - else: - continue - self._save_grad_stat(param_name, grad) - print_info_log(f"{param_name} is saved") - self._list_cache.flush() + self._hook_optimizer() diff --git a/debug/accuracy_tools/grad_tool/level_adapter.py b/debug/accuracy_tools/grad_tool/level_adapter.py index 64bb1e92a9272f2b071a9cc61094713ac7d84db2..e03bf2b110dc8041b356d83843e365e91236d32a 100644 --- a/debug/accuracy_tools/grad_tool/level_adapter.py +++ b/debug/accuracy_tools/grad_tool/level_adapter.py @@ -37,7 +37,6 @@ class LevelOps: param_grad = grad.clone().detach() is_positive = param_grad > 0 torch.save(is_positive, f'{save_path}/{param_name}.pt') - print_info_log(f'Save {param_name} bool tensor, it has {is_positive.sum()}/{is_positive.numel()} positive elements') class Level(ABC): diff --git a/debug/accuracy_tools/grad_tool/utils.py b/debug/accuracy_tools/grad_tool/utils.py index 8d0ed36ce30bdb502b26a6c5d7d0c1276ee8cc74..a7db58be29a9fbf55c04a8283cb68d09f9289796 100644 --- a/debug/accuracy_tools/grad_tool/utils.py +++ b/debug/accuracy_tools/grad_tool/utils.py @@ -1,6 +1,7 @@ import os import yaml import torch +import torch.distributed as dist import pandas as pd from ptdbg_ascend.src.python.ptdbg_ascend.common.file_check_util import FileOpen, create_directory, \ FileChecker, FileCheckConst @@ -8,34 +9,6 @@ from ptdbg_ascend.src.python.ptdbg_ascend.common.utils import check_file_or_dire print_warn_log -class ListCache(list): - threshold = 1000 - - def __init__(self, *args): - self._dump_count = 0 - super().__init__(*args) - - def __del__(self): - self.flush() - - def flush(self): - if len(self) == 0: - return - if not self._output_file: - print_warn_log("dumpfile path is not setted") - write_csv(self._output_file, self, []) - print_info_log(f"write {len(self)} items to {self._output_file} the {self._dump_count} time") - self.clear() - - def append(self, data): - list.append(self, data) - if len(self) >= ListCache.threshold: - self.flush() - - def set_output_file(self, output_file): - self._output_file = output_file - - def get_config(filepath): with FileOpen(filepath, 'r') as file: config = yaml.safe_load(file) @@ -52,6 +25,7 @@ def write_csv(filepath, content_list, header): filepath_checker.common_check() new_data = pd.DataFrame(list(content for content in content_list)) new_data.to_csv(filepath, mode='a+', header=False, index=False) + print_info_log(f"write {len(content_list)} items to {filepath}") def make_file_safety(file_path: str, permission=0o640): @@ -83,28 +57,19 @@ def check_numeral_list_ascend(lst): raise Exception("The input list should be ascending") -def get_tensor_rank(x): - if isinstance(x, (list, tuple)): - if len(x) > 0: - return get_tensor_rank(x[0]) - return None - elif isinstance(x, torch.Tensor): - device = x.device - if device.type == 'cpu': - return None - else: - return device.index - return None - - -def get_rank_id(tensor): +def get_rank_id(): if torch.distributed.is_initialized(): return torch.distributed.get_rank() - rank = get_tensor_rank(tensor) - if rank is not None: - return rank return os.getpid() def path_check(path, isdir=False): check_file_or_directory_path(path, isdir) + + +def print_rank_0(message): + if dist.is_initialized(): + if dist.get_rank() == 0: + print(message) + else: + print(message) diff --git a/debug/accuracy_tools/kj600/README.md b/debug/accuracy_tools/kj600/README.md new file mode 100644 index 0000000000000000000000000000000000000000..701b5abb64b937ee84c22205a938f2f7d271bdce --- /dev/null +++ b/debug/accuracy_tools/kj600/README.md @@ -0,0 +1,141 @@ +# kj600 模型训练状态监控工具 + +## 简介 + +本项目开发了名为kj600的模型训练状态监控工具,能够收集和聚合模型训练过程中的层和优化器的中间状态,帮助诊断模型训练过程中出现的异常情况。 + +## 安装 + +### 1. 安装依赖 + +| 依赖软件 | +| ----------- | +| PyTorch | +| torch_npu | +| torchvision | + +### 2. 安装 kj600 + +方式一:从 git 直接安装 + +``` +pip install git+https://gitee.com/xiangsen2/kj600.git +``` + +方式二:下载源码安装 + +``` +git clone https://gitee.com/xiangsen2/kj600.git +cd KJ600 +pip install -e . +``` + +# 快速上手 + + 下面以Ascend/ModelLink训练框架为例,给出kj600工具的使用方法。 + +1. 在ModelLink的根目录,创建json配置文件,如llama2_config.json,内容如下: + +``` +{ + "targets": { + "language_model.encoder.layers.0": {"input": "tuple[2]:0", "output": "tensor", "input_grad":"tuple[2]:0", "output_grad":"tuple[1]:0"} + }, + "module_ranks":"1,2,3,4", + "optimizer_ranks":"1,2,3,4" +} +``` + +每个要监控的module有特定的输入输出格式(依赖于模型实现),所以我们需要指定前向输入输出格式和反向计算时输入张量的梯度和输出张量的梯度格式。 如果不清楚的话可以先猜测, 格式规范与实际输入不同时会报详细错误。 我们也会随时更新更多常用module的格式规范。 + +下面详细解释各个字段: + +"targets":必选字段,指定需要监控的大模型层, 例如transformer的第0层language_model.encoder.layers.0。如果不清楚层命名, 可以使用空的json配置文件, 之后监控工具会打印模型中torch module的名字, 你可以从中选择你关心的module。 + +"input":可选字段,"tuple[2]:0"的意思是目标module的前向input参数为长度为2的tuple, 我们关心的是tuple第0个元素。 + +"output":必选字段,"tensor"的意思是目标module的前向output参数类型为tensor + +"input_grad":可选字段,"tuple[2]:0"的意思是目标module的后向input_grad参数是长度为2的tuple, 我们关心的是tuple的第0个元素。 + +"output_grad":必选字段,"tuple[1]:0"的意思是目标module的后向input_grad参数是长度为1的tuple, 我们关心的是tuple的第0个元素。 + +"module_ranks":可选字段,用于在分布式训练场景中希望控制在哪些rank开启module监控。如果不填,则默认在所有rank开启。 + +"optimizer_ranks":可选字段,用于在分布式训练场景中希望控制在哪些rank开启optimizer监控。如果不填,则默认在所有rank开启。 + +下面给出transformer架构模型中常见的module的前向计算的输入输出和反向计算输入张量的梯度和输出张量的梯度格式,以供参考: + +| module | input | output | input_grad | output_grad | +| ------------------------------------------------------------ | -------- | -------- | ---------- | ----------- | +| language_model.embedding.word_embeddings | tuple[1] | tensor | tuple[1] | tuple[1] | +| language_model.embedding.embedding_dropout | tuple[1] | tensor | tuple[1] | tuple[1] | +| language_model.encoder.layers.0 | tuple[2] | tensor | tuple[2] | tuple[1] | +| language_model.encoder.layers.0.input_norm | tuple[1] | tensor | tuple[1] | tuple[1] | +| language_model.encoder.layers.0.self_attention | tuple[2] | tuple[2] | tuple[2] | tuple[2] | +| language_model.encoder.layers.0.self_attention.query_key_value | tuple[1] | tuple[2] | tuple[1] | tuple[2] | +| language_model.encoder.layers.2.self_attention.core_attention_flash | tuple[3] | tensor | tuple[3] | tuple[1] | +| language_model.encoder.layers.0.self_attention.dense | tuple[1] | tuple[2] | tuple[1] | tuple[2] | +| language_model.encoder.layers.0.post_attention_norm | tuple[1] | tensor | tuple[1] | tuple[1] | +| language_model.encoder.layers.0.mlp | tuple[1] | tuple[2] | tuple[1] | tuple[2] | +| language_model.encoder.final_norm | tuple[1] | tensor | tuple[1] | tuple[1] | + +对于language_model.embedding.word_embeddings这类输入层,我们不关心输入的情况下,可以不填"input"和"input_grad",监控的状态中不会包含输入的相关信息。config文件示例如下: + +``` +{ + "targets": { + "language_model.embedding.word_embeddings": {"output": "tensor","output_grad":"tuple[1]:0"} + } +} +``` + +2. 在训练器中加入代码,开启kj600训练监控。 + + 例如在ModelLink/pretrain_gpt.py的model_provider GPTModel构造后加入以下代码: + + ``` + from kj600.module_hook import TrainerMon + hooker = TrainerMon("./llama2_config.json") + hooker.hook_modules(model=model, global_batch_size=args.global_batch_size, dp=args.data_parallel_size, micro_batch_size=args.micro_batch_size, fwd_or_bkd=0) + ``` + + 如果要监控混合精度优化器的动量和方差, 需要在混合精度优化器构造后加入如下代码: + + ``` + model, optimizer, opt_param_scheduler = setup_model_and_optimizer( + model_provider, model_type) + # 插入位置 + from kj600.module_hook import TrainerMon + TrainerMon.set_wrapped_optimizer(optimizer) + ``` + +3. 配置tensorboard写入的目录 + + ``` + export KJ600_OUTPUT_DIR=/xxx/output_dir + ``` + +4. 开始预训练,在日志中如果发现以下内容, 则说明指定的模块被成功监视。 + + ``` + > language_model.encoder.layers.0 is monitored successfully + > 1 out of 1 modules are monitored. + ``` + +5. 训练过程中,打开tensorboard,可以查看训练的中间状态: + +``` +tensorboard --logdir=$KJ600_OUTPUT_DIR +``` + +之后,运行以下SSH命令来建立端口转发,可以在本地通过http://localhost:6006访问tensorboard: + +``` +ssh -N -L localhost:6006:localhost:6006 your_username@remote_server_address +``` +## 高级用法 +### 有效秩(有内存和速度问题) +在工具配置文件中加入"params_effrank":"权重矩阵参数名" +"params_effrank": ["language_model.encoder.layers.0.self_attention.query_key_value.weight"] + diff --git a/debug/accuracy_tools/kj600/kj600/__init__.py b/debug/accuracy_tools/kj600/kj600/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/debug/accuracy_tools/kj600/kj600/config.json b/debug/accuracy_tools/kj600/kj600/config.json new file mode 100644 index 0000000000000000000000000000000000000000..733a3f0d8f8ea7d858f0a38b5c5cd196c97c6d68 --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/config.json @@ -0,0 +1,10 @@ +// config.json examples +// +// example1: +// { +// "targets": { +// "fc": {"input": "tuple[1]:0", "output": "tensor", "input_grad":"tuple[1]:0", "output_grad":"tuple[1]:0"}, +// "relu": {"input": "tuple[1]:0", "output": "tensor", "input_grad":"tuple[1]:0", "output_grad":"tuple[1]:0"} +// }, +// "module_ranks":"1,2,3,4" +// } \ No newline at end of file diff --git a/debug/accuracy_tools/kj600/kj600/features.py b/debug/accuracy_tools/kj600/kj600/features.py new file mode 100644 index 0000000000000000000000000000000000000000..7e726b5227dc675ad4278fd65a3097d6af1aa90e --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/features.py @@ -0,0 +1,45 @@ +import torch +from torch.autograd.functional import jacobian + + +@torch.no_grad +def square_sum(x: torch.tensor): + return (x * x).sum() + + +@torch.no_grad +def eff_rank(param: torch.tensor, threshold=1e-10): + U, S, Vh = torch.linalg.svd(param.float()) + rank = torch.sum(S > threshold) + return rank + + +# modular neural tangent kernel +@torch.no_grad +def mNTK(module: torch.nn.Module, x: torch.tensor): + J_theta_l = jacobian(module, x) + mntk = torch.matmul(J_theta_l, J_theta_l.t()) + return mntk + + +@torch.no_grad +def power_iteration(A, num_iterations): + b = torch.randn(A.size(1), 1) + for _ in range(num_iterations): + b = torch.matmul(A, b) + b_norm = torch.norm(b) + b = b / b_norm if b_norm != 0 else 0 + eigval = torch.matmul(torch.matmul(b.t(), A), b) + return eigval + + +@torch.no_grad +def lambda_max_subsample(module: torch.nn.Module, x: torch.tensor, num_iterations=100, subsample_size=None): + mntk = mNTK(module, x) + if subsample_size is None: + subsample_size = min(mntk.size(0), mntk.size(1)) + idx = torch.randperm(mntk.size(0))[:subsample_size] + subsampled = mntk[idx, :] + subsampled = subsampled[:, idx] + eigval = power_iteration(subsampled, num_iterations) + return eigval diff --git a/debug/accuracy_tools/kj600/kj600/module_hook.py b/debug/accuracy_tools/kj600/kj600/module_hook.py new file mode 100644 index 0000000000000000000000000000000000000000..570799ff7cdf8ca8768c59ef1622cee14b56941d --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/module_hook.py @@ -0,0 +1,234 @@ +import os +import uuid +from collections import defaultdict +from typing import List +from datetime import datetime +import torch +from torch.nn.modules.module import register_module_forward_hook +import torch.distributed as dist +from torch.optim.optimizer import register_optimizer_step_pre_hook, register_optimizer_step_post_hook +from torch.utils.tensorboard import SummaryWriter +from kj600.features import square_sum +from kj600.module_spec_verifier import get_config, validate_config_spec +from kj600.optimizer_collect import MixPrecsionOptimizerMon, print_rank_0 +from kj600.features import eff_rank + + +def get_summary_writer_tag_name(module_or_param_name:str, tag:str, rank): + if rank is None: + return f"{module_or_param_name}/{tag}" + else: + return f"{module_or_param_name}/{rank}/{tag}" + + +class ModuleHookContext: + def __init__(self, module_name) -> None: + self.step = 0 + self.micro_step = 0 + self.actv = [] + self.actvgrad = [] + self.module_name = module_name + self.format_by_arg = {} + self.verified = False + self.focused_in_col = 0 + self.focused_out_col = 0 + self.ignore_in = False # no need to care when no key 'input' or 'input_grad' found + + def set_format_by_arg(self, key_name:str, target_config:dict): + if key_name in target_config[self.module_name]: + self.format_by_arg[key_name] = target_config[self.module_name][key_name] + elif key_name in ['input', 'input_grad']: + self.ignore_in = True + else: + raise KeyError(f"Missing key: {key_name} of {self.module_name} in config.json") + + +class OptimizerContext: + def __init__(self) -> None: + self.step = 0 + self.param_gnorm = defaultdict(float) + self.param_exp_avg_norm = defaultdict(float) + self.param_exp_avg_sq_norm = defaultdict(float) + self.param_effective_rank = defaultdict(float) + + +class TrainerMon: + + @staticmethod + def set_wrapped_optimizer(_wrapped_optimizer): + MixPrecsionOptimizerMon.set_wrapped_optimizer(_wrapped_optimizer) + + def __init__(self, config_file_path) -> None: + self.module_fwd_hook_context_by_module = defaultdict(ModuleHookContext) + self.module_bwd_hook_context_by_module = defaultdict(ModuleHookContext) + self.optimizer_context = defaultdict(OptimizerContext) + self.params_have_main_grad = True + self.config = get_config(config_file_path) + self.module_rank_list = [int(rank) for rank in self.config.get("module_ranks", "").split(',') if rank.strip()] + + self.optimizer_hooked = False + output_base_dir = os.getenv('KJ600_OUTPUT_DIR', './kj600_output') + cur_time = datetime.now().strftime('%b%d_%H-%M-%S') + unique_id = str(uuid.uuid4())[:8] + if dist.is_initialized(): + if (dist.get_rank() in self.module_rank_list) or len(self.module_rank_list) == 0: + self.summary_writer = SummaryWriter(os.path.join(output_base_dir, f"{cur_time}-rank{dist.get_rank()}-{unique_id}")) + else: + self.summary_writer = SummaryWriter(os.path.join(output_base_dir, f"{cur_time}-{unique_id}")) + self.micro_batch_number = 0 + + self.param_name_list = [] + self.param2name = defaultdict(str) + + self.mix_precision_optimizer_mon = MixPrecsionOptimizerMon() + return + + def __del__(self): + if hasattr(self, "summary_writer"): + self.summary_writer.close() + + def _hook_module(self, target_name:str, module: torch.nn.Module, fwd_or_bkd): + paths = target_name.split('.') + if '_modules' not in module.__dict__: + # nothing to hook + return 0 + + def fwd_hook_fun(module, module_input, module_output): + context = self.module_fwd_hook_context_by_module[module] + if not context.format_by_arg: + context.set_format_by_arg('input', self.config['targets']) + context.set_format_by_arg('output', self.config['targets']) + if not context.verified: + if not context.ignore_in: + context.focused_in_col = validate_config_spec(context.format_by_arg['input'], module_input, context.module_name, 'input') + context.focused_out_col = validate_config_spec(context.format_by_arg['output'], module_output, context.module_name, 'output') + context.verified = True + # expect output be tensor type + if not context.ignore_in: + cared_input = module_input if context.focused_in_col is None else module_input[context.focused_in_col] + cared_input_cal_result = square_sum(cared_input) + else: + cared_input_cal_result = None + cared_output = module_output if context.focused_out_col is None else module_output[context.focused_out_col] + context.actv.append((cared_input_cal_result, square_sum(cared_output))) + + context.micro_step += 1 + if context.micro_step == self.micro_batch_number: + context.micro_step = 0 + context.step += 1 + return + + def bwd_hook_fun(module, input_grad, output_grad): + context = self.module_bwd_hook_context_by_module[module] + if not context.format_by_arg: + context.set_format_by_arg('input_grad', self.config['targets']) + context.set_format_by_arg('output_grad', self.config['targets']) + if not context.verified: + if not context.ignore_in: + context.focused_in_col = validate_config_spec(context.format_by_arg['input_grad'], input_grad, context.module_name, 'input_grad') + context.focused_out_col = validate_config_spec(context.format_by_arg['output_grad'], output_grad, context.module_name, 'output_grad') + context.verified = True + if not context.ignore_in: + cared_input_grad = input_grad if context.focused_in_col is None else input_grad[context.focused_in_col] + cared_input_grad_cal_result = square_sum(cared_input_grad) + else: + cared_input_grad_cal_result = None + cared_output_grad = output_grad if context.focused_out_col is None else output_grad[context.focused_out_col] + context.actvgrad.append((cared_input_grad_cal_result, square_sum(cared_output_grad))) + context.micro_step += 1 + if context.micro_step == self.micro_batch_number: + context.micro_step = 0 + context.step += 1 + return + + for name, submodule in module.named_modules(): + if name == target_name: + submodule.register_forward_hook(fwd_hook_fun) + self.module_fwd_hook_context_by_module[submodule] = ModuleHookContext(name) + submodule.register_full_backward_hook(bwd_hook_fun) + self.module_bwd_hook_context_by_module[submodule] = ModuleHookContext(name) + print_rank_0(f"> {name} is monitored successfully") + return 1 + return 0 + + def hook_modules(self, model:torch.nn.Module, global_batch_size, dp, micro_batch_size, fwd_or_bkd, params_have_main_grad=True): + # fwd=0, bkd=1 + # targets is module name list like ["xx.xxx1", "xxx.xxx2"] which can be obtained when first run. + print_rank_0("> module names:") + for name, _ in model.named_modules(): + print_rank_0(f"\t{name}") + self.micro_batch_number = global_batch_size // dp // micro_batch_size + + if not self.module_rank_list or (dist.is_initialized() and dist.get_rank() in self.module_rank_list): + hooked = 0 + for target, _ in self.config['targets'].items(): + hooked += self._hook_module(target, model, fwd_or_bkd=0) + print_rank_0(f"> {hooked} out of {len(self.config['targets'])} are monitored.") + else: + return + + if not self.optimizer_hooked: + self.optimizer_hooked = True + print_rank_0("> parameter names:") + for name, param in model.named_parameters(): + print_rank_0(f"\t{name}") + for target_module, _ in self.config['targets'].items(): + if name.startswith(target_module): # name : language_model.encoder.layers.0.mlp.weight, target_module:language_model.encoder.layers.0 + self.param_name_list.append(name) + self.param2name[param] = name + self.hook_optimizer() + self.params_have_main_grad = params_have_main_grad + return + + def hook_optimizer(self): + # in DDP by default use params_have_main_grad + def optimizer_pre_step_hook(optimizer, args, kwargs): + context = self.optimizer_context[optimizer] + for param, name in self.param2name.items(): + grad_for_norm = param.main_grad if self.params_have_main_grad else param.grad + context.param_gnorm[name] = grad_for_norm.detach().norm() + if "params_effrank" in self.config and name in self.config["params_effrank"]: + context.param_effective_rank[name] = eff_rank(param.detach()) + + context.param_exp_avg_norm, context.param_exp_avg_sq_norm = self.mix_precision_optimizer_mon.fetch_mv( + optimizer, self.param2name) + return + + def optimizer_post_step_hook(optimizer, args, kwargs): + context = self.optimizer_context[optimizer] + rank = dist.get_rank() if dist.is_initialized() else None + for _, fwd_context in self.module_fwd_hook_context_by_module.items(): + if not len(fwd_context.actv) == self.micro_batch_number: + raise Exception(f"fwd_context.actv not equal to micro_batch_number: {len(fwd_context.actv)}, {self.micro_batch_number}") + if not fwd_context.ignore_in: + x_norm = sum([x.item() for x, _ in fwd_context.actv]) + self.summary_writer.add_scalar(get_summary_writer_tag_name(fwd_context.module_name, 'input', rank), x_norm, context.step) + y_norm = sum([y.item() for _, y in fwd_context.actv]) + self.summary_writer.add_scalar(get_summary_writer_tag_name(fwd_context.module_name, 'output', rank), y_norm, context.step) + fwd_context.actv.clear() + + for _, bwd_context in self.module_bwd_hook_context_by_module.items(): + if not len(bwd_context.actvgrad) == self.micro_batch_number: + raise Exception(f"fwd_context.actvgrad not equal to micro_batch_number: {len(fwd_context.actvgrad)}, {self.micro_batch_number}") + if not bwd_context.ignore_in: + x_grad_norm = sum([x.item() for x, _ in bwd_context.actvgrad]) + self.summary_writer.add_scalar(get_summary_writer_tag_name(bwd_context.module_name, 'input_grad', rank), x_grad_norm, context.step) + y_grad_norm = sum([y.item() for _, y in bwd_context.actvgrad]) + self.summary_writer.add_scalar(get_summary_writer_tag_name(bwd_context.module_name, 'output_grad', rank), y_grad_norm, context.step) + bwd_context.actvgrad.clear() + + for param_name, grad_norm in context.param_gnorm.items(): + self.summary_writer.add_scalar(get_summary_writer_tag_name(param_name, 'weight_grad', rank), grad_norm.item(), context.step) + + for param_name, exp_avg_norm in context.param_exp_avg_norm.items(): + self.summary_writer.add_scalar(get_summary_writer_tag_name(param_name, 'exp_avg_norm', rank), exp_avg_norm.item(), context.step) + for param_name, exp_avg_sq_norm in context.param_exp_avg_sq_norm.items(): + self.summary_writer.add_scalar(get_summary_writer_tag_name(param_name, 'exp_avg_sq_norm', rank), exp_avg_sq_norm.item(), context.step) + + context.step += 1 + + return + if not self.module_rank_list or (dist.is_initialized() and dist.get_rank() in self.module_rank_list): + register_optimizer_step_pre_hook(optimizer_pre_step_hook) + register_optimizer_step_post_hook(optimizer_post_step_hook) + return diff --git a/debug/accuracy_tools/kj600/kj600/module_spec_verifier.py b/debug/accuracy_tools/kj600/kj600/module_spec_verifier.py new file mode 100644 index 0000000000000000000000000000000000000000..cfd1ebc6ffc56d6d821507f062704d62501723db --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/module_spec_verifier.py @@ -0,0 +1,68 @@ +import json +import re +import abc +import torch + + +def get_config(file_path='config.json'): + with open(file_path, 'r') as file: + config = json.load(file) + return config + +# 用于存储所有validator实现类的注册表 +config_validator_registry = {} + + +def register_config_validator(cls): + """装饰器 用于注册ConfigValidator的实现类""" + config_validator_registry[cls.__name__] = cls + return cls + + +class ConfigValidator(metaclass=abc.ABCMeta): + @abc.abstractmethod + def check_pattern_match(self, config_spec:str): + pass + + @abc.abstractmethod + def validate(self, actual_data, module_name:str, data_type:str, pattern_match): + pass + + +@register_config_validator +class TensorValidator(ConfigValidator): + def check_pattern_match(self, config_spec:str): + pattern = re.compile(r"tensor") + return pattern.match(config_spec) + + def validate(self, actual_data, module_name:str, data_type:str, pattern_match): + if not torch.is_tensor(actual_data): + raise ValueError(f"Format of {module_name} {data_type} does not match the required format 'tensor' in config.") + return None + + +@register_config_validator +class TupleValidator(ConfigValidator): + def check_pattern_match(self, config_spec:str): + pattern = re.compile(r"tuple\[(\d+)\]:(\d+)") + return pattern.match(config_spec) + + def validate(self, actual_data, module_name: str, data_type: str, pattern_match): + length, index = map(int, pattern_match.groups()) + if not (0 <= index < length): + raise ValueError(f"Format of {module_name} {data_type} in config.json does not match the required format 'tuple[x]:y'. y must be greater than or equal to 0 and less than x.") + if not isinstance(actual_data, tuple): + raise ValueError(f"Type of {module_name} {data_type} does not match spec of config.json, should be tuple, please check.") + if len(actual_data) != length: + raise ValueError(f"Length of {module_name} {data_type} does not match spec of config.json, should be {length}, actual is {len(actual_data)} please check.") + return index + + +def validate_config_spec(config_spec:str, actual_data, module_name:str, data_type:str): + for _, validator_cls in config_validator_registry.items(): + config_validator = validator_cls() + pattern_match = config_validator.check_pattern_match(config_spec) + if pattern_match: + focused_col = config_validator.validate(actual_data, module_name, data_type, pattern_match) + return focused_col + raise ValueError(f"config spec in {module_name} {data_type} not supported, expected spec:'tuple\[(\d+)\]:(\d+)' or 'tensor', actual spec: {config_spec}.") diff --git a/debug/accuracy_tools/kj600/kj600/optimizer_collect.py b/debug/accuracy_tools/kj600/kj600/optimizer_collect.py new file mode 100644 index 0000000000000000000000000000000000000000..d0991718c224f3d8c10fd11a0a1d9efc3488d9cc --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/optimizer_collect.py @@ -0,0 +1,46 @@ +from collections import defaultdict +import torch.distributed as dist + + +def print_rank_0(message, debug=False, force=False): + if dist.is_initialized(): + if dist.get_rank() == 0: + print(message) + else: + print(message) + + +class MixPrecsionOptimizerMon: + wrapped_optimizer = None + + @staticmethod + def set_wrapped_optimizer(_wrapped_optimizer): + MixPrecsionOptimizerMon.wrapped_optimizer = _wrapped_optimizer + + def __init__(self) -> None: + self.fp16_to_fp32_param = {} + + def fetch_mv(self, torch_opt, params2name): + mix_prec_opt = MixPrecsionOptimizerMon.wrapped_optimizer + + if not self.fp16_to_fp32_param and mix_prec_opt is not None: + for fp16_group, fp32_group in zip(mix_prec_opt.float16_groups, mix_prec_opt.fp32_from_float16_groups): + for fp16_param, fp32_param in zip(fp16_group, fp32_group): + self.fp16_to_fp32_param[fp16_param] = fp32_param + + exp_avg_norm_dict = defaultdict(float) + exp_avg_sq_norm_dict = defaultdict(float) + + for param, name in params2name.items(): + if param in self.fp16_to_fp32_param: + param = self.fp16_to_fp32_param[param] + + if param in torch_opt.state: + exp_avg = torch_opt.state[param]["exp_avg"] + exp_avg_sq = torch_opt.state[param]["exp_avg_sq"] + exp_avg_norm = exp_avg.detach().norm() + exp_avg_sq_norm = exp_avg_sq.detach().norm() + exp_avg_norm_dict[name] = exp_avg_norm + exp_avg_sq_norm_dict[name] = exp_avg_sq_norm + + return exp_avg_norm_dict, exp_avg_sq_norm_dict \ No newline at end of file diff --git a/debug/accuracy_tools/kj600/kj600/unittest/config_1.json b/debug/accuracy_tools/kj600/kj600/unittest/config_1.json new file mode 100644 index 0000000000000000000000000000000000000000..fc6196fc191c4f5792f772858b6d1e25374edc03 --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/unittest/config_1.json @@ -0,0 +1,6 @@ +{ + "targets": { + "fc": {"input": "tuple[1]:0", "output": "tensor", "input_grad":"tuple[1]:0", "output_grad":"tuple[1]:0"}, + "relu": {"input": "tuple[1]:0", "output": "tensor", "input_grad":"tuple[1]:0", "output_grad":"tuple[1]:0"} + } +} \ No newline at end of file diff --git a/debug/accuracy_tools/kj600/kj600/unittest/test_features.py b/debug/accuracy_tools/kj600/kj600/unittest/test_features.py new file mode 100644 index 0000000000000000000000000000000000000000..bc8c6dd71ab4e0bf708cf3d97d02dab3a2ded9cc --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/unittest/test_features.py @@ -0,0 +1,33 @@ +import unittest +import torch +import torch.nn as nn +import torch_npu +from kj600.features import eff_rank + + +class TestFeatureCalculation(unittest.TestCase): + def test_effective_rank(self): + param = torch.randn(10, 10).npu() + rank = eff_rank(param) + self.assertTrue(rank.item() >= 1) + + def test_lambda_max(self): + pass + # input_dim = 10 + # hidden_dim = 100 + # output_dim = 1 + # num_samples = 100 + # X = torch.randn(num_samples, input_dim) + # network = nn.Sequential( + # nn.Linear(input_dim, hidden_dim), + # nn.ReLU(), + # nn.Linear(hidden_dim, output_dim) + # ) + # Y = network(X) + # Y.backward() + # for name, param in network.named_parameters(): + # lm = lambda_max(param) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/debug/accuracy_tools/kj600/kj600/unittest/test_module_hook.py b/debug/accuracy_tools/kj600/kj600/unittest/test_module_hook.py new file mode 100644 index 0000000000000000000000000000000000000000..f077fc7004dafddc0836300d5e0ffc19d1ed3d06 --- /dev/null +++ b/debug/accuracy_tools/kj600/kj600/unittest/test_module_hook.py @@ -0,0 +1,78 @@ +import argparse +import torch_npu +import torch +import torch.nn.functional as F +from kj600.module_hook import TrainerMon # Modify PYTHONPATH to import TrainerMon +#from hook_api import reg_grad_hook, reg_grad_one_hook, reg_module_backward_hook, reg_module_forward_hook +#from torch.cuda.amp import GradScaler + +from torch.npu.amp import GradScaler + + +# from ptdbg_ascend import PrecisionDebugger as PD +# from monitor import GradientMonitor + +print(torch_npu.__version__) + +#debugger = PD(dump_path="./dump/", hook_name="dump", step=[1, 2, 3], enable_dataloader=False) +#debugger.configure_hook(mode="list", scope=["optim_Adam_step"], ) + +parser = argparse.ArgumentParser(prog="kj600 debug", description="kj600 sample code", epilog="") +parser.add_argument("-o", "--out_dir", type=str, default=".") +args = parser.parse_args() +DTYPE = torch.float32 + + +class Model(torch.nn.Module): + def __init__(self): + super().__init__() + self.fc = torch.nn.Linear(784, 10, dtype=DTYPE) + self.relu = torch.nn.ReLU() + + def forward(self, x): + return self.relu(self.fc(x).type(DTYPE)) + +npu = torch.device('npu:0') +net = Model().to(device=npu) + +config = { + "targets": { + "fc": {"input": "tuple[2]:0", "output": "tensor::"}, + "relu": {"input": "..", "output": ".."} + } +} +# reg_grad_hook(net, hook_factory=hook_factory, config=config) +# reg_grad_one_hook(net, hook=monitor_hook, config=config) +# net.fc.register_forward_hook(get_actv_hook("fc")) +# reg_module_forward_hook(net, module_fwd_hook, config) +# reg_module_backward_hook(net, module_bwd_hook, config) +optimizer = torch.optim.Adam(net.parameters(), lr=0.0001) + +hooker = TrainerMon('./kj600/unittest/config_1.json') +hooker.hook_modules(model=net, global_batch_size=2, dp=1, micro_batch_size=2, fwd_or_bkd=0, params_have_main_grad=False) +# hooker.hook_optimizer(optimizer) + + +class ToyDataset(torch.utils.data.Dataset): + def __init__(self): + self.data = torch.randn(16, 784, dtype=DTYPE, requires_grad=True) + self.labels = torch.randint(low=0, high=9, size=(16,)) + + def __len__(self): + return len(self.labels) + + def __getitem__(self, idx): + return self.data[idx].to(npu), self.labels[idx].to(npu) + +train_ds = ToyDataset() +train_loader = torch.utils.data.DataLoader(train_ds, shuffle=True, batch_size=2) + + +# scaler = GradScaler() +for (inputs, labels) in train_loader: + optimizer.zero_grad() + outputs = net(inputs) + loss = F.cross_entropy(outputs, labels) + + loss.backward() + optimizer.step() diff --git a/debug/accuracy_tools/kj600/pyproject.toml b/debug/accuracy_tools/kj600/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..8ab303b81b8a53424fc4815849f3662134445b5f --- /dev/null +++ b/debug/accuracy_tools/kj600/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "kj600" +version = "0.0.1" +dependencies = [ + "torch", + "torch_npu", + "torchvision", +] + +[tool.setuptools.packages] +find = {} # Scan the project directory with the default parameters \ No newline at end of file diff --git a/debug/accuracy_tools/ptdbg_ascend/README.md b/debug/accuracy_tools/ptdbg_ascend/README.md index 3f71267650fd8197d043a48923a8e1e65a888d2c..97db6dd193bbe068592b87afdbeed3806fb96aff 100644 --- a/debug/accuracy_tools/ptdbg_ascend/README.md +++ b/debug/accuracy_tools/ptdbg_ascend/README.md @@ -10,10 +10,7 @@ | ptdbg_ascend版本 | 发布日期 | 支持PyTorch版本 | 下载链接 | 参考指南 | 校验码 | | ---------------- | ---------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | - | 5.0.T4 | 2024-03-15 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T4-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T4-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T4](doc/ptdbg_ascend精度工具功能说明_v5.0.T4.md) | e350da21be8836dad7db4e7bc797f7aae93337b7fc743ab195595516b6251970 | - | 5.0.T3 | 2024-02-23 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T3-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T3-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T3](doc/ptdbg_ascend精度工具功能说明_v5.0.T3.md) | 6cbc38046eb0eaefdf1e698db2a8d9710e79b26b835f4c72fd399fee5817504c | - | 5.0.T2 | 2024-02-07 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T2-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T2-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T2](doc/ptdbg_ascend精度工具功能说明_v5.0.T2.md) | 088173a5815070ec4d0b0b9391c823793e11e8cbd9751ac58ceaf02b004ac1e5 | - | 5.0.T1 | 2024-01-24 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T1-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T1-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T1](doc/ptdbg_ascend精度工具功能说明_v5.0.T1.md) | ba9aee303d4342889c1988026be37526dedf25c2de64ad12e10ee2a02b7112bd | + | 5.0 | 2024-04-11 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0](doc/ptdbg_ascend精度工具功能说明_v5.0.md) | 15ce1fb598781a9a03c7e8a28b1a9c400b52562c806c35649e929115cbe8b4f4 | | 4.0 | 2023-11-23 | 1.11.0/2.0/2.1 | [ptdbg_ascend-4.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/4.0/ptdbg_ascend-4.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v4.0](doc/ptdbg_ascend精度工具功能说明_v4.0.md) | ba7ff7a1acffb1a2fab02fea76b6f957b2868bc6b66d72365622f6a8950406c6 | | 3.0 | 2023-10-16 | 1.8.1/1.11.0/2.0/2.1 | [ptdbg_ascend-3.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/3.0/ptdbg_ascend-3.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v3.0](doc/ptdbg_ascend精度工具功能说明_v3.0.md) | eb177ec795f8ae8b0c937a3cf543914f535bb64c76ba2e520fc6f0456ff6740b | | 2.0 | 2023-7-07 | 1.8.1/1.11.0/2.0 | [ptdbg_ascend-2.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/2.0/ptdbg_ascend-2.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v2.0](doc/ptdbg_ascend精度工具功能说明_v2.0.md) | 85e046f133f0f40ed660337ce8207249b1dac47ac668910625bea49809f31d66 | @@ -121,10 +118,7 @@ ptdbg_ascend精度工具的安装方式包括:**下载whl包安装**和**源 | ptdbg_ascend版本 | 发布日期 | 支持PyTorch版本 | 下载链接 | 参考指南 | 校验码 | | ---------------- | ---------- | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | - | 5.0.T4 | 2024-03-15 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T4-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T4-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T4](doc/ptdbg_ascend精度工具功能说明_v5.0.T4.md) | e350da21be8836dad7db4e7bc797f7aae93337b7fc743ab195595516b6251970 | - | 5.0.T3 | 2024-02-23 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T3-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T3-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T3](doc/ptdbg_ascend精度工具功能说明_v5.0.T3.md) | 6cbc38046eb0eaefdf1e698db2a8d9710e79b26b835f4c72fd399fee5817504c | - | 5.0.T2 | 2024-02-07 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T2-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T2-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T2](doc/ptdbg_ascend精度工具功能说明_v5.0.T2.md) | 088173a5815070ec4d0b0b9391c823793e11e8cbd9751ac58ceaf02b004ac1e5 | - | 5.0.T1 | 2024-01-24 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0.T1-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0.T1-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0.T1](doc/ptdbg_ascend精度工具功能说明_v5.0.T1.md) | ba9aee303d4342889c1988026be37526dedf25c2de64ad12e10ee2a02b7112bd | + | 5.0 | 2024-04-11 | 1.11.0/2.0/2.1 | [ptdbg_ascend-v5.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/5.0/ptdbg_ascend-5.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v5.0](doc/ptdbg_ascend精度工具功能说明_v5.0.md) | 15ce1fb598781a9a03c7e8a28b1a9c400b52562c806c35649e929115cbe8b4f4 | | 4.0 | 2023-11-23 | 1.11.0/2.0/2.1 | [ptdbg_ascend-4.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/4.0/ptdbg_ascend-4.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v4.0](doc/ptdbg_ascend精度工具功能说明_v4.0.md) | ba7ff7a1acffb1a2fab02fea76b6f957b2868bc6b66d72365622f6a8950406c6 | | 3.0 | 2023-10-16 | 1.8.1/1.11.0/2.0/2.1 | [ptdbg_ascend-3.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/3.0/ptdbg_ascend-3.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v3.0](doc/ptdbg_ascend精度工具功能说明_v3.0.md) | eb177ec795f8ae8b0c937a3cf543914f535bb64c76ba2e520fc6f0456ff6740b | | 2.0 | 2023-7-07 | 1.8.1/1.11.0/2.0 | [ptdbg_ascend-2.0-py3-none-any.whl](https://ptdbg.obs.myhuaweicloud.com/package/ptdbg_ascend/2.0/ptdbg_ascend-2.0-py3-none-any.whl) | [ptdbg_ascend精度工具功能说明_v2.0](doc/ptdbg_ascend精度工具功能说明_v2.0.md) | 85e046f133f0f40ed660337ce8207249b1dac47ac668910625bea49809f31d66 | diff --git a/debug/accuracy_tools/ptdbg_ascend/configure.py b/debug/accuracy_tools/ptdbg_ascend/configure.py index 7a3f87c08ef8973715ac016a312db1fffef4c359..3508896fba9d841df5d7056085e1785d184f07ba 100644 --- a/debug/accuracy_tools/ptdbg_ascend/configure.py +++ b/debug/accuracy_tools/ptdbg_ascend/configure.py @@ -19,6 +19,7 @@ _PYTORCH_VERSION_1_8 = "1.8" _PYTORCH_VERSION_1_11 = "1.11" _PYTORCH_VERSION_2_0 = "2.0" _PYTORCH_VERSION_2_1 = "2.1" +_PYTORCH_VERSION_2_2 = "2.2" _PYTHON_BIN_PATH_ENV = "ADAPTER_TARGET_PYTHON_PATH" _ASCEND_INSTALLED_PATH_ENV = "ASCEND_INSTALLED_PATH" @@ -50,7 +51,7 @@ def setup_python(env_path): """Get python install path.""" default_python_bin_path = sys.executable ask_python_bin_path = ('Please specify the location of python with valid ' - 'pytorch 1.11/2.0/2.1 site-packages installed. [Default ' + 'pytorch 1.11/2.0/2.1/2.2 site-packages installed. [Default ' 'is %s]\n(You can make this quiet by set env ' '[ADAPTER_TARGET_PYTHON_PATH]): ') % default_python_bin_path custom_python_bin_path = env_path @@ -80,9 +81,10 @@ def setup_python(env_path): if (not compile_args[0].startswith(_PYTORCH_VERSION_1_8)) and \ (not compile_args[0].startswith(_PYTORCH_VERSION_1_11)) and \ (not compile_args[0].startswith(_PYTORCH_VERSION_2_0)) and \ - (not compile_args[0].startswith(_PYTORCH_VERSION_2_1)): - print('Currently supported Pytorch version is %s/%s, we got %s.' - % (_PYTORCH_VERSION_1_11, _PYTORCH_VERSION_2_0, _PYTORCH_VERSION_2_1, compile_args[0])) + (not compile_args[0].startswith(_PYTORCH_VERSION_2_1)) and \ + (not compile_args[0].startswith(_PYTORCH_VERSION_2_2)): + print('Currently supported Pytorch version is %s/%s/%s/%s, we got %s.' + % (_PYTORCH_VERSION_1_11, _PYTORCH_VERSION_2_0, _PYTORCH_VERSION_2_1, _PYTORCH_VERSION_2_2, compile_args[0])) continue except subprocess.CalledProcessError: print('Pytorch is not installed or does not work properly.') diff --git "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T1.md" "b/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T1.md" deleted file mode 100644 index cc1e2f8ff1233272fb110334dd9cce763f3765b0..0000000000000000000000000000000000000000 --- "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T1.md" +++ /dev/null @@ -1,2105 +0,0 @@ -# **PyTorch精度工具使用指南** - -本文主要介绍PyTorch精度工具ptdbg_ascend的使用以及精度比对场景示例。 - -ptdbg_ascend工具的原理及安装请参见《[PyTorch精度工具](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/README.md)》。 - -ptdbg_ascend工具主要支持PyTorch API精度数据dump、溢出检测、精度比对以及parse数据解析功能。其中dump和溢出检测功能支持使用debugger和register_hook方式进行精度数据的dump和溢出检测,推荐使用debugger方式。 - -## PyTorch精度比对总体流程 - -1. 准备CPU或GPU训练工程。 - -2. 在环境下安装ptdbg_ascend工具。 - -3. 在训练脚本内插入ptdbg_ascend工具dump接口。 - -4. 执行训练dump数据。 - -5. 将CPU或GPU训练工程迁移为NPU训练工程。 - - 请参见《[PyTorch模型迁移和训练指南](https://www.hiascend.com/document/detail/zh/canncommercial/63RC1/modeldevpt/ptmigr/ptmigr_0001.html)》。 - -6. 在NPU环境下安装ptdbg_ascend工具。 - -7. 在NPU训练脚本内插入ptdbg_ascend工具dump接口。 - -8. NPU环境下执行训练dump数据。 - -9. 创建并配置精度比对脚本,例如compare.py。 - -10. 执行CPU或GPU dump与NPU dump数据的精度比对。 - -11. 比对结果分析。 - -## 快速入门(debugger方式) - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**debugger方式dump和溢出检测**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 单卡场景精度比对 - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump详细信息。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据目录为npu_dump,假设GPU dump数据目录为gpu_dump;dump将生成pkl数据文件api_stack_dump.pkl和npy数据目录api_stack_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config='./dump.json') - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景 - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定前向API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./overflow_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过[Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## 场景化示例 - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**CPU或GPU及NPU精度数据dump**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 多卡场景精度比对 - -精度工具支持多卡场景的精度比对,多卡场景的dump步骤与单卡场景完全一致,请参见“**单卡场景精度比对**”章节,不同的是多卡数据精度比对时需要使用“compare_distributed”函数进行比对。 - -**大模型场景下dump推荐使用debugger方式的手动模式。** - -如下示例: - -说明:多机多卡场景需要每个设备单独执行比对操作。 - -假设NPU dump npy数据目录为npu_dump/ptdbg_dump_v4.0,GPU dump npy数据目录为gpu_dump/ptdbg_dump_v4.0。 - -1. 创建比对脚本,例如compare_distributed.py,拷贝如下代码。 - - ```python - from ptdbg_ascend import * - compare_distributed('./npu_dump/ptdbg_dump_v4.0', './gpu_dump/ptdbg_dump_v4.0', './output') - ``` - -2. 执行比对: - - ```bash - python3 compare_distributed.py - ``` - -两次运行须用相同数量的卡,传入`compare_distributed`的两个文件夹下须有相同个数的rank文件夹,且不包含其他无关文件,否则将无法比对。 - -**多卡set_dump_path注意事项** - -多卡一般为多进程,须保证每个进程都正确调用PrecisionDebugger或set_dump_path,或把PrecisionDebugger或set_dump_path插入到import语句后,如: - -```python -from ptdbg_ascend import * -debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) -``` - -或 - -```python -from ptdbg_ascend import * -seed_all() -set_dump_path('./dump_resnet') -``` - -如此可保证set_dump_path在每个进程都被调用。 - -**多卡register_hook注意事项** - -register_hook需要在set_dump_path之后调用,也需要在每个进程上被调用,建议在搬运模型数据到卡之后调用。识别方法如下: - -- 找到训练代码中遍历epoch的for循环或遍历数据集的for循环,把register_hook放到循环开始前即可。 -- 找到训练代码中调用DDP或者DistributedDataParallel的代码行,把register_hook放到该代码行所在的代码块之后。 -- 若代码中均无以上两种情况,需要保证register_hook在模型定义之后插入,并配置rank参数。rank参数获取rank_id请参见“**[rank_id获取方法](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/rank_id获取方法.md)**”。 - -### NPU vs NPU精度比对 - -对于NPU vs NPU场景,是针对同一模型,进行迭代(模型、API版本升级或设备硬件升级)时存在的精度下降问题,对比相同模型在迭代前后版本的API计算数值,进行问题定位。 - -一般情况下迭代涉及NPU自定义算子,因此,可以仅dump NPU自定义算子进行比对。比对精度问题分析请参见“**单卡场景精度比对**”章节。 - -工具当前支持dump NPU自定义算子如下: - -| 序号 | NPU自定义算子 | -| :--- | ----------------------------------------------- | -| 1 | torch_npu.one_ | -| 2 | torch_npu.npu_sort_v2 | -| 3 | torch_npu.npu_transpose | -| 4 | torch_npu.npu_broadcast | -| 5 | torch_npu.npu_dtype_cast | -| 6 | torch_npu.empty_with_format | -| 7 | torch_npu.npu_one_hot | -| 8 | torch_npu.npu_stride_add | -| 9 | torch_npu.npu_ps_roi_pooling | -| 10 | torch_npu.npu_roi_align | -| 11 | torch_npu.npu_nms_v4 | -| 12 | torch_npu.npu_iou | -| 13 | torch_npu.npu_nms_with_mask | -| 14 | torch_npu.npu_pad | -| 15 | torch_npu.npu_bounding_box_encode | -| 16 | torch_npu.npu_bounding_box_decode | -| 17 | torch_npu.npu_batch_nms | -| 18 | torch_npu.npu_slice | -| 19 | torch_npu._npu_dropout | -| 20 | torch_npu.npu_indexing | -| 21 | torch_npu.npu_ifmr | -| 22 | torch_npu.npu_max | -| 23 | torch_npu.npu_scatter | -| 24 | torch_npu.npu_layer_norm_eval | -| 25 | torch_npu.npu_alloc_float_status | -| 26 | torch_npu.npu_confusion_transpose | -| 27 | torch_npu.npu_bmmV2 | -| 28 | torch_npu.fast_gelu | -| 29 | torch_npu.npu_sub_sample | -| 30 | torch_npu.npu_deformable_conv2d | -| 31 | torch_npu.npu_mish | -| 32 | torch_npu.npu_anchor_response_flags | -| 33 | torch_npu.npu_yolo_boxes_encode | -| 34 | torch_npu.npu_grid_assign_positive | -| 35 | torch_npu.npu_normalize_batch | -| 36 | torch_npu.npu_masked_fill_range | -| 37 | torch_npu.npu_linear | -| 38 | torch_npu.npu_bert_apply_adam | -| 39 | torch_npu.npu_giou | -| 40 | torch_npu.npu_ciou | -| 41 | torch_npu.npu_diou | -| 42 | torch_npu.npu_sign_bits_pack | -| 43 | torch_npu.npu_sign_bits_unpack | -| 44 | torch_npu.npu_flash_attention | -| 45 | torch_npu.npu_scaled_masked_softmax | -| 46 | torch_npu.npu_rotary_mul | -| 47 | torch_npu.npu_roi_align | -| 48 | torch_npu.npu_roi_alignbk | -| 49 | torch_npu.npu_ptiou | -| 50 | torch_npu.npu_fusion_attention | -| 51 | torch_npu.npu_dropout_with_add_softmax | -| 52 | torch_npu.npu_random_choice_with_mask | -| 53 | torch_npu.npu_rotated_iou | -| 54 | torch_npu.npu_conv2d | -| 55 | torch_npu.npu_conv3d | -| 56 | torch_npu.npu_softmax_cross_entropy_with_logits | -| 57 | torch_npu.npu_all_gather_base_mm | -| 58 | torch_npu.npu_swiglu | -| 59 | torch_npu.npu_rms_norm | -| 60 | torch_npu.npu_mm_reduce_scatter_base | -| 61 | torch_npu.npu_mm_all_reduce_base | -| 62 | torch_npu.npu_conv_transpose2d | -| 63 | torch_npu.npu_convolution | -| 64 | torch_npu.npu_convolution_transpose | -| 65 | torch_npu.npu_min | -| 66 | torch_npu.npu_nms_rotated | -| 67 | torch_npu.npu_reshape | -| 68 | torch_npu.npu_rotated_box_decode | -| 69 | torch_npu.npu_rotated_box_encode | -| 70 | torch_npu.npu_rotated_overlaps | -| 71 | torch_npu.npu_silu | -| 72 | torch_npu.npu_fused_attention_score | -| 73 | torch_npu.npu_multi_head_attention | -| 74 | torch_npu.npu_gru | -| 75 | torch_npu.npu_incre_flash_attention | -| 76 | torch_npu.npu_prompt_flash_attention | -| 77 | torch_npu.npu_lstm | -| 78 | torch_npu.npu_apply_adam | - -### 通信API的数据dump - -通信类API数据可以使用全量dump方式获取,若只dump通信类API数据,可以使用如下示例: - -```python -debugger.configure_hook(mode="api_list", api_list=["distributed"]) -``` - -或 - -```python -set_dump_switch("ON", mode="api_list", api_list=["distributed"]) -``` - -通信类API支持列表: - -| 序号 | Distributed | -| :--- | -------------------- | -| 1 | send | -| 2 | recv | -| 3 | broadcast | -| 4 | all_reduce | -| 5 | reduce | -| 6 | all_gather | -| 7 | gather | -| 8 | isend | -| 9 | irecv | -| 10 | scatter | -| 11 | reduce_scatter | -| 12 | _reduce_scatter_base | -| 13 | _all_gather_base | - -### 单卡场景精度比对(register_hook方式) - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - - # 在main函数开始前固定随机数 - seed_all() - - # 配置dump数据目录路径和名称 - set_dump_path("./npu_dump", dump_tag='all') - - # 注册dump回调函数 - register_hook(model, acc_cmp_dump) - - ... - - # 在第一个迭代开始的位置开启dump和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="api_stack", filter_switch="OFF") - - ... - - # 在第一个迭代结束的位置关闭dump - set_dump_switch("OFF") - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据文件为npu_dump.pkl,假设NPU dump npy数据目录为npu_dump,GPU dump数据文件为gpu_dump.pkl,GPU dump npy数据目录为gpu_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='forward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定前向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"], filter_switch="OFF") - - ... - - set_dump_switch("OFF") - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='backward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"], filter_switch="OFF") - set_backward_input(["./npu_dump/all_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - ... - - set_dump_switch("OFF") - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景(register_hook方式) - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check, overflow_nums=3) - - ... - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定API的ACL级别溢出数据 - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - - # 在期望溢出检测的step位置开始前打开溢出检测开关 - set_overflow_check_switch("ON") - - ... - - # 在step结束的位置关闭溢出检测开关 - set_overflow_check_switch("OFF") - - ... - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check) - - ... - ``` - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定反向API的ACL级别溢出数据 - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过 [Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## debugger方式dump和溢出检测(推荐) - -### PrecisionDebugger模块 - -**功能说明** - -PrecisionDebugger模块包含dump和溢出检测功能的总体配置项。可以指定dump目录,设置dump或溢出检测功能,指定dump的卡和迭代。 - -可以在from ptdbg_ascend import *和模型初始化之间的任意位置添加该模块。 - -**原型** - -```python -PrecisionDebugger(dump_path=None, hook_name=None, rank=None, step=[], enable_dataloader=False, model=None): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| dump_path | 设置dump数据目录路径,参数示例:"./dump_path"。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当**configure_hook**函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置dump_path时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时dump数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,dump_path和环境变量需要二选一。 | 否 | -| hook_name | dump模式,可取值dump和overflow_check,表示dump和溢出检测功能,二选一。 | 是 | -| rank | 指定对某张卡上的数据进行dump或溢出检测,默认未配置(表示dump所有卡的数据),须根据实际卡的Rank ID配置。应配置为大于0的正整数,且须根据实际卡的Rank ID配置,若所配置的值大于实际训练所运行的卡的Rank ID,则dump数据为空,比如当前环境Rank ID为0~7,实际训练运行0~3卡,此时若配置Rank ID为4或不存在的10等其他值,此时dump数据为空。 | 否 | -| step | 指定dump某个step的数据,默认未配置,表示dump所有step数据。dump特定step时,须指定为训练脚本中存在的step。step为list格式,可配置逐个step,例如:step=[0,1,2];也可以配置step范围,例如:step=list(range(0,9)),表示dump第0到第8个step。 | 否 | -| enable_dataloader | 自动控制开关,可取值True(开启)或False(关闭),默认为False。配置为True后自动识别dump step参数指定的迭代,并在该迭代执行完成后退出训练,此时start和stop函数可不配置,开启该开关要求训练脚本是通过torch.utils.data.dataloader方式加载数据;配置为False则需要配置start和stop函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()。 | 否 | -| model | 开启init dump模式,传入网络模型实例化的对象,配置该参数后,dump操作仅dump网络中init方法里调用的方法(nn.Module类),不会对所有API进行dump。参数示例: model=net,net为网络模型实例化的对象名称。默认未配置。
配置该参数时,PrecisionDebugger模块请在模型实例化之后调用。
该模式不支持“溢出检测”、”ACL级别数据dump“和“模块级精度数据dump”。此模式下dump文件名前缀为网络中定义的模块名或层名。 | 否 | - -#### init dump模式示例代码和数据落盘说明 - -**示例代码** - -```python -import os -import torch -import torch.nn as nn -import torch_npu -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") - - -class Net(nn.Module): - - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=2) - self.relu1 = nn.ReLU() - self.bn1 = nn.BatchNorm2d(16) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - output = self.relu1(x) - return output - -if __name__ == "__main__": - net = Net().npu() - # model参数传入net, 开启init dump 功能 - debugger = PrecisionDebugger(dump_path="./dump", hook_name="dump", model=net) - debugger.configure_hook(mode="api_stack") - debugger.start() - x = torch.randn(1, 1, 28, 28).npu() - out = net(x) - loss = out.sum() - loss.backward() - debugger.stop() -``` - -**落盘数据说明** - -该模式下dump数据命名格式为:`{Layer_name}_{Module_name}_{call_num}_{forward/backward}_{input/output}.npy` - -``` -# 按照上述用例代码进行dump,落盘数据命名示例如下: -conv1_Conv2d_0_forward_input.0.npy -conv1_Conv2d_0_forward_output.npy -relu1_ReLU_0_forward_input.0.npy -....... -bn1_BatchNorm2d_0_backward_output.2.npy -``` - -### configure_hook函数(可选) - -**功能说明** - -设置dump范围。 - -建议在**PrecisionDebugger**模块与模型初始化之间的任意位置添加,不添加此函数时默认使用mode="api_stack" dump整网数据。 - -**原型** - -dump: - -```python -debugger.configure_hook(mode="api_stack", scope=[], api_list=[], filter_switch="OFF", acl_config=None, backward_input=[], input_output_mode=["all"], summary_only=False) -``` - -溢出检测: - -```python -debugger.configure_hook(mode=None, acl_config=None, overflow_nums=1, need_replicate=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为api_stack。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围,mode="api_list"时,需要配置api_list=[],其他模式有需要时配置scope=[]。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"(表示开启过滤,即不dump)或"OFF"(表示关闭过滤)。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| acl_config | acl dump的配置文件。mode="acl"时,该参数必选;mode为其他值时,该参数不选。参数示例:acl_config='./dump.json'。dump.json配置文件详细介绍请参见“**dump.json配置文件说明**”。 | 否 | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 否 | -| input_output_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例input_output_mode=["backward"]或input_output_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | -| summary_mode | 控制dump文件输出的模式,可取值md5(dump仅输出包含md5值的pkl文件,用于验证数据的完整性)、summary(dump仅输出包含API统计信息的pkl文件)、all(dump输出包含API统计信息的pkl文件以及具体的npy文件),参数示例:summary_mode=md5,默认为all。summary_only=True时,不允许配置该参数。 | 否 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| need_replicate | 过程dump数据生成开关,执行溢出检测时,dump目录下会生成forward_real_data和backward_real_data的过程dump数据目录,可取值True(生成)或False(不生成),默认不生成。 | 否 | - -**函数示例** - -configure_hook可配置多种dump模式,示例如下: - -说明: - -以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -以下仅为该函数配置示例,完整代码请参见“**示例代码**”章节。 - -- 示例1:dump指定API列表 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config="./dump.json") - ``` - -- 示例5:dump指定反向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - -- 示例6:dump指定某一类API的API级别输入输出数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例7:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例8: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例9:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(input_output_mode=["backward"]) - ``` - -- 示例10:仅dump pkl文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(summary_only=True) - ``` - -- 示例11:溢出检测dump - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=1) - ``` - - dump执行时会在**PrecisionDebugger**模块的dump_path参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例11:dump溢出API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### start函数(可选) - -**功能说明** - -dump或溢出检测启动函数。 - -在模型初始化之后的任意位置添加。 - -**原型** - -```python -debugger.start() -``` - -该函数为类函数,可以使用debugger.start()也可以使用PrecisionDebugger.start()。 - -### stop函数(可选) - -**功能说明** - -dump或溢出检测停止函数。 - -在**start**函数之后的任意位置添加。 - -**原型** - -```python -debugger.stop() -``` - -该函数为类函数,可以使用debugger.stop()也可以使用PrecisionDebugger.stop()。 - -### 示例代码(自动模式) - -**需要保证用户训练代码是通过torch.utils.data.dataloader方式加载数据。** - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -### 示例代码(手动模式) - -一般情况下使用自动模式可以快速方便进行dump操作,但个别大模型可能在部分卡的训练操作中没有调用dataloader,这会导致自动模式无法dump指定迭代的数据,此时需要关闭自动模式手动在迭代前后插入start()和stop()函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()以标识dump结束。 - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -## register_hook方式dump和溢出检测 - -### 总体说明 - -- 本节主要介绍CPU或GPU及NPU精度数据dump和溢出检测所需要的函数以及示例。 - -- ptdbg_ascend工具默认情况下仅dump PyTorch模型的API输入输出数据进行精度比对,若在比对结果中发现某个API下可能存在ACL的精度问题,那么可以选择dump该API的ACL级别数据进行精度分析。 - -- 某些torch api的输出不是Tensor类型的数据。对于此类API的反向过程进行ACL dump,工具会在运行日志中给出对应的Warning(is not of tensor type and cannot be automatically derived)提示。如若想要进行该类API反向ACL dump,可以通过手动构建单API用例的方式进行ACL dump,具体用例可参见“**[反向ACL dump用例说明](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/%E5%8F%8D%E5%90%91ACL%20dump%E7%94%A8%E4%BE%8B%E8%AF%B4%E6%98%8E.md)**”。 - -- 工具性能:dump数据量较小时(小于5G),参考dump速度0.1GB/s;dump数据量较大时,参考dump速度0.2GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。Dump速度的计算方式:Dump数据量/(单个step添加Dump耗时-原始单个step耗时)。 - -### 约束 -- 进行CPU或GPU数据dump时,请安装torch包而非torch_npu包,避免工具无法识别使用场景,导致失败。 - -- TASK_QUEUE_ENABLE环境变量会导致API下发和执行异步进行,因此在ACL dump前需要将TASK_QUEUE_ENABLE关闭,即export TASK_QUEUE_ENABLE=0。 - -- 不建议在PyTorch训练脚本中同时添加dump接口和性能数据采集(如Ascend PyThon Profiler)接口,二者可能相互影响导致数据不准确。 - -### seed_all - -**功能说明** - -固定随机数。通过固定随机数保证模型的输入或输出一致。在训练主函数开始前调用,避免随机数固定不全。 - -使用form ptdbg import *后自动导入该函数,代码无需再次添加,若需要修改随机数种子和确定性计算模式,则需要通过添加该函数修改。 - -**函数原型** - -```python -seed_all(seed=1234, mode=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------ | ------------------------------------------------------------ | -------- | -| seed | 随机数种子。参数示例:seed=1000。默认值为:1234。 | 否 | -| mode | 确定性计算模式。可配置True或False。参数示例:mode=True。默认为False。
即使在相同的硬件和输入下,API多次执行的结果也可能不同,开启确定性计算是为了保证在相同的硬件和输入下,API多次执行的结果相同。
确定性计算会导致API执行性能降低,建议在发现模型多次执行结果不同的情况下开启。
rnn类算子、ReduceSum、ReduceMean等算子可能与确定性计算存在冲突,若开启确定性计算后多次执行的结果不相同,则考虑存在这些算子。 | 否 | - -**函数示例** - -seed_all函数的随机数种子,取默认值即可,无须配置;第二个参数默认关闭,不开启确定性计算时也无须配置。 - -- 示例1:仅固定随机数,不开启确定性计算 - - ```python - seed_all() - ``` - -- 示例2:固定随机数,开启确定性计算 - - ```python - seed_all(mode=True) - ``` - -**固定随机数范围** - -seed_all函数可固定随机数的范围如下表。 - -| API | 固定随机数 | -| ---------------------------------------- | --------------------------- | -| os.environ['PYTHONHASHSEED'] = str(seed) | 禁止Python中的hash随机化 | -| random.seed(seed) | 设置random随机生成器的种子 | -| np.random.seed(seed) | 设置numpy中随机生成器的种子 | -| torch.manual_seed(seed) | 设置当前CPU的随机种子 | -| torch.cuda.manual_seed(seed) | 设置当前GPU的随机种子 | -| torch.cuda.manual_seed_all(seed) | 设置所有GPU的随机种子 | -| torch_npu.npu.manual_seed(seed) | 设置当前NPU的随机种子 | -| torch_npu.npu.manual_seed_all(seed) | 设置所有NPU的随机种子 | -| torch.backends.cudnn.enable=False | 关闭cuDNN | -| torch.backends.cudnn.benchmark=False | cuDNN确定性地选择算法 | -| torch.backends.cudnn.deterministic=True | cuDNN仅使用确定性的卷积算法 | - -需要保证CPU或GPU以及NPU的模型输入完全一致,dump数据的比对才有意义,seed_all并不能保证模型输入完全一致,如下表所示场景需要保证输入的一致性。 - -| 场景 | 固定方法 | -| --------------- | ------------- | -| 数据集的shuffle | 关闭shuffle。 | -| dropout | 关闭dropout。 | - -关闭shuffle示例: - -```python -train_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size = batch_size, - shuffle = False, - num_workers = num_workers -) -``` - -关闭dropout: - -在使用from ptdbg import *后,工具会自动将torch.nn.functional.dropout、torch.nn.functional.dropout2d、torch.nn.functional.dropout3d、torch.nn.Dropout、torch.nn.Dropout2d、torch.nn.Dropout3d的接口参数p置为0。 - -### set_dump_path - -**功能说明** - -设置数据保存目录。建议在seed_all函数之后调用且需要保证训练进程能够调用该函数;多卡时须保证每个进程都能调用该函数。 - -**函数原型** - -```python -set_dump_path(fpath=None, dump_tag='ptdbg_dump') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| fpath | 设置数据目录路径。参数示例:'./dump_path'。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当set_dump_switch函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置fpath时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,fpath和环境变量需要二选一。 | 否 | -| dump_tag | 设置数据目录名称。参数示例:dump_tag='dump_conv2d'。默认数据目录命名为ptdbg_dump_{version}。
{version}为当前安装ptdbg_ascend工具版本。目录结构参见“**dump数据存盘说明**”。
配置该参数会将生成的`ptdbg_dump_{version}`目录名称变更为dump_tag配置的值,如`dump_conv2d_{version}`。 | 否 | - -**函数示例** - -- 示例1:设置数据目录路径 - - ```python - set_dump_path('./dump_path') - ``` - -- 示例2:设置数据目录名称 - - ```python - set_dump_path('./dump_path', dump_tag='dump_conv2d') - ``` - - -若以相同的数据目录多次dump,则会因同名导致覆盖;多次dump建议配置不同的dump_tag。 - -### register_hook - -**功能说明** - -注册工具钩子函数。在set_dump_path之后调用。 - -dump操作必选。 - -**函数原型** - -```python -register_hook(model, hook, overflow_nums=overflow_nums, dump_mode=dump_mode, dump_config=dump_config_file) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| model | 传入网络模型实例化的对象。参数示例: model=net,net为网络模型实例化的对象名称。 | 是 | -| hook | 注册工具的dump和溢出检测钩子。可取值overflow_check(表示溢出检测)和acc_cmp_dump(表示dump数据),二选一。 | 是 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| dump_mode | 控制针对溢出API的dump模式,可取值"acl"或"api"。配置acl时,表示dump ACL级别的溢出数据,此时set_dump_path参数不生效,dump数据目录由dump_config的.json文件配置。参数示例:dump_mode="acl"。默认不配置,即dump API级别的溢出数据。 | 否 | -| dump_config | acl dump的配置文件。dump_mode="acl"时,该参数必选;dump_mode="api"时,该参数不选。参数示例:dump_config='./dump.json'。 | 否 | - -**函数示例** - -- 示例1:注册工具钩子函数 - - ```python - register_hook(model, acc_cmp_dump) - ``` - -- 示例2:dump指定API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - ``` - - 需要配置set_dump_switch的mode="acl"以及scope指定为前向或反向API,请参见“**set_dump_switch”**的示例。 - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置dump数据目录。 - -- 示例3:溢出检测dump - - ```python - register_hook(model, overflow_check, overflow_nums=3) - ``` - - dump执行时会在set_dump_path的fpath参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例4:dump指定API的ACL级别溢出数据 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### set_dump_switch - -**功能说明** - -设置dump范围。建议在register_hook函数之后的脚本内任意位置插入,但进行精度问题排查建议参照“场景化示例 > 单卡场景精度比对”章节的顺序,先从第一个迭代开始的位置调用并dump整网数据。 - -dump操作必选。 - -**函数原型** - -```python -def set_dump_switch(switch, mode="all", scope=[], api_list=[], filter_switch="OFF", dump_mode=["all"], summary_only=False): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| --------------- | ------------------------------------------------------------ | -------- | -| switch | dump开关。可取值"ON"或"OFF"。须在选定dump开始的位置配置set_dump_switch("ON");dump结束的位置设置set_dump_switch("OFF")。 | 是 | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为all。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| dump_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例dump_mode=["backward"]或dump_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | - -**推荐配置** - -```python -set_dump_switch("ON", mode="api_stack", filter_switch="OFF") -``` - -开启dump数据和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量。 - -**函数示例** - -set_dump_switch可配置多种dump模式,示例如下: - -说明:以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -- 示例1:dump指定API列表 - - ```python - set_dump_switch("ON", mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - set_dump_switch("ON", mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - set_dump_switch("ON", mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件。 - -- 示例4:dump指定反向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件,并通过set_backward_input设置反向API输入的.npy文件。 - -- 示例5:dump指定某一类API的API级别输入输出数据 - - ```python - set_dump_switch("ON", mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例6:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - set_dump_switch("ON", mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例7: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - set_dump_switch("ON", filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例8:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - set_dump_switch("ON", dump_mode=["backward"]) - ``` - -- 示例9:仅dump pkl文件 - - ```python - set_dump_switch("ON", summary_only=True) - ``` - -以上示例均需要在结束dump的位置插入set_dump_switch("OFF")。 - -set_dump_switch配置mode为all或api_stack时,结束dump后,在dump目录下会自动生成compare_data.py比对脚本模板,示例如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": , - "bench_pkl_path": , - "npu_dump_data_dir": , - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -pkl_path和dump_data_dir字段会自动识别pkl和dump目录的路径,用户需要判断当前dump的环境是NPU、CPU或GPU,并将pkl_path和dump_data_dir字段填入下方dump_path_param函数对应的字段中,例如当前设备为NPU,那么填写方式如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": pkl_path, - "bench_pkl_path": , - "npu_dump_data_dir": dump_data_dir, - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -此时,另一侧数据的路径,需要用户另外识别并填入。 - -### set_overflow_check_switch - -**功能说明** - -置溢出检测范围。默认不配置该函数,全量进行溢出检测。 - -仅支持NPU环境。 - -**函数原型** - -```python -set_overflow_check_switch(switch, filter_switch='OFF') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| switch, | 检测开关。可取值"ON"或"OFF"。如果只在特定的step溢出检测,则在期望溢出检测的step位置开始前插入set_overflow_check_switch("ON"),在step结束的位置插入set_overflow_check_switch("OFF")。 | 是 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | - -**函数示例** - -- 示例1:指定范围溢出检测 - - ```python - register_hook(model, overflow_check) - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,dump执行时会在当前目录自动生成ptdbg_dump_{version}目录,保存溢出数据。 - -- 示例2:前向API的ACL级别范围溢出检测 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置溢出数据目录。 - -### set_backward_input - -**功能说明** - -设置反向ACL级别dump时需要的反向输入的.npy文件。 - -**函数原型** - -```python -set_backward_input(backward_input) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------------- | ------------------------------------------------------------ | -------- | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 是 | - -**函数示例** - -```python -register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') -set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) -set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) -``` - -## dump.json配置文件说明 - -**dump.json配置示例** - -```python -{ - "dump": - { - "dump_list":[], - "dump_path":"./dump/output", - "dump_mode":"all", - "dump_op_switch":"on" - } -} -``` - -**dump.json参数说明** - -| 字段名 | 说明 | -| -------------- | ------------------------------------------------------------ | -| dump_list | 待dump数据的API模型。为空,无需配置。 | -| dump_path | dump数据文件存储到运行环境的目录,主要用于指定ACL dump数据路径。支持配置绝对路径或相对路径。dump_path须为已存在目录。 | -| dump_mode | dump数据模式,配置如下:
- output:dump API的输出数据。默认值。
- input:dump API的输入数据。
- all:dump API的输入、输出数据。 | -| dump_op_switch | 单API模型dump数据开关,配置如下: * off:关闭单API模型dump,默认值。 * on:开启单API模型dump。 | - -**dump目录说明** - -配置register_hook的dump_config后,采集的dump数据会在{dump_path}/{time}/{deviceid}/{model_id}目录下生成,例如“/home/HwHiAiUser/output/20200808163566/0/0” - -```bash -├── 20230131172437 -│   └── 1 -│   ├── 0 -│   │   ├── Add.Add.45.0.1675157077183551 -│   │   ├── Cast.trans_Cast_0.31.0.1675157077159449 -│   │   ├── Cast.trans_Cast_5.43.0.1675157077180129 -│   │   ├── MatMul.MatMul.39.0.1675157077172961 -│   │   ├── Mul.Mul.29.0.1675157077155731 -│   │   ├── NPUAllocFloatStatus.NPUAllocFloatStatus.24.0.1675157077145262 -│   │   ├── TransData.trans_TransData_1.33.0.1675157077162791 -│   │   └── TransData.trans_TransData_4.41.0.1675157077176648 -│   ├── 1701737061 -│   │   └── Cast.trans_Cast_2.35.0.1675157077166214 -│   ├── 25 -│   │   └── NPUClearFloatStatus.NPUClearFloatStatus.26.0.1675157077150342 -│   └── 68 -│   └── TransData.trans_TransData_3.37.0.1675157077169473 -``` - -## 模块级精度数据dump - -### 总体说明 - -大模型场景下,通常不是简单的利用自动迁移能力实现GPU到NPU的训练脚本迁移,而是会对NPU网络进行一系列针对性的适配,因此,常常会造成迁移后的NPU模型存在部分子结构不能与GPU原始模型完全对应。模型结构不一致导致API调用类型及数量不一致,若直接按照API粒度进行精度数据dump和比对,则无法完全比对所有的API。 - -本节介绍的功能是对模型中的大粒度模块进行数据dump,使其比对时,对于无法以API粒度比对的模块可以直接以模块粒度进行比对。 - -模块指的是继承自nn.Module类模块,通常情况下这类模块就是一个小模型,可以被视为一个整体,dump数据时以模块为粒度进行dump。 - -### module_dump - -**功能说明** - -开启模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump(module, module_name) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------- | ------------------------------------------------------------ | -------- | -| module | 网络中实例化好的nn.Module类模块的model对象。 | 是 | -| module_name | 用户自定义的该model名称。主要用于dump数据文件的命名,便于在比对时识别模块级数据。 | 是 | - -### module_dump_end - -**功能说明** - -结束模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump_end() -``` - -### 示例代码 - -```python -# 根据需要import包 -import os -import torch -import torch.nn as nn -import torch_npu -import torch.nn.functional as F -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") -# 定义一个简单的网络 -class ModuleOP(nn.Module): - def __init__(self) -> None: - super().__init__() - self.linear_1 = nn.Linear(in_features=8, out_features=4) - self.linear_2 = nn.Linear(in_features=4, out_features=2) - def forward(self, x): - x1 = self.linear_1(x) - x2 = self.linear_2(x1) - r1 = F.relu(x2) - return r1 - -if __name__ == "__main__": - module = ModuleOP() - - # 注册工具 - pdbg = PrecisionDebugger("./dump_data/npu", hook_name="dump") - pdbg.start() - - x = torch.randn(10, 8) - module_dump(module, "MyModuleOP") # 开启模块级精度数据dump - out = module(x) - module_dump_end() # 结束模块级精度数据dump - loss = out.sum() - loss.backward() - pdbg.stop() -``` - -## dump数据存盘说明 - -dump结果目录结构示例如下: - -```bash -├── dump_path -│ └── ptdbg_dump_{version} -│ ├── step0 -│ | ├── rank0 -│ | │ ├── dump -| | | | ├── Tensor_permute_1_forward.npy -| | | | ├── MyModule_0_forward_input.npy # 开启模块级精度数据dump时存在模块级的dump数据文件 -| | | | ... -| | | | └── Fcuntion_linear_5_backward_output.npy -│ | │ └── dump.pkl -│ | ├── rank1 -| | | ├── dump -| | | | └── ... -| | | └── dump.pkl -│ | ├── ... -│ | | -| | └── rank7 -│ ├── step1 -│ | ├── ... -│ ├── step2 -``` - -dump过程中,npy文件在对应算子或者模块被执行后就会落盘,而pkl文件则需要在正常执行PrecisionDebugger.stop()或set_dump_switch("OFF")后才会被落盘保存,异常的程序终止会保存终止前被执行算子的相关npy文件,但是不会生成pkl文件。 - -其中`ptdbg_dump_{version}`为默认命名,debugger方式dump不支持修改该文件夹名称,使用set_dump_path函数则支持通过dump_tag参数修改文件夹名称;rank为设备上各卡的ID,每张卡上dump的数据会生成对应dump目录。 - -**精度比对dump场景** - -精度比对dump场景的结果如下: - -* dump.pkl文件:包含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表示2范数(平方根)。 - -* dump目录:目录下为npy格式的dump数据。 - - npy文件保存的前缀和PyTorch对应关系如下 - - | 前缀 | Torch模块 | - | ----------- | ------------------- | - | Tensor | torch.Tensor | - | Torch | torch | - | Functional | torch.nn.functional | - | NPU | NPU亲和算子 | - | VF | torch._VF | - | Aten | torch.ops.aten | - | Distributed | torch.distributed | - -当configure_hook或set_dump_switch配置mode参数(例如:mode="api_stack" )时,dump结果的文件名会添加api_stack前缀,dump结果如下: - -* api_stack_dump.pkl -* api_stack_dump目录 - -**溢出检测dump场景** - -PrecisionDebugger模块的hook_name参数或register_hook函数设置了overflow_check时,检测API溢出,dump结果的文件名格式为:`{api_type}_{api_name}_{API调用次数}_{前向反向}_{当前溢出次数}`,dump结果示例如下: - -* `Tensor_add_1_forward_1.pkl` -* `Tensor_add_1_forward_1`目录 - -## CPU或GPU与NPU精度数据比对 - -### 总体说明 - -- 本节主要介绍CPU或GPU与NPU精度数据比对的函数以及示例。 - -- 比对函数均通过单独创建精度比对脚本执行,可支持单卡和多卡场景的精度数据比对。 - -- 工具性能:比对数据量较小时(参考值单份文件小于10GB),参考比对速度0.1GB/s;比对数据量较大时,参考比对速度0.3GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。比对速度的计算方式:两份比对文件大小/比对耗时。 - -### 约束 - -- NPU自研API,在CPU或GPU若没有对应的API,该API的dump数据不比对。 - -- NPU与CPU或GPU的计算结果误差可能会随着模型的执行不断累积,最终会出现同一个API因为输入的数据差异较大而无法比对的情况。 - -- CPU或GPU与NPU中两个相同的API会因为调用次数不同导致无法比对或比对到错误的API,不影响整体运行,该API忽略。 - -### 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目录。参数示例:'./npu_dump/ptdbg_dump_v4.0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| bench_dump_dir | 配置CPU、GPU或NPU环境下的dump目录。参数示例:'./gpu_dump/ptdbg_dump_v4.0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。需要预先创建output_path目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_rank{npu_ID}-rank{cpu/gpu/npu_ID}_{timestamp}.csv`。 | 是 | -| **kwargs | 支持compare的所有可选参数。 | 否 | - -**函数示例** - -创建比对脚本,例如compare_distributed.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -compare_distributed('./npu_dump/ptdbg_dump_v4.0', './gpu_dump/ptdbg_dump_v4.0', './output') -``` - -### compare - -**功能说明** - -将CPU或GPU与NPU的dump文件进行比对,仅支持单机单卡。 - -**函数原型** - -```python -compare(input_param, output_path, stack_mode=False, auto_analyze=True, fuzzy_match=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------ | ------------------------------------------------------------ | -------- | -| input_param | 配置dump数据文件及目录。配置参数包括:
- "npu_pkl_path":指定NPU dump目录下的.pkl文件。参数示例:"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "bench_pkl_path":指定CPU、GPU或NPU dump目录下的.pkl文件。参数示例:"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "npu_dump_data_dir":"指定NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "bench_dump_data_dir":"指定CPU、GPU或NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "is_print_compare_log":配置是否开启日志打屏。可取值True或False。可选。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_{timestamp}.csv`。 | 是 | -| stack_mode | 配置stack_mode的开关。仅当dump数据时配置debugger.configure_hook或set_dump_switch的mode="api_stack"时需要开启。参数示例:stack_mode=True,默认为False。 | 否 | -| auto_analyze | 自动精度分析,开启后工具自动针对比对结果进行分析,识别到第一个精度不达标节点(在比对结果文件中的“Accuracy Reached or Not”列显示为No),并给出问题可能产生的原因(打屏展示并生成advisor_{timestamp}.txt文件)。可取值True或False,参数示例:auto_analyze=False,默认为True。 | 否 | -| fuzzy_match | 模糊匹配。开启后,对于网络中同一层级且命名仅调用次数不同的API,可匹配并进行比对。可取值True或False,参数示例:fuzzy_match=True,默认为False。 | 否 | - -**函数示例** - -单机单卡场景下创建比对脚本,例如compare.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### pkl文件比对 - -若使用**compare**或**compare_distributed**函数创建的比对脚本中,input_param参数只配置了npu_pkl_path和bench_pkl_path或使用summary_only方式dump时,可以进行pkl文件的比对,此时比对dump.pkl文件中的统计值,开启后的比对结果文件生成Max diff、Min diff和Mean diff,表示NPU dump数据中API的输入或输出与标杆数据输入或输出的最大最小平均值的差。可以通过该值判断API是否存在精度问题:当某个API的输入和输出的Max diff、Min diff、Mean diff和L2norm diff均为0或无限趋于0,那么可以判断该API无精度问题,反之则可能存在精度问题。 - -**比对脚本示例** - -```python -from ptdbg_ascend import compare -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### parse - -**功能说明** - -解析并提取dump信息中的堆栈信息及数据统计信息。 - -**函数原型** - -```python -parse(pkl_file, moudule_name_prefix) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------------- | ------------------------------------------------------------ | -------- | -| pkl_file | 指定dump数据文件中的pkl文件名。参数示例:"./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl"。 | 是 | -| moudule_name_prefix | 指定待提取的API接口前缀。参数示例:"Torch_norm_1_forward"。 | 是 | - -**函数示例** - -创建堆栈信息及数据统计信息提取脚本,例如parse.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl", "Torch_batch_normal_1_forward") -``` - -### 计算精度评价指标 - -PyTorch精度比对是以CPU或GPU的计算结果为标杆,计算Cosine(余弦相似度)、MaxAbsErr(最大绝对误差)和MaxRelativeErr(最大相对误差),根据这两个结果判断API在运行时是否存在精度问题。 - -计算精度评价指标: - -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中的元素逐个与对应的标杆数据对比,相对误差大于千分之一、千分之五的比例占总元素个数的比例小于千分之一、千分之五。该数据仅作为精度下降趋势的参考,并不参与计算精度是否通过的判定。 - -精度比对结果csv文件中只需要通过Accuracy Reached or Not来判断计算精度是否达标,判断标准如下: - -1. Cosine < 0.99 且 MaxAbsError > 0.001时,精度不达标,标记为“No”。 -2. Cosine < 0.9,精度不达标,标记为“No”。 -3. MaxAbsError > 1,精度不达标,标记为“No”。 -5. 其余情况下记为精度达标,标记为“Yes”。 - -## ptdbg_ascend.parse数据解析功能 - -ptdbg_ascend.parse为命令行交互式界面解析工具,提供更多的数据解析功能并且展示结果。 - -使用场景:本工具主要用于比对前后两次NPU ACL层级dump数据的一致性。 - -### 进入parse交互式界面 - -安装ptdbg_ascend工具后,可以通过使用命令 **python -m ptdbg_ascend.parse** 进入交互式界面,如下所示: - -```bash -python -m ptdbg_ascend.parse -Parse >>> -``` - -可在parse的界面中执行Shell命令,以及如下场景的相关解析命令: - -- 支持指定ACL层级算子数据比对。 -- 支持指定ACL层级算子数据转换及展示。 -- 支持交互式指定pkl文件中API对应dump数据查看。 -- 支持API进行可选层级比对和打印(统计级和像素级)。 - -Ctrl+C可以退出parse交互式界面。不退出parse交互式界面若需要执行非该界面下的内置Shell命令,且命令与parse交互式界面命令冲突时,非该界面命令需要使用run命令,在相关命令前加上run前缀,如下示例: - -```bash -python -m ptdbg_ascend.parse -Parse >>> run vim cli.py -Parse >>> vim cli.py -``` - -以上各场景详细介绍请参见下文章节。 - -### ACL层级算子数据比对 - -- 依赖:CANN包中的msaccucmp工具。 - -- 输入以下比对命令进行数据比对。 - - ```bash - vc -m my_dump_path -g golden_dump_path [-out output_path] - ``` - - | 参数名称 | 说明 | 是否必选 | - | --------- | ------------------------------------------------------------ | -------- | - | -m | 待比对dump数据目录。 | 是 | - | -g | dump数据目录。 | 是 | - | -out | 结果输出目录。 | 否 | - | -cmp_path | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py。 | 否 | - - - 输出结果:result_{timestamp}.csv文件。 - - 若指定-out参数需要用户传入输出路径,并且路径需要已存在。 - - 若未指定输出目录, 则比对结束后将结果保存在默认目录 “./parse_data/comapre_result”中,比对结束后会打印log提示输出结果存放路径。 - -**示例** - -```bash -# 传入待比对数据目录以及标杆数据目录 -Parse >>> vc -m ./my_dump_path -g ./golden_data_path -...... -# 比对结果打印 -[INFO] The comparison result have been written to "./parse_data/compare_result/result_20230818104735.csv". -[INFO] The command was completed and took 6 seconds. -[INFO] Compare finished!! -``` - -### ACL算子数据的npy转换 - -- 依赖:CANN包中的msaccucmp工具。 - -- 输入以下转换命令进行数据转换, 将ACL级别dump数据转为npy文件。 - - ```bash - dc -n file_name/file_path [-f format] [-out output_path] - ``` - - | 参数名称 | 说明 | 是否必选 | - | --------- | ------------------------------------------------------------ | -------- | - | -n | 需转换的dump数据文件或dump数据文件目录。 | 是 | - | -f | 开启format转换,指定该参数时需要配置format格式。当前内置的Format转换支持如下类型:
FRACTAL_NZ转换NCHW
FRACTAL_NZ转换成NHWC
FRACTAL_NZ转换ND
HWCN转换FRACTAL_Z
HWCN转换成NCHW
HWCN转换成NHWC
NC1HWC0转换成HWCN
NC1HWC0转换成NCHW
NC1HWC0转换成NHWC
NCHW转换成FRACTAL_Z
NCHW转换成NHWC
NHWC转换成FRACTAL_Z
NHWC转换成HWCN
NHWC转换成NCHW
NDC1HWC0转换成NCDHW | 否 | - | -out | 结果输出目录。 | 否 | - | -cmp_path | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py | 否 | - - [^]: 若传入单个dump文件,则转换单个文件,若传入dump文件目录则转换目录下所有dump文件。 - - - 输出结果:npy文件。 - - 若指定-out参数需要用户传入输出路径,并且路径需要已存在。 - - 若未指定输出目录, 则比对结束后将结果保存在默认目录 “./parse_data/convert_result”中,比对结束后会打印log提示输出结果存放路径及转换结果。 - -- 输入以下命令,展示npy数据统计信息。 - - ```bash - pt -n file_path - ``` - - | 参数名称 | 说明 | 是否必选 | - | -------- | ------------- | -------- | - | -n | npy文件路径。 | 是 | - - 打印统计信息:shape, dtype, max, min和mean。默认在npy文件路径下将该数据保存为txt文件。 - -**示例1** - -```bash -# 传入需转换的dump文件目录 -Parse >>> dc -n ./dump_data/ -...... -# 转换结果 -╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ SrcFile: ./dump_data/ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.0.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.1.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.output.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.output.0.npy │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -**示例2** - -```bash -# 查看某个dump数据块的数据信息 -# 默认会将数据中的tensor保存成 txt -Parse >>> pt -n ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy -...... -# 打印统计信息 -[Shape: (1, 16, 56, 56, 16)] [Dtype: float16] [Max: 452.0] [Min: -408.5] [Mean: -3.809] -Path: ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy -TextFile:./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy.txt -``` - -### pkl文件中指定API的dump数据信息查看 - -输入以下命令,解析并输出pkl文件中指定api的统计信息。 - -```bash -pk -f pkl_path -n api_name -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------- | -------- | -| -f | 指定pkl文件路径。 | 是 | -| -n | 指定API名称。 | 是 | - -- 输出结果:打印统计信息(shape, dtype, max和min mean)。 -- 若pkl文件中存在相应的堆栈信息,则会打印堆栈信息。 - -**示例** - -```bash -# 传入pkl文件及api名称 -Parse >>> pk -f ./torch_dump/ptdbg_v3.2/rank0/api_stack_dump.pkl -n Functional_conv2d_0_forward -...... -# 打印统计信息及堆栈(pkl文件不包含堆栈则不会打印堆栈) - -Statistic Info: - [Functional_conv2d_0_forward_input.0][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 1.576936960220337][min: -0.9757485389709473][mean: 0.4961632490158081] - [Functional_conv2d_0_forward_input.1][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 0.20064473152160645][min: -0.47102075815200806][mean: -0.20796933770179749] - [Functional_conv2d_0_forward_input.2][dtype: torch.float32][shape: [2]][max: 0.17380613088607788][min: -0.16853803396224976][mean: 0.0026340484619140625] - [Functional_conv2d_0_forward_output][dtype: torch.float32][shape: [2, 2, 1, 1]][max: 0.02364911139011383][min: -1.762906551361084][mean: -0.6710853576660156] -``` - -### API可选层级比对 - -输入以下命令, 进行统计级和像素级比对。 - -```bash -cn -m my_data*.npy -g gloden*.npy [-p num] [-al atol] [-rl rtol] -``` - -- 统计级比对:对tensor整体进行余弦值及相对误差的计算。 -- 像素级比对:对输入的两个npy文件进行逐元素比对。若两个tensor对应元素的相对误差或绝对误差大于**误差阈值**(-al和-rl配置)则被标记为错误数据。 - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------------------------------------- | -------- | -| -m | 待比对数据。 | 是 | -| -g | 标杆数据。 | 是 | -| -p | 设置比对结束后打印错误元素的个数,默认值20。 | 否 | -| -al | 判定数据存在精度问题的绝对误差阈值,默认0.001。 | 否 | -| -rl | 判定数据存在精度问题的相对误差阈值,默认0.001。 | 否 | -| -s | 将npy文件保存成txt文件,用于查看,默认开启。 | 否 | - -输出结果: - -- 统计级比对结果。 -- 两个文件的统计信息(shape, dtype, max, min和mean)。 -- 错误数据打印表格。 - -**示例** - -```bash -# 对比两个tensor的数据 -Parse >>> cn -m Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy -g InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy -p 10 -s -al 0.002 -rl 0.005 - Error Item Table Top Item Table -┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ ┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ Index ┃ Left ┃ Right ┃ Diff ┃ ┃ Index ┃ Left ┃ Right ┃ Diff ┃ -┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ ┡━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ -│ 155 │ 0.024600908 │ 0.022271132 │ 0.002329776 │ │ 0 │ -0.9206961 │ -0.9222216 │ 0.0015255213 │ -│ 247 │ 0.015752593 │ 0.017937578 │ 0.0021849852 │ │ 1 │ -0.6416973 │ -0.64051837 │ 0.0011789203 │ -│ 282 │ -0.0101207765 │ -0.007852031 │ 0.0022687456 │ │ 2 │ -0.35383835 │ -0.35433492 │ 0.0004965663 │ -│ 292 │ 0.019581757 │ 0.02240482 │ 0.0028230622 │ │ 3 │ -0.18851271 │ -0.18883198 │ 0.00031927228 │ -│ 640 │ -0.06593232 │ -0.06874806 │ 0.0028157383 │ │ 4 │ -0.43508735 │ -0.43534422 │ 0.00025686622 │ -│ 1420 │ 0.09293677 │ 0.09586689 │ 0.0029301196 │ │ 5 │ 1.4447614 │ 1.4466647 │ 0.0019032955 │ -│ 1462 │ -0.085207745 │ -0.088047795 │ 0.0028400496 │ │ 6 │ -0.3455438 │ -0.3444429 │ 0.0011008978 │ -│ 1891 │ -0.03433288 │ -0.036525503 │ 0.002192624 │ │ 7 │ -0.6560242 │ -0.6564579 │ 0.0004336834 │ -│ 2033 │ 0.06828873 │ 0.07139922 │ 0.0031104907 │ │ 8 │ -2.6964858 │ -2.6975214 │ 0.0010356903 │ -│ 2246 │ -0.06376442 │ -0.06121233 │ 0.002552092 │ │ 9 │ -0.73746175 │ -0.73650354 │ 0.00095820427 │ -└───────┴───────────────┴──────────────┴──────────────┘ └───────┴─────────────┴─────────────┴───────────────┘ -╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Left: | -│ |- NpyFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy | -│ |- TxtFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.846897] [Min: -8.368301] [Mean: -0.72565556] | -│ DstFile: │ -│ |- NpyFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy | -│ |- TxtFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.8425903] [Min: -8.374472] [Mean: -0.7256237] │ -│ NumCnt: 655360 │ -│ AllClose: False │ -│ CosSim: 0.99999493 │ -│ ErrorPer: 0.023504638671875 (rl= 0.005, al= 0.002) │ -╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -## FAQ - -[FAQ](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/FAQ.md) diff --git "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T2.md" "b/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T2.md" deleted file mode 100644 index 7cfed0192c27fd96839e40d17173c8bc242642f2..0000000000000000000000000000000000000000 --- "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T2.md" +++ /dev/null @@ -1,2163 +0,0 @@ -# **PyTorch精度工具使用指南** - -本文主要介绍PyTorch精度工具ptdbg_ascend的使用以及精度比对场景示例。 - -ptdbg_ascend工具的原理及安装请参见《[PyTorch精度工具](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/README.md)》。 - -ptdbg_ascend工具主要支持PyTorch API精度数据dump、溢出检测、精度比对以及parse数据解析功能。其中dump和溢出检测功能支持使用debugger和register_hook方式进行精度数据的dump和溢出检测,推荐使用debugger方式。 - -## PyTorch精度比对总体流程 - -1. 准备CPU或GPU训练工程。 - -2. 在环境下安装ptdbg_ascend工具。 - -3. 在训练脚本内插入ptdbg_ascend工具dump接口。 - -4. 执行训练dump数据。 - -5. 将CPU或GPU训练工程迁移为NPU训练工程。 - - 请参见《[PyTorch模型迁移和训练指南](https://www.hiascend.com/document/detail/zh/canncommercial/63RC1/modeldevpt/ptmigr/ptmigr_0001.html)》。 - -6. 在NPU环境下安装ptdbg_ascend工具。 - -7. 在NPU训练脚本内插入ptdbg_ascend工具dump接口。 - -8. NPU环境下执行训练dump数据。 - -9. 创建并配置精度比对脚本,例如compare.py。 - -10. 执行CPU或GPU dump与NPU dump数据的精度比对。 - -11. 比对结果分析。 - -## 快速入门(debugger方式) - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**debugger方式dump和溢出检测**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 单卡场景精度比对 - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump详细信息。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据目录为npu_dump,假设GPU dump数据目录为gpu_dump;dump将生成pkl数据文件api_stack_dump.pkl和npy数据目录api_stack_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config='./dump.json') - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景 - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定前向API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./overflow_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过[Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## 场景化示例 - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**CPU或GPU及NPU精度数据dump**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 多卡场景精度比对 - -精度工具支持多卡场景的精度比对,多卡场景的dump步骤与单卡场景完全一致,请参见“**单卡场景精度比对**”章节,不同的是多卡数据精度比对时需要使用“compare_distributed”函数进行比对。 - -**大模型场景下dump推荐使用debugger方式的手动模式。** - -如下示例: - -说明:多机多卡场景需要每个设备单独执行比对操作。 - -假设NPU dump npy数据目录为npu_dump/ptdbg_dump_v4.0,GPU dump npy数据目录为gpu_dump/ptdbg_dump_v4.0。 - -1. 创建比对脚本,例如compare_distributed.py,拷贝如下代码。 - - ```python - from ptdbg_ascend import * - compare_distributed('./npu_dump/ptdbg_dump_v4.0', './gpu_dump/ptdbg_dump_v4.0', './output') - ``` - -2. 执行比对: - - ```bash - python3 compare_distributed.py - ``` - -两次运行须用相同数量的卡,传入`compare_distributed`的两个文件夹下须有相同个数的rank文件夹,且不包含其他无关文件,否则将无法比对。 - -**多卡set_dump_path注意事项** - -多卡一般为多进程,须保证每个进程都正确调用PrecisionDebugger或set_dump_path,或把PrecisionDebugger或set_dump_path插入到import语句后,如: - -```python -from ptdbg_ascend import * -debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) -``` - -或 - -```python -from ptdbg_ascend import * -seed_all() -set_dump_path('./dump_resnet') -``` - -如此可保证set_dump_path在每个进程都被调用。 - -**多卡register_hook注意事项** - -register_hook需要在set_dump_path之后调用,也需要在每个进程上被调用,建议在搬运模型数据到卡之后调用。识别方法如下: - -- 找到训练代码中遍历epoch的for循环或遍历数据集的for循环,把register_hook放到循环开始前即可。 -- 找到训练代码中调用DDP或者DistributedDataParallel的代码行,把register_hook放到该代码行所在的代码块之后。 -- 若代码中均无以上两种情况,需要保证register_hook在模型定义之后插入,并配置rank参数。rank参数获取rank_id请参见“**[rank_id获取方法](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/rank_id获取方法.md)**”。 - -### NPU vs NPU精度比对 - -对于NPU vs NPU场景,是针对同一模型,进行迭代(模型、API版本升级或设备硬件升级)时存在的精度下降问题,对比相同模型在迭代前后版本的API计算数值,进行问题定位。 - -一般情况下迭代涉及NPU自定义算子,因此,可以仅dump NPU自定义算子进行比对。比对精度问题分析请参见“**单卡场景精度比对**”章节。 - -工具当前支持dump NPU自定义算子如下: - -| 序号 | NPU自定义算子 | -| :--- | ----------------------------------------------- | -| 1 | torch_npu.one_ | -| 2 | torch_npu.npu_sort_v2 | -| 3 | torch_npu.npu_transpose | -| 4 | torch_npu.npu_broadcast | -| 5 | torch_npu.npu_dtype_cast | -| 6 | torch_npu.empty_with_format | -| 7 | torch_npu.npu_one_hot | -| 8 | torch_npu.npu_stride_add | -| 9 | torch_npu.npu_ps_roi_pooling | -| 10 | torch_npu.npu_roi_align | -| 11 | torch_npu.npu_nms_v4 | -| 12 | torch_npu.npu_iou | -| 13 | torch_npu.npu_nms_with_mask | -| 14 | torch_npu.npu_pad | -| 15 | torch_npu.npu_bounding_box_encode | -| 16 | torch_npu.npu_bounding_box_decode | -| 17 | torch_npu.npu_batch_nms | -| 18 | torch_npu.npu_slice | -| 19 | torch_npu._npu_dropout | -| 20 | torch_npu.npu_indexing | -| 21 | torch_npu.npu_ifmr | -| 22 | torch_npu.npu_max | -| 23 | torch_npu.npu_scatter | -| 24 | torch_npu.npu_layer_norm_eval | -| 25 | torch_npu.npu_alloc_float_status | -| 26 | torch_npu.npu_confusion_transpose | -| 27 | torch_npu.npu_bmmV2 | -| 28 | torch_npu.fast_gelu | -| 29 | torch_npu.npu_sub_sample | -| 30 | torch_npu.npu_deformable_conv2d | -| 31 | torch_npu.npu_mish | -| 32 | torch_npu.npu_anchor_response_flags | -| 33 | torch_npu.npu_yolo_boxes_encode | -| 34 | torch_npu.npu_grid_assign_positive | -| 35 | torch_npu.npu_normalize_batch | -| 36 | torch_npu.npu_masked_fill_range | -| 37 | torch_npu.npu_linear | -| 38 | torch_npu.npu_bert_apply_adam | -| 39 | torch_npu.npu_giou | -| 40 | torch_npu.npu_ciou | -| 41 | torch_npu.npu_diou | -| 42 | torch_npu.npu_sign_bits_pack | -| 43 | torch_npu.npu_sign_bits_unpack | -| 44 | torch_npu.npu_flash_attention | -| 45 | torch_npu.npu_scaled_masked_softmax | -| 46 | torch_npu.npu_rotary_mul | -| 47 | torch_npu.npu_roi_align | -| 48 | torch_npu.npu_roi_alignbk | -| 49 | torch_npu.npu_ptiou | -| 50 | torch_npu.npu_fusion_attention | -| 51 | torch_npu.npu_dropout_with_add_softmax | -| 52 | torch_npu.npu_random_choice_with_mask | -| 53 | torch_npu.npu_rotated_iou | -| 54 | torch_npu.npu_conv2d | -| 55 | torch_npu.npu_conv3d | -| 56 | torch_npu.npu_softmax_cross_entropy_with_logits | -| 57 | torch_npu.npu_all_gather_base_mm | -| 58 | torch_npu.npu_swiglu | -| 59 | torch_npu.npu_rms_norm | -| 60 | torch_npu.npu_mm_reduce_scatter_base | -| 61 | torch_npu.npu_mm_all_reduce_base | -| 62 | torch_npu.npu_conv_transpose2d | -| 63 | torch_npu.npu_convolution | -| 64 | torch_npu.npu_convolution_transpose | -| 65 | torch_npu.npu_min | -| 66 | torch_npu.npu_nms_rotated | -| 67 | torch_npu.npu_reshape | -| 68 | torch_npu.npu_rotated_box_decode | -| 69 | torch_npu.npu_rotated_box_encode | -| 70 | torch_npu.npu_rotated_overlaps | -| 71 | torch_npu.npu_silu | -| 72 | torch_npu.npu_fused_attention_score | -| 73 | torch_npu.npu_multi_head_attention | -| 74 | torch_npu.npu_gru | -| 75 | torch_npu.npu_incre_flash_attention | -| 76 | torch_npu.npu_prompt_flash_attention | -| 77 | torch_npu.npu_lstm | -| 78 | torch_npu.npu_apply_adam | - -### 通信API的数据dump - -通信类API数据可以使用全量dump方式获取,若只dump通信类API数据,可以使用如下示例: - -```python -debugger.configure_hook(mode="api_list", api_list=["distributed"]) -``` - -或 - -```python -set_dump_switch("ON", mode="api_list", api_list=["distributed"]) -``` - -通信类API支持列表: - -| 序号 | Distributed | -| :--- | -------------------- | -| 1 | send | -| 2 | recv | -| 3 | broadcast | -| 4 | all_reduce | -| 5 | reduce | -| 6 | all_gather | -| 7 | gather | -| 8 | isend | -| 9 | irecv | -| 10 | scatter | -| 11 | reduce_scatter | -| 12 | _reduce_scatter_base | -| 13 | _all_gather_base | - -### 单卡场景精度比对(register_hook方式) - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - - # 在main函数开始前固定随机数 - seed_all() - - # 配置dump数据目录路径和名称 - set_dump_path("./npu_dump", dump_tag='all') - - # 注册dump回调函数 - register_hook(model, acc_cmp_dump) - - ... - - # 在第一个迭代开始的位置开启dump和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="api_stack", filter_switch="OFF") - - ... - - # 在第一个迭代结束的位置关闭dump - set_dump_switch("OFF") - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据文件为npu_dump.pkl,假设NPU dump npy数据目录为npu_dump,GPU dump数据文件为gpu_dump.pkl,GPU dump npy数据目录为gpu_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='forward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定前向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"], filter_switch="OFF") - - ... - - set_dump_switch("OFF") - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='backward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"], filter_switch="OFF") - set_backward_input(["./npu_dump/all_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - ... - - set_dump_switch("OFF") - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景(register_hook方式) - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check, overflow_nums=3) - - ... - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定API的ACL级别溢出数据 - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - - # 在期望溢出检测的step位置开始前打开溢出检测开关 - set_overflow_check_switch("ON") - - ... - - # 在step结束的位置关闭溢出检测开关 - set_overflow_check_switch("OFF") - - ... - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check) - - ... - ``` - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定反向API的ACL级别溢出数据 - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过 [Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## debugger方式dump和溢出检测(推荐) - -### PrecisionDebugger模块 - -**功能说明** - -PrecisionDebugger模块包含dump和溢出检测功能的总体配置项。可以指定dump目录,设置dump或溢出检测功能,指定dump的卡和迭代。 - -可以在from ptdbg_ascend import *和模型初始化之间的任意位置添加该模块。 - -**原型** - -```python -PrecisionDebugger(dump_path=None, hook_name=None, rank=None, step=[], enable_dataloader=False, model=None): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| dump_path | 设置dump数据目录路径,参数示例:"./dump_path"。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当**configure_hook**函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置dump_path时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时dump数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,dump_path和环境变量需要二选一。 | 否 | -| hook_name | dump模式,可取值dump和overflow_check,表示dump和溢出检测功能,二选一。 | 是 | -| rank | 指定对某张卡上的数据进行dump或溢出检测,默认未配置(表示dump所有卡的数据),须根据实际卡的Rank ID配置。应配置为大于0的正整数,且须根据实际卡的Rank ID配置,若所配置的值大于实际训练所运行的卡的Rank ID,则dump数据为空,比如当前环境Rank ID为0~7,实际训练运行0~3卡,此时若配置Rank ID为4或不存在的10等其他值,此时dump数据为空。 | 否 | -| step | 指定dump某个step的数据,默认未配置,表示dump所有step数据。dump特定step时,须指定为训练脚本中存在的step。step为list格式,可配置逐个step,例如:step=[0,1,2];也可以配置step范围,例如:step=list(range(0,9)),表示dump第0到第8个step。 | 否 | -| enable_dataloader | 自动控制开关,可取值True(开启)或False(关闭),默认为False。配置为True后自动识别dump step参数指定的迭代,并在该迭代执行完成后退出训练,此时start和stop函数可不配置,开启该开关要求训练脚本是通过torch.utils.data.dataloader方式加载数据;配置为False则需要配置start和stop函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()。 | 否 | -| model | 开启init dump模式,传入网络模型实例化的对象,配置该参数后,dump操作仅dump网络中init方法里调用的方法(nn.Module类),不会对所有API进行dump。参数示例: model=net,net为网络模型实例化的对象名称。默认未配置。
配置该参数时,PrecisionDebugger模块请在模型实例化之后调用。
该模式不支持“溢出检测”、”ACL级别数据dump“和“模块级精度数据dump”。此模式下dump文件名前缀为网络中定义的模块名或层名。 | 否 | - -#### init dump模式示例代码和数据落盘说明 - -**示例代码** - -```python -import os -import torch -import torch.nn as nn -import torch_npu -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") - - -class Net(nn.Module): - - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=2) - self.relu1 = nn.ReLU() - self.bn1 = nn.BatchNorm2d(16) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - output = self.relu1(x) - return output - -if __name__ == "__main__": - net = Net().npu() - # model参数传入net, 开启init dump 功能 - debugger = PrecisionDebugger(dump_path="./dump", hook_name="dump", model=net) - debugger.configure_hook(mode="api_stack") - debugger.start() - x = torch.randn(1, 1, 28, 28).npu() - out = net(x) - loss = out.sum() - loss.backward() - debugger.stop() -``` - -**落盘数据说明** - -该模式下dump数据命名格式为:`{Layer_name}_{Module_name}_{call_num}_{forward/backward}_{input/output}.npy` - -``` -# 按照上述用例代码进行dump,落盘数据命名示例如下: -conv1_Conv2d_0_forward_input.0.npy -conv1_Conv2d_0_forward_output.npy -relu1_ReLU_0_forward_input.0.npy -....... -bn1_BatchNorm2d_0_backward_output.2.npy -``` - -### configure_hook函数(可选) - -**功能说明** - -设置dump范围。 - -建议在**PrecisionDebugger**模块与模型初始化之间的任意位置添加,不添加此函数时默认使用mode="api_stack" dump整网数据。 - -**原型** - -dump: - -```python -debugger.configure_hook(mode="api_stack", scope=[], api_list=[], filter_switch="OFF", acl_config=None, backward_input=[], input_output_mode=["all"], summary_only=False) -``` - -溢出检测: - -```python -debugger.configure_hook(mode=None, acl_config=None, overflow_nums=1, need_replicate=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为api_stack。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围,mode="api_list"时,需要配置api_list=[],其他模式有需要时配置scope=[]。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"(表示开启过滤,即不dump)或"OFF"(表示关闭过滤)。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| acl_config | acl dump的配置文件。mode="acl"时,该参数必选;mode为其他值时,该参数不选。参数示例:acl_config='./dump.json'。dump.json配置文件详细介绍请参见“**dump.json配置文件说明**”。 | 否 | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 否 | -| input_output_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例input_output_mode=["backward"]或input_output_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | -| summary_mode | 控制dump文件输出的模式,可取值md5(dump仅输出包含md5值的pkl文件,用于验证数据的完整性)、summary(dump仅输出包含API统计信息的pkl文件)、all(dump输出包含API统计信息的pkl文件以及具体的npy文件),参数示例:summary_mode=md5,默认为all。summary_only=True时,不允许配置该参数。 | 否 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| need_replicate | 过程dump数据生成开关,执行溢出检测时,dump目录下会生成forward_real_data和backward_real_data的过程dump数据目录,可取值True(生成)或False(不生成),默认不生成。 | 否 | - -**函数示例** - -configure_hook可配置多种dump模式,示例如下: - -说明: - -以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -以下仅为该函数配置示例,完整代码请参见“**示例代码**”章节。 - -- 示例1:dump指定API列表 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config="./dump.json") - ``` - -- 示例5:dump指定反向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - -- 示例6:dump指定某一类API的API级别输入输出数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例7:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例8: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例9:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(input_output_mode=["backward"]) - ``` - -- 示例10:仅dump pkl文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(summary_only=True) - ``` - -- 示例11:溢出检测dump - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=1) - ``` - - dump执行时会在**PrecisionDebugger**模块的dump_path参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例11:dump溢出API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### start函数(可选) - -**功能说明** - -dump或溢出检测启动函数。 - -在模型初始化之后的任意位置添加。 - -**原型** - -```python -debugger.start() -``` - -该函数为类函数,可以使用debugger.start()也可以使用PrecisionDebugger.start()。 - -### stop函数(可选) - -**功能说明** - -dump或溢出检测停止函数。 - -在**start**函数之后的任意位置添加。 - -**原型** - -```python -debugger.stop() -``` - -该函数为类函数,可以使用debugger.stop()也可以使用PrecisionDebugger.stop()。 - -### 示例代码(自动模式) - -**需要保证用户训练代码是通过torch.utils.data.dataloader方式加载数据。** - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -### 示例代码(手动模式) - -一般情况下使用自动模式可以快速方便进行dump操作,但个别大模型可能在部分卡的训练操作中没有调用dataloader,这会导致自动模式无法dump指定迭代的数据,此时需要关闭自动模式手动在迭代前后插入start()和stop()函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()以标识dump结束。 - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -## register_hook方式dump和溢出检测 - -### 总体说明 - -- 本节主要介绍CPU或GPU及NPU精度数据dump和溢出检测所需要的函数以及示例。 - -- ptdbg_ascend工具默认情况下仅dump PyTorch模型的API输入输出数据进行精度比对,若在比对结果中发现某个API下可能存在ACL的精度问题,那么可以选择dump该API的ACL级别数据进行精度分析。 - -- 某些torch api的输出不是Tensor类型的数据。对于此类API的反向过程进行ACL dump,工具会在运行日志中给出对应的Warning(is not of tensor type and cannot be automatically derived)提示。如若想要进行该类API反向ACL dump,可以通过手动构建单API用例的方式进行ACL dump,具体用例可参见“**[反向ACL dump用例说明](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/%E5%8F%8D%E5%90%91ACL%20dump%E7%94%A8%E4%BE%8B%E8%AF%B4%E6%98%8E.md)**”。 - -- 工具性能:dump数据量较小时(小于5G),参考dump速度0.1GB/s;dump数据量较大时,参考dump速度0.2GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。Dump速度的计算方式:Dump数据量/(单个step添加Dump耗时-原始单个step耗时)。 - -### 约束 -- 进行CPU或GPU数据dump时,请安装torch包而非torch_npu包,避免工具无法识别使用场景,导致失败。 - -- TASK_QUEUE_ENABLE环境变量会导致API下发和执行异步进行,因此在ACL dump前需要将TASK_QUEUE_ENABLE关闭,即export TASK_QUEUE_ENABLE=0。 - -- 不建议在PyTorch训练脚本中同时添加dump接口和性能数据采集(如Ascend PyThon Profiler)接口,二者可能相互影响导致数据不准确。 - -### seed_all - -**功能说明** - -固定随机数。通过固定随机数保证模型的输入或输出一致。在训练主函数开始前调用,避免随机数固定不全。 - -使用form ptdbg import *后自动导入该函数,代码无需再次添加,若需要修改随机数种子和确定性计算模式,则需要通过添加该函数修改。 - -**函数原型** - -```python -seed_all(seed=1234, mode=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------ | ------------------------------------------------------------ | -------- | -| seed | 随机数种子。参数示例:seed=1000。默认值为:1234。 | 否 | -| mode | 确定性计算模式。可配置True或False。参数示例:mode=True。默认为False。
即使在相同的硬件和输入下,API多次执行的结果也可能不同,开启确定性计算是为了保证在相同的硬件和输入下,API多次执行的结果相同。
确定性计算会导致API执行性能降低,建议在发现模型多次执行结果不同的情况下开启。
rnn类算子、ReduceSum、ReduceMean等算子可能与确定性计算存在冲突,若开启确定性计算后多次执行的结果不相同,则考虑存在这些算子。 | 否 | - -**函数示例** - -seed_all函数的随机数种子,取默认值即可,无须配置;第二个参数默认关闭,不开启确定性计算时也无须配置。 - -- 示例1:仅固定随机数,不开启确定性计算 - - ```python - seed_all() - ``` - -- 示例2:固定随机数,开启确定性计算 - - ```python - seed_all(mode=True) - ``` - -**固定随机数范围** - -seed_all函数可固定随机数的范围如下表。 - -| API | 固定随机数 | -| ---------------------------------------- | --------------------------- | -| os.environ['PYTHONHASHSEED'] = str(seed) | 禁止Python中的hash随机化 | -| random.seed(seed) | 设置random随机生成器的种子 | -| np.random.seed(seed) | 设置numpy中随机生成器的种子 | -| torch.manual_seed(seed) | 设置当前CPU的随机种子 | -| torch.cuda.manual_seed(seed) | 设置当前GPU的随机种子 | -| torch.cuda.manual_seed_all(seed) | 设置所有GPU的随机种子 | -| torch_npu.npu.manual_seed(seed) | 设置当前NPU的随机种子 | -| torch_npu.npu.manual_seed_all(seed) | 设置所有NPU的随机种子 | -| torch.backends.cudnn.enable=False | 关闭cuDNN | -| torch.backends.cudnn.benchmark=False | cuDNN确定性地选择算法 | -| torch.backends.cudnn.deterministic=True | cuDNN仅使用确定性的卷积算法 | - -需要保证CPU或GPU以及NPU的模型输入完全一致,dump数据的比对才有意义,seed_all并不能保证模型输入完全一致,如下表所示场景需要保证输入的一致性。 - -| 场景 | 固定方法 | -| --------------- | ------------- | -| 数据集的shuffle | 关闭shuffle。 | -| dropout | 关闭dropout。 | - -关闭shuffle示例: - -```python -train_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size = batch_size, - shuffle = False, - num_workers = num_workers -) -``` - -关闭dropout: - -在使用from ptdbg import *后,工具会自动将torch.nn.functional.dropout、torch.nn.functional.dropout2d、torch.nn.functional.dropout3d、torch.nn.Dropout、torch.nn.Dropout2d、torch.nn.Dropout3d的接口参数p置为0。 - -### set_dump_path - -**功能说明** - -设置数据保存目录。建议在seed_all函数之后调用且需要保证训练进程能够调用该函数;多卡时须保证每个进程都能调用该函数。 - -**函数原型** - -```python -set_dump_path(fpath=None, dump_tag='ptdbg_dump') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| fpath | 设置数据目录路径。参数示例:'./dump_path'。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当set_dump_switch函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置fpath时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,fpath和环境变量需要二选一。 | 否 | -| dump_tag | 设置数据目录名称。参数示例:dump_tag='dump_conv2d'。默认数据目录命名为ptdbg_dump_{version}。
{version}为当前安装ptdbg_ascend工具版本。目录结构参见“**dump数据存盘说明**”。
配置该参数会将生成的`ptdbg_dump_{version}`目录名称变更为dump_tag配置的值,如`dump_conv2d_{version}`。 | 否 | - -**函数示例** - -- 示例1:设置数据目录路径 - - ```python - set_dump_path('./dump_path') - ``` - -- 示例2:设置数据目录名称 - - ```python - set_dump_path('./dump_path', dump_tag='dump_conv2d') - ``` - - -若以相同的数据目录多次dump,则会因同名导致覆盖;多次dump建议配置不同的dump_tag。 - -### register_hook - -**功能说明** - -注册工具钩子函数。在set_dump_path之后调用。 - -dump操作必选。 - -**函数原型** - -```python -register_hook(model, hook, overflow_nums=overflow_nums, dump_mode=dump_mode, dump_config=dump_config_file) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| model | 传入网络模型实例化的对象。参数示例: model=net,net为网络模型实例化的对象名称。 | 是 | -| hook | 注册工具的dump和溢出检测钩子。可取值overflow_check(表示溢出检测)和acc_cmp_dump(表示dump数据),二选一。 | 是 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| dump_mode | 控制针对溢出API的dump模式,可取值"acl"或"api"。配置acl时,表示dump ACL级别的溢出数据,此时set_dump_path参数不生效,dump数据目录由dump_config的.json文件配置。参数示例:dump_mode="acl"。默认不配置,即dump API级别的溢出数据。 | 否 | -| dump_config | acl dump的配置文件。dump_mode="acl"时,该参数必选;dump_mode="api"时,该参数不选。参数示例:dump_config='./dump.json'。 | 否 | - -**函数示例** - -- 示例1:注册工具钩子函数 - - ```python - register_hook(model, acc_cmp_dump) - ``` - -- 示例2:dump指定API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - ``` - - 需要配置set_dump_switch的mode="acl"以及scope指定为前向或反向API,请参见“**set_dump_switch”**的示例。 - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置dump数据目录。 - -- 示例3:溢出检测dump - - ```python - register_hook(model, overflow_check, overflow_nums=3) - ``` - - dump执行时会在set_dump_path的fpath参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例4:dump指定API的ACL级别溢出数据 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### set_dump_switch - -**功能说明** - -设置dump范围。建议在register_hook函数之后的脚本内任意位置插入,但进行精度问题排查建议参照“场景化示例 > 单卡场景精度比对”章节的顺序,先从第一个迭代开始的位置调用并dump整网数据。 - -dump操作必选。 - -**函数原型** - -```python -def set_dump_switch(switch, mode="all", scope=[], api_list=[], filter_switch="OFF", dump_mode=["all"], summary_only=False): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| --------------- | ------------------------------------------------------------ | -------- | -| switch | dump开关。可取值"ON"或"OFF"。须在选定dump开始的位置配置set_dump_switch("ON");dump结束的位置设置set_dump_switch("OFF")。 | 是 | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为all。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| dump_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例dump_mode=["backward"]或dump_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | - -**推荐配置** - -```python -set_dump_switch("ON", mode="api_stack", filter_switch="OFF") -``` - -开启dump数据和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量。 - -**函数示例** - -set_dump_switch可配置多种dump模式,示例如下: - -说明:以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -- 示例1:dump指定API列表 - - ```python - set_dump_switch("ON", mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - set_dump_switch("ON", mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - set_dump_switch("ON", mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件。 - -- 示例4:dump指定反向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件,并通过set_backward_input设置反向API输入的.npy文件。 - -- 示例5:dump指定某一类API的API级别输入输出数据 - - ```python - set_dump_switch("ON", mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例6:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - set_dump_switch("ON", mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例7: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - set_dump_switch("ON", filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例8:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - set_dump_switch("ON", dump_mode=["backward"]) - ``` - -- 示例9:仅dump pkl文件 - - ```python - set_dump_switch("ON", summary_only=True) - ``` - -以上示例均需要在结束dump的位置插入set_dump_switch("OFF")。 - -set_dump_switch配置mode为all或api_stack时,结束dump后,在dump目录下会自动生成compare_data.py比对脚本模板,示例如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": , - "bench_pkl_path": , - "npu_dump_data_dir": , - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -pkl_path和dump_data_dir字段会自动识别pkl和dump目录的路径,用户需要判断当前dump的环境是NPU、CPU或GPU,并将pkl_path和dump_data_dir字段填入下方dump_path_param函数对应的字段中,例如当前设备为NPU,那么填写方式如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": pkl_path, - "bench_pkl_path": , - "npu_dump_data_dir": dump_data_dir, - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -此时,另一侧数据的路径,需要用户另外识别并填入。 - -### set_overflow_check_switch - -**功能说明** - -置溢出检测范围。默认不配置该函数,全量进行溢出检测。 - -仅支持NPU环境。 - -**函数原型** - -```python -set_overflow_check_switch(switch, filter_switch='OFF') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| switch, | 检测开关。可取值"ON"或"OFF"。如果只在特定的step溢出检测,则在期望溢出检测的step位置开始前插入set_overflow_check_switch("ON"),在step结束的位置插入set_overflow_check_switch("OFF")。 | 是 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | - -**函数示例** - -- 示例1:指定范围溢出检测 - - ```python - register_hook(model, overflow_check) - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,dump执行时会在当前目录自动生成ptdbg_dump_{version}目录,保存溢出数据。 - -- 示例2:前向API的ACL级别范围溢出检测 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置溢出数据目录。 - -### set_backward_input - -**功能说明** - -设置反向ACL级别dump时需要的反向输入的.npy文件。 - -**函数原型** - -```python -set_backward_input(backward_input) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------------- | ------------------------------------------------------------ | -------- | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 是 | - -**函数示例** - -```python -register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') -set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) -set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) -``` - -## dump.json配置文件说明 - -**dump.json配置示例** - -```python -{ - "dump": - { - "dump_list":[], - "dump_path":"./dump/output", - "dump_mode":"all", - "dump_op_switch":"on" - } -} -``` - -**dump.json参数说明** - -| 字段名 | 说明 | -| -------------- | ------------------------------------------------------------ | -| dump_list | 待dump数据的API模型。为空,无需配置。 | -| dump_path | dump数据文件存储到运行环境的目录,主要用于指定ACL dump数据路径。支持配置绝对路径或相对路径。dump_path须为已存在目录。 | -| dump_mode | dump数据模式,配置如下:
- output:dump API的输出数据。默认值。
- input:dump API的输入数据。
- all:dump API的输入、输出数据。 | -| dump_op_switch | 单API模型dump数据开关,配置如下: * off:关闭单API模型dump,默认值。 * on:开启单API模型dump。 | - -**dump目录说明** - -配置register_hook的dump_config后,采集的dump数据会在{dump_path}/{time}/{deviceid}/{model_id}目录下生成,例如“/home/HwHiAiUser/output/20200808163566/0/0” - -```bash -├── 20230131172437 -│   └── 1 -│   ├── 0 -│   │   ├── Add.Add.45.0.1675157077183551 -│   │   ├── Cast.trans_Cast_0.31.0.1675157077159449 -│   │   ├── Cast.trans_Cast_5.43.0.1675157077180129 -│   │   ├── MatMul.MatMul.39.0.1675157077172961 -│   │   ├── Mul.Mul.29.0.1675157077155731 -│   │   ├── NPUAllocFloatStatus.NPUAllocFloatStatus.24.0.1675157077145262 -│   │   ├── TransData.trans_TransData_1.33.0.1675157077162791 -│   │   └── TransData.trans_TransData_4.41.0.1675157077176648 -│   ├── 1701737061 -│   │   └── Cast.trans_Cast_2.35.0.1675157077166214 -│   ├── 25 -│   │   └── NPUClearFloatStatus.NPUClearFloatStatus.26.0.1675157077150342 -│   └── 68 -│   └── TransData.trans_TransData_3.37.0.1675157077169473 -``` - -## 模块级精度数据dump - -### 总体说明 - -大模型场景下,通常不是简单的利用自动迁移能力实现GPU到NPU的训练脚本迁移,而是会对NPU网络进行一系列针对性的适配,因此,常常会造成迁移后的NPU模型存在部分子结构不能与GPU原始模型完全对应。模型结构不一致导致API调用类型及数量不一致,若直接按照API粒度进行精度数据dump和比对,则无法完全比对所有的API。 - -本节介绍的功能是对模型中的大粒度模块进行数据dump,使其比对时,对于无法以API粒度比对的模块可以直接以模块粒度进行比对。 - -模块指的是继承自nn.Module类模块,通常情况下这类模块就是一个小模型,可以被视为一个整体,dump数据时以模块为粒度进行dump。 - -### module_dump - -**功能说明** - -开启模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump(module, module_name) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------- | ------------------------------------------------------------ | -------- | -| module | 网络中实例化好的nn.Module类模块的model对象。 | 是 | -| module_name | 用户自定义的该model名称。主要用于dump数据文件的命名,便于在比对时识别模块级数据。 | 是 | - -### module_dump_end - -**功能说明** - -结束模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump_end() -``` - -### 示例代码 - -```python -# 根据需要import包 -import os -import torch -import torch.nn as nn -import torch_npu -import torch.nn.functional as F -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") -# 定义一个简单的网络 -class ModuleOP(nn.Module): - def __init__(self) -> None: - super().__init__() - self.linear_1 = nn.Linear(in_features=8, out_features=4) - self.linear_2 = nn.Linear(in_features=4, out_features=2) - def forward(self, x): - x1 = self.linear_1(x) - x2 = self.linear_2(x1) - r1 = F.relu(x2) - return r1 - -if __name__ == "__main__": - module = ModuleOP() - - # 注册工具 - pdbg = PrecisionDebugger("./dump_data/npu", hook_name="dump") - pdbg.start() - - x = torch.randn(10, 8) - module_dump(module, "MyModuleOP") # 开启模块级精度数据dump - out = module(x) - module_dump_end() # 结束模块级精度数据dump - loss = out.sum() - loss.backward() - pdbg.stop() -``` - -## dump数据存盘说明 - -dump结果目录结构示例如下: - -```bash -├── dump_path -│ └── ptdbg_dump_{version} -│ ├── step0 -│ | ├── rank0 -│ | │ ├── dump -| | | | ├── Tensor_permute_1_forward.npy -| | | | ├── MyModule_0_forward_input.npy # 开启模块级精度数据dump时存在模块级的dump数据文件 -| | | | ... -| | | | └── Fcuntion_linear_5_backward_output.npy -│ | │ └── dump.pkl -│ | ├── rank1 -| | | ├── dump -| | | | └── ... -| | | └── dump.pkl -│ | ├── ... -│ | | -| | └── rank7 -│ ├── step1 -│ | ├── ... -│ ├── step2 -``` - -dump过程中,npy文件在对应算子或者模块被执行后就会落盘,而pkl文件则需要在正常执行PrecisionDebugger.stop()或set_dump_switch("OFF")后才会被落盘保存,异常的程序终止会保存终止前被执行算子的相关npy文件,但是不会生成pkl文件。 - -其中`ptdbg_dump_{version}`为默认命名,debugger方式dump不支持修改该文件夹名称,使用set_dump_path函数则支持通过dump_tag参数修改文件夹名称;rank为设备上各卡的ID,每张卡上dump的数据会生成对应dump目录。 - -**精度比对dump场景** - -精度比对dump场景的结果如下: - -* dump.pkl文件:包含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表示2范数(平方根)。 - -* dump目录:目录下为npy格式的dump数据。 - - npy文件保存的前缀和PyTorch对应关系如下 - - | 前缀 | Torch模块 | - | ----------- | ------------------- | - | Tensor | torch.Tensor | - | Torch | torch | - | Functional | torch.nn.functional | - | NPU | NPU亲和算子 | - | VF | torch._VF | - | Aten | torch.ops.aten | - | Distributed | torch.distributed | - -当configure_hook或set_dump_switch配置mode参数(例如:mode="api_stack" )时,dump结果的文件名会添加api_stack前缀,dump结果如下: - -* api_stack_dump.pkl -* api_stack_dump目录 - -**溢出检测dump场景** - -PrecisionDebugger模块的hook_name参数或register_hook函数设置了overflow_check时,检测API溢出,dump结果的文件名格式为:`{api_type}_{api_name}_{API调用次数}_{前向反向}_{当前溢出次数}`,dump结果示例如下: - -* `Tensor_add_1_forward_1.pkl` -* `Tensor_add_1_forward_1`目录 - -## CPU或GPU与NPU精度数据比对 - -### 总体说明 - -- 本节主要介绍CPU或GPU与NPU精度数据比对的函数以及示例。 - -- 比对函数均通过单独创建精度比对脚本执行,可支持单卡和多卡场景的精度数据比对。 - -- 工具性能:比对数据量较小时(参考值单份文件小于10GB),参考比对速度0.1GB/s;比对数据量较大时,参考比对速度0.3GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。比对速度的计算方式:两份比对文件大小/比对耗时。 - -### 约束 - -- NPU自研API,在CPU或GPU若没有对应的API,该API的dump数据不比对。 - -- NPU与CPU或GPU的计算结果误差可能会随着模型的执行不断累积,最终会出现同一个API因为输入的数据差异较大而无法比对的情况。 - -- CPU或GPU与NPU中两个相同的API会因为调用次数不同导致无法比对或比对到错误的API,不影响整体运行,该API忽略。 - -### 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目录。参数示例:'./npu_dump/ptdbg_dump_v4.0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| bench_dump_dir | 配置CPU、GPU或NPU环境下的dump目录。参数示例:'./gpu_dump/ptdbg_dump_v4.0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。需要预先创建output_path目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_rank{npu_ID}-rank{cpu/gpu/npu_ID}_{timestamp}.csv`。 | 是 | -| **kwargs | 支持compare的所有可选参数。 | 否 | - -**函数示例** - -创建比对脚本,例如compare_distributed.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -compare_distributed('./npu_dump/ptdbg_dump_v4.0', './gpu_dump/ptdbg_dump_v4.0', './output') -``` - -### compare - -**功能说明** - -将CPU或GPU与NPU的dump文件进行比对,仅支持单机单卡。 - -**函数原型** - -```python -compare(input_param, output_path, stack_mode=False, auto_analyze=True, fuzzy_match=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------ | ------------------------------------------------------------ | -------- | -| input_param | 配置dump数据文件及目录。配置参数包括:
- "npu_pkl_path":指定NPU dump目录下的.pkl文件。参数示例:"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "bench_pkl_path":指定CPU、GPU或NPU dump目录下的.pkl文件。参数示例:"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "npu_dump_data_dir":"指定NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "bench_dump_data_dir":"指定CPU、GPU或NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "is_print_compare_log":配置是否开启日志打屏。可取值True或False。可选。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_{timestamp}.csv`。 | 是 | -| stack_mode | 配置stack_mode的开关。仅当dump数据时配置debugger.configure_hook或set_dump_switch的mode="api_stack"时需要开启。参数示例:stack_mode=True,默认为False。 | 否 | -| auto_analyze | 自动精度分析,开启后工具自动针对比对结果进行分析,识别到第一个精度不达标节点(在比对结果文件中的“Accuracy Reached or Not”列显示为No),并给出问题可能产生的原因(打屏展示并生成advisor_{timestamp}.txt文件)。可取值True或False,参数示例:auto_analyze=False,默认为True。 | 否 | -| fuzzy_match | 模糊匹配。开启后,对于网络中同一层级且命名仅调用次数不同的API,可匹配并进行比对。可取值True或False,参数示例:fuzzy_match=True,默认为False。 | 否 | - -**函数示例** - -单机单卡场景下创建比对脚本,例如compare.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### pkl文件比对 - -若使用**compare**或**compare_distributed**函数创建的比对脚本中,input_param参数只配置了npu_pkl_path和bench_pkl_path或使用summary_only方式dump时,可以进行pkl文件的比对,此时比对dump.pkl文件中的统计值,开启后的比对结果文件生成Max diff、Min diff和Mean diff,表示NPU dump数据中API的输入或输出与标杆数据输入或输出的最大最小平均值的差。可以通过该值判断API是否存在精度问题:当某个API的输入和输出的Max diff、Min diff、Mean diff和L2norm diff均为0或无限趋于0,那么可以判断该API无精度问题,反之则可能存在精度问题。 - -**比对脚本示例** - -```python -from ptdbg_ascend import compare -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### parse - -**功能说明** - -解析并提取dump信息中的堆栈信息及数据统计信息。 - -**函数原型** - -```python -parse(pkl_file, moudule_name_prefix) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------------- | ------------------------------------------------------------ | -------- | -| pkl_file | 指定dump数据文件中的pkl文件名。参数示例:"./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl"。 | 是 | -| moudule_name_prefix | 指定待提取的API接口前缀。参数示例:"Torch_norm_1_forward"。 | 是 | - -**函数示例** - -创建堆栈信息及数据统计信息提取脚本,例如parse.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl", "Torch_batch_normal_1_forward") -``` - -### 计算精度评价指标 - -PyTorch精度比对是以CPU或GPU的计算结果为标杆,计算Cosine(余弦相似度)、MaxAbsErr(最大绝对误差)和MaxRelativeErr(最大相对误差),根据这两个结果判断API在运行时是否存在精度问题。 - -计算精度评价指标: - -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中的元素逐个与对应的标杆数据对比,相对误差大于千分之一、千分之五的比例占总元素个数的比例小于千分之一、千分之五。该数据仅作为精度下降趋势的参考,并不参与计算精度是否通过的判定。 - -精度比对结果csv文件中只需要通过Accuracy Reached or Not来判断计算精度是否达标,判断标准如下: - -1. Cosine < 0.99 且 MaxAbsError > 0.001时,精度不达标,标记为“No”。 -2. Cosine < 0.9,精度不达标,标记为“No”。 -3. MaxAbsError > 1,精度不达标,标记为“No”。 -5. 其余情况下记为精度达标,标记为“Yes”。 - -## ptdbg_ascend.parse数据解析功能 - -ptdbg_ascend.parse为命令行交互式界面解析工具,提供更多的数据解析功能并且展示结果。 - -使用场景:本工具主要用于比对前后两次NPU ACL层级dump数据的一致性。 - -### 进入parse交互式界面 - -安装ptdbg_ascend工具后,可以通过使用命令 **python -m ptdbg_ascend.parse** 进入交互式界面,如下所示: - -```bash -python -m ptdbg_ascend.parse -Parse >>> -``` - -可在parse的界面中执行Shell命令,以及如下场景的相关解析命令: - -- 支持指定ACL层级算子数据比对。 -- 支持指定ACL层级算子数据转换及展示。 -- 支持交互式指定pkl文件中API对应dump数据查看。 -- 支持API进行可选层级比对和打印(统计级和像素级)。 - -Ctrl+C可以退出parse交互式界面。不退出parse交互式界面若需要执行非该界面下的内置Shell命令,且命令与parse交互式界面命令冲突时,非该界面命令需要使用run命令,在相关命令前加上run前缀,如下示例: - -```bash -python -m ptdbg_ascend.parse -Parse >>> run vim cli.py -Parse >>> vim cli.py -``` - -以上各场景详细介绍请参见下文章节。 - -### ACL层级算子数据批量转换 - -本功能会将原有待比对dump数据目录下的dump数据按照算子名和时间戳进行梳理并分类,之后再将dump数据转为为npy文件。 - -依赖:CANN包中的msaccucmp工具,需要安装Ascend-CANN-toolkit,详见《[CANN 软件安装指南](https://gitee.com/link?target=https%3A%2F%2Fwww.hiascend.com%2Fdocument%2Fdetail%2Fzh%2Fcanncommercial%2F700%2Fenvdeployment%2Finstg%2Finstg_0001.html)》。 - -输入以下比对命令进行数据转换。 - -```bash -cad -m my_dump_path [-out output_path] [-asc msaccucmp_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| -m | 待转换ACL dump数据目录。 | 是 | -| -out | 结果输出目录,须指定已存在的目录,默认为./parse_data/acl_batch_convert。未指定时保存在默认路径下,比对结束后会打印log提示输出结果存放路径。 | 否 | -| -asc | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py。 | 否 | - -**示例** - -```bash -# 传入待比对数据目录 -Parse >>> cad -m /home/xxx/my_dump_path/20000124003856/0 -# 转换结果打印 -...... -╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -# 转换前的dump文件 -│ SrcFile: /home/xxx/my_dump_path/20000124003856/0/272/TransData.trans_TransData_22.112.21.948645536672764 │ -# 转换后的npy文件 -│ - TransData.trans_TransData_22.112.21.948645536672764.output.0.npy │ -│ - TransData.trans_TransData_22.112.21.948645536672764.input.0.npy │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -...... -[INFO] The comparison result have been written to "./parse_data/acl_batch_convert". -``` - -输出结果: - -原dump数据目录: - -```bash -├── /home/xxx/my_dump_path/20000124003856/0/ -│ ├── 272 -│ │ ├── {op_type}.{op_name}.{task_id}.{stream_id}.{timestamp} -│ │ ... -│ ├── 512 -│ ... -``` - -转换后: - -```bash -├── ./parse_data/acl_batch_convert/{timestamp} -│ ├── {op_name1} -│ │ ├── {timestamp1} -│ │ | ├── {op_type}.{op_name}.{task_id}.{stream_id}.{timestamp}.{input/output}.{参数序号}.npy -│ │ | │ ... -│ │ ├── {timestamp2} -│ │ | ... -│ ├── {op_name2} -│ ├── ... -``` - -### ACL层级算子数据比对 - -本功能主要用于比对前后两次NPU ACL层级dump数据的一致性。 - -本功能支持批量比对,若需要进行批量比对,需要先将两份待比对的NPU ACL层级dump数据进行“**ACL层级算子数据批量转换**”,可以使两份数据更好的匹配;若直接进行dump数据的比对,建议只比对单个dump数据文件。 - -输入以下比对命令进行数据比对。 - -```bash -vc -m my_dump_path -g golden_dump_path [-out output_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| -m | 待比对ACL dump数据目录。 | 是 | -| -g | dump数据目录。 | 是 | -| -out | 结果输出目录,须指定已存在的目录,默认为./parse_data/acl_batch_comapre。未指定时保存在默认路径下,比对结束后会打印log提示输出结果存放路径。 | 否 | - -输出结果:batch_compare_{timestamp}.csv文件。 - -**示例** - -```bash -# 传入待比对数据目录以及标杆数据目录 -Parse >>> vc -m ./my_dump_path -g ./golden_data_path -[INFO]Compare result is saved in : parse_data/acl_batch_comapre/batch_compare_1707271118.csv -``` - -### ACL算子数据的npy转换 - -依赖:CANN包中的msaccucmp工具,需要安装Ascend-CANN-toolkit,详见《[CANN 软件安装指南](https://gitee.com/link?target=https%3A%2F%2Fwww.hiascend.com%2Fdocument%2Fdetail%2Fzh%2Fcanncommercial%2F700%2Fenvdeployment%2Finstg%2Finstg_0001.html)》。 - -输入以下转换命令进行数据转换, 将ACL级别dump数据转为npy文件。 - -```bash -dc -n file_name/file_path [-f format] [-out output_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| --------- | ------------------------------------------------------------ | -------- | -| -n | 需转换的dump数据文件或dump数据文件目录。 | 是 | -| -f | 开启format转换,指定该参数时需要配置format格式。当前内置的Format转换支持如下类型:
FRACTAL_NZ转换NCHW
FRACTAL_NZ转换成NHWC
FRACTAL_NZ转换ND
HWCN转换FRACTAL_Z
HWCN转换成NCHW
HWCN转换成NHWC
NC1HWC0转换成HWCN
NC1HWC0转换成NCHW
NC1HWC0转换成NHWC
NCHW转换成FRACTAL_Z
NCHW转换成NHWC
NHWC转换成FRACTAL_Z
NHWC转换成HWCN
NHWC转换成NCHW
NDC1HWC0转换成NCDHW | 否 | -| -out | 结果输出目录。 | 否 | -| -cmp_path | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py | 否 | - -[^]: 若传入单个dump文件,则转换单个文件,若传入dump文件目录则转换目录下所有dump文件。 - -- 输出结果:npy文件。 -- 若指定-out参数需要用户传入输出路径,并且路径需要已存在。 -- 若未指定输出目录, 则比对结束后将结果保存在默认目录 “./parse_data/convert_result”中,比对结束后会打印log提示输出结果存放路径及转换结果。 - -- 输入以下命令,展示npy数据统计信息。 - - ```bash - pt -n file_path - ``` - - | 参数名称 | 说明 | 是否必选 | - | -------- | ------------- | -------- | - | -n | npy文件路径。 | 是 | - - 打印统计信息:shape, dtype, max, min和mean。默认在npy文件路径下将该数据保存为txt文件。 - -**示例1** - -```bash -# 传入需转换的dump文件目录 -Parse >>> dc -n ./dump_data/ -...... -# 转换结果 -╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ SrcFile: ./dump_data/ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.0.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.1.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.output.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.output.0.npy │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -**示例2** - -```bash -# 查看某个dump数据块的数据信息 -# 默认会将数据中的tensor保存成 txt -Parse >>> pt -n ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy -...... -# 打印统计信息 -[Shape: (1, 16, 56, 56, 16)] [Dtype: float16] [Max: 452.0] [Min: -408.5] [Mean: -3.809] -Path: ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy -TextFile:./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy.txt -``` - -### pkl文件中指定API的dump数据信息查看 - -输入以下命令,解析并输出pkl文件中指定api的统计信息。 - -```bash -pk -f pkl_path -n api_name -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------- | -------- | -| -f | 指定pkl文件路径。 | 是 | -| -n | 指定API名称。 | 是 | - -- 输出结果:打印统计信息(shape, dtype, max和min mean)。 -- 若pkl文件中存在相应的堆栈信息,则会打印堆栈信息。 - -**示例** - -```bash -# 传入pkl文件及api名称 -Parse >>> pk -f ./torch_dump/ptdbg_v3.2/rank0/api_stack_dump.pkl -n Functional_conv2d_0_forward -...... -# 打印统计信息及堆栈(pkl文件不包含堆栈则不会打印堆栈) - -Statistic Info: - [Functional_conv2d_0_forward_input.0][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 1.576936960220337][min: -0.9757485389709473][mean: 0.4961632490158081] - [Functional_conv2d_0_forward_input.1][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 0.20064473152160645][min: -0.47102075815200806][mean: -0.20796933770179749] - [Functional_conv2d_0_forward_input.2][dtype: torch.float32][shape: [2]][max: 0.17380613088607788][min: -0.16853803396224976][mean: 0.0026340484619140625] - [Functional_conv2d_0_forward_output][dtype: torch.float32][shape: [2, 2, 1, 1]][max: 0.02364911139011383][min: -1.762906551361084][mean: -0.6710853576660156] -``` - -### API可选层级比对 - -输入以下命令, 进行统计级和像素级比对。 - -```bash -cn -m my_data*.npy -g gloden*.npy [-p num] [-al atol] [-rl rtol] -``` - -- 统计级比对:对tensor整体进行余弦值及相对误差的计算。 -- 像素级比对:对输入的两个npy文件进行逐元素比对。若两个tensor对应元素的相对误差或绝对误差大于**误差阈值**(-al和-rl配置)则被标记为错误数据。 - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------------------------------------- | -------- | -| -m | 待比对数据。 | 是 | -| -g | 标杆数据。 | 是 | -| -p | 设置比对结束后打印错误元素的个数,默认值20。 | 否 | -| -al | 判定数据存在精度问题的绝对误差阈值,默认0.001。 | 否 | -| -rl | 判定数据存在精度问题的相对误差阈值,默认0.001。 | 否 | -| -s | 将npy文件保存成txt文件,用于查看,默认开启。 | 否 | - -输出结果: - -- 统计级比对结果。 -- 两个文件的统计信息(shape, dtype, max, min和mean)。 -- 错误数据打印表格。 - -**示例** - -```bash -# 对比两个tensor的数据 -Parse >>> cn -m Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy -g InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy -p 10 -s -al 0.002 -rl 0.005 - Error Item Table Top Item Table -┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ ┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ Index ┃ Left ┃ Right ┃ Diff ┃ ┃ Index ┃ Left ┃ Right ┃ Diff ┃ -┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ ┡━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ -│ 155 │ 0.024600908 │ 0.022271132 │ 0.002329776 │ │ 0 │ -0.9206961 │ -0.9222216 │ 0.0015255213 │ -│ 247 │ 0.015752593 │ 0.017937578 │ 0.0021849852 │ │ 1 │ -0.6416973 │ -0.64051837 │ 0.0011789203 │ -│ 282 │ -0.0101207765 │ -0.007852031 │ 0.0022687456 │ │ 2 │ -0.35383835 │ -0.35433492 │ 0.0004965663 │ -│ 292 │ 0.019581757 │ 0.02240482 │ 0.0028230622 │ │ 3 │ -0.18851271 │ -0.18883198 │ 0.00031927228 │ -│ 640 │ -0.06593232 │ -0.06874806 │ 0.0028157383 │ │ 4 │ -0.43508735 │ -0.43534422 │ 0.00025686622 │ -│ 1420 │ 0.09293677 │ 0.09586689 │ 0.0029301196 │ │ 5 │ 1.4447614 │ 1.4466647 │ 0.0019032955 │ -│ 1462 │ -0.085207745 │ -0.088047795 │ 0.0028400496 │ │ 6 │ -0.3455438 │ -0.3444429 │ 0.0011008978 │ -│ 1891 │ -0.03433288 │ -0.036525503 │ 0.002192624 │ │ 7 │ -0.6560242 │ -0.6564579 │ 0.0004336834 │ -│ 2033 │ 0.06828873 │ 0.07139922 │ 0.0031104907 │ │ 8 │ -2.6964858 │ -2.6975214 │ 0.0010356903 │ -│ 2246 │ -0.06376442 │ -0.06121233 │ 0.002552092 │ │ 9 │ -0.73746175 │ -0.73650354 │ 0.00095820427 │ -└───────┴───────────────┴──────────────┴──────────────┘ └───────┴─────────────┴─────────────┴───────────────┘ -╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Left: | -│ |- NpyFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy | -│ |- TxtFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.846897] [Min: -8.368301] [Mean: -0.72565556] | -│ DstFile: │ -│ |- NpyFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy | -│ |- TxtFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.8425903] [Min: -8.374472] [Mean: -0.7256237] │ -│ NumCnt: 655360 │ -│ AllClose: False │ -│ CosSim: 0.99999493 │ -│ ErrorPer: 0.023504638671875 (rl= 0.005, al= 0.002) │ -╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -## FAQ - -[FAQ](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/FAQ.md) diff --git "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T3.md" "b/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T3.md" deleted file mode 100644 index 5385d74583888bd98b2f8aaf778e100d24cba5b2..0000000000000000000000000000000000000000 --- "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T3.md" +++ /dev/null @@ -1,2163 +0,0 @@ -# **PyTorch精度工具使用指南** - -本文主要介绍PyTorch精度工具ptdbg_ascend的使用以及精度比对场景示例。 - -ptdbg_ascend工具的原理及安装请参见《[PyTorch精度工具](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/README.md)》。 - -ptdbg_ascend工具主要支持PyTorch API精度数据dump、溢出检测、精度比对以及parse数据解析功能。其中dump和溢出检测功能支持使用debugger和register_hook方式进行精度数据的dump和溢出检测,推荐使用debugger方式。 - -## PyTorch精度比对总体流程 - -1. 准备CPU或GPU训练工程。 - -2. 在环境下安装ptdbg_ascend工具。 - -3. 在训练脚本内插入ptdbg_ascend工具dump接口。 - -4. 执行训练dump数据。 - -5. 将CPU或GPU训练工程迁移为NPU训练工程。 - - 请参见《[PyTorch模型迁移和训练指南](https://www.hiascend.com/document/detail/zh/canncommercial/63RC1/modeldevpt/ptmigr/ptmigr_0001.html)》。 - -6. 在NPU环境下安装ptdbg_ascend工具。 - -7. 在NPU训练脚本内插入ptdbg_ascend工具dump接口。 - -8. NPU环境下执行训练dump数据。 - -9. 创建并配置精度比对脚本,例如compare.py。 - -10. 执行CPU或GPU dump与NPU dump数据的精度比对。 - -11. 比对结果分析。 - -## 快速入门(debugger方式) - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**debugger方式dump和溢出检测**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 单卡场景精度比对 - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump详细信息。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据目录为npu_dump,假设GPU dump数据目录为gpu_dump;dump将生成pkl数据文件api_stack_dump.pkl和npy数据目录api_stack_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config='./dump.json') - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景 - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定前向API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=-1) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./overflow_dump", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./overflow_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过[Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## 场景化示例 - -本章节主要介绍通过ptdbg_ascend工具进行精度比对和分析,主要使用“**CPU或GPU及NPU精度数据dump**”和“**CPU或GPU与NPU精度数据比对**”章节中介绍的ptdbg_ascend工具接口。 - -### 多卡场景精度比对 - -精度工具支持多卡场景的精度比对,多卡场景的dump步骤与单卡场景完全一致,请参见“**单卡场景精度比对**”章节,不同的是多卡数据精度比对时需要使用“compare_distributed”函数进行比对。 - -**大模型场景下dump推荐使用debugger方式的手动模式。** - -如下示例: - -说明:多机多卡场景需要每个设备单独执行比对操作。 - -假设NPU dump npy数据目录为npu_dump/ptdbg_dump_v4.0,GPU dump npy数据目录为gpu_dump/ptdbg_dump_v4.0。 - -1. 创建比对脚本,例如compare_distributed.py,拷贝如下代码。 - - ```python - from ptdbg_ascend import * - compare_distributed('./npu_dump/ptdbg_dump_v4.0/step0', './gpu_dump/ptdbg_dump_v4.0/step0', './output') - ``` - -2. 执行比对: - - ```bash - python3 compare_distributed.py - ``` - -两次运行须用相同数量的卡,传入`compare_distributed`的两个文件夹下须有相同个数的rank文件夹,且不包含其他无关文件,否则将无法比对。 - -**多卡set_dump_path注意事项** - -多卡一般为多进程,须保证每个进程都正确调用PrecisionDebugger或set_dump_path,或把PrecisionDebugger或set_dump_path插入到import语句后,如: - -```python -from ptdbg_ascend import * -debugger = PrecisionDebugger(dump_path="./npu_dump", hook_name="dump", step=[0]) -``` - -或 - -```python -from ptdbg_ascend import * -seed_all() -set_dump_path('./dump_resnet') -``` - -如此可保证set_dump_path在每个进程都被调用。 - -**多卡register_hook注意事项** - -register_hook需要在set_dump_path之后调用,也需要在每个进程上被调用,建议在搬运模型数据到卡之后调用。识别方法如下: - -- 找到训练代码中遍历epoch的for循环或遍历数据集的for循环,把register_hook放到循环开始前即可。 -- 找到训练代码中调用DDP或者DistributedDataParallel的代码行,把register_hook放到该代码行所在的代码块之后。 -- 若代码中均无以上两种情况,需要保证register_hook在模型定义之后插入,并配置rank参数。rank参数获取rank_id请参见“**[rank_id获取方法](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/rank_id获取方法.md)**”。 - -### NPU vs NPU精度比对 - -对于NPU vs NPU场景,是针对同一模型,进行迭代(模型、API版本升级或设备硬件升级)时存在的精度下降问题,对比相同模型在迭代前后版本的API计算数值,进行问题定位。 - -一般情况下迭代涉及NPU自定义算子,因此,可以仅dump NPU自定义算子进行比对。比对精度问题分析请参见“**单卡场景精度比对**”章节。 - -工具当前支持dump NPU自定义算子如下: - -| 序号 | NPU自定义算子 | -| :--- | ----------------------------------------------- | -| 1 | torch_npu.one_ | -| 2 | torch_npu.npu_sort_v2 | -| 3 | torch_npu.npu_transpose | -| 4 | torch_npu.npu_broadcast | -| 5 | torch_npu.npu_dtype_cast | -| 6 | torch_npu.empty_with_format | -| 7 | torch_npu.npu_one_hot | -| 8 | torch_npu.npu_stride_add | -| 9 | torch_npu.npu_ps_roi_pooling | -| 10 | torch_npu.npu_roi_align | -| 11 | torch_npu.npu_nms_v4 | -| 12 | torch_npu.npu_iou | -| 13 | torch_npu.npu_nms_with_mask | -| 14 | torch_npu.npu_pad | -| 15 | torch_npu.npu_bounding_box_encode | -| 16 | torch_npu.npu_bounding_box_decode | -| 17 | torch_npu.npu_batch_nms | -| 18 | torch_npu.npu_slice | -| 19 | torch_npu._npu_dropout | -| 20 | torch_npu.npu_indexing | -| 21 | torch_npu.npu_ifmr | -| 22 | torch_npu.npu_max | -| 23 | torch_npu.npu_scatter | -| 24 | torch_npu.npu_layer_norm_eval | -| 25 | torch_npu.npu_alloc_float_status | -| 26 | torch_npu.npu_confusion_transpose | -| 27 | torch_npu.npu_bmmV2 | -| 28 | torch_npu.fast_gelu | -| 29 | torch_npu.npu_sub_sample | -| 30 | torch_npu.npu_deformable_conv2d | -| 31 | torch_npu.npu_mish | -| 32 | torch_npu.npu_anchor_response_flags | -| 33 | torch_npu.npu_yolo_boxes_encode | -| 34 | torch_npu.npu_grid_assign_positive | -| 35 | torch_npu.npu_normalize_batch | -| 36 | torch_npu.npu_masked_fill_range | -| 37 | torch_npu.npu_linear | -| 38 | torch_npu.npu_bert_apply_adam | -| 39 | torch_npu.npu_giou | -| 40 | torch_npu.npu_ciou | -| 41 | torch_npu.npu_diou | -| 42 | torch_npu.npu_sign_bits_pack | -| 43 | torch_npu.npu_sign_bits_unpack | -| 44 | torch_npu.npu_flash_attention | -| 45 | torch_npu.npu_scaled_masked_softmax | -| 46 | torch_npu.npu_rotary_mul | -| 47 | torch_npu.npu_roi_align | -| 48 | torch_npu.npu_roi_alignbk | -| 49 | torch_npu.npu_ptiou | -| 50 | torch_npu.npu_fusion_attention | -| 51 | torch_npu.npu_dropout_with_add_softmax | -| 52 | torch_npu.npu_random_choice_with_mask | -| 53 | torch_npu.npu_rotated_iou | -| 54 | torch_npu.npu_conv2d | -| 55 | torch_npu.npu_conv3d | -| 56 | torch_npu.npu_softmax_cross_entropy_with_logits | -| 57 | torch_npu.npu_all_gather_base_mm | -| 58 | torch_npu.npu_swiglu | -| 59 | torch_npu.npu_rms_norm | -| 60 | torch_npu.npu_mm_reduce_scatter_base | -| 61 | torch_npu.npu_mm_all_reduce_base | -| 62 | torch_npu.npu_conv_transpose2d | -| 63 | torch_npu.npu_convolution | -| 64 | torch_npu.npu_convolution_transpose | -| 65 | torch_npu.npu_min | -| 66 | torch_npu.npu_nms_rotated | -| 67 | torch_npu.npu_reshape | -| 68 | torch_npu.npu_rotated_box_decode | -| 69 | torch_npu.npu_rotated_box_encode | -| 70 | torch_npu.npu_rotated_overlaps | -| 71 | torch_npu.npu_silu | -| 72 | torch_npu.npu_fused_attention_score | -| 73 | torch_npu.npu_multi_head_attention | -| 74 | torch_npu.npu_gru | -| 75 | torch_npu.npu_incre_flash_attention | -| 76 | torch_npu.npu_prompt_flash_attention | -| 77 | torch_npu.npu_lstm | -| 78 | torch_npu.npu_apply_adam | - -### 通信API的数据dump - -通信类API数据可以使用全量dump方式获取,若只dump通信类API数据,可以使用如下示例: - -```python -debugger.configure_hook(mode="api_list", api_list=["distributed"]) -``` - -或 - -```python -set_dump_switch("ON", mode="api_list", api_list=["distributed"]) -``` - -通信类API支持列表: - -| 序号 | Distributed | -| :--- | -------------------- | -| 1 | send | -| 2 | recv | -| 3 | broadcast | -| 4 | all_reduce | -| 5 | reduce | -| 6 | all_gather | -| 7 | gather | -| 8 | isend | -| 9 | irecv | -| 10 | scatter | -| 11 | reduce_scatter | -| 12 | _reduce_scatter_base | -| 13 | _all_gather_base | - -### 单卡场景精度比对(register_hook方式) - -**精度分析建议** - -PyTorch训练场景的精度问题分析建议参考以下思路进行精度比对和比对结果分析: - -1. 整网比对:dump整网数据并进行精度比对,初步定位异常范围。 -2. 缩小范围:根据Accuracy Reached or Not找出不符合精度标准的API。 -3. 范围比对:对不符合精度标准的API重新dump。 -4. 分析原因并优化:分析API精度不符合标准的原因并进行优化调整。 -5. 整网比对:重新进行整网比对,判断优化后的API是否已符合精度标准以及是否出现新的精度问题。 -6. 重复1~5步,直到不存在精度问题为止。 - -**精度分析示例** - -1. dump整网数据。 - - 分别dump CPU或GPU以及NPU数据,在PyTorch训练脚本插入dump接口,示例代码如下(下面以NPU为例,CPU或GPU dump基本相同): - - ```python - from ptdbg_ascend import * - - # 在main函数开始前固定随机数 - seed_all() - - # 配置dump数据目录路径和名称 - set_dump_path("./npu_dump", dump_tag='all') - - # 注册dump回调函数 - register_hook(model, acc_cmp_dump) - - ... - - # 在第一个迭代开始的位置开启dump和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="api_stack", filter_switch="OFF") - - ... - - # 在第一个迭代结束的位置关闭dump - set_dump_switch("OFF") - ``` - -2. 比对整网数据。 - - 第1步中的NPU dump数据文件为npu_dump.pkl,假设NPU dump npy数据目录为npu_dump,GPU dump数据文件为gpu_dump.pkl,GPU dump npy数据目录为gpu_dump。 - - 创建并配置精度比对脚本,以创建compare.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - dump_result_param={ - "npu_pkl_path": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "bench_pkl_path": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", - "npu_dump_data_dir": "./npu_dump/all_v4.0/step0/rank0/api_stack_dump", - "bench_dump_data_dir": "./gpu_dump/all_v4.0/step0/rank0/api_stack_dump", - "is_print_compare_log": True - } - compare(dump_result_param, "./output", stack_mode=True) - ``` - - 执行比对: - - ```bash - python3 compare.py - ``` - - 在output目录下生成结果文件,包括:`compare_result_{timestamp}.csv`和`advisor_{timestamp}.txt` - -3. 找出存在问题的API。 - - 1. 根据`advisor_{timestamp}.txt`或打屏信息的提示,可找到存在精度问题的算子(Suspect Nodes)和专家建议(Expert Advice) - - ![auto_analyze_log](img/auto_analyze_log.png) - - 2. 根据第2步结果文件`compare_result_{timestamp}.csv`中的Accuracy Reached or No字段显示为NO的API,针对该API执行后续比对操作,分析该API存在的精度问题。 - -4. (可选)提取指定API的堆栈信息和dump数据统计信息。 - - 通过parse接口可以清晰的显示特定API的堆栈信息和dump数据统计信息,结合堆栈信息分析代码中可能存在的精度问题。 - - 创建并配置提取脚本,以创建parse.py为例,示例代码如下: - - ```python - from ptdbg_ascend import * - - # 提取dump信息中第1次调用的API:Torch_batch_normal的堆栈信息及数据统计信息 - parse("./npu_dump/all_v4.0/step0/rank0/api_stack_dump.pkl", "Torch_batch_normal_1_forward") - ``` - - 执行提取: - - ```bash - python3 parse.py - ``` - - - -5. (可选)指定API对其底层ACL数据进行dump。 - - - dump指定前向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='forward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定前向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"], filter_switch="OFF") - - ... - - set_dump_switch("OFF") - ``` - - - dump指定反向API的ACL级别数据 - - ```python - from ptdbg_ascend import * - - # 固定随机数,开启确定性计算 - seed_all(mode=True) - set_dump_path("./dump_path", dump_tag='backward') - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - - # dump指定反向API的ACL级别数据、bool和整型的tensor以及浮点、bool和整型的标量 - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"], filter_switch="OFF") - set_backward_input(["./npu_dump/all_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - - ... - - set_dump_switch("OFF") - ``` - -6. (可选)重新比对。 - - 根据第4或5步的dump数据重新配置compare.py并执行比对,可以对单API模型进行问题复现。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 - -### 溢出检测场景(register_hook方式) - -溢出检测是针对NPU的PyTorch API,检测是否存在溢出的情况。当前仅支持识别aicore浮点溢出。 - -溢出检测原理:针对溢出阶段,开启acl dump模式,重新对溢出阶段执行,落盘数据。 - -建议按照如下步骤操作: - -1. 在NPU环境下安装ptdbg_ascend工具。 - -2. 在NPU训练脚本内插入ptdbg_ascend工具溢出检测接口。 - - - 示例1:全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check, overflow_nums=3) - - ... - ``` - - 多卡使用时各卡单独计算溢出次数。 - - - 示例2:dump指定API的ACL级别溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定API的ACL级别溢出数据 - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - - # 在期望溢出检测的step位置开始前打开溢出检测开关 - set_overflow_check_switch("ON") - - ... - - # 在step结束的位置关闭溢出检测开关 - set_overflow_check_switch("OFF") - - ... - ``` - - - 示例3:dump指定反向API的ACL级别的溢出数据 - - 1. 进行全量溢出检测 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # 设置检测到3次溢出后退出训练 - register_hook(model, overflow_check) - - ... - ``` - - 2. dump指定反向API的ACL级别的溢出数据 - - ```python - from ptdbg_ascend import * - seed_all() - # 配置溢出数据目录路径和名称 - set_dump_path("./overflow_dump") - ... - # dump指定反向API的ACL级别溢出数据 - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 针对前向溢出API,可以通过overflow_nums,配置允许的溢出次数,并将每次溢出API的全部ACL数据dump下来,到达指定溢出次数后停止,停止后会看到堆栈打印包含如下字段。 - - ```bash - ValueError: [overflow xxx times]: dump file is saved in 'xxxxx.pkl'. - ``` - - 其中xxx times为用户设置的次数,xxxxx.pkl为文件生成路径。 - -3. NPU环境下执行训练dump溢出数据。 - - 针对输入正常但输出存在溢出的API,会训练执行目录下将溢出的API信息dump并保存为`forward_info_{pid}.json`和`backward_info_{pid}.json`,通过 [Ascend模型精度预检工具](https://gitee.com/ascend/att/tree/master/debug/accuracy_tools/api_accuracy_checker)对json文件进行解析,输出溢出API为正常溢出还是非正常溢出,从而帮助用户快速判断。 - - 精度预检工具执行命令如下: - - ```bash - # 下载att代码仓后执行如下命令 - export PYTHONPATH=$PYTHONPATH:$ATT_HOME/debug/accuracy_tools/ - cd $ATT_HOME/debug/accuracy_tools/api_accuracy_checker/run_ut - python run_overflow_check.py -forward ./forward_info_0.json - ``` - - 反向过程溢出的API暂不支持精度预检功能。 - - 当重复执行溢出检测dump操作时,需要删除上一次dump目录下的溢出检测dump数据,否则将因重名而报错。 - -**注意事项** - -* dump_mode="acl"场景下,会增加npu的内存消耗,请谨慎开启。 -* 部分API存在调用嵌套关系,比如functional.batch_norm实际调用torch.batch_norm,该场景会影响acl init初始化多次,导致功能异常。 -* 混合精度动态loss scale场景下,正常训练会有"Gradient overflow. SKipping step"日志,添加溢出检测后日志消失,可以通过设置环境变量export OVERFLOW_DEBUG_MODE_ENABLE=1,并将register_hook位置调整amp.initialize之前解决。此功能需要cann包配套支持,不支持版本执行报错EZ3003。 - -## debugger方式dump和溢出检测(推荐) - -### PrecisionDebugger模块 - -**功能说明** - -PrecisionDebugger模块包含dump和溢出检测功能的总体配置项。可以指定dump目录,设置dump或溢出检测功能,指定dump的卡和迭代。 - -可以在from ptdbg_ascend import *和模型初始化之间的任意位置添加该模块。 - -**原型** - -```python -PrecisionDebugger(dump_path=None, hook_name=None, rank=None, step=[], enable_dataloader=False, model=None): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| dump_path | 设置dump数据目录路径,参数示例:"./dump_path"。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当**configure_hook**函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置dump_path时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时dump数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,dump_path和环境变量需要二选一。 | 否 | -| hook_name | dump模式,可取值dump和overflow_check,表示dump和溢出检测功能,二选一。 | 是 | -| rank | 指定对某张卡上的数据进行dump或溢出检测,默认未配置(表示dump所有卡的数据),须根据实际卡的Rank ID配置。应配置为大于0的正整数,且须根据实际卡的Rank ID配置,若所配置的值大于实际训练所运行的卡的Rank ID,则dump数据为空,比如当前环境Rank ID为0~7,实际训练运行0~3卡,此时若配置Rank ID为4或不存在的10等其他值,此时dump数据为空。 | 否 | -| step | 指定dump某个step的数据,默认未配置,表示dump所有step数据。dump特定step时,须指定为训练脚本中存在的step。step为list格式,可配置逐个step,例如:step=[0,1,2];也可以配置step范围,例如:step=list(range(0,9)),表示dump第0到第8个step。 | 否 | -| enable_dataloader | 自动控制开关,可取值True(开启)或False(关闭),默认为False。配置为True后自动识别dump step参数指定的迭代,并在该迭代执行完成后退出训练,此时start和stop函数可不配置,开启该开关要求训练脚本是通过torch.utils.data.dataloader方式加载数据;配置为False则需要配置start和stop函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()。 | 否 | -| model | 开启init dump模式,传入网络模型实例化的对象,配置该参数后,dump操作仅dump网络中init方法里调用的方法(nn.Module类),不会对所有API进行dump。参数示例: model=net,net为网络模型实例化的对象名称。默认未配置。
配置该参数时,PrecisionDebugger模块请在模型实例化之后调用。
该模式不支持“溢出检测”、”ACL级别数据dump“和“模块级精度数据dump”。此模式下dump文件名前缀为网络中定义的模块名或层名。 | 否 | - -#### init dump模式示例代码和数据落盘说明 - -**示例代码** - -```python -import os -import torch -import torch.nn as nn -import torch_npu -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") - - -class Net(nn.Module): - - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=2) - self.relu1 = nn.ReLU() - self.bn1 = nn.BatchNorm2d(16) - - def forward(self, x): - x = self.conv1(x) - x = self.bn1(x) - output = self.relu1(x) - return output - -if __name__ == "__main__": - net = Net().npu() - # model参数传入net, 开启init dump 功能 - debugger = PrecisionDebugger(dump_path="./dump", hook_name="dump", model=net) - debugger.configure_hook(mode="api_stack") - debugger.start() - x = torch.randn(1, 1, 28, 28).npu() - out = net(x) - loss = out.sum() - loss.backward() - debugger.stop() -``` - -**落盘数据说明** - -该模式下dump数据命名格式为:`{Layer_name}_{Module_name}_{call_num}_{forward/backward}_{input/output}.npy` - -``` -# 按照上述用例代码进行dump,落盘数据命名示例如下: -conv1_Conv2d_0_forward_input.0.npy -conv1_Conv2d_0_forward_output.npy -relu1_ReLU_0_forward_input.0.npy -....... -bn1_BatchNorm2d_0_backward_output.2.npy -``` - -### configure_hook函数(可选) - -**功能说明** - -设置dump范围。 - -建议在**PrecisionDebugger**模块与模型初始化之间的任意位置添加,不添加此函数时默认使用mode="api_stack" dump整网数据。 - -**原型** - -dump: - -```python -debugger.configure_hook(mode="api_stack", scope=[], api_list=[], filter_switch="OFF", acl_config=None, backward_input=[], input_output_mode=["all"], summary_only=False) -``` - -溢出检测: - -```python -debugger.configure_hook(mode=None, acl_config=None, overflow_nums=1, need_replicate=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------------- | ------------------------------------------------------------ | -------- | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为api_stack。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围,mode="api_list"时,需要配置api_list=[],其他模式有需要时配置scope=[]。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"(表示开启过滤,即不dump)或"OFF"(表示关闭过滤)。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| acl_config | acl dump的配置文件。mode="acl"时,该参数必选;mode为其他值时,该参数不选。参数示例:acl_config='./dump.json'。dump.json配置文件详细介绍请参见“**dump.json配置文件说明**”。 | 否 | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 否 | -| input_output_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例input_output_mode=["backward"]或input_output_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | -| summary_mode | 控制dump文件输出的模式,可取值md5(dump仅输出包含md5值的pkl文件,用于验证数据的完整性)、summary(dump仅输出包含API统计信息的pkl文件)、all(dump输出包含API统计信息的pkl文件以及具体的npy文件),参数示例:summary_mode=md5,默认为all。summary_only=True时,不允许配置该参数。 | 否 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| need_replicate | 过程dump数据生成开关,执行溢出检测时,dump目录下会生成forward_real_data和backward_real_data的过程dump数据目录,可取值True(生成)或False(不生成),默认不生成。 | 否 | - -**函数示例** - -configure_hook可配置多种dump模式,示例如下: - -说明: - -以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -以下仅为该函数配置示例,完整代码请参见“**示例代码**”章节。 - -- 示例1:dump指定API列表 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Tensor_permute_1_forward"], acl_config="./dump.json") - ``` - -- 示例5:dump指定反向API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="acl", scope=["Functional_conv2d_1_backward"], acl_config="./dump.json", backward_input=["./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - -- 示例6:dump指定某一类API的API级别输入输出数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例7:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例8: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例9:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(input_output_mode=["backward"]) - ``` - -- 示例10:仅dump pkl文件 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - debugger.configure_hook(summary_only=True) - ``` - -- 示例11:溢出检测dump - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(overflow_nums=1) - ``` - - dump执行时会在**PrecisionDebugger**模块的dump_path参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例11:dump溢出API的ACL级别数据 - - ```python - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - debugger.configure_hook(mode="acl", acl_config="./dump.json") - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### start函数(可选) - -**功能说明** - -dump或溢出检测启动函数。 - -在模型初始化之后的任意位置添加。 - -**原型** - -```python -debugger.start() -``` - -该函数为类函数,可以使用debugger.start()也可以使用PrecisionDebugger.start()。 - -### stop函数(可选) - -**功能说明** - -dump或溢出检测停止函数。 - -在**start**函数之后的任意位置添加。 - -**原型** - -```python -debugger.stop() -``` - -该函数为类函数,可以使用debugger.stop()也可以使用PrecisionDebugger.stop()。 - -### 示例代码(自动模式) - -**需要保证用户训练代码是通过torch.utils.data.dataloader方式加载数据。** - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0,2], enable_dataloader=True) - # 请勿将以上初始化流程插入到循环代码中 - ``` - -### 示例代码(手动模式) - -一般情况下使用自动模式可以快速方便进行dump操作,但个别大模型可能在部分卡的训练操作中没有调用dataloader,这会导致自动模式无法dump指定迭代的数据,此时需要关闭自动模式手动在迭代前后插入start()和stop()函数,并在最后一个stop函数后或一个step结束的位置添加debugger.step()以标识dump结束。 - -- 示例1:开启dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="dump", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -- 示例2:开启溢出检测dump - - ```python - from ptdbg_ascend import * - debugger = PrecisionDebugger(dump_path="./dump_path", hook_name="overflow_check", step=[0]) - # 请勿将以上初始化流程插入到循环代码中 - - # 模型初始化 - # 下面代码也可以用PrecisionDebugger.start()和PrecisionDebugger.stop() - debugger.start() - - # 需要dump的代码片段1 - - debugger.stop() - debugger.start() - - # 需要dump的代码片段2 - - debugger.stop() - debugger.step() - ``` - -## register_hook方式dump和溢出检测 - -### 总体说明 - -- 本节主要介绍CPU或GPU及NPU精度数据dump和溢出检测所需要的函数以及示例。 - -- ptdbg_ascend工具默认情况下仅dump PyTorch模型的API输入输出数据进行精度比对,若在比对结果中发现某个API下可能存在ACL的精度问题,那么可以选择dump该API的ACL级别数据进行精度分析。 - -- 某些torch api的输出不是Tensor类型的数据。对于此类API的反向过程进行ACL dump,工具会在运行日志中给出对应的Warning(is not of tensor type and cannot be automatically derived)提示。如若想要进行该类API反向ACL dump,可以通过手动构建单API用例的方式进行ACL dump,具体用例可参见“**[反向ACL dump用例说明](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/%E5%8F%8D%E5%90%91ACL%20dump%E7%94%A8%E4%BE%8B%E8%AF%B4%E6%98%8E.md)**”。 - -- 工具性能:dump数据量较小时(小于5G),参考dump速度0.1GB/s;dump数据量较大时,参考dump速度0.2GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。Dump速度的计算方式:Dump数据量/(单个step添加Dump耗时-原始单个step耗时)。 - -### 约束 -- 进行CPU或GPU数据dump时,请安装torch包而非torch_npu包,避免工具无法识别使用场景,导致失败。 - -- TASK_QUEUE_ENABLE环境变量会导致API下发和执行异步进行,因此在ACL dump前需要将TASK_QUEUE_ENABLE关闭,即export TASK_QUEUE_ENABLE=0。 - -- 不建议在PyTorch训练脚本中同时添加dump接口和性能数据采集(如Ascend PyThon Profiler)接口,二者可能相互影响导致数据不准确。 - -### seed_all - -**功能说明** - -固定随机数。通过固定随机数保证模型的输入或输出一致。在训练主函数开始前调用,避免随机数固定不全。 - -使用form ptdbg import *后自动导入该函数,代码无需再次添加,若需要修改随机数种子和确定性计算模式,则需要通过添加该函数修改。 - -**函数原型** - -```python -seed_all(seed=1234, mode=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------ | ------------------------------------------------------------ | -------- | -| seed | 随机数种子。参数示例:seed=1000。默认值为:1234。 | 否 | -| mode | 确定性计算模式。可配置True或False。参数示例:mode=True。默认为False。
即使在相同的硬件和输入下,API多次执行的结果也可能不同,开启确定性计算是为了保证在相同的硬件和输入下,API多次执行的结果相同。
确定性计算会导致API执行性能降低,建议在发现模型多次执行结果不同的情况下开启。
rnn类算子、ReduceSum、ReduceMean等算子可能与确定性计算存在冲突,若开启确定性计算后多次执行的结果不相同,则考虑存在这些算子。 | 否 | - -**函数示例** - -seed_all函数的随机数种子,取默认值即可,无须配置;第二个参数默认关闭,不开启确定性计算时也无须配置。 - -- 示例1:仅固定随机数,不开启确定性计算 - - ```python - seed_all() - ``` - -- 示例2:固定随机数,开启确定性计算 - - ```python - seed_all(mode=True) - ``` - -**固定随机数范围** - -seed_all函数可固定随机数的范围如下表。 - -| API | 固定随机数 | -| ---------------------------------------- | --------------------------- | -| os.environ['PYTHONHASHSEED'] = str(seed) | 禁止Python中的hash随机化 | -| random.seed(seed) | 设置random随机生成器的种子 | -| np.random.seed(seed) | 设置numpy中随机生成器的种子 | -| torch.manual_seed(seed) | 设置当前CPU的随机种子 | -| torch.cuda.manual_seed(seed) | 设置当前GPU的随机种子 | -| torch.cuda.manual_seed_all(seed) | 设置所有GPU的随机种子 | -| torch_npu.npu.manual_seed(seed) | 设置当前NPU的随机种子 | -| torch_npu.npu.manual_seed_all(seed) | 设置所有NPU的随机种子 | -| torch.backends.cudnn.enable=False | 关闭cuDNN | -| torch.backends.cudnn.benchmark=False | cuDNN确定性地选择算法 | -| torch.backends.cudnn.deterministic=True | cuDNN仅使用确定性的卷积算法 | - -需要保证CPU或GPU以及NPU的模型输入完全一致,dump数据的比对才有意义,seed_all并不能保证模型输入完全一致,如下表所示场景需要保证输入的一致性。 - -| 场景 | 固定方法 | -| --------------- | ------------- | -| 数据集的shuffle | 关闭shuffle。 | -| dropout | 关闭dropout。 | - -关闭shuffle示例: - -```python -train_loader = torch.utils.data.DataLoader( - train_dataset, - batch_size = batch_size, - shuffle = False, - num_workers = num_workers -) -``` - -关闭dropout: - -在使用from ptdbg import *后,工具会自动将torch.nn.functional.dropout、torch.nn.functional.dropout2d、torch.nn.functional.dropout3d、torch.nn.Dropout、torch.nn.Dropout2d、torch.nn.Dropout3d的接口参数p置为0。 - -### set_dump_path - -**功能说明** - -设置数据保存目录。建议在seed_all函数之后调用且需要保证训练进程能够调用该函数;多卡时须保证每个进程都能调用该函数。 - -**函数原型** - -```python -set_dump_path(fpath=None, dump_tag='ptdbg_dump') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| fpath | 设置数据目录路径。参数示例:'./dump_path'。
默认在dump_path目录下生成`ptdbg_dump_{version}`目录,并在该目录下生成`dump.pkl`文件以及`dump`数据文件保存目录。
当set_dump_switch函数配置了mode参数时,`dump.pkl`文件以及`dump`数据文件保存目录名称添加mode参数值为前缀,详情请参见“**dump数据存盘说明**”。
未配置fpath时,也可以通过环境变量ASCEND_WORK_PATH配置dump路径,此时数据将落盘在${ASCEND_WORK_PATH}/dump_data下,自定义配置dump_path优先级高于环境变量,fpath和环境变量需要二选一。 | 否 | -| dump_tag | 设置数据目录名称。参数示例:dump_tag='dump_conv2d'。默认数据目录命名为ptdbg_dump_{version}。
{version}为当前安装ptdbg_ascend工具版本。目录结构参见“**dump数据存盘说明**”。
配置该参数会将生成的`ptdbg_dump_{version}`目录名称变更为dump_tag配置的值,如`dump_conv2d_{version}`。 | 否 | - -**函数示例** - -- 示例1:设置数据目录路径 - - ```python - set_dump_path('./dump_path') - ``` - -- 示例2:设置数据目录名称 - - ```python - set_dump_path('./dump_path', dump_tag='dump_conv2d') - ``` - - -若以相同的数据目录多次dump,则会因同名导致覆盖;多次dump建议配置不同的dump_tag。 - -### register_hook - -**功能说明** - -注册工具钩子函数。在set_dump_path之后调用。 - -dump操作必选。 - -**函数原型** - -```python -register_hook(model, hook, overflow_nums=overflow_nums, dump_mode=dump_mode, dump_config=dump_config_file) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| model | 传入网络模型实例化的对象。参数示例: model=net,net为网络模型实例化的对象名称。 | 是 | -| hook | 注册工具的dump和溢出检测钩子。可取值overflow_check(表示溢出检测)和acc_cmp_dump(表示dump数据),二选一。 | 是 | -| overflow_nums | 控制溢出次数,表示第N次溢出时,停止训练,过程中检测到溢出API对应ACL数据均dump。参数示例:overflow_nums=3。配置overflow_check时可配置,默认不配置,即检测到1次溢出,训练停止,配置为-1时,表示持续检测溢出直到训练结束。 | 否 | -| dump_mode | 控制针对溢出API的dump模式,可取值"acl"或"api"。配置acl时,表示dump ACL级别的溢出数据,此时set_dump_path参数不生效,dump数据目录由dump_config的.json文件配置。参数示例:dump_mode="acl"。默认不配置,即dump API级别的溢出数据。 | 否 | -| dump_config | acl dump的配置文件。dump_mode="acl"时,该参数必选;dump_mode="api"时,该参数不选。参数示例:dump_config='./dump.json'。 | 否 | - -**函数示例** - -- 示例1:注册工具钩子函数 - - ```python - register_hook(model, acc_cmp_dump) - ``` - -- 示例2:dump指定API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - ``` - - 需要配置set_dump_switch的mode="acl"以及scope指定为前向或反向API,请参见“**set_dump_switch”**的示例。 - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置dump数据目录。 - -- 示例3:溢出检测dump - - ```python - register_hook(model, overflow_check, overflow_nums=3) - ``` - - dump执行时会在set_dump_path的fpath参数指定的目录下生成ptdbg_dump_{version}目录,保存溢出数据。 - - 多卡场景时,需要检测到至少有一张卡溢出次数达到overflow_nums时,训练结束。 - - 仅支持NPU环境。 - -- 示例4:dump指定API的ACL级别溢出数据 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - ``` - - 该场景会在原有数据基础上,额外在dump.json文件配置的dump_path目录下生成一份ACL算子数据,该数据可通过“**ptdbg_ascend.parse**”工具进行解析。 - - 仅支持NPU环境。 - -### set_dump_switch - -**功能说明** - -设置dump范围。建议在register_hook函数之后的脚本内任意位置插入,但进行精度问题排查建议参照“场景化示例 > 单卡场景精度比对”章节的顺序,先从第一个迭代开始的位置调用并dump整网数据。 - -dump操作必选。 - -**函数原型** - -```python -def set_dump_switch(switch, mode="all", scope=[], api_list=[], filter_switch="OFF", dump_mode=["all"], summary_only=False): -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| --------------- | ------------------------------------------------------------ | -------- | -| switch | dump开关。可取值"ON"或"OFF"。须在选定dump开始的位置配置set_dump_switch("ON");dump结束的位置设置set_dump_switch("OFF")。 | 是 | -| mode | dump模式。可取值"all"、"list"、"range"、"stack"、"acl"、"api_list"、"api_stack",各参数含义请参见本节的“**函数示例**”。参数示例:mode="list"。默认为all。该参数配置值将作为dump数据文件名的前缀,详情请参见“**dump数据存盘说明**”。 | 否 | -| scope或api_list | dump范围。根据model配置的模式选择dump的API范围。参数示例:scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward"]、api_list=["relu"]。默认为空。 | 否 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | -| dump_mode | dump数据过滤。可取值"all"、"forward"、"backward"、"input"和"output",表示仅保存dump的数据中文件名包含"forward"、"backward"、"input"和"output"的前向、反向、输入或输出的.npy文件。参数示例dump_mode=["backward"]或dump_mode=["forward", "backward"]。默认为all,即保存所有dump的数据。除了all参数只能单独配置外,其他参数可以自由组合。 | 否 | -| summary_only | dump npy文件过滤,可取值True或False,配置为True后仅dump保存API统计信息的pkl文件,参数示例:summary_only=False,默认为False。 | 否 | - -**推荐配置** - -```python -set_dump_switch("ON", mode="api_stack", filter_switch="OFF") -``` - -开启dump数据和堆栈模式,同时为保证数据完整性开启dump bool和整型的tensor以及浮点、bool和整型的标量。 - -**函数示例** - -set_dump_switch可配置多种dump模式,示例如下: - -说明:以下均以dump部分API数据为例,API名可以从首次dump整网数据的结果csv文件中的NPU Name或Bench Name列获取。 - -- 示例1:dump指定API列表 - - ```python - set_dump_switch("ON", mode="list", scope=["Tensor_permute_1_forward", "Tensor_transpose_2_forward", "Torch_relu_3_backward"]) - ``` - -- 示例2:dump指定范围 - - ```python - set_dump_switch("ON", mode="range", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例3:STACK模式,只dump堆栈信息 - - ```python - set_dump_switch("ON", mode="stack", scope=["Tensor_abs_1_forward", "Tensor_transpose_3_forward"]) - ``` - -- 示例4:dump指定前向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Tensor_permute_1_forward"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件。 - -- 示例4:dump指定反向API的ACL级别数据 - - ```python - register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') - set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) - set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) - ``` - - 需要配置register_hook的dump_mode='acl'和dump_config配置文件,并通过set_backward_input设置反向API输入的.npy文件。 - -- 示例5:dump指定某一类API的API级别输入输出数据 - - ```python - set_dump_switch("ON", mode="api_list", api_list=["relu"]) - ``` - - mode="api_list"时不配置scope。 - -- 示例6:dump全部API级别输入输出数据以及相应堆栈信息 - - ```python - set_dump_switch("ON", mode="api_stack") - ``` - - mode="api_stack"时不配置scope。 - -- 示例7: dump全部API级别输入输出数据并包含bool和整型的tensor以及浮点、bool和整型的标量,配置为OFF,会dump bool和整型数据 - - ```python - set_dump_switch("ON", filter_switch="OFF") - ``` - - 配置filter_switch="OFF"同时也可以配置mode、scope和api_list,除dump ACL级别数据。 - -- 示例8:仅保存dump的数据文件名包含“backward”的反向.npy文件 - - ```python - set_dump_switch("ON", dump_mode=["backward"]) - ``` - -- 示例9:仅dump pkl文件 - - ```python - set_dump_switch("ON", summary_only=True) - ``` - -以上示例均需要在结束dump的位置插入set_dump_switch("OFF")。 - -set_dump_switch配置mode为all或api_stack时,结束dump后,在dump目录下会自动生成compare_data.py比对脚本模板,示例如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": , - "bench_pkl_path": , - "npu_dump_data_dir": , - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -pkl_path和dump_data_dir字段会自动识别pkl和dump目录的路径,用户需要判断当前dump的环境是NPU、CPU或GPU,并将pkl_path和dump_data_dir字段填入下方dump_path_param函数对应的字段中,例如当前设备为NPU,那么填写方式如下: - -```python -from ptdbg_ascend import compare - -pkl_path = "%s" -dump_data_dir = "%s" - -dump_path_param = { - "npu_pkl_path": pkl_path, - "bench_pkl_path": , - "npu_dump_data_dir": dump_data_dir, - "bench_dump_data_dir": , - "is_print_compare_log": True -} - -compare(dump_path_param, output_path="", stack_mode="%s") -``` - -此时,另一侧数据的路径,需要用户另外识别并填入。 - -### set_overflow_check_switch - -**功能说明** - -置溢出检测范围。默认不配置该函数,全量进行溢出检测。 - -仅支持NPU环境。 - -**函数原型** - -```python -set_overflow_check_switch(switch, filter_switch='OFF') -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------- | ------------------------------------------------------------ | -------- | -| switch, | 检测开关。可取值"ON"或"OFF"。如果只在特定的step溢出检测,则在期望溢出检测的step位置开始前插入set_overflow_check_switch("ON"),在step结束的位置插入set_overflow_check_switch("OFF")。 | 是 | -| filter_switch | dump bool和整型的tensor以及浮点、bool和整型的标量的过滤开关。可取值"ON"或"OFF"。参数示例:filter_switch="ON"。默认不配置,即filter_switch="OFF",表示dump上述数据。 | 否 | - -**函数示例** - -- 示例1:指定范围溢出检测 - - ```python - register_hook(model, overflow_check) - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,dump执行时会在当前目录自动生成ptdbg_dump_{version}目录,保存溢出数据。 - -- 示例2:前向API的ACL级别范围溢出检测 - - ```python - register_hook(model, overflow_check, dump_mode='acl', dump_config='./dump.json') - set_overflow_check_switch("ON") - - ... - - set_overflow_check_switch("OFF") - ``` - - 该场景set_dump_path不生效,由dump_config中的dump.json文件配置溢出数据目录。 - -### set_backward_input - -**功能说明** - -设置反向ACL级别dump时需要的反向输入的.npy文件。 - -**函数原型** - -```python -set_backward_input(backward_input) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| -------------- | ------------------------------------------------------------ | -------- | -| backward_input | 该输入文件为首次运行训练dump得到反向API输入的.npy文件。例如若需要dump Functional_conv2d_1 API的反向过程的输入输出,则需要在dump目录下查找命名包含Functional_conv2d_1、backward和input字段的.npy文件。 | 是 | - -**函数示例** - -```python -register_hook(model, acc_cmp_dump, dump_mode='acl', dump_config='./dump.json') -set_dump_switch("ON", mode="acl", scope=["Functional_conv2d_1_backward"]) -set_backward_input(["./npu_dump/dump_conv2d_v4.0/step0/rank0/dump/Functional_conv2d_1_backward_input.0.npy"]) -``` - -## dump.json配置文件说明 - -**dump.json配置示例** - -```python -{ - "dump": - { - "dump_list":[], - "dump_path":"./dump/output", - "dump_mode":"all", - "dump_op_switch":"on" - } -} -``` - -**dump.json参数说明** - -| 字段名 | 说明 | -| -------------- | ------------------------------------------------------------ | -| dump_list | 待dump数据的API模型。为空,无需配置。 | -| dump_path | dump数据文件存储到运行环境的目录,主要用于指定ACL dump数据路径。支持配置绝对路径或相对路径。dump_path须为已存在目录。 | -| dump_mode | dump数据模式,配置如下:
- output:dump API的输出数据。默认值。
- input:dump API的输入数据。
- all:dump API的输入、输出数据。 | -| dump_op_switch | 单API模型dump数据开关,配置如下: * off:关闭单API模型dump,默认值。 * on:开启单API模型dump。 | - -**dump目录说明** - -配置register_hook的dump_config后,采集的dump数据会在{dump_path}/{time}/{deviceid}/{model_id}目录下生成,例如“/home/HwHiAiUser/output/20200808163566/0/0” - -```bash -├── 20230131172437 -│   └── 1 -│   ├── 0 -│   │   ├── Add.Add.45.0.1675157077183551 -│   │   ├── Cast.trans_Cast_0.31.0.1675157077159449 -│   │   ├── Cast.trans_Cast_5.43.0.1675157077180129 -│   │   ├── MatMul.MatMul.39.0.1675157077172961 -│   │   ├── Mul.Mul.29.0.1675157077155731 -│   │   ├── NPUAllocFloatStatus.NPUAllocFloatStatus.24.0.1675157077145262 -│   │   ├── TransData.trans_TransData_1.33.0.1675157077162791 -│   │   └── TransData.trans_TransData_4.41.0.1675157077176648 -│   ├── 1701737061 -│   │   └── Cast.trans_Cast_2.35.0.1675157077166214 -│   ├── 25 -│   │   └── NPUClearFloatStatus.NPUClearFloatStatus.26.0.1675157077150342 -│   └── 68 -│   └── TransData.trans_TransData_3.37.0.1675157077169473 -``` - -## 模块级精度数据dump - -### 总体说明 - -大模型场景下,通常不是简单的利用自动迁移能力实现GPU到NPU的训练脚本迁移,而是会对NPU网络进行一系列针对性的适配,因此,常常会造成迁移后的NPU模型存在部分子结构不能与GPU原始模型完全对应。模型结构不一致导致API调用类型及数量不一致,若直接按照API粒度进行精度数据dump和比对,则无法完全比对所有的API。 - -本节介绍的功能是对模型中的大粒度模块进行数据dump,使其比对时,对于无法以API粒度比对的模块可以直接以模块粒度进行比对。 - -模块指的是继承自nn.Module类模块,通常情况下这类模块就是一个小模型,可以被视为一个整体,dump数据时以模块为粒度进行dump。 - -### module_dump - -**功能说明** - -开启模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump(module, module_name) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ----------- | ------------------------------------------------------------ | -------- | -| module | 网络中实例化好的nn.Module类模块的model对象。 | 是 | -| module_name | 用户自定义的该model名称。主要用于dump数据文件的命名,便于在比对时识别模块级数据。 | 是 | - -### module_dump_end - -**功能说明** - -结束模块级精度数据dump。 - -模块级精度数据dump时必选。 - -**函数原型** - -```python -module_dump_end() -``` - -### 示例代码 - -```python -# 根据需要import包 -import os -import torch -import torch.nn as nn -import torch_npu -import torch.nn.functional as F -from ptdbg_ascend import * - -torch.npu.set_device("npu:0") -# 定义一个简单的网络 -class ModuleOP(nn.Module): - def __init__(self) -> None: - super().__init__() - self.linear_1 = nn.Linear(in_features=8, out_features=4) - self.linear_2 = nn.Linear(in_features=4, out_features=2) - def forward(self, x): - x1 = self.linear_1(x) - x2 = self.linear_2(x1) - r1 = F.relu(x2) - return r1 - -if __name__ == "__main__": - module = ModuleOP() - - # 注册工具 - pdbg = PrecisionDebugger("./dump_data/npu", hook_name="dump") - pdbg.start() - - x = torch.randn(10, 8) - module_dump(module, "MyModuleOP") # 开启模块级精度数据dump - out = module(x) - module_dump_end() # 结束模块级精度数据dump - loss = out.sum() - loss.backward() - pdbg.stop() -``` - -## dump数据存盘说明 - -dump结果目录结构示例如下: - -```bash -├── dump_path -│ └── ptdbg_dump_{version} -│ ├── step0 -│ | ├── rank0 -│ | │ ├── dump -| | | | ├── Tensor_permute_1_forward.npy -| | | | ├── MyModule_0_forward_input.npy # 开启模块级精度数据dump时存在模块级的dump数据文件 -| | | | ... -| | | | └── Fcuntion_linear_5_backward_output.npy -│ | │ └── dump.pkl -│ | ├── rank1 -| | | ├── dump -| | | | └── ... -| | | └── dump.pkl -│ | ├── ... -│ | | -| | └── rank7 -│ ├── step1 -│ | ├── ... -│ ├── step2 -``` - -dump过程中,npy文件在对应算子或者模块被执行后就会落盘,而pkl文件则需要在正常执行PrecisionDebugger.stop()或set_dump_switch("OFF")后才会被落盘保存,异常的程序终止会保存终止前被执行算子的相关npy文件,但是不会生成pkl文件。 - -其中`ptdbg_dump_{version}`为默认命名,debugger方式dump不支持修改该文件夹名称,使用set_dump_path函数则支持通过dump_tag参数修改文件夹名称;rank为设备上各卡的ID,每张卡上dump的数据会生成对应dump目录。 - -**精度比对dump场景** - -精度比对dump场景的结果如下: - -* dump.pkl文件:包含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表示2范数(平方根)。 - -* dump目录:目录下为npy格式的dump数据。 - - npy文件保存的前缀和PyTorch对应关系如下 - - | 前缀 | Torch模块 | - | ----------- | ------------------- | - | Tensor | torch.Tensor | - | Torch | torch | - | Functional | torch.nn.functional | - | NPU | NPU亲和算子 | - | VF | torch._VF | - | Aten | torch.ops.aten | - | Distributed | torch.distributed | - -当configure_hook或set_dump_switch配置mode参数(例如:mode="api_stack" )时,dump结果的文件名会添加api_stack前缀,dump结果如下: - -* api_stack_dump.pkl -* api_stack_dump目录 - -**溢出检测dump场景** - -PrecisionDebugger模块的hook_name参数或register_hook函数设置了overflow_check时,检测API溢出,dump结果的文件名格式为:`{api_type}_{api_name}_{API调用次数}_{前向反向}_{当前溢出次数}`,dump结果示例如下: - -* `Tensor_add_1_forward_1.pkl` -* `Tensor_add_1_forward_1`目录 - -## CPU或GPU与NPU精度数据比对 - -### 总体说明 - -- 本节主要介绍CPU或GPU与NPU精度数据比对的函数以及示例。 - -- 比对函数均通过单独创建精度比对脚本执行,可支持单卡和多卡场景的精度数据比对。 - -- 工具性能:比对数据量较小时(参考值单份文件小于10GB),参考比对速度0.1GB/s;比对数据量较大时,参考比对速度0.3GB/s。 - 推荐环境配置:独占环境,CPU核心数192,固态硬盘(IO速度参考:固态硬盘 > 500MB/s,机械硬盘60 ~ 170MB/s)。 - - 用户环境性能弱于标准约束或非独占使用的比对速度酌情向下浮动。比对速度的计算方式:两份比对文件大小/比对耗时。 - -### 约束 - -- NPU自研API,在CPU或GPU若没有对应的API,该API的dump数据不比对。 - -- NPU与CPU或GPU的计算结果误差可能会随着模型的执行不断累积,最终会出现同一个API因为输入的数据差异较大而无法比对的情况。 - -- CPU或GPU与NPU中两个相同的API会因为调用次数不同导致无法比对或比对到错误的API,不影响整体运行,该API忽略。 - -### 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目录。dump数据目录须指定到step级。参数示例:'./npu_dump/ptdbg_dump_v4.0/step0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| bench_dump_dir | 配置CPU、GPU或NPU环境下的dump目录。参数示例:'./gpu_dump/ptdbg_dump_v4.0/step0'。register_hook方式可通过set_dump_path函数的dump_tag参数修改该目录名称。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。需要预先创建output_path目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_rank{npu_ID}-rank{cpu/gpu/npu_ID}_{timestamp}.csv`。 | 是 | -| **kwargs | 支持compare的所有可选参数。 | 否 | - -**函数示例** - -创建比对脚本,例如compare_distributed.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -compare_distributed('./npu_dump/ptdbg_dump_v4.0/step0', './gpu_dump/ptdbg_dump_v4.0/step0', './output') -``` - -### compare - -**功能说明** - -将CPU或GPU与NPU的dump文件进行比对,仅支持单机单卡。 - -**函数原型** - -```python -compare(input_param, output_path, stack_mode=False, auto_analyze=True, fuzzy_match=False) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------ | ------------------------------------------------------------ | -------- | -| input_param | 配置dump数据文件及目录。配置参数包括:
- "npu_pkl_path":指定NPU dump目录下的.pkl文件。参数示例:"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "bench_pkl_path":指定CPU、GPU或NPU dump目录下的.pkl文件。参数示例:"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl"。必选。
- "npu_dump_data_dir":"指定NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "bench_dump_data_dir":"指定CPU、GPU或NPU dump目录下的dump数据目录。参数示例:"npu_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump"。可选,仅比对pkl文件时不选。
- "is_print_compare_log":配置是否开启日志打屏。可取值True或False。可选。 | 是 | -| output_path | 配置比对结果csv文件存盘目录。参数示例:'./output'。文件名称基于时间戳自动生成,格式为:`compare_result_{timestamp}.csv`。 | 是 | -| stack_mode | 配置stack_mode的开关。仅当dump数据时配置debugger.configure_hook或set_dump_switch的mode="api_stack"时需要开启。参数示例:stack_mode=True,默认为False。 | 否 | -| auto_analyze | 自动精度分析,开启后工具自动针对比对结果进行分析,识别到第一个精度不达标节点(在比对结果文件中的“Accuracy Reached or Not”列显示为No),并给出问题可能产生的原因(打屏展示并生成advisor_{timestamp}.txt文件)。可取值True或False,参数示例:auto_analyze=False,默认为True。 | 否 | -| fuzzy_match | 模糊匹配。开启后,对于网络中同一层级且命名仅调用次数不同的API,可匹配并进行比对。可取值True或False,参数示例:fuzzy_match=True,默认为False。 | 否 | - -**函数示例** - -单机单卡场景下创建比对脚本,例如compare.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"npu_dump_data_dir": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"bench_dump_data_dir": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### pkl文件比对 - -若使用**compare**或**compare_distributed**函数创建的比对脚本中,input_param参数只配置了npu_pkl_path和bench_pkl_path或使用summary_only方式dump时,可以进行pkl文件的比对,此时比对dump.pkl文件中的统计值,开启后的比对结果文件生成Max diff、Min diff和Mean diff,表示NPU dump数据中API的输入或输出与标杆数据输入或输出的最大最小平均值的差。可以通过该值判断API是否存在精度问题:当某个API的输入和输出的Max diff、Min diff、Mean diff和L2norm diff均为0或无限趋于0,那么可以判断该API无精度问题,反之则可能存在精度问题。 - -**比对脚本示例** - -```python -from ptdbg_ascend import compare -dump_result_param={ -"npu_pkl_path": "./npu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"bench_pkl_path": "./gpu_dump/ptdbg_dump_v4.0/step0/rank0/api_stack_dump.pkl", -"is_print_compare_log": True -} -compare(dump_result_param, output_path="./output", stack_mode=True) -``` - -### parse - -**功能说明** - -解析并提取dump信息中的堆栈信息及数据统计信息。 - -**函数原型** - -```python -parse(pkl_file, moudule_name_prefix) -``` - -**参数说明** - -| 参数名 | 说明 | 是否必选 | -| ------------------- | ------------------------------------------------------------ | -------- | -| pkl_file | 指定dump数据文件中的pkl文件名。参数示例:"./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl"。 | 是 | -| moudule_name_prefix | 指定待提取的API接口前缀。参数示例:"Torch_norm_1_forward"。 | 是 | - -**函数示例** - -创建堆栈信息及数据统计信息提取脚本,例如parse.py,拷贝如下代码,具体参数请根据实际环境修改。 - -```python -from ptdbg_ascend import * -parse("./npu_dump/ptdbg_dump_v4.0/step0/rank0/dump.pkl", "Torch_batch_normal_1_forward") -``` - -### 计算精度评价指标 - -PyTorch精度比对是以CPU或GPU的计算结果为标杆,计算Cosine(余弦相似度)、MaxAbsErr(最大绝对误差)和MaxRelativeErr(最大相对误差),根据这两个结果判断API在运行时是否存在精度问题。 - -计算精度评价指标: - -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中的元素逐个与对应的标杆数据对比,相对误差大于千分之一、千分之五的比例占总元素个数的比例小于千分之一、千分之五。该数据仅作为精度下降趋势的参考,并不参与计算精度是否通过的判定。 - -精度比对结果csv文件中只需要通过Accuracy Reached or Not来判断计算精度是否达标,判断标准如下: - -1. Cosine < 0.99 且 MaxAbsError > 0.001时,精度不达标,标记为“No”。 -2. Cosine < 0.9,精度不达标,标记为“No”。 -3. MaxAbsError > 1,精度不达标,标记为“No”。 -5. 其余情况下记为精度达标,标记为“Yes”。 - -## ptdbg_ascend.parse数据解析功能 - -ptdbg_ascend.parse为命令行交互式界面解析工具,提供更多的数据解析功能并且展示结果。 - -使用场景:本工具主要用于比对前后两次NPU ACL层级dump数据的一致性。 - -### 进入parse交互式界面 - -安装ptdbg_ascend工具后,可以通过使用命令 **python -m ptdbg_ascend.parse** 进入交互式界面,如下所示: - -```bash -python -m ptdbg_ascend.parse -Parse >>> -``` - -可在parse的界面中执行Shell命令,以及如下场景的相关解析命令: - -- 支持指定ACL层级算子数据比对。 -- 支持指定ACL层级算子数据转换及展示。 -- 支持交互式指定pkl文件中API对应dump数据查看。 -- 支持API进行可选层级比对和打印(统计级和像素级)。 - -Ctrl+C可以退出parse交互式界面。不退出parse交互式界面若需要执行非该界面下的内置Shell命令,且命令与parse交互式界面命令冲突时,非该界面命令需要使用run命令,在相关命令前加上run前缀,如下示例: - -```bash -python -m ptdbg_ascend.parse -Parse >>> run vim cli.py -Parse >>> vim cli.py -``` - -以上各场景详细介绍请参见下文章节。 - -### ACL层级算子数据批量转换 - -本功能会将原有待比对dump数据目录下的dump数据按照算子名和时间戳进行梳理并分类,之后再将dump数据转为为npy文件。 - -依赖:CANN包中的msaccucmp工具,需要安装Ascend-CANN-toolkit,详见《[CANN 软件安装指南](https://gitee.com/link?target=https%3A%2F%2Fwww.hiascend.com%2Fdocument%2Fdetail%2Fzh%2Fcanncommercial%2F700%2Fenvdeployment%2Finstg%2Finstg_0001.html)》。 - -输入以下比对命令进行数据转换。 - -```bash -cad -m my_dump_path [-out output_path] [-asc msaccucmp_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| -m | 待转换ACL dump数据目录。 | 是 | -| -out | 结果输出目录,须指定已存在的目录,默认为./parse_data/acl_batch_convert。未指定时保存在默认路径下,比对结束后会打印log提示输出结果存放路径。 | 否 | -| -asc | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py。 | 否 | - -**示例** - -```bash -# 传入待比对数据目录 -Parse >>> cad -m /home/xxx/my_dump_path/20000124003856/0 -# 转换结果打印 -...... -╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -# 转换前的dump文件 -│ SrcFile: /home/xxx/my_dump_path/20000124003856/0/272/TransData.trans_TransData_22.112.21.948645536672764 │ -# 转换后的npy文件 -│ - TransData.trans_TransData_22.112.21.948645536672764.output.0.npy │ -│ - TransData.trans_TransData_22.112.21.948645536672764.input.0.npy │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -...... -[INFO] The comparison result have been written to "./parse_data/acl_batch_convert". -``` - -输出结果: - -原dump数据目录: - -```bash -├── /home/xxx/my_dump_path/20000124003856/0/ -│ ├── 272 -│ │ ├── {op_type}.{op_name}.{task_id}.{stream_id}.{timestamp} -│ │ ... -│ ├── 512 -│ ... -``` - -转换后: - -```bash -├── ./parse_data/acl_batch_convert/{timestamp} -│ ├── {op_name1} -│ │ ├── {timestamp1} -│ │ | ├── {op_type}.{op_name}.{task_id}.{stream_id}.{timestamp}.{input/output}.{参数序号}.npy -│ │ | │ ... -│ │ ├── {timestamp2} -│ │ | ... -│ ├── {op_name2} -│ ├── ... -``` - -### ACL层级算子数据比对 - -本功能主要用于比对前后两次NPU ACL层级dump数据的一致性。 - -本功能支持批量比对,若需要进行批量比对,需要先将两份待比对的NPU ACL层级dump数据进行“**ACL层级算子数据批量转换**”,可以使两份数据更好的匹配;若直接进行dump数据的比对,建议只比对单个dump数据文件。 - -输入以下比对命令进行数据比对。 - -```bash -vc -m my_dump_path -g golden_dump_path [-out output_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ------------------------------------------------------------ | -------- | -| -m | 待比对ACL dump数据目录。 | 是 | -| -g | dump数据目录。 | 是 | -| -out | 结果输出目录,须指定已存在的目录,默认为./parse_data/acl_batch_comapre。未指定时保存在默认路径下,比对结束后会打印log提示输出结果存放路径。 | 否 | - -输出结果:batch_compare_{timestamp}.csv文件。 - -**示例** - -```bash -# 传入待比对数据目录以及标杆数据目录 -Parse >>> vc -m ./my_dump_path -g ./golden_data_path -[INFO]Compare result is saved in : parse_data/acl_batch_comapre/batch_compare_1707271118.csv -``` - -### ACL算子数据的npy转换 - -依赖:CANN包中的msaccucmp工具,需要安装Ascend-CANN-toolkit,详见《[CANN 软件安装指南](https://gitee.com/link?target=https%3A%2F%2Fwww.hiascend.com%2Fdocument%2Fdetail%2Fzh%2Fcanncommercial%2F700%2Fenvdeployment%2Finstg%2Finstg_0001.html)》。 - -输入以下转换命令进行数据转换, 将ACL级别dump数据转为npy文件。 - -```bash -dc -n file_name/file_path [-f format] [-out output_path] -``` - -| 参数名称 | 说明 | 是否必选 | -| --------- | ------------------------------------------------------------ | -------- | -| -n | 需转换的dump数据文件或dump数据文件目录。 | 是 | -| -f | 开启format转换,指定该参数时需要配置format格式。当前内置的Format转换支持如下类型:
FRACTAL_NZ转换NCHW
FRACTAL_NZ转换成NHWC
FRACTAL_NZ转换ND
HWCN转换FRACTAL_Z
HWCN转换成NCHW
HWCN转换成NHWC
NC1HWC0转换成HWCN
NC1HWC0转换成NCHW
NC1HWC0转换成NHWC
NCHW转换成FRACTAL_Z
NCHW转换成NHWC
NHWC转换成FRACTAL_Z
NHWC转换成HWCN
NHWC转换成NCHW
NDC1HWC0转换成NCDHW | 否 | -| -out | 结果输出目录。 | 否 | -| -cmp_path | 指定msaccucmp路径,默认路径为:/usr/local/Ascend/ascend-toolkit/latest/tools/operator_cmp/compare/msaccucmp.py | 否 | - -[^]: 若传入单个dump文件,则转换单个文件,若传入dump文件目录则转换目录下所有dump文件。 - -- 输出结果:npy文件。 -- 若指定-out参数需要用户传入输出路径,并且路径需要已存在。 -- 若未指定输出目录, 则比对结束后将结果保存在默认目录 “./parse_data/convert_result”中,比对结束后会打印log提示输出结果存放路径及转换结果。 - -- 输入以下命令,展示npy数据统计信息。 - - ```bash - pt -n file_path - ``` - - | 参数名称 | 说明 | 是否必选 | - | -------- | ------------- | -------- | - | -n | npy文件路径。 | 是 | - - 打印统计信息:shape, dtype, max, min和mean。默认在npy文件路径下将该数据保存为txt文件。 - -**示例1** - -```bash -# 传入需转换的dump文件目录 -Parse >>> dc -n ./dump_data/ -...... -# 转换结果 -╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ SrcFile: ./dump_data/ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.0.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.1.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.1.npy │ -│ - Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.input.0.npy │ -│ - Add.fp32_vars_add_2fp32_vars_Relu_9.31.5.1636595794731103.output.0.npy │ -│ - Add.fp32_vars_add_3fp32_vars_Relu_12.40.5.1636595794846124.output.0.npy │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -**示例2** - -```bash -# 查看某个dump数据块的数据信息 -# 默认会将数据中的tensor保存成 txt -Parse >>> pt -n ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.output.0.npy -...... -# 打印统计信息 -[Shape: (1, 16, 56, 56, 16)] [Dtype: float16] [Max: 452.0] [Min: -408.5] [Mean: -3.809] -Path: ./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy -TextFile:./parse_data/dump_convert/Add.fp32_vars_add_1fp32_vars_Relu_6.24.5.1636595794631347.input.0.npy.txt -``` - -### pkl文件中指定API的dump数据信息查看 - -输入以下命令,解析并输出pkl文件中指定api的统计信息。 - -```bash -pk -f pkl_path -n api_name -``` - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------- | -------- | -| -f | 指定pkl文件路径。 | 是 | -| -n | 指定API名称。 | 是 | - -- 输出结果:打印统计信息(shape, dtype, max和min mean)。 -- 若pkl文件中存在相应的堆栈信息,则会打印堆栈信息。 - -**示例** - -```bash -# 传入pkl文件及api名称 -Parse >>> pk -f ./torch_dump/ptdbg_v3.2/rank0/api_stack_dump.pkl -n Functional_conv2d_0_forward -...... -# 打印统计信息及堆栈(pkl文件不包含堆栈则不会打印堆栈) - -Statistic Info: - [Functional_conv2d_0_forward_input.0][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 1.576936960220337][min: -0.9757485389709473][mean: 0.4961632490158081] - [Functional_conv2d_0_forward_input.1][dtype: torch.float32][shape: [2, 1, 2, 2]][max: 0.20064473152160645][min: -0.47102075815200806][mean: -0.20796933770179749] - [Functional_conv2d_0_forward_input.2][dtype: torch.float32][shape: [2]][max: 0.17380613088607788][min: -0.16853803396224976][mean: 0.0026340484619140625] - [Functional_conv2d_0_forward_output][dtype: torch.float32][shape: [2, 2, 1, 1]][max: 0.02364911139011383][min: -1.762906551361084][mean: -0.6710853576660156] -``` - -### API可选层级比对 - -输入以下命令, 进行统计级和像素级比对。 - -```bash -cn -m my_data*.npy -g gloden*.npy [-p num] [-al atol] [-rl rtol] -``` - -- 统计级比对:对tensor整体进行余弦值及相对误差的计算。 -- 像素级比对:对输入的两个npy文件进行逐元素比对。若两个tensor对应元素的相对误差或绝对误差大于**误差阈值**(-al和-rl配置)则被标记为错误数据。 - -| 参数名称 | 说明 | 是否必选 | -| -------- | ----------------------------------------------- | -------- | -| -m | 待比对数据。 | 是 | -| -g | 标杆数据。 | 是 | -| -p | 设置比对结束后打印错误元素的个数,默认值20。 | 否 | -| -al | 判定数据存在精度问题的绝对误差阈值,默认0.001。 | 否 | -| -rl | 判定数据存在精度问题的相对误差阈值,默认0.001。 | 否 | -| -s | 将npy文件保存成txt文件,用于查看,默认开启。 | 否 | - -输出结果: - -- 统计级比对结果。 -- 两个文件的统计信息(shape, dtype, max, min和mean)。 -- 错误数据打印表格。 - -**示例** - -```bash -# 对比两个tensor的数据 -Parse >>> cn -m Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy -g InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy -p 10 -s -al 0.002 -rl 0.005 - Error Item Table Top Item Table -┏━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓ ┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ Index ┃ Left ┃ Right ┃ Diff ┃ ┃ Index ┃ Left ┃ Right ┃ Diff ┃ -┡━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩ ┡━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ -│ 155 │ 0.024600908 │ 0.022271132 │ 0.002329776 │ │ 0 │ -0.9206961 │ -0.9222216 │ 0.0015255213 │ -│ 247 │ 0.015752593 │ 0.017937578 │ 0.0021849852 │ │ 1 │ -0.6416973 │ -0.64051837 │ 0.0011789203 │ -│ 282 │ -0.0101207765 │ -0.007852031 │ 0.0022687456 │ │ 2 │ -0.35383835 │ -0.35433492 │ 0.0004965663 │ -│ 292 │ 0.019581757 │ 0.02240482 │ 0.0028230622 │ │ 3 │ -0.18851271 │ -0.18883198 │ 0.00031927228 │ -│ 640 │ -0.06593232 │ -0.06874806 │ 0.0028157383 │ │ 4 │ -0.43508735 │ -0.43534422 │ 0.00025686622 │ -│ 1420 │ 0.09293677 │ 0.09586689 │ 0.0029301196 │ │ 5 │ 1.4447614 │ 1.4466647 │ 0.0019032955 │ -│ 1462 │ -0.085207745 │ -0.088047795 │ 0.0028400496 │ │ 6 │ -0.3455438 │ -0.3444429 │ 0.0011008978 │ -│ 1891 │ -0.03433288 │ -0.036525503 │ 0.002192624 │ │ 7 │ -0.6560242 │ -0.6564579 │ 0.0004336834 │ -│ 2033 │ 0.06828873 │ 0.07139922 │ 0.0031104907 │ │ 8 │ -2.6964858 │ -2.6975214 │ 0.0010356903 │ -│ 2246 │ -0.06376442 │ -0.06121233 │ 0.002552092 │ │ 9 │ -0.73746175 │ -0.73650354 │ 0.00095820427 │ -└───────┴───────────────┴──────────────┴──────────────┘ └───────┴─────────────┴─────────────┴───────────────┘ -╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Left: | -│ |- NpyFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy | -│ |- TxtFile: ./dump/temp/decode/Add.InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.323.1619494134703053.output.0.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.846897] [Min: -8.368301] [Mean: -0.72565556] | -│ DstFile: │ -│ |- NpyFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy | -│ |- TxtFile: ./dump/cpu/InceptionV3_InceptionV3_Mixed_7a_Branch_0_add_3.0.1619492699305998.npy.txt | -│ |- NpySpec: [Shape: (32, 8, 8, 320)] [Dtype: float32] [Max: 5.8425903] [Min: -8.374472] [Mean: -0.7256237] │ -│ NumCnt: 655360 │ -│ AllClose: False │ -│ CosSim: 0.99999493 │ -│ ErrorPer: 0.023504638671875 (rl= 0.005, al= 0.002) │ -╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -``` - -## FAQ - -[FAQ](https://gitee.com/ascend/att/blob/master/debug/accuracy_tools/ptdbg_ascend/doc/FAQ.md) diff --git "a/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T4.md" "b/debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.md" similarity index 100% rename from "debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.T4.md" rename to "debug/accuracy_tools/ptdbg_ascend/doc/ptdbg_ascend\347\262\276\345\272\246\345\267\245\345\205\267\345\212\237\350\203\275\350\257\264\346\230\216_v5.0.md" diff --git a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/advisor/advisor.py b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/advisor/advisor.py index bd91f33a3ed70ee35cc54fc618d714fc562fd9dd..6f34129aba0ab15e2817ba4edf8352e9b9b73c2f 100644 --- a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/advisor/advisor.py +++ b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/advisor/advisor.py @@ -20,7 +20,7 @@ import pandas as pd from .advisor_result import AdvisorResult from .advisor_const import AdvisorConst -from ..common.utils import CompareException, CompareConst +from ..common.utils import CompareException, CompareConst, Const from ..common.utils import print_info_log, print_warn_log, print_error_log from ..common.file_check_util import FileChecker, FileCheckConst @@ -42,9 +42,14 @@ class Advisor: % (self.input_file, str(os_err))) raise CompareException(CompareException.PARSE_FILE_ERROR) from os_err data_columns = df.columns.values - if not {CompareConst.ACCURACY, CompareConst.NPU_NAME}.issubset(data_columns): - print_error_log('Compare result file does not contain %s, %s columns.' % (CompareConst.ACCURACY, - CompareConst.NPU_NAME)) + if {CompareConst.ACCURACY, CompareConst.NPU_NAME}.issubset(data_columns): + self.file_type = Const.ALL + elif {CompareConst.RESULT, CompareConst.NPU_MD5}.issubset(data_columns): + self.file_type = Const.MD5 + elif {CompareConst.MAX_DIFF, CompareConst.RESULT}.issubset(data_columns): + self.file_type = Const.SUMMARY + else: + print_error_log('Compare result file does not meet the required conditions.') raise CompareException(CompareException.INVALID_FILE_ERROR) df.reset_index(inplace=True) # The value of index is consistent with the line number of csv, csv file first line is 2 @@ -87,15 +92,18 @@ class Advisor: message = AdvisorConst.BATCH_NORM_SUGGEST return message - @staticmethod - def analyze_unmatched(analyze_data): - accuracy_unmatched = analyze_data[analyze_data[CompareConst.ACCURACY] == CompareConst.ACCURACY_CHECK_UNMATCH] + def analyze_unmatched(self, analyze_data): + if self.file_type == Const.ALL: + accuracy_unmatched = analyze_data[analyze_data[CompareConst.ACCURACY] == CompareConst.ACCURACY_CHECK_UNMATCH] + else: + accuracy_unmatched = analyze_data[(analyze_data[CompareConst.NPU_SHAPE] == CompareConst.NAN) | + (analyze_data[CompareConst.BENCH_SHAPE] == CompareConst.NAN)] num_unmatch = len(accuracy_unmatched) if num_unmatch != 0: for i in range(len(accuracy_unmatched)): - item = analyze_data.iloc[i] + item = accuracy_unmatched.iloc[i] print_warn_log("The tensor name matches but the shape or dtype does not match: {}" - .format(item[CompareConst.NPU_NAME])) + .format(item[CompareConst.NPU_NAME])) def gen_advisor_result(self, pd_data): first_failing_data = pd_data.iloc[0] @@ -111,7 +119,12 @@ class Advisor: analyze_data = self._parse_input_file() print_info_log("Start analyzing the comparison result: %s" % self.input_file) self.analyze_unmatched(analyze_data) - failing_data = analyze_data[analyze_data[CompareConst.ACCURACY] == CompareConst.ACCURACY_CHECK_NO] + if self.file_type == Const.ALL: + failing_data = analyze_data[analyze_data[CompareConst.ACCURACY] == CompareConst.ACCURACY_CHECK_NO] + elif self.file_type == Const.MD5: + failing_data = analyze_data[analyze_data[CompareConst.RESULT] == CompareConst.DIFF] + elif self.file_type == Const.SUMMARY: + failing_data = analyze_data[analyze_data[CompareConst.RESULT] == CompareConst.WARNING] if failing_data.empty: print_info_log("All data from api input/output accuracy reached") result = AdvisorResult(AdvisorConst.NO_ERROR_API, AdvisorConst.NO_ERROR_API, AdvisorConst.NO_ERR_SUGGEST) diff --git a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/common/utils.py b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/common/utils.py index 3c2e5f02d9b3cb4f93e7885c75a4f4c7078aa76c..a264f89c77b06254bc99a776afa0efbf8f146f7c 100644 --- a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/common/utils.py +++ b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/common/utils.py @@ -41,7 +41,7 @@ except ImportError: else: is_gpu = False -torch_without_guard_version_list = ['2.1'] +torch_without_guard_version_list = ['2.1', '2.2'] for version in torch_without_guard_version_list: if torch.__version__.startswith(version): torch_without_guard_version = True @@ -115,8 +115,10 @@ class Const: MAX_SEED_VALUE = 2**32 - 1 - INPLACE_LIST = ["broadcast", "all_reduce", "reduce", "all_gather", "gather", "scatter", "reduce_scatter", - "_reduce_scatter_base", "_all_gather_base"] + INPLACE_LIST = [ + "broadcast", "all_reduce", "reduce", "all_gather", "gather", "scatter", "reduce_scatter", + "_reduce_scatter_base", "_all_gather_base", "send", "recv", "irecv", "isend" + ] class CompareConst: @@ -162,7 +164,7 @@ class CompareConst: SUMMARY_COMPARE_RESULT_HEADER = [ NPU_NAME, BENCH_NAME, NPU_DTYPE, BENCH_DTYPE, NPU_SHAPE, BENCH_SHAPE, MAX_DIFF, MIN_DIFF, MEAN_DIFF, NORM_DIFF, - NPU_MAX, NPU_MIN, NPU_MEAN, NPU_NORM, BENCH_MAX, BENCH_MIN, BENCH_MEAN, BENCH_NORM, ACCURACY, ERROR_MESSAGE + NPU_MAX, NPU_MIN, NPU_MEAN, NPU_NORM, BENCH_MAX, BENCH_MIN, BENCH_MEAN, BENCH_NORM, RESULT, ERROR_MESSAGE ] MD5_COMPARE_RESULT_HEADER = [ @@ -173,6 +175,9 @@ class CompareConst: NAN = 'Nan' SHAPE_UNMATCH = 'shape unmatched' DTYPE_UNMATCH = 'dtype unmatched' + PASS = 'Pass' + WARNING = 'Warning' + DIFF = 'Different' # accuracy standards COS_THRESHOLD = 0.99 @@ -198,6 +203,8 @@ class VersionCheck: V1_11 = "1.11" V2_0 = "2.0" V2_1 = "2.1" + V2_2 = "2.2" + @staticmethod def check_torch_version(version): diff --git a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/compare/acc_compare.py b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/compare/acc_compare.py index 0885ebb0ccc1b6bc280ca8f8f3e4ebbd6de11610..be1e0dae76e27bb63dab6016101cd84584cafd62 100644 --- a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/compare/acc_compare.py +++ b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/compare/acc_compare.py @@ -316,7 +316,7 @@ def get_accuracy(result, n_dict, b_dict, summary_compare=False, md5_compare=Fals err_msg = "" if md5_compare: result_item = [n_name, b_name, n_struct[0], b_struct[0], n_struct[1], b_struct[1], - n_struct[2], b_struct[2], n_struct[2] == b_struct[2]] + n_struct[2], b_struct[2], CompareConst.PASS if n_struct[2] == b_struct[2] else CompareConst.DIFF] if has_stack and index == 0 and key == "input_struct": result_item.extend(npu_stack_info) result.append(result_item) @@ -336,16 +336,21 @@ def get_accuracy(result, n_dict, b_dict, summary_compare=False, md5_compare=Fals if summary_compare: start_idx = CompareConst.SUMMARY_COMPARE_RESULT_HEADER.index(CompareConst.MAX_DIFF) - for i, val in enumerate(zip(npu_summery_data, bench_summery_data)): - if all(isinstance(value, (float, int)) for value in val): - result_item[start_idx + i] = val[0] - val[1] + warning_flag = False + for i, (npu_val, bench_val) in enumerate(zip(npu_summery_data, bench_summery_data)): + if isinstance(npu_val, (float, int)) and isinstance(bench_val, (float, int)): + diff = npu_val - bench_val + result_item[start_idx + i] = diff + magnitude_diff = abs(diff) / (max(abs(npu_val), abs(bench_val)) + 1e-10) + if magnitude_diff > 0.5: + warning_flag = True else: result_item[start_idx + i] = CompareConst.NAN - replace_res = map(lambda x: f'{str(x)}\t' if str(x) in ('inf', '-inf', 'nan') else x, - result_item[start_idx:]) - result_item[start_idx:] = list(replace_res) + accuracy_check = CompareConst.WARNING if warning_flag else "" + err_msg += "Need double check api accuracy." if warning_flag else "" + result_item[start_idx:] = [f'{str(x)}\t' if str(x) in ('inf', '-inf', 'nan') else x for x in result_item[start_idx:]] - result_item.append(CompareConst.ACCURACY_CHECK_YES) + result_item.append(accuracy_check if summary_compare else CompareConst.ACCURACY_CHECK_YES) result_item.append(err_msg) if has_stack and index == 0 and key == "input_struct": result_item.extend(npu_stack_info) @@ -622,9 +627,8 @@ def compare_core(input_parma, output_path, stack_mode=False, auto_analyze=True, compare_process([npu_pkl, bench_pkl, fout], stack_mode, fuzzy_match, summary_compare, md5_compare) if summary_compare: print_info_log(f"Summary compare result is {file_path}") - return - if not md5_compare: + if not md5_compare and not summary_compare: _do_multi_process(input_parma, file_path) change_mode(file_path, FileCheckConst.DATA_FILE_AUTHORITY) if auto_analyze: @@ -705,10 +709,6 @@ def compare_process(file_handles, stack_mode, fuzzy_match, summary_compare=False if stack_mode: header.append(CompareConst.STACK) result_df = pd.DataFrame(result, columns=header) - if summary_compare and not md5_compare: - header.remove(CompareConst.ACCURACY) - header.remove(CompareConst.ERROR_MESSAGE) - result_df = result_df[header] result_df.to_csv(output_csv_handle, index=False) diff --git a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/dump/dump.py b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/dump/dump.py index 2e49a9743b6a317ee401b2b8f0b31fb2ea68c07a..112b2102dc21294426d3a0623114408eaf49ee21 100644 --- a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/dump/dump.py +++ b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/dump/dump.py @@ -188,6 +188,39 @@ def dump_data(prefix, data_info): def thread_dump_data(prefix, data_info): DumpUtil.dump_thread_pool.submit(dump_data, prefix, data_info) +def underscore_replace(prefix): + """ + Replacing symbols to unify the format of ptdbg and pretest + """ + replaced_prefix = [] + consecutive_underscore_count = 0 + three_underscore_time = 0 + + for char in prefix: + if char == '_': + consecutive_underscore_count += 1 + if consecutive_underscore_count == 2: + # Two consecutive underscores, leave them unchanged + replaced_prefix.pop() + replaced_prefix.append('__') + elif consecutive_underscore_count == 3: + # Three consecutive underscores + three_underscore_time += 1 + replaced_prefix.pop() + if three_underscore_time % 2 == 1: + # Even index, replace the first underscore + replaced_prefix.append('.__') + else: + # Odd index, replace the third underscore + replaced_prefix.append('__.') + else: + # Single underscore, replace with a period + replaced_prefix.append('.') + else: + # Not an underscore, reset the count + consecutive_underscore_count = 0 + replaced_prefix.append(char) + return ''.join(replaced_prefix) def dump_data_by_rank_count(dump_step, prefix, data_info): print_info_log(f"ptdbg is analyzing rank{rank} api: {prefix}" + " " * 10, end='\r') @@ -213,7 +246,7 @@ def dump_stack_info(name_template): except Exception as e: print_warn_log("Dump stack info failed, error: {}".format(e)) stack_str.append('') - + prefix = name_template.format("stack_info") if DumpUtil.dump_switch_mode in Const.DUMP_MODE: complement_set = set(['forward', 'backward', 'input', 'output']) - set(DumpUtil.dump_mode) @@ -304,7 +337,7 @@ def dump_acc_cmp(name, in_feat, out_feat, dump_step, module): print_warn_log("The file does not exist, error: {}".format(e)) name_prefix = name - name_template = f"{name_prefix}" + "_{}" + name_template = f"{underscore_replace(name_prefix)}" + ".{}" if DumpUtil.is_single_rank is None: DumpUtil.is_single_rank = check_single_rank_folder(dump_dir) if DumpUtil.dump_switch_mode in [Const.ALL, Const.API_LIST]: diff --git a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/hook_module/wrap_distributed.py b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/hook_module/wrap_distributed.py index 48e92faa1b9294c7905be86e391fa54a1f7b153f..8d5140206a4acbc56846ff518d0b4df6586f0482 100644 --- a/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/hook_module/wrap_distributed.py +++ b/debug/accuracy_tools/ptdbg_ascend/src/python/ptdbg_ascend/hook_module/wrap_distributed.py @@ -56,7 +56,12 @@ class DistributedOPTemplate(HOOKModule): @torch_device_guard def forward(self, *args, **kwargs): - return distributed_func.get(self.op_name_)(*args, **kwargs) + if kwargs.get("async_op") or self.op_name_ in ["isend", "irecv"]: + handle = distributed_func.get(self.op_name_)(*args, **kwargs) + handle.wait() + return handle + else: + return distributed_func.get(self.op_name_)(*args, **kwargs) def wrap_distributed_op(op_name, hook): diff --git a/debug/accuracy_tools/ptdbg_ascend/test/ut/test_common_util.py b/debug/accuracy_tools/ptdbg_ascend/test/ut/test_common_util.py index 61cbf29f2135e30389eedea511b1c4431ee2fbe1..5d9394a20ce3be99f7e4a1cda4940fc950b87149 100644 --- a/debug/accuracy_tools/ptdbg_ascend/test/ut/test_common_util.py +++ b/debug/accuracy_tools/ptdbg_ascend/test/ut/test_common_util.py @@ -13,9 +13,12 @@ class TestCommonUtilsMethods(unittest.TestCase): V1_11 = "1.11" V2_0 = "2.0" V2_1 = "2.1" + V2_2 = "2.2" version_check = common.VersionCheck self.assertFalse(version_check.check_torch_version(V0_1)) - self.assertTrue(version_check.check_torch_version(V1_8) or version_check.check_torch_version(V1_11) or version_check.check_torch_version(V2_0) or version_check.check_torch_version(V2_1)) + self.assertTrue(version_check.check_torch_version(V1_8) or version_check.check_torch_version(V1_11) or + version_check.check_torch_version(V2_0) or version_check.check_torch_version(V2_1) + or version_check.check_torch_version(V2_2)) def test_check_mode_valid(self): mode_check = common.check_mode_valid diff --git a/debug/accuracy_tools/ptdbg_ascend/test/ut/test_dump.py b/debug/accuracy_tools/ptdbg_ascend/test/ut/test_dump.py index 9673c292ba20cef94b926986ddac270f748645e9..56440bbe555541cdb356006acef5b65c4a4fc1cb 100644 --- a/debug/accuracy_tools/ptdbg_ascend/test/ut/test_dump.py +++ b/debug/accuracy_tools/ptdbg_ascend/test/ut/test_dump.py @@ -47,3 +47,12 @@ class TestDump(unittest.TestCase): result = get_pkl_file_path() self.assertEqual(result, "") + def test_underscore_replacement(self): + prefix = "Torch_matmul_605_forward_input.0" + replaced_prefix = underscore_replacement(prefix) + self.assertEqual(replaced_prefix, "Torch.matmul.605.forward.input.0") + + prefix = "Tensor___getitem___488_forward_stack_info" + replaced_prefix = underscore_replacement(prefix) + self.assertEqual(replaced_prefix, "Tensor.__getitem__.488.forward.stack_info") +