深入解析CLIP与CoOp的文本编码器差异与优化策略

1. 从“看图说话”到“看图懂话”:CLIP与CoOp的核心使命

大家好,我是老张,在AI和计算机视觉这个行当里摸爬滚打了十几年。今天想和大家聊聊两个特别有意思的模型:CLIPCoOp。很多刚入门的朋友可能会被这两个名字唬住,觉得它们高深莫测。其实,我们可以用一个特别生活化的场景来理解它们:给图片找最匹配的文字描述

想象一下,你手机里有一张你家小狗的照片。你问AI:“这张图里是什么?”一个简单的模型可能会根据训练数据,猜是“狗”、“动物”或者“宠物”。但CLIP和CoOp这类模型要做的事情更高级:它们不是死记硬背,而是真正去理解图片内容和文字描述之间的关联。CLIP就像一个博览群书、见多识广的“通才”,它通过海量的“图片-文本”对进行训练,学会了将任何图片和任何文本在同一个语义空间里对齐。你给它一张小狗图,再给它“一只在奔跑的柯基”、“一辆红色的汽车”、“一片宁静的湖泊”这三个文本选项,它能非常准确地计算出图片和“柯基”描述最相似。

但CLIP这个“通才”有个小问题:它太通用了。当你把它用在一个非常具体的领域,比如医学影像识别(区分肺炎和正常胸片)或者细粒度鸟类分类时,它那套通用的“语言”可能就不够精准了。这时候,CoOp 就登场了。你可以把CoOp看作是CLIP的“专项特训生”。它继承了CLIP强大的视觉和文本理解骨架,但通过一种叫“可学习提示(Learnable Prompts)”的巧妙方法,让模型学会了用你这个专业领域的“行话”来思考和描述。它的目标不是重新发明轮子,而是优化CLIP的文本编码器,让它在你关心的任务上表现得更出色、更精准。

所以,简单来说,CLIP搭建了一个通用的“视觉-语言”理解桥梁,而CoOp则是在这座桥上,为特定的目的地修建了更快捷、更平稳的专用车道。今天,我们就来深入这座桥的“文本侧”施工细节,看看两者的文本编码器到底有何不同,以及CoOp是如何通过优化策略,让模型性能更上一层楼的。

2. 庖丁解牛:拆解CLIP文本编码器的两大核心组件

要理解CoOp的优化妙处,我们必须先吃透CLIP文本编码器的工作原理。很多教程会直接扔给你一个encode_text函数,但今天我们像拆解精密仪器一样,一步步来看。CLIP处理文本,最核心的就两步:把文字变成数学(Token Embedding),以及告诉模型文字的先后顺序(Positional Embedding)

2.1 Token Embedding:从文字到向量的“翻译官”

首先,CLIP怎么“读”文字呢?它不像我们一样看字符,而是看“令牌”(Token)。比如句子“a photo of a cat”,经过CLIP的分词器(tokenizer)处理,会变成一串数字ID,比如 [49406, 320, 1125, 539, 320, 2368, 49407, 0, 0, ...]。这里的49406代表句子开始,49407代表句子结束,0是填充位(因为CLIP固定输入长度是77)。

光有数字ID没用,模型看不懂。token_embedding 就是一个巨大的“密码本”(在PyTorch里是 nn.Embedding 层)。它的作用就是把这些数字ID,映射成一个高维的、稠密的向量。假设我们的模型维度(d_model)是512,那么一个长度为77的ID序列,经过token_embedding,就从形状 [batch_size, 77] 变成了 [batch_size, 77, 512]

你可以把这个过程想象成:每个单词(Token)原本只是一个孤立的、没有意义的编号(比如“猫”是2368)。通过查询“密码本”,我们为每个编号找到了一个在512维空间里的“家”。这个“家”的坐标(即512维向量)是在预训练过程中,通过看海量文本和图片一起学会的,它编码了这个单词的语义信息。“猫”和“狗”的向量在空间里会比较接近,而“猫”和“汽车”的向量就离得远。

我们来看一段简化的代码,帮你建立直观感受:

