Newer
Older
from sklearn import metrics
class AverageMeter:
def __init__(self, callback=None):
super().__init__()
return args[0]
else:
raise NotImplementedError
def reset(self):
for attr in filter(lambda a: not a.startswith('__'), dir(self)):
obj = getattr(self, attr)
if isinstance(obj, AverageMeter):
AverageMeter.reset(obj)
def update(self, *args, n=1):
self.val = self.compute(*args)
self.sum += self.val * n
self.count += n
self.avg = self.sum / self.count
def __repr__(self):
return 'val: {} avg: {} cnt: {}'.format(self.val, self.avg, self.count)
def __init__(self, n_classes=2, mode='separ', reduction='binary'):
super().__init__(None)
self._cm = AverageMeter(partial(metrics.confusion_matrix, labels=np.arange(n_classes)))
assert mode in ('accum', 'separ')
self.mode = mode
assert reduction in ('mean', 'none', 'binary')
if reduction == 'binary' and n_classes != 2:
raise ValueError("binary reduction only works in 2-class cases")
self.reduction = reduction
def _compute(self, cm):
raise NotImplementedError
def compute(self, cm):
if self.reduction == 'none':
# Do not reduce size
return self._compute(cm)
elif self.reduction == 'mean':
# Micro averaging
return self._compute(cm).mean()
else:
# The pos_class be 1
return self._compute(cm)[1]
def update(self, pred, true, n=1):
self._cm.update(true.ravel(), pred.ravel())
if self.mode == 'accum':
# Note that accumulation mode is special in that metric.val saves historical information.
# Therefore, metric.avg IS USUALLY NOT THE "AVERAGE" VALUE YOU WANT!!!
# Instead, metric.val is the averaged result in the sense of metric.avg in separ mode,
# while metric.avg can be considered as some average of average.
cm = self._cm.sum
elif self.mode == 'separ':
cm = self._cm.val
else:
raise NotImplementedError
super().update(cm, n=n)
def __repr__(self):
return self.__name__+' '+super().__repr__()
def _compute(self, cm):
return np.nan_to_num(np.diag(cm)/cm.sum(axis=0))
def _compute(self, cm):
return np.nan_to_num(np.diag(cm)/cm.sum(axis=1))
def __init__(self, n_classes=2, mode='separ'):
super().__init__(n_classes=n_classes, mode=mode, reduction='none')
def _compute(self, cm):
return np.nan_to_num(np.diag(cm).sum()/cm.sum())
def _compute(self, cm):
prec = np.nan_to_num(np.diag(cm)/cm.sum(axis=0))
recall = np.nan_to_num(np.diag(cm)/cm.sum(axis=1))
return np.nan_to_num(2*(prec*recall) / (prec+recall))