Why we need encoder
我们知道,对于计算机来说,图像、视频、文字等信号都是用离散的数字去表示的。以图片为例,对于神经网络而言,当然可以直接把像素直接作为输入,并基于这些像素去做各种的下游任务,例如图像分类、文生图片等。那么,encoder的意义到底是什么?
以我个人的观点来看,encoder的作用实际上是去除数据中的噪声,压缩数据的大小,提取数据之间的共性,其最终目的是获得一个更高效的、更适合下游任务的表示来替代原始的数据。例如,假设我们现在正在想知道一个图片所表示的文本(CLIP),数据集中有一张猫站在草地上的图片和一张猫站在地板上的图片,而对应的文本描述都是 a photo of a cat,我们显然希望能够将这两张图片用类似的特征去表示[1],这时我们就会使用encoder将原始的差异很大图片编码成两个类似的特征,并且,用来表示这张图所需的数字要小得多。例如一张512*512*3的图片,可能最终只需要2048维,也就是2048个数字去完全表示。
Auto Encoder
Auto Encoder,即自编码器,其是用在没有标签的数据集上的,也就是说是一种无监督的算法。相对于上文中提到的CLIP编码器,这种编码器没有任何的监督,其目的在于去除噪声、压缩数据,即用更少的数据去保留原数据更多的信息。它的作用可以类比于一个压缩软件,可以把数据压缩成另一种格式(类比.zip文件),并且还能再还原出原数据。
但是与压缩软件固定的算法不同,神经网络通常无法用明确的算法去定义原数据是怎么被转换为压缩后的数据的,那么我们如何知道压缩后的数据能不能表示原数据?直观的想法是,如果我们能从压缩后的数据还原出原始数据(类似解压缩操作),那么这个被压缩了的数据就一定蕴含了原数据的所有信息。
因此Auto Encoder通常包括一个encoder(负责压缩数据)和一个decoder(负责解压数据),其算法也可以描述为:
1 | #x: 原始数据 |
在训练的过程中,需要最小化原始数据x和解压缩后的数据y的区别,通常可以采用均方误差损失去进行优化。当训练完成后,decoder应当能够从encoder压缩后的数据还原出原始的数据,这时便可以使用encoder对原始数据进行编码,于是完成了数据的降维工作。
Denoising Auto Encoder
Auto Encoder看起来已经效果很棒了,我们似乎可以用这种方式去压缩并解压绝大部分的数据。然而,在自编码器的上空,“却依然笼罩着两朵乌云”。其一,如果给单独decoder一个数据,通常无法生成很好的结果。其二,有可能会出现Auto Encoder直接把输入复制到输出的情况,这种模型虽然损失函数是最小的,但却毫无意义。
首先,当我们有了decoder之后,我们会自然的想这样一件事情:能否把decoder单独使用,用来生成新的数据?然而,我们会发现即使给解码器一个满足压缩数据分布的数据,decoder依然无法生成合理的数据,而通常会生成噪声。
这是由于在auto encoder中,自编码器的输入输出是一种确定的关系,假设我们最后编码的特征只有1维,也就是一个数字来表示原数据。如果encoder把图片A编码为10,把图片B编码为12,那么10和12都建立了与原始数据的映射,而数字11却并没有建立任何映射。假如把decoder视为一个函数,那么11并不在函数的定义域内,因此decoder(11)的值也是未定义的,因此通常也是随机的噪声。
那么既然其原因在于decoder的定义域比较狭窄,那么问题就变成了如何拓展decoder的定义域。而decoder的定义域则是由encoder在训练的过程中确定的,decoder只会建立encoder的输出到原始数据的映射,也就是说,decoder的定义域便是encoder的值域。那么问题则转变为了如何拓宽encoder的值域,也就是encoder的编码范围。
依然考虑这个例子,为什么encoder的定义域是离散的,为什么10和12之间的数字均不是有效的特征?这是因为原始数据就是离散的表示,而encoder中每一步的操作又都是确定的,因此encoder的输出也自然而然的是离散的。这就导致了在这些离散点之间的区域是无效的。因此,为了扩展encoder的编码范围,而不是让encoder仅仅是将输入数据固定的映射到某些确定的特征上,我们可以把原数据变成连续的数据,而对于计算机来说,就是在原始数据上以某种概率分布添加噪声,这样每次输入的数据都是不同的,而强迫网络学到更深层次的信息。
以我个人的观点来看,加噪声的方式可以看做假定原数据遵循着某种概率分布(所谓的共性),加噪声类似于在这个概率分布上每次进行采样不同的值,从而迫使网络能够真正学习到数据背后的分布规律,从而所有满足这种分布的数据,都可以用encoder来编码表示,以此来增强网络的泛化性。
Variational Autoencoder
Variational Autoencoder, 即变分自编码器,更为彻底的解决了上文所提到的Auto Encoder的两个问题[4]。相比于上文中所提到的加噪声的思想,VAE的想法则更为激进。既然问题出现在压缩之后的数据不够连续,那么干脆直接由encoder确定一个概率分布来表示压缩后的数据,而decoder则需要从这个概率分布当中进行采样作为输入,进而解码出数据。同时,这个分布应该是与encoder无关的,一个先验的分布。如此一来,在训练完成之后,就不再需要encoder,只要从这个概率分布中随机的进行采样,那么decoder都能给出一个合理的,数据中没有的结果,因此与其他Auto Encoder不同,VAE可以被称为是一个真正的生成模型。
既然要确定一个概率分布,那么首先必须假定压缩后的特征满足某种概率分布,从而才能通过学习其参数,确定一个具体的概率分布。这是一种典型的贝叶斯学派的思想,因此论文名字也叫做Auto-Encoding Variational Bayes。
论文则选择了正态分布来表示数据,因此Encoder需要给出的就是正态分布的均值
实际上,通常Encoder会为一个图像预测多个正态分布,显然一张图片不会遵循多种分布,但由于我们这里的概率分布并不是通过数学公式计算出来的,而是由Encoder推断出来的,那么很有可能是不准确的。因此我们允许Encoder给出多种可能的答案,对于这些正态分布,vae都会进行一次采样,再交给decoder进行输出。
vae的前向过程很好理解,encoder提取出正态分布,decoder从正态分布中采样进行生成。但是问题在于,我们如何构造损失函数?注意到decoder的输入是和原始图像满足同一个概率分布的随机值,其输出也并不一定应该是原始图像,那么直接采用和Auto Encoder一样的重建损失函数显然是不合理的。
vae的优化
这是一些数学部分
本质上,参数为
我们的目标是:
要让encoder的输出去满足我们的的先验概率分布。
要让decoder能够从encoder生成的概率分布中生成出符合原数据分布的数据,也就是让decoder能够学习到分布
这样,训练之后我们任意给出一个满足先验概率分布
首先考虑第一个目标,根据贝叶斯公式,我们有
假设这个近似的概率分布为
到这里,已经可以优化我们的第一个目标,即让encoder的输出贴近一个正态分布,还剩下第二个目标:要让decoder学习从正态分布到原数据的分布。
我们可以用最大似然估计去估算decoder的参数,也就是说,一个合适的参数
由此我们的损失函数可以写为
这其中第一项是为了优化目标1,第二项则是为了优化目标2。
这里值得重新回头再看,既然我们最终的目标是让decoder能够生成符合原数据分布的数据,为什么不直接使用最大似然,也就是上述损失函数的第二项去完成优化,这样岂不是更简单?
需要注意的是,VAE的目标已经超越了拓展encoder的编码范围,而变成了得到一个生成模型。如果说之前的Auto Encoder的decoder只是为了encoder服务的话,那么在vae这里,便是encoder为了decoder而服务。从前由encoder决定特征分布的范围,decoder需要用encoder给出的特征重建。而现在则是decoder告诉encoder:我需要你将数据用正态分布表示,而我将从正态分布中采样,生成新的数据。
我们之前一直假定了encoder生成的特征是满足正态分布的,而损失函数的第一项的意义则是约束encoder的输出必须贴近正态分布(实际上给出的先验q(z)为一个标准正态分布),只有这样才真正做到了获得一个与encoder无关的特征分布,从而可以将decoder单独拿出来,作为生成器从正态分布中生成新的数据。原论文描述如下:
那么现在可以来真正的计算损失函数:
对于第一项KL散度,由于我们已经假设,
剩下唯一的问题便是采样该如何实现了,显然,对于一个正态分布
References
Learning Transferable Visual Models From Natural Language Supervision
Deconstructing Denoising Diffusion Models for Self-Supervised Learning
PS:
文中加粗表示的以我个人的观点来看意思是我本人是这样理解,且并没有特意去查专业的论文来佐证,有可能同样的观点已经被提出并证明,但我并没有看见,也有可能这种观点是错误的,那就是本人的水平问题了,欢迎指正。