import torch
import clip

# 加载模型和分词器
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

# 对文本进行分词,得到数字ID
text_inputs = clip.tokenize(["a photo of a cat", "a picture of a dog"]).to(device)
print("分词后的文本ID形状:", text_inputs.shape)  # 输出: torch.Size([2, 77])

# 获取token_embedding层
token_embedding_layer = model.token_embedding

# 将ID转换为向量
with torch.no_grad():
    token_embeddings = token_embedding_layer(text_inputs)
    print("Token Embedding后的形状:", token_embeddings.shape)  # 输出: torch.Size([2, 77, 512])

这段代码清晰地展示了从文本到向量的转换。token_embeddings 里的每一个 [512] 维向量,都代表了一个Token的语义。

2.2 Positional Embedding:为句子注入“顺序感”

好了,现在我们有了一堆代表单词的向量,但模型还不知道这些单词谁先谁后。“猫 a photo of a” 和 “a photo of a cat” 的意思天差地别。这就是 positional_embedding 出场的时候了。

positional_embedding 也是一个可学习的参数(nn.Parameter),它的形状是 [77, 512]。这代表序列中77个位置,每个位置都有一个独有的、512维的“位置向量”。这个向量编码了“这是句子中的第几个词”的信息。

关键操作来了:在CLIP的 encode_text 函数里,Token Embedding 和 Positional Embedding 是直接相加的

# 这是CLIP encode_text函数的核心步骤(简化版)
x = token_embeddings + positional_embedding

这个“相加”操作是Transformer架构的精华所在。它相当于在告诉模型:“这个向量不仅代表了‘猫’这个词的意思,还代表了它出现在这个句子的第五个位置。”通过这种相加,模型同时获得了词汇语义序列顺序两种信息。

那么,这个位置向量是怎么学来的呢?它是在CLIP预训练过程中,和模型其他部分一起被优化出来的。模型通过完成“预测图片匹配文本”的任务,自己摸索出什么样的位置信息表示对任务最有帮助。这比早期Transformer使用固定的正弦余弦函数来表示位置更加灵活。

小结一下:CLIP的文本编码器,通过 token_embedding 将离散的文字符号转化为连续的语义向量,再通过 positional_embedding 为这些向量注入顺序信息。两者相加后,送入后续的Transformer层进行深层次的交互和特征提取,最终输出一个能代表整个句子语义的、固定长度的文本特征向量。

3. CoOp的革新:当“提示”成为可学习的参数

理解了CLIP的运作机制,我们再来看CoOp,就会有一种“哦,原来如此”的顿悟感。CoOp的核心思想非常巧妙:既然CLIP的文本端这么强大,我们何必为了一个新任务去大动干戈地重新训练整个文本编码器呢?不如只动几个关键的地方,让它适应新任务。

这个“关键的地方”,就是提示(Prompt)。在原始CLIP中,提示是人工设计的模板,比如“a photo of a {label}.”。CoOp的作者思考:这个模板就一定是最优的吗?对于不同的任务(识别鸟类 vs. 识别汽车),最好的描述方式可能不同。于是,他们提出了 “可学习的提示”

3.1 从静态模板到动态参数:PromptLearner的设计

CoOp引入了一个新的模块叫 PromptLearner。它的任务不是像CLIP那样从固定的词汇表里查token_embedding,而是直接生成一组可优化的向量,作为文本编码器的输入

我们来看它是怎么初始化的。假设我们有N个类别(比如100种不同的鸟类),CoOp会为每个类别定义一段可学习的上下文(Context)向量,记作 ctx。初始化时,它可能只是一些随机值:

# 假设我们有n_ctx个可学习的上下文token(例如4个),模型维度dim=512
n_ctx = 4
dim = 512
# 为每个类别初始化上下文向量
ctx_vectors = torch.empty(n_ctx, dim)  # 形状: [4, 512]
nn.init.normal_(ctx_vectors, std=0.02)  # 用小的随机值初始化
self.ctx = nn.Parameter(ctx_vectors)  # 将其定义为可学习参数

