VLM--CLIP作分类任务的损失函数

info_nce_loss

这个是clip作对比学习的损失函数
各个博客上都有详细介绍了,我这里就不赘述

def info_nce_loss(image_features, text_features,logit_scale,labels, temperature=0.07):
    batch_size = image_features.shape[0]

    image_features = image_features / image_features.norm(dim=-1, keepdim=True)
    text_features = text_features / text_features.norm(dim=-1, keepdim=True)

    similarity_matrix = torch.matmul(image_features, text_features.T) / temperature

    logits_per_image = similarity_matrix
    logits_per_text = similarity_matrix.T

    # 构造标签,正样本对应的位置为1,其余为0,这里假设批次内第一个文本特征是对应图像的正样本文本特征
    gen_labels = torch.arange(batch_size).long().to(image_features.device)

    total_loss = (
        F.cross_entropy(logits_per_image, gen_labels)+
        F.cross_entropy(logits_per_text, gen_labels)
    )/2

    return total_loss, logits_per_image, logits_per_text

我踩的坑

微调 c l i p clip clip 做分类任务类别数为3

  1. 数据集为图像-文本对数据集:即一个数据样本为一个图像和对应的文本在json文件里。这里每个类别的图像的文本都是一样的,也就是a类别下图像可能会有细微不同,但是文本都是一样的
  2. 微调 c l i p clip clip 的结构同原始 c l i p clip clip 一致,输出的图像特征维度为 [ 输入图像数量 , 512 ] [输入图像数量,512] [输入图像数量,512],文本特征维度为 [ 输入的文本数量 , 512 ] [输入的文本数量,512] [输入的文本数量,512]这里选用不同的clip结构,输出维度可能有所不同
  3. 我微调过程输入 c l i p clip clip 的数据为 b a t c h _ s i z e batch\_size batch_size个图像、文本。输出的logit维度为 [ b a t c h _ s i z e , b a t c h _ s i z e ] [batch\_size,batch\_size] [batch_size,batch_size]

当使用 c l i p clip clip 去做分类任务假设类别为3时,直接使用上面的损失函数并不合适
因为:
g e n _ l a b e l s gen\_labels gen_labels会产生一个 [ 0 … … b a t c h _ s i z e − 1 ] [0……batch\_size-1] [0……batch_size1]的序列,接着和 l o g i t logit logit 做交叉熵。这里的 l o g i t logit logit 维度为 [ b a t c h _ s i z e , b a t c h _ s i z e ] [batch\_size,batch\_size] [batch_size,batch_size]

这意味着: l o g i t logit logit的对角线处的数据才会被 l o s s loss loss记录即第 i i i 个图像和第 i i i 个文本才是匹配的正样本,其余的为负样本。

这跟我实验设置下的分类任务有所冲突:因为我只有3个类别,而对于 l o g i t logit logit的第 i i i 行(即第 i i i 图像),只会跟第 i i i 列(即第 i i i 个文本)是正样本,而第 i i i 个图像应该和不止一个文本是正样本。例如:第0行图像和第0列的文本是正样本,还会和第 0 + 3 i , i = 0 , 1 , 2 … … 0+3i,i=0,1,2…… 0+3ii=0,1,2……列的文本是正样本,而 i n f o _ n c e _ l o s s info\_nce\_loss info_nce_loss会忽略掉后面的正样本

导致微调出来的 A C C ACC ACC F 1 F1 F1 都比较低

clip选用这样的损失函数,是因为其并不是做分类任务,而是直接用海量的互联网数据去预训练(a类别下图像可能会有细微不同,但是文本都是一样的这个情况存在的可能性小)

在这里插入图片描述

clip分类任务损失函数

def info_nce_loss(image_features, text_features,logit_scale,labels, temperature=0.07):
    """
    计算InfoNCE损失函数,模拟CLIP中的对比学习损失计算

    参数:
    image_features (torch.Tensor): 图像特征表示,形状为 [batch_size, feature_dim]
    text_features (torch.Tensor): 文本特征表示,形状为 [batch_size, feature_dim]
    temperature (float): 用于缩放相似度得分的温度参数,控制分布的平滑程度

    返回:
    loss (torch.Tensor): InfoNCE损失值
    """
    batch_size = image_features.shape[0]

    image_features = image_features / image_features.norm(dim=-1, keepdim=True)
    text_features = text_features / text_features.norm(dim=-1, keepdim=True)

    similarity_matrix = torch.matmul(image_features, text_features.T) / temperature

    logits_per_image = similarity_matrix
    logits_per_text = similarity_matrix.T

    gen_labels = labels
    total_loss = F.cross_entropy(logits_per_image, gen_labels)

    return total_loss, logits_per_image, logits_per_text
  1. 给每个图像-文本对记录类别 l a b e l label label
  2. 改变文本输入,每个 b a t c h _ s i z e batch\_size batch_size下输入的文本维度为 [ n _ c l a s s , ] [n\_class,] [n_class,],经过 c l i p _ e n c o d e r clip\_encoder clip_encoder 后维度为 [ n _ c l a s s , 512 ] [n\_class,512] [n_class,512]
  3. 接着做交叉熵计算
