Series · Transfer Learning · Chapter 7

迁移学习(七):零样本学习

从第一性原理推导零样本学习:DAP 属性原型、双线性与深度兼容性函数、DeViSE、生成式 ZSL 的 f-CLSWGAN、广义 ZSL 的偏置问题与校准方法,以及 CLIP 这种视觉-语言预训练带来的范式跃迁,附 PyTorch 核心实现。

你这辈子没见过斑马。但我告诉你它"长得像马,身上画了黑白条纹",下次走进动物园你一眼就能认出来。没标注样本、没有微调,只有一座语义桥梁把你已知的概念(马、条纹)和未知的物种连了起来。

零样本学习(Zero-Shot Learning, ZSL) 就是把这个本事教给机器。在一批已见类(seen classes) 上做有监督训练,测试时却要识别一组完全不重叠的未见类(unseen classes) —— 模型对未见类的唯一线索,是它们的语义描述:一组属性、类别名的词嵌入、一段文字、或者一句 CLIP 风格的提示语。模型能不能成功,全看它在共享的视觉-语义空间里学到了什么样的几何结构。

本文从一个公式出发 —— 图像与类别描述之间的兼容性函数 $F(x, c)$ —— 把过去十五年里出现的主流方法依次串起来:属性原型、双线性和深度兼容性、生成式特征合成、广义零样本学习的校准方法,以及 CLIP 这种把整个 ZSL 问题"用规模化"几乎吞掉的范式。

你将学到什么

  • ZSL 的形式化定义、已见 / 未见类划分
  • 属性表示与直接属性预测(DAP)
  • 兼容性函数:双线性、深度、排序损失(DeViSE、ALE)
  • 生成式 ZSL(f-CLSWGAN、f-VAEGAN-D2):把 ZSL 重新变回有监督学习
  • 广义零样本学习(GZSL) 为什么离开校准就崩溃,以及三类修法
  • CLIP:当今最有效、最易扩展的答案

前置知识

  • 本系列第 1–6 部分(尤其第 4 部分小样本学习、第 6 部分多任务学习)
  • PyTorch、softmax / 交叉熵训练
  • Word2Vec / GloVe 等词嵌入,以及对 GAN 的基本印象

1. 问题定义

记 $\mathcal{C}^s$ 为已见类集合(有标注训练数据),$\mathcal{C}^u$ 为未见类集合(一张标注图都没有)。ZSL 的标志性约束是

$$ \mathcal{C}^s \cap \mathcal{C}^u = \emptyset. $$

每个类别 $c$ —— 不管见过没见过 —— 都对应一个语义描述向量 $a_c \in \mathbb{R}^M$。这是已见类的信息能够"渗透"到未见类的唯一通道。它可以是:

  • 二值或连续的属性向量(如 有条纹有翅膀水生);
  • 类别名的词嵌入(Word2Vec、GloVe);
  • 一段百科文字的句嵌入(BERT [CLS]);
  • 视觉-语言模型里的提示语嵌入(CLIP)。

随后再分两种任务设定:

设定测试标签空间现实程度
经典 ZSL$y \in \mathcal{C}^u$简单基准;事先就知道测试图属于未见类。
广义 ZSL(GZSL)$y \in \mathcal{C}^s \cup \mathcal{C}^u$真实部署场景;难度高得多,因为模型天然偏向已见类。

把后面所有方法贯穿起来的就一个公式 —— 兼容性函数

$$ F: \mathcal{X} \times \mathcal{C} \to \mathbb{R}, \qquad \hat{y} = \arg\max_{c \in \mathcal{C}_{\text{test}}} F(x, c; \theta). $$

我们在已见类上学这个函数,然后指望语义空间的几何关系把它延拓到未见类。

有监督、小样本、零样本三种监督方式对比


2. 属性表示与 DAP

属性是最具可解释性的语义描述方式。每个属性都是人类定义的简短谓词,比如 有条纹四条腿有翅膀水生。常用的 AwA2(Animals with Attributes 2) 给 50 类动物各分配了 85 维属性向量;细粒度鸟类数据集 CUB-200-2011 用了 312 维属性。

