自编码器(autoencoder, AE)是一类在半监督学习和非监督学习中使用的网络,其功能是通过将输入信息作为学习目标,对输入信息进行表征学习,通常用于降维与异常检测。本文主要是学习传统的自编码器 AE 及其变形、和变分自编码器 VAE(Variational AE)。

自编码器

自编码器是一种无监督学习模型,利用神经网络进行表征学习将原始输入进行知识压缩,网络架构如下图所示:

模型由输入层、隐藏层及输出层构成,输出层神经元数量与输入层相同:

  • 编码器(encoder)可以表示为:

    h=f(W1Tx+a)h=f\left(W_{1}^{T} x+a\right)

  • 解码器(decoder)可以表示为:

    x^=g(W2Th+b)\hat{x}=g(W_{2}^{T} h+b)

模型相当于将原始输入进行重构,因此训练的目标是是重构后的x^\hat{x} 和原始输入xx 间的误差较小。

  • 当输入为二值时目标函数为交叉熵函数:

    L(W1,W2,a,b)=k=1n(xklogx^k+(1xk)log(1x^k)\mathcal{L}\left(W_{1}, W_{2}, a, b\right)=-\sum_{k=1}^{n}\left(x_{k} \log \hat{x}_{k}+\left(1-x_{k}\right) \log \left(1-\hat{x}_{k}\right)\right.

  • 当输入是任意实数时目标函数为均方误差:

    L(W1,W2,a,b)=12k=1nxkx^k2\mathcal{L}\left(W_{1}, W_{2}, a, b\right)=\frac{1}{2} \sum_{k=1}^{n}\left\|x_{k}-\hat{x}_{k}\right\|^{2}

自编码器结构简单但是仅对特定数据有效,模型迁移效果较差而且编码过程中会损失信息,主要用于数据降噪以及数据降维。

深度自编码器

通过增加隐藏层数量,进行多层次的抽象学习,这时可采用**“逐层预训练+微调”**来训练网络。

逐层预训练是指通过AE来训练深度神经网络的每一层参数。具体步骤如下:

  1. 对第一个隐藏层建立AE,进行无监督训练后,删除输出层即相应参数。
  2. 将第一个隐藏层输出作为输入,添加第二个隐藏层,同样构建AE并实施相同训练。
  3. 重复步骤2,直至完成所有隐藏层训练。

微调是指预训练之后还需要对网络进行监督学习训练。

稀疏自编码器

稀疏编码器与自编码器结构基本一致,区别在于隐藏层向量稀疏,即尽可能多的零元素,可以直观地理解为输入信息经稀疏编码后仅激活少量神经元。目标函数表示如下:

L(W1,W2,a,b)=12k=1nxkx^k2+λρ(h)\mathcal{L}\left(W_{1}, W_{2}, a, b\right)=\frac{1}{2} \sum_{k=1}^{n}\left\|x_{k}-\hat{x}_{k}\right\|^{2}+\lambda \rho(h)

式中ρ(h)\rho(h) 是稀疏性度量函数,相当于一个惩罚项,可采取如下三种形式:

  1. 隐藏层向量L1范数,即ρ(h)=h1\rho(h)=\|h\|_{1}

  2. 平均活性值,ρ(hi)=1Nk=1Nhik\rho\left(h_{i}\right)=\frac{1}{N} \sum_{k=1}^{N} h_{i}^{k}NN 为样本数。

  3. KL距离,KL距离通常用来度量两个分布之间的差异。我们希望隐藏层是稀疏的, 所以希望隐藏层每个神经元激活的概率ρ\rho^{'} 比较小,例如0.05。而神经元的平均活性值ρ(hi)\rho(h_i)可以看作神经元激活的概率,自然地可以将ρ(hi)\rho(h_i)ρ\rho^{'} 的距离作为惩罚项:

    KL(ρρ(hi))=ρlogρρ(hi)+(1ρ)log1ρ1ρ(hi)ρ(h)=i=1pKL(ρρ(hi))\begin{array}{c} K L\left(\rho^{'} \| \rho\left(h_{i}\right)\right)=\rho^{'} \log \frac{\rho^{'}}{\rho\left(h_{i}\right)}+\left(1-\rho^{'}\right) \log \frac{1-\rho^{'}}{1-\rho\left(h_{i}\right)} \\\\ \rho(h)=\sum_{i=1}^{p} K L\left(\rho^{'} \| \rho\left(h_{i}\right)\right) \end{array}

    式中pp 表示隐藏层神经元数量。

变分自编码器

如果将解码器看做一个生成模型,我们只要有低维向量表示,就可以用这个生成模型得到近似真实的样本。但是,这样的生成模型存在一个问题:低维向量表示必须是由真实样本通过编码器得到的,否则随机产生的低维向量表示通过生成模型几乎不可能得到近似真实的样本。那么,如果能将低维向量表示约束在一个分布(比如正态分布)中,那么从该分布中随机采样,产生的低维向量表示通过生成模型不是就能产生近似真实的样本了吗?

变分自编码器就是这样的一种自编码器,如下图所示。变分自编码器通过编码器学到的不是样本的低维向量表示,而是低维向量表示的分布。假设这个分布服从正态分布,然后在低维向量表示的分布中采样得到低维向量表示,接下来经过解码器还原出原始样本。

变分自编码器结构

但是,这样的结构无法保证采样变量ZiZ_i 与真是样本XiX_i 一一对应,也就难以保证变分自编码器的学习效果。所以,变分自编码器实际结构如下图所示:将真实样本X={X1,,Xn}X=\left\{X_{1}, \cdots, X_{n}\right\} 输入变分图自编码器,通过编码器(均值方差计算模块)学到每个样本对应的低维向量表示的均μ\mu 值和方差σ\sigma ,然后在N(μ,σ2)N(\mu, \sigma^2) 中采样出变量Z={Z1,,Zn}Z=\left\{Z_{1}, \cdots, Z_{n}\right\} ,再经过解码器(生成器)生成样本X^={X^1,,X^n}\hat{X}=\left\{\hat{X}_{1}, \cdots, \hat{X}_{n}\right\}

VAE 真实结构

损失函数衡量生成样本和真实样本之间的差异,例如:

LD=D(X^k,Xk)L_{D}=D\left(\hat{X}_{k}, X_{k}\right)

其中DD为距离度量函数。

如果只用距离度量作为损失函数,那么为了让生成器生成效果最佳,模型会把方差的值学为0,因为这样从正态分布中采样出来的是定值,有利于减小生成样本和真实样本之间的差异。但是,这样就退化成了普通的自编码器。为了解决这个问题,在损失函数中加入加入各独立正态分布和标准正态分布的 KL 散度((,2)(0,1))((,^2)||(0,1)) ,强迫各个正态分布逼近标准正态分布:

L(μ,σ2)=1/2i=1d(μ(i)2+σ(i)2logσ(i)21)L(\mu, \sigma^{2})=1 / 2 \sum_{i=1}^{d}\left(\mu_{(i)}^{2}+\sigma_{(i)}^{2}-\log \sigma_{(i)}^{2}-1\right)

因此新的损失函数定义如下:

L=LD+Lμ,σ2L=L_{D}+L_{\mu, \sigma^{2}}

一般使用随机梯度下降来训练变分自编码器,但是我们注意到变分自编码器的结构中有采样操作,而采样操作是无法进行反向传播的,也就无法参与梯度下降。这里,使用重参数技巧来避免这个问题,如下图所示:从N(μ,σ2)N(\mu, \sigma^2) 中采样一个ZZ ,相当于从N(0,1)N(0, 1) 中采样一个ϵ\epsilon,然后让Z=μ+ϵ×σZ=\mu + \epsilon \times \sigma,这样避免了"采样"参与梯度下降操作。

重参数技巧

此时,如果我们再将解码器看做生成模型,从正态分布中任意采样一个低维向量表示,经过生成模型都能得到一个近似真实的样本,甚至可以得到变分自编码器从未见过的样本。

PyTorch 实现

参考

联系作者