首页 > 解决方案 > Google Colaboratory 上的 Tensorflow 2.x 自定义损失函数

问题描述

问题

我正在尝试为我的 Tensorflow 2 模型编写自定义损失函数。我编写了以下函数来计算当我手动传入输入和输出张量时我正在寻找的损失。

def on_off_balance_loss(y_true: EagerTensor, y_pred: EagerTensor) -> float:
    y_true_array: ndarray = np.asarray(y_true).flatten()
    y_predict_array: ndarray = np.asarray(y_pred).flatten()

    on_delta: float = 0.999

    on_loss: float = 0
    off_loss: float = 0
    on_count: int = 0
    off_count: int = 0

    for i in range(len(y_true_array)):
        loss: float = cell_loss(y_true_array[i], y_predict_array[i])
        if y_true_array[i] > on_delta:
            on_count += 1
            on_loss = on_loss * ((on_count - 1) / on_count) + (loss / on_count)
        else:
            off_count += 1
            off_loss = off_loss * ((off_count - 1) / off_count) + (loss / off_count)

    on_factor: int = 4
    return (on_factor * on_loss + off_loss) / (on_factor + 1)

对于上下文,y_true由 1 和 0 作为浮点数的 2D 矩阵组成,其中 0 更为常见。因此,我的模型通过正确获取大部分 0 来获得良好的损失值,即使 1 的位置是更重要的指标。这种自定义损失更多地强调了 1 的位置。

我改为model.compile(loss="binary_crossentropy")尝试model.compile(loss=on_off_balance_loss)使用新的损失函数。这似乎不起作用,因为损失函数应该接收整批数据。所以,我尝试了这样的事情model.compile(loss=on_off_balance_batch_loss)

def on_off_balance_batch_loss(y_true, y_pred) -> float:
    y_trues: list = tf.unstack(y_true)
    y_preds: list = tf.unstack(y_pred)
    loss: float = 0

    for i in range(0, len(y_trues)):
        loss = loss * (i / (i + 1)) + (on_off_balance_loss(y_trues[i], y_preds[i]) / (i + 1))

    return loss

这行不通。的形状y_true(None, None, None), 的形状y_pred(None, X, Y),其中XY是 1 和 0 的二维数组的维度。

我在谷歌合作实验室工作。但是,在本地,np.asarray()似乎以在 Colaboratory 上引发错误的方式工作。所以,我不确定错误是出在我的损失函数中还是在 Colaboratory 中的某些设置上。我确保我在本地和 Colaboratory 上都使用 Tensorflow 2.3.0。

编辑:

我尝试添加run_eagerly=Truemodel.compile()使用.numpy()而不是np.asarray()in on_off_balance_loss()。这将输入的类型on_off_balance_batch_loss从更改TensorEagerTensor。这会导致错误ValueError: No gradients provided for any variable: ['lstm_3/lstm_cell_3/kernel:0', 'lstm_3/lstm_cell_3/recurrent_kernel:0', 'lstm_3/lstm_cell_3/bias:0', 'dense_2/kernel:0', 'dense_2/bias:0', 'lstm_4/lstm_cell_4/kernel:0', 'lstm_4/lstm_cell_4/recurrent_kernel:0', 'lstm_4/lstm_cell_4/bias:0', 'dense_3/kernel:0', 'dense_3/bias:0', 'lstm_5/lstm_cell_5/kernel:0', 'lstm_5/lstm_cell_5/recurrent_kernel:0', 'lstm_5/lstm_cell_5/bias:0'].。如果我使用也会发生同样的错误

def on_off_balance_batch_loss(y_true: EagerTensor, y_pred: EagerTensor) -> float:
    y_trues = tf.TensorArray(tf.float32, 1, dynamic_size=True, infer_shape=False).unstack(y_true)
    y_preds = tf.TensorArray(tf.float32, 1, dynamic_size=True, infer_shape=False).unstack(y_pred)

    loss: float = 0.0
    i: int = 0

    for tensor in range(y_trues.size()):
        elem_loss: float = on_off_balance_loss(y_trues.read(i), y_preds.read(i))
        loss = loss * (i / (i + 1)) + (elem_loss / (i + 1))
        i += 1

    return loss

并省略run_eagerly=True。甚至在出现错误之前,似乎整个程序的运行速度都比我使用默认损失函数时要慢。

标签: pythonnumpytensorflowgoogle-colaboratorytensorflow2.0

解决方案


好吧,事实证明解决方案比我上面尝试的要简单得多。下面是实现此类功能的样式。我将它与我的原始函数的输出进行了比较on_off_balance_loss,它匹配。

def on_off_equal_loss(y_true: Tensor, y_pred: Tensor) -> Tensor:
    on_delta: float = 0.99

    on_mask: Tensor = tf.greater_equal(y_true, on_delta)
    off_mask: Tensor = tf.less(y_true, on_delta)

    on_loss: Tensor = tf.divide(tf.reduce_sum(tf.abs(tf.subtract(
        y_true[on_mask], y_pred[on_mask]
    ))), tf.cast(tf.math.count_nonzero(on_mask), tf.float32))

    off_loss: Tensor = tf.divide(tf.reduce_sum(tf.abs(tf.subtract(
        y_true[off_mask], y_pred[off_mask]
    ))), tf.cast(tf.math.count_nonzero(off_mask), tf.float32))

    on_factor: float = 4.0
    return tf.divide(tf.add(tf.multiply(on_factor, on_loss), off_loss), on_factor + 1.0)

推荐阅读