把所有类别的属性向量堆起来,就得到一个 $|\mathcal{C}| \times M$ 的属性原型矩阵。例如:

$$ a_{\text{斑马}} = (\underbrace{1}_{\text{条纹}}, \underbrace{1}_{\text{四条腿}}, \underbrace{0}_{\text{翅膀}}, \underbrace{1}_{\text{蹄子}}, \ldots). $$

直接属性预测(DAP)

Lampert 等人 2009 年的工作 —— 也就是把现代 ZSL 这个问题正式立起来的那篇 —— 提出了一个非常清爽的两阶段流程:

阶段一:在已见类图像上,为每个属性单独训练一个二分类器:

$$ \hat{a}_m(x) = P(\text{属性 } m \mid x), \qquad m = 1, \ldots, M. $$

阶段二:测试时把 $M$ 个属性分类器全跑一遍得到 $\hat{a}(x)$,再选与之最近的未见类原型:

$$ \hat{y} = \arg\min_{c \in \mathcal{C}^u} d\bigl(\hat{a}(x),\, a_c\bigr). $$

举个例子:查询是一张斑马图。属性分类器在 有条纹四条腿有蹄子有鬃毛 上输出高分,在 翅膀水生 上输出低分。把 $\hat{a}(x)$ 跟原型矩阵做余弦相似度,斑马 排第一,老虎 紧随其后。

基于属性的零样本流程(DAP)

为什么能 work: 属性分类器之所以能迁移,是因为属性本身在不同类别之间是共享的。“条纹"这个谓词,对斑马(未见类)和老虎(已见类)来说是同一件事。

为什么不够好:

  1. 误差累积。属性分类器哪怕 90% 准确,$\hat{a}(x)$ 也是有噪声的,最近邻搜索还会把噪声放大。
  2. 属性独立性假设。DAP 默认在给定类别后属性互相独立,但 有翅膀会飞 显然高度相关。
  3. 标注成本。属性体系本身要专家定义、专家标注。

变体 IAP(Indirect Attribute Prediction) 反过来先预测已见类的概率分布、再通过属性矩阵转到未见类,但结构性局限依旧。


3. 兼容性函数:一个统一视角

不再把"预测属性"作为中间步骤,而是端到端地学 $F(x, c)$。

双线性(ALE / SJE)

最简单的形式是双线性:

$$ F(x, c) = \phi(x)^\top W\, a_c, $$

其中 $\phi(\cdot)$ 是 CNN 主干输出的 $d$ 维视觉特征,唯一的可训练参数是 $W \in \mathbb{R}^{d \times M}$。在已见类上用标准的 softmax 交叉熵训练:

$$ \mathcal{L}(x, y) = -\log \frac{\exp F(x, y)}{\sum_{c \in \mathcal{C}^s} \exp F(x, c)}. $$

Akata 等人的 ALE(2013)和 SJE(2015)把交叉熵换成了结构化排序损失 —— 要求正确类别的得分比每个错误类别至少高一个间隔 —— 实测上对未见类的泛化要明显好于普通 softmax。

深度兼容性 / 双塔

为捕获非线性的视觉-语义关系,用两个小 MLP 把两边都投到 $d$ 维共享空间:

$$ F(x, c) = \frac{f_v(\phi(x))^\top f_s(a_c)}{\|f_v(\phi(x))\|\, \|f_s(a_c)\|} \cdot \tau, $$

其中 $\tau$ 是可学习的温度。这种双塔结构,正是 DeViSE、CLIP、以及现代稠密检索的同一套骨架,区别只在数据规模。

共享语义嵌入空间

几何图景就是整个 ZSL 的全部直觉。训练时已见类的视觉特征围着对应的语义原型聚拢();未见类的原型(斑马狮子熊猫纯靠语义自己摆位置。一张查询图投到同一个空间里,落在最接近自己语义内容的未见类原型旁边 —— 那就是预测。

DeViSE:用词嵌入做语义侧

Frome 等人(2013)提出的 DeViSE(Deep Visual-Semantic Embedding)把人工属性换成了类别名的词嵌入(Word2Vec / GloVe)。架构是双塔,损失是 hinge 排序:

$$ \mathcal{L} = \sum_{c' \neq y} \max\bigl(0,\; m - F(x, y) + F(x, c')\bigr), $$

