当前位置:   article > 正文

深度学习-生成模型:AutoEncoder模型(Encoder-Decoder框架==Embedding+Generator)【Variational AutoEncoder已被GAN取代】

encoder模型

一、AutoEncoder概述

  • 作为一种无监督或者自监督算法,自编码器本质上是一种数据压缩算法。
  • 从现有情况来看,无监督学习很有可能是一把决定深度学习未来发展方向的钥匙,在缺乏高质量打标数据的监督机器学习时代,若是能在无监督学习方向上有所突破对于未来深度学习的发展意义重大。
    在这里插入图片描述
  • 所谓自编码器(Autoencoder,AE),就是一种利用反向传播算法使得输出值等于输入值的神经网络,它先将输入压缩成潜在空间表征,然后将这种压缩后的空间表征重构为输出。
    在这里插入图片描述
  • 它的隐藏成层的向量具有降维的作用。所以,从本质上来讲,自编码器是一种数据压缩算法,其压缩和解压缩算法都是通过神经网络来实现的。
  • AutoEncoder通常有两个方面的应用:
    1. 是数据去噪,
    2. 是为进行可视化而降维。
    3. 进行图像压缩
    4. 传统自编码器被用于降维或特征学习
  • 自编码器在适当的维度和系数约束下可以学习到比PCA等技术更有意义的数据映射。

二、AutoEncoder结构

  • 自编码器有如下三个特点:
    1. 数据相关性。就是指自编码器只能压缩与自己此前训练数据类似的数据,比如说我们使用mnist训练出来的自编码器用来压缩人脸图片,效果肯定会很差。
    2. 数据有损性。自编码器在解压时得到的输出与原始输入相比会有信息损失,所以自编码器是一种数据有损的压缩算法。
    3. 自动学习性。自动编码器是从数据样本中自动学习的,这意味着很容易对指定类的输入训练出一种特定的编码器,而不需要完成任何新工作。

在这里插入图片描述

  • 构建一个自编码器需要两部分:编码器(Encoder)和解码器(Decoder)。
    • 编码器将输入压缩为潜在空间表征,编码器会创建一个隐藏层(或多个隐藏层)包含了输入数据含义的低维向量。可以用函数 f ( x ) f(x) f(x) 来表示,
    • 解码器将潜在空间表征重构为输出,即通过隐藏层的低维向量重建输入数据。可以用函数 g ( x ) g(x) g(x) 来表示
  • 编码函数 f ( x ) f(x) f(x) 和解码函数 g ( x ) g(x) g(x) 都是神经网络模型。
    在这里插入图片描述

三、AutoEncoder(自编码器)与PCA(主成分分析)降维效果对比

  • 主成分分析(PCA)和自编码器(AutoEncoders, AE)是无监督学习中的两种代表性方法。
  • PCA 的地位不必多说,只要是讲到降维的书,一定会把 PCA 放到最前面,它与 LDA 同为机器学习中最基础的线性降维算法,SVM/Logistic Regression、PCA/LDA 也是最常被拿来作比较的两组算法。
  • 自编码器虽然不像 PCA 那般在教科书上随处可见,但是在早期被拿来做深度网络的逐层预训练,其地位可见一斑。尽管在 ReLU、Dropout 等神器出现之后,人们不再使用 AutoEncoders 来预训练,但它延伸出的稀疏 AutoEncoders,降噪 AutoEncoders 等仍然被广泛用于表示学习。
  • PCA 和 AutoEncoders 都是非概率的方法,它们分别有一种对应的概率形式叫做概率 PCA (ProbabilisTIc PCA) 和变分自编码器(VariaTIonal AE, VAE)。
  • AutoEncoder是一种类似于 PCA 的无监督机器学习算法。大体上,AutoEncoder可以看作是PCA的非线性补丁加强版,PCA的取得的效果是建立在降维基础上的。
  • AutoEncoder要最小化和 PCA 一样的目标函数。自动编码器的目标是学习函数 h ( x ) ≈ x h(x)≈x h(x)x。换句话说,它要学习一个近似的恒等函数,使得输出 x ^ \hat{x} x^ 近似等于输入 x x x
  • AutoEncoder是一种神经网络,这种神经网络的目标输出就是其输入。自动编码器属于神经网络家族,但它们也和 PCA(主成分分析)紧密相关。
  • 尽管AutoEncoder与 PCA 很相似,但自动编码器比 PCA 灵活得多。
  • 在编码过程中,AutoEncoder既能表征线性变换,也能表征非线性变换;而 PCA 只能执行线性变换。因为AutoEncoder的网络表征形式,所以可将其作为层用于构建深度学习网络。The linearity of PCA, however, places significant limitations on the kinds of feature dimensions that can be extracted。
  • 设置合适的维度和稀疏约束,AutoEncoder可以学习到比PCA等技术更有意思的数据投影。
  • PCA, which finds the directions of maximal variance in high-dimensional data, select only those axes that have the largest variance.
    在这里插入图片描述

四、AutoEncoder模型的损失函数

  • AE是一个自动编码器是一个非监督的学习模式,只需要输入数据,不需要label或者输入输出对的数据。
  • 虽然AE是一个非监督学习算法,如果它的解码器是线性重建数据,可以用MSE来表示它的损失函数:
    在这里插入图片描述
  • 如果解码器用Sigmoid的激活函数,那主要用交叉上损失函数:
    在这里插入图片描述

