parameters: (labels, embeddings, margin=1.0)
return_statement: return triplet_loss
docstring: Computes the triplet loss with semi-hard negative mining.
  
    The loss encourages the positive distances (between a pair of embeddings with
    the same labels) to be smaller than the minimum negative distance among
    which are at least greater than the positive distance plus the margin constant
    (called semi-hard negative) in the mini-batch. If no such negative exists,
    uses the largest negative distance instead.
    See: https://arxiv.org/abs/1503.03832.
  
    Args:
      labels: 1-D tf.int32 `Tensor` with shape [batch_size] of
        multiclass integer labels.
      embeddings: 2-D float `Tensor` of embedding vectors. Embeddings should
        be l2 normalized.
      margin: Float, margin term in the loss definition.
  
    Returns:
      triplet_loss: tf.float32 scalar.
docstring_summary: Computes the triplet loss with semi-hard negative mining.
docstring_tokens: Computes the triplet loss with semi - hard negative mining .
function: def triplet_semihard_loss(labels, embeddings, margin=1.0):
    """Computes the triplet loss with semi-hard negative mining.
  
    The loss encourages the positive distances (between a pair of embeddings with
    the same labels) to be smaller than the minimum negative distance among
    which are at least greater than the positive distance plus the margin constant
    (called semi-hard negative) in the mini-batch. If no such negative exists,
    uses the largest negative distance instead.
    See: https://arxiv.org/abs/1503.03832.
  
    Args:
      labels: 1-D tf.int32 `Tensor` with shape [batch_size] of
        multiclass integer labels.
      embeddings: 2-D float `Tensor` of embedding vectors. Embeddings should
        be l2 normalized.
      margin: Float, margin term in the loss definition.
  
    Returns:
      triplet_loss: tf.float32 scalar.
    """
    # Reshape [batch_size] label tensor to a [batch_size, 1] label tensor.
    lshape = array_ops.shape(labels)
    assert lshape.shape == 1
    labels = array_ops.reshape(labels, [lshape[0], 1])
  
    # Build pairwise squared distance matrix.
    pdist_matrix = pairwise_distance(embeddings, squared=True)
    # Build pairwise binary adjacency matrix.
    adjacency = math_ops.equal(labels, array_ops.transpose(labels))
    # Invert so we can select negatives only.
    adjacency_not = math_ops.logical_not(adjacency)
  
    batch_size = array_ops.size(labels)
  
    # Compute the mask.
    pdist_matrix_tile = array_ops.tile(pdist_matrix, [batch_size, 1])
    mask = math_ops.logical_and(
        array_ops.tile(adjacency_not, [batch_size, 1]),
        math_ops.greater(
            pdist_matrix_tile, array_ops.reshape(
                array_ops.transpose(pdist_matrix), [-1, 1])))
    mask_final = array_ops.reshape(
        math_ops.greater(
            math_ops.reduce_sum(
                math_ops.cast(mask, dtype=dtypes.float32), 1, keepdims=True),
            0.0), [batch_size, batch_size])
    mask_final = array_ops.transpose(mask_final)
  
    adjacency_not = math_ops.cast(adjacency_not, dtype=dtypes.float32)
    mask = math_ops.cast(mask, dtype=dtypes.float32)
  
    # negatives_outside: smallest D_an where D_an > D_ap.
    negatives_outside = array_ops.reshape(
        masked_minimum(pdist_matrix_tile, mask), [batch_size, batch_size])
    negatives_outside = array_ops.transpose(negatives_outside)
  
    # negatives_inside: largest D_an.
    negatives_inside = array_ops.tile(
        masked_maximum(pdist_matrix, adjacency_not), [1, batch_size])
    semi_hard_negatives = array_ops.where(
        mask_final, negatives_outside, negatives_inside)
  
    loss_mat = math_ops.add(margin, pdist_matrix - semi_hard_negatives)
  
    mask_positives = math_ops.cast(
        adjacency, dtype=dtypes.float32) - array_ops.diag(
            array_ops.ones([batch_size]))
  
    # In lifted-struct, the authors multiply 0.5 for upper triangular
    #   in semihard, they take all positive pairs except the diagonal.
    num_positives = math_ops.reduce_sum(mask_positives)
  
    triplet_loss = math_ops.truediv(
        math_ops.reduce_sum(
            math_ops.maximum(
                math_ops.multiply(loss_mat, mask_positives), 0.0)),
        num_positives,
        name='triplet_semihard_loss')
  
    return triplet_loss
function_tokens: def triplet_semihard_loss ( labels , embeddings , margin = 1.0 ) : # Reshape [batch_size] label tensor to a [batch_size, 1] label tensor. lshape = array_ops . shape ( labels ) assert lshape . shape == 1 labels = array_ops . reshape ( labels , [ lshape [ 0 ] , 1 ] ) # Build pairwise squared distance matrix. pdist_matrix = pairwise_distance ( embeddings , squared = True ) # Build pairwise binary adjacency matrix. adjacency = math_ops . equal ( labels , array_ops . transpose ( labels ) ) # Invert so we can select negatives only. adjacency_not = math_ops . logical_not ( adjacency ) batch_size = array_ops . size ( labels ) # Compute the mask. pdist_matrix_tile = array_ops . tile ( pdist_matrix , [ batch_size , 1 ] ) mask = math_ops . logical_and ( array_ops . tile ( adjacency_not , [ batch_size , 1 ] ) , math_ops . greater ( pdist_matrix_tile , array_ops . reshape ( array_ops . transpose ( pdist_matrix ) , [ - 1 , 1 ] ) ) ) mask_final = array_ops . reshape ( math_ops . greater ( math_ops . reduce_sum ( math_ops . cast ( mask , dtype = dtypes . float32 ) , 1 , keepdims = True ) , 0.0 ) , [ batch_size , batch_size ] ) mask_final = array_ops . transpose ( mask_final ) adjacency_not = math_ops . cast ( adjacency_not , dtype = dtypes . float32 ) mask = math_ops . cast ( mask , dtype = dtypes . float32 ) # negatives_outside: smallest D_an where D_an > D_ap. negatives_outside = array_ops . reshape ( masked_minimum ( pdist_matrix_tile , mask ) , [ batch_size , batch_size ] ) negatives_outside = array_ops . transpose ( negatives_outside ) # negatives_inside: largest D_an. negatives_inside = array_ops . tile ( masked_maximum ( pdist_matrix , adjacency_not ) , [ 1 , batch_size ] ) semi_hard_negatives = array_ops . where ( mask_final , negatives_outside , negatives_inside ) loss_mat = math_ops . add ( margin , pdist_matrix - semi_hard_negatives ) mask_positives = math_ops . cast ( adjacency , dtype = dtypes . float32 ) - array_ops . diag ( array_ops . ones ( [ batch_size ] ) ) # In lifted-struct, the authors multiply 0.5 for upper triangular #   in semihard, they take all positive pairs except the diagonal. num_positives = math_ops . reduce_sum ( mask_positives ) triplet_loss = math_ops . truediv ( math_ops . reduce_sum ( math_ops . maximum ( math_ops . multiply ( loss_mat , mask_positives ) , 0.0 ) ) , num_positives , name = 'triplet_semihard_loss' ) return triplet_loss