Tensorflow/Keras中惩罚相对距离的自定义损失函数选型咨询
解决标准MAPE不对称问题的对称损失函数方案
你精准地抓住了标准MAPE的核心缺陷——它对高估的惩罚远重于低估,完全不符合你想要的「相对距离对称惩罚」需求。下面给你几个在TensorFlow中可直接实现的替代方案,都是针对这个痛点设计的:
1. 对称平均绝对百分比误差(SMAPE)
这是最常用的对称化替代方案,工业界和学术界都广泛采用。它通过调整分母来消除方向偏差,公式为:
SMAPE = (1/n) × Σ[ |y_pred - y_test| / (|y_test| + |y_pred|) ] × 100
拿你的例子验证:
- 当y_test=5,y_pred=2时:|2-5|/(5+2)=3/7≈0.428
- 当y_test=2,y_pred=5时:|5-2|/(2+5)=3/7≈0.428
完全对称,完美匹配你的需求。而且它还解决了标准MAPE在y_test接近0时误差爆炸的问题。
TensorFlow自定义实现:
def smape_loss(y_true, y_pred): numerator = tf.abs(y_pred - y_true) denominator = tf.abs(y_true) + tf.abs(y_pred) # 避免分母为0,添加极小值epsilon denominator = tf.maximum(denominator, 1e-8) # 可选乘以100转换为百分比形式,不乘也不影响训练 return tf.reduce_mean(numerator / denominator) * 100
2. 对数对称损失
如果你更关注预测值与真实值的比例差异,对数对称损失会是很好的选择。它利用对数变换的特性,让「预测值是真实值的k倍」和「真实值是预测值的k倍」的损失完全相等,公式为:
Loss = (1/n) × Σ[ |ln(y_pred/y_test)| ]
还是用你的例子:
- y_test=5,y_pred=2:|ln(2/5)|≈0.916
- y_test=2,y_pred=5:|ln(5/2)|≈0.916
完全对称,而且这种损失对比例偏差的惩罚是线性的(对数尺度),适合需要严格控制相对比例的场景。
TensorFlow实现:
def log_symmetric_loss(y_true, y_pred): # 防止log(0)出现NaN,给真实值和预测值加极小值 y_true = tf.maximum(y_true, 1e-8) y_pred = tf.maximum(y_pred, 1e-8) ratio = y_pred / y_true return tf.reduce_mean(tf.abs(tf.math.log(ratio)))
3. 自定义对称MAPE(基于最大值的分母)
如果你想尽量贴近标准MAPE的形式,也可以把分母换成真实值和预测值绝对值的最大值,公式为:
Modified Symmetric MAPE = (1/n) × Σ[ |y_pred - y_test| / max(|y_test|, |y_pred|) ]
你的例子验证:
- y_test=5,y_pred=2:3/max(5,2)=0.6
- y_test=2,y_pred=5:3/max(2,5)=0.6
结果一致,而且损失值的范围在[0,1]之间,非常直观。
TensorFlow实现:
def modified_symmetric_mape(y_true, y_pred): numerator = tf.abs(y_pred - y_true) denominator = tf.maximum(tf.abs(y_true), tf.abs(y_pred)) denominator = tf.maximum(denominator, 1e-8) return tf.reduce_mean(numerator / denominator)
额外建议
- 如果你的目标变量存在接近0的取值,优先选择SMAPE或对数损失,它们都避免了标准MAPE在y_test≈0时的无穷大误差问题。
- 训练时建议先尝试SMAPE,它的解释性强,是对称百分比损失的首选方案。
- 所有自定义损失里的
1e-8是必须的,用来避免TensorFlow计算时出现除以0的NaN错误,破坏训练过程。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