五、AutoEncoder常用模型

AE的思想是在1986年被提出来的,在接下来的几年,AE的思想席卷了个大研究论文。关于AE比较有代表性的模型有一下几种:

1、Denoising AutoEncoder/去噪自动编码器

  • DAE的主要做法是,输入数据加入了噪声,输出的数据是完整的数据。DAE会强制隐藏层只去学习主要的特征,输出的数据就会是更好的鲁棒性。
  • DAE的一种方式是随机的删除数据集中的某些数据,然后用完整的数据去评判,DAE会尝试去预测恢复缺失的部分。
  • 一个关于手写数字集的DAE的展示图如下:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2、Adversarial Auto-Encoders/对抗自动编码器(GAN)

在这里插入图片描述
在这里插入图片描述

3、Sparse AutoEncoder/稀疏自动编码器

  • AE一般的方式是通过隐藏层中的少数的隐藏单元去发现有用的信息,但是AE也可以通过大量的隐藏单元去发现有用信息。
  • SAE的做法是把输入数据转化为高纬度的中间层,然后引入一个稀疏限制的规则。稀疏限制是在大部分时间,大部分的神经元的平均输出比较低。
  • 如果使用Sigmoid的激活函数,我们会尽量把输出变为0,如果是tanh的激活函数,我们会尽量把输出变为-1。
    在这里插入图片描述
    其中: a j a_j aj 是神经元的激活后的输出, p j p_j pj 是所有神经元输出的平均值,我们的目标是去最小化 p j p_j pj
  • K-Sparse AutoEncoder是SAE提升版本,KSAE是本身的隐藏神经元非常多,但只选择k个神经元是激活的,其他都是dropout状态,通过选择不同的激活函数和调整不同的k的阈值去训练。下面是通过调整k值,生成不同的手写数字的输出值。
    在这里插入图片描述

4、VAE(Variational AutoEncoder)/变分自动编码器

在这里插入图片描述

  • VAE结构是一个经典的autocoder模型,网络的组成也是编码器、解码器、loss。
  • VAE的机构和普通的AE结构有所不同。
  • 普通的AE结构如下,解码器直接使用编码器的输出向量。
    在这里插入图片描述
  • 上面的模型已经可以训练任意图片了。但是,我们想建一个产生式模型,而不是一个只是储存图片的网络。
  • 现在我们还不能产生任何未知的东西,因为我们不能随意产生合理的潜在变量。因为合理的潜在变量都是编码器从原始图片中产生的。这里有个简单的解决办法。我们可以对编码器添加约束,就是强迫它产生服从单位高斯分布的潜在变量。正是这种约束,把VAE和标准自编码器给区分开来了
  • VAE 的loss通常由两部分组成:首先是 reconstruction loss, 然后是KL散度( z z z 的分布和正态分布的差距)。
    在这里插入图片描述
  • 标准AutoEncoder的Encoder产生的是明确的、固定的 vector h h h
  • VAE 的 Encoder 会产生一个分布,这个分布用 均值 vector标准差 vector来表示。
  • VAE将经过神经网络编码后的隐藏层假设为一个标准的高斯分布,然后再从这个分布中采样一个特征,再用这个特征进行解码,期望得到与原始输入相同的结果,损失和AE几乎一样,只是增加编码推断分布与标准高斯分布的KL散度的正则项,显然增加这个正则项的目的就是防止模型退化成普通的AE,因为网络训练时为了尽量减小重构误差,必然使得方差逐渐被降到0,这样便不再会有随机采样噪声,也就变成了普通的AE。
    在这里插入图片描述
    其中:第一项即为交叉熵;第二项为KL-Divergence
    K L ( p , q ) = − ∫ p ( x ) log ⁡ q ( x ) d x + ∫ p ( x ) log ⁡ p ( x ) d x = 1 2 log ⁡ ( 2 π σ 2 2 ) + σ 1 2 + ( μ 1 − μ 2 ) 2 2 σ 2 2 − 1 2 ( 1 + log ⁡ 2 π σ 1 2 ) = log ⁡ σ 2 σ 1 + σ 1 2 + ( μ 1 − μ 2 ) 2 2 σ 2 2 − 1 2 = q 分 布 为 N ( 0 , 1 ) B 标 准 正 态 分 布 log ⁡ 1 σ 1 + σ 1 2 + ( μ 1 − 0 ) 2 2 ⋅ ( 1 ) 2 − 1 2 = log ⁡ 1 σ 1 + σ 1 2 + μ 1 2 2 − 1 2 = − 1 2 [ 2 log ⁡ σ 1 − ( σ 1 2 + μ 1 2 ) + 1 ] KL(p,q)=p(x)logq(x)dx+p(x)logp(x)dx=12log(2πσ22)+σ21+(μ1μ2)22σ2212(1+log2πσ21)=logσ2σ1+σ21+(μ1μ2)22σ2212\xlongequalqN(0,1)Blog1σ1+σ21+(μ10)22·(1)212=log1σ1+σ21+μ21212=12[2logσ1(σ21+μ21)+1] KL(p,q)=p(x)logq(x)dx+p(x)logp(x)dx=21log(2πσ22)+2σ22σ12+(μ1μ2)221(1+log2πσ12)=logσ1σ2+2σ22σ12+(μ1μ2)221qN(0,1)B logσ11+2(1)2σ12+(μ10)221=logσ11+2σ12+μ1221=21[2logσ1(σ12+μ12)+1]
  • 它妙就妙在它为每个输入 x x x, 生成了一个潜在概率分布 p ( z ∣ x ) p(z|x) p(zx) ,然后再从分布中进行随机采样,从而得到了连续完整的潜在空间,解决了AE中无法用于生成的问题。
    在这里插入图片描述
  • VAE除了能让我们能够自己产生随机的潜在变量,这种约束也能提高网络的产生图片的能力
  • 但是,VAE的一个劣势就是没有使用对抗网络,所以VAE会更趋向于产生模糊的图片

