Ejemplo n.º 1
0
    def forward_multi_candidate(self, l: torch.LongTensor,
                                rs: torch.LongTensor) -> torch.Tensor:
        batch_size = rs.size(0)
        cand_size = rs.size(1)

        if rs.ndimension() == 3:
            rx = rs.view(batch_size * cand_size, -1)  # L[Batch*Cand, Word]
        elif rs.ndimension() == 4:
            rx = rs.view(batch_size * cand_size, rs.size(2),
                         rs.size(3))  # L[Batch*Cand, Word, Char]

        l_embedded, l_mask = self.l_embedding(
            l)  # F[Batch, Word, Embedding], B[Batch, Word]
        rx_embedded, rx_mask = self.r_embedding(
            rx)  # F[Batch*Cand, Word, Embedding], B[Batch*Cand, Word]

        l_encoded = self.l_encoder(l_embedded, l_mask,
                                   True)  # F[Batch, Feature]
        rx_encoded = self.r_encoder(rx_embedded,
                                    rx_mask)  # F[Batch*Cand, Feature]
        lx_encoded = l_encoded.unsqueeze(dim=1) \
            .expand(batch_size, cand_size, -1) \
            .view(batch_size * cand_size, -1)  # F[Batch*Cand, Feature]

        x_joined = self.joiner(lx_encoded,
                               rx_encoded)  # F[Batch*Cand, Feature]
        joined = x_joined.view(batch_size, cand_size,
                               -1)  # F[Batch, Cand, Feature]
        return joined
Ejemplo n.º 2
0
def make_one_hot(
    labels: torch.LongTensor, 
    C=2,
) -> torch.FloatTensor:
    '''
    Converts an integer label torch.autograd.Variable to a one-hot Variable.

    Parameters
    ----------
    labels : torch.LongTensor or torch.cuda.LongTensor
        [N, 1], where N is batch size.
        Each value is an integer representing correct classification.
    C : int
        number of classes in labels.

    Returns
    -------
    target : torch.FloatTensor or torch.cuda.FloatTensor
        [N, C,], where C is class number. One-hot encoded.
    '''
    if labels.ndimension() < 2:
        labels = labels.unsqueeze(1)
    one_hot = torch.zeros([labels.size(0), C, ],
                          dtype=torch.float32, device=labels.device)
    target = one_hot.scatter_(1, labels, 1)

    return target
Ejemplo n.º 3
0
    def forward(self, scores: _torch.FloatTensor, relevance: _torch.LongTensor,
                n: _torch.LongTensor) -> _torch.FloatTensor:
        """Computes the loss for given batch of samples.

        Args:
            scores: A batch of per-query-document scores.
            relevance: A batch of per-query-document relevance labels.
            n: A batch of per-query number of documents (for padding purposes).
        """
        # Reshape relevance if necessary.
        if relevance.ndimension() == 2:
            relevance = relevance.reshape(
                (relevance.shape[0], relevance.shape[1], 1))
        if scores.ndimension() == 2:
            scores = scores.reshape((scores.shape[0], scores.shape[1], 1))

        # Compute ranking and sort scores and relevance
        ranking = _rank_by_score(scores, n)
        ranking = ranking.view((ranking.shape[0], ranking.shape[1], 1))
        scores = _torch.gather(scores, 1, ranking)
        relevance = _torch.gather(relevance, 1, ranking)

        # Compute pairwise differences for scores and relevances.
        score_pairs = _batch_pairs(scores)
        rel_pairs = _batch_pairs(relevance)

        # Compute loss per doc pair.
        loss_pairs = self._loss_per_doc_pair(score_pairs, rel_pairs, n)

        # Mask out padded documents per query in the batch
        n_grid = n[:, None, None].repeat(1, score_pairs.shape[1],
                                         score_pairs.shape[2])
        arange = _torch.arange(score_pairs.shape[1],
                               device=score_pairs.device)
        range_grid = _torch.max(*_torch.meshgrid([arange, arange]))
        range_grid = range_grid[None, :, :].repeat(n.shape[0], 1, 1)
        loss_pairs[n_grid <= range_grid] = 0.0

        # Reduce final list loss from per doc pair loss to a per query loss.
        loss = self._loss_reduction(loss_pairs)

        # Return loss
        return loss
