极智AI | 多模态领域先行者 详解CLIP算法实现

本文涉及的产品
视觉智能开放平台,图像通用资源包5000点
视觉智能开放平台,分割抠图1万点
视觉智能开放平台,视频资源包5000点
简介: 大家好,我是极智视界,本文详细介绍一下 CLIP 算法的设计与实现,包括代码。

大家好,我是极智视界,本文详细介绍一下 CLIP 算法的设计与实现,包括代码。

多模态一定不是一个新鲜的话语,随着 AI 的发展,也正成为一种趋势,而 CLIP 做的就是在多模态领域里迈出了简单的一步,之所以说简单,是因为 CLIP 使用的方法出奇的简单,但效果又出奇的好。CLIP 具有非常好的迁移学习能力,预训练好的模型可以在任意一个视觉分类数据集上取得不错的效果,而且是 Zero-Shoot 的,意思是完全不需要再去这些数据集上做训练,就能得到这么好的结果。

本文不止会介绍 CLIP 的原理,还会介绍 CLIP 的实现,包括代码。下面开始。

参考 Paper:《Learning Transferable Visual Models From Natural Language Supervision》。


1 CLIP 算法原理

CLIP 全称 Contrastive Language-Image Pre-training,具有十分强悍的迁移学习能力,为了佐证这个能力,在超过 30 多个视觉数据上进行测试,涵盖面十分广泛,包括 OCR、视频动作检测、坐标定位和许多细分类任务,在所有的结果中最炸裂的一条就是在 ImageNet 上的结果,CLIP 在不使用任意一张 ImageNet 图片训练的情况下,直接 Zero-Shoot 推理,就能获得跟之前有监督训练的 ResNet-50 同样优秀的结果。这在 CLIP 出来之前,很多人都认为这是不太可能的事情。说到这里,应该可以调足了大伙儿的口味了,下面讲讲 CLIP 到底是个啥。

回答 CLIP 是啥的最好的答案可能就是下面这张图。很直观,有三个阶段:

  • Contrastive pre-training:预训练阶段,使用图片 - 文本对进行对比学习训练;
  • Create dataset classifier from label text:提取预测类别文本特征;
  • Use for zero-shot predictiion:进行 Zero-Shoot 推理预测;

进行一些说明。在预训练阶段,对比学习十分灵活,你只需要定义好 正样本对 和 负样本对 就行了,其中能够配对的图片 - 文本对即为正样本。具体来说,先分别对图像和文本提特征,这时图像对应生成 I1、I2 ... In 的特征向量,文本对应生成 T1、T2 ... Tn 的特征向量,然后中间对角线为正样本,其余均为负样本。这样的话就形成了 n 个正样本,n^2 - n 个负样本,如下。一旦有了正负样本,模型就可以通过对比学习的方式训练起来了,完全不需要手工的标注。当然,自监督的训练需要大量的数据,OPEN AI 的这个训练数据量大约在 4亿个 的数量级。

由于训练数据巨大,训练是个十分耗费时间的事情,所以必须对训练策略进行一些改进以提升训练效率。采用对比学习进行训练的一个重要原因也是考虑到 训练效率。来看图,最下面的蓝线表示像 GPT2 这种预测型的任务(预测型的任务是指,我有一张图片,去预测图片对应的描述,这个时候因为人类的语言是个奇妙又伟大的东西,一张图往往能对应出多种文本描述,比如 姚明篮球打得好姚明真高,对于描述同一张图并不冲突),可以看到是最慢的。中间黄线是指一种 bag of words 的方式,不需要逐字逐句地去预测文本,文本已经抽象成特征,相应的约束也放宽了,这样做训练速度立马提高了 3 倍。接下来进一步放宽约束,不再去预测单词,而是去判断图片 - 文本对,也就是绿色的线 对比学习的方法,这样效率又可以一下提升至 4 倍。

等训练好了,然后进入前向预测阶段。首先需要对文本类别进行一些处理,拿 ImageNet 数据集的 1000 个类别来说,原始的类别都是单词,而 CLIP 预训练时候的文本端出入的是个句子,这样一来为了统一就需要把单词构造成句子,怎么做呢?可以使用 A photo of a {object}. 的提示模板 (prompt template) 进行构造,比如对于 dog,就构造成 A photo of a dog.,然后再送入 Text Encoder 进行特征提取,就 ImageNet 而言就会得到一个 1000 维的特征向量,整个过程如下:

