python - 用于 One-Hot 编码的 Keras 自定义损失
问题描述
我目前有一个我训练过的 DNN,它可以预测游戏所处状态的 one-hot 编码分类。基本上,假设有三个状态,0, 1, or 2.
现在,我通常会使用categorical_cross_entropy
损失函数,但我意识到并不是所有的分类对于我的状态都是不相等的。例如:
- 如果模型预测它应该是状态 1,那么如果分类错误,我的系统不会有任何成本,因为状态 1 基本上什么都不做,所以奖励 0x。
- 如果模型正确预测状态 0 或 2(即预测 = 2 并且正确 = 2),那么该奖励应该是 3 倍。
- 如果模型错误地预测状态 0 或 2(即预测 = 2 并且正确 = 0),那么奖励应该是 -1x。
我知道我们可以在 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
咨询的来源:
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)
我认为这就是我所追求的,但我还没有完全弄清楚如何构建正确的张量(应该是批量大小)以返回。
解决方案
构建条件自定义损失的最佳方法是使用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
推荐阅读
- uuid - 如何在android 11中生成唯一代码
- kindle - 如何在 Amazon Kindle Paperwhite eInk 电子书阅读器上安装新字体?
- spring-data - Spring Data Rest Controller (EndPoint) 的 Spring AOP 切入点
- c# - 替换列表中的字符串时出现问题,这是循环中对象的属性
- flutter - 我无法在灵活小部件中添加列小部件,错误:无法将 Null 类型的值分配给 const 构造函数中 Widget 类型的参数
- python - 将多个 xml 文件转换为 .csv 时出现 NotADirectoryError
- wordpress - 视频与文字不同步
- amazon-web-services - Wix + GoDaddy 域 + 子域的 AWS ELB
- ionic-framework - Snapchat 与离子电容器共享
- node.js - 错误:767:在 '{ 在 macOS 中安装节点时出现意外令牌