首页 > 解决方案 > 用于 One-Hot 编码的 Keras 自定义损失

问题描述

我目前有一个我训练过的 DNN,它可以预测游戏所处状态的 one-hot 编码分类。基本上,假设有三个状态,0, 1, or 2.

现在,我通常会使用categorical_cross_entropy损失函数,但我意识到并不是所有的分类对于我的状态都是不相等的。例如:

我知道我们可以在 Keras 中声明我们的自定义损失函数,但我一直卡在形成它。有人对如何转换该伪代码有建议吗?我不知道如何在向量操作中做到这一点。

附加问题:我认为我基本上是在追求奖励功能。这和损失函数一样吗?谢谢!

def custom_expectancy(y_expected, y_pred):
    
    # Get 0, 1 or 2
    expected_norm = tf.argmax(y_expected);
    predicted_norm = tf.argmax(y_pred);
    
    # Some pseudo code....
    # Now, if predicted == 1
    #     loss += 0
    # elif predicted == expected
    #     loss -= 3
    # elif predicted != expected
    #     loss += 1
    #
    # return loss

咨询的来源:

https://datascience.stackexchange.com/questions/55215/how-do-i-create-a-keras-custom-loss-function-for-a-one-hot-encoded-binary-classi

Keras 中使用 softmax 到 one-hot 的自定义损失

代码更新

import tensorflow as tf
def custom_expectancy(y_expected, y_pred):
    
    # Get 0, 1 or 2
    expected_norm = tf.argmax(y_expected);
    predicted_norm = tf.argmax(y_pred);
    
    results = tf.unstack(expected_norm)
    
    # Some pseudo code....
    # Now, if predicted == 1
    #     loss += 0
    # elif predicted == expected
    #     loss += 3
    # elif predicted != expected
    #     loss -= 1
    
    for idx in range(0, len(expected_norm)):
        predicted = predicted_norm[idx]
        expected = expected_norm[idx]
        
        if predicted == 1: # do nothing
            results[idx] = 0.0
        elif predicted == expected: # reward
            results[idx] = 3.0
        else: # wrong, so we lost
            results[idx] = -1.0
    
    
    return tf.stack(results)

认为这就是我所追求的,但我还没有完全弄清楚如何构建正确的张量(应该是批量大小)以返回。

标签: pythontensorflowmachine-learningkerasdeep-learning

解决方案


构建条件自定义损失的最佳方法是使用tf.keras.backend.switch不涉及循环。

在您的情况下,您应该组合 2 个switch条件表达式以获得所需的结果。

可以通过这种方式重现所需的损失函数:

def custom_expectancy(y_expected, y_pred):
    
    zeros = tf.cast(tf.reduce_sum(y_pred*0, axis=-1), tf.float32) ### important to produce gradient
    y_expected = tf.cast(tf.reshape(y_expected, (-1,)), tf.float32)
    class_pred = tf.argmax(y_pred, axis=-1)
    class_pred = tf.cast(class_pred, tf.float32)
    
    cond1 = (class_pred != y_expected) & (class_pred != 1)
    cond2 = (class_pred == y_expected) & (class_pred != 1)
    
    res1 = tf.keras.backend.switch(cond1, zeros -1, zeros)
    res2 = tf.keras.backend.switch(cond2, zeros +3, zeros)
    
    return res1 + res2

cond1模型错误地预测状态 0 或 2cond2时,以及模型正确地预测状态 0 或 2 时在哪里。标准状态为零,在未激活时cond1返回cond2

您会注意到,它y_expected可以作为整数编码状态的简单张量/数组传递(无需一次性处理它们)。

损失函数的工作原理如下:

true = tf.constant([[1],    [2],    [1],    [0]    ])  ## no need to one-hot
pred = tf.constant([[0,1,0],[0,0,1],[0,0,1],[0,1,0]])

custom_expectancy(true, pred)

返回:

<tf.Tensor: shape=(4,), dtype=float32, numpy=array([ 0.,  3., -1.,  0.], dtype=float32)>

That seems to be consistent with our needs.

To use the loss inside a model:

X = np.random.uniform(0,1, (1000,10))
y = np.random.randint(0,3, (1000)) ## no need to one-hot

model = Sequential([Dense(3, activation='softmax')])
model.compile(optimizer='adam', loss=custom_expectancy)
model.fit(X,y, epochs=3)

Here the running notebook


推荐阅读