最后就是推理见证效果的时候,怎么做的呢。这个时候无论你来了张什么样的图片,只要扔给 Image Encoder 进行特征提取,会生成一个一维的图片特征向量,然后拿这个图片特征和 1000 个文本特征做余弦相似度对比,最相似的即为我们想要的那个结果,比如这里应该会得到 A photo of a dog.,整个过程如下:

流程应该上面说的比较清楚了,然后再说扩展性 / 泛化能力。上面是拿 ImageNet 那 1000 个类进行展示的,在实际使用中,这个类别文本不限于 ImageNet 那 1000 个类,而预测的图片也不限于 ImageNet 那 1.28 万张图片。然而,我依旧可以使用检索相似度的方式去得到输出,这样网络的灵活性就特别高了。不难发现,类别文本提取的特征类似于人脸识别里的检索库,图片提取特征就是待检测的那个。

以上就是 CLIP 工作的总览,可以看到 CLIP 在一次预训练后,可以方便的迁移到其他视觉分类任务上进行 Zero-Shoot 的前向预测。

下面来看一些效果。

由于 CLIP 学习的是文本语义信息,而不是单类别信息,这样做的好处可以体现在迁移能力上。CLIP 不仅在 ImageNet 常规数据集上表现优秀,对于 ImageNet Sketch 素描图、ImageNet-R 动漫图等非常规图像上的迁移学习能力依旧表现的非常好,如下:

来看最重要的 Zero-Shoot / Few-Shoot 的能力,主要对比有监督的 Base 网络,涉及的迁移数据集有 27 个之多,可以看到 CLIP 的 Zero-Shoot 能力十分突出,如下。 其中 Linear Probe 的意思是指训练的时候把预训练好的模型权重冻住,直接用其提取特征,然后只是去训练最后的 fc 分类头。

下面来看 CLIP 的实现。


2 CLIP 算法实现

CLIP 的整个逻辑比较清晰、简单,先看下 CLIP 整体伪代码:

其中视觉图片编码部分可以选择 ResNet 也可以选择 Vision Transformer,而本文编码部分就选择 Transformer。这里我以视觉部分用 ViT 来进行 CLIP 的实现讲解。

先看整体调用代码:

import torch
from clip import clip
from PIL import Image
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)     # 预训练模型选择 ViT-B/32
image = preprocess(Image.open("./CLIP.png")).unsqueeze(0).to(device)  # 预处理
text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)   # 文本特征抽象化
with torch.no_grad():
    # image_features = model.encode_image(image)     # 图像提特征
    # text_features = model.encode_text(text)       # 文本提特征
    logits_per_image, logits_per_text = model(image, text)   # 输入图像、本文,输出
    probs = logits_per_image.softmax(dim=-1).cpu().numpy()
print("Label probs:", probs)  # prints: [[0.9927937  0.00421068 0.00299572]]

来看 CLIP 模型整体前向部分:

def forward(self, image, text):
  image_features = self.encode_image(image)    # 图片编码提特征
  text_features = self.encode_text(text)       # 文本编码提特征
  # 特征归一化
  image_features = image_features / image_features.norm(dim=1, keepdim=True)
  text_features = text_features / text_features.norm(dim=1, keepdim=True)
  # 计算余弦相似度
  logit_scale = self.logit_scale.exp()
  logits_per_image = logit_scale * image_features @ text_features.t()
  logits_per_text = logits_per_image.t()
  # shape = [global_batch_size, global_batch_size]
  return logits_per_image, logits_per_text

其中 图片编码提特征实现:

