我正在为一个高度不平衡的分类问题实现CNN,我想在tensorflow中实现custum指标,以使用Select Best Model回调函数。具体来说,我想实现平衡准确度得分,即每个类的平均召回率(请参阅sklearn implementation here),有人知道如何实现吗?
mspsb9vt1#
我也遇到了同样的问题,所以我实现了一个基于SparseCategoricalAccuracy的自定义类:
SparseCategoricalAccuracy
class BalancedSparseCategoricalAccuracy(keras.metrics.SparseCategoricalAccuracy): def __init__(self, name='balanced_sparse_categorical_accuracy', dtype=None): super().__init__(name, dtype=dtype) def update_state(self, y_true, y_pred, sample_weight=None): y_flat = y_true if y_true.shape.ndims == y_pred.shape.ndims: y_flat = tf.squeeze(y_flat, axis=[-1]) y_true_int = tf.cast(y_flat, tf.int32) cls_counts = tf.math.bincount(y_true_int) cls_counts = tf.math.reciprocal_no_nan(tf.cast(cls_counts, self.dtype)) weight = tf.gather(cls_counts, y_true_int) return super().update_state(y_true, y_pred, sample_weight=weight)
其思想是将每个类的权重设置为与其大小成反比。这段代码会从Autograph生成一些警告,但我相信这些都是Autograph错误,而且度量似乎工作正常。
rsaldnfx2#
我能想到3种方法来处理这种情况:1)随机欠采样-在此方法中,您可以从多数类中随机移除样本。2)随机过采样(Random Over-sampling)-在此方法中,可以通过复制样本来增加样本。3)加权交叉熵-您还可以使用加权交叉熵,以便补偿少数类的损失值。See here我个人尝试过方法2,它确实显著提高了准确性,但不同数据集的准确性可能有所不同
u0njafvf3#
注
看起来Recall类的实现/API(我将其用作答案的模板)在较新的TF版本中进行了修改(正如@guilaumme-gaudin所指出的),因此我建议您查看当前TF版本中使用的Recall实现,并从那里使用我在最初的帖子中描述的相同方法来实现度量。这样我就不必在TF团队每次修改其指标的实现/API时更新我的答案。
Recall
原员额
我不是Tensorflow方面的Maven,但在tf源代码中的指标实现之间使用了一些模式匹配,我得出了以下结论
from tensorflow.python.keras import backend as K from tensorflow.python.keras.metrics import Metric from tensorflow.python.keras.utils import metrics_utils from tensorflow.python.ops import init_ops from tensorflow.python.ops import math_ops from tensorflow.python.keras.utils.generic_utils import to_list class BACC(Metric): def __init__(self, thresholds=None, top_k=None, class_id=None, name=None, dtype=None): super(BACC, self).__init__(name=name, dtype=dtype) self.init_thresholds = thresholds self.top_k = top_k self.class_id = class_id default_threshold = 0.5 if top_k is None else metrics_utils.NEG_INF self.thresholds = metrics_utils.parse_init_thresholds( thresholds, default_threshold=default_threshold) self.true_positives = self.add_weight( 'true_positives', shape=(len(self.thresholds),), initializer=init_ops.zeros_initializer) self.true_negatives = self.add_weight( 'true_negatives', shape=(len(self.thresholds),), initializer=init_ops.zeros_initializer) self.false_positives = self.add_weight( 'false_positives', shape=(len(self.thresholds),), initializer=init_ops.zeros_initializer) self.false_negatives = self.add_weight( 'false_negatives', shape=(len(self.thresholds),), initializer=init_ops.zeros_initializer) def update_state(self, y_true, y_pred, sample_weight=None): return metrics_utils.update_confusion_matrix_variables( { metrics_utils.ConfusionMatrix.TRUE_POSITIVES: self.true_positives, metrics_utils.ConfusionMatrix.TRUE_NEGATIVES: self.true_negatives, metrics_utils.ConfusionMatrix.FALSE_POSITIVES: self.false_positives, metrics_utils.ConfusionMatrix.FALSE_NEGATIVES: self.false_negatives, }, y_true, y_pred, thresholds=self.thresholds, top_k=self.top_k, class_id=self.class_id, sample_weight=sample_weight) def result(self): """ Returns the Balanced Accuracy (average between recall and specificity) """ result = (math_ops.div_no_nan(self.true_positives, self.true_positives + self.false_negatives) + math_ops.div_no_nan(self.true_negatives, self.true_negatives + self.false_positives)) / 2 return result def reset_states(self): num_thresholds = len(to_list(self.thresholds)) K.batch_set_value( [(v, np.zeros((num_thresholds,))) for v in self.variables]) def get_config(self): config = { 'thresholds': self.init_thresholds, 'top_k': self.top_k, 'class_id': self.class_id } base_config = super(BACC, self).get_config() return dict(list(base_config.items()) + list(config.items()))
我只是将源代码中的Recall类实现作为模板,并对其进行了扩展,以确保它定义了TP、TN、FP和FN。之后,我修改了result方法,使其计算平衡的准确性,瞧:)我将结果与sklearn的平衡准确性得分进行了比较,结果是匹配的,所以我认为它是正确的,但是为了以防万一,请再次检查。
result
sklearn
ldfqzlk84#
**我还没有测试过这段代码,**但是看看tensorflow==2.1.0的源代码,这可能适用于二进制分类的情况:
tensorflow==2.1.0
from tensorflow.keras.metrics import Recall from tensorflow.python.ops import math_ops class BalancedBinaryAccuracy(Recall): def result(self): result = (math_ops.div_no_nan(self.true_positives, self.true_positives + self.false_negatives) + math_ops.div_no_nan(self.true_negatives, self.true_negatives + self.false_positives)) / 2 return result[0] if len(self.thresholds) == 1 else result
htrmnn0y5#
作为编写定制度量的替代方法,您可以使用已实施且可通过训练日志获得的度量来编写定制回调。例如,您可以如下定义训练平衡准确度回调:
class TrainBalancedAccuracyCallback(tf.keras.callbacks.Callback): def __init__(self, **kargs): super(TrainBalancedAccuracyCallback, self).__init__(**kargs) def on_epoch_end(self, epoch, logs={}): train_sensitivity = logs['tp'] / (logs['tp'] + logs['fn']) train_specificity = logs['tn'] / (logs['tn'] + logs['fp']) logs['train_sensitivity'] = train_sensitivity logs['train_specificity'] = train_specificity logs['train_balacc'] = (train_sensitivity + train_specificity) / 2 print('train_balacc', logs['train_balacc'])
验证也是如此:
class ValBalancedAccuracyCallback(tf.keras.callbacks.Callback): def __init__(self, **kargs): super(ValBalancedAccuracyCallback, self).__init__(**kargs) def on_epoch_end(self, epoch, logs={}): val_sensitivity = logs['val_tp'] / (logs['val_tp'] + logs['val_fn']) val_specificity = logs['val_tn'] / (logs['val_tn'] + logs['val_fp']) logs['val_sensitivity'] = val_sensitivity logs['val_specificity'] = val_specificity logs['val_balacc'] = (val_sensitivity + val_specificity) / 2 print('val_balacc', logs['val_balacc'])
然后你可以把这些作为模型fit方法的callback参数的值。
callback
5条答案
按热度按时间mspsb9vt1#
我也遇到了同样的问题,所以我实现了一个基于
SparseCategoricalAccuracy
的自定义类:其思想是将每个类的权重设置为与其大小成反比。
这段代码会从Autograph生成一些警告,但我相信这些都是Autograph错误,而且度量似乎工作正常。
rsaldnfx2#
我能想到3种方法来处理这种情况:
1)随机欠采样-在此方法中,您可以从多数类中随机移除样本。
2)随机过采样(Random Over-sampling)-在此方法中,可以通过复制样本来增加样本。
3)加权交叉熵-您还可以使用加权交叉熵,以便补偿少数类的损失值。See here
我个人尝试过方法2,它确实显著提高了准确性,但不同数据集的准确性可能有所不同
u0njafvf3#
注
看起来
Recall
类的实现/API(我将其用作答案的模板)在较新的TF版本中进行了修改(正如@guilaumme-gaudin所指出的),因此我建议您查看当前TF版本中使用的Recall
实现,并从那里使用我在最初的帖子中描述的相同方法来实现度量。这样我就不必在TF团队每次修改其指标的实现/API时更新我的答案。原员额
我不是Tensorflow方面的Maven,但在tf源代码中的指标实现之间使用了一些模式匹配,我得出了以下结论
我只是将源代码中的
Recall
类实现作为模板,并对其进行了扩展,以确保它定义了TP、TN、FP和FN。之后,我修改了
result
方法,使其计算平衡的准确性,瞧:)我将结果与
sklearn
的平衡准确性得分进行了比较,结果是匹配的,所以我认为它是正确的,但是为了以防万一,请再次检查。ldfqzlk84#
**我还没有测试过这段代码,**但是看看
tensorflow==2.1.0
的源代码,这可能适用于二进制分类的情况:htrmnn0y5#
作为编写定制度量的替代方法,您可以使用已实施且可通过训练日志获得的度量来编写定制回调。例如,您可以如下定义训练平衡准确度回调:
验证也是如此:
然后你可以把这些作为模型fit方法的
callback
参数的值。