CLIP:论文阅读 -- 视觉模型
更多内容:XiaoJ的知识星球
目录
- 1.CLIP概述
- 2.CLIP的方法
- 2.1. 自然语言监督
- 2.2. 创建足够大的数据集
- 2.3. 选择有效的预训练方法
- 2.4. 选择和缩放模型
- 1)CLIP模型选择:
- 2)模型缩放
- 2.5 训练
- 3.CLIP 核心伪代码
- 3.1. CLIP伪代码
- 3.2. CLIP伪代码介绍
- 3.3. 概念理解
- 3.4. 伪代码示例
CLIP:《Learning Transferable Visual Models From Natural Language Supervision》论文阅读
.
1.CLIP概述
CLIP(Contrastive Language–Image Pre-training)是一种创新的计算机视觉模型,旨在解决传统目标检测模型依赖固定分类体系和大量标注数据的局限性。该模型通过在4亿对互联网收集的图文数据上进行预训练,采用“预测图文匹配”的简单任务,成功从零开始学习到强大的图像表征能力。其核心优势在于:
-
突破固定分类限制 :利用自然语言作为通用接口,既能引用已学视觉概念,也可描述新类别,实现零样本迁移(zero-shot transfer),无需针对特定任务微调
-
跨任务泛化性能 :在超过30个计算机视觉任务(如OCR、视频动作识别、细粒度分类等)中达到或接近全监督模型水平。例如,在ImageNet上零样本迁移即可复现ResNet-50的精度,且不使用其原始训练数据
-
高效可扩展性 :预训练任务设计简洁,支持大规模数据训练,为后续多模态研究提供了开源代码和模型权重。
这一方法标志着从封闭域监督学习向开放域知识迁移的范式转变。
图1:CLIP
标准图像模型:联合训练图像特征提取器和线性分类器来预测某些标签;
CLIP:
-
联合训练图像编码器和文本编码器来预测一批(图像、文本)训练示例的正确配对。
-
在测试时,学习的文本编码器通过嵌入目标数据集类的名称或描述来合成零样本线性分类器。
2.CLIP的方法
2.1. 自然语言监督
CLIP的核心思想:
- 通过自然语言中的监督信号学习视觉表征(Natural Language Supervision)。
相比传统方法,自然语言监督具有显著优势:
-
可扩展性强:无需人工标注“机器学习兼容格式”(如ImageNet的1-of-N标签),可直接从互联网海量文本中被动学习;
-
零样本迁移能力:不仅学习视觉表征,还建立语言与视觉的关联,实现灵活的任务迁移(如动态识别未见过的类别)。
.
2.2. 创建足够大的数据集
CLIP团队指出,现有主流数据集(MS-COCO、Visual Genome、YFCC100M)存在规模或质量限制:
-
MS-COCO 和 Visual Genome:高质量人工标注,但仅约10万图像,远低于现代视觉模型常用的大规模数据(如Instagram的35亿图片);
-
YFCC100M:包含1亿图像,但元数据稀疏且质量参差,过滤后仅剩1500万条有效英文图文对,规模与ImageNet相当。
为此,CLIP构建了包含4亿图像-文本对的新数据集 WIT(WebImageText),以充分挖掘自然语言监督的潜力。
.
2.3. 选择有效的预训练方法
经典计算机视觉系统依赖海量算力,传统方法因依赖预测固定类别难以扩展至开放视觉概念。CLIP创新设计如下:
对比学习框架
-
将图文匹配转化为相似度对比任务,替代逐词预测;
-
最大化正图文对的余弦相似度,最小化负对相似度;
-
采用对称交叉熵损失。
架构优化
-
双编码器结构:独立图像编码器(ResNet/ViT)和文本编码器(Transformer);
-
简化设计:移除非线性投影层,仅保留线性映射;
-
动态温度参数:将温度系数τ设为可训练参数,避免超参调优;
训练优化
-
数据增强:仅使用随机方形裁剪;
-
文本处理:直接使用单句描述(无需复杂采样);
-
初始化策略:完全从零训练,不依赖ImageNet预训练权重;
.
2.4. 选择和缩放模型
1)CLIP模型选择:
图像编码器: ResNet-50或ViT
-
改进版ResNet-50 :基于ResNet-50,引入ResNetD改进(如抗锯齿模糊池化)和注意力池化机制(以全局平均池化特征为查询的多头注意力),替代原全局平均池化层;
-
Vision Transformer (ViT) :严格遵循Dosovitskiy等人的实现,仅增加卷积块与位置嵌入的层归一化,并调整初始化策略;
文本编码器 :
- 基于Transformer架构,采用Radford等人的改进设计,包含12层、512维宽、8头注意力的63M参数模型。输入文本经小写BPE编码(词汇量49,152),序列上限76词,通过[SOS]/[EOS]标记界定,最终以[EOS]位置的归一化特征线性投影至多模态嵌入空间 。
2)模型缩放
扩展ResNet图像编码器:采用复合缩放策略
-
不同于传统单独增加模型宽度或深度的方法,参考Tan & Le 的研究结论;
-
将新增计算资源平均分配至宽度、深度和分辨率三个维度,以提升整体效率 。
扩展文本编码器:
- 仅按ResNet宽度扩展比例线性增加其宽度,未调整深度,因实验表明CLIP性能对文本编码器容量变化不敏感 。
.
2.5 训练
CLIP团队训练了以下模型:
-
5个ResNets(ResNet-50、ResNet-101及3个基于EfficientNet风格扩展的RN50x4/RN50x16/RN50x64);
-
3个Vision Transformer(ViT-B/32、ViT-B/16、ViT-L/14),所有模型均训练32个epoch 。
训练采用Adam优化器(含解耦权重衰减正则化)和余弦学习率衰减策略,超参数通过网格搜索、随机搜索和手动调优在ResNet-50单epoch实验中确定,更大模型的参数根据经验调整 。
关键技术细节:
-
内存与速度优化:使用32,768的大批量、混合精度训练、梯度检查点、半精度Adam统计量及权重随机舍入;
-
分布式计算:将嵌入相似度计算分片至各GPU,仅计算本地批次所需子集;
-
温度参数:初始化为0.07,限制logits缩放不超过100以防止训练不稳定 。
最大规模模型耗时:
-
ResNet-RN50x64(592张V100 GPU)18天;
-
ViT-L/14(256张V100 GPU)12天;
-
对ViT-L/14额外进行1轮336像素预训练以提升性能(记作ViT-L/14@336px),该模型为论文主结果 。
3.CLIP 核心伪代码
3.1. CLIP伪代码
- 它将图像和文本特征映射到一个共享嵌入空间,通过对比学习来学习这些特征的关系。
# image_encoder - ResNet or Vision Transformer
# text_encoder - CBOW or Text Transformer
# I[n, h, w, c] - minibatch of aligned images
# T[n, l] - minibatch of aligned texts
# W_i[d_i, d_e] - learned proj of image to embed
# W_t[d_t, d_e] - learned proj of text to embed
# t - learned temperature parameter# extract feature representations of each modality
I_f = image_encoder(I) # [n, d_i]
T_f = text_encoder(T) # [n, d_t]# joint multimodal embedding [n, d_e]
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)# scaled pairwise cosine similarities [n, n]
logits = np.dot(I_e, T_e.T) * np.exp(t)# symmetric loss function
labels = np.arange(n)
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t) / 2
.
3.2. CLIP伪代码介绍
# image_encoder: 图像编码器,可以是 ResNet 或 Vision Transformer(ViT)。
# text_encoder: 文本编码器,可以是 CBOW(连续词袋模型)或基于 Transformer 的文本编码器(如 BERT、GPT)。
# I: 图像数据批次,形状为 [n, h, w, c],其中 n 是批量大小,h 和 w 是图像的高度和宽度,c 是通道数(通常是 3 对于 RGB 图像)。
# T: 文本数据批次,形状为 [n, l],其中 n 是批量大小,l 是每个文本的最大长度(通常通过填充或截断处理)。#################################################################
# 提取每个模态的特征表示
I_f = image_encoder(I) # [n, d_i]
T_f = text_encoder(T) # [n, d_t]
#I_f: 图像特征向量,形状为 [n, d_i],其中 d_i 是图像特征的维度。
#T_f: 文本特征向量,形状为 [n, d_t],其中 d_t 是文本特征的维度。#################################################################
# 联合多模态嵌入空间 [n, d_e]
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)
# W_i 和 W_t: 投影矩阵,分别用于将图像特征和文本特征映射到一个共享的嵌入空间。
# W_i 的形状为 [d_i, d_e];W_t 的形状为 [d_t, d_e];
# 其中 d_e 是嵌入空间的维度。
# I_e 和 T_e: 映射后的联合嵌入表示,形状均为 [n, d_e]。# 线性变换(Linear Projection):
# np.dot(I_f, W_i):将图像特征 I_f 投影到嵌入空间,得到 I_proj,形状为 [n, d_e]。
# np.dot(T_f, W_t):将文本特征 T_f 投影到嵌入空间,得到 T_proj,形状为 [n, d_e]。
# L2 归一化(Normalization):
# l2_normalize(I_proj, axis=1):对每个样本的嵌入向量进行 L2 归一化,使其长度为 1。归一化后的向量 I_e 的形状为 [n, d_e]。
# l2_normalize(T_proj, axis=1):对每个样本的嵌入向量进行 L2 归一化,使其长度为 1。归一化后的向量 T_e 的形状为 [n, d_e]。#################################################################
# 计算相似度矩阵 [n, n]
logits = np.dot(I_e, T_e.T) * np.exp(t)
# t: 温度参数(temperature parameter),用于控制相似度矩阵的尺度。
# logits: 相似度矩阵,形状为 [n, n]。
# I_e 的形状为 [n, d_e];
# T_e.T 是 T_e 的转置,形状为 [d_e, n];
# np.dot(I_e, T_e.T) 计算 I_e 和 T_e 之间的余弦相似度矩阵,因为 I_e 和 T_e 已经归一化。
# * np.exp(t) 对相似度矩阵进行缩放。#################################################################
# 计算损失函数
labels = np.arange(n)
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t) / 2
# labels: 标签数组,形状为 [n],表示每个图像和文本对的正确匹配关系。
# cross_entropy_loss: 交叉熵损失函数,用于计算预测概率分布与真实标签之间的差异。
# loss_i: 沿着行方向计算交叉熵损失,即每个图像与所有文本的匹配情况。
# loss_t: 沿着列方向计算交叉熵损失,即每个文本与所有图像的匹配情况。
# loss: 对称损失函数,取 loss_i 和 loss_t 的平均值,确保损失函数对称且均衡。
.
3.3. 概念理解
对比学习(Contrastive Learning):最大化匹配样本对间的相似度,同时最小化不匹配样本对间的相似度。
余弦相似度(Cosine Similarity): 衡量两向量间夹角余弦值,取值为 [-1, 1],值越大表示越相似。
-
归一化向量:通过 L2 归一化,将向量长度缩放到 1,使得余弦相似度仅取决于方向。
-
相似度矩阵:
logits
矩阵中的每个元素表示一对样本的相似度。
温度参数(Temperature Parameter): 温度参数 t
控制相似度矩阵的尺度
-
t > 1
: 增大相似度差距,使正样本对之间的相似度更高,负样本对之间的相似度更低。 -
t < 1
: 减小相似度差距,使模型对相似度变化不那么敏感。 -
t = 1
: 默认值,通常在实践中效果较好。
交叉熵损失(Cross-Entropy Loss): 衡量预测的概率分布与真实标签之间的差异。
.
3.4. 伪代码示例
假设我们有一个批量大小为 4 的数据集,图像和文本特征如下:
import numpy as npdef l2_normalize(x, axis=1):norm = np.linalg.norm(x, axis=axis, keepdims=True)return x / normdef cross_entropy_loss(logits, labels, axis=0):log_probs = np.log_softmax(logits, axis=axis)n_samples = logits.shape[axis]loss = -np.sum(log_probs[labels, np.arange(n_samples)]) / n_samplesreturn loss# 假设我们有 4 个样本,图像特征维度是 2048,文本是 300,统一投影到 512 维
n = 4
I_f = np.random.randn(n, 2048)
T_f = np.random.randn(n, 300)# 定义两个投影矩阵
W_i = np.random.randn(2048, 512)
W_t = np.random.randn(300, 512)# 定义温度参数
t = 0.07# 映射 + 归一化
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)# 计算相似度矩阵
logits = np.dot(I_e, T_e.T) * np.exp(t)# 计算损失函数
labels = np.arange(n)
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t) / 2print("Image Embedding Shape:", I_e.shape) # (4, 512)
print("Text Embedding Shape:", T_e.shape) # (4, 512)
print("Logits Shape:", logits.shape) # (4, 4)
print("Labels:", labels) # [0, 1, 2, 3]
print("Loss_i:", loss_i)
print("Loss_t:", loss_t)
print("Total Loss:", loss)
输出示例:
Image Embedding Shape: (4, 512)
Text Embedding Shape: (4, 512)
Logits Shape: (4, 4)
Labels: [0 1 2 3]
Loss_i: 1.0986122886681096
Loss_t: 1.0986122886681096
Total Loss: 1.0986122886681096
.
声明:资源可能存在第三方来源,若有侵权请联系删除!