然后,对于每个类别,比如“北方红衣凤头鸟”,CoOp会构建这样一个提示序列: [SOS] + [可学习的上下文向量] + [类名] + [EOS]

  • [SOS][EOS] 是CLIP固定的开始和结束符的嵌入向量,直接从CLIP的 token_embedding 里取,在训练中冻结(不更新)
  • [类名] 也是从CLIP的 token_embedding 里取对应单词的向量,同样冻结
  • 只有中间那部分 [可学习的上下文向量](即 self.ctx)是可学习的参数

这样,对于不同的类别,它们共享同一套可学习的上下文向量,但拼接上各自不同的类名。在训练时,模型通过对比学习(对比图片特征和这些文本特征的相似度),反向传播会不断调整这组 ctx 向量,让它们学会如何组织“语言”,才能最好地描述当前视觉任务中的类别。

3.2 文本编码器的“瘦身”与重构:TextEncoder的变化

由于输入不再是原始的单词ID,而是已经构建好的向量序列(prompts),CoOp的 TextEncoder 相比CLIP就做了一个重大的简化:它完全移除了 token_embedding 层!

我们对比一下两者的前向传播过程:

CLIP TextEncoder 核心步骤:

  1. 输入:单词ID text (形状 [batch, 77]
  2. 查表:x = self.token_embedding(text) (得到 [batch, 77, 512]
  3. 加位置:x = x + self.positional_embedding
  4. 过Transformer等后续层。

CoOp TextEncoder 核心步骤:

  1. 输入:已经构建好的提示向量 prompts (形状 [n_cls, 77, 512],n_cls是类别数)
  2. 直接加位置:x = prompts + self.positional_embedding
  3. 过Transformer等后续层。

看到了吗?CoOp的文本编码器跳过了“从ID到向量”的查表步骤,因为 PromptLearner 已经提供了优化好的向量。它直接进行位置编码和后续处理。这里的 positional_embedding 依然是CLIP预训练好的那个,在CoOp训练时通常也是可学习的,可以和 ctx 一起被微调,以更好地适应新任务的数据分布。

这种设计带来了巨大的优势:

  1. 参数效率极高:我们只需要学习很少的几个上下文向量(比如4个 * 512维 = 2048个参数),加上微调位置编码,就能让一个庞大的CLIP模型适应新任务。
  2. 避免了离散优化难题:如果去优化具体的单词,那是一个离散空间搜索问题,非常困难。而优化连续的向量空间,可以直接利用梯度下降,简单高效。
  3. 保留了CLIP的全部知识:视觉编码器、文本编码器的Transformer层、投影层全部保持冻结,CLIP从海量数据中学到的通用视觉-语言知识得以完整保留。

4. 实战对比:手把手跑通CLIP与CoOp的推理流程

理论说得再多,不如亲手跑一遍代码来得实在。下面我带大家写两段简单的推理代码,分别用原始的CLIP和CoOp的方式,来对同一张图片进行分类。你可以清晰地看到数据流的差异。

4.1 原始CLIP的零样本推理

假设我们有一张哈士奇的照片,想用CLIP判断它是“哈士奇”、“金毛”还是“汽车”。

import torch
import clip
from PIL import Image

# 1. 加载模型和预处理函数
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)

# 2. 准备图片
image = preprocess(Image.open("husky.jpg")).unsqueeze(0).to(device)

# 3. 手动构建文本提示(这是关键!)
class_names = ["husky", "golden retriever", "car"]
# 使用CLIP经典的提示模板
text_descriptions = [f"a photo of a {name}." for name in class_names]
# 分词并编码
text_inputs = clip.tokenize(text_descriptions).to(device)

# 4. 提取特征并计算相似度
with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text_inputs)
    
    # 归一化(计算余弦相似度前的标准操作)
    image_features = image_features / image_features.norm(dim=-1, keepdim=True)
    text_features = text_features / text_features.norm(dim=-1, keepdim=True)
    
    # 计算相似度(logits)
    logit_scale = model.logit_scale.exp()
    logits_per_image = logit_scale * image_features @ text_features.t()
    
    # 转换为概率
    probs = logits_per_image.softmax(dim=-1).cpu().numpy()