推断时与所有类别嵌入算分,包括从没看过任何图像的类别。

DeViSE:视觉编码器 + Word2Vec 文本编码器,共享嵌入空间

DeViSE 第一次证明了 ZSL 是可以扩展的:在 ImageNet 上训出的模型,能对 WordNet 里数万个零样本类别给出像样的预测。它的弱点是词嵌入编码的是语言相似性宠物 聚一起),而不是视觉相似性(按视觉应当聚成一堆的 斑马老虎美洲豹 —— 都有条纹 —— 在词向量里反而散开)。这种模态鸿沟(modality gap) 直接催生了后面所有的工作。


4. 生成式 ZSL:直接合成未见类的视觉特征

判别式 ZSL 学一个固定的兼容性函数,然后指望它能延拓。生成式 ZSL 反过来想:直接用语义描述合成未见类的视觉特征,再用合成特征 + 真实已见特征训一个完全普通的有监督分类器。ZSL 又变回了有监督学习。

f-CLSWGAN(Xian et al., 2018)

一个带分类器引导的条件 Wasserstein GAN:

  • 生成器 $G(z, a_c) \to \tilde{x}$。输入:噪声 $z \sim \mathcal{N}(0, I)$ 和类别语义 $a_c$;输出:合成的 CNN 特征。
  • 评论家 $D(x)$ 给真实 / 合成特征打分(无 sigmoid,Wasserstein 损失)。
  • 辅助分类器 $\mathrm{cls}$ 接在生成特征上,由真实已见类特征训练得到。

总目标是

$$ \mathcal{L} = \underbrace{\mathbb{E}[D(\tilde{x})] - \mathbb{E}[D(x)] + \lambda\,\mathrm{GP}}_{\text{WGAN-GP}} \;+\; \beta \cdot \underbrace{\mathbb{E}\bigl[-\log P(y \mid \tilde{x})\bigr]}_{\text{分类损失}}, $$

其中 $\mathrm{GP}$ 是标准的梯度惩罚 $\mathbb{E}[(\|\nabla_{\hat{x}} D(\hat{x})\|_2 - 1)^2]$。分类损失是关键 —— 它逼着合成特征不仅"像真”,还要类别可分

测试时:对每个未见类采样 $\tilde{x}^{(u)} \sim G(z, a_u)$,把 $\{(x, y) : y \in \mathcal{C}^s\} \cup \{(\tilde{x}^{(u)}, u)\}$ 喂进 softmax 训一个普通分类器即可。仅这一步,AwA2 上 GZSL 的调和平均 $H$ 就能从约 22 跳到约 58。

f-VAEGAN-D2

Xian 等人(2019)在前者基础上加了一个 VAE 编码器稳定训练,又加了第二个判别器利用未标注的测试图像(transductive)。“VAE 稳训练 + GAN 提质量 + 分类器保可分"这套配方,目前已经是特征生成式 ZSL 的标配。


5. 广义零样本学习:偏置问题

经典 ZSL 基准把测试标签空间限制在 $\mathcal{C}^u$ 内,等于偷偷绕开了真正的难点:实际部署时,你根本不知道一张图属于已见类还是未见类。

把标签空间放开到 $\mathcal{C}^s \cup \mathcal{C}^u$ 后,所有只在已见类图像上训出来的模型都会出现严重的已见类偏置。已见类的视觉特征落在 $F$ 已经熟悉的区域里;未见类的特征略显分布外,几乎一定输掉 argmax。

标准评估指标是已见准确率与未见准确率的调和平均数

$$ H = \frac{2 \cdot S \cdot U}{S + U}. $$

它会惩罚任何"赢了一边、输了另一边"的方法。一个 $S=88, U=12$ 的模型,$H$ 只有 21 —— 极差。

GZSL 偏置问题与三种修法

三类修法:

1. 校准堆叠(Chao et al., 2016)。 把所有已见类的得分整体减去一个常数:

$$ F_{\text{cal}}(x, c) = F(x, c) - \gamma \cdot \mathbb{1}[c \in \mathcal{C}^s]. $$

$\gamma$ 在验证集上搜一下就行。便宜、有效,是最先该尝试的强基线。

