API Reference: Losses & Assigners¶
Task-specific loss functions and label assigners.
Loss Functions¶
Classification Losses¶
corecv.losses.classification
¶
GPU-native classification loss functions for CoreCV.
Provides focal loss and label-smoothing cross-entropy, both implemented as
pure vectorised PyTorch operations with zero CPU-GPU synchronisations
during forward or backward passes. No Python for-loops, no .item()
calls, no .cpu() transfers — everything stays on VRAM.
Example
import torch from corecv.losses.classification import FocalLoss loss_fn = FocalLoss(alpha=0.25, gamma=2.0) logits = torch.randn(8, 80, 32, 32, device="cuda") targets = torch.randint(0, 80, (8, 32, 32), device="cuda") loss = loss_fn(logits, targets)
FocalLoss(alpha=0.25, gamma=2.0, reduction='mean')
¶
Bases: Module
Focal Loss for dense object detection (Lin et al. ICCV 2017).
Reduces the relative loss for well-classified examples, focusing training on hard negatives. The formulation is::
FL(p_t) = -alpha_t * (1 - p_t)^gamma * log(p_t)
where p_t = exp(-CE) and alpha_t is a per-class weighting factor.
This implementation computes the standard cross-entropy with
reduction='none' and applies the focal modulator entirely via
vectorised tensor ops — no Python loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alpha
|
float | Tensor
|
Weighting factor. Can be a scalar |
0.25
|
gamma
|
float
|
Focal focusing parameter. Higher values down-weight easy
examples more aggressively. Default |
2.0
|
reduction
|
str
|
Aggregation mode. |
'mean'
|
Example
loss_fn = FocalLoss(alpha=0.25, gamma=2.0, reduction="mean") logits = torch.randn(4, 80, 16, 16, device="cuda") targets = torch.randint(0, 80, (4, 16, 16), device="cuda") loss = loss_fn(logits, targets)
Initialise FocalLoss with alpha, gamma, and reduction mode.
Source code in src/corecv/losses/classification.py
forward(inputs, targets)
¶
Compute focal loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Tensor
|
Unnormalised logits of shape |
required |
targets
|
Tensor
|
Ground-truth class indices of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss depending on |
Source code in src/corecv/losses/classification.py
LabelSmoothingCrossEntropy(smoothing=0.1, reduction='mean')
¶
Bases: Module
Label-smoothing cross-entropy loss.
Instead of a hard one-hot target [0, 0, 1, 0, ...], this loss
assigns (1 - smoothing) probability mass to the correct class and
distributes smoothing / (K - 1) mass uniformly across all other
classes, where K is the number of classes. This discourages the
model from becoming over-confident and acts as a regulariser.
Formulation::
L = (1 - smoothing) * CE(y, p) + smoothing * mean_k(log_softmax(z))
where CE is the standard cross-entropy and mean_k averages the
log-softmax over all K classes.
The implementation is fully vectorised using F.log_softmax +
F.nll_loss with no Python loops and no CPU-GPU sync points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
smoothing
|
float
|
Label-smoothing factor in |
0.1
|
reduction
|
str
|
Aggregation mode ( |
'mean'
|
Example
loss_fn = LabelSmoothingCrossEntropy(smoothing=0.1) logits = torch.randn(16, 100, device="cuda") targets = torch.randint(0, 100, (16,), device="cuda") loss = loss_fn(logits, targets)
Initialise LabelSmoothingCrossEntropy with smoothing factor and reduction mode.
Source code in src/corecv/losses/classification.py
forward(inputs, targets)
¶
Compute label-smoothing cross-entropy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Tensor
|
Unnormalised logits of shape |
required |
targets
|
Tensor
|
Ground-truth class indices of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss depending on |
Source code in src/corecv/losses/classification.py
Detection Losses¶
corecv.losses.detection
¶
GPU-native detection loss functions for CoreCV.
Provides GIoU, CIoU, Quality Focal Loss and Varifocal Loss — all
implemented as pure vectorised PyTorch operations with zero CPU-GPU
synchronisations during forward or backward passes. No Python loops, no
.item() calls, no .cpu() transfers — every computation remains on
VRAM.
Supports both per-level tensors (B, 4, H, W) and flattened
tensors (N, 4) for detection boxes. All IoU losses use
torch.min / torch.max / torch.clamp for vectorised
intersection / union / enclosing-box computation.
Example
import torch from corecv.losses.detection import GIoULoss, CIoULoss giou = GIoULoss(reduction="mean") ciou = CIoULoss(reduction="mean") pred = torch.randn(4, 4, 32, 32, device="cuda").abs() * 100 tgt = torch.randn(4, 4, 32, 32, device="cuda").abs() * 100 loss = giou(pred, tgt)
CIoULoss(reduction='mean', eps=1e-07)
¶
Bases: Module
Complete IoU Loss (Zheng et al. CVPR 2020).
Extends DIoU with an aspect-ratio consistency term::
CIoU = IoU - rho²(b_pred, b_gt) / c² - alpha * v
where:
rho²is the squared centre distance.cis the diagonal of the enclosing box.vmeasures aspect-ratio similarity:v = (4 / pi²) * (arctan(w_gt/h_gt) - arctan(w_pred/h_pred))²alpha = v / (1 - IoU + v)balances the two penalty terms.
The loss is 1 - CIoU.
Fully vectorised: all operations run on batched tensors with no Python loops and no CPU-GPU synchronisations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reduction
|
str
|
|
'mean'
|
eps
|
float
|
Small constant for numerical stability. Default |
1e-07
|
Example
ciou = CIoULoss(reduction="mean") pred = torch.randn(4, 4, 32, 32, device="cuda").abs() * 100 tgt = torch.randn(4, 4, 32, 32, device="cuda").abs() * 100 loss = ciou(pred, tgt)
Initialise CIoULoss with reduction mode and epsilon.
Source code in src/corecv/losses/detection.py
forward(pred_boxes, target_boxes)
¶
Compute CIoU loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_boxes
|
Tensor
|
Predicted boxes, |
required |
target_boxes
|
Tensor
|
Target boxes, same shape. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss. |
Source code in src/corecv/losses/detection.py
GIoULoss(reduction='mean', eps=1e-07)
¶
Bases: Module
Generalized IoU Loss (Rezatofighi et al. CVPR 2019).
Extends IoU with a penalty term for non-overlapping boxes::
GIoU = IoU - (C - U) / C
where C is the enclosing-box area and U is the union area.
The loss is 1 - GIoU.
Fully vectorised: all operations use torch.min / torch.max /
torch.clamp on batched tensors with no Python loops.
Supports two input formats:
- Per-level:
(B, 4, H, W)— typical output of anchor-free detection heads at a single FPN level. - Flattened:
(N, 4)— all predictions from a single level (or all levels concatenated).
When per_sample is True (default) and the input is 4-D, the
loss is computed element-wise and returned with the requested
reduction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reduction
|
str
|
|
'mean'
|
eps
|
float
|
Small constant for numerical stability. Default |
1e-07
|
Example
giou = GIoULoss(reduction="mean") pred = torch.randn(4, 4, 32, 32, device="cuda").abs() tgt = torch.randn(4, 4, 32, 32, device="cuda").abs() loss = giou(pred, tgt)
Initialise GIoULoss with reduction mode and epsilon.
Source code in src/corecv/losses/detection.py
forward(pred_boxes, target_boxes)
¶
Compute GIoU loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_boxes
|
Tensor
|
Predicted boxes. Shape |
required |
target_boxes
|
Tensor
|
Target boxes. Same shape as pred_boxes. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss depending on |
Source code in src/corecv/losses/detection.py
QualityFocalLoss(beta=2.0, reduction='mean')
¶
Bases: Module
Quality Focal Loss (QFL) for anchor-free detection heads.
QFL replaces the standard binary cross-entropy with a modulated loss that treats the target as a continuous IoU quality score rather than a hard binary label::
loss(q) = |pred - q|^beta * BCE(pred, q)
where q is the IoU between the predicted and ground-truth box for
positive samples (and 0 for negatives).
This formulation encourages the classification branch to directly predict IoU quality, enabling better ranking at inference.
Fully vectorised: uses F.binary_cross_entropy_with_logits with a
pos_weight tensor and the quality-based weighting computed via
pure tensor ops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
beta
|
float
|
Focusing parameter of the quality modulation.
Default |
2.0
|
reduction
|
str
|
|
'mean'
|
Example
qfl = QualityFocalLoss(beta=2.0) pred = torch.randn(1000, 80, device="cuda") tgt_scores = torch.rand(1000, 80, device="cuda") # IoU quality tgt_labels = torch.randint(0, 80, (1000,), device="cuda") loss = qfl(pred, tgt_scores, tgt_labels)
Initialise QualityFocalLoss with beta and reduction mode.
Source code in src/corecv/losses/detection.py
forward(pred_scores, target_scores, target_labels)
¶
Compute Quality Focal Loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_scores
|
Tensor
|
Predicted classification logits.
Shape |
required |
target_scores
|
Tensor
|
Continuous quality targets (e.g. IoU values).
Same shape as pred_scores. Values in |
required |
target_labels
|
Tensor
|
Integer class labels of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss. |
Source code in src/corecv/losses/detection.py
VarifocalLoss(gamma=2.0, alpha=0.25, reduction='mean')
¶
Bases: Module
Varifocal Loss (VFL) for dense object detection.
An asymmetric focal loss that re-weights positive and negative samples differently based on the quality (IoU) of each sample::
VFL(p, q) = -q * log(p) if positive
= -alpha * p^gamma * log(1-p) if negative
where q is the IoU quality score (target) for positives, p is
the predicted probability, gamma is the focusing parameter, and
alpha balances positive / negative contribution.
This encourages the model to predict high scores only for high-quality detections while suppressing low-quality false positives.
Fully vectorised: all operations are on batched tensors with no Python loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gamma
|
float
|
Focusing parameter for negative samples. Default |
2.0
|
alpha
|
float
|
Balancing factor for negative samples. Default |
0.25
|
reduction
|
str
|
|
'mean'
|
Example
vfl = VarifocalLoss(gamma=2.0, alpha=0.25) pred = torch.randn(500, 80, device="cuda") tgt_scores = torch.rand(500, 80, device="cuda") tgt_labels = torch.randint(0, 80, (500,), device="cuda") loss = vfl(pred, tgt_scores, tgt_labels)
Initialise VarifocalLoss with gamma, alpha, and reduction mode.
Source code in src/corecv/losses/detection.py
forward(pred_scores, target_scores, target_labels)
¶
Compute Varifocal Loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_scores
|
Tensor
|
Predicted classification logits, |
required |
target_scores
|
Tensor
|
Quality targets in |
required |
target_labels
|
Tensor
|
Integer class labels, |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss. |
Source code in src/corecv/losses/detection.py
Segmentation Losses¶
corecv.losses.segmentation
¶
GPU-native segmentation loss functions for CoreCV.
Provides Dice loss and a combined CrossEntropy + Dice loss, both implemented
as pure vectorised PyTorch operations with zero CPU-GPU synchronisations
during forward or backward passes. No Python for-loops over channels or
batch elements — every computation uses broadcasting, F.one_hot, and
standard tensor ops that remain entirely on VRAM.
Example
import torch from corecv.losses.segmentation import DiceLoss loss_fn = DiceLoss(smooth=1.0) logits = torch.randn(4, 21, 256, 256, device="cuda") targets = torch.randint(0, 21, (4, 256, 256), device="cuda") loss = loss_fn(logits, targets)
CombinedSegmentationLoss(ce_weight=1.0, dice_weight=1.0, ignore_index=-100)
¶
Bases: Module
Combined Cross-Entropy + Dice loss for segmentation.
Summation of a weighted F.cross_entropy term and a
:class:DiceLoss term, designed for common semantic segmentation
training where both pixel-wise classification accuracy and region
overlap matter.
Handles ignore_index transparently: pixels marked with
ignore_index in the target are excluded from both the CE and
the Dice computation, ensuring no gradient leakage from unlabeled
regions.
The entire pipeline is 100% GPU-native with zero CPU-GPU sync points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ce_weight
|
float
|
Weight of the cross-entropy term. Default |
1.0
|
dice_weight
|
float
|
Weight of the Dice term. Default |
1.0
|
ignore_index
|
int
|
Class index to ignore during loss computation.
Default |
-100
|
Example
loss_fn = CombinedSegmentationLoss( ... ce_weight=1.0, dice_weight=1.0, ignore_index=255, ... ) logits = torch.randn(8, 21, 256, 256, device="cuda") targets = torch.full((8, 256, 256), 255, device="cuda") targets[:, :128, :128] = torch.randint(0, 21, (8, 128, 128), device="cuda") loss = loss_fn(logits, targets)
Initialise CombinedSegmentationLoss with term weights and ignore index.
Source code in src/corecv/losses/segmentation.py
forward(inputs, targets)
¶
Compute the combined CE + Dice loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Tensor
|
Logits of shape |
required |
targets
|
Tensor
|
Ground-truth class indices of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Weighted scalar loss. |
Source code in src/corecv/losses/segmentation.py
DiceLoss(smooth=1.0, reduction='mean')
¶
Bases: Module
Dice coefficient loss for semantic segmentation.
The Dice coefficient measures overlap between prediction and target::
Dice = 2 * sum(p * t) / (sum(p) + sum(t) + smooth)
The loss is 1 - Dice. A value of smooth (default 1.0)
prevents division by zero.
This implementation is fully vectorised: F.one_hot expands targets
to (B, H, W, C) in a single kernel call, then the Dice computation
is a sequence of batch reductions with no Python loops over channels or
spatial locations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
smooth
|
float
|
Laplace smoothing factor to avoid division by zero.
Default |
1.0
|
reduction
|
str
|
Aggregation mode ( |
'mean'
|
Note
Inputs should be logits (not probabilities). The module applies channel-wise softmax internally.
Example
loss_fn = DiceLoss(smooth=1.0) logits = torch.randn(2, 10, 128, 128, device="cuda") targets = torch.randint(0, 10, (2, 128, 128), device="cuda") loss = loss_fn(logits, targets)
Initialise DiceLoss with smoothing factor and reduction mode.
Source code in src/corecv/losses/segmentation.py
forward(inputs, targets)
¶
Compute Dice loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Tensor
|
Logits of shape |
required |
targets
|
Tensor
|
Ground-truth class indices of shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar or tensor loss depending on |
Source code in src/corecv/losses/segmentation.py
Assigners¶
Hungarian Assigner¶
corecv.losses.assigners.hungarian
¶
GPU-native Hungarian matching and set criterion for query detection.
Implements bipartite 1-to-1 assignment (Hungarian algorithm) and the
associated SetCriterion used to train RT-DETR / D-FINE style
query-based detection heads.
All loss computations (focal classification, L1 bounding-box, GIoU) are
pure vectorised PyTorch operations with zero CPU-GPU synchronisations
during forward and backward passes. The only CPU round-trip is the
combinatorial assignment step itself, which operates on a small cost matrix
of shape (num_queries, num_gt) per batch element — the heavy lifting
(the full pairwise cost-matrix construction) stays entirely on VRAM.
Architecture overview::
pred_scores: (B, num_queries, num_classes)
pred_boxes: (B, num_queries, 4) — normalised cxcywh
gt_labels: List[Tensor] of per-image targets
gt_boxes: List[Tensor] of per-image targets
|
v HungarianMatcher.forward() (cost_class + L1 + GIoU)
|
indices: List[Tuple[Tensor, Tensor]] (pred_idx, gt_idx) per image
|
v SetCriterion.loss() (focal + L1 + GIoU + aux)
|
scalar loss
Example
import torch from corecv.losses.assigners import HungarianMatcher, SetCriterion matcher = HungarianMatcher(cost_class=2.0, cost_bbox=5.0, cost_giou=2.0) criterion = SetCriterion(num_classes=80, matcher=matcher) pred_scores = torch.randn(2, 300, 80) pred_boxes = torch.sigmoid(torch.randn(2, 300, 4)) gt_labels = [torch.tensor([0, 5]), torch.tensor([1, 2, 3])] gt_boxes = [torch.rand(2, 4), torch.rand(3, 4)] loss, aux = criterion(pred_scores, pred_boxes, gt_labels, gt_boxes)
HungarianMatcher(cost_class=2.0, cost_bbox=5.0, cost_giou=2.0, focal_alpha=0.25, focal_gamma=2.0)
¶
Bases: Module
Bipartite 1-to-1 assignment via the Hungarian algorithm.
Computes a cost matrix from three terms — classification cost, L1 bounding-box cost, and GIoU cost — then solves the linear-sum assignment to find the optimal prediction-to-ground-truth pairing.
The cost matrix for each image is::
cost = cost_class * focal_cost
+ cost_bbox * l1_cost
+ cost_giou * (1 - giou_matrix)
where:
focal_costis derived from the negative focal-log-probability for the target class.l1_costis the element-wise L1 distance between normalised(cx, cy, w, h)boxes.giou_matrixis the pairwise GIoU between all predictions and all ground-truth boxes.
The assignment is performed per image independently. The heavy
pairwise-cost computation runs entirely on VRAM; only the small
(num_queries, num_gt) cost matrix is transferred to CPU for
scipy.optimize.linear_sum_assignment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cost_class
|
float
|
Weight of the focal classification cost.
Default |
2.0
|
cost_bbox
|
float
|
Weight of the L1 bounding-box cost.
Default |
5.0
|
cost_giou
|
float
|
Weight of the GIoU cost. Default |
2.0
|
focal_alpha
|
float
|
Focal-loss alpha for class weighting.
Default |
0.25
|
focal_gamma
|
float
|
Focal-loss gamma for focusing parameter.
Default |
2.0
|
Example
matcher = HungarianMatcher(cost_class=2.0, cost_bbox=5.0, cost_giou=2.0) pred_scores = torch.randn(2, 300, 80) pred_boxes = torch.sigmoid(torch.randn(2, 300, 4)) gt_labels = [torch.tensor([0, 5]), torch.tensor([1, 2, 3])] gt_boxes = [torch.rand(2, 4), torch.rand(3, 4)] indices = matcher(pred_scores, pred_boxes, gt_labels, gt_boxes)
Initialise HungarianMatcher with cost weights and focal parameters.
Source code in src/corecv/losses/assigners/hungarian.py
forward(pred_scores, pred_boxes, gt_labels, gt_boxes)
¶
Compute optimal bipartite matching for each image in the batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_scores
|
Tensor
|
Classification logits of shape
|
required |
pred_boxes
|
Tensor
|
Predicted normalised |
required |
gt_labels
|
list[Tensor]
|
List of |
required |
gt_boxes
|
list[Tensor]
|
List of |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[Tensor, Tensor]]
|
List of |
list[tuple[Tensor, Tensor]]
|
element is a 1-D |
list[tuple[Tensor, Tensor]]
|
image has zero ground-truth boxes, both tensors are empty. |
Source code in src/corecv/losses/assigners/hungarian.py
SetCriterion(num_classes, matcher, focal_alpha=0.25, focal_gamma=2.0, loss_cls_weight=1.0, loss_bbox_weight=5.0, loss_giou_weight=2.0, aux_loss_weights=None, no_object_weight=0.1)
¶
Bases: Module
Set-based criterion for training query detection heads (RT-DETR / D-FINE).
Uses :class:HungarianMatcher to establish 1-to-1 assignments between
predictions and ground-truth boxes, then computes a weighted sum of:
- Classification loss — sigmoid focal loss applied to matched queries; unmatched queries are treated as background (class 0 with target 0 in the "no-object" formulation, or omitted entirely).
- Bounding-box L1 loss — element-wise L1 on matched boxes only.
- GIoU loss —
1 - GIoUon matched boxes only.
Supports auxiliary losses from intermediate decoder layers: when
return_intermediate=True is used in the head, pass the lists of
intermediate predictions and the criterion will apply the same loss
at every decoder layer with optional per-layer weights.
All loss computations are 100 % GPU-native — the only CPU round-trip
is the assignment step inside :class:HungarianMatcher.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_classes
|
int
|
Number of foreground object classes (excluding the
implicit |
required |
matcher
|
HungarianMatcher
|
A :class: |
required |
focal_alpha
|
float
|
Focal-loss alpha. Default |
0.25
|
focal_gamma
|
float
|
Focal-loss gamma. Default |
2.0
|
loss_cls_weight
|
float
|
Weight of the classification loss.
Default |
1.0
|
loss_bbox_weight
|
float
|
Weight of the L1 bounding-box loss.
Default |
5.0
|
loss_giou_weight
|
float
|
Weight of the GIoU loss. Default |
2.0
|
aux_loss_weights
|
list[float] | None
|
Per-layer weights for auxiliary decoder losses.
If |
None
|
no_object_weight
|
float
|
Weight assigned to the "no-object" class for
unmatched queries. Default |
0.1
|
Example
matcher = HungarianMatcher(cost_class=2.0, cost_bbox=5.0, cost_giou=2.0) criterion = SetCriterion(num_classes=80, matcher=matcher) pred_scores = torch.randn(2, 300, 80) pred_boxes = torch.sigmoid(torch.randn(2, 300, 4)) gt_labels = [torch.tensor([0, 5]), torch.tensor([1, 2, 3])] gt_boxes = [torch.rand(2, 4), torch.rand(3, 4)] loss, aux = criterion(pred_scores, pred_boxes, gt_labels, gt_boxes)
Initialise SetCriterion with matcher and loss weights.
Source code in src/corecv/losses/assigners/hungarian.py
forward(pred_scores, pred_boxes, gt_labels, gt_boxes, intermediate_cls=None, intermediate_reg=None)
¶
Compute the set-matching loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_scores
|
Tensor
|
Final classification logits
|
required |
pred_boxes
|
Tensor
|
Final predicted normalised |
required |
gt_labels
|
list[Tensor]
|
List of |
required |
gt_boxes
|
list[Tensor]
|
List of |
required |
intermediate_cls
|
list[Tensor] | None
|
Optional list of intermediate classification
logits from decoder layers, each of shape
|
None
|
intermediate_reg
|
list[Tensor] | None
|
Optional list of intermediate bounding-box
predictions from decoder layers, each of shape
|
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Tuple of: |
dict[str, Tensor]
|
|
tuple[Tensor, dict[str, Tensor]]
|
|
Source code in src/corecv/losses/assigners/hungarian.py
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | |
Task-Aligned Assigner (TAL)¶
corecv.losses.assigners.tal
¶
GPU-native Task-Aligned Assigner (TAL) for anchor-free detection.
Implements the dynamic sample assignment strategy from TOOD: Task-Aligned One-stage Object Detection (Tian et al., AAAI 2021). The alignment metric used for positive sample selection is::
t = cls_score^alpha * IoU^beta
For each ground-truth box, the top-k predictions with the highest TAL alignment metric are selected as positive samples. All remaining predictions are treated as negatives.
The assigner is designed to integrate with
:class:~corecv.models.heads.detection.decoupled_anchor_free.DecoupledAnchorFreeHead
and downstream losses
(:class:~corecv.losses.detection.QualityFocalLoss /
:class:~corecv.losses.detection.VarifocalLoss for classification,
:class:~corecv.losses.detection.GIoULoss /
:class:~corecv.losses.detection.CIoULoss for bounding-box regression).
All computations are pure vectorised PyTorch operations with zero
CPU-GPU synchronisations during assignment. No .item() calls, no
.cpu() transfers — everything stays on VRAM.
Architecture overview::
pred_scores: List[(B, C, H_l, W_l)] — per-level classification logits
pred_boxes: List[(B, 4, H_l, W_l)] — per-level (l, t, r, b) regression
strides: List[int] — per-level strides (e.g. 8, 16, 32)
gt_labels: List[Tensor] — per-image GT class labels
gt_boxes: List[Tensor] — per-image GT boxes (x1, y1, x2, y2)
|
v TaskAlignedAssigner.forward()
|
assignment dict with:
pos_mask — (N_total,) bool tensor per image
neg_mask — (N_total,) bool tensor per image
assigned_gt_inds — (N_total,) long tensor per image
assigned_labels — (N_total,) long tensor per image
pos_ious — (num_pos,) float tensor per image (IoU quality)
Example
from corecv.losses.assigners.tal import TaskAlignedAssigner assigner = TaskAlignedAssigner(num_classes=80, topk=13)
Given per-level predictions from DecoupledAnchorFreeHead:¶
cls_logits: [(B, 80, H_l, W_l), ...], reg_pred: [(B, 4, H_l, W_l), ...]¶
strides: [8, 16, 32], gt_labels: per-image, gt_boxes: per-image (xyxy)¶
assignment = assigner(cls_logits, reg_pred, strides, gt_labels, gt_boxes)
TaskAlignedAssigner(num_classes, topk=13, alpha=0.5, beta=6.0)
¶
Bases: Module
Task-Aligned dynamic assigner for anchor-free detection.
Implements the alignment-metric-based sample assignment from TOOD.
For each ground-truth box, the topk predictions with the highest
alignment score t = cls^alpha * IoU^beta are selected as
positives. When a prediction is selected by multiple ground-truth
boxes, it is assigned to the one yielding the highest alignment
metric.
The assigner produces per-image assignment results that can be consumed directly by the CoreCV detection losses:
- :class:
~corecv.losses.detection.QualityFocalLossor :class:~corecv.losses.detection.VarifocalLossfor classification (usingassigned_labelsandpos_iousas quality targets). - :class:
~corecv.losses.detection.GIoULossor :class:~corecv.losses.detection.CIoULossfor bounding-box regression (usingpos_maskandassigned_gt_indsto gather matched predictions and targets).
All operations run entirely on GPU — there are zero CPU-GPU synchronisations during the forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_classes
|
int
|
Number of foreground object classes. |
required |
topk
|
int
|
Number of top predictions selected per ground-truth box.
Typical values: |
13
|
alpha
|
float
|
Exponent for the classification score in the alignment
metric. Default |
0.5
|
beta
|
float
|
Exponent for the IoU in the alignment metric. Higher
values bias selection towards higher-quality boxes. Default
|
6.0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Example
assigner = TaskAlignedAssigner(num_classes=80, topk=13) cls_logits = [torch.randn(2, 80, 80, 80), ... torch.randn(2, 80, 40, 40), ... torch.randn(2, 80, 20, 20)] reg_pred = [torch.randn(2, 4, 80, 80).abs(), ... torch.randn(2, 4, 40, 40).abs(), ... torch.randn(2, 4, 20, 20).abs()] strides = [8, 16, 32] gt_labels = [torch.tensor([0, 5]), torch.tensor([1, 2, 3])] gt_boxes = [torch.rand(2, 4) * 400 + 50, ... torch.rand(3, 4) * 400 + 50] assignment = assigner(cls_logits, reg_pred, strides, ... gt_labels, gt_boxes)
Initialise the Task-Aligned Assigner.
Source code in src/corecv/losses/assigners/tal.py
forward(pred_scores, pred_boxes, strides, gt_labels, gt_boxes)
¶
Compute task-aligned assignment for each image in the batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pred_scores
|
list[Tensor]
|
Per-level classification logits. Each element
is a tensor of shape |
required |
pred_boxes
|
list[Tensor]
|
Per-level bounding-box regression predictions.
Each element is a tensor of shape |
required |
strides
|
list[int]
|
Per-level stride values (e.g. |
required |
gt_labels
|
list[Tensor]
|
List of |
required |
gt_boxes
|
list[Tensor]
|
List of |
required |
Returns:
| Type | Description |
|---|---|
dict[str, list[Tensor]]
|
Dictionary with the following keys (all values are lists |
dict[str, list[Tensor]]
|
of length |
dict[str, list[Tensor]]
|
|
dict[str, list[Tensor]]
|
|
dict[str, list[Tensor]]
|
|
dict[str, list[Tensor]]
|
|
dict[str, list[Tensor]]
|
|
dict[str, list[Tensor]]
|
Here |
dict[str, list[Tensor]]
|
|
Source code in src/corecv/losses/assigners/tal.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | |