
今天我们继续展开CLIP 模型的精细度解析,遵循 “理论铺垫→基础实践→进阶深化”的过程,初次接触的建议先看看基础篇《CLIP 模型全维度解析:理解理论基础强化基础范例.16》,先建立 CLIP 的核心认知,再逐步拆解高阶应用的底层逻辑,遵循 “是什么→为什么→怎么用→怎么用好“的学习逻辑。
CLIP,全称Contrastive Language-Image Pre-training,是由OpenAI 2021年发布的跨模态预训练模型,核心是让模型学会图像和文本的语义对应关系,比如看到猫的图片,能对应到 “a photo of a cat” 这个文本;看到 “一辆红色的车”,能匹配到对应的图片。
跨模态学习
对比学习
零样本学习

流程说明:
一、训练阶段(A)
二、推理阶段:零样本分类(B)
三、推理阶段:图像检索(C)
整个流程中,训练阶段得到的预训练模型(图像编码器和文本编码器)被用于两个推理阶段。训练阶段通过对比学习使图像和文本在同一个特征空间中对齐,从而使得在推理阶段可以通过计算特征向量的相似度来进行零样本分类或图像检索。
核心总结:

关键细节:
在 CLIP 出现前,计算机视觉(CV)和自然语言处理(NLP)是两个 “割裂” 的领域:
CLIP 的核心价值:打破 CV 和 NLP 的模态壁垒,用 “无标注图文对” 训练出通用模型,无需标注即可适配新的视觉任务。
CLIP的训练过程,可以比喻为在一张白纸上绘制一张精确的“语义地图”。图像和文本是两种不同的语言,CLIP的任务是创建一个“通用翻译坐标系”,让这两种语言可以互相对照。
CLIP的数据获取可以抽象的理解为从互联网的混沌中汲取智慧,它吞噬了从互联网上公开收集的约4亿个 (图像, 文本) 对。这些文本不是干净的类别标签,而是网页的alt属性、图片标题、描述文字等。它们是嘈杂的、多样化的、自然产生的。这一点至关重要,因为:
CLIP采用经典的“双塔”架构,即双塔编码器,这是其简洁性和有效性的关键。
核心设计要求:两个编码器的输出,必须是同维度的向量,并且我们将这些向量的长度归一化为1(即放在一个单位超球面上)。这是为了让图像和文本的特征可以直接进行数学比较。

这是CLIP的核心所在。它不要求图像和文本特征完全相等,只要求匹配的对的特征比不匹配的对更相似。
具体操作流程:
这个过程的直观比喻:
经过在海量数据上、以超大批次、进行长时间的优化后,两个编码器内部参数被调整到最佳状态。此时,它们就像两台精密的翻译机,能将任何输入的图像和文本,都翻译成同一种“语义坐标语言”,并且确保“说的是一回事”的两个翻译,其坐标非常接近。
训练完成后,我们就得到了一张绘制好的、高度结构化的语义地图。推理,就是在这张地图上进行查询和测量。
从“这是什么”到“这和哪个描述最配”,这是CLIP最引人注目的能力。其过程极其优雅,完全复用了训练时学到的相关性判断能力。
详细步骤分解:
通俗理解:
在语义空间中寻最近邻,这几乎是零样本分类的直接扩展。
单一模板可能有偏见:
在指定图片库中检索,输入文本,提取文本特征;计算文本特征与所有图片特征的相似度;按相似度排序,返回最匹配的图片。
import torch
import clip
from PIL import Image
import os
import numpy as np
import matplotlib.pyplot as plt
# 解决中文显示
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 初始化模型
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
# 1. 构建图片库(初学者先创建image_library文件夹,放入几张测试图片)
image_dir = "test_images/"
if not os.path.exists(image_dir):
os.makedirs(image_dir)
print("请在test_images文件夹中放入测试图片后重新运行!")
exit()
# 获取所有图片路径
image_paths = [os.path.join(image_dir, f) for f in os.listdir(image_dir)
if f.endswith((".jpg", ".png"))]
if not image_paths:
print("图片库为空!")
exit()
# 2. 批量提取图片特征(初学者注意批量处理的效率)
image_features = []
with torch.no_grad():
for path in image_paths:
img = preprocess(Image.open(path)).unsqueeze(0).to(device)
feat = model.encode_image(img)
feat /= feat.norm(dim=-1, keepdim=True)
image_features.append(feat.cpu().numpy())
# 转换为数组,方便计算
image_features = np.concatenate(image_features, axis=0)
# 3. 输入文本,提取文本特征
text_query = "a cute cat" # 初学者可替换成自己的查询文本
text = clip.tokenize([text_query]).to(device)
with torch.no_grad():
text_feature = model.encode_text(text)
text_feature /= text_feature.norm(dim=-1, keepdim=True)
text_feature = text_feature.cpu().numpy()
# 4. 计算相似度并排序
similarities = np.dot(text_feature, image_features.T).squeeze(0)
# 按相似度降序排序
sorted_idx = similarities.argsort()[::-1]
# 5. 输出结果(初学者重点看Top3)
print(f"文本查询:{text_query}")
print("匹配结果(Top3):")
for i, idx in enumerate(sorted_idx[:3]):
print(f" Top{i+1}: {image_paths[idx]} → 相似度:{similarities[idx]:.4f} (比例:{similarities[idx]/similarities.max():.2%})")
# 6. 生成结果图片并显示原图和匹配结果
# 创建结果图片
plt.figure(figsize=(15, 10))
plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.05, wspace=0.1, hspace=0.1)
# 显示原图
plt.subplot(2, 2, 1)
original_img = Image.open(image_paths[sorted_idx[0]])
plt.imshow(original_img)
plt.title(f"匹配图: {image_paths[sorted_idx[0]]}")
plt.axis('off')
# 显示Top1匹配结果
plt.subplot(2, 2, 2)
top1_img = Image.open(image_paths[sorted_idx[0]])
plt.imshow(top1_img)
plt.title(f"Top1: 相似度 {similarities[sorted_idx[0]]:.4f} (比例: {similarities[sorted_idx[0]]/similarities.max():.2%})")
plt.axis('off')
# 显示Top2匹配结果
plt.subplot(2, 2, 3)
top2_img = Image.open(image_paths[sorted_idx[1]])
plt.imshow(top2_img)
plt.title(f"Top2: 相似度 {similarities[sorted_idx[1]]:.4f} (比例: {similarities[sorted_idx[1]]/similarities.max():.2%})")
plt.axis('off')
# 显示Top3匹配结果
plt.subplot(2, 2, 4)
top3_img = Image.open(image_paths[sorted_idx[2]])
plt.imshow(top3_img)
plt.title(f"Top3: 相似度 {similarities[sorted_idx[2]]:.4f} (比例: {similarities[sorted_idx[2]]/similarities.max():.2%})")
plt.axis('off')
# 保存结果图片
result_path = "result_comparison.png"
plt.savefig(result_path)
plt.close()
# 显示结果图片
result_img = Image.open(result_path)
result_img.show(title="匹配结果对比")输出结果:
文本查询:a cute cat 匹配结果(Top3): Top1: test_images/cat.jpg → 相似度:0.2803 (比例:100.00%) Top2: test_images/cat2.jpg → 相似度:0.2451 (比例:87.44%) Top3: test_images/dog.jpg → 相似度:0.2023 (比例:72.17%)

import torch
import clip
from PIL import Image
import numpy as np
# 初始化模型(重复代码,初学者可直接复用)
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
# 1. 自定义分类类别(初学者可替换成自己的类别)
categories = ["cat", "dog", "car", "tree", "book"]
# 2. 基础模板(初学者先掌握)
template = "a photo of a {}"
# 生成文本提示
text_prompts = [template.format(cat) for cat in categories]
text = clip.tokenize(text_prompts).to(device)
# 3. 加载待分类图片
image_path = "street.jpg"
image = preprocess(Image.open(image_path)).unsqueeze(0).to(device)
# 4. 推理
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)
# 计算相似度
similarity = (image_features @ text_features.T).squeeze(0).cpu().numpy()
# 5. 预测结果
pred_idx = np.argmax(similarity)
pred_category = categories[pred_idx]
# 输出(初学者重点看各类别得分)
print("各类别相似度得分:")
for cat, score in zip(categories, similarity):
print(f" {cat}: {score:.4f}")
print(f"\n预测类别:{pred_category}")
# 仅修改模板部分,其余代码与基础版一致
categories = ["cat", "dog", "car", "tree", "book"]
# 多模板(初学者可直接复用)
templates = [
"a photo of a {}",
"a picture of a {}",
"an image of a {}",
"a photo of the {}"
]
# 生成所有模板的文本提示
text_prompts = []
for cat in categories:
for template in templates:
text_prompts.append(template.format(cat))
text = clip.tokenize(text_prompts).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)
similarity = (image_features @ text_features.T).squeeze(0).cpu().numpy()
# 聚合相似度(初学者重点理解这一步)
category_scores = []
for i in range(len(categories)):
# 每个类别对应len(templates)个模板
start = i * len(templates)
end = start + len(templates)
avg_score = similarity[start:end].mean() # 取平均
category_scores.append(avg_score)
# 预测
pred_idx = np.argmax(category_scores)
pred_category = categories[pred_idx]
print("多模板聚合后得分:")
for cat, score in zip(categories, category_scores):
print(f" {cat}: {score:.4f}")
import matplotlib.pyplot as plt
# 创建子图
plt.figure(figsize=(15, 6))
# 左侧显示原始图片
plt.subplot(1, 2, 1)
img = Image.open(image_path)
plt.imshow(img)
plt.axis('off')
plt.title('Original Image')
# 右侧显示柱状图
plt.subplot(1, 2, 2)
plt.bar(categories, category_scores, color='skyblue')
plt.xlabel('Categories')
plt.ylabel('Similarity Score')
plt.title('Multi-Template Aggregated Scores')
plt.xticks(rotation=45)
plt.tight_layout()
# 保存图片
plt.savefig('similarity_scores.png')
plt.show()
print(f"\n预测类别:{pred_category}")输出结果:
各类别相似度得分: cat: 0.2453 dog: 0.2394 car: 0.1951 tree: 0.1563 book: 0.1933 预测类别:cat 多模板聚合后得分: cat: 0.2461 dog: 0.2416 car: 0.1935 tree: 0.1609 book: 0.1912 预测类别:cat
原图:

结果图:

CLIP是一个逻辑清晰、目标纯粹、规模化执行的多模态关联引擎。需要通过对比学习,在共享空间中对齐图像和文本的语义,并通过前所未有的大数据、大批次、大模型训练,从而实现将下游任务(分类、检索)巧妙地转化为预训练任务(图文相关性评估)的实例,达到完成准确推理的目的。
我们需要理解CLIP的本质,它是一个大规模模式匹配器,它不理解内容,但能从4亿个例子中学会什么东西通常和什么描述一起出现。用好它的关键在于让我们的使用方式尽量接近它的训练方式。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。