2. 生成式特征合成(见第 4 节)。一旦能合成未见类特征,GZSL 就退化成了一个数据均衡的有监督分类问题。

3. OOD 门控。 训一个二分类检测器判断"这张图到底属于已见还是未见”,再路由到对应的子分类器。整体性能受限于这个 OOD 检测器本身。


6. CLIP 与视觉-语言预训练时代

回过头看,CLIP(Radford et al., 2021)就是第 3 节那种深度双塔兼容性函数 —— 只不过训练数据是从网络爬来的 4 亿张图文对,损失是双向对比交叉熵:

$$ \mathcal{L} = -\sum_i \log \frac{\exp\bigl(I_i \cdot T_i / \tau\bigr)}{\sum_j \exp\bigl(I_i \cdot T_j / \tau\bigr)} \;+\; (\text{在文本一侧对称地再来一遍}). $$

零样本推断时,用文字现场搭分类器。给定 $K$ 类问题,写一个提示模板(如 "a photo of a {class}"),把 $K$ 个提示喂给文本塔得到 $K$ 个文本向量,新图像走视觉塔得到一个图像向量,谁的余弦相似度最高就预测谁。

CLIP 零样本分类流程

两点值得记住的影响:

  1. 不再需要语义工程。类别用自然语言描述。要新增一个类?写一句话就行。
  2. GZSL 不再是独立问题。CLIP 在它"训过"和"没训过"的类别上准确率相当,因为它从一开始就不是按类别判别的方式训练出来的。

基准曲线印证了这条主线:

AwA2 与 CUB 零样本基准的演进

DAP(2009)→ ALE / DeViSE(2013):双线性与排序兼容性;SAE(2017):语义自编码器;f-CLSWGAN(2018)→ CADA-VAE(2019):生成式特征合成关闭了 GZSL 的差距;CLIP(2021):大规模对比预训练把上限推向了新的高度。


7. 实现

下面是深度兼容性模型和特征生成 GAN 的精简 PyTorch 骨架,足够看清楚结构。完整训练脚手架(AwA2 数据加载、ZSL 与 GZSL 评估)在文末参考实现里。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import torch
import torch.nn as nn
import torch.nn.functional as F


class DeepCompatibility(nn.Module):
    """DeViSE / ALE / CLIP 共用的视觉-语义双塔兼容性。"""

    def __init__(self, visual_dim: int, semantic_dim: int,
                 embed_dim: int = 512, temperature: float = 10.0):
        super().__init__()
        self.vis = nn.Sequential(
            nn.Linear(visual_dim, 1024), nn.ReLU(),
            nn.Dropout(0.5), nn.Linear(1024, embed_dim),
        )
        self.sem = nn.Sequential(
            nn.Linear(semantic_dim, 512), nn.ReLU(),
            nn.Dropout(0.5), nn.Linear(512, embed_dim),
        )
        self.tau = temperature

    def forward(self, x: torch.Tensor, a: torch.Tensor) -> torch.Tensor:
        """对每张图像、每个类别打分,返回 [B, C] logits。"""
        v = F.normalize(self.vis(x), dim=-1)        # [B, d]
        s = F.normalize(self.sem(a), dim=-1)        # [C, d]
        return self.tau * v @ s.t()


class FeatureGenerator(nn.Module):
    """f-CLSWGAN 风格的条件特征生成器。"""

    def __init__(self, noise_dim: int, semantic_dim: int, feature_dim: int):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(noise_dim + semantic_dim, 4096), nn.LeakyReLU(0.2),
            nn.Linear(4096, feature_dim), nn.ReLU(),
        )

    def forward(self, z: torch.Tensor, a: torch.Tensor) -> torch.Tensor:
        return self.net(torch.cat([z, a], dim=-1))


