跳到主要内容

4. 视觉编码器与对齐

1. 为什么 VLA 需要专门的视觉编码器?

1.1 VLA 中视觉编码器的角色

在 VLA(Vision-Language-Action)模型中,视觉编码器是第一个处理环节:

摄像头图像 → [视觉编码器] → 视觉特征 → [融合模块] → [LLM] → 动作输出

你现在学的部分

视觉编码器的作用是将原始像素转化为高层语义特征,让后续的语言模型能够"理解"图像内容。

1.2 为什么不直接用 ViT?

你可能已经学过 ViT(Vision Transformer),但 VLA 中通常不会从头训练一个 ViT,而是使用预训练好的视觉编码器

方式说明问题
从头训练 ViT随机初始化,在任务数据上训练机器人数据太少,学不到好的视觉特征
使用预训练编码器在大规模数据上预训练好的模型需要选择合适的预训练方式

目前 VLA 中最常用的三种视觉编码器:

编码器预训练方式代表模型VLA 中的典型用法
CLIP ViT对比学习(图文对)OpenAI CLIPRT-2, OpenVLA
SigLIPSigmoid 对比学习Google SigLIPPaLM-E, OpenVLA
DINOv2自监督学习Meta DINOv2Theia, 部分 VLA

2. CLIP(Contrastive Language-Image Pre-training)

CLIP 训练与迁移总览(CLIP, Fig.1)
CLIP 训练与迁移总览(CLIP, Fig.1)

2.1 CLIP 的核心思想

一句话概括:让图像和描述它的文本在同一个向量空间中"靠近"。

CLIP 由 OpenAI 于 2021 年发布,核心创新是用自然语言监督来学习视觉表征

训练数据:4 亿个 (图像, 文本描述) 对
来自互联网自然采集,无需人工标注

为什么这很重要?

  • 传统视觉模型需要人工标注的分类标签(如 ImageNet 的 1000 类)
  • CLIP 直接利用网络上现成的图文对,无需人工标注
  • 这使得 CLIP 能学到更广泛的视觉概念(不局限于预定义类别)
  • 学到的特征天然与自然语言对齐,方便 VLA 理解文本指令

2.2 CLIP 架构

┌──────────────┐
图像 ──────────→ │ Image │ ──→ 图像特征向量 f_img ∈ R^d
│ Encoder │
│ (ViT-L/14) │
└──────────────┘
↕ 对比学习:拉近配对,推远非配对
┌──────────────┐
文本 ──────────→ │ Text │ ──→ 文本特征向量 f_txt ∈ R^d
"a photo of │ Encoder │
a cat" │ (Transformer)│
└──────────────┘

关键点:

  • Image Encoder:可以是 ViT 或 ResNet,VLA 中通常用 ViT-L/14 或 ViT-H/14
  • Text Encoder:标准 Transformer(类似 GPT)
  • 共享的嵌入空间:图像和文本被映射到同一个 d 维空间

架构设计的精髓

  • 两个编码器完全独立,各自处理自己的模态
  • 最终通过投影层将两种特征映射到同一维度(如 512 或 768)
  • 在这个共享空间中,语义相近的图文对距离近,不相关的距离远
  • 这种设计使得图像和文本可以直接通过向量运算(如点积)比较相似度

2.3 对比学习损失(InfoNCE Loss)

2.3.1 核心直觉

给定一个 batch 的 N 个(图像, 文本)对:

  • 正样本:配对的图文是正样本(对角线)
  • 负样本:不配对的图文是负样本(非对角线)
Text_1 Text_2 Text_3 Text_4
Image_1 [ ✓ ✗ ✗ ✗ ]
Image_2 [ ✗ ✓ ✗ ✗ ]
Image_3 [ ✗ ✗ ✓ ✗ ]
Image_4 [ ✗ ✗ ✗ ✓ ]

✓ = 正样本(相似度应该高)
✗ = 负样本(相似度应该低)

