在技术领域,经常认为需要了解市场上所有可用的服务或工具,以完成特定的任务。非常认同这种心态,以至于可以不断探索和尝试不同的工具组合,以产生有用的推论。但这并不意味着不赞成用已经拥有的东西进行创造性思考的心态。很多时候,对任何领域都有一定的知识范围和推广者,可以使工作变得简单,同时没有人能完全知道相同数量和类型的知识。这意味着都不知道其他人可能集体知道的许多可能性。
今天,将尝试集中讨论用已经知道的东西进行创造性思考的想法。本文讨论了深度学习中处理各种无监督学习问题的非常流行的技术或架构:自编码器。自编码器是一种特定的神经网络架构,由两个通过瓶颈层(潜在维度层)连接的网络组成。这两个网络在功能和执行结果上是相反的。
第一个网络被称为编码器,它接收输入样本,并生成数值以较小的维度(潜在维度)表示数据。编码的数据旨在尽可能多地保留原始输入的性质和特征,用较少的值(潜在维度)理解原始输入。得到的较小维度编码表示的输出就是所说的瓶颈层(潜在维度层)。
架构中的第二个网络紧接在瓶颈层之后,使网络作为连续工作,因为这些潜在维度现在是第二个网络的输入,也称为解码器。以下是一个基本自编码器网络的样子:自编码器网络。让看看代码,教神经网络如何从时尚MNIST数据集中生成图像。更多关于数据集的信息请查看附加链接,为了简要理解,只需知道该数据集包含大小为(28,28)的灰度图像,基于时尚服装对象有10个类别。
导入必要的库/模块:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras import layers, losses
from tensorflow.keras.models import Model
1. 获取数据。一旦从tensorflow.keras.datasets获取数据,需要将像素值的范围从(0-255)重新缩放到(0,1),为此将所有像素值除以255.0以标准化它们。注意:由于自编码器能够进行无监督学习(无需标签),这就是希望通过本文实现的,将忽略时尚MNIST的训练和测试数据集中的标签。
(x_train, _), (x_test, _) = fashion_mnist.load_data()
x_train = x_train.astype('float32')/255.
x_test = x_test.astype('float32')/255.
print(x_train.shape)
print(x_test.shape)
如所见,在运行此代码后,有60,000张训练图像和10,000张测试图像。
2. 添加噪声。原始数据集的图像中没有噪声,但对于任务,只能学习去噪已经包含噪声的图像。因此,对于情况,让在数据中添加一些噪声。
在这里,正在处理灰度图像,它们只有一个通道,但该通道没有作为附加维度在数据集中提及,因此为了添加噪声的目的,需要为训练和测试集中的每个图像添加一个值为‘1’的维度,这对应于灰度通道。
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]
print(x_train.shape)
# 添加噪声,噪声强度由noise_factor处理
noise_factor = 0.2
x_train_noisy = x_train + noise_factor*tf.random.normal(shape=x_train.shape)
x_test_noisy = x_test + noise_factor * tf.random.normal(shape=x_test.shape)
x_train_noisy = tf.clip_by_value(x_train_noisy, clip_value_min = 0., clip_value_max = 1.)
x_test_noisy = tf.clip_by_value(x_test_noisy, clip_value_min = 0., clip_value_max = 1.)
让可视化这些带噪声的图像:
n = 10
plt.figure(figsize=(20,2))
for i in range(n):
plt.subplot(1, n, i+1)
plt.imshow(tf.squeeze(x_train_noisy[i]))
plt.title('Original + Noise')
plt.gray()
plt.show()
3. 创建一个卷积自编码器网络。创建这个卷积自编码器网络,以学习这些图像的有意义的表示及其非常重要的特征。通过这种理解,将能够从这些图像的潜在维度值生成去噪版本的数据集。
class Conv_AutoEncoder(Model):
def __init__(self):
super(Conv_AutoEncoder, self).__init__()
self.encoder = tf.keras.Sequential([
layers.Input(shape=(28,28,1)),
layers.Conv2D(16, (3,3), activation='relu', padding='same', strides=2),
layers.Conv2D(8, (3,3), activation='relu', padding='same', strides=2)
])
self.decoder = tf.keras.Sequential([
layers.Conv2DTranspose(8, kernel_size=3, strides = 2, activation='relu', padding='same'),
layers.Conv2DTranspose(16, kernel_size=3, strides=2, activation='relu', padding='same',),
layers.Conv2D(1, kernel_size=(3,3), activation='sigmoid', padding='same')
])
def call(self,x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decoded
conv_autoencoder = Conv_AutoEncoder()
conv_autoencoder.compile(loss=losses.MeanSquaredError(), optimizer = 'adam')
使用TensorFlow的子类化API创建了这个网络。在Conv_AutoEncoder类中继承了Model类,并定义了网络作为该类(self.encoder和self.decoder)的个别方法。编码器:选择了一个非常基础的模型架构,用于演示目的,包括输入层和2个卷积2D层。解码器:为了通过转置卷积将图像上采样到原始大小,使用2个卷积2D转置层和1个主流卷积2D层,以获得通道维度为1(与灰度图像相同)。
小任务:使用conv_autoencoder.encoder.summary()和conv_autoencoder.decoder.summary()观察这些网络的更详细架构。
4. 训练模型并测试它。如上所述,没有这些图像的标签,但拥有原始图像和带噪声的图像,可以训练模型,以理解由带噪声的图像创建的潜在空间中的原始图像的表示。这将给一个训练有素的解码器网络,以从带噪声图像的潜在表示中去除噪声并获得更清晰的图像。
训练:conv_autoencoder.fit(x_train_noisy, x_train, epochs=10, validation_data = (x_test_noisy, x_test))。训练损失和验证损失几乎相同,这意味着没有过拟合,而且在过去4个周期中,两者的损失都没有显著下降,这表明它几乎达到了图像之间的最佳表示。
n = 10
plt.figure(figsize=(20, 4))
for i in range(n):
# 显示原始
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test_noisy[i])
plt.title("original")
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# 显示重建
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(conv_decoded_images[i])
plt.title("reconstructed")
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()