Ejemplo n.º 4
0
def paste(background: Tensor, patch: Tensor, x: LongTensor, y: LongTensor, mask: Optional[Tensor] = None):
    """
    Pastes the given patch into the background image tensor at the specified location.
    Optionally a mask of the same size as the patch can be passed in to blend the
    pasted contents with the background.

    :param background: A batch of image tensors of shape (B, C, H, W) that represent the background
    :param patch: A batch of image tensors of shape (B, C, h, w) which values get pasted into the background
    :param x: The horizontal integer coordinates relative to the top left corner of the background image.
        This tensor must be a one-dimensional tensor of shape (B, ).
    :param y: The vertical integer coordinates relative to the top left corner of the background image.
        This tensor must be a one-dimensional tensor of shape (B, ).
    :param mask: A mask of the same size as the patch that is used to blend foreground and background values.
        It is optional and defaults to ones (all is foreground).
    :return: The composite tensor of background and foreground values of shape (B, C, H, W).

    Note:
        1.  The X- and Y-coordinates can exceed the range of the background image (negative and positive).
            The background will be dynamically padded and cropped again after pasting such that the
            contents can go over the borders of the background image.
        2.  Currently it only supports integer locations.
        3.  All tensors must be on the same device.
    """
    # background: (B, C, H, W)
    # patch, mask: (B, C, h, w)
    # x, y: (B, )
    b, c, H, W = background.shape
    _, _, h, w = patch.shape
    mask = torch.ones_like(patch) if mask is None else mask
    device = background.device
    assert b == patch.size(0) == mask.size(0)
    assert b == x.size(0) == y.size(0)
    assert c == patch.size(1) == mask.size(1)
    assert h == mask.size(-2)
    assert w == mask.size(-1)
    assert 1 == x.ndimension() == y.ndimension()
    assert device == patch.device == x.device == y.device == mask.device
    x = x.long()
    y = y.long()

    # dynamically pad background for patches that go over borders
    left = min(x.min().abs().item(), 0)
    top = min(y.min().abs().item(), 0)
    right = max(x.max().item() + w - W, 0)
    bottom = max(y.max().item() + h - H, 0)
    background = nn.functional.pad(background, pad=[left, right, top, bottom])

    # generate indices
    gridb, gridc, gridy, gridx = torch.meshgrid(
        torch.arange(b, device=device),
        torch.arange(c, device=device),
        torch.arange(h, device=device),
        torch.arange(w, device=device)
    )
    x = x.view(b, 1, 1, 1).repeat(1, c, h, w)
    y = y.view(b, 1, 1, 1).repeat(1, c, h, w)
    x = x + gridx + left
    y = y + gridy + top

    # we need to ignore negative indices, or pasted conent will be rolled to the other side
    mask = mask * (x >= 0) * (y >= 0)
    # paste
    one = torch.tensor(1, dtype=mask.dtype)
    background[(gridb, gridc, y, x)] = mask * patch + (one - mask) * background[(gridb, gridc, y, x)]
    # crop away the padded regions
    background = background[..., top:(top + H), left:(left + W)]
    return background
Ejemplo n.º 5
0
    def print_confusion_metrics(
        self,
        predicted_probs: torch.FloatTensor,
        labels: torch.LongTensor,
        labels_mask: Optional[torch.ByteTensor] = None,
    ) -> None:
        """ Prints confusion matrix

        Parameters
        ----------
        predicted_probs : torch.FloatTensor
            Predicted Probabilities ``[batch_size, num_classes]``
        labels : torch.FloatTensor
            True labels of the size ``[batch_size, 1]``
        labels_mask : Optional[torch.ByteTensor]
            Labels mask indicating 1 in thos places where the true label is ignored
            Otherwise 0. It should be of same size as labels

        """
        assert predicted_probs.ndimension() == 2, self.msg_printer.fail(
            "The predicted probs should "
            "have 2 dimensions. The probs "
            "that you passed have shape "
            "{0}".format(predicted_probs.size())
        )

        assert labels.ndimension() == 2, self.msg_printer.fail(
            "The labels should have 2 dimension."
            "The labels that you passed have shape "
            "{0}".format(labels.size())
        )

        if labels_mask is None:
            labels_mask = torch.zeros_like(labels).type(torch.ByteTensor)

        # TODO: for now k=1, change it to different number of ks
        top_probs, top_indices = predicted_probs.topk(k=1, dim=1)

        # convert to 1d numpy
        top_indices_numpy = top_indices.cpu().numpy().tolist()

        # convert labels to 1 dimension
        true_labels_numpy = labels.cpu().numpy().tolist()

        confusion_mtrx, classes = self.classification_metrics_utils.get_confusion_matrix_and_labels(
            predicted_tag_indices=top_indices_numpy,
            true_tag_indices=true_labels_numpy,
            masked_label_indices=labels_mask,
        )

        if self.idx2labelname_mapping is not None:
            classes_with_names = [
                f"cls_{class_}({self.idx2labelname_mapping[class_]})"
                for class_ in classes
            ]
        else:
            classes_with_names = classes

        assert (
            len(classes) == confusion_mtrx.shape[1]
        ), f"len(classes) = {len(classes)} confusion matrix shape {confusion_mtrx.shape}"

        header = [f"{class_}" for class_ in classes]
        header.insert(0, "pred(cols)/true(rows)")

        confusion_mtrx = pd.DataFrame(confusion_mtrx)
        confusion_mtrx.insert(0, "class_name", classes_with_names)

        self.msg_printer.table(
            data=confusion_mtrx.values.tolist(), header=header, divider=True
        )