### Clip 损失函数定义与实现 #### 背景介绍 Clip 是一种用于对比学习的模型架构,广泛应用于图像和文本之间的联合表示学习。其核心目标是最小化正样本对(即匹配的图像-文本对)的距离,同时最大化负样本对(不匹配的图像-文本对)的距离。这种机制通常通过设计特定的损失函数来实现。 #### Clip 损失函数的核心原理 Clip损失函数基于对比学习的思想,具体采用的是 **InfoNCE (Noise Contrastive Estimation)** 或类似的变体形式。该损失函数的目标是优化图像嵌入 \(I\) 和文本嵌入 \(T\) 之间的相似度矩阵 \(S\)[^5]: \[ S_{i,j} = \frac{\exp(\text{sim}(I_i, T_j))}{\sum_k \exp(\text{sim}(I_i, T_k))}, \] 其中: - \(\text{sim}\) 表示余弦相似度; - \(I_i\) 和 \(T_j\) 分别代表第 \(i\) 个图像嵌入和第 \(j\) 个文本嵌入; - 温度参数 \(\tau\) 控制分布的锐利程度。 最终的损失函数可以写成如下形式[^6]: \[ L = -\log \left( \frac{\exp(S_{ii}/\tau)}{\sum_j \exp(S_{ij}/\tau)} \right). \] 这表明对于每一对正样本 (\(I_i, T_i\)),我们希望它们的相似度相对于其他负样本更高。 --- #### PyTorch 中的实现 在 PyTorch 中,可以通过以下方式实现 Clip 的 InfoNCE 损失函数: ```python import torch import torch.nn.functional as F def clip_loss(image_embeddings, text_embeddings, temperature=0.07): """ 计算 Clip 对比损失。 参数: image_embeddings: 图像嵌入张量,形状为 [batch_size, embedding_dim]. text_embeddings: 文本嵌入张量,形状为 [batch_size, embedding_dim]. temperature: 温度超参数,默认值为 0.07. 返回: loss: 标量损失值。 """ batch_size = image_embeddings.shape[0] # 归一化嵌入向量 image_embeddings_norm = F.normalize(image_embeddings, dim=-1) text_embeddings_norm = F.normalize(text_embeddings, dim=-1) # 计算相似度矩阵 logits_per_image = torch.matmul(image_embeddings_norm, text_embeddings_norm.t()) / temperature # 构造标签矩阵 labels = torch.arange(batch_size, device=image_embeddings.device) # 计算交叉熵损失 loss_i = F.cross_entropy(logits_per_image, labels) loss_t = F.cross_entropy(logits_per_image.t(), labels) return (loss_i + loss_t) / 2 ``` 上述代码实现了双向对比损失计算方法,分别从图像到文本以及从文本到图像两个方向进行优化[^7]。 --- #### TensorFlow 中的实现 在 TensorFlow 中,同样可以利用 `tf.keras.losses.CategoricalCrossentropy` 来实现 Clip 的对比损失函数: ```python import tensorflow as tf @tf.function def clip_loss(image_embeddings, text_embeddings, temperature=0.07): """ 计算 Clip 对比损失。 参数: image_embeddings: 图像嵌入张量,形状为 [batch_size, embedding_dim]. text_embeddings: 文本嵌入张量,形状为 [batch_size, embedding_dim]. temperature: 温度超参数,默认值为 0.07. 返回: loss: 标量损失值。 """ batch_size = tf.shape(image_embeddings)[0] # 归一化嵌入向量 image_embeddings_norm = tf.math.l2_normalize(image_embeddings, axis=-1) text_embeddings_norm = tf.math.l2_normalize(text_embeddings, axis=-1) # 计算相似度矩阵 logits_per_image = tf.matmul( image_embeddings_norm, text_embeddings_norm, transpose_b=True ) / temperature # 创建标签矩阵 labels = tf.range(start=0, limit=batch_size, dtype=tf.int32) # 计算交叉熵损失 loss_i = tf.reduce_mean(tf.keras.losses.sparse_categorical_crossentropy(labels, logits_per_image)) loss_t = tf.reduce_mean(tf.keras.losses.sparse_categorical_crossentropy(labels, tf.transpose(logits_per_image))) return (loss_i + loss_t) / 2 ``` 此版本适用于 TensorFlow 2.x 并支持自动求导功能[^8]。 --- #### 总结 无论是 PyTorch 还是 TensorFlow,Clip 损失函数的设计都围绕着对比学习的核心思想展开。两者的主要区别在于 API 风格的不同,但在逻辑上完全一致。值得注意的是,在实际应用中可能还需要调整温度参数或其他超参以适应具体的任务需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值