趋近智
搭建一个简单的全连接自编码器,并使用 TensorFlow/Keras 在 MNIST 手写数字数据集上训练它。这个经典自编码器架构的实践实现展示了编码器、瓶颈层和解码器如何协同工作,以习得数据的压缩表示。
我们的目标是训练一个网络,该网络接收一个784维向量(即展平的28x28 MNIST图像)作为输入 ,将其编码为一个低得多的维度的潜在表示 ,然后将 解码回一个784维向量 ,使其与原始输入 尽可能接近。
首先,我们需要导入所需的库并加载 MNIST 数据集。我们会将像素值归一化到 [0, 1] 范围,这是图像数据处理的常规做法,有助于模型训练的稳定性。我们还会将 28x28 的图像展平为784大小的向量。
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.datasets import mnist
# 加载 MNIST 数据集
(x_train, _), (x_test, _) = mnist.load_data() # 我们只需要图像,不需要标签
# 将像素值归一化到 [0, 1] 并展平图像
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print(f"训练数据形状: {x_train.shape}")
print(f"测试数据形状: {x_test.shape}")
训练数据形状: (60000, 784)
测试数据形状: (10000, 784)
我们将使用 Keras 的函数式 API 或顺序式 API 来构建自编码器。对于这个简单例子,顺序式 API 足以应对。
# 定义输入形状和潜在维度
input_dim = 784
latent_dim = 32
# --- 编码器 ---
encoder = keras.Sequential(
[
keras.Input(shape=(input_dim,)),
layers.Dense(128, activation="relu"),
layers.Dense(64, activation="relu"),
layers.Dense(latent_dim, activation="relu", name="bottleneck"), # 瓶颈层
],
name="encoder",
)
# --- 解码器 ---
decoder = keras.Sequential(
[
keras.Input(shape=(latent_dim,)),
layers.Dense(64, activation="relu"),
layers.Dense(128, activation="relu"),
layers.Dense(input_dim, activation="sigmoid"), # 输出层
],
name="decoder",
)
# --- 自编码器(编码器 + 解码器) ---
autoencoder = keras.Sequential(
[
encoder,
decoder,
],
name="autoencoder",
)
# 显示模型摘要
encoder.summary()
decoder.summary()
autoencoder.summary()
这是我们简单自编码器的结构图:
一个简单的全连接自编码器架构。编码器将784维输入映射到32维瓶颈层,解码器则重建784维输出。
在训练之前,我们需要编译 autoencoder 模型。我们需要指定优化器和损失函数。
我们训练模型以最小化这个重建损失,将输入数据 x_train 同时作为输入和目标输出。
# 编译自编码器
autoencoder.compile(optimizer='adam', loss='mse') # 使用均方误差损失
# 训练自编码器
epochs = 20
batch_size = 256
history = autoencoder.fit(x_train, x_train, # 输入和目标相同
epochs=epochs,
batch_size=batch_size,
shuffle=True,
validation_data=(x_test, x_test)) # 在测试集上评估重建效果
在训练过程中,Keras 会为每个迭代周期输出训练集和验证集上的损失。我们会看到损失随着时间推移而降低,这表明模型正在学习更准确地重建输入图像。
我们可以通过绘制损失曲线来可视化训练进程:
MNIST 数据集上简单自编码器经过20个迭代周期的训练和验证损失(MSE,对数尺度)。两种损失都稳步下降,表明学习成功。
训练结束后,我们可以通过视觉比较原始测试图像与它们的重建结果来评估自编码器的表现。我们使用训练好的 autoencoder 模型来预测测试集 x_test 的重建图像。
# 预测测试集的重建图像
reconstructed_imgs = autoencoder.predict(x_test)
# --- 可视化 ---
n = 10 # 要显示的数字数量
plt.figure(figsize=(20, 4))
for i in range(n):
# 显示原始图像
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == 0:
ax.set_title("原始图像", loc='left', fontsize=12, pad=10)
# 显示重建图像
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(reconstructed_imgs[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
if i == 0:
ax.set_title("重建图像", loc='left', fontsize=12, pad=10)
plt.suptitle("原始与重建 MNIST 数字对比", fontsize=16)
plt.show()
您应该会发现重建的数字虽然可识别,但与原始图像相比略显模糊。这种细节的丢失是预料之中的,因为信息必须通过32维瓶颈层进行压缩。自编码器习得了保留重建所需的最显著特征,同时舍弃了一些更精细的细节或噪声。
在本实践部分,我们成功地在 MNIST 数据集上实现了并训练了一个基本的全连接自编码器。我们涉及了:
这个例子体现了自编码器的核心功能:学习一个压缩表示(编码),并从该表示重建输入(解码)。尽管对于简单数据来说很有效,但这种基本架构存在局限,尤其是在过拟合和习得的潜在空间结构方面。在下一章中,我们将研究旨在解决这些问题并习得更稳定表示的正则化自编码器。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造