# 图片编码提特征   image_features = self.encode_image(image)
class VisionTransformer(nn.Module):
    def __init__(self, input_resolution: int, patch_size: int, width: int, layers: int, heads: int, output_dim: int):
        super().__init__()
        self.input_resolution = input_resolution
        self.output_dim = output_dim
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=width, kernel_size=patch_size, stride=patch_size, bias=False)
        scale = width ** -0.5
        self.class_embedding = nn.Parameter(scale * torch.randn(width))
        self.positional_embedding = nn.Parameter(scale * torch.randn((input_resolution // patch_size) ** 2 + 1, width))
        self.ln_pre = LayerNorm(width)
        self.transformer = Transformer(width, layers, heads)
        self.ln_post = LayerNorm(width)
        self.proj = nn.Parameter(scale * torch.randn(width, output_dim))
    def forward(self, x: torch.Tensor):
        x = self.conv1(x)  # shape = [*, width, grid, grid]
        # x = x.reshape(x.shape[0], x.shape[1], -1)  # shape = [*, width, grid ** 2]
        x = torch.reshape(x, (x.shape[0], x.shape[1], -1))
        x = x.permute(0, 2, 1)  # shape = [*, grid ** 2, width]
        x = torch.cat([self.class_embedding.to(x.dtype) + torch.zeros(x.shape[0], 1, x.shape[-1], dtype=x.dtype, device=x.device), x], dim=1)  # shape = [*, grid ** 2 + 1, width]
        x = x + self.positional_embedding.to(x.dtype)
        x = self.ln_pre(x)
        x = x.permute(1, 0, 2)  # NLD -> LND
        x = self.transformer(x)
        x = x.permute(1, 0, 2)  # LND -> NLD
        x = self.ln_post(x[:, 0, :])
        if self.proj is not None:
            x = x @ self.proj
        return x

文本编码提特征实现如下:

# 文本编码提特征  text_features = self.encode_text(text)
def encode_text(self, text):
  x = self.token_embedding(text).type(self.dtype)  # [batch_size, n_ctx, d_model]
  x = x + self.positional_embedding.type(self.dtype)
  x = x.permute(1, 0, 2)  # NLD -> LND
  x = self.transformer(x)
  x = x.permute(1, 0, 2)  # LND -> NLD
  x = self.ln_final(x).type(self.dtype)
  # x.shape = [batch_size, n_ctx, transformer.width]
  # take features from the eot embedding (eot_token is the highest number in each sequence)
  x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection
  return x

其中文本 transformer 模块的实现如下:

class Transformer(nn.Module):
    def __init__(self, width: int, layers: int, heads: int, attn_mask: torch.Tensor = None):
        super().__init__()
        self.width = width
        self.layers = layers
        self.resblocks = nn.Sequential(*[ResidualAttentionBlock(width, heads, attn_mask) for _ in range(layers)])
    def forward(self, x: torch.Tensor):
        return self.resblocks(x)

最后当图像、文本特征都提取好后,对特征进行后处理及余弦相似度计算:

# normalized features
image_features = image_features / image_features.norm(dim=1, keepdim=True)
text_features = text_features / text_features.norm(dim=1, keepdim=True)
# cosine similarity as logits
logit_scale = self.logit_scale.exp()
logits_per_image = logit_scale * image_features @ text_features.t()
logits_per_text = logits_per_image.t()

这样实现就差不多讲完了。


好了,以上分享了 多模态领域先行者 CLIP 的算法原理和实现。希望我的分享能对你的学习有一点帮助。


logo_show.gif

相关文章
|
1月前
|
人工智能 编解码 算法
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
本文介绍了通义灵码2.0 AI程序员在嵌入式开发中的实战应用。通过安装VS Code插件并登录阿里云账号,用户可切换至DeepSeek V3模型,利用其强大的代码生成能力。实战案例中,AI程序员根据自然语言描述快速生成了C语言的base64编解码算法,包括源代码、头文件、测试代码和CMake编译脚本。即使在编译错误和需求迭代的情况下,AI程序员也能迅速分析问题并修复代码,最终成功实现功能。作者认为,通义灵码2.0显著提升了开发效率,打破了编程语言限制,是AI编程从辅助工具向工程级协同开发转变的重要标志,值得开发者广泛使用。
7996 69
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
|
9天前
|
人工智能 自然语言处理
AudioX:颠覆创作!多模态AI一键生成电影级音效+配乐,耳朵的终极盛宴
AudioX 是香港科技大学和月之暗面联合推出的扩散变换器模型,能够从文本、视频、图像等多种模态生成高质量音频和音乐,具备强大的跨模态学习能力和泛化能力。
112 36
AudioX:颠覆创作!多模态AI一键生成电影级音效+配乐,耳朵的终极盛宴
|
3天前
|
人工智能 监控 数据可视化
Agent TARS:一键让AI托管电脑!字节开源PC端多模态AI助手,无缝集成浏览器与系统操作
Agent TARS 是一款开源的多模态AI助手,能够通过视觉解析网页并无缝集成命令行和文件系统,帮助用户高效完成复杂任务。
1858 3
Agent TARS:一键让AI托管电脑!字节开源PC端多模态AI助手,无缝集成浏览器与系统操作
|
13天前
|
人工智能 自然语言处理 API
MM-StoryAgent:交大阿里联合开源!多模态AI一键生成儿童故事绘本+配音
MM-StoryAgent 是上海交通大学与阿里巴巴联合推出的开源多模态、多智能体框架,用于生成沉浸式的有声故事绘本视频,支持文本、图像、语音等多种模态的生成与对齐。
88 7
MM-StoryAgent:交大阿里联合开源!多模态AI一键生成儿童故事绘本+配音
|
13天前
|
人工智能 安全 数据可视化
一键部署谷歌最新开源多模态AI模型 Gemma 3:单GPU性能碾压Llama!支持35+种语言
Gemma 3 是谷歌最新推出的开源多模态AI模型,支持超过35种语言,具备文本、图像及短视频处理能力,提供四种模型尺寸,优化单GPU性能,适用于多种AI应用场景。
254 8
一键部署谷歌最新开源多模态AI模型 Gemma 3:单GPU性能碾压Llama!支持35+种语言
|
21天前
|
数据采集 人工智能 文字识别
OmniAlign-V:20万高质量多模态数据集开源,让AI模型真正对齐人类偏好
OmniAlign-V 是由上海交通大学、上海AI Lab等机构联合推出的高质量多模态数据集,旨在提升多模态大语言模型与人类偏好的对齐能力。该数据集包含约20万个多模态训练样本,涵盖自然图像和信息图表,结合开放式问答对,支持知识问答、推理任务和创造性任务。
67 10
OmniAlign-V:20万高质量多模态数据集开源,让AI模型真正对齐人类偏好
|
2天前
|
人工智能 编解码 算法
如何在Python下实现摄像头|屏幕|AI视觉算法数据的RTMP直播推送
本文详细讲解了在Python环境下使用大牛直播SDK实现RTMP推流的过程。从技术背景到代码实现,涵盖Python生态优势、AI视觉算法应用、RTMP稳定性及跨平台支持等内容。通过丰富功能如音频编码、视频编码、实时预览等,结合实际代码示例,为开发者提供完整指南。同时探讨C接口转换Python时的注意事项,包括数据类型映射、内存管理、回调函数等关键点。最终总结Python在RTMP推流与AI视觉算法结合中的重要性与前景,为行业应用带来便利与革新。
|
2天前
|
人工智能 监控 算法
Python下的毫秒级延迟RTSP|RTMP播放器技术探究和AI视觉算法对接
本文深入解析了基于Python实现的RTSP/RTMP播放器,探讨其代码结构、实现原理及优化策略。播放器通过大牛直播SDK提供的接口,支持低延迟播放,适用于实时监控、视频会议和智能分析等场景。文章详细介绍了播放控制、硬件解码、录像与截图功能,并分析了回调机制和UI设计。此外,还讨论了性能优化方法(如硬件加速、异步处理)和功能扩展(如音量调节、多格式支持)。针对AI视觉算法对接,文章提供了YUV/RGB数据处理示例,便于开发者在Python环境下进行算法集成。最终,播放器凭借低延迟、高兼容性和灵活扩展性,为实时交互场景提供了高效解决方案。
|
1月前
|
人工智能 数据可视化
文本、图像、点云任意模态输入,AI能够一键生成高质量CAD模型了
《CAD-MLLM: Unifying Multimodality-Conditioned CAD Generation With MLLM》提出了一种新型系统CAD-MLLM,能够根据文本、图像、点云或其组合生成高质量的CAD模型。该系统基于大型语言模型(LLM),通过多模态数据对齐和渐进式训练策略,实现了高效的CAD模型生成。作者创建了首个包含文本、图像、点云和命令序列的多模态数据集Omni-CAD,包含约450K个实例。实验表明,CAD-MLLM在多个任务上表现出色,特别是在点云条件生成任务中显著优于现有方法。未来工作将聚焦于提升计算效率、增加数据多样性及探索新模态。
228 18
|
27天前
|
数据采集 人工智能 编解码
算法系统协同优化,vivo与港中文推出BlueLM-V-3B,手机秒变多模态AI专家
BlueLM-V-3B是由vivo与香港中文大学共同研发的多模态大型语言模型,专为移动设备优化。它通过算法和系统协同优化,实现了高效部署和快速生成速度(24.4 token/s),并在OpenCompass基准测试中取得优异成绩(66.1分)。模型小巧,语言部分含27亿参数,视觉编码器含4000万参数,适合移动设备使用。尽管如此,低端设备可能仍面临资源压力,实际应用效果需进一步验证。论文链接:https://arxiv.org/abs/2411.10640。
42 9

热门文章

最新文章