直觉理解

  • 把这个 N×N 的相似度矩阵想象成一个"匹配表"
  • 理想情况下,对角线元素(正确配对)的值应该远大于其他位置
  • 对比学习的目标就是让模型学会"找到对角线"
  • Batch Size 很重要:N 越大,负样本越多,学习信号越强(CLIP 使用 32,768 的超大 batch)

直觉理解:为什么大 Batch Size 这么重要?

想象你在学习"猫"的概念:

  • 小 batch(N=4):每次只看 4 张图和 4 段文字。模型可能学到"棕色的东西"就是猫,因为样本太少,没有足够的反例。
  • 大 batch(N=32K):每次同时看 32K 张图和 32K 段文字。模型必须区分猫、狗、老虎、兔子……所有动物,才能把"猫的图像"和"猫的文字"正确配对。

大 batch 强制模型学到更本质、更鲁棒的特征,而不是依赖表面的颜色或纹理。

对比学习之所以能让特征空间具有语义结构,是因为它同时施加了"拉近"和"推远"两种力:语义相近的概念(如"猫"和"小猫")因为经常与相似的图像配对而被拉到邻近区域,而语义无关的概念则被大量负样本推开。经过大规模训练后,整个嵌入空间自然形成了按语义聚类的拓扑结构——相似概念聚集成簇,不同概念彼此远离。

2.3.2 数学形式

设 batch 中有 N 个图文对,图像特征 ,文本特征

相似度矩阵

公式解读

  • 是图像特征和文本特征的点积,衡量两者的相似程度
  • 分母 是 L2 范数的乘积,用于归一化(即计算余弦相似度)
  • 温度参数(temperature),控制分布的"锐度":
    • 越小,softmax 后的分布越尖锐(更确定)
    • 越大,分布越平滑(更均匀)
    • CLIP 中 是可学习的,初始值通常为

图像到文本方向(对每一行做 softmax):

公式解读

  • 对于每张图像 ,我们希望它与正确配对的文本 的相似度 最高
  • 分母 是图像 与所有文本的相似度之和
  • 这本质上是一个 N 分类的交叉熵损失:给定图像,预测哪个文本是正确配对

文本到图像方向(对每一列做 softmax):

公式解读

  • 对称地,对于每段文本 ,我们希望它与正确配对的图像 的相似度最高
  • 这是从文本找图像的方向,确保双向对齐

最终损失

为什么需要双向损失?

  • 单向损失可能导致"坍缩":所有图像映射到同一点也能最小化单向损失
  • 双向损失确保图像和文本都能区分彼此,形成结构化的嵌入空间

2.3.3 代码实现

import torch
import torch.nn as nn
import torch.nn.functional as F

class CLIPLoss(nn.Module):
"""CLIP 对比学习损失"""
def __init__(self):
super().__init__()
# 可学习的温度参数,初始化为 log(1/0.07)
self.logit_scale = nn.Parameter(torch.ones([]) * torch.log(torch.tensor(1/0.07)))

def forward(self, image_features, text_features):
"""
image_features: (N, D) 归一化后的图像特征
text_features: (N, D) 归一化后的文本特征
"""
# 对特征做 L2 归一化,使点积等价于余弦相似度
image_features = F.normalize(image_features, dim=-1) # (N, D) → 每行 L2 范数为 1
text_features = F.normalize(text_features, dim=-1) # (N, D) → 每行 L2 范数为 1

# 计算 N×N 相似度矩阵,乘以温度系数放大差异
logit_scale = self.logit_scale.exp()
logits_per_image = logit_scale * image_features @ text_features.t() # (N, D) @ (D, N) → (N, N)
logits_per_text = logits_per_image.t() # (N, N),转置即为文本→图像方向

# 对角线位置是正样本,对每行做 N 分类:label[i]=i 表示图像 i 对应文本 i
labels = torch.arange(len(image_features), device=image_features.device) # (N,)

# 双向交叉熵:图像→文本 + 文本→图像,确保双向对齐
loss_i2t = F.cross_entropy(logits_per_image, labels)
loss_t2i = F.cross_entropy(logits_per_text, labels)