print("CLIP预测概率:")
for name, prob in zip(class_names, probs[0]):
    print(f"  {name}: {prob:.4f}")

这段代码中,文本端是完全固定的text_descriptions 是我们人工设计的模板,encode_text 函数会走完我们第二章讲的所有流程(Token Embedding -> + Positional -> Transformer...)。

4.2 CoOp风格的提示学习推理

现在,我们模拟CoOp已经训练好的情况。假设我们有一个训练好的 PromptLearner,它已经为“宠物狗分类”这个任务学习到了一组最优的上下文向量。

# 假设这是我们训练好的CoOp模型的一部分
class SimpleCoOPInference:
    def __init__(self, clip_model, learned_ctx, class_names):
        self.clip_model = clip_model
        self.dtype = clip_model.dtype
        # learned_ctx 形状: [n_ctx, dim],例如 [4, 512],这是训练好的
        self.ctx = learned_ctx
        self.class_names = class_names
        
        # 1. 构建冻结的前后缀
        # SOS 和 EOS 的token ID是固定的
        self.sos_token_id = clip.tokenize("")[0, 0] # 获取SOS的ID
        self.eos_token_id = clip.tokenize("")[0, 1] # 获取EOS的ID(假设在第二个位置)
        
        with torch.no_grad():
            sos_embedding = clip_model.token_embedding(torch.tensor([[self.sos_token_id]]).to(device)).type(self.dtype)
            eos_embedding = clip_model.token_embedding(torch.tensor([[self.eos_token_id]]).to(device)).type(self.dtype)
            
        self.register_buffer("token_prefix", sos_embedding) # [1, 1, dim]
        self.register_buffer("token_suffix", eos_embedding) # [1, 1, dim]
        
        # 2. 为每个类名获取其token embedding(冻结)
        self.class_embeddings = []
        for name in class_names:
            tokenized_name = clip.tokenize([name]).to(device)
            with torch.no_grad():
                # 只取类名部分的embedding(去掉SOS和EOS)
                name_embed = clip_model.token_embedding(tokenized_name).type(self.dtype)[0, 1:-1, :] # 形状不定
                self.class_embeddings.append(name_embed)
    
    def get_prompts(self):
        """为所有类别生成提示向量"""
        all_prompts = []
        for name_embed in self.class_embeddings:
            # 拼接: [SOS] + [ctx] + [class] + [EOS]
            # 注意维度对齐,这里是一个简化示意
            prompt = torch.cat([
                self.token_prefix,          # [1, 1, dim]
                self.ctx.unsqueeze(0),      # [1, n_ctx, dim]
                name_embed.unsqueeze(0),    # [1, name_len, dim]
                self.token_suffix           # [1, 1, dim]
            ], dim=1) # 在序列长度维度拼接
            all_prompts.append(prompt)
        # 将所有类别的提示堆叠
        prompts = torch.cat(all_prompts, dim=0) # [n_cls, total_len, dim]
        return prompts

# 使用模拟的CoOp进行推理
class_names = ["husky", "golden retriever", "car"]
# 假设我们有一个训练好的上下文向量,这里用随机值模拟
n_ctx = 4
learned_ctx = torch.randn(n_ctx, 512).to(device) # 模拟训练好的参数

coop_infer = SimpleCoOPInference(model, learned_ctx, class_names)
prompts = coop_infer.get_prompts() # 形状: [3, 77, 512]

