Jack He's Blog

some idea or work

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
.
├── checkpoints
│ └── resnet50-0676ba61.pth
├── CODE_OF_CONDUCT.md
├── codespell_ignored.txt
├── configs
│ ├── datasets # 数据集
│ │ ├── aircraft
│ │ ├── cifar10
│ │ ├── cifar100
│ │ ├── covid
│ │ ├── imagenet
│ │ ├── imagenet200
│ │ ├── mnist
│ │ ├── mvtec
│ │ ├── osr_cifar50
│ │ ├── osr_cifar6
│ │ ├── osr_mnist6
│ │ ├── osr_tin20
│ │ └── plankton
│ ├── networks # training方法用到的网络结构
│ │ ├── arpl_gan.yml
│ │ ├── arpl_net.yml
│ │ ....
│ │ ├── vit.yml
│ │ └── vos_net.yml
│ ├── pipelines
│ │ ├── test
│ │ └── train
│ ├── postprocessors # post-hoc方法用到的处理方法
│ │ ├── ash.yml
│ │ ├── cider.yml
│ │ ...
│ │ ├── ssd.yml
│ │ ├── temp_scaling.yml
│ │ └── vim.yml
│ └── preprocessors # 前处理器
│ ├── augmix_preprocessor.yml
│ ├── base_preprocessor.yml
│ ├── csi_preprocessor.yml
│ ├── cutout_preprocessor.yml
│ ├── cutpaste_preprocessor.yml
│ ├── draem_preprocessor.yml
│ ├── patchcore_preprocessor.yml
│ ├── pixmix_preprocessor.yml
│ └── randaugment_preprocessor.yml
├── CONTRIBUTING.md
├── data
│ ├── benchmark_imglist
│ │ ├── cifar10
│ │ ├── cifar100
│ │ ...
│ │ └── plankton
│ ├── benchmark_imglist2
│ │ ├── ID_test.txt
│ │ ├── ID_train.txt
│ │ ├── ID_val.txt
│ │ └── OOD_test.txt
│ ├── cifar10
│ │ ├── cifar-10-batches-py
│ │ ├── cifar-10-python.tar.gz
│ │ ├── test_cifar10.txt
│ │ ├── test_images
│ │ ├── train_cifar10.txt
│ │ └── train_images
│ ├── DYB-PlanktonNet
│ │ ├── 001_Polychaeta_most with eggs
│ │ ├── 002_Polychaeta larva
│ │ ├── 003_Polychaeta_Type A
│ │ ├── 004_Polychaeta_Type B
│ │ ├── 005_Polychaeta_Type C
│ │ ├── 006_Polychaeta_Type D
│ │ ├── 007_Polychaeta_Type E
│ │ ├── 008_Polychaeta_Type F
│ │ ├── 009_Penilia avirostris
│ │ ├── 010_Evadne tergestina
│ │ ├── 011_Acartia sp.A
│ │ ├── 012_Acartia sp.B
│ │ ├── 013_Acartia sp.C
│ │ ├── 014_Calanopia sp
│ │ ├── 015_Labidocera sp
│ │ ├── 016_Tortanus gracilis
│ │ ├── 017_Calanoid with egg
│ │ ├── 018_Calanoid Nauplii
│ │ ├── 019_Calanoid_Type A
│ │ ├── 020_Calanoid_Type B
│ │ ├── 021_Calanoid_Type C
│ │ ├── 022_Calanoid_Type D
│ │ ├── 023_Oithona sp.A with egg
│ │ ├── 024_Oithona sp.B with egg
│ │ ├── 025_Cyclopoid_Type A_with egg
│ │ ├── 026_Cyclopoid_Type A
│ │ ├── 027_Harpacticoid_mating
│ │ ├── 028_Harpacticoid
│ │ ├── 029_Microsetella sp
│ │ ├── 030_Monstrilla sp.A
│ │ ├── 031_Monstrilla sp.B
│ │ ├── 032_Monstrilloid
│ │ ├── 033_Caligus sp
│ │ ├── 034_Copepod_Type A
│ │ ├── 035_Caprella sp
│ │ ├── 036_Amphipoda_Type A
│ │ ├── 037_Amphipoda_Type B
│ │ ├── 038_Amphipoda_Type C
│ │ ├── 039_Gammarids_Type A
│ │ ├── 040_Gammarids_Type B
│ │ ├── 041_Gammarids_Type C
│ │ ├── 042_Cymodoce sp
│ │ ├── 043_Lucifer sp
│ │ ├── 044_Macrura larvae
│ │ ├── 045_Megalopa larva_Phase 1_Type A
│ │ ├── 046_Megalopa larva_Phase 1_Type B
│ │ ├── 047_Megalopa larva_Phase 1_Type C
│ │ ├── 048_Megalopa larva_Phase 1_Type D
│ │ ├── 049_Megalopa larva_Phase 2
│ │ ├── 050_Porcrellanidae larva
│ │ ├── 051_Shrimp-like larva_Type A
│ │ ├── 052_Shrimp-like larva_Type B
│ │ ├── 053_Shrimp-like_Type A
│ │ ├── 054_Shrimp-like_Type B
│ │ ├── 055_Shrimp-like_Type C
│ │ ├── 056_Shrimp-like_Type D
│ │ ├── 057_Shrimp-like_Type E
│ │ ├── 058_Shrimp-like_Type F
│ │ ├── 059_Ostracoda
│ │ ├── 060_Cumacea_Type A
│ │ ├── 061_Cumacea_Type B
│ │ ├── 062_Chaetognatha
│ │ ├── 063_Oikopleura sp. parts
│ │ ├── 064_Oikopleura sp
│ │ ├── 065_Tunicata_Type A
│ │ ├── 066_Actiniaria larva
│ │ ├── 067_Hydroid
│ │ ├── 068_Jellyfish
│ │ ├── 069_Jelly-like
│ │ ├── 070_Bryozoan larva
│ │ ├── 071_Creseis acicula
│ │ ├── 072_Gelatinous Zooplankton
│ │ ├── 073_Unknown_Type A
│ │ ├── 074_Unknown_Type B
│ │ ├── 075_Unknown_Type C
│ │ ├── 076_Unknown_Type D
│ │ ├── 077_Balanomorpha exuviate
│ │ ├── 078_Crustacean limb_Type A
│ │ ├── 079_Crustacean limb_Type B
│ │ ├── 080_Fish egg
│ │ ├── 081_Fish Larvae
│ │ ├── 082_Noctiluca scintillans
│ │ ├── 083_Particle_filamentous_Type A
│ │ ├── 084_Particle_filamentous_Type B
│ │ ├── 085_Particle_bluish
│ │ ├── 086_Particle_molts
│ │ ├── 087_Particle_translucent flocs
│ │ ├── 088_Particle_yellowish flocs
│ │ ├── 089_Particle_yellowish rods
│ │ ├── 090_Bubbles
│ │ ├── 091_Phaeocystis globosa
│ │ └── 092_Fish tail
│ ├── ID_train.txt
│ ├── ID_val.txt
│ ├── images_classic
│ │ ├── cifar10
│ │ └── mnist
│ └── OOD_test.txt
├── easy_dev.ipynb
├── environment.yml
├── id_acc_eval.py
├── imglist_generator.py
├── LICENSE
├── logs
│ ├── plankton50_ash_inference.log
│ ...
│ ├── plankton50_temp_scaling_inference.log
│ └── plankton50_vim_inference.log
├── main.py
├── openood
│ ├── datasets
│ │ ├── base_dataset.py
│ │ ├── feature_dataset.py
│ │ ├── imglist_augmix_dataset.py
│ │ ├── imglist_dataset.py
│ │ ├── imglist_extradata_dataset.py # 构造含有额外数据的数据集
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ ├── udg_dataset.py
│ │ └── utils.py
│ ├── evaluation_api
│ │ ├── datasets.py
│ │ ├── evaluator.py
│ │ ├── __init__.py
│ │ ├── postprocessor.py
│ │ ├── preprocessor.py
│ │ └── __pycache__
│ ├── evaluators
│ │ ├── ad_evaluator.py
│ │ ├── arpl_evaluator.py
│ │ ├── base_evaluator.py
│ │ ├── ece_evaluator.py
│ │ ├── fsood_evaluator.py
│ │ ├── __init__.py
│ │ ├── metrics.py
│ │ ├── mos_evaluator.py
│ │ ├── ood_evaluator.py
│ │ ├── osr_evaluator.py
│ │ ├── patchcore_evaluator.py
│ │ ├── __pycache__
│ │ └── utils.py
│ ├── __init__.py
│ ├── losses
│ │ ├── draem_loss.py
│ │ ├── focal.py
│ │ ├── __init__.py
│ │ ├── kdad_losses.py
│ │ ├── __pycache__
│ │ ├── rd4ad_loss.py
│ │ ├── reweight.py
│ │ ├── sce.py
│ │ └── ssim.py
│ ├── networks # training方法使用的网络框架
│ │ ├── arpl_net.py
│ │ ├── ash_net.py
│ │ ...
│ │ ├── vit.py
│ │ └── wrn.py
│ ├── pipelines # 各种pipeline
│ │ ├── feat_extract_opengan_pipeline.py
│ │ ├── feat_extract_pipeline.py
│ │ ├── finetune_pipeline.py
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ ├── test_acc_pipeline.py
│ │ ├── test_ad_pipeline.py
│ │ ├── test_ood_pipeline_aps.py
│ │ ├── test_ood_pipeline.py
│ │ ├── train_ad_pipeline.py
│ │ ├── train_aux_pipeline.py
│ │ ├── train_ddt_pipeline.py
│ │ ├── train_oe_pipeline.py
│ │ ├── train_only_pipeline.py
│ │ ├── train_opengan_pipeline.py
│ │ ├── train_pipeline.py # 训练代码
│ │ └── utils.py
│ ├── postprocessors
│ │ ├── ash_postprocessor.py
│ │ ├── base_postprocessor.py
│ │ ...
│ │ ├── utils.py
│ │ └── vim_postprocessor.py
│ ├── preprocessors
│ │ ├── augmix_preprocessor.py
│ │ ├── base_preprocessor.py
│ │ ├── cider_preprocessor.py
│ │ ├── csi_preprocessor.py
│ │ ├── cutout_preprocessor.py
│ │ ├── cutpaste_preprocessor.py
│ │ ├── draem_preprocessor.py
│ │ ├── __init__.py
│ │ ├── pixmix_preprocessor.py
│ │ ├── __pycache__
│ │ ├── randaugment_preprocessor.py
│ │ ├── test_preprocessor.py
│ │ ├── transform.py
│ │ └── utils.py
│ ├── __pycache__
│ │ ├── __init__.cpython-310.pyc
│ │ ├── __init__.cpython-312.pyc
│ │ └── __init__.cpython-38.pyc
│ ├── recorders
│ │ ├── ad_recorder.py
│ │ ├── arpl_recorder.py
│ │ ...
│ │ ├── opengan_recorder.py
│ │ ├── __pycache__
│ │ ├── rd4ad_recorder.py
│ │ └── utils.py
│ ├── trainers # 相关的训练函数
│ │ ├── arpl_gan_trainer.py
│ │ ├── arpl_trainer.py
│ │ ├── augmix_trainer.py
│ │ ...
│ │ ├── udg_trainer.py
│ │ ├── utils.py
│ │ └── vos_trainer.py
│ └── utils
│ ├── comm.py
│ ├── config.py
│ ├── __init__.py
│ ├── launch.py
│ ├── logger.py
│ └── __pycache__
├── plot_plankton.ipynb
├── python
├── README.md
├── resnet50-0676ba61.pth
├── results # 相关结果
│ ├── cifar10_resnet18_32x32_base_e100_lr0.1_default
│ │ └── s0
│ ├── old_plankton50_resnet50_base_e100_lr0.1_default
│ │ ├── cifar10_res18_acc94.30.ckpt
│ │ ├── ood
│ │ ├── s0
│ │ └── scores_new
│ └── plankton50_resnet50_base_e100_lr0.1_default
│ ├── merged_results.csv
│ ├── ood
│ └── s0
├── scripts
│ ├── ad # 异常检测方法
│ │ ├── cutpaste
│ │ ...
│ │ └── rd4ad
│ ├── basics # 基础数据集
│ │ ├── cifar10
│ │ ├── cifar100
│ │ ├── covid
│ │ ├── imagenet
│ │ ├── imagenet200
│ │ ├── mnist
│ │ ├── osr_cifar50
│ │ ├── osr_cifar6
│ │ ├── osr_mnist6
│ │ └── osr_tin20
│ ├── eval_ood_imagenet.py
│ ├── eval_ood.py # OOD评测函数
│ ├── ood # OOD方法
│ │ ├── ash
│ │ ├── cider
│ │ ...
│ │ ├── udg
│ │ ├── vim
│ │ └── vos
│ ├── osr # OSR 识别方法
│ │ ├── arpl
│ │ ├── opengan
│ │ └── openmax
│ ├── sweep
│ │ ├── sweep_hyperparam.py
│ │ ├── sweep_posthoc_ood.sh
│ │ ├── sweep_posthoc_osr.sh
│ │ ├── sweep_posthoc.py
│ │ ├── sweep_posthoc.sh
│ │ ├── sweep_posthoc_total.sh
│ │ ├── sweep_train.py
│ │ ├── sweep_train.sh
│ │ └── write_metrics.py
│ └── uncertainty # 没有具体归类的方法
│ ├── augmix
│ ├── cutmix
│ ├── cutout
│ ├── deepaugment
│ ├── ensemble
│ ├── mc_dropout
│ ├── mixup
│ ├── pixmix
│ ├── randaugment
│ ├── regmixup
│ ├── rts
│ ├── styleaug
│ └── temp_scaling
├── setup.py
├── split_dataset_by_class.py
├── split_dataset_ood_val.py
├── split_dataset.py
├── test_all_plankton.py
└── tools
├── plot
│ └── tsne_tools.py
├── report
│ ├── plot.ipynb
│ ├── report_gmm.ipynb
│ └── report_table.ipynb
└── sweep
└── hyperparam.py

