天天看点

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

先说下目的吧,看图像检索相关的损失函数时,这个损失函数应该算是绕不开的前人工作。所以这里针对看到的一些文章,也做做自己的相关总结。主要是关于源码的学习啦。

文章参考链接:

1.https://zhuanlan.zhihu.com/p/72516633(这个更易懂)

2.https://zhuanlan.zhihu.com/p/82199561

代码参考链接:

1.https://github.com/wangz10/contrastive_loss/blob/97b6166e41eef8ea0810577c9511eee85eaa8d5d/losses.py#L50

2.https://github.com/tensorflow/addons/blob/master/tensorflow_addons/losses/contrastive.py#L26

1.概念整理

Contrastive loss 的公式如下:

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

yi与yj分别为样本对的标签,fi与fj分别为样本对输入经过backbone得到的特征向量,1{.}表示括号内为真时,输出1,括号内为假时输出0。m为阈值,是预先设定的超参数,表示不同类样本之间的距离应超过该margin值。

解释:前面部分目的是正样本对的距离更小,后面部分目的是负样本对的距离更大,m为人为设定的阈值。

优点:能够让正样本对尽可能的近,负样本对尽可能的远,这样可以增大类间差异,减小类内差异。

缺点:负样本对无差别性(相近的负样本对与较远的负样本对优化任务相同)。

2.tensorflow addons源码

在github上找到较为匹配的(https://github.com/wangz10/contrastive_loss/blob/97b6166e41eef8ea0810577c9511eee85eaa8d5d/losses.py#L50)。

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

发现这个地方是tfa下面的模块,刚开始以为是作者导入错了,后来发现了这样一行:

import tensorflow_addons as tfa
           

看到这个地方,就想到可能是其他开发者开发的一个库了。不过没想到官方也将这个模块进行了说明。

由tf框架上的额外实现--Tensorflow Addons。下面是我查到的官网上的一个介绍:

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

大致目录结果是这样(https://github.com/tensorflow/addons/tree/master/tensorflow_addons):

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

根据函数的调用关系:

https://github.com/wangz10/contrastive_loss/blob/97b6166e41eef8ea0810577c9511eee85eaa8d5d/losses.py#L50

loss = tfa.losses.contrastive_loss(y_contrasts, d_vec, margin=margin)
           

就找到了contrastive_loss的实现:

https://github.com/tensorflow/addons/blob/master/tensorflow_addons/losses/contrastive.py#L26

def contrastive_loss(
    y_true: TensorLike, y_pred: TensorLike, margin: Number = 1.0
) -> tf.Tensor:
    r"""Computes the contrastive loss between `y_true` and `y_pred`.
    This loss encourages the embedding to be close to each other for
    the samples of the same label and the embedding to be far apart at least
    by the margin constant for the samples of different labels.
    The euclidean distances `y_pred` between two embedding matrices
    `a` and `b` with shape `[batch_size, hidden_size]` can be computed
    as follows:
    >>> a = tf.constant([[1, 2],
    ...                 [3, 4],
    ...                 [5, 6]], dtype=tf.float16)
    >>> b = tf.constant([[5, 9],
    ...                 [3, 6],
    ...                 [1, 8]], dtype=tf.float16)
    >>> y_pred = tf.linalg.norm(a - b, axis=1)
    >>> y_pred
    <tf.Tensor: shape=(3,), dtype=float16, numpy=array([8.06 , 2.   , 4.473],
    dtype=float16)>
    <... Note: constants a & b have been used purely for
    example purposes and have no significant value ...>
    See: http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
    Args:
      y_true: 1-D integer `Tensor` with shape `[batch_size]` of
        binary labels indicating positive vs negative pair.
      y_pred: 1-D float `Tensor` with shape `[batch_size]` of
        distances between two embedding matrices.
      margin: margin term in the loss definition.
    Returns:
      contrastive_loss: 1-D float `Tensor` with shape `[batch_size]`.
    """
    y_pred = tf.convert_to_tensor(y_pred)
    y_true = tf.dtypes.cast(y_true, y_pred.dtype)
    return y_true * tf.math.square(y_pred) + (1.0 - y_true) * tf.math.square(
        tf.math.maximum(margin - y_pred, 0.0)
    )

           

此处与1基础中的公式对比,发现公式基本一样了。还是一句句分析一下吧。

第一句:

y_pred = tf.convert_to_tensor(y_pred)
           

此处参考(tf.convert_to_tensor的用法),会发现tf.convert_to_tensor这个函数,能够将列表与numpy矩阵都可以转换为tensorflow tensor类型。 

【tensorflow】关于tensorflow的losses.contrastive_loss解析1.概念整理2.tensorflow addons源码3.实际中用

第二句:

y_true = tf.dtypes.cast(y_true, y_pred.dtype)
           

tf.dtypes.cast应该是做的强制类型转换,转换为相同的类型。

第三句:

return y_true * tf.math.square(y_pred) + (1.0 - y_true) * tf.math.square(
        tf.math.maximum(margin - y_pred, 0.0)
    )
           

返回的就是基础1中的公式类似了。y_true=1时,应该就是正样本对,y_pred就是两者特征向量的距离,目的是让正样本对距离尽可能接近;y_true=0时,应该就是负样本对,y_pred就是两者特征向量的距离,目的是让负样本对距离尽可能远离,一直到等于margin边界距离为止。

3.实际中用

大致看了下代码,实际中用应该可以参考此处:

https://github.com/wangz10/contrastive_loss/blob/97b6166e41eef8ea0810577c9511eee85eaa8d5d/losses.py#L50

def max_margin_contrastive_loss(z, y, margin=1.0, metric='euclidean'):
    '''
    Wrapper for the maximum margin contrastive loss (Hadsell et al. 2006)
    `tfa.losses.contrastive_loss`
    Args:
        z: hidden vector of shape [bsz, n_features].
        y: ground truth of shape [bsz].
        metric: one of ('euclidean', 'cosine')
    '''
    # compute pair-wise distance matrix
    if metric == 'euclidean':
        D = pdist_euclidean(z)
    elif metric == 'cosine':
        D = 1 - tf.matmul(z, z, transpose_a=False, transpose_b=True)
    # convert squareform matrix to vector form
    d_vec = square_to_vec(D)
    # make contrastive labels
    y_contrasts = get_contrast_batch_labels(y)
    loss = tfa.losses.contrastive_loss(y_contrasts, d_vec, margin=margin)
    # exploding/varnishing gradients on large batch?
    return tf.reduce_mean(loss)
           

如果后续我自己写的话,应该也会附录在这里。这篇写的时间跨度也挺大,昨晚一直写到困,上午本来想看个电影,算了,先写完吧,不写后面更难拿起来了。okay,去游泳了。

继续阅读