рд╕рд╛рдордЧреНрд░реА рдкрд░ рдЬрд╛рдПрдВ

рдХреЗ рд▓рд┐рдП рд╕рдВрджрд░реНрдн ultralytics/models/utils/loss.py

рдиреЛрдЯ

рдпрд╣ рдлрд╝рд╛рдЗрд▓ рдпрд╣рд╛рдБ рдЙрдкрд▓рдмреНрдз рд╣реИ https://github.com/ultralytics/ultralytics/рдмреВрдБрдж/рдореБрдЦреНрдп/ultralytics/models/utils/loss.py рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдпрджрд┐ рдЖрдк рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рджреЗрдЦрддреЗ рд╣реИрдВ рддреЛ рдХреГрдкрдпрд╛ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдХрд╛ рдпреЛрдЧрджрд╛рди рдХрд░рдХреЗ рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдВ ЁЯЫая╕Пред ЁЯЩП рдзрдиреНрдпрд╡рд╛рдж !



ultralytics.models.utils.loss.DETRLoss

рдХрд╛ рд░реВрдк: Module

DETR (DEtection TRansformer) рд╣рд╛рдирд┐ рд╡рд░реНрдЧред рдпрд╣ рд╡рд░реНрдЧ рдЧрдгрдирд╛ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреЗ рд▓рд┐рдП рд╡рд┐рднрд┐рдиреНрди рд╣рд╛рдирд┐ рдШрдЯрдХреЛрдВ рдХреЛ рд▓реМрдЯрд╛рддрд╛ рд╣реИ DETR рдСрдмреНрдЬреЗрдХреНрдЯ рдбрд┐рдЯреЗрдХреНрд╢рди рдореЙрдбрд▓ред рдпрд╣ рд╡рд░реНрдЧреАрдХрд░рдг рд╣рд╛рдирд┐, рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рд╣рд╛рдирд┐, GIoU рд╣рд╛рдирд┐ рдФрд░ рд╡реИрдХрд▓реНрдкрд┐рдХ рд░реВрдк рд╕реЗ рд╕рд╣рд╛рдпрдХ рдХреА рдЧрдгрдирд╛ рдХрд░рддрд╛ рд╣реИ рдШрд╛рдЯрд╛ред

рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдБ:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
nc int

рдХрдХреНрд╖рд╛рдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛.

loss_gain dict

рд╡рд┐рднрд┐рдиреНрди рд╣рд╛рдирд┐ рдШрдЯрдХреЛрдВ рдХреЗ рд▓рд┐рдП рдЧреБрдгрд╛рдВрдХред

aux_loss bool

рдХреНрдпрд╛ рд╕рд╣рд╛рдпрдХ рдиреБрдХрд╕рд╛рди рдХреА рдЧрдгрдирд╛ рдХрд░рдирд╛ рд╣реИред

use_fl bool

FocalLoss рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдпрд╛ рдирд╣реАрдВред

use_vfl bool

VarifocalLoss рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдпрд╛ рдирд╣реАрдВред

use_uni_match bool

рд╕рд╣рд╛рдпрдХ рд╢рд╛рдЦрд╛ рдХреЗ рд▓рд┐рдП рд▓реЗрдмрд▓ рдЕрд╕рд╛рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкрд░рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред

uni_match_ind int

рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкрд░рдд рдХреЗ рдирд┐рд╢реНрдЪрд┐рдд рд╕реВрдЪрдХрд╛рдВрдХ рдпрджрд┐ use_uni_match рд╕рддреНрдп рд╣реИред

matcher HungarianMatcher

рдорд┐рд▓рд╛рди рд▓рд╛рдЧрдд рдФрд░ рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдкрд░ рдЖрдкрддреНрддрд┐ред

fl FocalLoss or None

рдлреЛрдХрд▓ рд▓реЙрд╕ рдСрдмреНрдЬреЗрдХреНрдЯ рдЕрдЧрд░ use_fl рд╕рдЪ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдХреЛрдИ рдирд╣реАрдВред

vfl VarifocalLoss or None

Varifocal рд╣рд╛рдирд┐ рд╡рд╕реНрддреБ рдпрджрд┐ use_vfl рд╕рдЪ рд╣реИ, рдЕрдиреНрдпрдерд╛ рдХреЛрдИ рдирд╣реАрдВред

