diff --git a/debug/accuracy_tools/api_accuracy_checker/compare/compare.py b/debug/accuracy_tools/api_accuracy_checker/compare/compare.py index 15bfb1904c810f756600daef1a90ab1d4176ac0d..cc7c2c83b7bbffa896d16841fa229b3f20c352a7 100644 --- a/debug/accuracy_tools/api_accuracy_checker/compare/compare.py +++ b/debug/accuracy_tools/api_accuracy_checker/compare/compare.py @@ -160,9 +160,16 @@ class Comparator: _, api_name, _ = full_api_name.split("*") 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) - bwd_success_status, bwd_compare_alg_results = (CompareConst.PASS, []) if not (bench_grad and npu_grad) else compare_func(api_name, bench_grad[0], npu_grad[0]) if "dropout" in full_api_name else compare_func(api_name, bench_grad, npu_grad) + if not (bench_grad and npu_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]) + 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) - return fwd_success_status == CompareConst.PASS, bwd_success_status == CompareConst.PASS + return fwd_success_status == CompareConst.PASS, bwd_success_status == CompareConst.PASS \ + or bwd_success_status == CompareConst.SPACE def _compare_core_wrapper(self, api_name, bench_output, device_output): detailed_result_total = [] diff --git a/debug/accuracy_tools/api_accuracy_checker/dump/api_info.py b/debug/accuracy_tools/api_accuracy_checker/dump/api_info.py index e14405fe143e0a39030b6cca18a615c48e802682..50ad39166fb03ae210af92217f424cfdbe6e1eb4 100644 --- a/debug/accuracy_tools/api_accuracy_checker/dump/api_info.py +++ b/debug/accuracy_tools/api_accuracy_checker/dump/api_info.py @@ -12,16 +12,39 @@ from ptdbg_ascend.src.python.ptdbg_ascend.common.utils import check_path_before_ def get_tensor_extremum(data, operator): if data.dtype is torch.bool: if data.numel() == 0: - return False + return False, False if operator == 'max': - return True in data + return True in data, True in data elif operator == 'min': - return False not in data - data_clone = data.clone().detach() + return False not in data, False not in data + data_clone = data.float().clone().detach() if operator == 'max': - return torch._C._VariableFunctionsClass.max(data_clone.float()).item() + max_result = torch._C._VariableFunctionsClass.max(data_clone).item() + if np.isinf(max_result) or np.isnan(max_result): + return handle_tensor_extremum_nan_inf(data_clone, operator), max_result + else: + return max_result, max_result + else: + min_result = torch._C._VariableFunctionsClass.min(data_clone).item() + if np.isinf(min_result) or np.isnan(min_result): + return handle_tensor_extremum_nan_inf(data_clone, operator), min_result + else: + return min_result, min_result + + +def handle_tensor_extremum_nan_inf(data_clone, operator): + data_nan = torch._C._VariableFunctionsClass.isnan(data_clone) + if int(torch._C._VariableFunctionsClass.sum(data_nan)) == data_clone.numel(): + return float('nan') + finite_mask = torch._C._VariableFunctionsClass.isfinite(data_clone) + if int(torch._C._VariableFunctionsClass.sum(finite_mask)) > 0: + finite_values = data_clone[finite_mask] + return torch._C._VariableFunctionsClass.max(finite_values).item() if operator == 'max' else \ + torch._C._VariableFunctionsClass.min(finite_values).item() else: - return torch._C._VariableFunctionsClass.min(data_clone.float()).item() + data_no_nan = data_clone[~data_nan] + return torch._C._VariableFunctionsClass.max(data_no_nan).item() if operator == 'max' else \ + torch._C._VariableFunctionsClass.min(data_no_nan).item() def get_type_name(name): @@ -118,8 +141,12 @@ class APIInfo: single_arg.update({'type': 'torch.Tensor'}) single_arg.update({'dtype': str(arg.dtype)}) single_arg.update({'shape': arg.shape}) - single_arg.update({'Max': transfer_types(get_tensor_extremum(arg, 'max'), str(arg.dtype))}) - single_arg.update({'Min': transfer_types(get_tensor_extremum(arg, 'min'), str(arg.dtype))}) + max_handle, max_origin = get_tensor_extremum(arg, 'max') + single_arg.update({'Max': transfer_types(max_handle, str(arg.dtype))}) + single_arg.update({'Max_origin': transfer_types(max_origin, str(arg.dtype))}) + min_handle, min_origin = get_tensor_extremum(arg, 'min') + single_arg.update({'Min': transfer_types(min_handle, str(arg.dtype))}) + single_arg.update({'Min_origin': transfer_types(min_origin, str(arg.dtype))}) single_arg.update({'requires_grad': arg.requires_grad}) else: api_args = self.api_name + '.' + str(self.args_num) diff --git a/debug/accuracy_tools/api_accuracy_checker/run_ut/data_generate.py b/debug/accuracy_tools/api_accuracy_checker/run_ut/data_generate.py index ec3c539f7f86149b9a8c1f26864bbcb5751748bf..9ff8f5ce0175ee8a0d19a5e7ac6ab665fec98d12 100644 --- a/debug/accuracy_tools/api_accuracy_checker/run_ut/data_generate.py +++ b/debug/accuracy_tools/api_accuracy_checker/run_ut/data_generate.py @@ -16,6 +16,7 @@ """ import os +import math import torch import numpy @@ -105,6 +106,9 @@ def gen_random_tensor(info, convert_type): """ check_object_type(info, dict) low, high = info.get('Min'), info.get('Max') + low_origin, high_origin = info.get('Min_origin'), info.get('Max_origin') + low_info = [low, low_origin] + high_info = [high, high_origin] data_dtype = info.get('dtype') shape = tuple(info.get('shape')) if not isinstance(low, (int, float)) or not isinstance(high, (int, float)): @@ -113,11 +117,11 @@ def gen_random_tensor(info, convert_type): if data_dtype == "torch.bool": data = gen_bool_tensor(low, high, shape) else: - data = gen_common_tensor(low, high, shape, data_dtype, convert_type) + data = gen_common_tensor(low_info, high_info, shape, data_dtype, convert_type) return data -def gen_common_tensor(low, high, shape, data_dtype, convert_type): +def gen_common_tensor(low_info, high_info, shape, data_dtype, convert_type): """ Function Description: Based on API basic information, generate int or float tensor @@ -132,13 +136,26 @@ def gen_common_tensor(low, high, shape, data_dtype, convert_type): ori_dtype = Const.CONVERT.get(convert_type)[0] if ori_dtype == data_dtype: data_dtype = Const.CONVERT.get(convert_type)[1] + low, low_origin = low_info[0], low_info[1] + high, high_origin = high_info[0], high_info[1] if data_dtype in FLOAT_TYPE: - if high in [float('inf'), float('-inf')] or low in [float('inf'), float('-inf')]: - error_info = 'Parameter contains inf, skip comparison.' - raise CompareException(CompareException.INVALID_PARAM_ERROR, error_info) - scale = high - low + if math.isnan(high): + tensor = torch._C._VariableFunctionsClass.full(shape, float('nan'), dtype=eval(data_dtype)) + return tensor + low_scale, high_scale = low, high + dtype_finfo = torch.finfo(eval(data_dtype)) + if high == float('inf'): + high_scale = dtype_finfo.max + elif high == float('-inf'): + high_scale = dtype_finfo.min + if low == float('inf'): + low_scale = dtype_finfo.max + elif low == float('-inf'): + low_scale = dtype_finfo.min + + scale = high_scale - low_scale rand01 = torch.rand(shape, dtype=eval(data_dtype)) - tensor = rand01 * scale + low + tensor = rand01 * scale + low_scale elif 'int' in data_dtype or 'long' in data_dtype: low, high = int(low), int(high) tensor = torch.randint(low, high + 1, shape, dtype=eval(data_dtype)) @@ -148,8 +165,21 @@ def gen_common_tensor(low, high, shape, data_dtype, convert_type): if tensor.nelement() == 0: return tensor tmp_tensor = tensor.reshape(-1) - tmp_tensor[0] = low - tmp_tensor[-1] = high + if high_origin and math.isnan(high_origin): + if tmp_tensor.numel() <= 2: + tmp_tensor[0] = float('nan') + tmp_tensor[-1] = high + else: + tmp_tensor[0] = low + tmp_tensor[1] = float('nan') + tmp_tensor[-1] = high + else: + tmp_tensor[0] = low + tmp_tensor[-1] = high + if high_origin in [float('inf'), float('-inf')]: + tmp_tensor[-1] = high_origin + if low_origin in [float('inf'), float('-inf')]: + tmp_tensor[0] = low_origin data = tmp_tensor.reshape(shape) return data diff --git a/debug/accuracy_tools/api_accuracy_checker/test/resources/forward.json b/debug/accuracy_tools/api_accuracy_checker/test/resources/forward.json index 5f54e077bfd0425be200f89e14a8ba131d3a3a8b..f938f352460a87222bdb5346873904cb420996cc 100644 --- a/debug/accuracy_tools/api_accuracy_checker/test/resources/forward.json +++ b/debug/accuracy_tools/api_accuracy_checker/test/resources/forward.json @@ -1,3 +1,3 @@ { - "Functional*silu*0": {"args": [{"type": "torch.Tensor", "dtype": "torch.float32", "shape": [2, 2560, 24, 24], "Max": 5.7421875, "Min": -5.125, "requires_grad": true}], "kwargs" :{"inplace": {"type": "bool", "value": false}}} + "Functional*silu*0": {"args": [{"type": "torch.Tensor", "dtype": "torch.float32", "shape": [2, 2560, 24, 24], "Max": 5.7421875, "Max_origin": 5.7421875, "Min": -5.125, "Min_origin": -5.125, "requires_grad": true}], "kwargs" :{"inplace": {"type": "bool", "value": false}}} } \ No newline at end of file diff --git a/debug/accuracy_tools/api_accuracy_checker/test/ut/dump/test_api_info.py b/debug/accuracy_tools/api_accuracy_checker/test/ut/dump/test_api_info.py index 8951d5523ae2db277bf41e0417ae71456a27af4a..2c03d56e722decc424052367dfe9700ba3df94ce 100644 --- a/debug/accuracy_tools/api_accuracy_checker/test/ut/dump/test_api_info.py +++ b/debug/accuracy_tools/api_accuracy_checker/test/ut/dump/test_api_info.py @@ -2,6 +2,7 @@ import os import shutil import unittest import torch +import numpy as np from api_accuracy_checker.dump.api_info import APIInfo, ForwardAPIInfo, BackwardAPIInfo, transfer_types, \ get_tensor_extremum, get_type_name, is_builtin_class, analyze_device_in_kwargs, analyze_dtype_in_kwargs from api_accuracy_checker.common.config import msCheckerConfig @@ -55,10 +56,52 @@ class TestAPIInfo(unittest.TestCase): def test_get_tensor_extremum(self): data = torch.tensor([1, 2, 3]) - result_max = get_tensor_extremum(data, 'max') - result_min = get_tensor_extremum(data, 'min') + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') self.assertEqual(result_max, 3) self.assertEqual(result_min, 1) + self.assertEqual(result_max_origin, 3) + self.assertEqual(result_min_origin, 1) + + data = torch.tensor([1, float("inf"), 2, 3]) + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') + self.assertEqual(result_max, 3) + self.assertEqual(result_min, 1) + self.assertEqual(result_max_origin, float("inf")) + self.assertEqual(result_min_origin, 1) + + data = torch.tensor([1, float("-inf"), 2, 3]) + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') + self.assertEqual(result_max, 3) + self.assertEqual(result_min, 1) + self.assertEqual(result_max_origin, 3) + self.assertEqual(result_min_origin, float("-inf")) + + data = torch.tensor([1, float("inf"), float("nan"), 3]) + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') + self.assertEqual(result_max, 3) + self.assertEqual(result_min, 1) + self.assertTrue(np.isnan(result_max_origin)) + self.assertTrue(np.isnan(result_min_origin)) + + data = torch.tensor([float("inf"), float("nan")]) + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') + self.assertEqual(result_max, float("inf")) + self.assertEqual(result_min, float("inf")) + self.assertTrue(np.isnan(result_max_origin)) + self.assertTrue(np.isnan(result_min_origin)) + + data = torch.tensor([float("nan"), float("nan")]) + result_max, result_max_origin = get_tensor_extremum(data, 'max') + result_min, result_min_origin = get_tensor_extremum(data, 'min') + self.assertTrue(np.isnan(result_max)) + self.assertTrue(np.isnan(result_min)) + self.assertTrue(np.isnan(result_max_origin)) + self.assertTrue(np.isnan(result_min_origin)) def test_get_type_name(self): name = "" diff --git a/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_data_generate.py b/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_data_generate.py index 3f79ecf17b2583474a8de08e5c4b3862e5924590..b98f84d516404665b5c3284f1e03f14eedddac55 100644 --- a/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_data_generate.py +++ b/debug/accuracy_tools/api_accuracy_checker/test/ut/run_ut/test_data_generate.py @@ -73,9 +73,12 @@ class TestDataGenerateMethods(unittest.TestCase): def test_gen_common_tensor(self): info = api_info_dict.get('args')[0] low, high = info.get('Min'), info.get('Max') + low_origin, high_origin = info.get('Min_origin'), info.get('Max_origin') + low_info = [low, low_origin] + high_info = [high, high_origin] data_dtype = info.get('dtype') shape = tuple(info.get('shape')) - data = gen_common_tensor(low, high, shape, data_dtype, None) + data = gen_common_tensor(low_info, high_info, shape, data_dtype, None) max_diff = abs(data.max() - max_value) min_diff = abs(data.min() - min_value) self.assertEqual(data.dtype, torch.float32)