【翻译】Sklearn与TensorFlow机器学习实用指南 —— 第15章 自编码器

简介: 关联权重 当自编码器整齐地对称时,就像我们刚刚构建的那样,一种常用技术是将解码器层的权重与编码器层的权重相关联。 这样减少了模型中的权重数量,加快了训练速度,并限制了过度拟合的风险。 不幸的是,使用fully_connected()函数在 TensorFlow 中实现相关权重有点麻烦;手动定义层实际上更容易。

本文来自云栖社区官方钉群“Python技术进阶”,了解相关信息可以关注“Python技术进阶”。

关联权重

当自编码器整齐地对称时,就像我们刚刚构建的那样,一种常用技术是将解码器层的权重与编码器层的权重相关联。 这样减少了模型中的权重数量,加快了训练速度,并限制了过度拟合的风险。

image

不幸的是,使用fully_connected()函数在 TensorFlow 中实现相关权重有点麻烦;手动定义层实际上更容易。 代码结尾明显更加冗长:

activation = tf.nn.elu
regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
initializer = tf.contrib.layers.variance_scaling_initializer()

X = tf.placeholder(tf.float32, shape=[None, n_inputs])

weights1_init = initializer([n_inputs, n_hidden1])
weights2_init = initializer([n_hidden1, n_hidden2])

weights1 = tf.Variable(weights1_init, dtype=tf.float32, name="weights1")
weights2 = tf.Variable(weights2_init, dtype=tf.float32, name="weights2")
weights3 = tf.transpose(weights2, name="weights3") # tied weights
weights4 = tf.transpose(weights1, name="weights4") # tied weights

biases1 = tf.Variable(tf.zeros(n_hidden1), name="biases1")
biases2 = tf.Variable(tf.zeros(n_hidden2), name="biases2")
biases3 = tf.Variable(tf.zeros(n_hidden3), name="biases3")
biases4 = tf.Variable(tf.zeros(n_outputs), name="biases4")

hidden1 = activation(tf.matmul(X, weights1) + biases1)
hidden2 = activation(tf.matmul(hidden1, weights2) + biases2)
hidden3 = activation(tf.matmul(hidden2, weights3) + biases3)
outputs = tf.matmul(hidden3, weights4) + biases4

reconstruction_loss = tf.reduce_mean(tf.square(outputs - X))
reg_loss = regularizer(weights1) + regularizer(weights2)
loss = reconstruction_loss + reg_loss

optimizer = tf.train.AdamOptimizer(learning_rate)
training_op = optimizer.minimize(loss)

init = tf.global_variables_initializer()

这段代码非常简单,但有几件重要的事情需要注意:

首先,权重 3 和权重 4 不是变量,它们分别是权重 2 和权重 1 的转置(它们与它们“绑定”)。
其次,由于它们不是变量,所以规范它们是没有用的:我们只调整权重 1 和权重 2。
第三,偏置永远不会被束缚,并且永远不会正规化。

一次训练一个自编码器

我们不是一次完成整个栈式自编码器的训练,而是一次训练一个浅自编码器,然后将所有这些自编码器堆叠到一个栈式自编码器(因此名称)中,通常要快得多,如图 15-4 所示。 这对于非常深的自编码器特别有用。

image

在训练的第一阶段,第一个自编码器学习重构输入。 在第二阶段,第二个自编码器学习重构第一个自编码器隐藏层的输出。 最后,您只需使用所有这些自编码器来构建一个大三明治,如图 15-4 所示(即,您首先将每个自编码器的隐藏层,然后按相反顺序堆叠输出层)。 这给你最后的栈式自编码器。 您可以用这种方式轻松地训练更多的自编码器,构建一个非常深的栈式自编码器。

为了实现这种多阶段训练算法,最简单的方法是对每个阶段使用不同的 TensorFlow 图。 训练完一个自编码器后,您只需通过它运行训练集并捕获隐藏层的输出。 这个输出作为下一个自编码器的训练集。 一旦所有自编码器都以这种方式进行了训练,您只需复制每个自编码器的权重和偏置,然后使用它们来构建堆叠的自编码器。 实现这种方法非常简单,所以我们不在这里详细说明,但请查阅 Jupyter notebooks 中的代码作为示例。

