TensorFlow深度学习实战(24)——卷积自编码器详解与实现

发布于:2025-07-08 ⋅ 阅读:(21) ⋅ 点赞:(0)

TensorFlow深度学习实战(24)——卷积自编码器详解与实现

0. 前言

我们已经学习了自编码器 (AutoEncoder) 的原理,并使用 TensorFlow 搭建了全连接自编码器,但我们使用的数据集较为简单,每张图像只有一个通道(每张图像都为黑白图像)且图像相对较小 (28 x 28)。但在现实场景中,图像数据通常为彩色图像( 3 个通道)且图像尺寸通常较大。在本节中,我们将实现能够处理多维输入图像的卷积自编码器,为了与普通自编码器进行对比,同样使用 MNIST 数据集。

1. 卷积自编码器

可以通过堆叠多个编码器和解码器层来构建深度自编码器,这类自编码器称为堆叠自编码器 (Stacked autoencoder)。一个编码器提取的特征会作为输入传递给下一个编码器,堆叠自编码器可以作为一个整体网络进行训练,以最小化重建误差。此外,每个独立的编码器/解码器网络可以首先进行预训练,然后对整个网络进行微调。当深度自编码器网络使用卷积网络时,称之为卷积自编码器。
与传统的全连接自编码器不同,卷积自编码器 (Convolutional Autoencoder) 利用卷积层和池化层替代了全连接层,以处理具有高维空间结构的图像数据。这样的设计使得卷积自编码器能够在较少的参数量下对输入数据进行降维和压缩,同时保留重要的空间特征。卷积自编码器架构如下所示:

卷积自编码器

从上图中可以看出,输入图像被表示为瓶颈层中的潜空间变量,用于重建图像。图像经过多次卷积(编码器)得到低维潜空间表示,然后在解码器中,将潜空间变量还原为原始尺寸,使解码器的输出能够近似恢复原始输入。
本质上,卷积自编码器在其网络中使用卷积、池化操作来代替原始自编码器的全连接操作,并使用上采样操作 (UpSampling2D) 对特征图进行上采样。了解卷积自编码器的原理后,使用 TensorFlow 实现卷积自编码器。

2. 基于卷积自编码器去除图像噪声

我们已经学习了如何使用全连接网络作为编码器和解码器,从带噪声的输入图像中重建 MNIST 手写数字。但我们知道对于图像,卷积网络可以得到更优异的结果,在本节中,我们将使用卷积网络作为编码器和解码器,在编码器和解码器网络中使用多个卷积层,即堆叠卷积层(以及最大池化层或上采样层),并将整个自编码器作为一个整体进行训练。

(1) 导入所需的模块,并定义超参数:

import numpy as np
import tensorflow as tf
import tensorflow.keras as K
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Dense, Conv2D, MaxPooling2D, UpSampling2D

batch_size = 128
max_epochs = 50
filters = [32,32,16]

(2) 读取数据并对其进行预处理,添加噪声并将输入限制范围在 [0-1] 之间,这是因为模型将使用二元交叉熵损失(替代均方误差损失),并且解码器的最终输出将通过 sigmoid 激活函数,将其限制在 [0-1] 之间:

(x_train, _), (x_test, _) = K.datasets.mnist.load_data()

x_train = x_train / 255.
x_test = x_test / 255.

x_train = np.reshape(x_train, (len(x_train),28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))

noise = 0.5
x_train_noisy = x_train + noise * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)
x_test_noisy = x_test + noise * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)

x_train_noisy = np.clip(x_train_noisy, 0, 1)
x_test_noisy = np.clip(x_test_noisy, 0, 1)

x_train_noisy = x_train_noisy.astype('float32')
x_test_noisy = x_test_noisy.astype('float32')

(3) 定义编码器。编码器由三层卷积层组成,每层卷积层后面使用一个最大池化层。由于本节使用 MNIST 数据集,输入图像的形状为 28 × 28 (单通道),输出图像的大小是 4 × 4 (由于最后一个卷积层有 16 个滤波器,因此图像有 16 个通道):

class Encoder(K.layers.Layer):
    def __init__(self, filters):
        super(Encoder, self).__init__()
        self.conv1 = Conv2D(filters=filters[0], kernel_size=3, strides=1, activation='relu', padding='same')
        self.conv2 = Conv2D(filters=filters[1], kernel_size=3, strides=1, activation='relu', padding='same')
        self.conv3 = Conv2D(filters=filters[2], kernel_size=3, strides=1, activation='relu', padding='same')
        self.pool = MaxPooling2D((2, 2), padding='same')

    def call(self, input_features):
        x = self.conv1(input_features)
        #print("Ex1", x.shape)
        x = self.pool(x)
        #print("Ex2", x.shape)
        x = self.conv2(x)
        x = self.pool(x)
        x = self.conv3(x)
        x = self.pool(x)
        return x