249 directories, 347 files

训练命令:

1
2
3
4
5
python main.py  /
--config configs/datasets/plankton/plankton50.yml configs/pipelines/train/baseline.yml
configs/networks/resnet50.yml
--num_workers 4
--num_gpus 1

验证命令:

1
python scripts/eval_ood.py --id-data plankton50 --save-score --save-csv --root ./results/plankton50_resnet50_base_e100_lr0.1_default --postprocessor "styleaugment"

Introduction

技术回顾

研究人员通过寻找ID样例自然拥有而OOD样例容易违反的属性(反之亦然),设计了不少评分函数。得分主要来源于三个来源:
(1)概率,如最大softmax的概率[13]和最小softmax与平均类别(mean class conditional)的kl -散度[12];
(2)logit,如最大logit [12], logsumexp函数超过logit [25];
(3)特征,如特征与其低维嵌入的预像[27]之间的残差的范数、特征与类质心[23]之间的最小马氏距离等。

在这些方法中,OOD分数可以直接从现有模型中计算出来,而无需重新训练,使得部署变得轻松。

然而,如图所示,它们的性能受到其信息源的单一性的限制:单独使用特征忽略了具有类相关信息的分类权重;使用logit或softmax只会忽略零空间[3]中的特征变化,该空间携带与类无关的信息;softmax进一步抛弃了对数范数。
image.png|500
矩形使用的是特征空间,三角形使用的是Logit,菱形使用的是softmax

[!question] 为了应对OOD样本中表现出的巨大多样性,我们提出了一个问题,设计一个使用多种来源的OOD分数是否有帮助?

在现有技术成功的基础上,我们设计了一个新的评分函数,称为虚拟logit匹配(ViM)分数,这是一个构建的虚拟OOD类的softmax分数,其logit由特征和现有logit共同决定。具体来说,评分函数首先提取特征对主子空间的残差,然后通过将其在训练样本上的均值与平均最大logits匹配,将其转换为有效logit。最后,所设计的OOD类的softmax概率就是OOD分数。 从ViM的构造中,我们可以直观地看到,原始logits越小,残差越大,它越有可能是OOD。

与上述方法不同的是,另一项研究通过施加专门的正则化损失[5,16,18,40]或通过暴露生成的或真实收集的OOD样本[22,37],对网络学习到的特征进行调整,以更好地识别ID和OOD。由于它们都需要对网络进行再训练,我们在这里简单地提到它们,而不深入研究细节。(training 方法)

数据集介绍

近年来,大规模语义空间中的OOD检测受到越来越多的关注[12,15,18,29],将OOD检测方法推向了现实应用。然而,目前缺乏用于大规模ID数据集的干净、真实的OOD数据集成为该领域的一个障碍。

以前的OOD数据集是从公共数据集中挑选出来的,这些数据集是用预定义的标签列表收集的,如iNaturalist、Texture和ImageNet-21k(表1)。这可能会导致有偏差的表现比较,特别是第5节中描述的小覆盖率的可黑客性。为了避免这种风险,我们从具有自然类分布的OpenImage数据集[21]中为ImageNet-1K[4]模型构建了一个新的OOD基准OpenImage-o。它包含17,632张手动过滤的图像,比最近的ImageNet-O[15]数据集大7.8倍。

image.png

Motivation: Logit中缺失的信息

  • 基于特征的OOD分数(例如Mahalanobis和Residual)擅长检测ImageNet-O中的OOD,而所有基于logit/概率的方法都落后。
  • 最先进的基于概率的方法 KL 匹配的 AUROC 仍然低于纹理数据集特征空间中直接设计的 OOD 分数。

image.png

基于零空间的OOD评分

将特征分解为 $x = x^{W⊥} + x^W$,其中$x^{W⊥}$和$x^W$分别是$x$在$W^⊥$和$W$上的投影,$W^⊥$是$W^T$的零空间,而$W$是$\mathbf{W}$的列空间,$x^{W⊥}$不影响分类,但是对OOD检测有重要作用
$$
NuSA(x)=\frac{\sqrt{ \lvert \lvert x \rvert \rvert ^2-\lvert \lvert x^{W⊥} \rvert \rvert ^2 }}{\lvert \lvert x \rvert \rvert }
$$
NuSA得分实际上是特征向量在主空间上的能力占总能量的比例。NuSA得分在0到1之间。当特征向量完全在主空间时,NuSA得分为1,当特征向量完全在零空间时,NuSA得分为0.OOD样本在零空间上的能量较大,因此其NuSA得分较低。

image.png

基于主空间的OOD评分

低维流形 (Low-Dimensional Manifold) 是指数据或特征在高维空间中实际位于一个相对较低维度的子空间上。这一概念广泛应用于机器学习和数据分析中,特别是在降维、特征提取和异常检测等领域。

假设特征向量分布在一个低维流形上,并使用通过原点$O$的线性子空间作为模型。定义主空间为由矩阵 $X^TX$ 的最大$D$个特征值对应的特征向量所张成的$D$维子空间$P$。偏离主空间的特征可能是OOD样本。因此考虑计算特征与主空间的偏差:
$$
Residual(x)=\lvert \lvert x^{P⊥} \rvert \rvert
$$
这里的$x^{P⊥}$是$x$到$P⊥$的投影。残差分数与[27]中的重构误差相似,只不过它们采用了非线性流形学习进行降维。注意,在投影到logits之后,这种偏差被破坏了,因为矩阵$W^T$投影到比特征空间更低的维度空间。

NuSA 和 Residual的缺点

与 logit/概率方法相比,NuSA 和 Residual 都不考虑特定于各个 ID 类的信息,即它们是类不可知的。因此,这些分数忽略了与每个 ID 类别的特征相似性,并且不知道输入最类似于哪个类别。
这解释了为什么它们在iNaturalist OOD基准上的表现更差,因为iNaturalist样本需要区分细粒度类别之间的细微差异。

image.png

虚拟logit匹配

Logits 包含与类相关的信息,但特征空间中存在无法从 Logits 恢复的与类无关的信息。为了统一 OOD 检测的类不可知和类相关信息,提出了通过 Virtual-logit 匹配(缩写为 ViM)的 OOD 评分。

具体步骤包含三步,分别针对特征、logit和概率进行操作:

  • 提取残差:提取输入特征 $x$ 相对于主子空间(即主要成分分析得到的低维子空间)的残差$x^{P\perp}$。这意味着将输入特征分解为主成分和残差两部分。
  • 转换为logit:通过将这些残差的均值匹配到训练样本中的平均最大logit,将残差转换为有效的logit。这一步的目的是使得残差能反映出与现有logit相当的概率信息。
  • 计算softmax概率:计算这个虚拟OOD类别的softmax概率,这个概率即为ViM得分。

image.png
image.png

OpenImage-O 数据集

为 ID 数据集 ImageNet-1K 构建了一个名为 OpenImage-O 的新 OOD 数据集:

  • 它是手动注释的,具有自然多样化的分布,并且具有17,632张图像的大规模。
  • 它的构建是为了克服现有 OOD 基准的几个缺点。
  • OpenImage-O 是从 OpenImageV3 的测试集中逐图像选择的,包括从 Flickr 收集的 125,436 张图像,没有预定义的类名称或标签列表,从而实现自然的类统计并避免初始设计偏差。
  1. 图像级注释的必要性
    过去的一些工作仅仅根据类别标签从其他数据集中选择部分数据来进行OOD检测。虽然类别级别的注释成本较低,但生成的数据集可能包含大量不符合预期的噪音。因此,简单地通过查询标签创建OOD数据集是不可靠的,需要对每个图像进行人工检查以确认其有效性。
  2. 小覆盖范围的可破解性
    如果 OOD 数据集有一个中心主题,例如纹理,其分布不太多样化,那么它可能很容易被“黑客攻击”。
  3. OpenImage-O 的构建过程
    基于 OpenImage-v3 数据集构建 OpenImage-O:
  • 对于测试集中的每张图像,我们让人工标注人员确定它是否是 OOD 样本。
  • 为了辅助标记,我们将任务简化为将图像与 ImageNet-1K 分类模型预测的前 10 个类别区分开来,即,如果图像不属于这 10 个类别中的任何一个,则该图像为 OOD。
  • 提供类别标签以及每个类别中与测试图像最相似的图像(通过特征空间中的余弦相似度来测量)以进行可视化。

为了进一步提高标注质量,我们设计了几种方案:
(1)如果标注者无法确定图像是否属于10个类别中的任何一个,则可以选择“困难”;
(2)每张图像由至少两个标注器独立标注,并取两者一致的OOD图像集;
(3)抽查检验,确保质量。

Learning Transferable Visual Models From Natural Language Supervision

利用自然语言的监督信号,训练一个迁移性能好的视觉模型

image.png
zero-shot

多模态
利用自然语言提供的信号训练

在图片-文本对上做对比学习

  • 正样本:图片文本对(对角线元素) $n$
  • 负样本:非对角线$n^2-n$

prompt template