# 使用修改后的TextEncoder(移除token_embedding)进行编码
with torch.no_grad():
    # 注意:这里需要用到CoOp的TextEncoder,它接受prompts而不是token IDs
    # 为演示,我们简化处理:直接将prompts加上位置编码后,取EOS位置的特征作为文本特征
    # 实际CoOp的TextEncoder是一个完整的Transformer编码器
    positional_embedding = model.positional_embedding.type(model.dtype)
    x = prompts + positional_embedding[:prompts.size(1)] # 添加位置信息
    # ... 这里应通过Transformer编码器等步骤,最终得到文本特征 text_features
    # 假设我们得到了 text_features
    
    # 后续计算与CLIP相同
    image_features = model.encode_image(image)
    image_features = image_features / image_features.norm(dim=-1, keepdim=True)
    text_features = text_features / text_features.norm(dim=-1, keepdim=True)
    logits_per_image = model.logit_scale.exp() * image_features @ text_features.t()
    probs = logits_per_image.softmax(dim=-1).cpu().numpy()

print("CoOp预测概率:")
for name, prob in zip(class_names, probs[0]):
    print(f"  {name}: {prob:.4f}")

通过对比可以看到,CoOp的推理流程中,文本输入不再是原始的单词,而是由可学习参数ctx和冻结的类名嵌入构建好的向量。模型优化的目标,就是找到那组能让“哈士奇”图片特征与“[SOS] + [最优上下文] + husky + [EOS]”这个向量最匹配的 ctx

5. 优化策略深潜:如何训练一个更好的CoOp模型

如果你打算在自己的数据集上应用CoOp,那么理解其训练细节和优化策略至关重要。这里我结合自己的实战经验,分享几个关键点和容易踩的坑。

5.1 训练循环与损失函数

CoOp的训练本质上是提示向量(Context)的微调。视觉编码器、文本编码器的Transformer层等大部分参数都是冻结的。训练数据就是你的目标数据集,比如一个狗品种分类数据集,每张图片都有标签。

训练循环的核心步骤如下:

  1. 前向传播

    • 图片输入冻结的CLIP视觉编码器,得到图像特征 image_features
    • PromptLearner 根据当前可学习的 ctx 和类别名,生成当前批次的 prompts 向量。
    • prompts 输入到修改后的 TextEncoder(没有token_embedding的那版),得到文本特征 text_features
    • 对图像和文本特征进行L2归一化,然后计算缩放的余弦相似度作为logits。
  2. 计算损失

    • 损失函数就是最经典的交叉熵损失。将上面得到的logits看作每个类别的得分,与图片的真实标签计算损失。
    • 这里的关键是,logits是通过对比图像特征和所有类别的文本特征得到的,这自然形成了一个分类任务。
  3. 反向传播与优化

    • 损失反向传播,只会更新那些被设置为可学习的参数。主要包括:
      • PromptLearner 中的上下文向量 ctx
      • (可选)文本编码器中的 positional_embedding
      • (可选,但CoOp原论文通常冻结)logit_scale 参数。
    • 使用像AdamW这样的优化器进行更新。

一个简化的训练代码框架如下:

# 假设我们已经定义了 CustomCLIP 类(包含PromptLearner和TextEncoder)
model = CustomCLIP(cfg, classnames, clip_model).to(device)
# 只优化prompt相关的参数和可能的位置编码
optimizer = torch.optim.AdamW([
    {'params': model.prompt_learner.parameters()},
    {'params': model.text_encoder.positional_embedding, 'lr': lr * 0.1} # 通常给更小的学习率
], lr=lr)

for epoch in range(num_epochs):
    for images, labels in dataloader: # labels是类别的索引
        images, labels = images.to(device), labels.to(device)
        
        # 前向
        logits = model(images) # 内部已处理prompts生成和特征对比
        loss = F.cross_entropy(logits, labels)
        
        # 反向
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

