代码分析
import torch
import cn_clip.clip as clip
from PIL import Image
from cn_clip.clip import load_from_name, available_models
print("Torch version:", torch.__version__)
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Available models:", available_models())
# Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']
model, preprocess = load_from_name(
"ViT-B-16", device=device, download_root='./')
model.eval()
image = preprocess(Image.open("data_examples/truck.jpg")
).unsqueeze(0).to(device)
cls_list = ["狗", "汽车", "白色皮卡", "火车", "皮卡"]
text = clip.tokenize(cls_list).to(device)
with torch.no_grad():
image_features = model.encode_image(image)
text_features = model.encode_text(text)
# 对特征进行归一化,请使用归一化后的图文特征用于下游任务
image_features /= image_features.norm(dim=-1, keepdim=True)
text_features /= text_features.norm(dim=-1, keepdim=True)
logits_per_image, logits_per_text = model.get_similarity(image, text)
probs = logits_per_image.softmax(dim=-1).cpu().numpy()
from IPython.display import Image, display
display(Image(filename="data_examples/truck.jpg"))
for i in range(len(cls_list)):
print(f"{cls_list[i]}: {probs[0][i]}")
这段代码演示了如何使用 CLIP(Contrastive Language-Image Pretraining)模型进行图像和文本之间的匹配和相似性计算。特别是使用了中文版本的 CLIP(cn_clip
),即支持中文文本的 CLIP 模型。
让我们逐行分析这段代码,理解其功能和原理。
import torch
import cn_clip.clip as clip
from PIL import Image
from cn_clip.clip import load_from_name, available_models
import torch
: 导入 PyTorch,这是一个流行的深度学习框架,CLIP 模型依赖于它进行张量计算和模型推理。import cn_clip.clip as clip
: 从cn_clip
库中导入 CLIP 模型的核心功能,并命名为clip
。cn_clip
是 CLIP 的中文版本,支持中文文本输入。from PIL import Image
: 导入 PIL 库中的Image
模块,用于加载和处理图像。from cn_clip.clip import load_from_name, available_models
: 从cn_clip.clip
中导入load_from_name
函数和available_models
列表。load_from_name
用于加载指定名称的模型。available_models
是一个列表,包含了所有可用的 CLIP 模型名称。
print("Torch version:", torch.__version__)
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Available models:", available_models())
print("Torch version:", torch.__version__)
: 输出当前 PyTorch 的版本。device = "cuda" if torch.cuda.is_available() else "cpu"
: 检查是否有可用的 GPU,如果有,则使用cuda
设备,否则使用 CPU。print("Available models:", available_models())
: 输出所有可用的 CLIP 模型名称。
# Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']
model, preprocess = load_from_name("ViT-B-16", device=device, download_root='./')
model.eval()
model, preprocess = load_from_name("ViT-B-16", device=device, download_root='./')
:- 通过
load_from_name
函数加载指定的模型(这里是ViT-B-16
),并指定设备(GPU 或 CPU)和下载路径。 - 返回值
model
是加载的模型,preprocess
是一个预处理函数,用于将图像处理成模型可以接受的格式。
- 通过
model.eval()
: 将模型设置为评估模式。这是一个常见的步骤,尤其是在推理阶段,它禁用了 dropout 和批归一化的训练行为。
image = preprocess(Image.open("data_examples/truck.jpg")).unsqueeze(0).to(device)
cls_list = ["狗", "汽车", "白色皮卡", "火车", "皮卡"]
text = clip.tokenize(cls_list).to(device)
image = preprocess(Image.open("data_examples/truck.jpg")).unsqueeze(0).to(device)
:- 使用 PIL 打开指定路径的图像文件。
- 使用预处理函数
preprocess
处理图像,使其符合模型的输入要求。 unsqueeze(0)
增加一个批次维度,使图像的形状适应模型的输入需求(即批量大小)。- 使用
to(device)
将图像数据移动到指定设备(CPU 或 GPU)。
cls_list = ["狗", "汽车", "白色皮卡", "火车", "皮卡"]
:- 定义一个包含几个中文描述词的列表,用于与图像进行匹配。
text = clip.tokenize(cls_list).to(device)
:- 使用
clip.tokenize
函数将文本列表转换为模型可以接受的张量格式。 - 同样地,使用
to(device)
将文本数据移动到指定设备。
- 使用
with torch.no_grad():
image_features = model.encode_image(image)
text_features = model.encode_text(text)
# 对特征进行归一化,请使用归一化后的图文特征用于下游任务
image_features /= image_features.norm(dim=-1, keepdim=True)
text_features /= text_features.norm(dim=-1, keepdim=True)
logits_per_image, logits_per_text = model.get_similarity(image, text)
probs = logits_per_image.softmax(dim=-1).cpu().numpy()
with torch.no_grad():
:- 使用
torch.no_grad()
上下文管理器,禁用梯度计算,这可以减少内存消耗并加速计算,因为我们只是在进行推理而不是训练。
- 使用
image_features = model.encode_image(image)
:- 使用模型的
encode_image
方法将图像输入编码为图像特征向量。
- 使用模型的
text_features = model.encode_text(text)
:- 使用模型的
encode_text
方法将文本输入编码为文本特征向量。
- 使用模型的
image_features /= image_features.norm(dim=-1, keepdim=True)
和text_features /= text_features.norm(dim=-1, keepdim=True)
:- 对图像和文本的特征向量进行归一化处理。这一步确保特征向量的长度(即欧几里得范数)为1,便于后续的相似性计算。
logits_per_image, logits_per_text = model.get_similarity(image, text)
:- 使用模型的
get_similarity
方法计算图像和文本之间的相似性分数。这些分数是图像特征和文本特征之间的点积。
- 使用模型的
probs = logits_per_image.softmax(dim=-1).cpu().numpy()
:- 将图像的相似性分数通过 softmax 函数转换为概率分布,表示每个文本描述与图像匹配的概率。
- 使用
cpu()
将张量移动到 CPU,然后转换为 NumPy 数组以便于进一步处理或打印。
from IPython.display import Image, display
display(Image(filename="data_examples/truck.jpg"))
for i in range(len(cls_list)):
print(f"{cls_list[i]}: {probs[0][i]}")
from IPython.display import Image, display
:- 从
IPython.display
模块中导入Image
和display
函数,用于在 Jupyter Notebook 或 IPython 环境中显示图像。
- 从
display(Image(filename="data_examples/truck.jpg"))
:- 显示指定路径的图像文件。
for i in range(len(cls_list)):
和print(f"{cls_list[i]}: {probs[0][i]}")
:- 遍历文本描述列表
cls_list
,打印每个文本描述与图像的匹配概率。
- 遍历文本描述列表
CLIP 和 cn_clip
的区别和联系
-
CLIP: 由 OpenAI 提出的 CLIP 模型(Contrastive Language-Image Pretraining)是一个多模态模型,可以将图像和文本编码到同一个向量空间中,从而能够进行图像与文本之间的相似性匹配。CLIP 主要是用英语训练的,因此更适用于处理英文文本和图像匹配。
-
cn_clip
:cn_clip
是 CLIP 的中文版本,专为支持中文文本和图像之间的匹配而训练。它使用了中文语料进行预训练,可以处理和理解中文文本。
两者的联系:
- 相同的原理:CLIP 和
cn_clip
都基于相同的对比学习原理,将图像和文本映射到相同的向量空间中,并通过最大化正确图像-文本对之间的相似性和最小化错误对之间的相似性来进行训练。 - 不同的语料:CLIP 使用的是英文语料,而
cn_clip
使用的是中文语料。因此,它们分别在各自的语言领域内表现出色。
代码执行结果
PS E:\study\OPENAI-BASE-ENV> & D:/Python311/python.exe e:/study/OPENAI-BASE-ENV/fine-tuning-lab/web_demo/13_clip_en.py
Torch version: 2.3.0+cu121
Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']
Loading vision model config from D:\Python311\Lib\site-packages\cn_clip\clip\model_configs\ViT-B-16.json
Loading text model config from D:\Python311\Lib\site-packages\cn_clip\clip\model_configs\RoBERTa-wwm-ext-base-chinese.json
Model info {'embed_dim': 512, 'image_resolution': 224, 'vision_layers': 12, 'vision_width': 768, 'vision_patch_size': 16, 'vocab_size': 21128, 'text_attention_probs_dropout_prob': 0.1, 'text_hidden_act': 'gelu', 'text_hidden_dropout_prob': 0.1, 'text_hidden_size': 768, 'text_initializer_range': 0.02, 'text_intermediate_size': 3072, 'text_max_position_embeddings': 512, 'text_num_attention_heads': 12, 'text_num_hidden_layers': 12, 'text_type_vocab_size': 2}
D:\Python311\Lib\site-packages\torch\nn\functional.py:5504: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at ..\aten\src\ATen\native\transformers\cuda\sdp_utils.cpp:455.)
attn_output = scaled_dot_product_attention(q, k, v, attn_mask, dropout_p, is_causal)
<IPython.core.display.Image object>
狗: 1.3709068298339844e-06
汽车: 0.0012388229370117188
白色皮卡: 0.705078125
火车: 2.6524066925048828e-05
皮卡: 0.2939453125
分析代码执行过程与输出结果
环境和模型信息
Torch version: 2.3.0+cu121
Available models: ['ViT-B-16', 'ViT-L-14', 'ViT-L-14-336', 'ViT-H-14', 'RN50']
Loading vision model config from D:\Python311\Lib\site-packages\cn_clip\clip\model_configs\ViT-B-16.json
Loading text model config from D:\Python311\Lib\site-packages\cn_clip\clip\model_configs\RoBERTa-wwm-ext-base-chinese.json
- Torch version: 2.3.0+cu121: 指出使用的 PyTorch 版本是 2.3.0,并且包含了 CUDA 121 支持,这表明模型可以在 GPU 上加速运行。
- Available models: …: 列出
cn_clip
库中可用的模型名称,这些模型是不同的视觉和文本模型组合,支持不同的应用场景。 - Loading vision model config…: 加载视觉模型(ViT-B-16)的配置文件,这个文件包含了视觉模型的架构和参数信息。
- Loading text model config…: 加载文本模型(RoBERTa-wwm-ext-base-chinese)的配置文件,这个文件包含了文本模型的架构和参数信息。
模型结构信息
Model info {'embed_dim': 512, 'image_resolution': 224, 'vision_layers': 12, 'vision_width': 768, 'vision_patch_size': 16, 'vocab_size': 21128, 'text_attention_probs_dropout_prob': 0.1, 'text_hidden_act': 'gelu', 'text_hidden_dropout_prob': 0.1, 'text_hidden_size': 768, 'text_initializer_range': 0.02, 'text_intermediate_size': 3072, 'text_max_position_embeddings': 512, 'text_num_attention_heads': 12, 'text_num_hidden_layers': 12, 'text_type_vocab_size': 2}
这个输出提供了模型的详细配置:
- embed_dim: 嵌入维度,512 表示图像和文本的特征向量维度是 512。
- image_resolution: 图像分辨率,224 表示输入图像的尺寸为 224x224 像素。
- vision_layers: 视觉模型的层数,这里是 12 层。
- vision_width: 视觉模型每层的宽度,即每层的通道数为 768。
- vision_patch_size: 每个图像块的大小,16 表示图像被分割成 16x16 的小块。
- vocab_size: 文本模型的词汇表大小,21128 表示可以处理的不同词汇的数量。
- **text_*: 文本模型的其他参数,如注意力概率的 dropout 率、隐藏层的激活函数(gelu)、最大位置嵌入数(512)等。
- text_num_hidden_layers: 文本模型的隐藏层数,12 层。
警告信息
D:\Python311\Lib\site-packages\torch\nn\functional.py:5504: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at ..\aten\src\ATen\native\transformers\cuda\sdp_utils.cpp:455.)
attn_output = scaled_dot_product_attention(q, k, v, attn_mask, dropout_p, is_causal)
- UserWarning: 这是一个警告,表明当前的 PyTorch 版本没有使用 “flash attention” 优化。这种优化可以加速自注意力机制的计算,但在没有这种优化的情况下,模型依然可以正常工作,只是性能上可能会稍微差一点。
显示图像
<IPython.core.display.Image object>
- 这是在代码中使用
display(Image(filename="data_examples/truck.jpg"))
语句显示的图像。这一步只是展示输入图像,帮助我们更直观地理解后续的文本与图像匹配结果。
匹配结果输出
狗: 1.3709068298339844e-06
汽车: 0.0012388229370117188
白色皮卡: 0.705078125
火车: 2.6524066925048828e-05
皮卡: 0.2939453125
这些结果是模型计算出每个文本标签与输入图像的匹配概率。解释这些概率的含义可以帮助我们理解模型的工作原理:
-
狗: 1.3709068298339844e-06:
- 非常低的概率,表明模型认为图像与“狗”几乎没有匹配度。
-
汽车: 0.0012388229370117188:
- 较低的概率,表明图像与“汽车”不太匹配,尽管比“狗”略高。
-
白色皮卡: 0.705078125:
- 很高的概率,表明图像与“白色皮卡”有很高的匹配度。由于图像实际上是一辆白色的卡车,这与这个标签非常吻合。
-
火车: 2.6524066925048828e-05:
- 很低的概率,表明图像与“火车”几乎没有匹配度。
-
皮卡: 0.2939453125:
- 适中的概率,表明图像与“皮卡”有一定的匹配度。由于“皮卡”也可以指某种卡车,尽管不如“白色皮卡”那么精确,这个结果是合理的。
原理解释
-
特征提取与匹配:
- CLIP 模型通过视觉模型(ViT-B-16)将输入图像编码为特征向量,并通过文本模型(基于 RoBERTa 的中文模型)将文本描述编码为特征向量。
- 这些特征向量被映射到一个共享的向量空间中,使得相似的图像和文本特征向量在空间中靠得更近。
-
相似性计算:
- 通过计算图像特征向量与每个文本特征向量之间的点积,得到它们的相似性分数(logits)。
- 然后,这些分数通过 softmax 函数转换为概率,表示每个文本与图像匹配的概率。
-
归一化处理:
- 在计算相似性之前,对图像和文本的特征向量进行归一化,使得每个向量的长度为1。这有助于稳定计算,并使得点积的结果直接反映向量之间的角度相似性。
clip
和 cn_clip
的区别与联系
clip
: 是 OpenAI 提供的基于英语训练的多模态模型,用于图像和英文文本之间的匹配。cn_clip
: 是clip
的中文版本,针对中文文本进行了调整和优化,使用了中文语料进行预训练,更适合处理中文的多模态任务。
两者的核心思想和模型架构相似,主要区别在于训练语料和处理的语言不同。对于中文应用,cn_clip
提供了更好的性能和支持。
使用场景
这些模型可以广泛应用于:
- 图片搜索与推荐: 根据文字描述找到最匹配的图像,或根据图像找到相关的描述。
- 自动图像标注: 为图像生成准确的文字标签。
- 多模态问答系统: 根据图像和文本进行智能问答。
- 电子商务: 根据用户输入的文本描述推荐合适的产品图像。
通过上述代码和结果的分析,我们可以看出,CLIP 模型在处理多模态任务时具有强大的功能和灵活性,尤其是在不同语言环境下,如中文,通过 cn_clip
的支持,可以更好地满足特定语言的应用需求。