return (loss_i2t + loss_t2i) / 2

2.4 CLIP Image Encoder 细节

VLA 中通常使用 CLIP 的 ViT 变体:

变体Patch Size隐藏维度层数头数图像分辨率输出维度
ViT-B/32327681212224512
ViT-B/16167681212224512
ViT-L/141410242416224768
ViT-H/1414128032162241024

VLA 中的常见选择:ViT-L/14(性能与效率的平衡)

import torch
from transformers import CLIPModel, CLIPProcessor

# 加载预训练 CLIP
model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")

# 提取图像特征
image = ... # PIL Image
inputs = processor(images=image, return_tensors="pt")

# 方式 1:获取 [CLS] token 的全局特征
with torch.no_grad():
outputs = model.vision_model(**inputs)
cls_feature = outputs.pooler_output # (1, 768) 经过投影的全局特征

# 方式 2:获取所有 patch token 的特征(VLA 中更常用!)
patch_features = outputs.last_hidden_state # (1, 257, 1024)
# 257 = 1 (CLS) + 256 (16x16 patches)
# 去掉 CLS token
patch_features = patch_features[:, 1:, :] # (1, 256, 1024)

重要:在 VLA 中,通常使用所有 patch token 的特征(而非仅 CLS token),因为机器人操作需要空间位置信息。

2.5 CLIP 用于 VLA 的优势与局限

维度优势局限
语义理解强大的语义表示,理解物体、场景空间细粒度不够(因为训练目标是图文匹配)
泛化性在大量互联网数据上训练,泛化好对机器人特定视角可能适配不足
语言对齐特征天然与语言对齐缺少精确的空间位置信息
使用便利HuggingFace 直接加载,生态完善分辨率固定(224/336),需要特殊处理高分辨率

2.6 CLIP 的 Zero-Shot 分类能力

CLIP 无需任何额外训练即可做图像分类,这体现了它强大的视觉-语言对齐能力:

# Zero-Shot 分类
texts = ["a photo of a cat", "a photo of a dog", "a photo of a robot arm"]
inputs = processor(text=texts, images=image, return_tensors="pt", padding=True)

with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits_per_image # (1, 3) 图像与每段文本的相似度
probs = logits.softmax(dim=-1) # 归一化为概率
print(probs) # 例如: tensor([[0.02, 0.01, 0.97]])

这个能力在 VLA 中很重要:模型能理解自然语言指令中描述的物体和场景。


3. SigLIP(Sigmoid Loss for Language-Image Pre-training)

3.1 SigLIP 的动机

CLIP 的 Softmax 对比损失有一个问题:需要全局的 batch 信息

CLIP 的问题:
┌──────────────────────────────────────────────┐
│ Softmax 需要对整个 batch 做归一化 │
│ │
│ logits = [s_11, s_12, ..., s_1N] │
│ prob_1 = exp(s_11) / Σ_j exp(s_1j) │
│ ↑ │
│ 需要所有 N 个样本的信息 │
│ │
│ 这限制了分布式训练的效率(需要跨 GPU 通信) │
└──────────────────────────────────────────────┘

3.2 SigLIP 的解决方案:Sigmoid Loss

核心改变:把 N 分类的 Softmax 问题,变成 N^2 个独立的二分类问题。

每个 (图像_i, 文本_j) 对独立判断"是否匹配":

其中:

  • sigmoid 函数,将任意实数映射到
  • 标签
  • 是图像 和文本 的相似度(带温度和偏置)

公式解读

  • 这是标准的二分类交叉熵损失(BCE),应用于 个图文对
  • 对于正样本(),损失为 ,希望 越大越好
  • 对于负样本(),损失为 ,希望 越小越好
  • 关键优势:每个图文对的损失计算是独立的,不需要全局归一化
class SigLIPLoss(nn.Module):
"""SigLIP Sigmoid 对比学习损失"""
def __init__(self):
super().__init__()
self.logit_scale = nn.Parameter(torch.ones([]) * torch.log(torch.tensor(10.0)))
self.logit_bias = nn.Parameter(torch.tensor(-10.0))

def forward(self, image_features, text_features):
"""
image_features: (N, D)
text_features: (N, D)
"""
image_features = F.normalize(image_features, dim=-1)
text_features = F.normalize(text_features, dim=-1)

logit_scale = self.logit_scale.exp()
logits = logit_scale * image_features @ text_features.t() + self.logit_bias # (N, N)

# 标签矩阵:对角线为 1,其余为 -1
N = logits.shape[0]
labels = 2 * torch.eye(N, device=logits.device) - 1 # 1 or -1

# Sigmoid 二分类损失
loss = -F.logsigmoid(labels * logits).mean()

return loss

3.3 SigLIP vs CLIP 对比

维度CLIP (Softmax)SigLIP (Sigmoid)
损失函数N 路 Softmax 交叉熵N^2 个二分类 BCE
归一化范围需要全局 batch每对独立计算
分布式训练需要跨 GPU 通信相似度矩阵每个 GPU 可独立计算
Batch Size越大越好,32K+对 batch size 更鲁棒
性能同等设置下略优
额外参数温度 τ温度 τ + 偏置 b

工程实践解读:CLIP 的 Softmax 需要在整个 batch 内归一化,这意味着训练时必须使用超大 batch(CLIP 原始实现使用了 32,768 的 batch size),在普通机器上复现代价极高。SigLIP 改用 Sigmoid 损失,每个图文对独立判断是否匹配,不依赖全局 batch 的负样本,小 batch 也能有效训练——这是学术界和资源有限的工程团队更倾向选择 SigLIP 的核心原因之一。

3.4 SigLIP 模型变体

模型图像分辨率参数量输出特征维度
SigLIP-B/1622486M (vision)768
SigLIP-L/16256303M (vision)1024
SigLIP-SO400M/14384400M (vision)1152

VLA 中的常见选择:SigLIP-SO400M/14-384(OpenVLA、PaLM-E 等使用)

from transformers import SiglipModel, SiglipProcessor

# 加载 SigLIP
model = SiglipModel.from_pretrained("google/siglip-so400m-patch14-384")
processor = SiglipProcessor.from_pretrained("google/siglip-so400m-patch14-384")

# 提取视觉特征
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
vision_outputs = model.vision_model(**inputs)

# 所有 patch token 特征 (VLA 中常用)
patch_features = vision_outputs.last_hidden_state # (1, 729, 1152)
# 729 = 27 x 27 patches (384/14 ≈ 27)

# 全局特征
pooled = vision_outputs.pooler_output # (1, 1152)

3.5 为什么 VLA 越来越倾向用 SigLIP?

  1. 性能更优:同等参数量下表现更好
  2. 训练更高效:sigmoid 损失在分布式训练中通信开销更小
  3. 更高分辨率:SigLIP-SO400M 支持 384 分辨率,提供更细粒度的空间信息
  4. Google 生态:PaLM-E、RT-2-X 等 Google 系 VLA 都使用 SigLIP

4. DINOv2(Self-Supervised Vision Transformer)

DINOv2 数据处理与训练流程(DINOv2, Fig.3)
DINOv2 数据处理与训练流程(DINOv2, Fig.3)

4.1 与 CLIP/SigLIP 的根本区别

CLIP / SigLIP: DINOv2:
有监督 (图文对) 自监督 (仅图像)
学习"这张图说的是什么" 学习"这张图的结构是什么"
全局语义 + 语言对齐 局部空间特征 + 几何信息

适合:理解指令中的物体和场景 适合:理解空间布局和几何关系

自监督特征与监督特征的本质区别

DINOv2 不需要任何标注,模型通过图像自身的结构一致性来学习特征。正因为没有语言标签的约束,模型不会把注意力全部集中在"物体是什么"上,而是同时学习纹理、边缘、几何形状等低层次信息。这些低层次的结构细节在监督训练中往往被忽略,却正是精细操控任务(如毫米级抓取、位姿估计)所需要的:机器人需要知道的不只是"这是一个杯子",还要知道"杯子的握持点在哪里、边缘轮廓怎么分布"。