刚刚只是做训练,得到了特征,还没有分类头。于是需要 prompt template 进行帮助分类

以ImageNet为例,把每个类对应的每一个单词组合起来变成每一个句子(eg. 这是一架飞机),再进行encoder,得到文本的特征

[!ques] 为什么不直接抽取单词的文本特征
训练的时候是丢一个句子文本进行训练,因此如果直接抽取单词的特征,那么就跟训练的时候不太一样了,就不太符合训练特征

拿图片特征与文本特征计算余弦相似度,最相似的对应图片对应的类

彻底摆脱了categorical label的限制,不需要一个提前定好标签的列表了。任意给一张照片,都可以通过给模型喂不同的文本句子,从而判断有没有我感兴趣的物体

把视觉和文字的语义特征联系到一起,因此迁移效果强,语义分类效果好

image.png

即使domain变化巨大,分类效果还是很好

效果:

  • 图像生成
    image.png
    image.png

  • 物体检测
    摆脱基础类限制,可以得到更丰富的语义信息
    image.png

  • ocr:
    image.png

摘要

最先进的计算机视觉系统经过训练可以预测一组固定的预定对象类别。这种受限的监督形式限制了它们的通用性和可用性,因为需要额外的标记数据来指定任何其他视觉概念。直接从原始文本中学习图像是一种很有前途的替代方案,它利用了更广泛的监督来源。我们证明,预测哪个标题与哪个图像对应的简单预训练任务是一种高效且可扩展的方法,可以在从互联网收集的 4 亿对(图像、文本)数据集上从头开始学习 SOTA 图像表示。预训练后,使用自然语言来引用学习的视觉概念(或描述新的视觉概念),从而实现模型零样本传输到下游任务。 我们通过对 30 多个不同的现有计算机视觉数据集进行基准测试来研究这种方法的性能,涵盖 OCR、视频中的动作识别、地理定位和许多类型的细粒度对象分类等任务。该模型可以轻松地迁移到大多数任务,并且通常可以与完全监督的基线竞争,而无需任何数据集特定的训练。 例如,我们在 ImageNet 零样本上匹配原始 ResNet-50 的准确性,而无需使用其所训练的 128 万个训练样本中的任何一个。 我们在 https://github.com/OpenAI/CLIP 发布了代码和预训练模型权重。

Intro

直接从原始文本中学习的预训练方法在过去几年中彻底改变了 NLP。自回归和掩码语言建模任务无关的目标在计算、模型容量和数据方面已经扩展了多个数量级,稳步提高了能力。 “文本到文本”作为标准化输入输出接口的发展,使任务无关架构能够零样本传输到下游数据集消除了对专门输出头或数据集特定定制的需要。 像 GPT-3这样的旗舰系统现在在许多具有定制模型的任务中具有竞争力,同时几乎不需要或不需要特定于数据集的训练数据。

不需要花费心思设计针对某一具体任务的输出头/针对数据集的特殊处理

大规模没有标注的数据反而比有标注的数据更好

从NLP扩展到CV

自监督训练

  • 虽然作为概念证明令人兴奋,但使用自然语言监督进行图像表示学习仍然很少。这可能是因为在普通基准测试中演示的表现比替代方法低得多。

    • 例如,Li等人(2017)在零样本学习设置下在ImageNet上仅达到11.5%的准确率。这远低于目前技术水平的88.4%的准确率(Xie et al., 2020)。它甚至低于经典计算机视觉方法的50%准确率(Deng et al., 2012)。
  • 相反,对薄弱监管的范围更窄但目标更明确的使用改善了表现。

    • Mahajan等人(2018)表明,预测Instagram图像上与imagenet相关的标签是一项有效的预训练任务。当对ImageNet进行微调时,这些预训练模型的准确率提高了5%以上,并改善了当时的整体技术水平。Kolesnikov等人(2019)和Dosovitskiy等人(2020)也通过预训练模型来预测带有噪声标记的JFT-300M数据集的类别,在更广泛的迁移基准集上取得了巨大的收益。
  • 这条工作路线代表了当前实用主义的中间立场,即从有限数量的监督“黄金标签”和从几乎无限数量的原始文本中学习之间进行学习。

    • 然而,这并非没有妥协。两者的作品都经过精心设计,并在工艺限制下,他们的监督类分别为1000类和18291类。自然语言能够通过其通用性来表达并监督更广泛的视觉概念。这两种方法还使用静态softmax分类器来执行预测,并且缺乏动态输出的机制。这严重削弱了它们的灵活性,限制了它们的“零样本学习”能力。
  • 规模很重要

    • 弱监督学习:大规模数据集
    • 我们创建了一个包含4亿对(图像,文本)的新数据集,并证明了从头开始训练的ConVIRT的简化版本,我们称之为CLIP,用于对比语言-图像预训练
    • 我们通过训练一系列八个模型来研究CLIP的可扩展性,这些模型跨越了近2个数量级的计算,并观察到迁移表现是计算的平滑可预测函数

ConVIRT简化版本
迁移学习效果与模型大小有关

linear-probe
更加稳健

Approch

我们方法的核心思想是从自然语言的监督中学习感知

这一行的共同之处并不是使用特定方法的任何细节,而是对自然语言作为训练信号的欣赏。所有这些方法都是从自然语言监督视觉中学习的。尽管早期的工作在使用主题模型和n-gram表示时与自然语言的复杂性作斗争,但深度上下文表示学习的改进表明,我们现在有了有效利用这种丰富的监督来源的工具。

  • 潜在的优势
    • 与用于图像分类的标准众包标签相比,扩展自然语言监督要容易得多,因为它不要求注释采用经典的“机器学习兼容格式”,例如标准的1-of-N多数投票“金标”。相反,研究自然语言的方法可以被动地从互联网上大量文本的监督中学习。
    • 与大多数无监督或自监督学习方法相比,从自然语言中学习也有重要的优势,因为它不仅“只是”学习表征,而且还将表征与语言联系起来,从而实现灵活的零样本学习迁移。在接下来的小节中,我们将详细介绍我们选定的具体方法。

需要足够大的数据集和足够好的标注WIT(WebImageText)

预训练方法

我们发现训练效率是成功扩展自然语言监督的关键,我们根据这个指标选择了最终的预训练方法。

我们最初的方法类似于VirTex,从头开始联合训练图像CNN文本转换器来预测图像的标题。然而,我们在有效扩展这种方法时遇到了困难。在图2中,我们展示了一个6300万个参数的转换器语言模型,它已经使用了两倍的ResNet-50图像编码器的计算,它学习识别ImageNet类别的速度比预测相同文本的单词袋编码的简单基线慢三倍。

这两种方法有一个关键的相似之处。他们试图预测每幅图片的准确文字。这是一项艰巨的任务,因为与图像同时出现的描述、评论和相关文本种类繁多。

  • 生成变成了判别

最近在图像对比表征学习方面的工作发现,对比目标比其等效的预测目标可以更好地学习表征(Tian et al., 2019)。其他研究发现,尽管图像的生成模型可以学习高质量的图像表示,但与具有相同表现的对比模型相比,它们需要超过一个数量级的计算量(Chen等人,2020a)。注意到这些发现,我们探索了训练一个系统来解决潜在的更容易的代理任务,即只预测哪个文本作为一个整体与哪个图像配对,而不是该文本的确切单词。从相同的词袋编码基线开始,我们将预测目标替换为图2中的对比目标,并观察到零样本学习转移到ImageNet的效率进一步提高了4倍。

  • 蓝线:逐字逐句预测
  • 黄线:预测抽成的特征
  • 绿色:判断是不是图片文本对

image.png

训练

image.png

单模态正样本变成多模态正样本

编码器均不训练
使用线性投射层
随机裁剪

视觉部分:ResNet/Vision Transformer
文本部分:Transformer

混精度训练,省内存,提高训练速度
相似度在多GPU计算

在更大尺寸图片上fine-tune

实验

零样本迁移

在计算机视觉中,零样本学习通常是指对图像分类中未见过的物体类别进行泛化的研究(Lampert et al., 2009)。相反,我们在更广泛的意义上使用这个术语,并研究对未见过的数据集的泛化。我们将其作为执行看不见的任务的代理,正如Larochelle等人(2008)在零数据学习论文中所期望的那样。
虽然无监督学习领域的许多研究都集中在机器学习系统的表示学习能力上,但我们鼓励研究零样本转移作为衡量机器学习系统任务学习能力的一种方法。在此视图中,数据集评估特定分布上任务的表现。
然而,许多流行的计算机视觉数据集是由研究界创建的,主要是作为指导通用图像分类方法开发的基准,而不是衡量特定任务的表现。虽然可以合理地说SVHN数据集测量了谷歌街景照片分布上的街道号码转录任务,但尚不清楚CIFAR-10数据集测量的是什么“真正的”任务。然而,很明显,CIFAR-10是从TinyImages中提取的分布(Torralba et al., 2008)。在这类数据集上,零样本学习转移更多的是评价CLIP对分布转移和领域泛化的鲁棒性,而不是任务泛化。

prompt engineering and ensembling

大多数标准图像分类数据集都将命名或描述类别的信息视为事后的想法,这使得基于自然语言的零样本 转移成为可能。绝大多数数据集仅使用标签的数字id对图像进行注释,并包含将这些id映射回其英文名称的文件。有些数据集,如Flowers102和GTSRB,在其发布版本中似乎根本不包括这种映射,从而完全防止了零样本 传输对于许多数据集,我们观察到这些标签的选择可能有些随意,并且没有预料到与零样本 转移相关的问题,这依赖于任务描述以成功转移

一个常见的问题是一词多义。当类的名称是提供给CLIP的文本编码器的唯一信息时,由于缺乏上下文,它无法区分指的是哪个词义。在某些情况下,同一个单词的多个含义可能会作为不同的类别包含在同一个数据集中!这发生在ImageNet中,它包含建筑起重机和飞行起重机。另一个例子是在Oxford-IIIT Pet数据集的类别中发现的,从上下文来看,单词boxer显然指的是一种狗,但对于缺乏上下文的文本编码器来说,它很可能指的是一种运动员。

我们遇到的另一个问题是,在我们的预训练数据集中,与图像配对的文本只是一个单词的情况相对较少。通常文本是一个完整的句子,以某种方式描述图像。为了帮助弥合这种分布差距,我们发现使用提示模板“A photo of A {label}.”是一个很好的默认值,可以帮助指定文本是关于图像的内容(解决了歧义性的问题)。这通常比仅使用标签文本的基线提高表现。
例如,仅仅使用这个提示符就可以将ImageNet上的准确率提高1.3%。类似于围绕GPT3的“提示工程”讨论(Brown et al., 2020;Gao等人,2020),

  • 通过为每个任务定制提示文本,可以显著提高零样本表现。
    下面是一些不详尽的例子。我们在几个细粒度的图像分类数据集中发现,它有助于指定类别。例如,在Oxford-IIIT Pets中,使用”A photo of a {label}, a type of pet.” 来帮助提供背景,效果很好。同样,在Food101上指定一种食物类型和在FGVC飞机上指定一种飞机类型也有所帮助。对于OCR数据集,我们发现在要识别的文本或数字周围加上引号可以提高表现。最后,我们发现在卫星图像分类数据集中,它有助于指定图像是这种形式,我们使用“a satellite photo of a {label}.”的变体

我们还尝试了多个零样本分类器的集成,作为提高性能的另一种方法。 这些分类器是通过使用不同的上下文提示来计算的,例如“‘A photo of a big {label}”和“A photo of a small {label}”。 我们在嵌入空间而不是概率空间上构建集成。 这使我们能够缓存一组平均文本嵌入,以便在分摊到许多预测时,整体的计算成本与使用单个分类器的成本相同。

zero-shot

我们观察到许多生成的零样本分类器的集成可以可靠地提高性能并将其用于大多数数据集。 在 ImageNet 上,我们集成了 80 个不同的上下文提示,这比上面讨论的单个默认提示额外提高了 3.5% 的性能。 当综合考虑时,快速工程和集成可将 ImageNet 的准确性提高近 5%。 在图 4 中,我们直观地看到了即时工程和集成如何改变一组 CLIP 模型的性能,与直接嵌入类名的无上下文基线方法相比,如 Li 等人所做的那样。

image.png

对给物体分类的数据集,表现得会更好;但要是更难的数据集,会表现更差,因为难以用文本表示(需要特定语义知识)

few-shot

把图片的编码器冻结,做Linear Probe

image.png

每条曲线是二十个数据集的合并

特征学习

linear-probe: 把预训练的模型冻结,再训练一个线性分类头(最后一层)
fine-tune: 把整个网络放开,做端到端学习

选择了linear-probe: 为了测试迁移能力,防止下游任务污染编码器
更好反映预训练模型的好坏

越靠近左上角 trade-off做得越好

image.png

image.png

分布偏移也不掉点

不足

训练量逼近SOTA需要扩大1000倍
细分类效果差
抽象概念和更难的任务
OOD问题泛化差
数据利用不高效:自监督和伪标
数据没有清洗,可能有偏见
在部署多模式模型之前,人们需要仔细研究它们在给定背景和领域中的行为
鉴于其零样本功能,CLIP 为数据相对较少的任务提供了显着的优势。然而,对于许多按需监控任务(例如面部识别)来说,存在大型数据集和高性能监督模型。因此,CLIP 对此类用途的相对吸引力较低。 此外,CLIP 不适用于常见的监视相关任务,例如对象检测和语义分割。 这意味着当设计时考虑到这些用途的模型(例如 Detectron2(Wu 等人,2019))广泛使用时,它在某些监视任务中的用途有限

创新

打破固定种类收集的样式
推理模型更方便

主要的序列转导模型是基于复杂的循环或卷积神经网络,包括一个编码器和一个解码器。表现最好的模型还通过注意机制连接编码器和解码器。我们提出了一个新的简单的网络架构,Transformer,完全基于注意力机制,完全摒弃递归和卷积。在两个机器翻译任务上的实验表明,这些模型在质量上更优越,同时更具并行性,并且需要更少的训练时间。我们的模型在WMT 2014英语-德语翻译任务上实现了28.4 BLEU,比现有的最佳结果(包括集合)提高了2个BLEU以上。我们通过将Transformer成功地应用于具有大量和有限训练数据的英语选区解析,证明了它可以很好地推广到其他任务。

intro

  • 介绍主流序列建模和传导问题方法——基于RNN
  • RNN介绍与优缺点分析
    • 缺点:$h_{t}$ 时序生成,因此无法并行化;存在长时记忆问题;内存开销较大
  • Attention与RNN结合使用,将编码器传导到解码器
  • 提出Transformer,可以进行并行化
    image.png|424

相关工作 (background)

  • 卷积网络对长序列难以建模学习,但是有多个通道可以做不同的识别
  • 如果使用注意力机制就可以减少操作数
  • 自注意力机制
  • memory network

模型

编码器:将 $(x_{1},x_{2},\dots,x_{n})$ 编码成 $\mathbf{z}=(z_{1},\dots,z_{n})$ ,其中 $z_{t}$ 表示 $x_{t}$ 的向量表示
解码器:接收编码器输出,生成 $(y_{1},\dots,y_{m})$ 序列,其中解码器每次生成下一个符号时均使用之前生成的符号作为附加输入(自回归)

编码器

  • 多头自注意力层
  • 前馈网络(MLP)
  • 残差连接
  • 层归一化
  • $LayerNorm(x + Sublayer(x))$

每一层输出均为512,方便残差连接

LayerNorm

二维情况:
对每个样本做标准化
image.png|145
三维情况:
image.png|300

BatchNorm

二维情况:
计算同一个feature在一个batch的均值,方差,然后标准化。
对于测试,则将测试集上同一个feature的所有数据计算均值和方差

image.png|238

三维情况:
image.png|300

对比

LayerNorm:

  • 计算同一个样本均值方差,比较稳定

image.png|233

BatchNorm:

  • 当样本数量少(小批量)的时候,均值和方差会剧烈波动

image.png|325

解码器

  • 多头自注意力
  • 掩码注意力:当前的输出依赖当前与之前的输入,保证训练和预测一致

举个例子:
image.png|650
对于Decoder来说,第一个输入是一个[<Begin>]开始符,然后需要预测出I;然后第二个输入是[<Begin>, I],需要预测第二个…以此类推
Decoder 可以在训练的过程中使用 Teacher Forcing 并且并行化训练,即将正确的单词序列 (<Begin> I have a cat) 和对应输出 (I have a cat <end>) 传递到 Decoder。那么在预测第 i 个输出时,就要将第 i+1 之后的单词掩盖住,注意 Mask 操作是在 Self-Attention 的 Softmax 之前使用的。

注意力

注意函数可以描述为将查询和一组键值对映射到输出,其中查询query、键key、值value和输出output都是向量。输出是作为值的加权和计算的,其中分配给每个值的权重是由查询与相应键的兼容性(相似度)函数计算的。

随着q不一样,相似度不同,因此对应的$\alpha$权重也不一样

scaled dot-product attention

输入由维度 $d_{k}$ 的query和key以及维度 $d_{v}$ 的值组成。我们计算query与所有key的点积,然后每个都除以 $\sqrt{ d_{k} }$ ,并应用softmax求出权重。使用矩阵乘法可以得到下式:

$$
Attention(Q,K,V)=softmax\left( \frac{QK^T}{\sqrt{ d_{k} }} \right)V
$$

再将权重作用于value上得到输出
image.png|575

常见的注意力机制

  • 加性注意力:使用一个前馈网络来计算
  • 点乘注意力:同上

点乘需要除以 $\sqrt{ d_{k} }$ ,当 $d_{k}$ 较小的时候相似,但是如果较大时,向量的差距会变大,这样乘的时候梯度会变小,可能会跑不动

image.png

掩码

对于$Q_{t}$的计算,将$k_{t},\dots,k_{n}$这些不知道的值换成一个很大的负数,这样得到的权重就会趋近于0

多头注意力

这一块看李宏毅的更清楚

类似于多个卷积核的思想,想要学习不同的模式
多头注意允许模型在不同位置共同注意来自不同表示子空间的信息。对于单一注意力头,平均会抑制这一点
投影到低的维度,这样有更多的参数可以学习

$$
MultiHead(Q,K,V) = Concat(head_{1},\dots,head_{h})W^O
$$

其中

$$
head_{i}=Attention(QW^Q_{i},KW^K_{i},VW^V_{i})
$$

image.png

image.png

  • base:8个头
  • big:16个头

image.png|500

详细介绍

举个栗子:
image.png|500
有了这个矩阵后,需要计算Q、K、V:
image.png|300
随后需要将Q矩阵和K矩阵进行相乘,再过个softmax(和归一化),得到对应的注意力矩阵:
image.png|400
将$QK^T$注意力矩阵与$V$相乘,得到结果:
image.png|400

对于多头情况,只需要对同一个输入过不同的self-attention,然后把输出拼起来再过一次全连接即可:
image.png|375
image.png|375

注意力机制运用

Transformer以三种不同的方式使用多头注意:
• 在“编码器-解码器注意”层中,查询来自前一个解码器层,而记忆键和值来自编码器的输出。这允许解码器中的每个位置都参与输入序列中的所有位置。这模仿了序列到序列模型中典型的编码器-解码器注意机制。

• 编码器包含自注意力机制层。在自注意力机制层中,所有的键、值和查询都来自同一个地方,在这种情况下,是编码器中前一层的输出。编码器中的每个位置都可以处理编码器前一层中的所有位置。

image.png|177
三个输入分别表示 key, value, query,是由同一个句子经过编码后转化的向量矩阵,再经过权重矩阵得到

• 类似地,解码器中的自注意力机制层允许解码器中的每个位置关注解码器中的所有位置,直至并包括该位置。我们需要防止解码器中的向左信息流以保持自回归特性。我们通过屏蔽(设置为−∞) softmax输入中对应于非法连接的所有值来实现缩放点积注意力。

前馈网络

除了注意子层之外,编码器和解码器中的每一层都包含一个完全连接的前馈网络,该网络分别相同地应用于每个位置。这包括两个线性转换,中间有一个ReLU激活。

每个词就是一个position,使用MLP对每一个词作用一次
$$
FFN(x) = \max(0, xW_{1}+b_{1})W_{2}+b_{2}
$$
虽然线性变换在不同位置上是相同的,但它们在每一层之间使用不同的参数。另一种描述它的方式是两个核大小为1的卷积。输入和输出的维数$d_{model} = 512$,内层的维数$d_{ff} = 2048$。

意思是先把512投影成2048,然后由于存在残差连接,故需要将2048再投影成512

attention:抓取整个序列的内容进行汇聚
MLP:对汇聚的信息进行分别处理

对比Transformer和RNN
image.png|525

Embedding

将token映射成向量

与其他序列转导模型类似,我们使用learned embeddings将输入token和输出token转换为维度 $d_{model}$ 的向量。
我们还使用usual learned 线性变换和softmax函数将解码器输出转换为预测的下一个token概率。在我们的模型中,我们在两个嵌入层和pre-softmax线性变换之间共享相同的权重矩阵。在嵌入层中,我们将这些权重乘以 $\sqrt{d_{model}}$ 进行归一化。

Position Encoding

对于时序顺序,attention不会处理,因此需要用位置编码,在输入里面加入时序信息

在原论文中采用正弦和余弦函数进行位置编码,任何一个值,可以用长向量来表示,然后再和原本的向量相加,于是就将时序信息加入到数据中。向量相加,维度不变,长度方向改变

$$
PE_{(pos,2i)}=\sin (pos/(10000^{2i/dmodel}))
$$

$$
PE_{(pos,2i+1)}=\cos (pos/(10000^{2i/dmodel}))
$$

Why Self-Attention

image.png

一个是每层的总计算复杂度。另一个是可以并行化的计算量,通过所需的最小顺序操作数来衡量。
第三个是网络中远程依赖关系之间的路径长度。学习远程依赖关系是许多序列转导任务中的关键挑战。影响学习这种依赖关系能力的一个关键因素是网络中向前和向后信号必须经过的路径长度。输入和输出序列中任意位置组合之间的路径越短,学习远程依赖关系[12]就越容易。因此,我们还比较了由不同层类型组成的网络中任意两个输入和输出位置之间的最大路径长度

实验

我们在标准的WMT 2014英语-德语数据集上进行训练,该数据集由大约450万句对组成。句子使用字节对编码进行编码,具有大约37000个标记的共享源-目标词汇表。对于英语-法语,我们使用了更大的WMT 2014英语-法语数据集,该数据集由36M个句子组成,并将标记拆分为32000个单词块的词汇。句子对按近似序列长度进行批处理。每个训练批包含一组句子对,其中包含大约25000个源标记和25000个目标标记。

image.png

我们将Dropout应用于每个子层的输出,然后将其添加到子层输入并归一化。此外,我们将dropout应用于编码器和解码器堆栈中的嵌入和位置编码之和。对于基本模型,我们使用$P_{drop} = 0.1$的速率。

在训练过程中,我们使用值为$ϵ_{ls} = 0.1$的标签平滑。这损害了困惑,因为模型学会了更不确定,但提高了准确性和BLEU分数

Transformer 是一个基于自注意力的序列到序列模型,与基于循环神经网络的序列到序列模型不同,其可以能够并行计算。

序列到序列模型

序列到序列模型输入和输出都是一个序列,输入与输出序列长度之间的关系有两种情况。第一种情况下,输入跟输出的长度一样;第二种情况下,机器决定输出的长度。

image.png|525

  • 语音识别:输入是声音信号,输出是语音识别的结果,即输入的这段声音信号所对应的文字。我们用圆圈来代表文字,比如每个圆圈代表中文里面的一个方块字。输入跟输出的长度有一些关系,但没有绝对的关系,输入的声音信号的长度是 T,并无法根据 T 得到输出的长度 N。其实可以由机器自己决定输出的长度,由机器去听这段声音信号的内容,决定输出的语音识别结果。
  • 机器翻译:机器输入一个语言的句子,输出另外一个语言的句子。输入句子的长度是N,输出句子的长度是 N′。输入“机器学习”四个字,输出是两个英语的词汇:“machine learning”,N 跟 N′之间的关系由机器决定。
  • 语音翻译:我们对机器说一句话,比如“machine learning”,机器直接把听到的英语的声音信号翻译成中文

[!question] Q: 既然把语音识别系统跟机器翻译系统接起来就能达到语音翻译的效果,那么为什么要做语音翻译?
世界上很多语言是没有文字的,无法做语音识别。因此需要对这些语言做语音翻译,直接把它翻译成文字。

语音合成

输入文字,输出声音信号

以闽南语的语音合成为例,其使用的模型还是分成两阶,首先模型会先把白话文的文字转成闽南语的拼音,再把闽南语的拼音转成声音信号。

聊天机器人

image.png|600

问答任务

很多自然语言处理的任务都可以想成是问答(Question Answering,QA)的任务

  • 翻译。机器读的文章是一个英语句子,问题是这个句子的德文翻译是什么?输出的答案就是德文。
  • 自动做摘要:给机器读一篇长的文章,让它把长的文章的重点找出来,即给机器一段文字,问题是这段文字的摘要是什么。
  • 情感分析:机器要自动判断一个句子是正面的还是负面的。如果把情感分析看成是问答的问题,问题是给定句子是正面还是负面的,希望机器给出答案

因此各式各样的自然语言处理的问题往往都可以看作是问答的问题,而问答的问题可以用序列到序列模型来解。序列到序列模型的输入是一篇文章和一个问题,输出就是问题的答案。问题加文章合起来是一段很长的文字,答案是一段文字。只要是输入一个序列,输出是一个序列,序列到序列模型就可以解

句法分析

很多问题都可以用序列到序列模型来解,以句法分析(syntactic parsing)为例,如图所示,给机器一段文字:比如“deep learning is very powerful”,机器要产生一个句法的分析树,即句法树(syntactic tree)。

image.png|500

在句法分析的任务中,输入是一段文字,输出是一个树状的结构,而一个树状的结构可以看成一个序列,该序列代表了这个树的结构

image.png|325

多标签分类

多标签分类(multi-label classification)任务也可以用序列到序列模型。多类的分类跟多标签的分类是不一样的。多分类问题(multi-class classification)是指分类的类别数大于 2。而多标签分类是指同一个东西可以属于多个类。

多标签分类问题不能直接把它当作一个多分类问题的问题来解。比如把这些文章丢到一个分类器里面,本来分类器只会输出分数最高的答案,如果直接取一个阈值(threshold),只输出分数最高的前三名。这种方法是不可行的,因为每篇文章对应的类别的数量根本不一样。因此需要用序列到序列模型来做

image.png|500

Transformer结构

一般的序列到序列模型会分成编码器和解码器,如图所示。编码器负责处理输入的序列,再把处理好的结果“丢”给解码器,由解码器决定要输出的序列。

image.png|350

Transformer编码器

编码器输入一排向量,输出另外一排向量
Transformer的编码器使用的是自注意力,输入一排向量,输出另外一个同样长度的向量。

image.png|250

image.png|500

编码器里面会分成很多的块(block),每一个块都是输入一排向量,输出一排向量。输入一排向量到第一个块,第一个块输出另外一排向量,以此类推,最后一个块会输出最终的向量序列。

编码器的每个块并不是神经网络的一层,每个块的结构如图所示,在每个块里面,输入一排向量后做自注意力,考虑整个序列的信息,输出另外一排向量。接下来这排向量会“丢”到全连接网络网络里面,输出另外一排向量,这一排向量就是块的输出

image.png|400

Transformer 的编码器的每个块并不是神经网络的一层,每个块的结构如图 7.11 所示,在每个块里面,输入一排向量后做自注意力,考虑整个序列的信息,输出另外一排向量。接下来这排向量会“丢”到全连接网络网络里面,输出另外一排向量,这一排向量就是块的输出

Transformer 里面加入了残差连接(residual connection)的设计,如图所示,最左边的向量 b 输入到自注意力层后得到向量 a,输出向量 a 加上其输入向量 b 得到新的输出。得到残差的结果以后,再做层归一化(layer normalization)。归一化后再输入全连接网络,残差连接后再得到新的输出。最后再进行一次层归一化,得到编码块里面一个块的输出。

image.png|300

[!info] 层归一化和批量归一化

  • 层归一化不需要考虑批量的信息,输入一个向量,输出一个向量
    • 它是对同一个特征、同一个样本里面的不同维度计算均值和标准差,接着做归一化
    • $x_{i}’=\frac{x_{i}-m}{\sigma}$
    • 就是对一个句子进行归一化
  • 批量标准化是对不同样本、不同特征同一个维度计算均值和标准差
    • 就是对一个batch句子的同一个位置做归一化

下图给出了 Transformer 的编码器结构,其中 N× 表示重复 N 次。首先,在输入的地方需要加上位置编码。如果只用自注意力,没有位置的信息,所以需要加上位置信息。多头自注意力就是自注意力的块。经过自注意力后,还要加上残差连接和层归一化。接下来还要经过全连接的前馈神经网络(这一块的目的是为了提高模型的非线性,通常维度为隐藏层维度的4倍),接着再做一次残差连接和层归一化,这才是一个块的输出,这个块会重复 N 次。

[!question] 为什么有位置编码?
Transformer不采用RNN的结构,而是使用全局信息,不能利用单词的顺序信息,而这部分信息对于NLP来说非常重要。所以 Transformer 中使用位置 Embedding 保存单词在序列中的相对或绝对位置。
计算方式:
$$
PE_{(pos,2i)}=\sin\left( \frac{pos}{10000^{2i/d}} \right)
$$

$$
PE_{(pos,2i+1)}=\cos\left( \frac{pos}{10000^{2i/d}} \right)
$$

这样计算的好处是不需要训练,只需要计算,而且容易计算相对位置,对于固定长度的间距k,PE(pos + k)可以通过正余弦展开计算

image.png|525

image.png|300

解码器

以语音识别为例,输入一段声音,输出一串文字。

要让解码器产生输出,首先要先给它一个代表开始的特殊符号 <BOS>,即 Begin Of Sequence,这是一个特殊的词元(token)。

在词表(vocabulary)里面,在本来解码器可能产生的文字里面多加一个特殊的符号<BOS>。在机器学习里面,假设要处理自然语言处理的问题,每一个词元都可以用一个独热的向量来表示。独热向量其中一维是 1,其他都是 0,所以 <BOS> 也是用独热向量来表示,其中一维是 1,其他是 0。

接下来解码器会“吐”出一个向量,该向量的长度跟词表的大小是一样的(eg.中文里常用汉字的数量)。在产生这个向量之前,跟做分类一样,通常会先进行一个 softmax 操作。这个向量里面的分数是一个分布,该向量里面的值全部加起来,总和是 1。这个向量会给每一个中文字一个分,分数最高的中文字就是最终的输出。“机”的分数最高,所以“机”就当做是解码器的第一个输出。

image.png|500

接下来把“机”当成解码器新的输入。根据两个输入:特殊符号 <BOS>和“机”,解码器输出一个向量,向量里面会给出每一个中文字的分数。因此,每次解码器的输入是其前一个时间点的输出,反复输入进行迭代。

但是也可能会输入错误,使得一步错步步错的误差传播问题

image.png|475

会发现除了中间多了多头自注意力和残差连接部分,其余部分实际上encoder和decoder是一样的,解码器最后会执行一个softmax,以使得其输出变为概率

此外还有个掩码多头自注意力,原来的自注意力输入一排向量,输出另外一排向量,这一排中每个向量都要看过完整的输入以后才做决定。掩蔽自注意力的不
同点是不能再看右边的部分,如图 7.19 所示,产生 $b^1$ 的时候,只能考虑 $a^1$ 的信息,不能再考虑 $a^2、a^3、a^4$。

是因为防止后面的信息对前面产生影响,decoder的输出是先有$a^1$才有$a^2$,再有后面的$a^3$和$a^4$。所以实际上有$a^2$想要计算$b^2$的时候,$a^3$和$a^4$是没有的,所以无法考虑。解码器的输出是一个一个产生的,所以只能考虑其左边的东西,没有办法考虑其右边的东西

image.png|500

[!question] 实际应用中输入跟输出长度的关系是非常复杂的,我们无法从输入序列的长度知道输出序列的长度,因此解码器必须决定输出的序列的长度

要让解码器停止运作,需要特别准备一个特别的符号 **<EOS>**。产生完“习”以后,再把“习”当作解码器的输入以后,解码器就要能够输出 <EOS>,解码器看到编码器输出的嵌入、<BOS>、“机”、“器”、“学”、“习”以后,其产生出来的向量里面 <EOS> 的概率必须是最大的,于是输出 <EOS>

image.png|600

Q、K、V模式:
image.png|525

非自回归解码器

自回归的模型是先输入 <BOS>,输出 $w_1$,再把 $w_1$ 当做输入,再输出 $w_2$,直到输出 <EOS> 为止。
假设产生是中文的句子,非自回归不是一次产生一个字,它是一次把整个句子都产生出来。
非自回归的解码器可能“吃”的是一整排的 <BOS> 词元,一次产生产生一排词元。
比如输入 4 个 <BOS>的词元到非自回归的解码器,它就产生 4 个中文的字。因为输出的长度是未知的,所以当做非自回归解码器输入的 <BOS> 的数量也是未知的,因此有如下两个做法:

  • 用分类器解决:分类器接收编码器输入,输出一个数字,数字代表解码器的输出长度。
  • 给编码器输入一组<BOS>词元,规定词元数量。

优点:

  • 平行化,计算迅速
  • 能够控制输出长度 eg.语音合成领域可以通过调整长度控制输出速度

编码器-解码器注意力

image.png|500

image.png|450

编码器输入一排向量,输出一排向量 $a^1,a^2,a^3$ ,解码器先读入<BOS>,经过 mask attention 得到一个向量,然后将这个向量乘上一个矩阵,得到查询$q$,$a^1,a^2,a^3$也都产生键 $k^1,k^2,k^3$ 。把 $q$ 和 $k^1,k^2,k^3$ 计算注意力分数,得到 $\alpha_{1}, \alpha_{2},\alpha_{3}$,做softmax得到 $\alpha_{1}’,\alpha_{2}’,\alpha_{3}’$。于是可以计算 $v$ :

$$
v=\alpha_{1}’ \times v^1 + \alpha_{2}’ \times v^2 + \alpha_{3}’ \times v^3
$$

$v$ 接下来会“丢”给全连接网络,这个步骤 $q$ 来自于解码器,$k$ 跟 $v$ 来自于编码器,该步骤就叫做编码器-解码器注意力,所以解码器就是凭借着产生一个 $q$,去编码器这边抽取信息出来,当做接下来的解码器的全连接网络的输入。

训练过程

image.png|450

计算标准答案(ground truth)与分布之间的交叉熵,希望该交叉熵的值越低越好
每一次解码器在产生一个中文字的时候做了一次类似分类的问题。假设中文字有四千个,就是做有四千个类别的分类的问题。

如图所示,实际训练的时候,输出应该是“机器学习”。解码器第一次的输出、第二次的输出、第三次的输出、第四次输出应该分别就是“机”、“器”、“学”、“习”这四个中文字的独热向量,输出跟这四个字的独热向量越接近越好。

在训练的时候,每一个输出跟其对应的正确答案都有一个交叉熵。图中做了四次分类问题,希望这些分类的问题交叉熵总和越小越好。

注意解码器最后还需要输出一个<EOS>

教师强制:解码器训练时,在输入的时候给它正确的答案

训练技巧

复制机制(copy mechanism)

对于很多任务,解码器没有必要自己创造输出,可以从输入的东西里复制一些东西

对话服务:从使用者的词汇中复制一些内容
摘要任务:从文章里直接复制一些信息

Pointer Network 和 Copy Network

引导注意力

有时候训练结果会有异常

强迫attention的样貌,例如强制学习注意力要从左到右
image.png

束搜索

假设解码器就只能产生两个字 A 和 B,假如世界上只有两个字 A 跟 B,即词表 $V = {A, B}$。对解码器而言,每一次在第一个时间步(time step),它在 A、B 里面决定一个。比如解码器可能选 B 当作输入,再从 A、B 中选一个。在上文中,每一次解码器都是选分数最高的那一个。假设 A 的分数是 $0.6$,B 的分数是 $0.4$,解码器的第一次就会输出 A。接下来假设 B 的分数为 $0.6$,A 的分数为 $0.4$,解码器就会输出 B。

以此类推,每次找分数最高的词元作为输出的方法称为贪心搜索(greedy search),其也被称为贪心解码

但贪心不一定最优。第一步可以先稍微舍弃一点东西,第一步虽然 B 是 0.4,但先选 B。选了 B,第二步时 B 的可能性就大增就变成 0.9。到第三步时,B 的可能性也是0.9。绿色路径虽然第一步选了一个较差的输出,但是接下来的结果是好的。

image.png

没办法进行暴搜。束搜索是用较有效的方法找一个近似解.

如果任务答案非常明确,这种任务束搜索比较有用;但是如果需要有创造力,束搜索没那么好

加入噪声

语音合成中,模型的训练和测试过程均需要加入噪声,这样能提高结果

使用强化学习训练

评估标准是BLEU,但是训练的时候是最小化交叉熵,两者存在一定区别,最小化交叉熵不一定可以最大化BLEU分数
但如果要计算BLEU分数,损失无法做微分

因此可以用强化学习进行训练,把损失函数作为强化学习的奖励,把解码器作为智能体

计划采样

测试的时候,解码器看到的是自己的输出,因此它会看到一些错误的东西。但是在训练的时候,解码器看到的是完全正确的,这种不一致的现象叫做曝光偏差(exposure bias)。

假设解码器在训练的时候永远只看过正确的东西,在测试的时候,只要有一个错,就会一步错步步错。因为解码器从来没有看过错的东西,它看到错的东西会非常的惊奇,接下来它产生的结果可能都会错掉。

有一个可以的思考的方向是:给解码器的输入加一些错误的东西,不要给解码器都是正确的答案,偶尔给它一些错的东西,它反而会学得更好,这一技巧称为计划采样(scheduled sampling)

image.png|375

0%