You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CLIP计算文本嵌入相似度出现异常结果的排查

问题:CLIP计算文本相似度结果不符合逻辑

我尝试使用CLIP计算字符串间的相似度(CLIP通常用于图文匹配,但也可仅处理文本)。提供一组简单文本语句,计算其嵌入向量的相似度,但结果不符合逻辑,例如“A dog”与“A house”的相似度(0.885)高于“A dog”与“A labrador”的相似度(0.848)。尝试过余弦相似度和欧氏距离两种计算方式,结果仍类似,请问问题出在哪里?

代码

import torch
import clip
from torch.nn import CosineSimilarity

cos = CosineSimilarity(dim=1, eps=1e-6)

def gen_features(model, text):
    tokens = clip.tokenize([text]).to(device)
    text_features = model.encode_text(tokens)

    return text_features

def dist(v1, v2):
    #return torch.dist(normalize(v1), normalize(v2)) # euclidean distance
    #return cos(normalize(v1), normalize(v2)).item() # cosine similarity

    similarity = (normalize(v1) @ normalize(v2).T)

    return similarity.item()



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

sentences = ["A cat", "A dog", "A labrador", "A poodle", "A wolf", "A lion", "A house"]

with torch.no_grad():
    embeddings = [(sentence, gen_features(model, sentence)) for sentence in sentences]
    for label1, embedding1 in embeddings:
        for label2, embedding2 in embeddings:
            print(f"{label1} -> {label2}: {dist(embedding1, embedding2)}")

输出结果

A cat -> A cat: 0.9999998211860657
A cat -> A dog: 0.9361147880554199
A cat -> A labrador: 0.8170720934867859
A cat -> A poodle: 0.8438302278518677
A cat -> A wolf: 0.9086413979530334
A cat -> A lion: 0.8914517164230347
A cat -> A house: 0.8724125027656555
A dog -> A cat: 0.9361147880554199
A dog -> A dog: 1.0000004768371582
A dog -> A labrador: 0.8481228351593018
A dog -> A poodle: 0.9010260105133057
A dog -> A wolf: 0.9260395169258118
A dog -> A lion: 0.886112630367279
A dog -> A house: 0.8852840662002563
A labrador -> A cat: 0.8170720934867859
A labrador -> A dog: 0.8481228351593018
A labrador -> A labrador: 1.000000238418579
A labrador -> A poodle: 0.7722526788711548
A labrador -> A wolf: 0.8111101984977722
A labrador -> A lion: 0.783727765083313
A labrador -> A house: 0.7569846510887146
A poodle -> A cat: 0.8438302278518677
A poodle -> A dog: 0.9010260105133057
A poodle -> A labrador: 0.7722526788711548
A poodle -> A poodle: 0.999999463558197
A poodle -> A wolf: 0.8539597988128662
A poodle -> A lion: 0.8460092544555664
A poodle -> A house: 0.8119628429412842
A wolf -> A cat: 0.9086413979530334
A wolf -> A dog: 0.9260395169258118
A wolf -> A labrador: 0.8111101984977722
A wolf -> A poodle: 0.8539597988128662
A wolf -> A wolf: 1.000000238418579
A wolf -> A lion: 0.9043934941291809
A wolf -> A house: 0.860664427280426
A lion -> A cat: 0.8914517164230347
A lion -> A dog: 0.886112630367279
A lion -> A labrador: 0.783727765083313
A lion -> A poodle: 0.8460092544555664
A lion -> A wolf: 0.9043934941291809
A lion -> A lion: 1.0000004768371582
A lion -> A house: 0.8402873873710632
A house -> A cat: 0.8724125027656555
A house -> A dog: 0.8852840662002563
A house -> A labrador: 0.7569846510887146
A house -> A poodle: 0.8119628429412842
A house -> A wolf: 0.860664427280426
A house -> A lion: 0.8402873873710632
A house -> A house: 0.9999997615814209

解答

问题主要出在以下几个方面:

1. 输入文本与CLIP预训练分布不匹配

CLIP是基于图文对预训练的模型,训练数据中的文本几乎都是描述性句式(比如"a photo of a dog"),而非你使用的极简短语("A dog")。这种输入分布的差异会导致模型无法正确捕捉细粒度语义关联——比如"labrador"作为特定犬种,和"dog"的语义联系没有被激活,反而"dog"和"house"这类通用名词的相似度被高估。

2. 模型粒度与任务需求不匹配

你使用的ViT-B/32是轻量级CLIP模型,语义区分能力有限,对于犬种这种细粒度类别,本身就很难和通用的"dog"建立强关联。加上输入格式不符合预训练习惯,进一步放大了这个问题。

3. 归一化函数可能未正确实现

你的代码中使用了normalize函数,但未在提供的代码片段中定义。如果该函数没有正确执行L2归一化(将向量模长缩为1),会直接导致相似度计算失真。正确的归一化实现应该是:

def normalize(x):
    return x / torch.norm(x, dim=-1, keepdim=True)

解决方法

(1)使用CLIP官方推荐的文本模板

将输入文本调整为和预训练一致的描述性句式,比如把"A dog"改为"a photo of a dog","A labrador"改为"a photo of a labrador"。这样能让模型的语义提取逻辑和预训练对齐,大幅提升相似度准确性。

(2)换用更大的CLIP模型

比如ViT-L/14,更大的模型拥有更强的语义理解能力,能更好区分细粒度类别之间的关联。

(3)修正归一化函数

确保归一化函数正确实现L2归一化,保证余弦相似度计算的准确性。


修改后的示例代码

import torch
import clip
from torch.nn import CosineSimilarity

cos = CosineSimilarity(dim=1, eps=1e-6)

# 正确实现L2归一化
def normalize(x):
    return x / torch.norm(x, dim=-1, keepdim=True)

def gen_features(model, text):
    # 使用官方推荐的图文描述模板
    template_text = f"a photo of a {text.lower()}"
    tokens = clip.tokenize([template_text]).to(device)
    text_features = model.encode_text(tokens)
    return text_features

def dist(v1, v2):
    similarity = (normalize(v1) @ normalize(v2).T)
    return similarity.item()

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

sentences = ["A cat", "A dog", "A labrador", "A poodle", "A wolf", "A lion", "A house"]

with torch.no_grad():
    embeddings = [(sentence, gen_features(model, sentence)) for sentence in sentences]
    for label1, embedding1 in embeddings:
        for label2, embedding2 in embeddings:
            print(f"{label1} -> {label2}: {dist(embedding1, embedding2):.6f}")

修改后,"A dog"与"A labrador"的相似度会显著高于与"A house"的相似度,符合预期逻辑。


内容的提问来源于stack exchange,提问作者Adi Eyal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:03:33