CLIP 文本编码器 15 行代码讲透:一句描述如何给陌生图片分类

📅 发布时间:2026/9/2 10:01:15
CLIP 文本编码器 15 行代码讲透:一句描述如何给陌生图片分类 CLIP 文本编码器 15 行代码讲透一句描述如何给陌生图片分类【免费下载链接】CLIPCLIP (Contrastive Language-Image Pretraining), Predict the most relevant text snippet given an image项目地址: https://gitcode.com/GitHub_Trending/cl/CLIP给 CLIP 一张它从没见过的照片和三句英文描述它能直接告诉你哪句最贴切——不用先拿橙子训练它。这就是零样本分类zero-shot classification而 CLIPContrastive Language-Image Pretraining是在海量图片描述配对上训练出来的网络靠向量相似度把文本和图片匹配起来。本文拆开它的文本编码器也就是把你那句话变成数字的那一半。黑盒总览CLIP 如何把图片和一句话变成一个匹配分先看官方架构图两个塔各出一路向量谁也别管谁内部怎么算输入一张图加一段文本——文本先被切成固定 77 位的整数序列token 序列输出512 维文本向量 一个图像向量两者做余弦相似度cosine similarity就是匹配分边界文本最多 77 个 token超了默认直接报错模型在英文数据上训练建议只喂英文。数据流是一条直线没有回路接下来全文只跟一条线a photo of a cat里的cat这个词按流经顺序一站一站走。取号窗口SimpleTokenizer 如何把词切成 BPE tokencat 先被小写化、清洗空白再按正则切成词单元逐个到词表里查编号——词表共 49408 个条目首尾各有一个特殊标记起始符 |startoftext|sot和结束符 |endoftext|eot。这像机场取号不管你说什么先领一个号。不同寻常的是号本身有讲究高频词是现成的短编号长词生词会被 BPEByte Pair Encoding字节对编码拆成几个常见组合再陌生的组合也能用字节级编号兜底词表因此永远不会不够用。clip/simple_tokenizer.py 负责切分clip/clip.py 的tokenize()负责装箱不足 77 位用 0 补齐得到形状[N, 77]的定长整数矩阵——句子再短也把整排座位坐满最后一位真实座位放 eot。泊位牌token_embedding 与 positional_embedding 如何把词和位置绑上编号本身没有意义cat 的号和 dog 的号长得一样。clip/model.py 的CLIP.__init__里有两张表self.token_embedding nn.Embedding(vocab_size, transformer_width)把每个编号查成 768 维向量相当于每辆车一个泊位货物才是含义self.positional_embedding一个可学习的 (77, 768) 参数每个位置一块泊位牌告诉你站在哪一格。encode_text开头把两者直接相加x self.token_embedding(text) self.positional_embedding。从此每个 token 既有身份也有位置。注意位置牌不是正弦公式那种固定写法而是随机初始化的可训练参数含义全靠数据喂出来。单行柜台Transformer 因果注意力如何只允许词看前面768 维向量进入Transformer12 层同构的ResidualAttentionBlock叠起来每层只做两件事——多头自注意力multi-head attention 4 倍宽的前馈网络各配层归一化和残差连接。关键在注意力掩码。build_attention_mask()把 77×77 矩阵的上三角填成 -inf只留下三角可用效果像单行柜台cat 计算注意力时只能看到它前面sot、a、photo、of和它自己看不到后面的词。这种因果causal结构沿用自自回归语言模型而 CLIP 只做编码不做生成好处是每句话的每个位置向量都天然攒下了从头到它为止的全部信息。盖章处text_projection 如何输出 512 维文本向量12 层过完77 个位置各有一个 768 维向量可最终只交一个向量。encode_text的倒数第二行完成盖章x self.ln_final(x) x x[torch.arange(x.shape[0]), text.argmax(dim-1)] self.text_projectiontext.argmax(dim-1)是个省事的技巧eot 的编号是词表最大值49407填充位是 0所以每行取最大值的位置恰好就是真正的末词位置不必单独找 eot。该位置 768 维向量乘以 768×512 的投影矩阵落进文本和图像共用的 512 维空间。之后调用model(image, text)时两侧向量先各自归一化再乘一个可学习的logit_scale温度参数分数越大代表越像。为什么这么设计CLIP 文本编码器的 3 个关键决策为什么用下三角掩码让每个词只看前面因为这套架构是从自回归语言模型里顺来的。双向注意力BERT 式做编码任务本可以但统一的因果结构让代码路径只有一套文本侧不用特判。代价是每个词只能单向积累上下文——好在只要最后取一个信息最全的位置当代表单向积累完全够用。为什么取 eot 位置的向量而不是对整句求平均eot 是因果序列里的最后一个它的向量听过了前面所有词是句级信息攒得最满的位置。若对全句向量求平均每个位置都含大量关于自身语境的冗余信息一平均反而稀释若取第一个词它只看见了 sot等于没读。取 eot 相当于盖章人最后签字——最后签字的人看过整份文件。为什么长度固定 77 tokens因为positional_embedding是一张写死的 (77, 768) 参数表批处理按定长张量计算变长会让填充和掩码管理复杂化。77 也足够装下 a photo of a xxx 这类零样本分类模板里的绝大多数描述。代价是上限肉眼可见超了默认就抛RuntimeError排查方法见后文。动手验证15 行代码跑通 CLIP 文本编码装好 PyTorch 并安装本仓库后下面 15 行就能跑完整个文本编码链路import torch, clip model, _ clip.load(ViT-B/32) tokens clip.tokenize([a photo of a cat, a photo of a dog]) with torch.no_grad(): feats model.encode_text(tokens) f feats / feats.norm(dim-1, keepdimTrue) sim f f.T print(feats.shape) # torch.Size([2, 512]) print(sim)输出是一个 2×2 相似度矩阵对角线为 1自己和自己非对角项是两句的语义相似度。cat 句和 dog 句通常能到 0.9 左右——模板相同、只差一个词这正说明向量记住的是语义结构而不只是字面。Input is too long for context length 77 报错怎么排查这是新手踩得最多的坑文本切分后超过 77 个 token而tokenize默认truncateFalse直接抛错而不是悄悄截断。两种处理# 方案 A自动截断到 77并保证末位仍是 eot tokens clip.tokenize(long_text, truncateTrue)方案 B 是手动精简文本。做零样本分类时通常用模板包裹类名a photo of a {class}长度天然可控如果你发现自己在喂长段落值得停下来想想——这句话真的需要那么多词吗CLIP 模型怎么选4 个档位参数对比各模型文本编码器规格不同见 clip/clip.py 的_MODELS表与官方论文模型Transformer 层数隐藏维度输出维度一句话建议RN50125121024卷积架构、推理快适合 CPU 和显存紧张的场景ViT-B/3212768512默认起手式普通 GPU 就能跑先用它验证任务ViT-B/1612768512图块更小、精度更高显存占用明显上升ViT-L/14241024768官方精度最强的主力档显存吃紧时再考虑经验值先用 ViT-B/32 试水精度不够再升 ViT-B/16 或 ViT-L/14机器老就降回 RN50输出维度只影响精度-成本比例不影响 API 用法。总结与延伸阅读一句话收束CLIP 文本编码器把句子排成 77 位的定长队列用因果 Transformer 让 eot 位置攒下全文信息再投影到 512 维共用空间与图像向量比相似度——所谓零样本分类本质就是一次余弦计算。延伸阅读均为仓库内本地文件README.md安装与 API 说明notebooks/Interacting_with_CLIP.ipynb官方图文交互示例clip/model.py文本编码器完整实现data/prompts.md零样本评测用的提示模板集【免费下载链接】CLIPCLIP (Contrastive Language-Image Pretraining), Predict the most relevant text snippet given an image项目地址: https://gitcode.com/GitHub_Trending/cl/CLIP创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考