Spaces:
Runtime error
Runtime error
File size: 6,167 Bytes
749745d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# Copyright (c) Aishwarya Kamath & Nicolas Carion. Licensed under the Apache License 2.0. All Rights Reserved
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
"""
Modules to compute the matching cost and solve the corresponding LSAP.
"""
import torch
from scipy.optimize import linear_sum_assignment
from torch import nn
import pdb
from maskrcnn_benchmark.layers.set_loss import generalized_box_iou, box_iou
class HungarianMatcherCustom(nn.Module):
"""This class computes an assignment between the targets and the predictions of the network
For efficiency reasons, the targets don't include the no_object. Because of this, in general,
there are more predictions than targets. In this case, we do a 1-to-1 matching of the best predictions,
while the others are un-matched (and thus treated as non-objects).
"""
def __init__(self, cost_class: float = 1, cost_bbox: float = 1, cost_giou: float = 1, special = False):
"""Creates the matcher
Params:
cost_class: This is the relative weight of the classification error in the matching cost
cost_bbox: This is the relative weight of the L1 error of the bounding box coordinates in the matching cost
cost_giou: This is the relative weight of the giou loss of the bounding box in the matching cost
"""
super().__init__()
self.cost_class = cost_class
self.cost_bbox = cost_bbox
self.cost_giou = cost_giou
self.norm = nn.Softmax(-1)
self.special = special
assert cost_class != 0 or cost_bbox != 0 or cost_giou != 0, "all costs cant be 0"
@torch.no_grad()
def forward(self, outputs, targets):
"""Performs the matching
Params:
outputs: This is a dict that contains at least these entries:
"pred_logits": Tensor of dim [batch_size, num_queries, num_classes] with the classification logits
"pred_boxes": Tensor of dim [batch_size, num_queries, 4] with the predicted box coordinates
targets: This is a list of targets (len(targets) = batch_size), where each target is a dict containing:
"labels": Tensor of dim [num_target_boxes] (where num_target_boxes is the number of ground-truth
objects in the target) containing the class labels
"boxes": Tensor of dim [num_target_boxes, 4] containing the target box coordinates
Returns:
A list of size batch_size, containing tuples of (index_i, index_j) where:
- index_i is the indices of the selected predictions (in order)
- index_j is the indices of the corresponding selected targets (in order)
For each batch element, it holds:
len(index_i) = len(index_j) = min(num_queries, num_target_boxes)
"""
bs, num_queries = outputs["pred_logits"].shape[:2]
# We flatten to compute the cost matrices in a batch
out_prob = outputs["pred_logits"].flatten(0, 1) # [batch_size * num_queries, num_classes]
# out_prob_bg = 1 - out_prob
# out_prob = torch.cat([out_prob_bg, out_prob], dim = 1)
out_bbox = outputs["pred_boxes"].flatten(0, 1) # [batch_size * num_queries, 4]
# Also concat the target labels and boxes
tgt_bbox = targets["pred_boxes"].flatten(0, 1) # [batch_size * num_target_boxes, 4]
tgt_prob = targets["pred_logits"].flatten(0, 1) # [batch_size * num_target_boxes, num_classes]
# tgt_prob_bg = 1 - tgt_prob
# tgt_prob = torch.cat([tgt_prob_bg, tgt_prob], dim = 1)
# Compute the soft-cross entropy between the predicted token alignment and the GT one for each box
# import pdb
cost_class = out_prob - tgt_prob.transpose(0,1)
cost_class = cost_class.abs()
# Compute the L1 cost between boxes
cost_bbox = torch.cdist(out_bbox, tgt_bbox, p=1)
# Compute the giou cost betwen boxes
# cost_giou = -generalized_box_iou(box_cxcywh_to_xyxy(out_bbox), box_cxcywh_to_xyxy(tgt_bbox))
cost_giou, _ = box_iou(out_bbox, tgt_bbox)
cost_giou = -cost_giou
# Final cost matrix
C = self.cost_bbox * cost_bbox + self.cost_class * cost_class + self.cost_giou * cost_giou
C = C.view(bs, num_queries, -1).cpu()
C_class = cost_class
C_class = C_class.view(bs, num_queries, -1).cpu()
C_bbox = cost_bbox
C_bbox = C_bbox.view(bs, num_queries, -1).cpu()
#C[torch.isnan(C)] = 0.0
#C[torch.isinf(C)] = 0.0
#print(C)
sizes = [tgt_bbox.size(0)] # assum b = 1
indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(sizes, -1))]
assignment = [(torch.as_tensor(i, dtype=torch.int64), torch.as_tensor(j, dtype=torch.int64)) for i, j in indices]
# calculate the total cost;
assignment = assignment[0]
C = C[0]
C_class = C_class[0]
C_bbox = C_bbox[0]
cost = 0
selected_entries = []
cost_class = 0
cost_bbox = 0
cost_matched_box = 0
if self.special: # calculate the difference between boxes
for first_index, second_index in zip(assignment[0], assignment[1]):
if -C[first_index, second_index] > 0.5:
cost += C_class[first_index, second_index]
selected_entries.append(C[first_index, second_index])
cost_class += C_class[first_index, second_index]
cost_bbox += C_bbox[first_index, second_index]
else:
for first_index, second_index in zip(assignment[0], assignment[1]):
cost += C[first_index, second_index]
selected_entries.append(C[first_index, second_index])
cost_class += C_class[first_index, second_index]
cost_bbox += C_bbox[first_index, second_index]
print(selected_entries, cost)
return cost, len(selected_entries), selected_entries, cost_class, cost_bbox |