5.2 关键超参数与调优经验

  1. 上下文长度(n_ctx):这是最重要的超参数之一。它决定了你有几个可学习的“提示词”。原论文发现,对于大多数任务,4或8个token就足够了。太短可能表达能力不够,太长则容易过拟合,并且会增加不必要的计算量。我的经验是,从4开始尝试,如果性能饱和再考虑增加到8或16。

  2. 提示初始化ctx 的初始化很重要。原论文使用从高斯分布中采样的小随机值(std=0.02)。你也可以尝试用一些有意义的单词的嵌入向量来初始化(例如,对于鸟类分类,用“这是一只”的嵌入来初始化),但这不一定总能带来提升,有时随机初始化反而更好,因为它给了模型更大的探索空间。

  3. 类名位置与提示模板:CoOp原论文探索了两种主要设置:

    • 结尾(End):提示放在类名之前,即 [SOS][ctx][class][EOS]。这是最常用且通常效果最好的设置。
    • 中间(Middle):提示放在类名中间,如 [SOS][ctx_front][class][ctx_back][EOS]。这提供了更大的灵活性,但参数翻倍,更容易过拟合。 对于新手,我强烈建议从“结尾”位置开始。
  4. 学习率与优化器:由于大部分参数是冻结的,只有少量参数需要更新,学习率不宜过大。通常设置在 1e-35e-3 之间。对于 positional_embedding 这类从预训练模型继承的参数,如果选择微调,应该给予更小的学习率(例如主学习率的十分之一)。优化器选择AdamW并搭配适度的权重衰减(如0.02)是个不错的起点。

  5. 数据增强与正则化:虽然CoOp参数很少,但在小数据集上仍然可能过拟合。使用标准的图像增强(随机裁剪、水平翻转、颜色抖动等)至关重要。此外,可以尝试:

    • 提示Dropout:随机将 ctx 向量中的部分token置零,是一种有效的正则化。
    • 标签平滑:在交叉熵损失中使用标签平滑,可以防止模型对训练数据过于自信。

5.3 我踩过的坑与避坑指南

  1. 维度对齐错误:这是最常见的bug。当你拼接 [SOS]ctx类名嵌入[EOS] 时,必须确保它们在序列长度维度(第二个维度)的总和等于文本编码器预期的长度(CLIP通常是77)。你需要精确计算类名被分词后的token数量,并确保 n_ctx 设置正确,使得总长度不超过77,不足部分用填充。

  2. 忘记归一化:在计算余弦相似度之前,必须image_featurestext_features 进行L2归一化。忘记这一步会导致相似度计算错误,模型无法收敛。

  3. 错误地微调了太多参数:CoOp的魅力在于轻量微调。如果你不小心将视觉编码器或文本Transformer也设置为可训练,不仅会大幅增加训练时间和显存消耗,还很可能因为数据量小而导致灾难性遗忘,效果反而比原始CLIP还差。务必仔细检查模型的 requires_grad 属性。

  4. 评估模式切换:在训练和验证/测试时,要确保模型处于正确的模式(model.train()model.eval())。特别是如果使用了Dropout等技巧,在评估时一定要切换。

  5. logit_scale的处理:CLIP的 logit_scale 是一个可学习的温度参数,用于缩放相似度。在CoOp中,通常选择冻结它,使用CLIP预训练好的值。如果放开训练,在小数据集上它可能变得不稳定。

6. 超越基础:CoOp的进阶变体与应用思考

掌握了基础的CoOp之后,我们来看看这个思路还能玩出什么花样,以及在实际项目中如何选择。

6.1 CoOp的家族成员

CoOp的成功催生了一系列改进工作,它们主要围绕“如何设计更好的可学习提示”展开:

  • CoCoOp:作者团队后续提出的工作,它认为不同图片应该有不同的提示。CoCoOp引入了一个轻量的网络(Meta-Net),根据输入图片动态生成提示向量,而不是所有图片共享同一套静态提示。这在处理类内差异大、分布复杂的任务时表现更好。
  • ProGrad:为了避免提示学习过程中遗忘CLIP原有的通用知识,ProGrad提出了一种梯度约束方法。在更新提示时,检查梯度方向是否与CLIP原始任务的梯度方向一致,如果冲突则进行缓和,从而实现了更好的泛化性。
  • Tip-Adapter:这是一种几乎不需要训练的方法。它利用训练集的图像特征和标签,构建一个缓存库(Cache)。在推理时,通过查询缓存库来增强文本特征,从而提升性能。这种方法训练极快,适合快速原型验证。

6.2 何时该用CLIP,何时该选CoOp?

经过这么多实践,我的选择策略是这样的:

  • 使用原始CLIP进行零样本推理

    • 场景:任务非常开放,类别无法预先穷举(例如,从任意描述中检索图片);或者你只是想快速验证一个想法,没有标注数据。
    • 优势:开箱即用,无需训练,最具通用性。
    • 缺点:在专业、细粒度任务上精度可能不足;提示模板需要人工设计,且对结果影响大。
  • 使用CoOp进行少样本微调

    • 场景:你有明确、固定的类别集合(比如自家的商品分类、特定的缺陷类型),并且每个类别有一些标注数据(哪怕每类只有几个样本)。
    • 优势:参数效率极高,训练快,通常在少样本设置下能大幅超越零样本CLIP;避免了人工设计提示词的麻烦。
    • 缺点:需要少量标注数据;学到的提示可能过拟合到训练数据,在分布外数据上泛化能力可能下降。
  • 尝试更高级的变体(如CoCoOp)

    • 场景:你的数据集中,同一类别的图片视觉内容差异很大(例如,“会议”这个类别可能包含会议室、线上会议截图、一群人围坐讨论等截然不同的画面)。
    • 优势:动态提示能更好地适应输入图片的多样性。
    • 缺点:引入了额外的参数和计算量。

6.3 将思路迁移到其他模态与任务

CoOp的思想并不局限于视觉-语言模型。这种“冻结主干,优化输入提示”的范式,具有很强的启发性:

  • 音频分类:对于像AudioCLIP这样的模型,是否可以通过学习一段可听的“音频提示”来微调模型,以适应新的声音类别?
  • 视频理解:在视频-语言模型中,是否可以学习一组代表动作、场景的时序提示向量?
  • 其他下游任务:除了分类,CoOp的思路也可以用于检索、检测、分割等任务。核心思想都是:用轻量的可学习参数,将预训练大模型的能力引导到你的特定任务上来

在我最近的一个工业质检项目中,我们就有类似的应用。我们有一个预训练好的视觉异常检测模型,它对通用缺陷敏感,但对某种特定材质的细微划痕不敏感。我们没有重新训练整个模型,而是借鉴CoOp的思想,在模型的特征空间里添加了几个可学习的“查询向量”。这些向量通过少量正常和异常样本进行学习,最终学会了聚焦于那种特定划痕的模式,以极小的代价显著提升了在该场景下的检出率。这让我深刻体会到,在当今大模型时代,学会如何高效地“引导”和“激发”现有模型的能力,往往比从头训练一个新模型更加重要和实用。