4.2 DINOv2 训练原理

直觉理解:自蒸馏是什么?

CLIP 靠"文字告诉我这张图是什么"来学习特征;DINOv2 靠"图像自身的一致性"来学习特征。

类比:你不需要别人告诉你,也能认出同一只猫的不同角度照片——因为它们有相同的纹理、形状和结构。DINOv2 就是让模型学会这种"同一物体的不同视角应该有相似特征"的能力。

具体做法:把同一张图裁成两个不同的视图,让"学生网络"的输出去匹配"教师网络"的输出。教师网络是学生网络的滑动平均(EMA),不直接训练,只跟着学生慢慢更新。

DINOv2 使用自蒸馏(Self-Distillation) + 掩码图像建模(Masked Image Modeling)

4.2.1 自蒸馏框架

┌──────────────┐
全局视图 ────────→│ Teacher │──→ 教师输出 t
(大裁剪) │ (EMA 更新) │
└──────────────┘
↕ 蒸馏损失:让学生输出接近教师输出
┌──────────────┐
局部视图 ────────→│ Student │──→ 学生输出 s
(小裁剪/掩码) │ (梯度更新) │
└──────────────┘
  • Teacher:学生网络的指数移动平均(EMA),不直接参与梯度更新
  • Student:通过梯度下降更新
  • 数据增强:对同一张图像生成多个视图(全局/局部裁剪)

EMA 教师网络之所以能提供稳定的学习信号,是因为它的参数是学生网络历史参数的加权平均(,动量 通常为 0.996–0.999),这种平滑更新过滤掉了单步梯度的噪声波动,使教师的输出分布变化缓慢且一致。如果教师也用梯度直接更新,师生网络会互相追逐、快速震荡,最终坍缩到所有输入都映射为相同向量的退化解;而 EMA 的"惯性"打破了这种对称性,为学生提供了一个始终略微领先、缓慢演进的学习目标。

4.2.2 自蒸馏损失

def dino_loss(student_output, teacher_output, center, temp_s=0.1, temp_t=0.04):
"""
DINO 自蒸馏损失

student_output: 学生网络输出 (B, D)
teacher_output: 教师网络输出 (B, D) - 已 detach
center: 教师输出的移动平均中心
"""
# 教师用更低温度 → 更"尖锐"的分布
teacher_probs = F.softmax((teacher_output - center) / temp_t, dim=-1)

# 学生用更高温度 → 更"平滑"的分布
student_log_probs = F.log_softmax(student_output / temp_s, dim=-1)

# 交叉熵:让学生模仿教师
loss = -torch.sum(teacher_probs * student_log_probs, dim=-1).mean()
return loss

4.2.3 掩码图像建模(iBOT 组件)

原始图像 → Patch 序列: [P1, P2, P3, P4, P5, P6, ...]
↓ 随机掩码部分 patch
掩码后: [P1, [M], P3, [M], P5, P6, ...]
↓ 学生网络处理
学生输出: [z1, z2', z3, z4', z5, z6, ...]
↓ 教师网络处理原始图像
教师输出: [z1, z2, z3, z4, z5, z6, ...]

损失: 让 z2' 接近 z2,z4' 接近 z4(被掩码位置的 token 要还原)

这使得 DINOv2 学到了局部空间特征——知道每个 patch 位置"应该有什么"。

4.3 DINOv2 的独特能力

DINOv2 学到了惊人的空间理解能力:

DINOv2 特征可以直接用于:
├── 语义分割(不需要微调)
├── 深度估计(不需要微调)
├── 物体对应关系(同一物体不同视角的特征匹配)
└── 场景理解(空间布局)

4.3.1 DINOv2 特征的空间性质

from transformers import Dinov2Model, AutoImageProcessor