def gzsl_evaluate(model: DeepCompatibility, features: torch.Tensor,
                  labels: torch.Tensor, attrs: torch.Tensor,
                  seen_ids: torch.Tensor, unseen_ids: torch.Tensor,
                  gamma: float = 0.0) -> tuple[float, float, float]:
    """在 GZSL 测试集上计算 (S, U, H),可选校准堆叠。"""
    model.eval()
    with torch.no_grad():
        logits = model(features, attrs)                 # [N, C]
        logits[:, seen_ids] -= gamma                    # 已见类整体降权
        pred = logits.argmax(dim=-1)
        seen_mask = torch.isin(labels, seen_ids)
        S = (pred[seen_mask] == labels[seen_mask]).float().mean().item()
        U = (pred[~seen_mask] == labels[~seen_mask]).float().mean().item()
    H = 2 * S * U / (S + U + 1e-8)
    return S, U, H

gzsl_evaluate 里的 gamma 就是校准堆叠 —— GZSL 上首先该试的最简修法。


8. Q&A

Q1:什么时候选 ZSL,而不是小样本或主动学习? 满足以下任一条件就可以考虑:(a) 长尾类别又宽又不稳定,新类来得比标注还快;(b) 你已经有现成的丰富语义资源,比如属性体系、商品目录、文字描述;(c) 你能直接用上预训练的视觉-语言模型(CLIP、SigLIP、OpenCLIP),那干脆跳过 ZSL 专用机器,直接用 CLIP 推断。

Q2:属性、词嵌入、句嵌入、CLIP 提示,到底用哪个? 属性单维信息量最大但需要专家设计;词嵌入扩展性好但编码的是语言相似而非视觉相似;句嵌入是个折中;CLIP 提示在大多数自然图像基准上压倒所有低层方法 —— 因为它的视觉、文本编码器是联合训练的。

Q3:GZSL 为什么不校准就崩? 训练目标只奖励已见类的正确预测,已见和未见 logit 之间没有任何尺度对齐。测试时已见类得分系统性更高,几乎每次都抢走 argmax。校准堆叠、生成合成、OOD 门控本质上都在攻这个不平衡。

Q4:CLIP 之后,传统 ZSL 研究还有意义吗? 对自然图像:基本没有了。但对开源数据稀缺的领域 —— 医学影像、工业缺陷、遥感图 —— 老一套属性 / 语义机制仍然是数据效率最高的冷启动方式。而且 CLIP 本身就是一个深度双塔兼容性模型,本文搭建的概念框架仍然完全适用。

Q5:怎么避开 Hubness 问题? Hubness 是高维空间的病态:少数"枢纽"原型成了太多查询的最近邻。常见做法:打分前在两侧都做 L2 归一化(上面代码里已有);用基于排名的相似度(CSLS、ZestNorm);或对每个类别的得分做标准化。


参考文献

  • Lampert, C. H., Nickisch, H., & Harmeling, S. (2009). Learning to detect unseen object classes by between-class attribute transfer. CVPR. —— DAP,AwA 数据集。
  • Frome, A., et al. (2013). DeViSE: A deep visual-semantic embedding model. NeurIPS.
  • Akata, Z., Perronnin, F., Harchaoui, Z., & Schmid, C. (2013). Label-embedding for attribute-based classification. CVPR. —— ALE。
  • Chao, W.-L., et al. (2016). An empirical study and analysis of generalized zero-shot learning for object recognition in the wild. ECCV. —— GZSL 与校准堆叠。
  • Xian, Y., Lampert, C. H., Schiele, B., & Akata, Z. (2019). Zero-shot learning — A comprehensive evaluation of the good, the bad and the ugly. TPAMI.
  • Xian, Y., Lorenz, T., Schiele, B., & Akata, Z. (2018). Feature generating networks for zero-shot learning. CVPR. —— f-CLSWGAN。
  • Xian, Y., Sharma, S., Schiele, B., & Akata, Z. (2019). f-VAEGAN-D2: A feature generating framework for any-shot learning. CVPR.
  • Schönfeld, E., et al. (2019). Generalized zero- and few-shot learning via aligned variational autoencoders. CVPR. —— CADA-VAE。
  • Radford, A., et al. (2021). Learning transferable visual models from natural language supervision. ICML. —— CLIP。

系列导航

部分主题
1基础与核心概念
2预训练与微调
3域适应
4小样本学习
5知识蒸馏
6多任务学习
7零样本学习(本文)
8多模态迁移
9参数高效微调
10持续学习
11跨语言迁移
12工业应用与最佳实践

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub