Python 无监督学习实用指南:6~10(3)https://developer.aliyun.com/article/1426888
现在,我们可以初始化超参数,如下所示:
n_components = 2 learning_rate = 0.0001 max_iterations = 1000 stabilization_cycles = 5 threshold = 0.00001 W = np.random.normal(0.0, 0.5, size=(Xs.shape[1], n_components)) V = np.tril(np.random.normal(0.0, 0.01, size=(n_components, n_components))) np.fill_diagonal(V, 0.0) prev_W = np.zeros((Xs.shape[1], n_components)) t = 0
因此,我们选择采用等于 0.00001 的停止阈值(比较基于权重矩阵的两次连续计算的 Frobenius 范数)和最多 1,000 次迭代。 我们还设置了五个稳定周期和固定的学习率η = 0.0001
。 我们可以开始学习过程,如下所示:
import numpy as np while np.linalg.norm(W - prev_W, ord='fro') > threshold and t < max_iterations: prev_W = W.copy() t += 1 for i in range(Xs.shape[0]): y_p = np.zeros((n_components, 1)) xi = np.expand_dims(Xs[i], 1) y = None for _ in range(stabilization_cycles): y = np.dot(W.T, xi) + np.dot(V, y_p) y_p = y.copy() dW = np.zeros((Xs.shape[1], n_components)) dV = np.zeros((n_components, n_components)) for t in range(n_components): y2 = np.power(y[t], 2) dW[:, t] = np.squeeze((y[t] * xi) + (y2 * np.expand_dims(W[:, t], 1))) dV[t, :] = -np.squeeze((y[t] * y) + (y2 * np.expand_dims(V[t, :], 1))) W += (learning_rate * dW) V += (learning_rate * dV) V = np.tril(V) np.fill_diagonal(V, 0.0) W /= np.linalg.norm(W, axis=0).reshape((1, n_components)) print('Final weights: {}'.format(W))
前一个块的输出如下:
Final weights: [[-0.60814345 -0.80365858] [-0.79382715 0.59509065]]
如预期的那样,权重收敛到协方差矩阵的特征向量。 我们还计算最终的协方差矩阵,以检查其值:
import numpy as np Y_comp = np.zeros((Xs.shape[0], n_components)) for i in range(Xs.shape[0]): y_p = np.zeros((n_components, 1)) xi = np.expand_dims(Xs[i], 1) for _ in range(stabilization_cycles): Y_comp[i] = np.squeeze(np.dot(W.T, xi) + np.dot(V.T, y_p)) y_p = y.copy() print('Final covariance matrix: {}'.format(np.cov(Y_comp.T)))
输出如下:
Final covariance matrix: [[28.9963492 0.31487817] [ 0.31487817 12.01606874]]
同样,最终协方差矩阵是去相关的(误差可忽略不计)。 Rubner-Tavan 的网络通常比 Sanger 网络快,这是因为反希伯来语的反馈加快了收敛速度。 因此,当采用这种模型时,它们应该是首选。 但是,调整学习率非常重要,这样可以避免振荡。 我建议从一个较小的值开始,然后稍微增加它,直到迭代次数达到最小值为止。 另外,也可以从较高的学习率入手,以便更快地进行初始校正,并可以通过使用线性(如 Sanger 网络)或指数衰减逐步降低学习率。
无监督的深度信念网络
在本节中,我们将讨论一个非常著名的生成模型,该模型可以在无监督的情况下使用,以执行从预定义的数据生成过程中提取的输入数据集X
的降维。 由于本书没有特定的先决条件,并且数学上的复杂度很高,因此我们将简要介绍这些概念,而无需提供证明,也不会对算法的结构进行深入分析。 在讨论深度信念网络(DBN)之前,有必要介绍另一种模型受限玻尔兹曼机(RBM), 被视为 DBN 的构建块。
受限玻尔兹曼机
在《动态系统的信息处理:谐波理论的基础》中,提出了这个网络作为概率生成模型,也称为 Harmonium。 换句话说,RBM 的目标是学习未知分布(即数据生成过程),以便生成所有可能的样本。 下图显示了通用结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fl7yozbE-1681652675172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/6f0f4654-5cfe-4dc4-b79b-606c14a2ec71.png)]
通用受限玻尔兹曼机的结构
神经元x[i]
是可观察到的(也就是说,它们代表 RBM 必须学习的过程生成的向量),而h[j]
是潜在的(也就是说,它们是隐藏的并且有助于x[i]
假定的值)。 由于没有任何进一步的细节,我们需要说这个模型具有马尔科夫随机场(MRF)的结构,这是由于相同层的神经元之间没有连接(即描述网络的图是二分图)。 MRF 的一个重要特性是可以用吉布斯分布对整个联合概率p(x, h; θ)
进行建模:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0m2YPBH-1681652675172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/c997f7bc-603a-4fd5-8bdf-6ab3fad3a30c.png)]
指数E(x, h, θ)
发挥物理系统能量的作用,在我们的情况下,它等于:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VH6MKW1-1681652675172)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/fec60140-16e2-454b-a5dd-61909bce0d08.png)]
该公式的主要假设是所有神经元都是伯努利分布的(即x[i], h[j] ~ B(0, 1)
) 项b[i]
和c[j]
是可观察和潜在单位的偏差。 给定数据生成过程p_data
,必须优化 RBM,以便p(x; θ)
的可能性最大化。 跳过所有中间步骤(可以在前面的文章中找到),可以证明以下几点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppaSWkHn-1681652675173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/e1cdc672-50a0-425c-8811-603832e1b5dd.png)]
在先前的公式中,$1
是 Sigmoid 函数。 给定这两个表达式,可以得出(省略操作)对数似然率相对于所有可学习变量的梯度:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jd44giQz-1681652675173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/145060cb-3837-4aa0-922f-ec99defbc34a.png)]
很容易理解,所有梯度的第一项都非常容易计算,而所有第二项都需要所有可能的可观测值之和。 这显然是一个棘手的问题,无法以封闭的形式解决。 因此,Hinton(训练受限的玻尔兹曼机器的实用指南》)提出了一种名为对比发散的算法 ,可用于查找近似解。 对这种方法的解释需要了解马尔可夫链(这不是前提条件)。 但是,我们可以概括地说该策略是通过有限(少量)采样步骤(通常,一个步骤就足以获得良好的结果)来计算梯度的近似值。 这种方法可以非常有效地训练 RBM,并使深层信念网络易于使用并且非常有效。
深度信念网络
DBN 是基于 RBM 的堆叠模型。 下图显示了通用结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pTBD8Klu-1681652675173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/283459b4-8a2a-418c-98a0-128affb35d4b.png)]
通用 DBN 的结构
第一层包含可见单元,其余所有单元都是潜在单元。 在无监督的情况下,目标是学习未知分布,找出样本的内部表示。 实际上,当潜在单元的数量少于输入单元的数量时,模型将学习如何使用低维子空间对分布进行编码。 Hinton 和 Osindero(在《深层信念网络的快速学习算法》)提出了逐步贪婪训练程序(这是通常执行的程序)。 每对层都被认为是 RBM,并使用对比发散算法进行训练。 一旦对 RBM 进行了训练,则隐藏层将成为后续 RBM 的可观察层,并且该过程将一直持续到最后一个。 因此,DBN 开发了一系列内部表示形式(这就是为什么将其定义为深度网络的原因),其中每个级别都接受了较低级别特征的训练。 该过程与可变自编码器并无不同。 但是,在这种情况下,模型的结构更加僵化(例如,无法使用卷积单元)。 而且,输出不是输入的重建,而是内部表示。 因此,考虑上一节中讨论的公式,如果有必要反转过程(即给定内部表示,获得输入),则必须使用以下公式从最顶层进行采样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FyuvwdR6-1681652675173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/ee120b1c-ad77-481c-9f39-0d90a1f6c72c.png)]
当然,必须向后重复此过程,直到到达实际的输入层为止。 DBN 非常强大(例如,在天体物理学领域有一些科学应用),即使它们的结构不像其他更新的模型那样灵活。 但是,复杂度通常较高,因此,我总是建议从较小的模型开始,仅在最终精度不足以满足特定目的的情况下才增加层和/或神经元的数量。
无监督 DBN 的示例
在此示例中,我们要使用 DBN 来查找 MNIST 数据集的低维表示。 由于这些模型的复杂性很容易增加,我们将限制该过程为 500 个随机样本。 该实现基于 Deep-belief-network 包,该包同时支持 NumPy 和 TensorFlow。 在前一种情况下,必须从dbn
包中导入类(其名称保持不变),而在后一种情况下,必须是dbn.tensorflow
包。 在此示例中,我们将使用要求较少的 NumPy 版本,但也请读者检查 TensorFlow 版本。
让我们从加载和规范化数据集开始,如下所示:
import numpy as np from sklearn.datasets import load_digits from sklearn.utils import shuffle nb_samples = 500 digits = load_digits() X_train = digits['data'] / np.max(digits['data']) Y_train = digits['target'] X_train, Y_train = shuffle(X_train, Y_train, random_state=1000) X_train = X_train[0:nb_samples] Y_train = Y_train[0:nb_samples]
现在,我们可以使用以下结构实例化UnsupervisedDBN
类:
- 64 个输入神经元(从数据集中隐式检测到)
- 32 个 Sigmoid 神经元
- 32 个 Sigmoid 神经元
- 16 个 Sigmoid 神经元
因此,最后一个表示形式由 16 个值(原始大小的四分之一)组成。 我们将η = 0.025
的学习率设置为每批 16 个样本(当然,我们邀请您检查其他配置,以最大程度地减少重构误差)。 以下代码段初始化并训练模型:
from dbn import UnsupervisedDBN unsupervised_dbn = UnsupervisedDBN(hidden_layers_structure=[32, 32, 16], learning_rate_rbm=0.025, n_epochs_rbm=500, batch_size=16, activation_function='sigmoid') X_dbn = unsupervised_dbn.fit_transform(X_train)
在训练过程的最后,我们可以将分布投影到二维空间上,然后分析分布。 像往常一样,我们将采用 t-SNE 算法,该算法可确保找到最相似的低维分布:
from sklearn.manifold import TSNE tsne = TSNE(n_components=2, perplexity=10, random_state=1000) X_tsne = tsne.fit_transform(X_dbn)
下图显示了投影样本的图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oL3jYTWn-1681652675173)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/359d9068-dc6b-45f6-bf32-e6c4ff19cc1f.png)]
无监督 DBN 输出表示的 t-SNE 图
如您所见,大多数块都具有很强的凝聚力,这表明数字的特殊属性已在较低维空间中成功表示。 在某些情况下,相同的数字组被划分为更多的群集,但是总的来说,噪点(隔离点)的数量非常少。 例如,包含数字2
的组用符号x
表示。 大部分样本在0 < x[0] < 30
,x[1] < -40
范围内; 但是,一个子组也位于-10 < x[1] < 10
范围内。 如果我们检查这个小集群的邻居,它们是由代表数字8
(用正方形表示)的样本组成的。 容易理解,某些格式错误的二进制与格式错误的八进制非常相似,这证明了拆分原始群集的合理性。 从统计角度来看,所解释的方差可能会产生不同的影响。 在某些情况下,只有几个组件足以确定类的特殊特征,但这通常是不正确的。 当属于不同类别的样本显示出相似性时,只能由于次级成分的差异而做出区分。 在处理包含几乎(甚至部分)重叠样本的数据集时,这一考虑非常重要。 进行降维时,数据科学家的主要任务不是检查总体解释的方差,而是了解是否存在受降维不利影响的区域。 在这种情况下,可以定义多个检测规则(例如,当样本x[i] ∈ R[1]
或x[i] ∈ R[4] → x[i]
具有y[k]
标签)或尝试避免使用模型来创建此细分(在这种情况下,我们邀请您测试更复杂的 DBN 和更高维的输出表示形式)。
总结
在本章中,我们讨论了用于解决无监督任务的一些非常常见的神经模型。 自编码器使您可以查找数据集的低维表示形式,而没有对其复杂性的特定限制。 特别是,深度卷积网络的使用有助于检测和学习高级和低级几何特征,当内部代码也比原始维数短得多时,这可以导致非常准确的重构。 我们还讨论了如何为自编码器增加稀疏性,以及如何使用这些模型对样本进行降噪。 标准自编码器的一个稍有不同的变体是变分自编码器,它是一种生成模型,可以提高学习从中得出数据集的数据生成过程的能力。
Sanger 和 Rubner-Tavan 的网络是神经模型,能够在不进行任何统计预处理的情况下提取数据集的前k
主成分。 它们还具有以在线方式自然工作的优势(尽管标准 PCA 经常需要整个数据集,即使存在表现稍逊于脱机算法的增量变体),也可以按降序提取组件。 我们讨论的最后一个模型是在无监督的情况下的 DBN。 我们描述了其构建基块 RBM 的生成属性,然后分析了此类模型如何学习数据生成过程的内部(通常为低维)表示。
在下一章中,我们将讨论其他神经模型:生成对抗网络(GAN)和自组织映射(SOM)。 前者可以学习输入分布并从中获取新样本,而后者则基于大脑某些特定区域的功能,并训练它们的单位以接受特定的输入模式。
问题
- 在自编码器中,编码器和解码器都必须在结构上对称。 它是否正确?
- 给定数据集
X
及其转换Y
,根据自编码器产生的代码,可以在Y
找到X
中包含的所有信息。 它是否正确? - 代码
z[i] ∈ (0, 1)^128
的sum(z[i]) = 36
。 稀疏吗? - 如果
std(z[i]) = 0.03
,代码是否稀疏? - Sanger 网络需要协方差矩阵的列作为输入向量。 它是否正确?
- 我们如何确定 Rubner-Tavan 网络提取的每个成分的重要性?
- 给定一个随机向量,
h[i] ∈ R^m
(m
是 DBN 的输出维数),是否可以确定最可能对应的输入样本?
进一步阅读
Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion, Vincent, P., Larochelle, H., Lajoie, I., Bengio, Y., and Manzagol, P., Journal of Machine Learning Research 11, 2010
Sparse Autoencoder, CS294A, Ng, A., Stanford University
Auto-Encoding Variational Bayes, Kingma. D. P. and Welling, M., arXiv:1312.6114 [stat.ML]
Theoretical Neuroscience, Dayan, P. and Abbott, L. F., The MIT Press, 2005
Optimal Unsupervised Learning in a Single-Layer Linear Feedforward Neural Network, Neural Networks, Sanger, T. D., 2, 1989
A Self-Organizing Network for Principal-Components Analysis, Europhysics Letters, Rubner, J. and Tavan, P., 10(7), 1989
Information Processing in Dynamical Systems: Foundations of Harmony Theory, Parallel Distributed Processing, Smolensky, Paul, Vol 1, The MIT Press, 1986
A Practical Guide to Training Restricted Boltzmann Machines, Hinton, G., Dept. Computer Science, University of Toronto, 2010
A Fast Learning Algorithm for Deep Belief Nets, Hinton G. E., Osindero S., and Teh Y. W., Neural Computation, 18/7, 2005
Machine Learning Algorithms, Second Edition, Bonaccorso, G., Packt, 2018
Mastering Machine Learning Algorithms, Bonaccorso, G., Packt, 2018
九、生成对抗网络和 SOM
在本章中,我们将结束无监督学习的整个过程,讨论一些可以用于执行数据生成过程的非常流行的神经模型,以及可以从中提取的新样本。 此外,我们将分析自组织映射的功能,该功能可以调整其结构,以便特定单元可以响应不同的输入模式。
特别是,我们将讨论以下主题:
- 生成对抗网络(GAN)
- 深度卷积 GAN(DCGAN)
- Wasserstein GAN(WGAN)
- 自组织映射(SOM)
技术要求
本章将介绍的代码需要以下内容:
- Python3.5+(强烈建议使用 Anaconda 发行版)
- 库如下:
- SciPy 0.19+
- NumPy 1.10+
- Scikit-Learn 0.20+
- Pandas 0.22+
- Matplotlib 2.0+
- Seaborn 0.9+
- TensorFlow 1.5+
- Keras 2+(仅适用于数据集工具函数)
生成对抗网络
这些生成模型由 Goodfellow 和其他研究人员提出(在《生成对抗网络》中),以利用对抗训练的功能以及深度神经网络的灵活性。 无需过多的技术细节,我们就可以将对抗训练的概念作为一种基于博弈论的技术进行介绍,其目标是优化两个相互竞争的智能体。 当一个特工试图欺骗其对手时,另一名特工必须学习如何区分正确的输入和伪造的输入。 特别是,GAN 是一个模型,它分为两个定义明确的组件:
- 生成器
- 判别器(也称为评论家)
让我们首先假设有一个数据生成过程,p_data
,以及一个数据集X
,该数据集是从m
样本中提取的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WmCQmRw2-1681652675174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/51747182-bf27-4942-aa5c-7bc1d719034b.png)]
为简单起见,假定数据集具有一个维度; 但是,这不是约束也不是限制。 生成器是一个参数化函数(通常使用神经网络),该函数馈入有噪声的样本,并提供n
维向量作为输出:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXrrZHrf-1681652675174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/4579d40e-3f19-4f5b-8f1b-70ea0fe5e3be.png)]
换句话说,生成器是样本x ∈ R^n
上均匀分布到另一分布p[g](x)
的变换。 GAN 的主要目标如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvX72J74-1681652675174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/956dc503-ac1c-451e-89a1-2f47d07bb5d1.png)]
但是,与通过直接训练整个模型来实现这一目标的自编码器相反,在 GAN 中,目标是通过在生成器和判别器之间进行的游戏来实现的,这是另一个需要采样的参数化函数,x[i] ∈ R^n
,并返回概率:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kbmt7jfW-1681652675174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/4bd5bc8f-ede6-45e8-80ea-543706b24852.png)]
判别器的作用是区分从p_data
(返回大概率)提取的样本与由g(z; θ[g])
(返回低概率)。 但是,由于生成器的目标是变得越来越有能力复制p_data
,因此其作用是学习如何用数据生成过程的几乎完美复制品中的样本来欺骗判别器。 因此,考虑到判别器,目标是最大化以下条件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5SM1APRW-1681652675174)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/6776cf30-d26d-4c1b-aa66-65165ebac3a2.png)]
但是,这是 minimax 游戏,这意味着两个对手A
和B
都必须尝试最小化(A
) 和最大化(B
),这是相同的目标。 在这种情况下,生成器的目标是最小化先前的双重成本函数的第二项:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBfDL6RA-1681652675175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/1c69083f-4200-4599-a7af-1b594ab3bc20.png)]
实际上,当两个智能体都成功地优化了目标时,判别器将能够区分从p_data
提取的样本和异常值,并且生成器将能够输出属于p_data
的合成样本。 但是,必须明确的是,可以通过使用单个目标来表达问题,并且训练过程的目标是找出最佳参数集,θ = {θ[d], θ[g]}
,因此判别器将其最大化,而生成器将其最小化。 必须同时优化两个智能体,但是实际上,过程是交替的(例如,生成器,判别器,生成器等)。 目标可以用更紧凑的形式表示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4aZ9dNhf-1681652675175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/304d8dd9-8212-4b27-a550-71c85664a763.png)]
因此,通过解决以下问题可以达到最佳效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8fJXimI-1681652675175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/3ef1bf38-3d09-434a-993d-0a410667c1fe.png)]
根据博弈论,这是一个不合作的博弈,它承认纳什均衡点。 当满足这种条件时,如果我们假设双方都知道对手的策略,那么他们就没有理由再改变自己的策略了。 在 GAN 的情况下,这种情况意味着一旦达到平衡(甚至只是理论上),生成器就可以继续输出样本,并确保它们不会被判别器误分类。 同时,判别器没有理由改变其策略,因为它可以完美地区分p_data
和任何其他分布。 从动态角度来看,两个组件的训练速度都是不对称的。 尽管生成器通常需要更多的迭代,但判别器可以非常迅速地收敛。 但是,这样的过早收敛对于整体表现可能非常危险。 实际上,由于判别器提供的反馈,生成器也达到了最佳状态。 不幸的是,当梯度很小时,这种贡献可以忽略不计,其明显的结果是,生成器错过了提高其输出更好样本能力的机会(例如,当样本是图像时,它们的质量可能会保持非常低,甚至具有复杂的架构)。 这种情况并不取决于生成器固有的容量不足,而是取决于判别器收敛(或非常接近收敛)后开始应用的有限次校正。 在实践中,由于没有特定的规则,唯一有效的建议是在训练过程中检查两个损失函数。 如果判别器的损失下降得太快,而生成器的损失仍然很大,那么通常最好在单个判别器步骤中插入更多的生成器训练步骤。
分析 GAN
假设我们有一个 GAN,该 GAN 已通过使用从p_data(x)
中提取的数据集X
进行了适当的训练。 Goodfellow 等人证明,给定生成器分布p[g](x)
,最佳判别器如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LeFZKmqb-1681652675175)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/bf275808-f597-4c3c-a017-6ecaa9f12a79.png)]
可以使用最佳判别器来重写全局目标:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7PJN0A2q-1681652675176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/fb61b945-753c-4d3a-ae24-b25d49b47952.png)]
现在,我们可以扩展前面的表达式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hOIbBLi-1681652675176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/ecbc89dd-1468-4030-9b3b-f8afd8001855.png)]
现在,让我们考虑两个分布a
和b
之间的 Kullback-Leibler 散度:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZLciP87o-1681652675176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/9e4f728c-21ba-4c78-951a-099df4986119.png)]
考虑前面的表达式,经过一些简单的操作,很容易证明以下相等:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTvIwdN1-1681652675176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/ea76a19a-d431-4d64-8ab4-2353158ec476.png)]
因此,目标可以表示为数据生成过程和生成器分布之间的 Jensen-Shannon 散度的函数。 与 Kullback-Leibler 散度的主要区别在于0 ≤ D[JS](p_data || p[g]) ≤ log(2)
,并且是对称的。 这种重新定义并不奇怪,因为 GAN 的真正目标是成为一个能够成功复制p_data
的生成模型,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-42e4iaj7-1681652675176)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/d5d8548e-abc4-4567-9a42-2def53203f69.png)]
GAN 的目标是将生成的模型分布朝p_data
方向移动,以尝试使重叠最大化
初始分布通常与目标分布完全不同; 因此,GAN 必须同时调整形状并将其移向p_data
。 重叠完成后,Jensen-Shannon 散度达到最小值,并且优化完成。 但是,正如我们将在下一节中讨论的那样,由于 Jensen-Shannon 散度的特性,此过程并不总是如此平稳地运行,并且 GAN 可以达到次理想的极小值,离期望的最终配置很远。
模式崩溃
给定一个概率分布,最常出现的值(在离散情况下)或对应于概率密度函数最大值的值(在连续情况下)称为模式。 如果考虑后一种情况,则其 PDF 具有单个最大值的分布称为单峰。 当有两个局部极大值时,称为双峰,等(通常,当存在多个众数时,分布简称为多峰)。 以下屏幕快照显示了两个示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ldLHdLGn-1681652675177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/a8df35ed-d5d1-4e6f-b381-f22b67ef545a.png)]
单峰(左)和双峰(右)分布的示例
当处理复杂的数据集时,我们无法轻松地估计模式数量。 但是,可以合理地假设数据生成过程是多模式的。 有时,当样本基于共同的结构时,可以有一个主导模式和几个次要模式。 但是通常,如果样本在结构上不同,则具有单一模式的可能性非常低(当然,如果对相同基本元素进行少量修改,则可能具有单一模式,但这不是要考虑的有效情况) 帐户)。
现在,让我们想象一下我们正在处理人脸图片的多模式分布(例如下一节将要讨论的示例中的人脸图片)。 模式的内容是什么? 很难精确地回答这个问题,但是很容易理解,对应于最大数据生成过程的人脸应该包含数据集中最常见的元素(例如,如果 80% 的人留着胡须 ,我们可以合理地假设该模式将包含它)。
我们在使用 GAN 时面临的最著名,最棘手的问题之一就是模式崩溃,它涉及到次优的最终配置,其中生成器冻结在某个模式附近,并不断提供输出。 发生这种情况的原因非常难以分析(实际上,只有理论),但是我们可以理解如果重新考虑 minimax 游戏,为什么会发生这种情况。 当我们要训练两个不同的分量时,即使保证了纳什均衡,在几次迭代之后,对于最常见的模式,判别器也会变得非常有选择性。 当然,当训练生成器以欺骗判别器时,实现此目标的最简单方法是简单地避免所有采样远离模式。 这种行为增加了判别器的选择性,并创建了一个反馈过程,使 GAN 陷入只有数据生成过程只有一小部分区域的状态。
在梯度方面,判别器提供的用于优化生成器的信息很快变得非常稀缺,因为最常见的样本不需要任何调整。 另一方面,当生成器开始避免所有p(x)
不接近最大值的样本时,它们不会将判别器暴露给新的,可能有效的样本,因此梯度将保持很小,直到消失为零。 不幸的是,没有可以用来避免此问题的全局策略,但是在本章中,我们将讨论一种建议的方法,以减轻模式崩溃(WGAN)的风险。 特别是,我们将把注意力集中在 Jensen-Shannon 发散的局限性上,在某些情况下,由于没有大的梯度,这可能导致 GAN 达到次优配置。 在本简介中,重要的是,不熟悉这些模型的读者应意识到风险,并能够在发生模式崩溃时识别出它。
此时,我们可以继续进行实际操作,并使用 TensorFlow 建模真实的 GAN。
深度卷积 GAN 的示例
现在,我们可以基于《使用深度卷积生成对抗网络的无监督表示学习》和 Olivetti faces 数据集,该数据集足够小以允许进行快速训练。
让我们首先加载数据集并标准化范围(-1, 1)
中的值,如下所示:
from sklearn.datasets import fetch_olivetti_faces faces = fetch_olivetti_faces(shuffle=True, random_state=1000) X_train = faces['images'] X_train = (2.0 * X_train) - 1.0 width = X_train.shape[1] height = X_train.shape[2]
以下屏幕快照显示了一些示例面孔:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G60JFzTZ-1681652675177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/49bdaa5a-37a8-4fce-b39c-fa8b9ec22294.png)]
从 Olivetti 人脸数据集中抽取的人脸样本
即使所有人脸的结构都相似,但眼睛(戴或不戴眼镜),鼻子和嘴巴的形状也存在细微差别。 而且,有些人留着胡须,表情却大不相同(微笑,严肃,盯着相机远处的东西,等等)。 因此,我们需要期待多峰分布,可能具有对应于平均人脸结构的主要模式,以及对应于具有特定,共同特征的子集的其他几种模式。
此时,我们可以定义主要常量,如下所示:
nb_samples = 400 code_length = 512 nb_epochs = 500 batch_size = 50 nb_iterations = int(nb_samples / batch_size)
有400
64×64 灰度样本(每个样本对应 4,096 个分量)。 在此示例中,我们选择采用具有512
分量的噪声代码向量,并以50
个样本批量训练500
周期的模型。 这样的值不是基于黄金规则的,因为(尤其是对于 GAN)几乎不可能知道哪个设置会产生最佳结果。 因此,与往常一样,我强烈建议在做出决定之前检查不同的超参数集。
当训练过程不太长时,可以使用一组统一采样的超参数(例如批大小属于{20, 50, 100, 200}
)检查生成器和判别器的平均损失。 例如,如果某个最佳值似乎在范围(50, 100)
内,那么一个好的策略是提取一些随机值并重新训练模型。 可以重复进行此过程,直到采样值之间的差异可以忽略不计为止。 当然,考虑到这些模型的复杂性,只有使用专用硬件(即多个 GPU 或 TPU)才能进行彻底的搜索。 因此,另一个建议是从经过测试的配置开始(即使上下文不同),并进行小的修改,以便针对特定任务优化它们。 在此示例中,我们根据原始论文设置了许多值,但是我邀请读者在自定义更改后重新运行代码并观察差异。
现在,我们可以基于以下结构为生成器定义 DAG:
- 具有 1,024 个
4×4
过滤器的 2D 卷积,步幅为(1, 1)
,有效填充和线性输出 - 批量规范化和 LReLU 激活(当输入值为负时,表现更高;实际上,当
x < 0
时,标准 ReLU 的梯度为零,而 LReLU 的常数较小) 允许稍微修改的梯度) - 带有
(2, 2)
步幅,相同填充和线性输出的 512 个4×4
过滤器的 2D 卷积 - 批量规范化和 LReLU 激活
- 256 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 128 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 1 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及双曲正切输出
以下代码段显示了生成器的代码:
import tensorflow as tf def generator(z, is_training=True): with tf.variable_scope('generator'): conv_0 = tf.layers.conv2d_transpose(inputs=z, filters=1024, kernel_size=(4, 4), padding='valid') b_conv_0 = tf.layers.batch_normalization(inputs=conv_0, training=is_training) conv_1 = tf.layers.conv2d_transpose(inputs=tf.nn.leaky_relu(b_conv_0), filters=512, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_1 = tf.layers.batch_normalization(inputs=conv_1, training=is_training) conv_2 = tf.layers.conv2d_transpose(inputs=tf.nn.leaky_relu(b_conv_1), filters=256, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_2 = tf.layers.batch_normalization(inputs=conv_2, training=is_training) conv_3 = tf.layers.conv2d_transpose(inputs=tf.nn.leaky_relu(b_conv_2), filters=128, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_3 = tf.layers.batch_normalization(inputs=conv_3, training=is_training) conv_4 = tf.layers.conv2d_transpose(inputs=tf.nn.leaky_relu(b_conv_3), filters=1, kernel_size=(4, 4), strides=(2, 2), padding='same') return tf.nn.tanh(conv_4)
该代码很简单,但是有助于阐明对变量范围上下文的需要(通过命令tf.variable_scope('generator')
定义)。 由于我们需要以其他方式训练模型,因此在优化生成器时,仅必须更新其变量。 因此,我们在命名范围内定义了所有层,从而允许强制优化器仅工作所有可训练变量的子集。
判别器的 DAG 基于以下对称结构:
- 具有
(2, 2)
步幅的 1284×4
个过滤器的 2D 卷积,相同填充,以及 LReLU 输出 - 256 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 带有 512 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 1,024
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 1 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,有效填充,以及线性输出(预期输出为 sigmoid,可以表示一个概率,但是我们将直接在损失函数内部执行此变换)
判别器的代码为,如下所示:
import tensorflow as tf def discriminator(x, is_training=True, reuse_variables=True): with tf.variable_scope('discriminator', reuse=reuse_variables): conv_0 = tf.layers.conv2d(inputs=x, filters=128, kernel_size=(4, 4), strides=(2, 2), padding='same') conv_1 = tf.layers.conv2d(inputs=tf.nn.leaky_relu(conv_0), filters=256, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_1 = tf.layers.batch_normalization(inputs=conv_1, training=is_training) conv_2 = tf.layers.conv2d(inputs=tf.nn.leaky_relu(b_conv_1), filters=512, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_2 = tf.layers.batch_normalization(inputs=conv_2, training=is_training) conv_3 = tf.layers.conv2d(inputs=tf.nn.leaky_relu(b_conv_2), filters=1024, kernel_size=(4, 4), strides=(2, 2), padding='same') b_conv_3 = tf.layers.batch_normalization(inputs=conv_3, training=is_training) conv_4 = tf.layers.conv2d(inputs=tf.nn.leaky_relu(b_conv_3), filters=1, kernel_size=(4, 4), padding='valid') return conv_4
同样,在这种情况下,我们需要声明一个专用的变量作用域。 但是,由于判别器在两个不同的上下文中使用(即,对真实样本和生成样本的评估),我们需要在第二个声明中要求重用变量。 如果未设置此类标志,则对函数的每次调用都会产生新的变量集,对应于不同的标识符。
声明了两个主要组件后,我们可以初始化图并为 GAN 设置整个 DAG,如下所示:
import tensorflow as tf graph = tf.Graph() with graph.as_default(): input_x = tf.placeholder(tf.float32, shape=(None, width, height, 1)) input_z = tf.placeholder(tf.float32, shape=(None, code_length)) is_training = tf.placeholder(tf.bool) gen = generator(z=tf.reshape(input_z, (-1, 1, 1, code_length)), is_training=is_training) discr_1_l = discriminator(x=input_x, is_training=is_training, reuse_variables=False) discr_2_l = discriminator(x=gen, is_training=is_training, reuse_variables=True) loss_d_1 = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.ones_like(discr_1_l), logits=discr_1_l)) loss_d_2 = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.zeros_like(discr_2_l), logits=discr_2_l)) loss_d = loss_d_1 + loss_d_2 loss_g = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits(labels=tf.ones_like(discr_2_l), logits=discr_2_l)) variables_g = [variable for variable in tf.trainable_variables() if variable.name.startswith('generator')] variables_d = [variable for variable in tf.trainable_variables() if variable.name.startswith('discriminator')] with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)): training_step_d = tf.train.AdamOptimizer(0.0001, beta1=0.5).minimize(loss=loss_d, var_list=variables_d) training_step_g = tf.train.AdamOptimizer(0.0005, beta1=0.5).minimize(loss=loss_g, var_list=variables_g)
第一块包含占位符的声明。 为了清楚起见,虽然input_x
和input_z
的目的很容易理解,但is_training
可能不太明显。 此布尔值标志的目的是允许在生产阶段禁用批量规范化(必须仅在训练阶段有效)。 下一步包括声明生成器和两个判别器(它们在形式上是相同的,因为变量是共享的,但是其中一个被提供了真实的样本,而另一个必须评估生成器的输出)。 然后,是时候定义损失函数了,它是基于一种可以加快计算速度并增加数值稳定性的技巧。
函数tf.nn.sigmoid_cross_entropy_with_logits()
接受对率(这就是为什么我们没有将 Sigmoid 变换直接应用于判别器输出的原因),并允许我们执行以下向量计算:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dSbSUD5Z-1681652675177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/cb4a2f99-5422-4c6f-bc83-588e81037426.png)]
因此,由于loss_d_1
是真实样本的损失函数,因此我们使用运算符tf.ones_like()
将所有标签设置为 1; 因此,S 形交叉熵的第二项变为零,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WhiI829t-1681652675177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/cc9c07fc-72da-4fae-b60c-9c17149f81cc.png)]
相反,loss_d_2
恰好需要 Sigmoid 交叉熵的第二项。 因此,我们将所有标签设置为零,以获得损失函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vFPVPHyN-1681652675177)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/4bebedd1-9438-42e7-aae9-c18aa72d2a49.png)]
相同的概念适用于生成器损失函数。 下一步需要定义两个 Adam 优化器。 如前所述,我们需要隔离变量以进行隔行训练。 因此,minimize()
函数现在被提供了损失和必须更新的变量集。 TensorFlow 官方文档中建议使用上下文声明tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
,只要采用批量规范化,其目标是仅在计算均值和方差之后才允许执行训练步骤(有关此技术的更多详细信息,请检查原始论文:《批量规范化:通过减少内部协变量移位》。
此时,我们可以创建一个会话并初始化所有变量,如下所示:
import tensorflow as tf session = tf.InteractiveSession(graph=graph) tf.global_variables_initializer().run()
一旦一切准备就绪,就可以开始训练过程。 以下代码片段显示了对判别器和生成器进行交替训练的代码:
import numpy as np samples_range = np.arange(nb_samples) for e in range(nb_epochs): d_losses = [] g_losses = [] for i in range(nb_iterations): Xi = np.random.choice(samples_range, size=batch_size) X = np.expand_dims(X_train[Xi], axis=3) Z = np.random.uniform(-1.0, 1.0, size=(batch_size, code_length)).astype(np.float32) _, d_loss = session.run([training_step_d, loss_d], feed_dict={ input_x: X, input_z: Z, is_training: True }) d_losses.append(d_loss) Z = np.random.uniform(-1.0, 1.0, size=(batch_size, code_length)).astype(np.float32) _, g_loss = session.run([training_step_g, loss_g], feed_dict={ input_x: X, input_z: Z, is_training: True }) g_losses.append(g_loss) print('Epoch {}) Avg. discriminator loss: {} - Avg. generator loss: {}'.format(e + 1, np.mean(d_losses), np.mean(g_losses)))
在这两个步骤中,我们为网络提供一批真实的图像(在生成器优化期间不会使用)和统一采样的代码Z
,其中每个分量为z[i] ~ U(-1, 1)
。 为了减轻模式崩溃的风险,我们将在每次迭代开始时对集合进行洗牌。 这不是一个可靠的方法,但是至少可以确保避免可能导致 GAN 达到次优配置的相互关系。
在训练过程结束时,我们可以生成一些样本面孔,如下所示:
import numpy as np Z = np.random.uniform(-1.0, 1.0, size=(20, code_length)).astype(np.float32) Ys = session.run([gen], feed_dict={ input_z: Z, is_training: False }) Ys = np.squeeze((Ys[0] + 1.0) * 0.5 * 255.0).astype(np.uint8)
结果显示在以下屏幕截图中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zwOvflNg-1681652675178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/29d10031-02b1-448c-b339-0163f2d2bf5e.png)]
DCGAN 生成的样本人脸
可以看到,质量非常高,较长的训练阶段会有所帮助(以及更深的超参数搜索)。 但是,GAN 已成功学习了如何通过使用同一组属性来生成新面孔。 表达式和视觉元素(例如,眼睛的形状,眼镜的存在等)都重新应用于不同的模型,以便产生从相同原始数据生成过程中绘制的潜在面孔。 例如,第七名和第八名基于具有修改属性的一个人。 原始图片如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GAEdBdTP-1681652675178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/eeea0b30-618e-4267-9766-78baabc05fac.png)]
对应于 Olivetti 人之一的原始图片
嘴的结构对于两个生成的样本都是相同的,但是从第二个样本来看,我们可以确认已经从其他样本中提取了许多元素(鼻子,眼睛,前额和方向),从而产生了不存在的人。 即使模型正常工作,也会出现部分模式崩溃,因为某些面孔(具有其相对属性,例如眼镜)比其他面孔更常见。 相反,一些女性面孔(数据集中的少数)已与男性属性合并,从而产生了样本,例如包含所生成样本的图像的第一行的第二个或底部行的第八个。 作为练习,我邀请读者使用不同的参数和其他数据集(包含灰度和 RGB 图像,例如 Cifar-10 或 STL-10)来重新训练模型。
The screenshots that are shown in this and other examples in this chapter are often based on random generations; therefore, in order to increase the reproducibility, I suggest setting both the NumPy and TensorFlow random seed equal to 1000
. The commands are: np.random.seed(1000)
and tf.set_random_seed(1000)
.
Wasserstein GAN
给定概率分布p(x)
,集合D[p] = {x: p(x) > 0}
被称为支撑 。 如果p(x)
和q(x)
的两个分布具有脱节的支撑(即D[p] ∩ D[q] = {∅}
),詹森-香农散度等于log(2)
。 这意味着梯度为零,并且无法进行任何校正。 在涉及 GAN 的一般情况下, p[g](x)
和p_data
完全不可能重叠( 但是,您可以期望有最小的重叠); 因此,梯度很小,权重的更新也很小。 这样的问题可能会阻止训练过程,并使 GAN 陷入无法逃避的次优配置状态。 因此, Arjovsky,Chintala 和 Bottou (在《Wasserstein GAN》中)基于称为 Wasserstein 距离的更稳健的差异度量,提出了一个略有不同的模型。(或“地球移动者”的距离):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cRpyLPkk-1681652675178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/cf700f07-35ed-4e38-80db-a046bbe07d21.png)]
为了理解前面的公式,必须说∏(p_data, p[g])
是包含所有可能的联合分布的集合。 数据生成过程和生成器分布。 因此,Wasserstein 距离等于范数||x - y||
的期望值的最小值。假设一对(x, y)
是分布μ ~ ∏(p_data, p[g])
。 即使这个概念很简单,这种定义也不是很直观,并且可以通过考虑两个二维 Blob(其距离是两个最近点之间的距离)来概括。 显然,支撑支点不相交的问题已被完全克服,此外,度量也与实际分布距离成比例。 不幸的是,我们没有使用有限集。 因此,Wasserstein 距离的计算可能非常低效,并且几乎不可能用于现实生活中的任务。 但是, Kantorovich-Rubinstein 定理(由于超出了本书的范围,因此未进行全面分析)使我们可以通过使用特殊的支持函数f(x)
来简化表达式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lx4BbZ5a-1681652675178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/6409c4e2-844b-44d2-bd22-655dd88d2e4b.png)]
该定理施加的主要约束是f(x)
必须是 L-Lipschitz 函数,也就是说,给定非负常数L
,则适用 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBbYXLSq-1681652675178)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/a97ddbd4-a379-4fc0-aad7-4de7a552abcd.png)]
考虑使用神经网络参数化的函数f(·)
,全局目标变为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pXrcxE8-1681652675179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/ac4ed2f2-c377-4788-9950-1e7768520a25.png)]
在这种特定情况下,判别器通常被称为批判者,因此f(x; θ[c])
扮演着这个角色。 由于这样的函数必须是 L-Lipschitz,因此作者建议在应用校正后就剪切所有变量$1[$2]
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a2wrufT9-1681652675179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/c6e68853-8d2d-4ea5-9670-8fe1ad4803ac.png)]
该方法不是非常有效,因为它会减慢学习过程。 但是,当函数执行一组有限变量的操作时,假定输出始终受常数约束,并且可以应用 Kantorovich-Rubinstein 定理。 当然,由于参数化通常需要许多变量(有时数百万或更多),因此裁剪常数应保持很小(例如 0.01)。 此外,由于剪辑的存在会影响批判者的训练速度,因此也有必要在每次迭代中增加批判者训练步骤的数量(例如,批判者 5 次,生成器 1 次,依此类推。 )。
将 DCGAN 转变为 WGAN
在此示例中,我们将使用 Fashion MNIST 数据集(由 Keras 直接提供)基于 Wasserstein 距离实现 DCGAN。 该集合由 60,000 张 28×28 灰度的衣服图像组成,由 Zalando 引入,以替代标准 MNIST 数据集,该数据集的类别太容易被许多分类器分离。 考虑到此类网络所需的训练时间,我们决定将过程限制为 5,000 个样本,但是拥有足够资源的读者可以选择增加或消除此限制。
第一步包括加载,切片和规范化数据集(在(-1, 1)
范围内),如下所示:
import numpy as np from keras.datasets import fashion_mnist nb_samples = 5000 (X_train, _), (_, _) = fashion_mnist.load_data() X_train = X_train.astype(np.float32)[0:nb_samples] / 255.0 X_train = (2.0 * X_train) - 1.0 width = X_train.shape[1] height = X_train.shape[2]
以下屏幕快照显示了一些示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Otxl1BaA-1681652675179)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-unsup-learn-py/img/aac13c86-1214-4c02-9078-f6b3fd5ecbf3.png)]
从 Fashion MNIST 数据集中提取的样本
现在,我们可以基于 DCGAN 的同一层定义生成器 DAG,如下所示:
- 具有 1,024 个
4×4
过滤器的 2D 卷积,步幅为(1, 1)
,有效填充,以及线性输出 - 批量规范化和 LReLU 激活
- 带有 512 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 256 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 128 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及线性输出 - 批量规范化和 LReLU 激活
- 具有 1 个
4×4
过滤器的 2D 卷积,步幅为(2, 2)
,相同填充,以及双曲正切输出
Python 无监督学习实用指南:6~10(5)https://developer.aliyun.com/article/1426890