Ref: https://github.com/keras-team/keras/issues/2115
Keras提供的损失函数binary_crossentropy和categorical_crossentropy没有加权,如果想实现样本的不同权重功能有一种策略是对损失函数加权处理。
二分加权交叉熵损失
class WeightedBinaryCrossEntropy(object):
def __init__(self, pos_ratio):
neg_ratio = 1. - pos_ratio
self.pos_ratio = tf.constant(pos_ratio, tf.float32)
self.weights = tf.constant(neg_ratio / pos_ratio, tf.float32)
self.__name__ = "weighted_binary_crossentropy({0})".format(pos_ratio)
def __call__(self, y_true, y_pred):
return self.weighted_binary_crossentropy(y_true, y_pred)
def weighted_binary_crossentropy(self, y_true, y_pred):
# Transform to logits
epsilon = tf.convert_to_tensor(K.common._EPSILON, y_pred.dtype.base_dtype)
y_pred = tf.clip_by_value(y_pred, epsilon, 1 - epsilon)
y_pred = tf.log(y_pred / (1 - y_pred))
cost = tf.nn.weighted_cross_entropy_with_logits(y_true, y_pred, self.weights)
return K.mean(cost * self.pos_ratio, axis=-1)
多分类加权交叉熵
class WeightedCategoricalCrossEntropy(object):
def __init__(self, weights):
nb_cl = len(weights)
self.weights = np.ones((nb_cl, nb_cl))
for class_idx, class_weight in weights.items():
self.weights[0][class_idx] = class_weight
self.weights[class_idx][0] = class_weight
self.__name__ = 'w_categorical_crossentropy'
def __call__(self, y_true, y_pred):
return self.w_categorical_crossentropy(y_true, y_pred)
def w_categorical_crossentropy(self, y_true, y_pred):
nb_cl = len(self.weights)
final_mask = K.zeros_like(y_pred[..., 0])
y_pred_max = K.max(y_pred, axis=-1)
y_pred_max = K.expand_dims(y_pred_max, axis=-1)
y_pred_max_mat = K.equal(y_pred, y_pred_max)
for c_p, c_t in itertools.product(range(nb_cl), range(nb_cl)):
w = K.cast(self.weights[c_t, c_p], K.floatx())
y_p = K.cast(y_pred_max_mat[..., c_p], K.floatx())
y_t = K.cast(y_pred_max_mat[..., c_t], K.floatx())
final_mask += w * y_p * y_t
return K.categorical_crossentropy(y_pred, y_true) * final_mask