作者 | Abien Fred Agarap
来源 | Medium
编辑 | 代码医生团队
Google宣布对全球最受欢迎的开源机器学习库TensorFlow进行重大升级,承诺注重简单性和易用性,eager execution,直观的高级API以及在任何平台上灵活构建模型。
这篇文章是一个尝试,为TensorFlow 2.0工作的实体做出贡献。将讨论自动编码器的子类API实现。
https://www.tensorflow.org/guide/keras#model_subclassing
要安装TensorFlow 2.0,建议为其创建虚拟环境,
pip install tensorflow==2.0.0-alpha
或者如果系统中有GPU,
pip install tensorflow-gpu==2.0.0-alpha
其安装的更多细节请查看tensorflow.org上的指南。
https://www.tensorflow.org/install
在深入研究代码之前,首先讨论一下自动编码器是什么。
自动编码器
处理机器学习中的大量数据,这自然会导致更多的计算。但是可以选择对模型学习贡献最大的数据部分,从而减少计算量。选择数据重要部分的过程称为特征选择,它是自动编码器的用例之一。
究竟什么是自动编码器?首先回忆一下,一个神经网络是一个用于计算模型找到一个函数 描述的关系数据之间的特征 x和其值(一个回归任务)或标签(一个分类任务) y ,即( y = f(x))。自动编码器也是一个神经网络。而不是找到函数映射的功能 x到其相应的值或标签y,它的目的是找到函数映射功能x本身x。
自动编码器内部会发生什么。用一个自动编码器的图形说明。
自动编码器是一种人工神经网络,旨在学习如何重建数据。
从上图可以看出,自动编码器由两部分组成:(1)学习数据表示的编码器,即数据的重要特征z,以及(2)基于其如何根据其思想z重建数据的解码器结构化。
确定自动编码器想要找到将x映射到x的函数。它通过其组件实现。在数学上,
z是编码器从输入数据x获知的学习数据。
x-hat是解码器基于学习的表示z的重建数据。
所述编码器 H-SUB-E学习数据表示z从输入特征x,则所述表示作为输入到解码器 H-SUB-d ,以重构原始数据x。
下面进一步剖析这个模型。
编码器
第一个组件,即编码器,类似于传统的前馈网络。但是它不负责预测值或标签。相反它的任务是了解数据的结构,即数据表示z。
所述编码器学习给定的数据的表示。
该编码是通过将数据输入完成X到编码器的隐蔽层h为了学习数据表示z = f(h(x)) 。可以Encoder按如下方式实现该层
class Encoder(tf.keras.layers.Layer):
def __init__(self, intermediate_dim):
super(Encoder, self).__init__()
self.hidden_layer = tf.keras.layers.Dense(units=intermediate_dim, activation=tf.nn.relu)
self.output_layer = tf.keras.layers.Dense(units=intermediate_dim, activation=tf.nn.relu)
def call(self, input_features):
activation = self.hidden_layer(input_features)
return self.output_layer(activation)
该编码器的层自动编码写入TensorFlow 2.0子类API。
首先定义一个Encoder 继承的类,将tf.keras.layers.Layer其定义为层而不是模型。为什么是图层而不是模型?回想一下,编码器是一个组件的的自动编码器模型。
通过代码,该Encoder层被定义为具有单个隐藏的神经元层(self.hidden_layer)以学习输入特征的激活。然后将隐藏层连接到一个层(self.output_layer),该层将数据表示编码为较低维度,其中包含它认为重要的特征。因此Encoder层的“输出” 是输入数据x的学习数据表示z。
解码器
第二个组件即解码器也类似于前馈网络。但是它不是将数据减少到较低维度,而是将数据从其较低维度表示z重建为其原始维度x。
所述解码器学习从其低维表示重构数据。
该解码是通过将低维表示完成z到解码器的隐藏层h为了重建数据到其原始尺寸x = f(h(z))。可以按如下方式实现解码器层
class Decoder(tf.keras.layers.Layer):
def __init__(self, intermediate_dim, original_dim):
super(Decoder, self).__init__()
self.hidden_layer = tf.keras.layers.Dense(units=intermediate_dim, activation=tf.nn.relu)
self.output_layer = tf.keras.layers.Dense(units=original_dim, activation=tf.nn.relu)
def call(self, code):
activation = self.hidden_layer(code)
return self.output_layer(activation)
该解码器的层自动编码写入TensorFlow 2.0子类API。
定义一个Decoder也继承了它的类tf.keras.layers.Layer。
该Decoder层还被定义为具有单个隐藏的神经元层,以通过编码器从学习的表示重建输入特征。然后将其隐藏层连接到一个层,该层将数据表示从较低维度解码为其原始维度。因此解码器层的“输出”是来自数据表示z的重建数据x。最终解码器的输出是自动编码器的输出。
现在已经定义了autoencoder的组件,最终可以构建模型。
构建Autoencoder模型
现在可以通过实例化层和层来构建自动编码器模型。EncoderDecoder
class Autoencoder(tf.keras.Model):
def __init__(self, intermediate_dim, original_dim):
super(Autoencoder, self).__init__()
self.encoder = Encoder(intermediate_dim=intermediate_dim)
self.decoder = Decoder(intermediate_dim=intermediate_dim, original_dim=original_dim)
def call(self, input_features):
code = self.encoder(input_features)
reconstructed = self.decoder(code)
return reconstructed
用TensorFlow 2.0子类化API编写的自动编码器模型。
如上所述使用编码器层的输出作为解码器层的输入。就是这样了?不,不完全是。
到目前为止只讨论了自动编码器的组件以及如何构建它,但还没有讨论它是如何实际学习的。到目前为止所知道的只是数据流 ; 从输入层到学习数据表示的编码器层,并使用该表示作为重构原始数据的解码器层的输入。
与其他神经网络一样,自动编码器通过反向传播进行学习。然而不是比较模型的值或标签,而是比较重建数据 x-hat和原始数据x。将这个比较称为重建误差函数,它由下式给出,
在这种情况下,重建损失是您可能熟悉的均方误差函数。
在TensorFlow中,上述等式可表示如下,
def loss(model, original):
reconstruction_error = tf.reduce_mean(tf.square(tf.subtract(model(original), original)))
return reconstruction_error
使用TensorFlow核心操作编写的重建损失。
还有一些要添加的东西。现在已经定义了损失函数,最终可以为模型编写训练函数。
def train(loss, model, opt, original):
with tf.GradientTape() as tape:
gradients = tape.gradient(loss(model, original), model.trainable_variables)
gradient_variables = zip(gradients, model.trainable_variables)
opt.apply_gradients(gradient_variables)
训练函数用TensorFlow 2.0编写,遵循通过使用GradientTape强制编写前向传递的范例。
这种实现反向传播的方式能够跟踪梯度以及优化算法的应用。
http://ruder.io/optimizing-gradient-descent/
在此之前,实例化一个Autoencoder之前定义的类,以及一个要使用的优化算法。然后加载想要重建的数据。这篇文章使用MNIST手写数字数据集。
http://yann.lecun.com/exdb/mnist/
可以使用TensorBoard可视化训练结果,需要使用为结果定义摘要文件编写器tf.summary.create_file_writer。
autoencoder = Autoencoder(intermediate_dim=64, original_dim=784)
opt = tf.optimizers.SGD(learning_rate=learning_rate, momentum=momentum)
(training_features, _), (test_features, _) = tf.keras.datasets.mnist.load_data()
training_features = training_features / np.max(training_features)
training_features = training_features.reshape(training_features.shape[0],
training_features.shape[1] * training_features.shape[2]).astype(np.float32)
training_dataset = tf.data.Dataset.from_tensor_slices(training_features).batch(batch_size)
writer = tf.summary.create_file_writer('tmp')
with writer.as_default():
with tf.summary.record_if(True):
for epoch in range(epochs):
for step, batch_features in enumerate(training_dataset):
train(loss, autoencoder, opt, batch_features)
loss_values = loss(autoencoder, batch_features)
original = tf.reshape(batch_features, (batch_features.shape[0], 28, 28, 1))
reconstructed = tf.reshape(autoencoder(tf.constant(batch_features)), (batch_features.shape[0], 28, 28, 1))
tf.summary.scalar('loss', loss_values, step=step)
tf.summary.image('original', original, max_outputs=10, step=step)
tf.summary.image('reconstructed', reconstructed, max_outputs=10, step=step)
实例化自动编码器模型和优化功能。加载MNIST数据集。最后循环训练自编码器模型。
接下来使用定义的摘要文件编码器,并使用记录训练摘要tf.summary.record_if。
终于可以(现在真实地)训练模型,通过为它提供小批量数据,并通过之前定义的train函数计算其每次迭代的损失和梯度,该函数接受定义的误差函数,自动编码器模型,优化算法,以及小批量的数据。
在训练模型的每次迭代之后,计算的重建误差应该减小以查看模型是否实际学习(就像在其他神经网络中一样)。最后为了在TensorBoard中记录训练摘要,使用tf.summary.scalar记录重建误差值,以及tf.summary.image记录原始数据和重建数据的小批量。
在一些epochs之后,可以开始看到MNIST图像的相对良好的重建。
MNIST手写数字数据集的结果。顶行的图像是原始图像,而底行的图像是重建的图像。
重建的图像可能足够好,但它们非常模糊。可以做很多事情来改善这个结果,例如添加更多层和/或神经元,或者使用卷积神经网络架构作为自动编码器模型的基础,或者使用不同类型的自动编码器。
总结
自动编码器对降低维数非常有用。但它也可以用于数据去噪,以及用于学习数据集的分布。希望在本文中已经涵盖了足够的内容,让您了解有关自动编码器的更多信息!
完整代码链接
https://gist.github.com/AFAgarap/326af55e36be0529c507f1599f88c06e
参考
2015 年异构系统上的大规模机器学习。
https://blog.keras.io/building-autoencoders-in-keras.html