Clip模型使用

image

代码文件结构

image

clip.py

CLIP模块提供了以下方法:

clip.available_models()

返回可用的CLIP模型的名

import clip

models = clip.available_models()
print(models)

#结果
['RN50', 'RN101', 'RN50x4', 'RN50x16', 'RN50x64', 'ViT-B/32', 'ViT-B/16', 'ViT-L/14', 'ViT-L/14@336px']

clip.load(name, device=..., jit=False)

根据clip.available_models()返回的模型名称,返回模型(nn.model)以及模型所需的TorchVision变换。如有必要,它将下载模型。name参数也可以是本地检查点文件的路径。
可以可选地指定运行模型的设备,默认情况下,如果有CUDA设备则使用第一个CUDA设备,否则使用CPU。
当jit为False时,将加载非JIT版本的模型,jit模型经过优化,推理效率更高,但不容易更改;
当jit为True时,使用build_model加载模型,非jit模型适用于需要更改模型的情况。

返回值:
model : torch.nn.Module
The CLIP model
preprocess : Callable[[PIL.Image], torch.Tensor]
A torchvision transform that converts a PIL image into a tensor that the returned model can take as its input

import torch
import clip

device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

print(model)

结果:
image

clip.tokenize(text: Union[str, List[str]], context_length=77)

返回一个LongTensor,其中包含给定文本输入的标记化序列。这可以作为模型的输入使用。

import torch
import clip

device = "cuda" if torch.cuda.is_available() else "cpu"
text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)

print(text.shape)
#结果
torch.Size([3, 77])

对于clip.load()返回的model,有3个关键用法:

model.encode_image(image: Tensor)

给定一批图像,返回由CLIP模型的视觉部分编码的图像特征。

model.encode_text(text: Tensor)

给定一批文本标记,返回由CLIP模型的语言部分编码的文本特征。

model(image: Tensor, text: Tensor)

给定一批图像和一批文本标记,返回两个张量,分别包含与每个图像和文本输入相对应的日志得分。这些值是对应图像和文本特征之间的余弦相似度,乘以100。

官方demo:

import torch
import clip
from PIL import Image

device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

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]]

文本编码器

将文本["a diagram", "a dog", "a cat"]编码层text_featrue有几个步骤:

text = clip.tokenize(["a diagram", "a dog", "a cat"])
text_features = model.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

clip.tokenize()

该函数用于将文本字符串列表转换为可以输入到 CLIP 模型中的标记化张量。此函数处理标记化过程,包括添加特殊标记、填充和截断文本以适应模型的预期输入格式。

text = clip.tokenize(["i am a handsome boy"]
print(text)
#结果
ensor([49406,   328,   687,   320,  7438,  1876, 49407,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
            0,     0,     0,     0,     0,     0,     0], device='cuda:0',
       dtype=torch.int32)

49406表示截断,5个单词使用5个索引表示,77个长下文长度大学能表示70个单词左右的句子

self.token_embedding()

token_embedding 的作用是将输入的文本标记(tokens)转换为嵌入向量(embedding vectors)。这些嵌入向量是模型可以处理的数值表示形式。具体来说,token_embedding 是一个嵌入层(embedding layer),它将每个标记映射到一个高维向量空间中,这些向量捕捉了标记之间的语义关系。 在 CLIP 模型中,token_embedding 的输出形状为 [batch_size, n_ctx, d_model],其中:

  • batch_size 是输入批次的大小。
  • n_ctx 是上下文长度,表示每个输入序列的最大长度,这里是77。
  • d_model 是嵌入维度,表示每个标记的嵌入向量的维度。
    通过这种方式,文本数据可以被转换为模型可以处理的数值形式,从而进行进一步的计算和处理。

例子

import torch
import torch.nn as nn

# 假设我们有1000个词,每个词的向量维度是10
num_embeddings = 1000
embedding_dim = 5

# 定义一个Embedding层
embedding = nn.Embedding(num_embeddings, embedding_dim)

# 假设我们有一些词的索引
indices = torch.tensor([1, 2, 4, 5])

# 通过Embedding层获取这些词的向量表示
embeddings = embedding(indices)
print(embeddings)

# 结果
tensor([[-1.4052,  1.0736,  1.2210,  1.3236, -0.2328],
        [-0.5652,  0.0780,  0.5559, -0.3371,  0.3103],
        [ 0.6764, -0.4679, -0.4171,  0.3488, -0.9274],
        [ 1.4365, -0.2475,  0.2176, -1.9012,  0.4145]],
       grad_fn=<EmbeddingBackward0>)

每个单词 并非使用稀疏的one-hot编码表示,而是将整数索引映射到对应的固定大小的密集向量

self.positional_embedding

位置编码,本质上是一个可学习的torch,shape[77,512],通过广播机制和x(x.shape[3,77,512])相加

self.transformer()

Transformer 模型,它对输入数据进行一系列的变换和计算,通常包括多头自注意力机制和前馈神经网络层。

ln_final

LayerNorm(transformer_width),归一化

x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection

索引选择:x[torch.arange(x.shape[0]), text.argmax(dim=-1)] 从张量 x 中选择特定的元素。torch.arange(x.shape[0]) 生成一个从 0 到 x.shape[0] - 1 的序列,表示批次中的每个样本。text.argmax(dim=-1) 返回 text 张量中每个样本的最大值的索引。通过这种方式,可以从 x 中选择每个样本对应的特定元素。
矩阵乘法:@ self.text_projection 将选择的元素与 self.text_projection 进行矩阵乘法。self.text_projection 是一个投影矩阵,用于将选择的元素映射到另一个空间。
总结来说,这行代码从 x 中选择每个样本的特定元素,并将其投影到另一个空间。

posted @ 2024-11-29 11:42  seekwhale13  阅读(19)  评论(0编辑  收藏  举报