model = Dinov2Model.from_pretrained("facebook/dinov2-large")
processor = AutoImageProcessor.from_pretrained("facebook/dinov2-large")

inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)

# Patch-level 特征:每个 patch 都有独立的语义特征
patch_features = outputs.last_hidden_state[:, 1:, :] # (1, 256, 1024)
# 去掉 CLS token,剩余 256 个 patch 特征

# 可以 reshape 为 2D 特征图
H = W = int(patch_features.shape[1] ** 0.5) # 16
feature_map = patch_features.reshape(1, H, W, -1) # (1, 16, 16, 1024)
# 这个 feature_map 保留了空间结构!

4.4 DINOv2 模型变体

模型参数量隐藏维度层数Patch Size
DINOv2-S22M3841214
DINOv2-B86M7681214
DINOv2-L300M10242414
DINOv2-G1.1B15364014

4.5 DINOv2 用于 VLA 的场景

Prismatic 双视觉表征融合(Prismatic VLMs, Fig.7)
Prismatic 双视觉表征融合(Prismatic VLMs, Fig.7)

DINOv2 通常不单独用于 VLA,而是与 CLIP/SigLIP 互补使用

方案 1:仅用 CLIP/SigLIP(大多数 VLA 的选择)
图像 → CLIP ViT → 视觉 token → LLM
优点:语言对齐好,理解指令中的物体
缺点:空间信息可能不够精确

方案 2:仅用 DINOv2
图像 → DINOv2 → 视觉 token → LLM
优点:空间信息好,几何理解强
缺点:没有语言对齐,需要额外对齐

方案 3:CLIP + DINOv2 互补(如 Theia, Prismatic VLM)
图像 → CLIP ViT → concat → 视觉 token → LLM
→ DINOv2 → ↗
优点:兼顾语义理解和空间信息
缺点:计算量更大,token 数更多

Prismatic VLM(OpenVLA 使用的视觉骨架)就采用了方案 3:

# Prismatic VLM 的双编码器特征融合(简化版)
class DualVisionEncoder(nn.Module):
def __init__(self, clip_model, dino_model, projection_dim):
super().__init__()
self.clip_encoder = clip_model.vision_model
self.dino_encoder = dino_model

# 将两种特征投影到统一维度
self.clip_proj = nn.Linear(1024, projection_dim) # CLIP ViT-L
self.dino_proj = nn.Linear(1024, projection_dim) # DINOv2-L

def forward(self, images):
# 分别提取特征
clip_features = self.clip_encoder(images).last_hidden_state[:, 1:, :]
dino_features = self.dino_encoder(images).last_hidden_state[:, 1:, :]

# 投影 + 拼接
clip_proj = self.clip_proj(clip_features) # (B, N, D)
dino_proj = self.dino_proj(dino_features) # (B, N, D)

# 沿特征维度拼接
fused = torch.cat([clip_proj, dino_proj], dim=-1) # (B, N, 2D)
return fused

5. 三者详细对比

5.1 预训练范式对比

维度CLIPSigLIPDINOv2
监督信号图文对图文对仅图像
训练方式Softmax 对比学习Sigmoid 对比学习自蒸馏 + 掩码建模
训练数据量4 亿图文对WebLI 数据集1.42 亿图像
语言对齐
空间特征中等中等
语义理解中等(无语言)

5.2 输出特征对比

CLIP ViT-L/14 (224x224):
CLS token: (1, 768) ← 全局语义
Patch tokens: (1, 256, 1024) ← 16x16 grid

SigLIP-SO400M/14 (384x384):
CLS token: (1, 1152) ← 全局语义
Patch tokens: (1, 729, 1152) ← 27x27 grid(更高分辨率!)

DINOv2-L/14 (224x224):
CLS token: (1, 1024) ← 全局表示
Patch tokens: (1, 256, 1024) ← 16x16 grid(空间信息更丰富)

5.3 在 VLA 中的实际使用