device device

рдЙрдкрдХрд░рдг рдЬрд┐рд╕ рдкрд░ рдЯреЗрдВрд╕рд░ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
class DETRLoss(nn.Module):
    """
    DETR (DEtection TRansformer) Loss class. This class calculates and returns the different loss components for the
    DETR object detection model. It computes classification loss, bounding box loss, GIoU loss, and optionally auxiliary
    losses.

    Attributes:
        nc (int): The number of classes.
        loss_gain (dict): Coefficients for different loss components.
        aux_loss (bool): Whether to compute auxiliary losses.
        use_fl (bool): Use FocalLoss or not.
        use_vfl (bool): Use VarifocalLoss or not.
        use_uni_match (bool): Whether to use a fixed layer to assign labels for the auxiliary branch.
        uni_match_ind (int): The fixed indices of a layer to use if `use_uni_match` is True.
        matcher (HungarianMatcher): Object to compute matching cost and indices.
        fl (FocalLoss or None): Focal Loss object if `use_fl` is True, otherwise None.
        vfl (VarifocalLoss or None): Varifocal Loss object if `use_vfl` is True, otherwise None.
        device (torch.device): Device on which tensors are stored.
    """

    def __init__(
        self, nc=80, loss_gain=None, aux_loss=True, use_fl=True, use_vfl=False, use_uni_match=False, uni_match_ind=0
    ):
        """
        DETR loss function.

        Args:
            nc (int): The number of classes.
            loss_gain (dict): The coefficient of loss.
            aux_loss (bool): If 'aux_loss = True', loss at each decoder layer are to be used.
            use_vfl (bool): Use VarifocalLoss or not.
            use_uni_match (bool): Whether to use a fixed layer to assign labels for auxiliary branch.
            uni_match_ind (int): The fixed indices of a layer.
        """
        super().__init__()

        if loss_gain is None:
            loss_gain = {"class": 1, "bbox": 5, "giou": 2, "no_object": 0.1, "mask": 1, "dice": 1}
        self.nc = nc
        self.matcher = HungarianMatcher(cost_gain={"class": 2, "bbox": 5, "giou": 2})
        self.loss_gain = loss_gain
        self.aux_loss = aux_loss
        self.fl = FocalLoss() if use_fl else None
        self.vfl = VarifocalLoss() if use_vfl else None

        self.use_uni_match = use_uni_match
        self.uni_match_ind = uni_match_ind
        self.device = None

    def _get_loss_class(self, pred_scores, targets, gt_scores, num_gts, postfix=""):
        """Computes the classification loss based on predictions, target values, and ground truth scores."""
        # Logits: [b, query, num_classes], gt_class: list[[n, 1]]
        name_class = f"loss_class{postfix}"
        bs, nq = pred_scores.shape[:2]
        # one_hot = F.one_hot(targets, self.nc + 1)[..., :-1]  # (bs, num_queries, num_classes)
        one_hot = torch.zeros((bs, nq, self.nc + 1), dtype=torch.int64, device=targets.device)
        one_hot.scatter_(2, targets.unsqueeze(-1), 1)
        one_hot = one_hot[..., :-1]
        gt_scores = gt_scores.view(bs, nq, 1) * one_hot

        if self.fl:
            if num_gts and self.vfl:
                loss_cls = self.vfl(pred_scores, gt_scores, one_hot)
            else:
                loss_cls = self.fl(pred_scores, one_hot.float())
            loss_cls /= max(num_gts, 1) / nq
        else:
            loss_cls = nn.BCEWithLogitsLoss(reduction="none")(pred_scores, gt_scores).mean(1).sum()  # YOLO CLS loss

        return {name_class: loss_cls.squeeze() * self.loss_gain["class"]}

    def _get_loss_bbox(self, pred_bboxes, gt_bboxes, postfix=""):
        """Calculates and returns the bounding box loss and GIoU loss for the predicted and ground truth bounding
        boxes.
        """
        # Boxes: [b, query, 4], gt_bbox: list[[n, 4]]
        name_bbox = f"loss_bbox{postfix}"
        name_giou = f"loss_giou{postfix}"

        loss = {}
        if len(gt_bboxes) == 0:
            loss[name_bbox] = torch.tensor(0.0, device=self.device)
            loss[name_giou] = torch.tensor(0.0, device=self.device)
            return loss

        loss[name_bbox] = self.loss_gain["bbox"] * F.l1_loss(pred_bboxes, gt_bboxes, reduction="sum") / len(gt_bboxes)
        loss[name_giou] = 1.0 - bbox_iou(pred_bboxes, gt_bboxes, xywh=True, GIoU=True)
        loss[name_giou] = loss[name_giou].sum() / len(gt_bboxes)
        loss[name_giou] = self.loss_gain["giou"] * loss[name_giou]
        return {k: v.squeeze() for k, v in loss.items()}

    # This function is for future RT-DETR Segment models
    # def _get_loss_mask(self, masks, gt_mask, match_indices, postfix=''):
    #     # masks: [b, query, h, w], gt_mask: list[[n, H, W]]
    #     name_mask = f'loss_mask{postfix}'
    #     name_dice = f'loss_dice{postfix}'
    #
    #     loss = {}
    #     if sum(len(a) for a in gt_mask) == 0:
    #         loss[name_mask] = torch.tensor(0., device=self.device)
    #         loss[name_dice] = torch.tensor(0., device=self.device)
    #         return loss
    #
    #     num_gts = len(gt_mask)
    #     src_masks, target_masks = self._get_assigned_bboxes(masks, gt_mask, match_indices)
    #     src_masks = F.interpolate(src_masks.unsqueeze(0), size=target_masks.shape[-2:], mode='bilinear')[0]
    #     # TODO: torch does not have `sigmoid_focal_loss`, but it's not urgent since we don't use mask branch for now.
    #     loss[name_mask] = self.loss_gain['mask'] * F.sigmoid_focal_loss(src_masks, target_masks,
    #                                                                     torch.tensor([num_gts], dtype=torch.float32))
    #     loss[name_dice] = self.loss_gain['dice'] * self._dice_loss(src_masks, target_masks, num_gts)
    #     return loss

    # This function is for future RT-DETR Segment models
    # @staticmethod
    # def _dice_loss(inputs, targets, num_gts):
    #     inputs = F.sigmoid(inputs).flatten(1)
    #     targets = targets.flatten(1)
    #     numerator = 2 * (inputs * targets).sum(1)
    #     denominator = inputs.sum(-1) + targets.sum(-1)
    #     loss = 1 - (numerator + 1) / (denominator + 1)
    #     return loss.sum() / num_gts

    def _get_loss_aux(
        self,
        pred_bboxes,
        pred_scores,
        gt_bboxes,
        gt_cls,
        gt_groups,
        match_indices=None,
        postfix="",
        masks=None,
        gt_mask=None,
    ):
        """Get auxiliary losses."""
        # NOTE: loss class, bbox, giou, mask, dice
        loss = torch.zeros(5 if masks is not None else 3, device=pred_bboxes.device)
        if match_indices is None and self.use_uni_match:
            match_indices = self.matcher(
                pred_bboxes[self.uni_match_ind],
                pred_scores[self.uni_match_ind],
                gt_bboxes,
                gt_cls,
                gt_groups,
                masks=masks[self.uni_match_ind] if masks is not None else None,
                gt_mask=gt_mask,
            )
        for i, (aux_bboxes, aux_scores) in enumerate(zip(pred_bboxes, pred_scores)):
            aux_masks = masks[i] if masks is not None else None
            loss_ = self._get_loss(
                aux_bboxes,
                aux_scores,
                gt_bboxes,
                gt_cls,
                gt_groups,
                masks=aux_masks,
                gt_mask=gt_mask,
                postfix=postfix,
                match_indices=match_indices,
            )
            loss[0] += loss_[f"loss_class{postfix}"]
            loss[1] += loss_[f"loss_bbox{postfix}"]
            loss[2] += loss_[f"loss_giou{postfix}"]
            # if masks is not None and gt_mask is not None:
            #     loss_ = self._get_loss_mask(aux_masks, gt_mask, match_indices, postfix)
            #     loss[3] += loss_[f'loss_mask{postfix}']
            #     loss[4] += loss_[f'loss_dice{postfix}']

        loss = {
            f"loss_class_aux{postfix}": loss[0],
            f"loss_bbox_aux{postfix}": loss[1],
            f"loss_giou_aux{postfix}": loss[2],
        }
        # if masks is not None and gt_mask is not None:
        #     loss[f'loss_mask_aux{postfix}'] = loss[3]
        #     loss[f'loss_dice_aux{postfix}'] = loss[4]
        return loss

    @staticmethod
    def _get_index(match_indices):
        """Returns batch indices, source indices, and destination indices from provided match indices."""
        batch_idx = torch.cat([torch.full_like(src, i) for i, (src, _) in enumerate(match_indices)])
        src_idx = torch.cat([src for (src, _) in match_indices])
        dst_idx = torch.cat([dst for (_, dst) in match_indices])
        return (batch_idx, src_idx), dst_idx

    def _get_assigned_bboxes(self, pred_bboxes, gt_bboxes, match_indices):
        """Assigns predicted bounding boxes to ground truth bounding boxes based on the match indices."""
        pred_assigned = torch.cat(
            [
                t[i] if len(i) > 0 else torch.zeros(0, t.shape[-1], device=self.device)
                for t, (i, _) in zip(pred_bboxes, match_indices)
            ]
        )
        gt_assigned = torch.cat(
            [
                t[j] if len(j) > 0 else torch.zeros(0, t.shape[-1], device=self.device)
                for t, (_, j) in zip(gt_bboxes, match_indices)
            ]
        )
        return pred_assigned, gt_assigned

    def _get_loss(
        self,
        pred_bboxes,
        pred_scores,
        gt_bboxes,
        gt_cls,
        gt_groups,
        masks=None,
        gt_mask=None,
        postfix="",
        match_indices=None,
    ):
        """Get losses."""
        if match_indices is None:
            match_indices = self.matcher(
                pred_bboxes, pred_scores, gt_bboxes, gt_cls, gt_groups, masks=masks, gt_mask=gt_mask
            )

        idx, gt_idx = self._get_index(match_indices)
        pred_bboxes, gt_bboxes = pred_bboxes[idx], gt_bboxes[gt_idx]

        bs, nq = pred_scores.shape[:2]
        targets = torch.full((bs, nq), self.nc, device=pred_scores.device, dtype=gt_cls.dtype)
        targets[idx] = gt_cls[gt_idx]

        gt_scores = torch.zeros([bs, nq], device=pred_scores.device)
        if len(gt_bboxes):
            gt_scores[idx] = bbox_iou(pred_bboxes.detach(), gt_bboxes, xywh=True).squeeze(-1)

        loss = {}
        loss.update(self._get_loss_class(pred_scores, targets, gt_scores, len(gt_bboxes), postfix))
        loss.update(self._get_loss_bbox(pred_bboxes, gt_bboxes, postfix))
        # if masks is not None and gt_mask is not None:
        #     loss.update(self._get_loss_mask(masks, gt_mask, match_indices, postfix))
        return loss

    def forward(self, pred_bboxes, pred_scores, batch, postfix="", **kwargs):
        """
        Args:
            pred_bboxes (torch.Tensor): [l, b, query, 4]
            pred_scores (torch.Tensor): [l, b, query, num_classes]
            batch (dict): A dict includes:
                gt_cls (torch.Tensor) with shape [num_gts, ],
                gt_bboxes (torch.Tensor): [num_gts, 4],
                gt_groups (List(int)): a list of batch size length includes the number of gts of each image.
            postfix (str): postfix of loss name.
        """
        self.device = pred_bboxes.device
        match_indices = kwargs.get("match_indices", None)
        gt_cls, gt_bboxes, gt_groups = batch["cls"], batch["bboxes"], batch["gt_groups"]

        total_loss = self._get_loss(
            pred_bboxes[-1], pred_scores[-1], gt_bboxes, gt_cls, gt_groups, postfix=postfix, match_indices=match_indices
        )

        if self.aux_loss:
            total_loss.update(
                self._get_loss_aux(
                    pred_bboxes[:-1], pred_scores[:-1], gt_bboxes, gt_cls, gt_groups, match_indices, postfix
                )
            )

        return total_loss

__init__(nc=80, loss_gain=None, aux_loss=True, use_fl=True, use_vfl=False, use_uni_match=False, uni_match_ind=0)

DETR рдиреБрдХрд╕рд╛рди рд╕рдорд╛рд░реЛрд╣ред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
nc int

рдХрдХреНрд╖рд╛рдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛.

80
loss_gain dict

рд╣рд╛рдирд┐ рдХрд╛ рдЧреБрдгрд╛рдВрдХред

None
aux_loss bool

рдпрджрд┐ 'aux_loss = рд╕рддреНрдп' рд╣реИ, рддреЛ рдкреНрд░рддреНрдпреЗрдХ рдбрд┐рдХреЛрдбрд░ рдкрд░рдд рдкрд░ рдиреБрдХрд╕рд╛рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╣реИред

True
use_vfl bool

VarifocalLoss рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдпрд╛ рдирд╣реАрдВред

False
use_uni_match bool

рд╕рд╣рд╛рдпрдХ рд╢рд╛рдЦрд╛ рдХреЗ рд▓рд┐рдП рд▓реЗрдмрд▓ рдЕрд╕рд╛рдЗрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкрд░рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВред

False
uni_match_ind int

рдПрдХ рдкрд░рдд рдХреЗ рдирд┐рд╢реНрдЪрд┐рдд рд╕реВрдЪрдХрд╛рдВрдХред

0
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
def __init__(
    self, nc=80, loss_gain=None, aux_loss=True, use_fl=True, use_vfl=False, use_uni_match=False, uni_match_ind=0
):
    """
    DETR loss function.

    Args:
        nc (int): The number of classes.
        loss_gain (dict): The coefficient of loss.
        aux_loss (bool): If 'aux_loss = True', loss at each decoder layer are to be used.
        use_vfl (bool): Use VarifocalLoss or not.
        use_uni_match (bool): Whether to use a fixed layer to assign labels for auxiliary branch.
        uni_match_ind (int): The fixed indices of a layer.
    """
    super().__init__()

    if loss_gain is None:
        loss_gain = {"class": 1, "bbox": 5, "giou": 2, "no_object": 0.1, "mask": 1, "dice": 1}
    self.nc = nc
    self.matcher = HungarianMatcher(cost_gain={"class": 2, "bbox": 5, "giou": 2})
    self.loss_gain = loss_gain
    self.aux_loss = aux_loss
    self.fl = FocalLoss() if use_fl else None
    self.vfl = VarifocalLoss() if use_vfl else None

    self.use_uni_match = use_uni_match
    self.uni_match_ind = uni_match_ind
    self.device = None

forward(pred_bboxes, pred_scores, batch, postfix='', **kwargs)

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
pred_bboxes Tensor

