使用自编码器进行MNIST图像重建
使用自编码器重建MNIST图像
简介
在互联网上信息如此丰富的情况下,研究人员和科学家正努力开发更高效和安全的数据传输方法。自编码器由于其简单直观的结构而成为实现这一目标的有价值的工具。通常,在自编码器训练完成后,可以将编码器权重发送给发送方,将解码器权重发送给接收方。这使得发送方可以以编码格式发送数据,节省时间和成本,而接收方可以接收压缩数据。本文探讨了自编码器在MNIST图像重构中的令人兴奋的应用,特别是在Python的PyTorch框架中使用MNIST数字数据库。
学习目标
- 本文着重介绍构建能够对MNIST图像进行编码的TensorFlow自编码器。
- 我们将实现加载和处理数据库的函数,并创建数据点的动态转换。
- 将使用带有噪声和真实图像作为输入生成编码器-解码器结构的自编码器。
- 探索自编码器在深度学习中的重要性、应用原理以及提高模型性能的潜力。
本文是作为数据科学博文马拉松的一部分发表的。
自编码器的架构
自编码器可以分为三个主要组件:
编码器:该模块将训练-验证-测试集中的输入数据进行压缩,生成一个编码表示。通常,编码后的图像数据比输入数据更小。
瓶颈:瓶颈模块保持知识表示的压缩,并将其作为网络的关键部分。数据维度成为一个逐渐减小的障碍。
解码器:解码器模块在“解压缩”过程中对数据表示进行恢复,使其恢复到原始形式。解码器的输出结果然后与真实值或初始输入数据进行比较。
解码器模块帮助“解压缩”数据显示并以其编码形式进行重构。解码器的输出结果然后与真实值或原始输入数据进行比较。
编码器、瓶颈和解码器之间的关系
编码器
编码器通过池化模块和卷积块对输入数据进行压缩,起着重要的作用。这种压缩产生了一个称为块的紧凑图像。
经过一段延迟后,解码器开始工作。它由返回压缩到原始图像格式的特征的高级模块组成。在基本的自编码器中,解码器旨在无论噪声减少如何,都能够重构与输入类似的输出。MNIST图像重构使用自编码器
然而,在可变自编码器的情况下,输入不是输入的重构。相反,它根据提供给模型的输入数据创建一个全新的图像。这种差异使得可变自编码器能够对生成的图像具有一定的控制,并产生不同的结果。
瓶颈
尽管瓶颈是神经系统中最小的部分,但它非常重要。它作为一个关键元素,限制了从编码器到解码器的数据流量,只允许最关键的数据通过。通过限制流量,瓶颈确保关键属性得以保留并在恢复中使用。
通过设计障碍来提取图像中的最大信息,从而代表了输入知识的类型。编码器-解码器结构使得能够从图像中提取有价值的信息,并在网络中建立有意义的连接。
这种压缩形式的处理防止了神经系统对输入和信息过载的记忆。一般而言,障碍越小,冗余风险越低。
然而,非常小的缓冲区可能会限制存储的数据量,增加通过编码器的池化层丢失关键数据的可能性。
解码器
解码器由一个上行和一个卷积块组成,用于重构输出中断。
一旦输入到达解码器,解码器接收到压缩表示后,它就成为一个“解压器”。解码器的作用是根据从压缩图像中提取的隐藏属性重建图像。通过使用这个隐藏属性,解码器通过反转编码器进行的压缩过程有效地重构图像。
如何训练自编码器?
在设置自编码器之前,有四个重要的超参数:
- 编码器大小:编码器大小,也称为块大小,是自编码器调整中的一个重要超参数。它指定了数据压缩的级别。此外,编码器大小可以作为正则化项。
- 多个层:与其他神经网络一样,编码器和解码器的深度是自编码器的一个重要超参数。增加深度会增加模型的复杂性,而减少深度会增加处理速度。
- 每个层中的点数:每个层中的点数决定了每个层中使用的权重。通常,随着自编码器中的下一层,点数会减少,表示输入在减少。
- 损失恢复:选择用于训练自编码器的损失函数取决于所需的输入-输出适应性。当处理图像数据时,用于重构的常见损失函数包括均方误差(MSE)损失和L1损失。如果输入和输出在范围[0,1]内,例如MNIST中的情况,也可以使用二进制交叉熵作为重构损失。
要求
我们需要这个库和帮助函数来在Tensorflow中创建一个自编码器。
Tensorflow:首先,我们应该导入Tensorflow库和创建模型所需的所有必要组件,使其能够读取和生成MNIST图像。
NumPy:接下来,我们导入numpy,一个处理数字的强大库,我们将用它来预处理和重新组织数据库。
Matplotlib:我们将使用matplotlib绘图库来可视化和评估模型的性能。
- data_proc(dat)函数接受帮助函数作为数据,并将其调整为模型所需的大小。
- gen_noise(dat)帮助函数设计为接受数组作为输入,应用高斯噪声,并确保生成的值在(0,1)范围内。
- Two Arrays是一个显示帮助函数(dat1,dat2),它接受一个输入数组和一个预测图像数组,并将它们放入两行中。
构建自编码器
在下一部分中,我们将学习如何使用TensorFlow创建一个简单的自编码器,并使用MNIST图像对其进行训练。首先,我们将概述加载和处理MNIST数据的步骤,以满足我们的要求。一旦数据格式正确,我们就会构建和训练模型。
网络架构包括三个主要组件:编码器、瓶颈和解码器。编码器负责压缩输入图像,同时保留有价值的信息。瓶颈确定哪些特征对解码器的传递是必要的。最后,解码器使用瓶颈结果重构图像。在这个重构过程中,自编码器旨在学习数据的隐藏位置。
我们必须导入一些库并编写一些函数来创建一个模型来读取和创建MNIST图像。使用TensorFlow库导入它及其他相关组件。还要导入NumPy数值处理库和Matplotlib绘图库。这个库将帮助我们执行一些操作和可视化结果。
导入库
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras.layers import *
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Model
此外,我们需要一些辅助函数的实现。初始化函数负责接收一个数组作为输入,并将其大小更改为模型所需的大小。
def data_proc(dat):
larr = len(dat)
return np.reshape(dat.astype("float32") /255.0 , (larr, 28,28,1))
我们还需要添加一个操作数组的第二个辅助函数。此函数向数组添加高斯噪声,并确保生成的值介于0和1之间。
def gen_noise(dat):
return np.clip(dat + 0.4 * np.random.normal(loc=0.0, scale=1.0, size=dat.shape), 0.0, 1.0)
评估模型性能
为了评估我们的模型性能,重要的是可视化大量的图像。为此,我们可以使用一个输入函数,该函数接受两个数组,一个是投影图像的集合,另一个是将它们放入两行的第三个函数。
def display(dat1, dat2):
ind = np.random.randint(len(dat1), size=10)
im1 = dat1[ind, :]
im2 = dat2[ind, :]
for i, (a, b) in enumerate(zip(im1, im2)):
plt_axis = plt.subplot(2, n, i + 1)
plt.imshow(a.reshape(28, 28))
plt.gray()
plt_axis.get_xaxis().set_visible(False)
plt_axis.get_yaxis().set_visible(False)
plt_axis = plt.subplot(2, n, i + 1 + n)
plt.imshow(b.reshape(28, 28))
plt.gray()
plt_axis.get_xaxis().set_visible(False)
plt_axis.get_yaxis().set_visible(False)
plt.show()
数据集准备
MNIST数据集已经在TensorFlow中提供,分为训练集和测试集。我们可以直接加载这个数据库,并使用之前定义的默认处理函数。此外,我们使用之前定义的gen_noise函数为输入数据的后半部分生成噪声版本的原始MNIST图像。需要注意的是,输入噪声水平会影响图像的扭曲,使得模型重构困难。我们将原始图像和噪声想象为整个过程的一部分。
(ds_train, _), (ds_test, _) = mnist.load_data()
ds_train,ds_test = data_proc(ds_train), data_proc(ds_test)
noisy_ds_train, noisy_ds_test = gen_noise(ds_train), gen_noise(ds_test)
display(ds_train, noisy_ds_train)
编码器定义
网络的编码器部分使用了卷积和最大池化层,并采用ReLU激活函数。其目标是在将输入数据发送到网络之前对其进行降维。这一步的期望输出是原始数据的压缩版本。鉴于MNIST图像的尺寸为28x28x1,我们创建了具有特定形状的输入。
inps = Input(shape=(28, 28, 1))
x = Conv2D(32, (3, 3), activation="relu", padding="same")(inps)
x = MaxPooling2D((2, 2), padding="same")(x)
x = Conv2D(32, (3, 3), activation="relu", padding="same")(x)
x = MaxPooling2D((2, 2), padding="same")(x)
瓶颈定义
与其他元素不同,瓶颈不需要显式编程。由于最大池化编码器层产生了高度压缩的最终输出,解码器被训练来利用这种压缩表示重构图像。瓶颈的架构可以在更复杂的自动编码器实现中进行修改。
解码器定义
解码器由步长为2的转置卷积组成。模型的最后一层使用了简单的2D卷积和sigmoid激活函数。这个组件的目的是根据压缩表示重构图像。转置卷积用于上采样,允许更大的步长,并减少了上采样图像所需的步骤数。
x = Conv2DTranspose(32, (3, 3),activation="relu", padding="same", strides=2)(x)
x = Conv2DTranspose(32, (3, 3),activation="relu", padding="same", strides=2)(x)
x = Conv2D(1, (3, 3), activation="sigmoid", padding="same")(x)
模型训练
在定义模型之后,必须使用优化器和损失函数对其进行配置。在本文中,我们将使用Adam优化器,并选择二进制交叉熵损失函数进行训练。
conv_autoenc_model = Model(inps, x)
conv_autoenc_model.compile(optimizer="adam", loss="binary_crossentropy")
conv_autoenc_model.summary()
输出
一旦模型建立完成,我们可以使用之前在文章中创建的修改后的MNIST图像进行训练。训练过程包括以批量大小为128的方式运行模型50个epochs。此外,我们为模型提供了验证数据。
conv_autoenc_model.fit(
x=ds_train,
y=ds_train,
epochs=50,
batch_size=128,
shuffle=True,
validation_data=(ds_test, ds_test),
)
重建图像
一旦我们训练了模型,我们就可以生成预测并重建图像。我们可以使用之前定义的函数来显示生成的图像。
preds = conv_autoenc_model.predict(ds_test)
display(ds_test, preds)
结论
自编码器是一种人工神经网络,可以用于学习无监督的数据编码。其主要目标是获得低维表示,通常称为编码,以减少高维数据的维度。网格可以实现高效的数据表示和分析,捕捉输入图像的最重要特征或特性。
要点
- 自编码器是神经网络中使用的无监督学习技术。通过训练网络来过滤不需要的信号噪声,设计它以学习高效的数据表示(编码)。
- 自编码器具有许多应用,包括图像处理、图像压缩,甚至有时候图像生成。
- 尽管自编码器在理论上的基础很简单,但是教它们学习有意义的输入数据表示可能是具有挑战性的。
- 自编码器有多种应用,例如主成分分析(PCA),一种降维技术,图像渲染和许多其他任务。
常见问题
本文中显示的媒体不属于Analytics Vidhya所有,而是根据作者的自由裁量使用的。