VLA 模型使用的视觉编码器特征提取方式
RT-2CLIP ViT-LPatch tokens
PaLM-EViT-22B (类 SigLIP 训练)Patch tokens
OpenVLASigLIP + DINOv2 (Prismatic)Patch tokens 拼接
Octo从头训练的小 ViTPatch tokens
pi0SigLIPPatch tokens

5.4 VLA 中如何选择视觉编码器

在实际搭建 VLA 系统时,视觉编码器的选择取决于任务类型和资源约束,并没有放之四海而皆准的答案。以下是几条实用的决策思路:

  • 任务需要强语义理解(理解物体类别、场景语义、指令中的物体描述):优先选择 CLIP 或 SigLIP。它们通过大规模图文对训练,特征天然与语言语义对齐,模型能直接将"红色杯子"等自然语言描述与视觉特征挂钩,无需额外的对齐工作。

  • 任务需要细粒度空间感知(精确抓取、位姿估计、像素级定位):优先考虑 DINOv2。自监督训练保留了更多纹理和几何细节,patch 级特征的空间结构性远强于图文对比学习的产物。

  • SigLIP 优于 CLIP 的场景:训练资源有限时,SigLIP 的 Sigmoid 损失使每个样本独立计算,不依赖全局 batch 的负样本,小 batch 下也能有效收敛,复现成本远低于需要 32K batch 的 CLIP。

  • 实际 VLA 项目的常见选择:OpenVLA 使用 SigLIP(配合 DINOv2 的双编码器方案),RT-2 使用 CLIP ViT-C,Theia 专门研究了 DINOv2 特征在操控任务中的价值。

结论:没有绝对最优的编码器,任务类型和数据规模决定最终选择。


6. 图像预处理:从原始图像到模型输入

6.1 标准预处理流程

原始图像 Resize 归一化 Patch Embedding
(任意尺寸) ──→ (224×224×3) 或 (384×384×3) ──→ (标准化像素值) ──→ Token 序列
↓ ↓
双线性插值/ 减均值除标准差
Bicubic 插值 (ImageNet 统计量)

6.2 预处理代码详解

三种编码器的预处理流程结构相同(Resize → ToTensor → Normalize),主要差异在于输入分辨率和归一化参数:

编码器输入分辨率Resize 方式归一化均值 (mean)归一化标准差 (std)
CLIP ViT-L/14224×224Resize(224) + CenterCrop(224)[0.4815, 0.4578, 0.4082][0.2686, 0.2613, 0.2758]
SigLIP-SO400M384×384Resize((384, 384))[0.5, 0.5, 0.5][0.5, 0.5, 0.5]
DINOv2-L224×224Resize(256) + CenterCrop(224)[0.485, 0.456, 0.406][0.229, 0.224, 0.225]

以 CLIP 为例,标准预处理代码如下(SigLIP 和 DINOv2 只需替换分辨率和归一化参数):

from torchvision import transforms

# 标准 CLIP 预处理
clip_transform = transforms.Compose([
transforms.Resize(224, interpolation=transforms.InterpolationMode.BICUBIC),
transforms.CenterCrop(224),
transforms.ToTensor(), # [0, 255] → [0, 1]
transforms.Normalize(
mean=[0.48145466, 0.4578275, 0.40821073], # CLIP 特定的均值
std=[0.26862954, 0.26130258, 0.27577711] # CLIP 特定的标准差
),
])

6.3 分辨率与 Patch Embedding 的关系

输入分辨率Patch SizeGrid 大小Token 数量含义
224×2241416×16256每个 token 覆盖 14×14 像素区域
224×2241614×14196每个 token 覆盖 16×16 像素区域
384×3841427×27729更高分辨率,更多 token
336×3361424×24576LLaVA-1.5 使用的分辨率

VLA 中的考量

  • 更多 token = 更多空间信息 = 更好的操作精度
  • 但更多 token = LLM 需要处理更长序列 = 推理更慢
  • 需要在精度和速度之间权衡

6.4 VLA 中的图像预处理特殊注意