[рдПрд▓, рдмреА, рдХреНрд╡реЗрд░реА, 4]

рдЖрд╡рд╢реНрдпрдХ
pred_scores Tensor

[рдПрд▓, рдмреА, рдХреНрд╡реЗрд░реА, num_classes]

рдЖрд╡рд╢реНрдпрдХ
batch dict

рдПрдХ рдбрд┐рдХреНрдЯ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ: gt_cls (torch.Tensor) рдЖрдХрд╛рд░ рдХреЗ рд╕рд╛рде [num_gts,], gt_bboxes (torch.Tensor): [num_gts, 4], gt_groups (рд╕реВрдЪреА (int)): рдмреИрдЪ рдЖрдХрд╛рд░ рдХреА рд▓рдВрдмрд╛рдИ рдХреА рд╕реВрдЪреА рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдЫрд╡рд┐ рдХреЗ gts рдХреА рд╕рдВрдЦреНрдпрд╛ рд╢рд╛рдорд┐рд▓ рд╣реЛрддреА рд╣реИред

рдЖрд╡рд╢реНрдпрдХ
postfix str

рдиреБрдХрд╕рд╛рди рдХреЗ рдирд╛рдо рдХрд╛ рдкреЛрд╕реНрдЯрдлрд┐рдХреНрд╕ред

''
рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
def forward(self, pred_bboxes, pred_scores, batch, postfix="", **kwargs):
    """
    Args:
        pred_bboxes (torch.Tensor): [l, b, query, 4]
        pred_scores (torch.Tensor): [l, b, query, num_classes]
        batch (dict): A dict includes:
            gt_cls (torch.Tensor) with shape [num_gts, ],
            gt_bboxes (torch.Tensor): [num_gts, 4],
            gt_groups (List(int)): a list of batch size length includes the number of gts of each image.
        postfix (str): postfix of loss name.
    """
    self.device = pred_bboxes.device
    match_indices = kwargs.get("match_indices", None)
    gt_cls, gt_bboxes, gt_groups = batch["cls"], batch["bboxes"], batch["gt_groups"]

    total_loss = self._get_loss(
        pred_bboxes[-1], pred_scores[-1], gt_bboxes, gt_cls, gt_groups, postfix=postfix, match_indices=match_indices
    )

    if self.aux_loss:
        total_loss.update(
            self._get_loss_aux(
                pred_bboxes[:-1], pred_scores[:-1], gt_bboxes, gt_cls, gt_groups, match_indices, postfix
            )
        )

    return total_loss



ultralytics.models.utils.loss.RTDETRDetectionLoss

рдХрд╛ рд░реВрдк: DETRLoss

рд░реАрдпрд▓-рдЯрд╛рдЗрдо рдбреАрдкрдЯреНрд░реИрдХрд░ (RT-DETR) рдбрд┐рдЯреЗрдХреНрд╢рди рд▓реЙрд╕ рдХреНрд▓рд╛рд╕ рдЬреЛ DETRLoss рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рддрд╛ рд╣реИред

рдпрд╣ рд╡рд░реНрдЧ рдХреЗ рд▓рд┐рдП рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рдиреБрдХрд╕рд╛рди рдХреА рдЧрдгрдирд╛ рдХрд░рддрд╛ рд╣реИ RT-DETR рдореЙрдбрд▓, рдЬрд┐рд╕рдореЗрдВ рдорд╛рдирдХ рдкрд╣рдЪрд╛рди рд╣рд╛рдирд┐ рдХреЗ рд╕рд╛рде-рд╕рд╛рде рдореЗрдЯрд╛рдбреЗрдЯрд╛ рдХреЛ рдбреАрдиреЛрдЗрдЬрд╝ рдХрд░рдиреЗ рдХреЗ рд╕рд╛рде рдкреНрд░рджрд╛рди рдХрд┐рдП рдЬрд╛рдиреЗ рдкрд░ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдбреАрдиреЛрдЗрдЬрд╝рд┐рдВрдЧ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд╣рд╛рдирд┐ред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
class RTDETRDetectionLoss(DETRLoss):
    """
    Real-Time DeepTracker (RT-DETR) Detection Loss class that extends the DETRLoss.

    This class computes the detection loss for the RT-DETR model, which includes the standard detection loss as well as
    an additional denoising training loss when provided with denoising metadata.
    """

    def forward(self, preds, batch, dn_bboxes=None, dn_scores=None, dn_meta=None):
        """
        Forward pass to compute the detection loss.

        Args:
            preds (tuple): Predicted bounding boxes and scores.
            batch (dict): Batch data containing ground truth information.
            dn_bboxes (torch.Tensor, optional): Denoising bounding boxes. Default is None.
            dn_scores (torch.Tensor, optional): Denoising scores. Default is None.
            dn_meta (dict, optional): Metadata for denoising. Default is None.

        Returns:
            (dict): Dictionary containing the total loss and, if applicable, the denoising loss.
        """
        pred_bboxes, pred_scores = preds
        total_loss = super().forward(pred_bboxes, pred_scores, batch)

        # Check for denoising metadata to compute denoising training loss
        if dn_meta is not None:
            dn_pos_idx, dn_num_group = dn_meta["dn_pos_idx"], dn_meta["dn_num_group"]
            assert len(batch["gt_groups"]) == len(dn_pos_idx)

            # Get the match indices for denoising
            match_indices = self.get_dn_match_indices(dn_pos_idx, dn_num_group, batch["gt_groups"])

            # Compute the denoising training loss
            dn_loss = super().forward(dn_bboxes, dn_scores, batch, postfix="_dn", match_indices=match_indices)
            total_loss.update(dn_loss)
        else:
            # If no denoising metadata is provided, set denoising loss to zero
            total_loss.update({f"{k}_dn": torch.tensor(0.0, device=self.device) for k in total_loss.keys()})

        return total_loss

    @staticmethod
    def get_dn_match_indices(dn_pos_idx, dn_num_group, gt_groups):
        """
        Get the match indices for denoising.

        Args:
            dn_pos_idx (List[torch.Tensor]): List of tensors containing positive indices for denoising.
            dn_num_group (int): Number of denoising groups.
            gt_groups (List[int]): List of integers representing the number of ground truths for each image.

        Returns:
            (List[tuple]): List of tuples containing matched indices for denoising.
        """
        dn_match_indices = []
        idx_groups = torch.as_tensor([0, *gt_groups[:-1]]).cumsum_(0)
        for i, num_gt in enumerate(gt_groups):
            if num_gt > 0:
                gt_idx = torch.arange(end=num_gt, dtype=torch.long) + idx_groups[i]
                gt_idx = gt_idx.repeat(dn_num_group)
                assert len(dn_pos_idx[i]) == len(gt_idx), "Expected the same length, "
                f"but got {len(dn_pos_idx[i])} and {len(gt_idx)} respectively."
                dn_match_indices.append((dn_pos_idx[i], gt_idx))
            else:
                dn_match_indices.append((torch.zeros([0], dtype=torch.long), torch.zeros([0], dtype=torch.long)))
        return dn_match_indices

forward(preds, batch, dn_bboxes=None, dn_scores=None, dn_meta=None)

рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреЗ рдиреБрдХрд╕рд╛рди рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлреЙрд░рд╡рд░реНрдб рдкрд╛рд╕ред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
preds tuple

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдФрд░ рд╕реНрдХреЛрд░ рдХреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдХреАред

рдЖрд╡рд╢реНрдпрдХ
batch dict

рдЬрдореАрдиреА рд╕рдЪреНрдЪрд╛рдИ рдХреА рдЬрд╛рдирдХрд╛рд░реА рд╡рд╛рд▓рд╛ рдмреИрдЪ рдбреЗрдЯрд╛ред

рдЖрд╡рд╢реНрдпрдХ
dn_bboxes Tensor

рдмрд╛рдЙрдВрдбрд┐рдВрдЧ рдмреЙрдХреНрд╕ рдХреЛ рдбреАрдиреЛрдЗрдЬрд╝ рдХрд░рдирд╛ред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЛрдИ рдирд╣реАрдВ рд╣реИред

None
dn_scores Tensor

рд╕реНрдХреЛрд░ рдХреЛ рдХрдо рдХрд░рдирд╛ред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЛрдИ рдирд╣реАрдВ рд╣реИред

None
dn_meta dict

denoising рдХреЗ рд▓рд┐рдП рдореЗрдЯрд╛рдбреЗрдЯрд╛ред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЛрдИ рдирд╣реАрдВ рд╣реИред

None

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
dict

рд╢рдмреНрджрдХреЛрд╢ рдЬрд┐рд╕рдореЗрдВ рдХреБрд▓ рдиреБрдХрд╕рд╛рди рдФрд░, рдпрджрд┐ рд▓рд╛рдЧреВ рд╣реЛ, рддреЛ рдбреАрдиреЛрдЗрдЬрд╝рд┐рдВрдЧ рд▓реЙрд╕ рд╢рд╛рдорд┐рд▓ рд╣реИред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
def forward(self, preds, batch, dn_bboxes=None, dn_scores=None, dn_meta=None):
    """
    Forward pass to compute the detection loss.

    Args:
        preds (tuple): Predicted bounding boxes and scores.
        batch (dict): Batch data containing ground truth information.
        dn_bboxes (torch.Tensor, optional): Denoising bounding boxes. Default is None.
        dn_scores (torch.Tensor, optional): Denoising scores. Default is None.
        dn_meta (dict, optional): Metadata for denoising. Default is None.

    Returns:
        (dict): Dictionary containing the total loss and, if applicable, the denoising loss.
    """
    pred_bboxes, pred_scores = preds
    total_loss = super().forward(pred_bboxes, pred_scores, batch)

    # Check for denoising metadata to compute denoising training loss
    if dn_meta is not None:
        dn_pos_idx, dn_num_group = dn_meta["dn_pos_idx"], dn_meta["dn_num_group"]
        assert len(batch["gt_groups"]) == len(dn_pos_idx)

        # Get the match indices for denoising
        match_indices = self.get_dn_match_indices(dn_pos_idx, dn_num_group, batch["gt_groups"])

        # Compute the denoising training loss
        dn_loss = super().forward(dn_bboxes, dn_scores, batch, postfix="_dn", match_indices=match_indices)
        total_loss.update(dn_loss)
    else:
        # If no denoising metadata is provided, set denoising loss to zero
        total_loss.update({f"{k}_dn": torch.tensor(0.0, device=self.device) for k in total_loss.keys()})

    return total_loss

