仅评估 tf.Tensor 的非零值
Posted
技术标签:
【中文标题】仅评估 tf.Tensor 的非零值【英文标题】:Only evaluate non-zero values of tf.Tensor 【发布时间】:2018-07-04 00:25:46 【问题描述】:我正在训练使用 Keras 训练神经网络,并且我使用自己的度量函数作为损失函数。原因是测试集中的实际值有很多 NaN 值。让我举一个测试集中实际值的例子:
12
NaN
NaN
NaN
8
NaN
NaN
3
在我的数据预处理中,我用零替换了所有的 NaN 值,因此上面的示例在每个 NaN 行上都包含零。
神经网络产生如下输出:
14
12
9
9
8
7
6
3
我只想计算非零值之间的均方根误差。所以对于上面的例子,它应该只计算第 1、5 和 8 行的 RMSE。为此,我创建了以下函数:
from sklearn.metrics import mean_squared_error
from math import sqrt
[...]
def evaluation_metric(y_true, y_pred):
y_true = y_true[np.nonzero(y_true)]
y_pred = y_pred[np.nonzero(y_true)]
error = sqrt(mean_squared_error(y_true, y_pred))
return error
当您手动测试函数时,通过输入测试集中的实际值和使用随机权重初始化的神经网络的输出,它运行良好并产生错误值。我能够使用进化方法来优化权重,并且我能够通过调整网络的权重来优化这个误差度量。
现在,我想使用 Keras 的 model.compile
函数以 evaluation_metric
作为损失函数来训练网络。当我跑步时:
model.compile(loss=evaluation_metric, optimizer='rmsprop', metrics=[evaluation_metric])
我收到以下错误:
TypeError:不允许将
tf.Tensor
用作Pythonbool
。使用if t is not None:
而不是if t:
来测试是否定义了张量,并使用TensorFlow ops(如tf.cond)来执行以张量值为条件的子图。
我认为这与np.nonzero
的使用有关。由于我正在使用 Keras,我可能应该使用 Keras 后端的功能,或者使用 tf.cond 之类的东西来检查 y_true 的非零值。
有人可以帮我解决这个问题吗?
编辑
应用以下修复后,代码可以工作:
def evaluation_metric(y_true, y_pred):
y_true = y_true * (y_true != 0)
y_pred = y_pred * (y_true != 0)
error = root_mean_squared_error(y_true, y_pred)
return error
以及以下用于计算 tf 对象的 RMSE 的函数:
def root_mean_squared_error(y_true, y_pred):
return K.sqrt(K.mean(K.square(y_pred - y_true), axis=-1))
【问题讨论】:
如果我没记错的话,y_true = y_true * (y_true != 0)
如果值为 0,则将值乘以 0,否则乘以 1,或者换句话说,它什么都不做,因此可以删除
另外,在自定义损失函数中使用 y_pred = y_pred * (y_true != 0)
时,我得到了 TypeError: Input 'y' of 'Mul' Op has type bool that does not match type float32 of argument 'x'.
。
我改用y_pred = y_pred * tf.cast((y_true != 0), 'float32')
【参考方案1】:
是的,确实问题在于使用numpy
函数。这是一个快速修复:
def evaluation_metric(y_true, y_pred):
y_true = y_true * (y_true != 0)
y_pred = y_pred * (y_true != 0)
error = sqrt(mean_squared_error(y_true, y_pred))
return error
【讨论】:
当我还通过以下方式调整 RMSE 误差度量时,您的修复工作有效,因此 Keras 可以使用它:我将sqrt(mean_squared_error(y_true, y_pred))
替换为 def root_mean_squared_error(y_true, y_pred): return K.sqrt(K.mean(K.square(y_pred - y_true), axis=-1))
认为 (5,1) 为 pred (1,2) 为真, (0,1) 为掩码。正确的结果是 rmse=1。但是这个解决方案会给你 rmse=sqrt(0.5)!
这实际上是这种损失的一个很好的性质——每个数据点的影响完全相同——数据点中na
s的数量并不影响实际的训练过程。【参考方案2】:
我会自己在 tensorflow 中编写指标,例如:
import tensorflow as tf
import numpy as np
data = np.array([0, 1, 2, 0, 0, 3, 7, 0]).astype(np.float32)
pred = np.random.randn(8).astype(np.float32)
gt = np.random.randn(8).astype(np.float32)
data_op = tf.convert_to_tensor(data)
pred_op = tf.convert_to_tensor(pred)
gt_op = tf.convert_to_tensor(gt)
expected = np.sqrt(((gt[data != 0] - pred[data != 0]) ** 2).mean())
def nonzero_mean(gt_op, pred_op, data_op):
mask_op = 1 - tf.cast(tf.equal(data_op, 0), tf.float32)
actual_op = ((gt_op - pred_op) * mask_op)**2
actual_op = tf.reduce_sum(actual_op) / tf.cast(tf.count_nonzero(mask_op), tf.float32)
actual_op = tf.sqrt(actual_op)
return actual_op
with tf.Session() as sess:
actual = sess.run(nonzero_mean(gt_op, pred_op, data_op))
print actual, expected
y_true != 0
在普通的 Tensorflow 中是不可能的。不确定,如果 keras 在这里有什么魔力。
【讨论】:
确实如此!我通过将sqrt(mean_squared_error(y_true, y_pred))
替换为def root_mean_squared_error(y_true, y_pred): return K.sqrt(K.mean(K.square(y_pred - y_true), axis=-1))
使我的指标起作用了,不过,感谢您的建议!
那就是 Keras 的魔法。但在数学上是错误的。你没有得到你感兴趣的数量的平均值。你得到的东西小于平均值(取决于有多少零)。将你得到的与我在 numpy 中的参考实现进行比较。
您的实现中的 gt_op 是什么?在我的实现中,我有y_true
,它是带有零的实际输出值(在您的实现中是data
?)我有y_pred
,这是Keras 的预测输出(pred_op
或gt_op
?)
以上是关于仅评估 tf.Tensor 的非零值的主要内容,如果未能解决你的问题,请参考以下文章