VLA 场景下的图像预处理与通常的视觉任务有本质区别:机器人摄像头返回的图像携带了完整的空间语义,预处理必须保留这些信息。具体而言,不能做随机水平翻转,因为翻转会颠倒左右方向的语义("拿左边的杯子"会变成拿右边);不能做大幅随机裁剪,否则会丢失工作台边缘、障碍物等关键环境信息;Resize 时也需注意保持长宽比,避免引入几何畸变。可以安全使用的增强手段包括颜色抖动(color jitter)、轻微缩放和高斯模糊——这些只改变外观而不改变空间关系。


7. 冻结 vs 微调:VLA 中如何使用视觉编码器

7.1 三种使用策略

策略 1:完全冻结(Frozen) ← 最常见
视觉编码器参数不更新,只训练后续模块
优点:训练快,不会破坏预训练特征
缺点:无法适配特定视觉域

策略 2:部分微调(Partial Fine-tuning)
冻结前几层,微调后几层
优点:平衡适配性和稳定性

策略 3:LoRA 微调
在视觉编码器中注入 LoRA 适配器
优点:极少参数即可适配,不破坏原始特征
# 策略 1:完全冻结 — 将视觉编码器所有参数的 requires_grad 设为 False
vision_encoder.requires_grad_(False)

# 策略 2:部分微调 — 冻结前 N 层,只放开最后几层的梯度
for layer in vision_encoder.layers[:N]: layer.requires_grad_(False)

# 策略 3:LoRA 微调 — 通过 PEFT 库对 Q/V 投影注入低秩适配器(r=16)
vision_encoder = get_peft_model(vision_encoder, LoraConfig(r=16, target_modules=["q_proj", "v_proj"]))

8. 总结与学习路线建议

8.1 核心知识速查

概念一句话总结
CLIP用对比学习让图像和文本在同一空间对齐
SigLIPCLIP 的改进版,用 Sigmoid 替代 Softmax,训练效率更高
DINOv2自监督视觉模型,擅长空间和几何理解
Patch TokensVLA 中取所有 patch 的特征(而非仅 CLS),保留空间信息
冻结编码器VLA 中最常见的做法,不更新视觉编码器参数
双编码器SigLIP + DINOv2 互补,兼顾语义和空间

8.2 建议学习顺序

1. 先跑通 CLIP 的使用示例(HuggingFace)
→ 理解 image_features 和 text_features 的含义

2. 学习 InfoNCE Loss 的数学推导
→ 理解对比学习的本质

3. 对比 CLIP 和 SigLIP 的代码差异
→ 理解 Sigmoid vs Softmax 的 trade-off

4. 用 DINOv2 做一个简单的可视化
→ 看看 DINOv2 的 patch features 能否分割物体

5. 阅读 OpenVLA 中 Prismatic VLM 的视觉编码器代码
→ 理解双编码器融合的实际实现

9. 本章小结与下一章预告

本章回答了两个核心问题:为什么 VLA 需要专门的视觉编码器,以及如何在不同编码器之间做出选择

视觉编码器的"对齐"目标不只是让图像变成向量——而是让视觉特征和语言特征进入同一个可比较的语义空间。CLIP 和 SigLIP 通过大规模图文对训练实现了这种对齐;DINOv2 则专注于保留图像自身的结构信息,为精细操控提供更丰富的空间特征。

这种对齐是下一章(第 5 章:多模态融合与 VLM)的基础:只有特征已经在语义上对齐,投影层的任务才相对简单。如果视觉特征和语言特征处于完全不同的语义空间,即使最复杂的融合模块也难以弥合这道鸿沟。

在"抓杯子"任务中: CLIP/SigLIP 让模型能把"红色杯子"的图像和文字描述关联起来;DINOv2 则提供了杯子的边缘、位置等精细空间信息。

下一章预告: 即使特征已经对齐,维度还不匹配——视觉特征是一组 token 向量,LLM 的输入是文本 token 序列,两者如何"拼"在一起?如何让 LLM 同时看到图像和文字并联合推理?这就是多模态融合和 VLM 架构要解决的核心问题。


10. 参考资料