def _roc_compute( preds: Tensor, target: Tensor, num_classes: int, pos_label: int, sample_weights: Optional[Sequence] = None, ) -> Union[Tuple[Tensor, Tensor, Tensor], Tuple[List[Tensor], List[Tensor], List[Tensor]]]: with torch.no_grad(): if num_classes == 1 and preds.ndim == 1: # binary fps, tps, thresholds = _binary_clf_curve( preds=preds, target=target, sample_weights=sample_weights, pos_label=pos_label) # Add an extra threshold position # to make sure that the curve starts at (0, 0) tps = torch.cat( [torch.zeros(1, dtype=tps.dtype, device=tps.device), tps]) fps = torch.cat( [torch.zeros(1, dtype=fps.dtype, device=fps.device), fps]) thresholds = torch.cat([thresholds[0][None] + 1, thresholds]) if fps[-1] <= 0: raise ValueError( "No negative samples in targets, false positive value should be meaningless" ) fpr = fps / fps[-1] if tps[-1] <= 0: raise ValueError( "No positive samples in targets, true positive value should be meaningless" ) tpr = tps / tps[-1] return fpr, tpr, thresholds # Recursively call per class fpr, tpr, thresholds = [], [], [] for c in range(num_classes): if preds.shape == target.shape: preds_c = preds[:, c] target_c = target[:, c] pos_label = 1 else: preds_c = preds[:, c] target_c = target pos_label = c res = roc( preds=preds_c, target=target_c, num_classes=1, pos_label=pos_label, sample_weights=sample_weights, ) fpr.append(res[0]) tpr.append(res[1]) thresholds.append(res[2]) return fpr, tpr, thresholds
def _roc_compute_single_class( preds: Tensor, target: Tensor, pos_label: int, sample_weights: Optional[Sequence] = None, ) -> Tuple[Tensor, Tensor, Tensor]: fps, tps, thresholds = _binary_clf_curve(preds=preds, target=target, sample_weights=sample_weights, pos_label=pos_label) # Add an extra threshold position to make sure that the curve starts at (0, 0) tps = torch.cat([torch.zeros(1, dtype=tps.dtype, device=tps.device), tps]) fps = torch.cat([torch.zeros(1, dtype=fps.dtype, device=fps.device), fps]) thresholds = torch.cat([thresholds[0][None] + 1, thresholds]) if fps[-1] <= 0: raise ValueError( "No negative samples in targets, false positive value should be meaningless" ) fpr = fps / fps[-1] if tps[-1] <= 0: raise ValueError( "No positive samples in targets, true positive value should be meaningless" ) tpr = tps / tps[-1] return fpr, tpr, thresholds
def _roc_compute_single_class( preds: Tensor, target: Tensor, pos_label: int, sample_weights: Optional[Sequence] = None, ) -> Tuple[Tensor, Tensor, Tensor]: """Computes Receiver Operating Characteristic for single class inputs. Returns tensor with false positive rates, tensor with true positive rates, tensor with thresholds used for computing false- and true postive rates. Args: preds: Predicted tensor target: Ground truth tensor pos_label: integer determining the positive class. Default is ``None`` which for binary problem is translate to 1. For multiclass problems this argument should not be set as we iteratively change it in the range [0,num_classes-1] sample_weights: sample weights for each data point """ fps, tps, thresholds = _binary_clf_curve(preds=preds, target=target, sample_weights=sample_weights, pos_label=pos_label) # Add an extra threshold position to make sure that the curve starts at (0, 0) tps = torch.cat([torch.zeros(1, dtype=tps.dtype, device=tps.device), tps]) fps = torch.cat([torch.zeros(1, dtype=fps.dtype, device=fps.device), fps]) thresholds = torch.cat([thresholds[0][None] + 1, thresholds]) if fps[-1] <= 0: rank_zero_warn( "No negative samples in targets, false positive value should be meaningless." " Returning zero tensor in false positive score", UserWarning, ) fpr = torch.zeros_like(thresholds) else: fpr = fps / fps[-1] if tps[-1] <= 0: rank_zero_warn( "No positive samples in targets, true positive value should be meaningless." " Returning zero tensor in true positive score", UserWarning, ) tpr = torch.zeros_like(thresholds) else: tpr = tps / tps[-1] return fpr, tpr, thresholds
def test_binary_clf_curve(sample_weight, pos_label, exp_shape): # TODO: move back the pred and target to test func arguments # if you fix the array inside the function, you'd also have fix the shape, # because when the array changes, you also have to fix the shape seed_everything(0) pred = torch.randint(low=51, high=99, size=(100, ), dtype=torch.float) / 100 target = torch.tensor([0, 1] * 50, dtype=torch.int) if sample_weight is not None: sample_weight = torch.ones_like(pred) * sample_weight fps, tps, thresh = _binary_clf_curve(preds=pred, target=target, sample_weights=sample_weight, pos_label=pos_label) assert isinstance(tps, torch.Tensor) assert isinstance(fps, torch.Tensor) assert isinstance(thresh, torch.Tensor) assert tps.shape == (exp_shape, ) assert fps.shape == (exp_shape, ) assert thresh.shape == (exp_shape, )