摘 要 随着互联网影视产业的迅速发展,电影资源呈指数级增长,类型也愈加多样化,但是用户面对如此庞大的影片库时,会遇到信息过载、筛选效率低下、观影决策成本高这些难题,而传统的电影平台大多只是对基本的分类和热度进行展示,并没有提供个性化的推荐服务,社交互动以及一体化管理的能力也比较薄弱。 该系统使用的是Spring Boot+Vue前后端分离的方式进行开发,设计并开发了一个电影推荐和评论系统。开发系统中存在三个问题,即无法准确地判断出用户的喜好、电影推荐功能上线之初没有用户数据,因此推荐效果不佳、大量用户同时评论打分时系统容易出现不稳定的情况。为了克服上述问题,本文查阅相关资料,不断迭代开发原型,并进行实际测试,完成整个系统的全部过程,即系统需求分析、架构设计、功能开发、系统测试。系统分为普通用户和管理员两种身份,具有注册登录、电影浏览、电影推荐、评分评论、影片收藏、个人中心、后台管理等功能,采用结合用户喜好和电影热度的简单推荐方式,提供热门电影推荐和个性化推荐,很好地解决了推荐功能初始没有数据、数据较少的问题。项目用接口来完成前后端的数据交互,使用MySQL数据库存储用户、电影、评论、收藏等各种数据,并且加入权限验证、密码加密等安全措施,保证系统安全稳定并且便于后期扩展。除此之外,系统还增加了电影资讯查看、首页轮播图设置、编辑资讯等功能,使整个电影平台的服务更加全面,使用更加高效。经过全面的功能测试、接口测试和性能测试,系统各个模块运行稳定,推荐接口响应时间小于300ms,主要的互动操作成功率大于98%,可以大大降低用户的选片成本,提高观影决策的速度和社区互动的效果。 本文给出一套轻量级、易部署、可复用的电影推荐类Web应用工程实现方案,相比于传统的单一列表展示型平台,个性化服务、社交互动体验和后台管理效率都有明显的提高,可以给影视文化数字化传播、推荐算法轻量化工程实践
源码直接下载地址: https://pan.quark.cn/s/a4b39357ea24 在 Excel 中进行阳历阴历的相互转换,对于处理集体信息(例如通讯录)统计工作具有显著的实用性。本文将具体阐述如何借助 VBA 编辑器在 Excel 环境下完成阳历阴历的互换转换。首先,需要打开相应的 Excel 文件,通过按 Alt+F11 激活 VBA 编辑器,随后选择插入菜单下的模块选项,将提供的代码片段复制到新模块中,并保存更改后关闭编辑器。完成上述步骤后,即可在指定单元格中调用以下四个函数以达成转换目标。 1. 阴阳历转换功能: 函数 `Lunar(SolarDate[, Part = 0 | 1 | 2 | 3])` 负责将阳历日期转换为对应的阴历日期。参数 `Part` 决定转换内容的详略程度,其值可为 0、1、2 或 3,分别对应完整日期、阴历年、阴历月及阴历日的转换。 函数 `Solar(LunarDate[, LunarMonth = 0 | 1])` 适用于将阴历日期转换为阳历日期。参数 `LunarMonth` 用于指定月份的归属,可为 0 或 1,分别代表转换至阳历月份或保留阴历月份。 2. 生日日期转换功能: 函数 `lunarbirth("1975-5-6")` 能够计算出阴历生日所对应的阳历日期。相对地,函数 `solarbirth("1975-5-6")` 用于推算阳历生日对应的阴历日期。 3. 日期信息计算功能: 函数 `LunarData(q_year)` 提供阴历日期的详细数据,涵盖阴历年、阴历月、阴历日等信息。函数 `ConvDataA` 存储了阴历阳历的日期数据,包括阴历年、阴历月、阴历日、阳历月、阳历日等字段。 4. 应用...
下载代码方式:https://pan.quark.cn/s/a4b39357ea24 在信息技术行业中,特别是在人工智能(AI)的子领域——计算机视觉方面,动作分析是一个至关重要的组成部分。这一议题“Python-PyTorch动作分析模型库”有着紧密的联系,它涵盖了运用Python编程语言和PyTorch框架来构建和应用深度学习模型,旨在解析和判定视频中的行为。接下来,我们将详细研究这一领域的重要概念。 **PyTorch** 是由Facebook开发的一个开源且功能强大的深度学习平台,它具备动态计算图特性,从而让模型构建和调试过程更加便捷。PyTorch的关键在于Tensor类,该类是数值运算的基础,同时支持自动计算梯度,为神经网络的训练提供了便利。 **动作分析** 是计算机视觉中的一个核心任务,其目的是识别视频中的特定行为,例如奔跑、跳跃、招手等。这项任务通常包括从视频材料中提取图像帧,然后对单个帧或帧序列进行特征提取,最终借助已训练的模型进行分类。 在描述中提及的“流行动作分析模型”,或许涵盖了当前研究领域的主流模型,例如**双流卷积网络**,这种模型融合了空间和时间信息,通过分别处理RGB图像和光流图像来提高行为分析的精确度。还可能包括**时间分割网络(TSN)**,它通过跨长时间范围的样本选择来把握行为的整体特征。另外,更前沿的模型如**时间迁移模块(TSM)** 和 **非局部神经网络** 也可能被纳入其中,这些模型通过创新的网络构造来更有效地捕捉时间序列中的动态变化。 **某类数据集** 可能是指像UCF-101或Kinetics这样的标准动作分析数据集,它们包含了大量标注好的视频片段,用于模型的训练和性能评估。这些数据集在动作分析研究中被...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值