Keras:加权二元交叉熵
Keras: weighted binary crossentropy
我尝试用 Keras 实现一个加权二进制交叉熵,但我不确定代码是否正确。训练输出似乎有点混乱。经过几个时期后,我的准确度仅为 ~0.15。我认为那太少了(即使是随机猜测)。
输出中一般有11%的1和89%的0,因此权重为w_zero=0.89和w_one=0.11。
我的代码:
def create_weighted_binary_crossentropy(zero_weight, one_weight):
def weighted_binary_crossentropy(y_true, y_pred):
# Original binary crossentropy (see losses.py):
# K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)
# Calculate the binary crossentropy
b_ce = K.binary_crossentropy(y_true, y_pred)
# Apply the weights
weight_vector = y_true * one_weight + (1. - y_true) * zero_weight
weighted_b_ce = weight_vector * b_ce
# Return the mean error
return K.mean(weighted_b_ce)
return weighted_binary_crossentropy
也许有人看出了什么问题?
谢谢
通常情况下,少数class的权重会更高class。使用 one_weight=0.89, zero_weight=0.11
会更好(顺便说一句,您可以按照评论中的建议使用 class_weight={0: 0.11, 1: 0.89}
)。
在 class 不平衡的情况下,您的模型看到的零比零多得多。它还将学习预测比 1 更多的零,因为这样做可以最大限度地减少训练损失。这也是为什么您看到的精度接近 0.11 的原因。如果对模型预测取平均值,它应该非常接近于零。
使用class权重的目的是改变损失函数,使训练损失不能被"easy solution"最小化(即预测零),这就是为什么它会最好使用更大的权重。
请注意,最佳权重不一定是 0.89 和 0.11。有时您可能不得不尝试诸如取对数或平方根(或任何满足 one_weight > zero_weight
的权重)之类的操作来使其工作。
我认为在 model.fit 中使用 class 权重是不正确的。 {0:0.11, 1:0.89},这里的0是索引,不是0class。
Keras 文档:https://keras.io/models/sequential/
class_weight:可选的字典映射 class 索引(整数)到权重(浮点)值,用于加权损失函数(仅在训练期间)。这对于告诉模型 "pay more attention" 从代表性不足的 class.
中抽取样本很有用。
您可以使用 sklearn module 自动计算每个 class 的权重,如下所示:
# Import
import numpy as np
from sklearn.utils import class_weight
# Example model
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(1, activation='sigmoid'))
# Use binary crossentropy loss
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
# Calculate the weights for each class so that we can balance the data
weights = class_weight.compute_class_weight('balanced',
np.unique(y_train),
y_train)
# Add the class weights to the training
model.fit(x_train, y_train, epochs=10, batch_size=32, class_weight=weights)
请注意 class_weight.compute_class_weight()
的输出是一个像这样的 numpy 数组:[2.57569845 0.68250928]
.
在model.fit
中使用class_weights
略有不同:它实际上更新样本而不是计算加权损失。
我还发现 class_weights
以及 sample_weights
在 TF 2.0.0 中被忽略,当 x
作为 TFDataset 或生成器发送到 model.fit 时.我相信它在 TF 2.1.0+ 中已修复。
这是我的多热编码标签的加权二元交叉熵函数。
import tensorflow as tf
import tensorflow.keras.backend as K
import numpy as np
# weighted loss functions
def weighted_binary_cross_entropy(weights: dict, from_logits: bool = False):
'''
Return a function for calculating weighted binary cross entropy
It should be used for multi-hot encoded labels
# Example
y_true = tf.convert_to_tensor([1, 0, 0, 0, 0, 0], dtype=tf.int64)
y_pred = tf.convert_to_tensor([0.6, 0.1, 0.1, 0.9, 0.1, 0.], dtype=tf.float32)
weights = {
0: 1.,
1: 2.
}
# with weights
loss_fn = get_loss_for_multilabels(weights=weights, from_logits=False)
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.6067193, shape=(), dtype=float32)
# without weights
loss_fn = get_loss_for_multilabels()
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.52158177, shape=(), dtype=float32)
# Another example
y_true = tf.convert_to_tensor([[0., 1.], [0., 0.]], dtype=tf.float32)
y_pred = tf.convert_to_tensor([[0.6, 0.4], [0.4, 0.6]], dtype=tf.float32)
weights = {
0: 1.,
1: 2.
}
# with weights
loss_fn = get_loss_for_multilabels(weights=weights, from_logits=False)
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(1.0439969, shape=(), dtype=float32)
# without weights
loss_fn = get_loss_for_multilabels()
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.81492424, shape=(), dtype=float32)
@param weights A dict setting weights for 0 and 1 label. e.g.
{
0: 1.
1: 8.
}
For this case, we want to emphasise those true (1) label,
because we have many false (0) label. e.g.
[
[0 1 0 0 0 0 0 0 0 1]
[0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 1 0 0 0 0 0]
]
@param from_logits If False, we apply sigmoid to each logit
@return A function to calcualte (weighted) binary cross entropy
'''
assert 0 in weights
assert 1 in weights
def weighted_cross_entropy_fn(y_true, y_pred):
tf_y_true = tf.cast(y_true, dtype=y_pred.dtype)
tf_y_pred = tf.cast(y_pred, dtype=y_pred.dtype)
weights_v = tf.where(tf.equal(tf_y_true, 1), weights[1], weights[0])
ce = K.binary_crossentropy(tf_y_true, tf_y_pred, from_logits=from_logits)
loss = K.mean(tf.multiply(ce, weights_v))
return loss
return weighted_cross_entropy_fn
您可以像这样计算权重并获得像这样的二元交叉熵,它将以编程方式将 one_weight 设为 0.11,将 1 设为 0.89:
one_weight = (1-num_of_ones)/(num_of_ones + num_of_zeros)
zero_weight = (1-num_of_zeros)/(num_of_ones + num_of_zeros)
def weighted_binary_crossentropy(zero_weight, one_weight):
def weighted_binary_crossentropy(y_true, y_pred):
b_ce = K.binary_crossentropy(y_true, y_pred)
# weighted calc
weight_vector = y_true * one_weight + (1 - y_true) * zero_weight
weighted_b_ce = weight_vector * b_ce
return K.mean(weighted_b_ce)
return weighted_binary_crossentropy
如果您需要权重不同于训练损失的加权验证损失,您可以使用 tensorflow.keras.model.fit() 的参数 validation_data,将验证数据集作为Numpy 数组的元组,其中包含您的验证数据、标签和每个样本的权重。
请注意,您必须使用此技术将每个样本映射到其权重(此处为 class)。
在此处关注 link:
https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit
tensorflow documentation
我尝试用 Keras 实现一个加权二进制交叉熵,但我不确定代码是否正确。训练输出似乎有点混乱。经过几个时期后,我的准确度仅为 ~0.15。我认为那太少了(即使是随机猜测)。
输出中一般有11%的1和89%的0,因此权重为w_zero=0.89和w_one=0.11。
我的代码:
def create_weighted_binary_crossentropy(zero_weight, one_weight):
def weighted_binary_crossentropy(y_true, y_pred):
# Original binary crossentropy (see losses.py):
# K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)
# Calculate the binary crossentropy
b_ce = K.binary_crossentropy(y_true, y_pred)
# Apply the weights
weight_vector = y_true * one_weight + (1. - y_true) * zero_weight
weighted_b_ce = weight_vector * b_ce
# Return the mean error
return K.mean(weighted_b_ce)
return weighted_binary_crossentropy
也许有人看出了什么问题?
谢谢
通常情况下,少数class的权重会更高class。使用 one_weight=0.89, zero_weight=0.11
会更好(顺便说一句,您可以按照评论中的建议使用 class_weight={0: 0.11, 1: 0.89}
)。
在 class 不平衡的情况下,您的模型看到的零比零多得多。它还将学习预测比 1 更多的零,因为这样做可以最大限度地减少训练损失。这也是为什么您看到的精度接近 0.11 的原因。如果对模型预测取平均值,它应该非常接近于零。
使用class权重的目的是改变损失函数,使训练损失不能被"easy solution"最小化(即预测零),这就是为什么它会最好使用更大的权重。
请注意,最佳权重不一定是 0.89 和 0.11。有时您可能不得不尝试诸如取对数或平方根(或任何满足 one_weight > zero_weight
的权重)之类的操作来使其工作。
我认为在 model.fit 中使用 class 权重是不正确的。 {0:0.11, 1:0.89},这里的0是索引,不是0class。 Keras 文档:https://keras.io/models/sequential/ class_weight:可选的字典映射 class 索引(整数)到权重(浮点)值,用于加权损失函数(仅在训练期间)。这对于告诉模型 "pay more attention" 从代表性不足的 class.
中抽取样本很有用。您可以使用 sklearn module 自动计算每个 class 的权重,如下所示:
# Import
import numpy as np
from sklearn.utils import class_weight
# Example model
model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(1, activation='sigmoid'))
# Use binary crossentropy loss
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
# Calculate the weights for each class so that we can balance the data
weights = class_weight.compute_class_weight('balanced',
np.unique(y_train),
y_train)
# Add the class weights to the training
model.fit(x_train, y_train, epochs=10, batch_size=32, class_weight=weights)
请注意 class_weight.compute_class_weight()
的输出是一个像这样的 numpy 数组:[2.57569845 0.68250928]
.
在model.fit
中使用class_weights
略有不同:它实际上更新样本而不是计算加权损失。
我还发现 class_weights
以及 sample_weights
在 TF 2.0.0 中被忽略,当 x
作为 TFDataset 或生成器发送到 model.fit 时.我相信它在 TF 2.1.0+ 中已修复。
这是我的多热编码标签的加权二元交叉熵函数。
import tensorflow as tf
import tensorflow.keras.backend as K
import numpy as np
# weighted loss functions
def weighted_binary_cross_entropy(weights: dict, from_logits: bool = False):
'''
Return a function for calculating weighted binary cross entropy
It should be used for multi-hot encoded labels
# Example
y_true = tf.convert_to_tensor([1, 0, 0, 0, 0, 0], dtype=tf.int64)
y_pred = tf.convert_to_tensor([0.6, 0.1, 0.1, 0.9, 0.1, 0.], dtype=tf.float32)
weights = {
0: 1.,
1: 2.
}
# with weights
loss_fn = get_loss_for_multilabels(weights=weights, from_logits=False)
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.6067193, shape=(), dtype=float32)
# without weights
loss_fn = get_loss_for_multilabels()
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.52158177, shape=(), dtype=float32)
# Another example
y_true = tf.convert_to_tensor([[0., 1.], [0., 0.]], dtype=tf.float32)
y_pred = tf.convert_to_tensor([[0.6, 0.4], [0.4, 0.6]], dtype=tf.float32)
weights = {
0: 1.,
1: 2.
}
# with weights
loss_fn = get_loss_for_multilabels(weights=weights, from_logits=False)
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(1.0439969, shape=(), dtype=float32)
# without weights
loss_fn = get_loss_for_multilabels()
loss = loss_fn(y_true, y_pred)
print(loss)
# tf.Tensor(0.81492424, shape=(), dtype=float32)
@param weights A dict setting weights for 0 and 1 label. e.g.
{
0: 1.
1: 8.
}
For this case, we want to emphasise those true (1) label,
because we have many false (0) label. e.g.
[
[0 1 0 0 0 0 0 0 0 1]
[0 0 0 0 1 0 0 0 0 0]
[0 0 0 0 1 0 0 0 0 0]
]
@param from_logits If False, we apply sigmoid to each logit
@return A function to calcualte (weighted) binary cross entropy
'''
assert 0 in weights
assert 1 in weights
def weighted_cross_entropy_fn(y_true, y_pred):
tf_y_true = tf.cast(y_true, dtype=y_pred.dtype)
tf_y_pred = tf.cast(y_pred, dtype=y_pred.dtype)
weights_v = tf.where(tf.equal(tf_y_true, 1), weights[1], weights[0])
ce = K.binary_crossentropy(tf_y_true, tf_y_pred, from_logits=from_logits)
loss = K.mean(tf.multiply(ce, weights_v))
return loss
return weighted_cross_entropy_fn
您可以像这样计算权重并获得像这样的二元交叉熵,它将以编程方式将 one_weight 设为 0.11,将 1 设为 0.89:
one_weight = (1-num_of_ones)/(num_of_ones + num_of_zeros)
zero_weight = (1-num_of_zeros)/(num_of_ones + num_of_zeros)
def weighted_binary_crossentropy(zero_weight, one_weight):
def weighted_binary_crossentropy(y_true, y_pred):
b_ce = K.binary_crossentropy(y_true, y_pred)
# weighted calc
weight_vector = y_true * one_weight + (1 - y_true) * zero_weight
weighted_b_ce = weight_vector * b_ce
return K.mean(weighted_b_ce)
return weighted_binary_crossentropy
如果您需要权重不同于训练损失的加权验证损失,您可以使用 tensorflow.keras.model.fit() 的参数 validation_data,将验证数据集作为Numpy 数组的元组,其中包含您的验证数据、标签和每个样本的权重。
请注意,您必须使用此技术将每个样本映射到其权重(此处为 class)。
在此处关注 link: https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit
tensorflow documentation