- SignalDesk2 hr ago
学习目标 :掌握 ViT 的完整流程与维度变化 → 掌握 Patch Embedding、CLS Token、Position Embedding → 掌握 CLIP 双塔架构、对比学习、InfoNCE Loss、Temperature 难度 : 重要性 : 世间万象,本不属于文字的领域。然而,当图像化作 Token,当视觉与语言在同一片识海中产生共鸣……那道横亘于两个世界之间的壁垒,终于出现了裂痕。 ViT ViT 的思想十分简单:既然 Transformer 可以处理文本 Token 序列,那么我们能不能把一张图片也转换成 Token 序列,再直接交给 Transformer Encoder? 整体流程 输入是一系列图片,每张图片的维度可以理解为 [C, H, W] , C 是通道数,一般 RGB 三通道或者单通道(黑白图)。然后一共 B 张图片,一般认为输入维度是 [B, 3, 224, 224] 。 接着对图片进行分块,切成 14 14 = 196 块,每一块被称为一个 patch,一个 patch 的大小是 [3, 16, 16] , 现在的维度就是 [B, 196, 3, 16, 16] 经过 Flatten and Linear 投影 Flatten 是将每一个 patch 转化为一条列向量,维度就是 31616=768 ,所以最后的维度是 [B, 196, 768] 。而 Linear 则是进行线性投影,将 768 维度的信息转化为隐藏维度 D 。不过一般来说 D=768 , 所以后面都直接用 768 表示了。 这里有一个面试的细节,在手撕的代码的时候这里是直接用 Conv2d ,为什么可以这样? self.patch_embed = nn.Conv2d( in_channels=3, out_channels=768, kernel_size=16, stride=16 ) 其实是因为,他就是一次移动16格,并且卷积核的大小为1616,刚好每次移动到下一个patch上,然后把3维转化为768维 另外这里的 Patch Size 也是一个重点。如果 Patch Size 减半获得更细节的处理,意味着 Patch 数量会翻四倍,导致后面的 Attention Score 的元素数量增加 16 倍。(大概就是计算量翻16倍了)。因此这是 视觉细节保留与 Token 数量之间的权衡 ,是 ViT、现代 VLM 高分辨率处理以及视觉 Token 压缩中非常重要的问题。 添加 CLS Token 和 Position Embedding 前面都一切正常,但是这里为什么要添加 CLS Token,这是什么意思?在后面会详细说明,CLS Token 在很多任务中有极其关键的作用。 现在就理解为多加一个 Patch 的信息(图片里面一般一个 Patch = 一个 Token),现在维度是 [B, 197, 768] 。而 Position Embedding 就和 Transformer 中的作用一致,但是并不是原始 Transformer 用的正弦位置编码。而是用的可学习的一维绝对位置编码,即一个位置编码矩阵: E_{\mathrm{pos}}\in\mathbb{R}^{197\times768} 直接 Add 到上述加了 CLS Token 后的向量上。 送入到 Transformer Encoder, 对于 ViT-B/16 的经典配置中,有 12 层 Encoder ,隐藏维度 D = 768 ,中间 MLP 层的维度为 3072。其中有两处和原始 Transformer 不同 ViT 采用 Pre-LN 结构,这在 [Transformer] 篇里面有提到。 MLP 使用 GELU 而不是 ReLU CLS Token 一个平平无奇的 D 维向量…吗? 为什么需要CLS Token? ViT 经过一系列操作之后,最后的输出就是 X_{\mathrm{out}} \in\mathbb{R}^{B\times197\times768} 但是 ViT 原本想要完成的任务是图像分类,它实际上只需要最后这个图片属于什么类别,即每一个图片输出一个结果即可。有一个简单的方法,就是直接对后面 patch 取平均,这样可以得到一张图片的信息。 但是 ViT 选择额外 引入一个专门用于学习图像级表示的 Token(借鉴的 BERT),这个Token被称为 CLS Token(Class Token) 。这个 Token 的作用就是在训练的过程中学习到整个图像的分类信息,初始的时候他就是随机化的一个向量。 最后 CLS Token 会被送入到线性层,然后 Softmax 后得到类别的概率 z \in \mathbb{R}^{1000} ,训练时通常直接将 Logits 送入 CrossEntropyLoss。 \mathcal L_{\mathrm{cls}} =-\log P(y_{\mathrm{true}}\mid I) CLS Token 的额外扩展 DeiT —— 添加第二个特殊 Token 核心思想:如果一个 CLS Token 可以负责学习真实类别,那么我们能不能再增加一个特殊 Token,让它负责学习教师模型提供的知识?也就是输入为: [\mathrm{CLS},\mathrm{DIST},P_1,\ldots,P_N] 。他的价值在于,可以设计多个特殊 Token 表示各种不同的信息,有各种不同的学习目标。 DINO 系列 它通过 Teacher–Student 的自蒸馏机制,让不同视角下的图像表示具有一致性,并观察到了很有价值的 ViT 特征行为。例如,一张图片经过不同裁剪与数据增强后,Teacher 和 Student 需要学习保持相关的图像级表示。 在这种结构中,CLS Token 不再仅仅用于输出 ImageNet 的固定类别,而可以成为一种可迁移的全局视觉特征。训练后的 CLS 表示可以用于图像检索、分类、相似度计算等任务。 Register Token —— 为什么有些 ViT 还要引入额外 Token? 这篇论文观察到,某些训练好的 ViT 会在图像中信息量很低的背景区域产生异常大的 Patch 特征范数。作者认为,模型有时会将这些背景 Patch 重新利用为内部计算的信息存储位置,从而影响空间特征图的质量。 为了解决这个问题,论文引入了额外的可学习 Register Tokens。这些 Tokens 的作用大概是用于吸收一些杂质信息,作为一种"寄存器",让模型更关注于该关注的东西。 CLIP —— CLS 不一定要学习固定分类标签 这里就留到下文再详细说明了 CLIP 原始 ViT 主要学习如何通过图像监督提取视觉特征。那么,如果我们希望模型不仅知道图片属于哪个类别,还能理解“猫”“狗”“红色汽车”这样的自然语言描述,并把图片与文字建立联系,应该怎么办? 如果说 ViT 解决的是“如何把图片编码成特征”,那么 CLIP 解决的就是“如何让图片和文字的特征能够相互匹配”。总的来说,他通过 Image Encoder 和 Text Encoder 分别提取特征,再使用对比学习,让匹配的图文特征更加接近,不匹配的图文特征相互远离。 为什么要用 CLIP,直接用 ViT 进行分类匹配不可以吗? 在 ViT 的最后,我们可以知道 CLS Token 的维度是 [B, 768] 的,然后经过分类头(Linear)映射为 1000 个类别的 Logits,这样当然可以实现图像分类,ViT 就是这么做的。但是现在的分类模型经过训练,他只会认识这 1000 个类别,对于分布外的类别他完全不熟悉。 那么有没有一种办法,让模型不再局限于训练阶段指定的固定类别,而是能够直接利用自然语言描述来识别图像?这也就是 CLIP 的研究思路,将 固定的图像分类 转化为 图像和文本的匹配问题 流程 有一系列图片和一系列文字,假设图片的维度为 [B, 3, 224, 224] ,文字的维度为 [B, ?] ,这里 ? 指的是文字还不清楚分别是多长,长度不统一。分别输入到 Image Encoder 和 Text Encoder 之中。这里的 Image Encoder 可以直接采用 ViT ,或者 ResNet 也可以。 而 Text Encoder 则是 Transformer。 图片经过 ViT 后,经过 Patch Embedding, Add CLS Token and Position Embedding,ViT Encoder 后,取得最后的 CLS 表示为 [B, 768] ,将 CLS 映射到 CLIP 维度的空间(取 512),即经过一个 W \in \mathbb{R}^{768 \times 512} 矩阵,得到向量 I \in \mathbb{R}^{B \times512} 。 文字先需要经过 Tokenizer,转换成 Token IDs。原始 OpenAI CLIP 的默认文本 Context Length 为 77,因此经过特殊 Token、Padding 等处理后,可以得到 T_{\mathrm{ids}}\in\mathbb{R}^{B\times77} 。接下来同样经过 Token Embedding 和 Position Embedding,假设隐藏维度是 512,则现在的维度就是 [B, N, D] = [B, 77, 512] ,然后经过采用 Causal Self-Attention 的 Transformer Block, 维度不变。这里有一个很大的问题,就是 ViT 取了 CLS Token,那么文字需要取哪一个 Token 来作为匹配的呢? CLIP 选择了末尾的 EOT ,原因是 MHA 后,EOT 作为文本的末尾可以访问到前面的所有状态(其他 Token 都不行,因为 Decoder 中 MHA 是 Csusal Mask 的),再经过 LayerNorm 和 Linear ,最终得到向量: T\in\mathbb{R}^{B\times512} 经过各自的 Encoder 后,二者维度相同,接下来就是要在这个 512 维的空间中,将文字和图片进行对齐。训练过程可以查看上图的相似度矩阵,实际上就是对角线是正样本(正确的匹配),其他是负样本。所以希望训练后这个矩阵满足: S_{ii}>S_{ij},\qquad j\ne i \qquad and \qquad S_{ii}>S_{ji},\qquad j\ne i 关于相似度矩阵及损失函数细节的详细知识在下文进行说明。 最后训练完毕,推理的时候只需要给定一段图片和文字,就可以得到他们的相似度了。 相似度矩阵及损失函数细节 这一块是 CLIP 的核心… 相似度矩阵如何计算? 假设 Image Encoder 和 Text Encoder 都已经完成特征提取与线性投影。 I,T\in\mathbb R^{B\times D} 其中 B 是 Batch Size, D=512 是图文共同的特征维度。 先进行归一化,再进行点积匹配。归一化的原因是防止长度影响,我们只想看向量的方向,方向一致就更接近(其实就是利用余弦相似度的性质)。 S=\hat I\hat T^\top ,其中 \hat{} 表示归一化。维度就变化为 [B, B] ,其中 S_{i,j} 表示第 i 张图片和第 j 段文本的匹配分数。 为什么不仅要让 S_{i,i} 分数高,还要让 S_{i,j}(i \not = j) 分数低 如果只让 S_{i,i} 分数高作为训练目标的话,模型会将整个 S 矩阵学习为全 1 矩阵,这样是最简单的。这样模型根本没有区分度,所以还要让 S_{i,j}(i \not = j) 分数低,目的是“要让正确配对的相似度相对于错误配对更高”。所以实际上就是对于一张图片,要从一个 Batch 里面的一系列文本选择最正确的一条 (有点像分类问题了) 但是相似度还只是分数,可能有正有负,我们如果要利用交叉熵损失进行训练,就先需要转化为真正的概率,用 Softmax 进行转化。然后利用 CELoss: \mathcal L=-\sum_j y_j\log p_j 对于一个 batch 内的全部图片,可以把每张图片对应的 Loss 求平均: \mathcal L_{\mathrm{I2T}} = -\frac1B\sum_{i=1}^{B} \log \frac{e^{S_{ii}}} {\sum_{j=1}^{B}e^{S_{ij}}} 但是实际上这个 Softmax 还是存在一个问题的,假设相似度分别为 [0.8, 0.2, 0.1] ,按道理来说 0.8 对应的位置的概率应该转化后很高,但是实际上经过 softmax 后,大概只有 49%。原因是,经过 L2 Normalization 后,余弦相似度被限制在 [-1,1] ,这些数值直接进入 Softmax,得到的分布可能比较平缓。 这时候就引入了 Temperature,先把这些相似度放大,再进行 Softmax。例如对上面的结果同时 * 5,得到 [4, 1, 0.5] ,这时候效果就会很好。通常写为 Z=\frac{S}{\tau} ,这里的 \tau 就是温度系数。 \tau 越小,分数的差异越容易被放大,Softmax 通常越尖锐; \tau 越大,Softmax 通常越平缓。 完整损失函数 上面提到的只有一个过程,即 I2T,但是还可以反过来,对于一个文本,找到最对应他的图片。即 T2I 损失函数: \mathcal L_{\mathrm{T2I}} = -\frac1B\sum_{i=1}^{B} \log \frac{\exp(Z_{ii})} {\sum_j\exp(Z_{ji})} 最后损失函数就是这两个的平均。 CLIP进阶 Zero-shot Classification 要理解这一点,先要理解,CLIP 明明训练的是图文匹配,为什么可以用来作为图像分类? 因为实际上,我们可以提供一组 Text,比如 [cat, dog, ...] ,然后送入
- 情报分类:工作与职业机会
- 分类依据:内容涉及招聘、求职或职业发展
- 信息来源:服务器 / LINUX DO - 最新话题
- 发布时间:2026/10/14 21:07:29
- No replies yet