Skip to content
Back to skills

Separate Pos Neg Dice Tracking

ASecurity

Track dice score separately for positive (mask-present) and negative (empty-mask) images to avoid division distortion

  • 61 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 12, 2026
datapythongo

Security analysis

A100/100

Scanned September 12, 2026

npx -y skills add wenmin-wu/ds-skills --skill separate-pos-neg-dice-tracking --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Separate Pos Neg Dice Tracking?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Separate Pos Neg Dice Tracking
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wenmin-wu-separate-pos-neg-dice-tracking/badge)](https://www.skillsdirectory.com/skills/wenmin-wu-separate-pos-neg-dice-tracking)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cv-separate-pos-neg-dice-tracking
description: Track dice score separately for positive (mask-present) and negative (empty-mask) images to avoid division distortion
---

## Overview

Standard dice metric breaks on empty masks: `2|A∩B|/(|A|+|B|) = 0/0`. Most datasets hack around this by adding a small epsilon, which distorts scores for negative images. A cleaner approach: track dice separately for positive and negative cases. For negative images, dice is simply 1 if the prediction is also empty, else 0. This gives interpretable per-category scores and reveals whether the model is failing on positives or leaking false positives on negatives.

## Quick Start

```python
import torch

def dice_metric_split(probability, truth, threshold=0.5):
    """Return (all_dice, dice_neg, dice_pos) — dice split by mask presence."""
    with torch.no_grad():
        p = (probability > threshold).float().view(len(truth), -1)
        t = (truth > 0.5).float().view(len(truth), -1)
        t_sum = t.sum(-1)
        p_sum = p.sum(-1)

        neg_idx = torch.nonzero(t_sum == 0).squeeze(-1)
        pos_idx = torch.nonzero(t_sum >= 1).squeeze(-1)

        # Negative: 1 if prediction is also empty, else 0
        dice_neg = (p_sum == 0).float()
        # Positive: standard dice
        dice_pos = 2 * (p * t).sum(-1) / ((p + t).sum(-1) + 1e-7)

        dice_neg = dice_neg[neg_idx]
        dice_pos = dice_pos[pos_idx]
    return torch.cat([dice_pos, dice_neg]), dice_neg, dice_pos
```

## Workflow

1. Binarize probability map with threshold
2. Split batch by ground-truth mask sum: zero → negative, positive → positive
3. For negatives, dice = 1 iff prediction sum is also zero (perfect empty prediction)
4. For positives, compute standard dice
5. Report both aggregates separately in logs — watch them diverge to diagnose failure modes

## Key Decisions

- **Negative definition**: Ground-truth sum == 0 (no mask pixels). Different from "negative predictions".
- **Per-image vs per-batch**: Compute per-image to avoid one large positive mask dominating the score.
- **Actionable signal**: If `dice_neg` drops, model is hallucinating masks on healthy images — add a classification gate. If `dice_pos` drops, improve segmentation directly.
- **vs. epsilon smoothing**: Epsilon hides the failure mode by giving fake dice=1 to empty pairs. Split tracking exposes it.

## References

- [UNet with ResNet34 encoder (Pytorch)](https://www.kaggle.com/code/rishabhiitbhu/unet-with-resnet34-encoder-pytorch)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…