另一种方法是使用包含整个栈式自编码器的单个图,以及执行每个训练阶段的一些额外操作,如图 15-5 所示。

image

这值得解释一下:

图中的中央列是完整的栈式自编码器。这部分可以在训练后使用。
左列是运行第一阶段训练所需的一系列操作。它创建一个绕过隐藏层 2 和 3 的输出层。该输出层与堆叠的自编码器的输出层共享相同的权重和偏置。此外还有旨在使输出尽可能接近输入的训练操作。因此,该阶段将训练隐藏层1和输出层(即,第一自编码器)的权重和偏置。
图中的右列是运行第二阶段训练所需的一组操作。它增加了训练操作,目的是使隐藏层 3 的输出尽可能接近隐藏层 1 的输出。注意,我们必须在运行阶段 2 时冻结隐藏层 1。此阶段将训练隐藏层 2 和 3 的权重和偏置(即第二自编码器)。

TensorFlow 代码如下所示:

[...] # Build the whole stacked autoencoder normally.
# In this example, the weights are not tied.
optimizer = tf.train.AdamOptimizer(learning_rate)

with tf.name_scope("phase1"):
    phase1_outputs = tf.matmul(hidden1, weights4) + biases4
    phase1_reconstruction_loss = tf.reduce_mean(tf.square(phase1_outputs - X))
    phase1_reg_loss = regularizer(weights1) + regularizer(weights4)
    phase1_loss = phase1_reconstruction_loss + phase1_reg_loss
    phase1_training_op = optimizer.minimize(phase1_loss)

with tf.name_scope("phase2"):
    phase2_reconstruction_loss = tf.reduce_mean(tf.square(hidden3 - hidden1))
    phase2_reg_loss = regularizer(weights2) + regularizer(weights3)
    phase2_loss = phase2_reconstruction_loss + phase2_reg_loss
    train_vars = [weights2, biases2, weights3, biases3]
    phase2_training_op = optimizer.minimize(phase2_loss, var_list=train_vars)

第一阶段比较简单:我们只创建一个跳过隐藏层 2 和 3 的输出层,然后构建训练操作以最小化输出和输入之间的距离(加上一些正则化)。

第二阶段只是增加了将隐藏层 3 和隐藏层 1 的输出之间的距离最小化的操作(还有一些正则化)。 最重要的是,我们向minim()方法提供可训练变量的列表,确保省略权重 1 和偏差 1;这有效地冻结了阶段 2 期间的隐藏层 1。

在执行阶段,你需要做的就是为阶段 1 一些迭代进行训练操作,然后阶段 2 训练运行更多的迭代。

由于隐藏层 1 在阶段 2 期间被冻结,所以对于任何给定的训练实例其输出将总是相同的。 为了避免在每个时期重新计算隐藏层1的输出,您可以在阶段 1 结束时为整个训练集计算它,然后直接在阶段 2 中输入隐藏层 1 的缓存输出。这可以得到一个不错的性能上的提升。

可视化重建

确保自编码器得到适当训练的一种方法是比较输入和输出。 它们必须非常相似,差异应该是不重要的细节。 我们来绘制两个随机数字及其重建:

n_test_digits = 2
X_test = mnist.test.images[:n_test_digits]

with tf.Session() as sess:
    [...] # Train the Autoencoder
    outputs_val = outputs.eval(feed_dict={X: X_test})

def plot_image(image, shape=[28, 28]):
    plt.imshow(image.reshape(shape), cmap="Greys", interpolation="nearest")
    plt.axis("off")

for digit_index in range(n_test_digits):
    plt.subplot(n_test_digits, 2, digit_index * 2 + 1)
    plot_image(X_test[digit_index])
    plt.subplot(n_test_digits, 2, digit_index * 2 + 2)
    plot_image(outputs_val[digit_index])

image

看起来够接近。 所以自编码器已经适当地学会了重现它,但是它学到了有用的特性? 让我们来看看。

可视化功能

一旦你的自编码器学习了一些功能,你可能想看看它们。 有各种各样的技术。 可以说最简单的技术是在每个隐藏层中考虑每个神经元,并找到最能激活它的训练实例。 这对顶层隐藏层特别有用,因为它们通常会捕获相对较大的功能,您可以在包含它们的一组训练实例中轻松找到这些功能。 例如,如果神经元在图片中看到一只猫时强烈激活,那么激活它的图片最显眼的地方都会包含猫。 然而,对于较低层,这种技术并不能很好地工作,因为这些特征更小,更抽象,因此很难准确理解神经元正在为什么而兴奋。

让我们看看另一种技术。 对于第一个隐藏层中的每个神经元,您可以创建一个图像,其中像素的强度对应于给定神经元的连接权重。 例如,以下代码绘制了第一个隐藏层中五个神经元学习的特征:

with tf.Session() as sess:
    [...] # train autoencoder
    weights1_val = weights1.eval()

for i in range(5):
    plt.subplot(1, 5, i + 1)
    plot_image(weights1_val.T[i])

您可能会得到如图 15-7 所示的低级功能。

image

前四个特征似乎对应于小块,而第五个特征似乎寻找垂直笔划(请注意,这些特征来自堆叠去噪自编码器,我们将在后面讨论)。

另一种技术是给自编码器提供一个随机输入图像,测量您感兴趣的神经元的激活,然后执行反向传播来调整图像,使神经元激活得更多。 如果迭代数次(执行渐变上升),图像将逐渐变成最令人兴奋的图像(用于神经元)。 这是一种有用的技术,用于可视化神经元正在寻找的输入类型。

最后,如果使用自编码器执行无监督预训练(例如,对于分类任务),验证自编码器学习的特征是否有用的一种简单方法是测量分类器的性能。

无监督预训练使用栈式自编码器

正如我们在第 11 章中讨论的那样,如果您正在处理复杂的监督任务,但您没有大量标记的训练数据,则一种解决方案是找到执行类似任务的神经网络,然后重新使用其较低层。 这样就可以仅使用很少的训练数据来训练高性能模型,因为您的神经网络不必学习所有的低级特征;它将重新使用现有网络学习的特征检测器。

同样,如果您有一个大型数据集,但大多数数据集未标记,您可以先使用所有数据训练栈式自编码器,然后重新使用较低层为实际任务创建一个神经网络,并使用标记数据对其进行训练。 例如,图 15-8 显示了如何使用栈式自编码器为分类神经网络执行无监督预训练。 正如前面讨论过的,栈式自编码器本身通常每次都会训练一个自编码器。 在训练分类器时,如果您确实没有太多标记的训练数据,则可能需要冻结预训练层(至少是较低层)。

image

这种情况实际上很常见,因为构建一个大型的无标签数据集通常很便宜(例如,一个简单的脚本可以从互联网上下载数百万张图像),但只能由人类可靠地标记它们(例如,将图像分类为可爱或不可爱)。 标记实例是耗时且昂贵的,因此只有几千个标记实例是很常见的。

正如我们前面所讨论的那样,当前深度学习海啸的触发因素之一是 Geoffrey Hinton 等人在 2006 年的发现,深度神经网络可以以无监督的方式进行预训练。 他们使用受限玻尔兹曼机器(见附录 E),但在 2007 年 Yoshua Bengio 等人表明自编码器也起作用。

TensorFlow 的实现没有什么特别之处:只需使用所有训练数据训练自编码器,然后重用其编码器层以创建一个新的神经网络(有关如何重用预训练层的更多详细信息,请参阅第 11 章或查看 Jupyte notebooks 中的代码示例)。

到目前为止,为了强制自编码器学习有趣的特性,我们限制了编码层的大小,使其不够完善。 实际上可以使用许多其他类型的约束,包括允许编码层与输入一样大或甚至更大的约束,导致过度完成的自编码器。 现在我们来看看其中的一些方法。

降噪自编码(DAE)

另一种强制自编码器学习有用功能的方法是为其输入添加噪声,对其进行训练以恢复原始的无噪声输入。 这可以防止自编码器将其输入复制到其输出,因此最终不得不在数据中查找模式。

自 20 世纪 80 年代以来,使用自编码器消除噪音的想法已经出现(例如,在 Yann LeCun 的 1987 年硕士论文中提到过)。 在 2008 年的一篇论文中,帕斯卡尔文森特等人。 表明自编码器也可用于特征提取。 在 2010 年的一篇文章中 Vincent 等人引入堆叠降噪自编码器。

噪声可以是纯粹的高斯噪声添加到输入,或者它可以随机关闭输入,就像 drop out(在第 11 章介绍)。 图 15-9 显示了这两个选项。

image

TensorFlow 实现

在 TensorFlow 中实现去噪自编码器并不难。 我们从高斯噪声开始。 这实际上就像训练一个常规的自编码器一样,除了给输入添加噪声外,重建损耗是根据原始输入计算的:

X = tf.placeholder(tf.float32, shape=[None, n_inputs])
X_noisy = X + tf.random_normal(tf.shape(X))
[...]
hidden1 = activation(tf.matmul(X_noisy, weights1) + biases1)
[...]
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
[...]

由于X的形状只是在构造阶段部分定义的,我们不能预先知道我们必须添加到X中的噪声的形状。我们不能调用X.get_shape(),因为这只会返回部分定义的X的形状 ([None,n_inputs])和random_normal()需要一个完全定义的形状,因此会引发异常。 相反,我们调用tf.shape(X),它将创建一个操作,该操作将在运行时返回X的形状,该操作将在此时完全定义。

实施更普遍的 dropout 版本,而且这个版本并不困难:

from tensorflow.contrib.layers import dropout

keep_prob = 0.7

is_training = tf.placeholder_with_default(False, shape=(), name='is_training')
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
X_drop = dropout(X, keep_prob, is_training=is_training)
[...]
hidden1 = activation(tf.matmul(X_drop, weights1) + biases1)
[...]
reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) # MSE
[...]

在训练期间,我们必须使用feed_dict将is_training设置为True(如第 11 章所述):

sess.run(training_op, feed_dict={X: X_batch, is_training: True})

但是,在测试期间,不需要将is_training设置为False,因为我们将其设置为对placeholder_with_default()函数调用的默认值。

原文发布时间为:2018-07-05
本文作者:ApacheCN【翻译】
本文来自云栖社区官方钉群“Python技术进阶”,了解相关信息可以关注“Python技术进阶”。

Python技术进阶交流群


_2019_01_15_10_28_39

相关文章
|
1天前
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Django开发Web网页端操作界面,实现用户上传一张交通标志图片,识别其名称。
27 6
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
|
30天前
|
机器学习/深度学习 数据采集 TensorFlow
使用TensorFlow进行模型训练:一次实战探索
【8月更文挑战第22天】本文通过实战案例详解使用TensorFlow进行模型训练的过程。首先确保已安装TensorFlow,接着预处理数据,包括加载、增强及归一化。然后利用`tf.keras`构建卷积神经网络模型,并配置训练参数。最后通过回调机制训练模型,并对模型性能进行评估。此流程为机器学习项目提供了一个实用指南。
|
21天前
|
持续交付 测试技术 jenkins
JSF 邂逅持续集成,紧跟技术热点潮流,开启高效开发之旅,引发开发者强烈情感共鸣
【8月更文挑战第31天】在快速发展的软件开发领域,JavaServer Faces(JSF)这一强大的Java Web应用框架与持续集成(CI)结合,可显著提升开发效率及软件质量。持续集成通过频繁的代码集成及自动化构建测试,实现快速反馈、高质量代码、加强团队协作及简化部署流程。以Jenkins为例,配合Maven或Gradle,可轻松搭建JSF项目的CI环境,通过JUnit和Selenium编写自动化测试,确保每次构建的稳定性和正确性。
42 0
|
21天前
|
缓存 开发者 测试技术
跨平台应用开发必备秘籍:运用 Uno Platform 打造高性能与优雅设计兼备的多平台应用,全面解析从代码共享到最佳实践的每一个细节
【8月更文挑战第31天】Uno Platform 是一种强大的工具,允许开发者使用 C# 和 XAML 构建跨平台应用。本文探讨了 Uno Platform 中实现跨平台应用的最佳实践,包括代码共享、平台特定功能、性能优化及测试等方面。通过共享代码、采用 MVVM 模式、使用条件编译指令以及优化性能,开发者可以高效构建高质量应用。Uno Platform 支持多种测试方法,确保应用在各平台上的稳定性和可靠性。这使得 Uno Platform 成为个人项目和企业应用的理想选择。
27 0
|
21天前
|
UED 存储 数据管理
深度解析 Uno Platform 离线状态处理技巧:从网络检测到本地存储同步,全方位提升跨平台应用在无网环境下的用户体验与数据管理策略
【8月更文挑战第31天】处理离线状态下的用户体验是现代应用开发的关键。本文通过在线笔记应用案例,介绍如何使用 Uno Platform 优雅地应对离线状态。首先,利用 `NetworkInformation` 类检测网络状态;其次,使用 SQLite 实现离线存储;然后,在网络恢复时同步数据;最后,通过 UI 反馈提升用户体验。
35 0
|
21天前
|
开发者 算法 虚拟化
惊爆!Uno Platform 调试与性能分析终极攻略,从工具运用到代码优化,带你攻克开发难题成就完美应用
【8月更文挑战第31天】在 Uno Platform 中,调试可通过 Visual Studio 设置断点和逐步执行代码实现,同时浏览器开发者工具有助于 Web 版本调试。性能分析则利用 Visual Studio 的性能分析器检查 CPU 和内存使用情况,还可通过记录时间戳进行简单分析。优化性能涉及代码逻辑优化、资源管理和用户界面简化,综合利用平台提供的工具和技术,确保应用高效稳定运行。
32 0
|
21天前
|
前端开发 开发者 设计模式
揭秘Uno Platform状态管理之道:INotifyPropertyChanged、依赖注入、MVVM大对决,帮你找到最佳策略!
【8月更文挑战第31天】本文对比分析了 Uno Platform 中的关键状态管理策略,包括内置的 INotifyPropertyChanged、依赖注入及 MVVM 框架。INotifyPropertyChanged 方案简单易用,适合小型项目;依赖注入则更灵活,支持状态共享与持久化,适用于复杂场景;MVVM 框架通过分离视图、视图模型和模型,使状态管理更清晰,适合大型项目。开发者可根据项目需求和技术栈选择合适的状态管理方案,以实现高效管理。
28 0
|
21天前
|
机器学习/深度学习 TensorFlow 数据处理
分布式训练在TensorFlow中的全面应用指南:掌握多机多卡配置与实践技巧,让大规模数据集训练变得轻而易举,大幅提升模型训练效率与性能
【8月更文挑战第31天】本文详细介绍了如何在Tensorflow中实现多机多卡的分布式训练,涵盖环境配置、模型定义、数据处理及训练执行等关键环节。通过具体示例代码,展示了使用`MultiWorkerMirroredStrategy`进行分布式训练的过程,帮助读者更好地应对大规模数据集与复杂模型带来的挑战,提升训练效率。
24 0
|
21天前
|
机器学习/深度学习 缓存 TensorFlow
TensorFlow 数据管道优化超重要!掌握这些关键技巧,大幅提升模型训练效率!
【8月更文挑战第31天】在机器学习领域,高效的数据处理对构建优秀模型至关重要。TensorFlow作为深度学习框架,其数据管道优化能显著提升模型训练效率。数据管道如同模型生命线,负责将原始数据转化为可理解形式。低效的数据管道会限制模型性能,即便模型架构先进。优化方法包括:合理利用数据加载与预处理功能,使用`tf.data.Dataset` API并行读取文件;使用`tf.image`进行图像数据增强;缓存数据避免重复读取,使用`cache`和`prefetch`方法提高效率。通过这些方法,可以大幅提升数据管道效率,加快模型训练速度。
30 0
|
21天前
|
机器学习/深度学习 并行计算 TensorFlow
GPU加速TensorFlow模型训练:从环境配置到代码实践的全方位指南,助你大幅提升深度学习应用性能,让模型训练不再等待
【8月更文挑战第31天】本文以随笔形式探讨了如何在TensorFlow中利用GPU加速模型训练,并提供了详细的实践指南。从安装支持GPU的TensorFlow版本到配置NVIDIA CUDA及cuDNN库,再到构建CNN模型并使用MNIST数据集训练,全面展示了GPU加速的重要性与实现方法。通过对比CPU与GPU上的训练效果,突显了GPU在提升训练速度方面的显著优势。最后,还介绍了如何借助TensorBoard监控训练过程,以便进一步优化模型。
35 0

热门文章

最新文章