PyTorchMetrics MAPE值异常偏高问题排查与方案咨询
在人脸识别课程作业中使用PyTorch计算MAPE值时,使用torchmetrics.MeanAbsolutePercentageError类得到的结果毫无意义,但自行编写带epsilon参数的MAPE计算函数结果正常。经排查推测问题源于真实值数组中存在0,但未在torchmetrics官方文档中找到相关说明。现需解答:
- torchmetrics中是否有方法规避该问题?
- MAPE公式中的epsilon是否未设置,若未设置该如何指定其值?
- torchmetrics计算结果异常的原因是什么?
计算函数代码
def calculate_mape_torch(preds, targets): """Calculate MAPE using PyTorch method. Args: preds: array with ground truth values targets: array with predictions from model Returns: MAPE """ if not isinstance(preds, torch.Tensor): preds = torch.tensor(preds) if not isinstance(targets, torch.Tensor): targets = torch.tensor(targets) mape = MeanAbsolutePercentageError() return mape(preds, targets) * 100 def calculate_mape(preds, targets, epsilon=1): """Calculate the Mean Absolute Percentage Error. Args: preds: array with ground truth values targets: array with predictions from model epsilon: value to avoid divide by zero problem Returns: MAPE """ preds_flatten = preds.flatten("F") targets_flatten = targets.flatten("F") return np.sum(np.abs(targets_flatten - preds_flatten) / np.maximum(epsilon, targets_flatten)) / len(preds_flatten) * 100
测试案例1
y_true = np.array([[1, 0, 3], [4, 5, 6]]) y_pred = np.array([[3, 2, 2], [7, 3, 6]])
结果:
>>> calculate_mape(y_pred, y_true) 91.38888888888889 >>> calculate_mape_torch(y_pred, y_true) tensor(28490084.)
测试案例2
y_true = np.array([[1, 2, 3], [4, 5, 6]]) y_pred = np.array([[3, 2, 2], [7, 3, 6]])
结果:
>>> calculate_mape(y_pred, y_true) 58.05555555555556 >>> calculate_mape_torch(y_pred, y_true) tensor(58.0556)
问题原因
当真实值(targets)中存在0时,torchmetrics.MeanAbsolutePercentageError的默认计算逻辑会直接执行abs(pred - target)/target,此时除以0会产生无穷大(inf)值,无穷大参与后续均值计算后,最终得到异常的超大数值(如测试案例1中的28490084.)。而自定义函数通过np.maximum(epsilon, targets_flatten)将分母最小限制为epsilon,避免了除以0的情况,因此结果正常。
解决方法
1. 使用torchmetrics内置参数规避问题
较新版本的torchmetrics为MeanAbsolutePercentageError类提供了ignore_zero参数,设置为True时会自动忽略真实值为0的样本,避免除以0的计算:
from torchmetrics import MeanAbsolutePercentageError def calculate_mape_torch_fixed(preds, targets): if not isinstance(preds, torch.Tensor): preds = torch.tensor(preds) if not isinstance(targets, torch.Tensor): targets = torch.tensor(targets) # 忽略真实值为0的样本 mape = MeanAbsolutePercentageError(ignore_zero=True) return mape(preds, targets) * 100
测试案例1使用该函数会得到合理结果(忽略0样本后计算)。
2. 自定义带epsilon的计算逻辑
如果需要保留所有样本(包括真实值为0的),可以手动实现带epsilon的MAPE计算,类似自定义函数的逻辑:
import torch def calculate_mape_torch_with_epsilon(preds, targets, epsilon=1): if not isinstance(preds, torch.Tensor): preds = torch.tensor(preds) if not isinstance(targets, torch.Tensor): targets = torch.tensor(targets) abs_error = torch.abs(preds - targets) # 将分母限制为不小于epsilon,避免除以0 relative_error = abs_error / torch.maximum(torch.tensor(epsilon, device=targets.device), targets) mape = torch.mean(relative_error) * 100 return mape
该函数可以指定任意epsilon值,解决除以0的问题。
3. 版本注意事项
早期版本的torchmetrics可能没有ignore_zero参数,建议升级到最新稳定版;若无法升级,直接使用自定义带epsilon的计算逻辑即可。
内容的提问来源于stack exchange,提问作者cicciodevoto