4.1 变分推断

  • 变分自编码器(VAE)的想法和名字的由来便是变分推断了,那么什么是变分推断呢?
  • 变分推断是MCMC搞不定场景的一种替代算法,它考虑一个贝叶斯推断问题,给定观测变量 x ∈ R k x \in \mathbb{R}^k xRk 和潜变量 z ∈ R d z \in \mathbb{R}^d zRd,其联合概率分布为 p ( z , x ) = p ( z ) p ( x ∣ z ) p(z, x) = p(z)p(x|z) p(z,x)=p(z)p(xz) , 目标是计算后验分布 p ( z ∣ x ) p(z|x) p(zx)。然后我们可以假设一个变分分布 q ( z ) q(z) q(z) 来自分布族 Q Q Q,通过最小化KL散度来近似后验分布 p ( z ∣ x ) p(z|x) p(zx) :
    q ∗ = arg min ⁡ q ( z ) ∈ Q K L ( q ( z ) ∣ ∣ p ( z ∣ x ) ) q=\argminq(z)QKL(q(z)||p(z|x)) q=q(z)QargminKL(q(z)p(zx))
  • 这么一来,就成功的将一个贝叶斯推断问题转化为了一个优化问题~

4.2 变分推导过程

  • 有了变分推断的认知,我们再回过头去看一下VAE模型的整体框架,VAE就是将AE的编码和解码过程转化为了一个贝叶斯概率模型:我们的训练数据即为观测变量 x x x, 假设它由不能直接观测到的潜变量 z z z 生成, 于是,生成观测变量过程便是似然分布: p ( x ∣ z ) p(x|z) p(xz) ,也就是解码器,因而编码器自然就是后验分布: p ( z ∣ x ) p(z|x) p(zx) .
  • 根据贝叶斯公式,建立先验、后验和似然的关系:
    p ( z ∣ x ) = p ( x ∣ z ) p ( z ) p ( x ) = ∫ z p ( x ∣ z ) p ( z ) p ( x ) d z p(z|x) = \frac{p(x|z)p(z)}{p(x)} = \int_z \frac{p(x|z)p(z)}{p(x)}dz p(zx)=p(x)p(xz)p(z)=zp(x)p(xz)p(z)dz
  • 接下来,基于上面变分推断的思想,我们假设变分分布 q x ( z ) q_x(z) qx(z), 通过最小化KL散度来近似后验分布 p ( z ∣ x ) p(z|x) p(zx) ,于是,最佳的 q x ∗ q_x^* qx便是:
    q x ∗ = a r g m i n ( K L ( q x ( z ) ∣ ∣ p ( z ∣ x ) ) = a r g m i n ( E q x ( z ) [ l o g   q x ( z ) − l o g   p ( x ∣ z ) − l o g   p ( z ) ] + l o g   p ( x ) ) qx=argmin(KL(qx(z)||p(z|x))=argmin(Eqx(z)[log qx(z)log p(x|z)log p(z)]+log p(x)) qx=argmin(KL(qx(z)p(zx))=argmin(Eqx(z)[log qx(z)log p(xz)log p(z)]+log p(x))
  • 因为训练数据 x x x 是确定的,因此 l o g   p ( x ) log~p(x) log p(x) 是一个常数,于是上面的优化问题等价于:
    q x ∗ = a r g m i n ( E q x ( z ) [ l o g   q x ( z ) − l o g   p ( x ∣ z ) − l o g   p ( z ) ] = a r g m i n ( E q x ( z ) [ − l o g   p ( x ∣ z ) + ( l o g   p ( z ) − l o g   q x ( z ) ) ] ) = a r g m i n ( E q x ( z ) [ − l o g   p ( x ∣ z ) + K L ( q x ( z ) ∣ ∣ p ( z ) ) ] ) qx=argmin(Eqx(z)[log qx(z)log p(x|z)log p(z)]=argmin(Eqx(z)[log p(x|z)+(log p(z)log qx(z))])=argmin(Eqx(z)[log p(x|z)+KL(qx(z)||p(z))]) qx=argmin(Eqx(z)[log qx(z)log p(xz)log p(z)]=argmin(Eqx(z)[log p(xz)+(log p(z)log qx(z))])=argmin(Eqx(z)[log p(xz)+KL(qx(z)p(z))])
  • 此时,观察一下优化方程的形式…已经是我们前面所说的VAE的损失函数了~~
  • 显然,跟我们希望解码准确的目标是一致的。要解码的准,则 p ( x ∣ z ) p(x|z) p(xz) 应该尽可能的小,编码特征 z z z 的分布 q x ( z ) q_x(z) qx(z) p ( z ) p(z) p(z) 尽可能的接近,此时恰好 − l o g   p ( x ∣ z ) -log~p(x|z) log p(xz) K L ( q x ( z ) ∣ ∣ p ( z ) ) KL(q_x(z)||p(z)) KL(qx(z)p(z)) 都尽可能的小,与损失的优化的目标也一致。

4.3 如何计算极值

  • 正如前面所提到的AE潜变量的局限性,我们希望VAE的潜变量分布 p ( z ) p(z) p(z) 应该能满足海量的输入数据 x x x 并且相互独立,基于中心极限定理,以及为了方便采样,我们有理由直接假设 p ( z ) p(z) p(z) 是一个标准的高斯分布 N ( 0 , 1 ) \mathcal{N}(0,1) N(0,1) .

4.4 编码部分

  • 我们先来看一下编码部分,我们希望拟合一个分布 q x ( z ) = N ( μ , σ ) q_x(z)=\mathcal{N}(\mu,\sigma) qx(z)=N(μ,σ) 尽可能接近 p ( z ) = N ( 0 , 1 ) p(z) =\mathcal{N}(0,1) p(z)=N(0,1), 关键就在于基于输入 x x x 计算 μ \mu μ σ \sigma σ, 直接算有点困难,于是就使用两个神经网络 f ( x ) f(x) f(x) g ( x ) g(x) g(x) 来无脑拟合 μ \mu μ σ \sigma σ
  • 值得一提的是,很多地方实际使用的 f ( x ) f(x) f(x) g ( x ) g(x) g(x) 两部分神经网络并不是独立的,而是有一部分交集,即他们都先通过一个 h ( x ) h(x) h(x) 映射到一个中间层 h h h, 然后分别对 h h h 计算 f ( h ) f(h) f(h) g ( h ) g(h) g(h). 这样错的好处的话一方面是可以减少参数数量,另外这样算应该会导致拟合的效果差一些,算是防止过拟合吧。

4.5 解码部分

  • 解码,即从潜变量 z z z 生成数据 x x x 的过程,在于最大化似然 p ( x ∣ z ) p(x|z) p(xz) ,那这应该是个什么分布呢?通常我们假设它是一个伯努利分布或是高斯分布。
  • 知道了分布类型,那计算 − l o g   p ( x ∣ z ) -log~p(x|z) log p(xz) 最小值其实只要把分布公式带进去算就可以了.
4.5.1 高斯分布

a r g m i n ( − log ⁡   q ( x ∣ z ) ) = a r g m i n 1 2 ∥ x − μ ~ ( z ) σ ~ ( z ) ∥ 2 + c 2 log ⁡   2 π + 1 2 = a r g m i n 1 2 ∥ x − μ ~ ( z ) σ ~ ( z ) ∥ 2 argmin(log q(x|z))=argmin12x˜μ(z)˜σ(z)2+c2log 2π+12=argmin12x˜μ(z)˜σ(z)2 argmin(log q(xz))=argmin21σ~(z)xμ~(z)2+2clog 2π+21=argmin21σ~(z)xμ~(z)2

  • 和预期一样,演变为了均方误差。
4.5.2 伯努利分布
  • 假设伯努利的二元分布是 P P P 1 − P 1-P 1P (注意这里是输出没一维组成的向量)
    a r g m i n ( − log ⁡ q ( x ∣ z ) ) = a r g m i n ( − x log ⁡ P − ( 1 − x ) log ⁡ ( 1 − P ) ) argmin ( -\log q(x|z)) = argmin( - x \log P - (1-x) \log (1 -P)) argmin(logq(xz))=argmin(xlogP(1x)log(1P))
  • 正好就是交叉熵的损失。然后,将编码和解码部分组合到一起,就形成了完整的VAE网络。

4.6 Reparameterization trick

在这里插入图片描述

  • 训练的时候似乎出了点问题。从编码得到的分布 N ( μ , σ ) \mathcal{N}(\mu,\sigma) N(μ,σ) 随机采样 z z z 的这个过程没法求导,没法进行误差反向传播。
  • Reparameterization trick :解决VAE 的 Encoder 产生的分布函数无梯度可优化问题
    z = μ + ε ⋅ σ = f ( x ) + ε ⋅ g ( x ) , ε ∼ N ( 0 , 1 ) z = μ+ε·σ=f(x) +ε·g(x) , ε\sim N(0,1) z=μ+εσ=f(x)+εg(x),εN(0,1)
    在这里插入图片描述
  • 这样一来将采样变成了一个数值变换,整个过程便可导了。
  • 这样,训练好模型之后,我们可以直接将解码部分拿出来,通过标准高斯分布随机采样源源不断的生成数据了。

5、Contractive AutoEncoder/收缩自动编码器

  • CAE的主要目标是使得隐藏层向量对输入数据的微小的变动能够有更强的鲁棒性。CAE的做法是在普通AE的基础上加上一个惩罚项。公式如下:
    在这里插入图片描述
  • CAE和DAE目标是相似的,DAE是通过加入噪声,重构来提升模型的鲁棒性,CAE是通过增加雅克比矩阵的惩罚项来提高模型鲁棒性。

五、AutoEncoder用途

1、AutoEncoder用于Text Retrieval

在这里插入图片描述
在这里插入图片描述

2、AutoEncoder用于Similar Image Search

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

3、AutoEncoder用于Pre-training DNN(初始化)

  • 现在的network 比较强大,基本不用Pre-training,但是当数据集中有大量unlabel 数据时AutoEncoder做Pre-training还是有用的
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4、AutoEncoder用于CNN

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、Feature Disentangle用于变声器(Voice Conversion)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、AutoEncoder案例

1、原始AutoEncoder-fashion_mnist数据集

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import Sequential, layers
from PIL import Image

tf.random.set_seed(22)
np.random.seed(22)

assert tf.__version__.startswith('2.')


# 合并多张图片
def save_images(imgs, name):
    new_im = Image.new('L', (280, 280))
    index = 0
    for i in range(0, 280, 28):
        for j in range(0, 280, 28):
            im = imgs[index]
            im = Image.fromarray(im, mode='L')
            new_im.paste(im, (i, j))
            index += 1
    new_im.save(name)


h_dim = 10
batch_size = 10000
lr = 1e-3

# 一、获取数据集
(X_train, Y_train), (X_val, Y_val) = keras.datasets.fashion_mnist.load_data()

# 二、数据处理:处理训练集为batch模型(不需要目标值)
X_train = X_train.astype(np.float32) / 255.
db_train = tf.data.Dataset.from_tensor_slices(X_train)
db_train = db_train.shuffle(batch_size * 5).batch(batch_size)
X_val = X_val.astype(np.float32) / 255.
db_val = tf.data.Dataset.from_tensor_slices(X_val)
db_val = db_val.batch(batch_size)
print('X_train.shpae = {0},Y_train.shpae = {1},tf.reduce_max(Y_train) = {2},tf.reduce_min(Y_train) = {3}'.format(X_train.shape, Y_train.shape, tf.reduce_max(Y_train), tf.reduce_min(Y_train)))
print('X_val.shpae = {0},Y_val.shpae = {1},tf.reduce_max(Y_val) = {2},tf.reduce_min(Y_val) = {3}'.format(X_val.shape, Y_val.shape, tf.reduce_max(Y_val), tf.reduce_min(Y_val)))


# 三、创建AutoEncoder神经网络模型
class AutoEncoder(keras.Model):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        # Encoders
        self.encoder = Sequential([
            layers.Dense(256, activation=tf.nn.relu),  # [b, 784] => [b, 256]
            layers.Dense(128, activation=tf.nn.relu),  # [b, 256] => [b, 128]
            layers.Dense(h_dim)  # [b, 128] => [b, 10]
        ])
        # Decoders
        self.decoder = Sequential([
            layers.Dense(128, activation=tf.nn.relu),  # [b, 10] => [b, 128]
            layers.Dense(256, activation=tf.nn.relu),  # [b, 128] => [b, 256]
            layers.Dense(784)  # [b, 256] => [b, 784]
        ])

    def call(self, inputs, training=None):
        # [b, 784] => [b, 10]
        h_out = self.encoder(inputs)
        # [b, 10] => [b, 784]
        x_hat = self.decoder(h_out)

        return x_hat


# 四、实例化AutoEncoder神经网络模型
model = AutoEncoder()
model.build(input_shape=(None, 784))
model.summary()
optimizer = tf.optimizers.Adam(lr=lr)


# 五、训练模型:整体数据集进行一次梯度下降来更新模型参数,整体数据集迭代一次,一般用epoch。每个epoch中含有batch_step_no个step,每个step中样本的数量就是设置的每个batch所含有的样本数量。
def train_epoch(epoch_no):
    for batch_step_no, X_batch in enumerate(db_train):  # 每次计算一个batch的数据,循环结束则计算完毕整体数据的一次前向传播;每个batch的序号一般用step表示(batch_step_no)
        X_batch = tf.reshape(X_batch, [-1, 784])  # [b, 28, 28] => [b, 784]
        print('\tX_batch.shape = {0}'.format(X_batch.shape))
        with tf.GradientTape() as tape:
            X_batch_hat_logits = model(X_batch)
            print('\tX_batch_hat_logits.shape = {0}'.format(X_batch_hat_logits.shape))
            CrossEntropy_Loss = tf.losses.binary_crossentropy(X_batch, X_batch_hat_logits, from_logits=True)
            CrossEntropy_Loss = tf.reduce_mean(CrossEntropy_Loss)
        grads = tape.gradient(CrossEntropy_Loss, model.trainable_variables)
        optimizer.apply_gradients(zip(grads, model.trainable_variables))
        print('\t第{0}个epoch-->第{1}个batch step的初始时的:CrossEntropy_Loss = {2}'.format(epoch_no, batch_step_no + 1, CrossEntropy_Loss))


# 六、模型评估 test/evluation
def evluation(epoch_no):
    X_batch = next(iter(db_val))
    X_batch_784 = tf.reshape(X_batch, [-1, 784])
    print('\tX_batch_784.shape = {0}'.format(X_batch_784.shape))
    X_batch_logits_hat = model(X_batch_784)    # model包含了encoder、decoder两大步骤
    X_batch_prob_hat = tf.sigmoid(X_batch_logits_hat)
    X_batch_prob_hat = tf.reshape(X_batch_prob_hat, [-1, 28, 28])  # [b, 784] => [b, 28, 28]
    print('\tX_batch_prob_hat.shape = {0}'.format(X_batch_prob_hat.shape))
    # X_batch_concat = tf.concat([X_batch, X_batch_prob_hat], axis=0)  # [b, 28, 28] => [2b, 28, 28]
    # print('\tX_batch_concat.shape = {0}'.format(X_batch_concat.shape))
    # X_batch_concat = X_batch_prob_hat
    X_batch_hat = X_batch_prob_hat.numpy() * 255.
    X_batch_hat = X_batch_hat.astype(np.uint8)
    save_images(X_batch_hat, 'AutoEncoder_images/rec_epoch_%d.png' % epoch_no)


# 六、整体数据迭代多次梯度下降来更新模型参数
def train():
    epoch_count = 3  # epoch_count为整体数据集迭代梯度下降次数
    for epoch_no in range(1, epoch_count + 1):
        print('\n\n利用整体数据集进行模型的第{0}轮Epoch迭代开始:**********************************************************************************************************************************'.format(epoch_no))
        train_epoch(epoch_no)
        evluation(epoch_no)
        print('利用整体数据集进行模型的第{0}轮Epoch迭代结束:**********************************************************************************************************************************'.format(epoch_no))


if __name__ == '__main__':
    train()
  • 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

在这里插入图片描述

打印结果:

X_train.shpae = (60000, 28, 28),Y_train.shpae = (60000,),tf.reduce_max(Y_train) = 9,tf.reduce_min(Y_train) = 0
X_val.shpae = (10000, 28, 28),Y_val.shpae = (10000,),tf.reduce_max(Y_val) = 9,tf.reduce_min(Y_val) = 0
Model: "auto_encoder"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
sequential (Sequential)      (None, 10)                235146    
_________________________________________________________________
sequential_1 (Sequential)    (None, 784)               235920    
=================================================================
Total params: 471,066
Trainable params: 471,066
Non-trainable params: 0
_________________________________________________________________


利用整体数据集进行模型的第1轮Epoch迭代开始:**********************************************************************************************************************************
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 0.6930073499679565
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->2个batch step的初始时的:CrossEntropy_Loss = 0.690101146697998
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->3个batch step的初始时的:CrossEntropy_Loss = 0.6838963627815247
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->4个batch step的初始时的:CrossEntropy_Loss = 0.6729660034179688
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->5个batch step的初始时的:CrossEntropy_Loss = 0.6565828323364258
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)1个epoch-->6个batch step的初始时的:CrossEntropy_Loss = 0.6356714963912964
	X_batch_784.shape = (10000, 784)
	X_batch_prob_hat.shape = (10000, 28, 28)
利用整体数据集进行模型的第1轮Epoch迭代结束:**********************************************************************************************************************************


利用整体数据集进行模型的第2轮Epoch迭代开始:**********************************************************************************************************************************
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 0.6140648722648621
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->2个batch step的初始时的:CrossEntropy_Loss = 0.595238447189331
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->3个batch step的初始时的:CrossEntropy_Loss = 0.5807722806930542
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->4个batch step的初始时的:CrossEntropy_Loss = 0.5614200234413147
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->5个batch step的初始时的:CrossEntropy_Loss = 0.5468764901161194
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)2个epoch-->6个batch step的初始时的:CrossEntropy_Loss = 0.5310028195381165
	X_batch_784.shape = (10000, 784)
	X_batch_prob_hat.shape = (10000, 28, 28)
利用整体数据集进行模型的第2轮Epoch迭代结束:**********************************************************************************************************************************


利用整体数据集进行模型的第3轮Epoch迭代开始:**********************************************************************************************************************************
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 0.5188546180725098
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->2个batch step的初始时的:CrossEntropy_Loss = 0.5084328055381775
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->3个batch step的初始时的:CrossEntropy_Loss = 0.49996528029441833
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->4个batch step的初始时的:CrossEntropy_Loss = 0.4927319288253784
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->5个batch step的初始时的:CrossEntropy_Loss = 0.4851318299770355
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)3个epoch-->6个batch step的初始时的:CrossEntropy_Loss = 0.4777812361717224
	X_batch_784.shape = (10000, 784)
	X_batch_prob_hat.shape = (10000, 28, 28)
利用整体数据集进行模型的第3轮Epoch迭代结束:**********************************************************************************************************************************


利用整体数据集进行模型的第4轮Epoch迭代开始:**********************************************************************************************************************************
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 0.4697949290275574
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->2个batch step的初始时的:CrossEntropy_Loss = 0.4621494710445404
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->3个batch step的初始时的:CrossEntropy_Loss = 0.45390141010284424
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->4个batch step的初始时的:CrossEntropy_Loss = 0.447597473859787
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->5个batch step的初始时的:CrossEntropy_Loss = 0.4424560070037842
	X_batch.shape = (10000, 784)
	X_batch_hat_logits.shape = (10000, 784)4个epoch-->6个batch step的初始时的:CrossEntropy_Loss = 0.4317522943019867
	X_batch_784.shape = (10000, 784)
	X_batch_prob_hat.shape = (10000, 28, 28)
利用整体数据集进行模型的第4轮Epoch迭代结束:**********************************************************************************************************************************

Process finished with exit code 0
  • 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

2、VAE-fashion_mnist数据集

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
import numpy as np
from tensorflow import keras
from tensorflow.keras import Sequential, layers
from PIL import Image

tf.random.set_seed(22)
np.random.seed(22)

assert tf.__version__.startswith('2.')


# 合并多张图片
def save_images(imgs, name):
    new_im = Image.new('L', (280, 280))
    index = 0
    for i in range(0, 280, 28):
        for j in range(0, 280, 28):
            im = imgs[index]
            im = Image.fromarray(im, mode='L')
            new_im.paste(im, (i, j))
            index += 1
    new_im.save(name)


h_dim = 20
batch_size = 500
lr = 1e-3

# 一、获取数据集
(X_train, Y_train), (X_val, Y_val) = keras.datasets.fashion_mnist.load_data()

# 二、数据处理:处理训练集为batch模型(不需要目标值)
X_train = X_train.astype(np.float32) / 255.
db_train = tf.data.Dataset.from_tensor_slices(X_train)
db_train = db_train.shuffle(batch_size * 5).batch(batch_size)
X_val = X_val.astype(np.float32) / 255.
db_val = tf.data.Dataset.from_tensor_slices(X_val)
db_val = db_val.batch(batch_size)
print('X_train.shpae = {0},Y_train.shpae = {1},tf.reduce_max(Y_train) = {2},tf.reduce_min(Y_train) = {3}'.format(X_train.shape, Y_train.shape, tf.reduce_max(Y_train), tf.reduce_min(Y_train)))
print('X_val.shpae = {0},Y_val.shpae = {1},tf.reduce_max(Y_val) = {2},tf.reduce_min(Y_val) = {3}'.format(X_val.shape, Y_val.shape, tf.reduce_max(Y_val), tf.reduce_min(Y_val)))

z_dim = 10


class VAE(keras.Model):
    def __init__(self):
        super(VAE, self).__init__()
        # Encoder
        self.fc1 = layers.Dense(128)  # 将图片降维得 latent vector:[b, 784] => [b, 128]
        self.fc2 = layers.Dense(z_dim)  # fc1-->fc2--获得latent vector的均值预测值:[b, 128] => [b, 10]
        self.fc3 = layers.Dense(z_dim)  # fc1-->fc3--获得latent vector的方差预测值:[b, 128] => [b, 10]
        # Decoder
        self.fc4 = layers.Dense(128)  # [b, 20] => [b, 128]
        self.fc5 = layers.Dense(784)  # [b, 128] => [b, 784]

    def encoder(self, x):
        h = tf.nn.relu(self.fc1(x))  # 将图片降维得 latent vector:[b, 784] => [b, 128]
        mu = self.fc2(h)  # fc1-->fc2--获得latent vector的均值预测值:[b, 128] => [b, 10]
        log_var = self.fc3(h)  # fc1-->fc3--获得latent vector的方差预测值:[b, 128] => [b, 10]
        return mu, log_var

    def decoder(self, z):
        out = tf.nn.relu(self.fc4(z))  # [b, 20] => [b, 128]
        out = self.fc5(out)  # [b, 128] => [b, 784]
        return out

    def reparameterize(self, mu, log_var):
        eps = tf.random.normal(log_var.shape)
        std = tf.exp(log_var) ** 0.5
        z = mu + std * eps
        return z

    def call(self, inputs, training=None):
        mu, log_var = self.encoder(inputs)  # [b, 784] => [b, z_dim], [b, z_dim]
        z = self.reparameterize(mu, log_var)  # reparameterization trick
        x_hat = self.decoder(z)
        return x_hat, mu, log_var


# 四、实例化VAE神经网络模型
model = VAE()   # model包含了encoder、reparameterize、decoder三大步骤
model.build(input_shape=(4, 784))
optimizer = tf.optimizers.Adam(lr)


# 五、训练模型:整体数据集进行一次梯度下降来更新模型参数,整体数据集迭代一次,一般用epoch。每个epoch中含有batch_step_no个step,每个step中样本的数量就是设置的每个batch所含有的样本数量。
def train_epoch(epoch_no):
    for batch_step_no, X_batch in enumerate(db_train):  # 每次计算一个batch的数据,循环结束则计算完毕整体数据的一次前向传播;每个batch的序号一般用step表示(batch_step_no)
        X_batch = tf.reshape(X_batch, [-1, 784])  # [b, 28, 28] => [b, 784]
        with tf.GradientTape() as tape:
            x_rec_logits, mu, log_var = model(X_batch)
            # 计算reconstruction损失
            rec_loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=X_batch, logits=x_rec_logits)
            rec_loss = tf.reduce_sum(rec_loss) / X_batch.shape[0]
            # 计算 kl-Divergence (mu, var) ~ N (0, 1);https://stats.stackexchange.com/questions/7440/kl-divergence-between-two-univariate-gaussians
            kl_div = -0.5 * (log_var + 1 - mu ** 2 - tf.exp(log_var))
            kl_div = tf.reduce_sum(kl_div) / X_batch.shape[0]
            # 合并rec_loss与kl_div作为模型的整体损失
            Loss = rec_loss + 1. * kl_div  # 系数 1 表示 rec_loss 与 kl_div之间的权衡
        grads = tape.gradient(Loss, model.trainable_variables)
        optimizer.apply_gradients(zip(grads, model.trainable_variables))
        if batch_step_no % 60 == 0:
            print('\t第{0}个epoch-->第{1}个batch step的初始时的:CrossEntropy_Loss = {2}'.format(epoch_no, batch_step_no + 1, Loss))


# 六、模型评估 test/evluation
def evluation(epoch_no):
    # sampling:生成新图片
    z = tf.random.normal((batch_size, z_dim))
    logits = model.decoder(z)   # 利用decoder作为generator将随机数据z生成原来数据集中没有的图片
    x_hat = tf.sigmoid(logits)
    x_hat = tf.reshape(x_hat, [-1, 28, 28]).numpy() * 255.
    x_hat = x_hat.astype(np.uint8)
    save_images(x_hat, 'vae_images/sampled_epoch%d.png' % epoch_no)
    # reconstruction
    x = next(iter(db_val))
    x = tf.reshape(x, [-1, 784])
    x_hat_logits, _, _ = model(x)   # model包含了encoder、reparameterize、decoder三大步骤
    x_hat = tf.sigmoid(x_hat_logits)
    x_hat = tf.reshape(x_hat, [-1, 28, 28]).numpy() * 255.
    x_hat = x_hat.astype(np.uint8)
    save_images(x_hat, 'vae_images/rec_epoch%d.png' % epoch_no)


# 六、整体数据迭代多次梯度下降来更新模型参数
def train():
    epoch_count = 3  # epoch_count为整体数据集迭代梯度下降次数
    for epoch_no in range(1, epoch_count + 1):
        print('\n\n利用整体数据集进行模型的第{0}轮Epoch迭代开始:**********************************************************************************************************************************'.format(epoch_no))
        train_epoch(epoch_no)
        evluation(epoch_no)
        print('利用整体数据集进行模型的第{0}轮Epoch迭代结束:**********************************************************************************************************************************'.format(epoch_no))


if __name__ == '__main__':
    train()
  • 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

在这里插入图片描述

打印结果:

X_train.shpae = (60000, 28, 28),Y_train.shpae = (60000,),tf.reduce_max(Y_train) = 9,tf.reduce_min(Y_train) = 0
X_val.shpae = (10000, 28, 28),Y_val.shpae = (10000,),tf.reduce_max(Y_val) = 9,tf.reduce_min(Y_val) = 0


利用整体数据集进行模型的第1轮Epoch迭代开始:**********************************************************************************************************************************1个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 548.287597656251个epoch-->61个batch step的初始时的:CrossEntropy_Loss = 332.11102294921875
利用整体数据集进行模型的第1轮Epoch迭代结束:**********************************************************************************************************************************


利用整体数据集进行模型的第2轮Epoch迭代开始:**********************************************************************************************************************************2个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 296.12142944335942个epoch-->61个batch step的初始时的:CrossEntropy_Loss = 281.6827697753906
利用整体数据集进行模型的第2轮Epoch迭代结束:**********************************************************************************************************************************


利用整体数据集进行模型的第3轮Epoch迭代开始:**********************************************************************************************************************************3个epoch-->1个batch step的初始时的:CrossEntropy_Loss = 268.884277343753个epoch-->61个batch step的初始时的:CrossEntropy_Loss = 265.4090576171875
利用整体数据集进行模型的第3轮Epoch迭代结束:**********************************************************************************************************************************

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22



参考资料:
A wizard’s guide to Adversarial Autoencoders: Part 1, Autoencoder?
Applied Deep Learning - Part 3: Autoencoders
Ref: Rifai, Salah, et al. "Contractive auto-encoders: Explicit invariance during feature extraction.“ Proceedings of the 28th International Conference on Machine Learning (ICML-11). 2011.
Vincent, Pascal, et al. “Extracting and composing robust features with denoising autoencoders.” ICML, 2008.
自动编码器与PCA的比较
深度学习笔记 | 第17讲:深度生成模型之自编码器(AutoEncoder)
autoencoder
Building Autoencoders in Keras
1. AutoEncoder介绍
几种AutoEncoder原理
Dropout in (Deep) Machine learning
A wizard’s guide to Adversarial Autoencoders: Part 2, Exploring latent space with Adversarial Autoencoders.
解析Variational AutoEncoder(VAE)
VAE(Variational Autoencoder)的原理
Variational AutoEncoder系列
Variational autoencoders.
Variational_Auto-Encoder
Variational Autoencoder in TensorFlow
Generate Anime Character with Variational Auto-encoder

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/295874
推荐阅读
相关标签
  

闽ICP备14008679号