Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negative labels , how to get loss ? #1084

Open
ohjuntack opened this issue Jul 15, 2024 · 1 comment
Open

Negative labels , how to get loss ? #1084

ohjuntack opened this issue Jul 15, 2024 · 1 comment

Comments

@ohjuntack
Copy link

ohjuntack commented Jul 15, 2024

Hi !

The OWL-ViT paper specifies that around 50 negative labels should be added for training. I want to find this process in the code.

@ohjuntack
Copy link
Author

ohjuntack commented Jul 15, 2024

@dataclasses.dataclass
class AddRandomNegativeLabels(NamedPreprocessOp):
  """Adds randomly sampled labels as additional negative labels.

  This is similar to the Federated Loss proposed in
  https://arxiv.org/pdf/2103.07461.pdf, but samples negatives in proportion to
  their appearance in the dataset, rather than in proportion to the square root
  of their frequency (for simplicity).

  The op works by maintaining a queue of labels seen in the dataset. For each
  dataset example, a number of candidate labels are randomly drawn from the
  queue. Labels that do not appear as positives in the example are added to the
  negatives, up to total_num_negatives.

  To keep the queue full, all text labels of the example, and the candidate
  labels previously sampled from the queue, are enqueued back. After warmup,
  sampled labels from the queue will have the same distribution as in the
  dataset.

  If negative integer labels are present in the features, this op will remove
  them, because they are obsolete after adding randomly sampled negatives.

  Attributes:
    total_num_negatives: Random negatives will be added to the input features to
      bring the total number of negatives to total_num_negatives.
    queue_capacity: Maximal size of the label queue. On average, the queue size
      will be maintained at half of the maximum.
    queue: tf.queue.RandomShuffleQueue. Will be added automatically.
  """

  total_num_negatives: int = 50
  queue_capacity: int = 100_000
  queue: Optional[tf.queue.RandomShuffleQueue] = None

  def __post_init__(self):
    self.queue = tf.queue.RandomShuffleQueue(
        capacity=self.queue_capacity,
        min_after_dequeue=0,
        dtypes=[tf.string],
        shapes=[tf.TensorShape([])],
        shared_name='random_negatives_queue')
    # Initialize with empty strings:
    self.queue.enqueue_many(tf.constant([''] * self.queue_capacity))

  def apply(self, features: image_ops.Features) -> image_ops.Features:
    # Draw candidate negative labels:
    candidate_labels = self.queue.dequeue_many(self.total_num_negatives * 2)

    # Fill queue back up:
    labels_to_enqueue = tf.concat([
        features[modalities.INSTANCE_TEXT_LABELS],
        features[modalities.NEGATIVE_TEXT_LABELS],
        candidate_labels,
    ], axis=0)
    labels_to_enqueue = tf.boolean_mask(
        labels_to_enqueue, tf.not_equal(labels_to_enqueue, PADDING_QUERY),
        name='labels_to_enqueue')
    target_size = self.queue_capacity // 2
    needed_elements = tf.clip_by_value(
        target_size - self.queue.size(), 0, tf.size(labels_to_enqueue))
    enqueue_op = self.queue.enqueue_many(
        tf.slice(labels_to_enqueue, begin=[0], size=[needed_elements]))

    # Get negatives that are not in positives:
    with tf.control_dependencies([enqueue_op]):
      candidate_negatives = tf.sparse.to_dense(
          tf.sets.difference(
              candidate_labels[None, ...],
              features[modalities.INSTANCE_TEXT_LABELS][None, ...]))[0]

    # Set operations sort the labels alphabetically, so we shuffle again:
    candidate_negatives = tf.random.shuffle(candidate_negatives)

    # New negatives contain all the old negatives, plus randomly sampled ones,
    # up to total_num_negatives. In addition, we ensure that padding ('') is not
    # present by including it as first element before applying tf.unique and
    # then slicing it off:
    new_negatives = tf.concat([
        tf.constant([PADDING_QUERY]),
        features[modalities.NEGATIVE_TEXT_LABELS],
        candidate_negatives,
    ], axis=0)
    orig_num_negatives = tf.shape(features[modalities.NEGATIVE_TEXT_LABELS])[0]
    new_num_negatives = tf.maximum(self.total_num_negatives, orig_num_negatives)
    features[modalities.NEGATIVE_TEXT_LABELS] = tf.unique(
        new_negatives)[0][1:(new_num_negatives + 1)]

    # Negative integer labels are now obsolete:
    if modalities.NEGATIVE_LABELS in features:
      logging.info('Removing obsolete field %s from features.',
                   modalities.NEGATIVE_LABELS)
      features.pop(modalities.NEGATIVE_LABELS)

    return features

I've understood how to obtain negative labels using the provided code. However, I'm curious about the process of calculating the negative loss using the code.

thank you !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant