当前位置: 首页 > news >正文

【大模型学习 | CLIP 原理代码实现】

​## Learning Transferable Visual Models From Natural Language Supervision

作者在摘要中指出,传统的监督式学习方法限制了视觉模型的泛化能力,特别是在迁移到新任务或新类别时的能力有限。以往的图像识别任务通常依赖于人为定义的分类标签进行训练,这种方式不仅数据成本高,而且模型更容易过拟合于训练类别。为了解决这一问题,CLIP 提出了一个新的预训练框架:利用网络上现成的大规模图文对(如标题+图像)作为监督信号,将图像与自然语言描述进行匹配,从而在无需特定分类标签的情况下,学习具有通用性的视觉表征。

一、数据缺陷

🔴 目前的图像数据存在着质量低、数据量不足的情况,例如 ① MS-COCO 虽然是高质量的标注数据,但也只有十万张图像,在目前的视觉系统来说属于小数据量; ② YFCC100M 有 一亿张图像,但是质量低,过滤后只有1500万张图像,与 ImageNet数据大小相同;现有视觉系统尚未能充分利用这些语言资源中的监督信息,限制了自然语言在视觉模型中的潜力发挥。

🟢 作者构建了一个4亿对的数据集WIT(图像,文本对),根据50万个关键词搜集相关的图文对,最后数据的文本数与GPT-2训练所需数据集大小相似;

二、预训练方法

🔴 作者首先尝试了直接采用联合学习的方式,采用CNN和Transformer来预测图像标题;(Transformer采用了6300万个参数,识别图像的类别会比训练一个词袋模型慢三倍);这两种方法来学习识别图像都有一个相同点:预测图像的准确文字;

在这里插入图片描述

🟢 作者提出一种图文匹配的更简单的代理任务,而不是精确预测每个单词;给定 N × N N \times N N×N 的图文对,在 CLIP 中,图像和文本分别通过两个独立的编码器(视觉 Transformer 和文本 Transformer)进行编码,基于余弦相似度学习多模态的嵌入空间,最大化配对图文之间的相似度,最小化不匹配对的相似度,并通过symeertric entropy loss优化相似得分:

🟢 训练模型并没有采用预训练权重模型,而是从头训练;并通过线性映射将不同模态编码器的表示映射到嵌入空间中;

🧠 为什么不采用非线性映射?

  • 作者并没有发现线性映射和非线性映射的差别,并推测非线性映射在一些纯自监督任务中(如 SimCLR、MoCo)可能会适应图像中更细节的特征,形成一种“共适应(co-adaptation)”,但在 CLIP 这样具有明确语言监督的图文对齐任务中,线性映射已经足够表达语义关系,且更稳定、更易训练;
# 线性映射文本、图像# 线性映射文本、图像# 线性映射文本、图像# 线性映射文本、图像te'z
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)师弟# symeertric entropy loss
loss_i = cross_entropy_loss(logits, labels, axis=0)  # 图像 → 文本,即图像作为query
loss_t = cross_entropy_loss(logits, labels, axis=1)  # 文本 → 图像,即文本作为query
loss = (loss_i + loss_t) / 2

🟢 在图像特征提取模型上,作者采用了两种架构: ① ResNet modifications (We also replace the global average pooling layer with an attention pooling mechanism.) ② Vit (with only the minor modification of adding an additional layer normalization)

🧠 为什么在将 patch + 位置编码输入 Transformer 之前加入layer normalization?

  • 稳定训练过程:合并后的向量(patch + 位置编码)可能存在数值不一致(例如 patch embedding 和 position embedding 的分布不同),LayerNorm 有助于规范化 patch + 位置编码的数值分布,防止某些 token 占主导。

🟢 文本编码器:① CBOW ② Transformer 12层 512维 8个注意力头

维度视觉编码器(如 ResNet)文本编码器(Transformer)
宽度增加通道数等比例增加 d_model
深度增加层数固定不变
分辨率增加输入图像尺寸不适用(文本长度固定)

在这里插入图片描述

三、代码实现

from PIL import Image
import requestsfrom transformers import CLIPProcessor, CLIPModelmodel = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)inputs = processor(text=["a photo of a cat", "a photo of a dog"], images=image, return_tensors="pt", padding=True)outputs = model(**inputs)
logits_per_image = outputs.logits_per_image # this is the image-text similarity score
probs = logits_per_image.softmax(dim=1) # we can take the softmax to get the label probabilities
  • QAT动态量化
from transformers import CLIPModel
import torch
import torch.quantizationmodel = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")# 对 Linear 层做动态量化
quantized_model = torch.quantization.quantize_dynamic(model,{torch.nn.Linear},  # 可以加上 torch.nn.LayerNorm 等dtype=torch.qint8
)# 保存模型
torch.save(quantized_model.state_dict(), "clip_quantized.pth")
  • 微调
import torch
from torch import nn
from torch.utils.data import DataLoader, Dataset
from transformers import CLIPProcessor, CLIPModel
from PIL import Image# 1. 加载模型和 processor
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")# 2. 冻结图像和文本编码器(只训练投影头)
for param in model.vision_model.parameters():param.requires_grad = False
for param in model.text_model.parameters():param.requires_grad = False# 3. 自定义小数据集(图像路径 + 对应文本)
class MyDataset(Dataset):def __init__(self, image_paths, texts):self.image_paths = image_pathsself.texts = textsdef __len__(self):return len(self.image_paths)def __getitem__(self, idx):image = Image.open(self.image_paths[idx]).convert("RGB")text = self.texts[idx]return image, text# 替换为你自己的图像路径和描述文本
image_paths = ["cat.jpg", "dog.jpg", "apple.jpg"]
texts = ["a photo of a cat", "a photo of a dog", "a photo of an apple"]
dataset = MyDataset(image_paths, texts)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)# 4. 定义优化器(只优化 projection 层)
optimizer = torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=5e-5)
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)# 5. 训练 loop
for epoch in range(10):model.train()total_loss = 0for images, texts in dataloader:inputs = processor(text=texts, images=images, return_tensors="pt", padding=True, truncation=True).to(device)outputs = model(**inputs)logits_per_image = outputs.logits_per_image  # [batch, batch]logits_per_text = outputs.logits_per_text    # [batch, batch]ground_truth = torch.arange(len(images)).to(device)# CLIP 的双向对比损失loss_i = nn.CrossEntropyLoss()(logits_per_image, ground_truth)loss_t = nn.CrossEntropyLoss()(logits_per_text, ground_truth)loss = (loss_i + loss_t) / 2loss.backward()optimizer.step()optimizer.zero_grad()total_loss += loss.item()print(f"Epoch {epoch+1}, Loss: {total_loss:.4f}")
http://www.lqws.cn/news/570421.html

相关文章:

  • Matlab自学笔记六十一:快速上手解方程
  • Vue 与react 生命周期对比
  • 什么是DID(Decentralized Identifier,去中心化身份)
  • 如何优化RK3588集群的性能?支持12个RK3588云手机阵列
  • C++ 设计模式—简略版
  • MySQL的调控按钮
  • 【linux】权限深入解析
  • C/C++数据结构之动态数组
  • Vulkan 学习(18)---- 使用 ValidationLayer
  • 洛谷日常刷题3
  • 通过交互式可视化探索波动方程-AI云计算数值分析和代码验证
  • Xcode 中的 Compilation Mode 是管什么的
  • 模拟与可视化复杂非线性偏微分方程:从KdV到云端几何问题-AI云计算数值分析和代码验证
  • 现代 JavaScript (ES6+) 入门到实战(一):告别 var!拥抱 let 与 const,彻底搞懂作用域
  • 80%的知识库场景选择FastGPT,20%的复杂场景选择Dify
  • 概率论符号和公式整理
  • Dify私有化知识库搭建并通过ChatFlow智能机器人使用知识库的详细操作步骤
  • C# 合并两个byte数组的几种方法
  • linux运维学习第10周
  • 手机射频功放测试学习(二)——手机线性功放的静态电流和小信号(S-Parameter)测试
  • 计算机组成原理与体系结构-实验二 ALU(Proteus 8.15)
  • 电子计数跳绳原型
  • 数据结构 哈希表、栈的应用与链式队列 6.29 (尾)
  • Hive SQL 快速入门指南
  • GO 语言学习 之 数组和切片
  • Docker镜像制作案例
  • MATLAB中formattedDisplayText函数用法
  • 用户行为序列建模(篇八)-【阿里】DIEN
  • 由dbc文件解析can消息(一)
  • 信创背景下应用软件迁移解析:从政策解读到落地实践方案