get_dn_match_indices(dn_pos_idx, dn_num_group, gt_groups) staticmethod

denoising рдХреЗ рд▓рд┐рдП рдореИрдЪ рд╕реВрдЪрдХрд╛рдВрдХ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред

рдкреИрд░рд╛рдореАрдЯрд░:

рдирд╛рдо рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо рдЪреВрдХ
dn_pos_idx List[Tensor]

рдбреАрдиреЛрдЗрдЬрд╝рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рд╕рдХрд╛рд░рд╛рддреНрдордХ рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рд╡рд╛рд▓реЗ рдЯреЗрдВрд╕рд░ рдХреА рд╕реВрдЪреАред

рдЖрд╡рд╢реНрдпрдХ
dn_num_group int

рд╕рдВрдкреНрд░рджрд╛рдп рд╕рдореВрд╣реЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ред

рдЖрд╡рд╢реНрдпрдХ
gt_groups List[int]

рдкреНрд░рддреНрдпреЗрдХ рдЫрд╡рд┐ рдХреЗ рд▓рд┐рдП рдЬрдореАрдиреА рд╕рддреНрдп рдХреА рд╕рдВрдЦреНрдпрд╛ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдкреВрд░реНрдгрд╛рдВрдХреЛрдВ рдХреА рд╕реВрдЪреАред

рдЖрд╡рд╢реНрдпрдХ

рджреЗрддрд╛:

рдкреНрд░рдХрд╛рд░ рдпрд╛ рдХрд╝рд┐рд╕реНтАНрдо
List[tuple]

рдбреАрдиреЛрдЗрдЬрд╝рд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдорд┐рд▓рд╛рди рдХрд┐рдП рдЧрдП рд╕реВрдЪрдХрд╛рдВрдХреЛрдВ рд╡рд╛рд▓реЗ рдЯреБрдкрд▓реНрд╕ рдХреА рд╕реВрдЪреАред

рдореЗрдВ рд╕реНрд░реЛрдд рдХреЛрдб ultralytics/models/utils/loss.py
@staticmethod
def get_dn_match_indices(dn_pos_idx, dn_num_group, gt_groups):
    """
    Get the match indices for denoising.

    Args:
        dn_pos_idx (List[torch.Tensor]): List of tensors containing positive indices for denoising.
        dn_num_group (int): Number of denoising groups.
        gt_groups (List[int]): List of integers representing the number of ground truths for each image.

    Returns:
        (List[tuple]): List of tuples containing matched indices for denoising.
    """
    dn_match_indices = []
    idx_groups = torch.as_tensor([0, *gt_groups[:-1]]).cumsum_(0)
    for i, num_gt in enumerate(gt_groups):
        if num_gt > 0:
            gt_idx = torch.arange(end=num_gt, dtype=torch.long) + idx_groups[i]
            gt_idx = gt_idx.repeat(dn_num_group)
            assert len(dn_pos_idx[i]) == len(gt_idx), "Expected the same length, "
            f"but got {len(dn_pos_idx[i])} and {len(gt_idx)} respectively."
            dn_match_indices.append((dn_pos_idx[i], gt_idx))
        else:
            dn_match_indices.append((torch.zeros([0], dtype=torch.long), torch.zeros([0], dtype=torch.long)))
    return dn_match_indices





2023-11-12 рдмрдирд╛рдпрд╛ рдЧрдпрд╛, рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ 2024-05-08
рд▓реЗрдЦрдХ: рдмреБрд░рд╣рд╛рди-рдХреНрдпреВ (1), рдЧреНрд▓реЗрди-рдЬреЛрдЪрд░ (3), рд▓рд╛рдлрд┐рдВрдЧ-рдХреНрдпреВ (1)