(4) 解码器的设计完全相反,使用上采样来增加图像的大小。我们可以打印每一步后形状的变化,也可以使用 model.summary() 函数获取模型摘要。编码器类和解码器类基于 TensorFlow Keras Layers 类,其内部包含多个层,可以使用此方法构建复杂的自定义层:

class Decoder(K.layers.Layer):
    def __init__(self, filters):
        super(Decoder, self).__init__()
        self.conv1 = Conv2D(filters=filters[2], kernel_size=3, strides=1, activation='relu', padding='same')
        self.conv2 = Conv2D(filters=filters[1], kernel_size=3, strides=1, activation='relu', padding='same')
        self.conv3 = Conv2D(filters=filters[0], kernel_size=3, strides=1, activation='relu', padding='valid')
        self.conv4 = Conv2D(1, 3, 1, activation='sigmoid', padding='same')
        self.upsample = UpSampling2D((2, 2))
  
    def call(self, encoded):
        x = self.conv1(encoded)
        #print("dx1", x.shape)
        x = self.upsample(x)
        #print("dx2", x.shape)
        x = self.conv2(x)
        x = self.upsample(x)
        x = self.conv3(x)
        x = self.upsample(x)
        return self.conv4(x)

(5) 组合编码器和解码器,创建自编码器模型:

class Autoencoder(K.Model):
    def __init__(self, filters):
        super(Autoencoder, self).__init__()
        self.loss = []
        self.encoder = Encoder(filters)
        self.decoder = Decoder(filters)

    def call(self, input_features):
        #print(input_features.shape)
        encoded = self.encoder(input_features)
        #print(encoded.shape)
        reconstructed = self.decoder(encoded)
        #print(reconstructed.shape)
        return reconstructed

(6) 实例化模型,在 compile() 方法中指定二元交叉熵作为损失函数,Adam 作为优化器。然后在训练数据集上拟合模型:

model = Autoencoder(filters)

model.compile(loss='binary_crossentropy', optimizer='adam')

loss = model.fit(x_train_noisy,
                x_train,
                validation_data=(x_test_noisy, x_test),
                epochs=max_epochs,
                verbose=2,
                batch_size=batch_size)

(7) 绘制模型训练期间的损失变化曲线:

plt.plot(range(max_epochs), loss.history['loss'])
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.show()

训练过程检测

可以看到随着模型训练,损失曲线的变化,在 50epoch 中,损失减少到了 0.0988

3. 图像重建结果

最后,可以看到从带噪声的输入图像重建出的图像:

number = 10  # how many digits we will display
plt.figure(figsize=(20, 4))
for index in range(number):
    # display original
    ax = plt.subplot(2, number, index + 1)
    plt.imshow(x_test_noisy[index].reshape(28, 28), cmap='gray')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # display reconstruction
    ax = plt.subplot(2, number, index + 1 + number)
    plt.imshow(tf.reshape(model(x_test_noisy)[index], (28, 28)), cmap='gray')
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

可以看到卷积自编码器得到的重建图像比原始自编码器得到的重建图像更加清晰和锐利,其关键在于卷积层的堆叠。

重建结果

小结

卷积自编码器是一种基于卷积神经网络结构的自编码器,适用于处理图像数据。卷积自编码器在图像处理领域有广泛的应用,包括图像去噪、图像压缩、图像生成等任务。通过训练卷积自编码器,可以提取出输入图像的关键特征,并实现对图像数据的降维和压缩,同时保留重要的空间信息。在本节中,我们介绍了卷积自编码器的模型架构,使用 TensorFlow 从零开始在 MNIST 数据集上训练了一个简单的卷积自编码器,用于去除图像中的噪声信号。

系列链接

TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模型实现图像分类
TensorFlow深度学习实战(10)——迁移学习详解
TensorFlow深度学习实战(11)——风格迁移详解
TensorFlow深度学习实战(12)——词嵌入技术详解
TensorFlow深度学习实战(13)——神经嵌入详解
TensorFlow深度学习实战(14)——循环神经网络详解
TensorFlow深度学习实战(15)——编码器-解码器架构
TensorFlow深度学习实战(16)——注意力机制详解
TensorFlow深度学习实战(17)——主成分分析详解
TensorFlow深度学习实战(18)——K-means 聚类详解
TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(20)——自组织映射详解
TensorFlow深度学习实战(21)——Transformer架构详解与实现
TensorFlow深度学习实战(22)——从零开始实现Transformer机器翻译
TensorFlow深度学习实战(23)——自编码器详解与实现


网站公告

今日签到

点亮在社区的每一天
去签到