sethah commented on a change in pull request #9777: [MX-9588] Add micro averaging strategy for F1 metric URL: https://github.com/apache/incubator-mxnet/pull/9777#discussion_r167989350
########## File path: python/mxnet/metric.py ########## @@ -475,8 +475,84 @@ def update(self, labels, preds): self.num_inst += num_samples +class _BinaryClassificationMixin(object): + """ + Private mixin for keeping track of TPR, FPR, TNR, FNR counts for a classification metric. + """ + + def __init__(self): + self._true_positives = 0 + self._false_negatives = 0 + self._false_positives = 0 + self._true_negatives = 0 + + def _update_binary_stats(self, label, pred): + """ + Update various binary classification counts for a single (label, pred) + pair. + + Parameters + ---------- + label : `NDArray` + The labels of the data. + + pred : `NDArray` + Predicted values. + """ + pred = pred.asnumpy() + label = label.asnumpy().astype('int32') + pred_label = numpy.argmax(pred, axis=1) + + check_label_shapes(label, pred) + if len(numpy.unique(label)) > 2: + raise ValueError("%s currently only supports binary classification." + % self.__class__.__name__) + + for y_pred, y_true in zip(pred_label, label): + if y_pred == 1 and y_true == 1: + self._true_positives += 1. + elif y_pred == 1 and y_true == 0: + self._false_positives += 1. + elif y_pred == 0 and y_true == 1: + self._false_negatives += 1. + else: + self._true_negatives += 1. + + + @property + def _precision(self): + if self._true_positives + self._false_positives > 0: + return self._true_positives / (self._true_positives + self._false_positives) + else: + return 0. + + @property + def _recall(self): + if self._true_positives + self._false_negatives > 0: + return self._true_positives / (self._true_positives + self._false_negatives) + else: + return 0. + + @property + def _fscore(self): + if self._precision + self._recall > 0: + return 2 * self._precision * self._recall / (self._precision + self._recall) + else: + return 0. + + @property + def _total_examples(self): + return self._false_negatives + self._false_positives + \ + self._true_negatives + self._true_positives + + def _reset_stats(self): + self._false_positives = 0 + self._false_negatives = 0 + self._true_positives = 0 + self._true_negatives = 0 + @register -class F1(EvalMetric): +class F1(EvalMetric, _BinaryClassificationMixin): Review comment: I think we can make a `BinaryClassificationMetric` class that is intended to be abstract and inherits from `EvalMetric` without affecting backward compatibility. It seemed more appropriate as a mixin here since it would be useless as a concrete class and doesn't actually implement any functionality required by `EvalMetric`. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org With regards, Apache Git Services