自编码器(Auto Encoder)原理及其python实现

一.原理

自编码器由两部分组成:
编码器(encoder):这部分能将输入压缩成潜在空间表征,可以用编码函数h=f(x)表示。
解码器(decoder):这部分重构来自潜在空间表征的输入,可以用解码函数r=g(h)表示。
因此,整个自编码器可以用函数g(f(x)) = r 来描述,其中输出r与原始输入x相近。

自编码器(Auto Encoder)可以认为是只有一层隐含层的神经网络,通过压缩和还原实现对特征的重构。输入数据是特征,输入层到隐含层是编码器,能将输入压缩成潜在空间表征;隐含层到输出层是解码器,重构来自潜在空间表征的输入。其中自编码器的输入输出神经元个数都等于特征维度。训练这个自编码器,使得输出的特征和输入的特征尽可能一致。自编码器试图复现其原始输入,因此,在训练中,网络中的输出应与输入相同,即y=x,因此,一个自编码器的输入、输出应有相同的结构。我们利用训练数据训练这个网络,等训练结束后,这个网络即学习出了x→h→x的能力。对我们来说,此时的h是至关重要的,因为它是在尽量不损失信息量的情况下,对原始数据的另一种表达。
其结构如下:
在这里插入图片描述

二.为什么要使用自编码器

我们希望通过训练输出值等于输入值的自编码器,让潜在表征h将具有价值属性。从自编码器获得有用特征的一种方法是,限制h的维度使其小于输入x, 这种情况下称作有损自编码器。通过训练有损表征,使得自编码器能学习到数据中最重要的特征。

三.代码实现

以手写数字识别为例

1.原始自编码器

input_size = 784
hidden_size = 64
output_size = 784
 
x = Input(shape=(input_size,))
 
# Encoder
h = Dense(hidden_size, activation='relu')(x)
 
# Decoder
r = Dense(output_size, activation='sigmoid')(h)
 
autoencoder = Model(input=x, output=r)
autoencoder.compile(optimizer='adam', loss='mse')

2.多层(堆叠)自编码器

input_size = 784
hidden_size = 128
code_size = 64
 
x = Input(shape=(input_size,))
 
# Encoder
hidden_1 = Dense(hidden_size, activation='relu')(x)
h = Dense(code_size, activation='relu')(hidden_1)
 
# Decoder
hidden_2 = Dense(hidden_size, activation='relu')(h)
r = Dense(input_size, activation='sigmoid')(hidden_2)
 
autoencoder = Model(input=x, output=r)
autoencoder.compile(optimizer='adam', loss='mse')

3.卷积自编码器

除了全连接层,自编码器也可以应用到卷积层,原理是一样的,但是要使用3D矢量(如图像)而不是展平后的一维矢量。对输入图像进行下采样,以提供较小维度的潜在表征,来迫使自编码器从压缩后的数据进行学习。

x = Input(shape=(28, 28,1)) 
 
# Encoder
conv1_1 = Conv2D(16, (3, 3), activation='relu', padding='same')(x)
pool1 = MaxPooling2D((2, 2), padding='same')(conv1_1)
conv1_2 = Conv2D(8, (3, 3), activation='relu', padding='same')(pool1)
pool2 = MaxPooling2D((2, 2), padding='same')(conv1_2)
conv1_3 = Conv2D(8, (3, 3), activation='relu', padding='same')(pool2)
h = MaxPooling2D((2, 2), padding='same')(conv1_3)
 
# Decoder
conv2_1 = Conv2D(8, (3, 3), activation='relu', padding='same')(h)
up1 = UpSampling2D((2, 2))(conv2_1)
conv2_2 = Conv2D(8, (3, 3), activation='relu', padding='same')(up1)
up2 = UpSampling2D((2, 2))(conv2_2)
conv2_3 = Conv2D(16, (3, 3), activation='relu')(up2)
up3 = UpSampling2D((2, 2))(conv2_3)
r = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(up3)
 
autoencoder = Model(input=x, output=r)
autoencoder.compile(optimizer='adam', loss='mse')

4.正则自编码器

除了施加一个比输入维度小的隐含层,一些其他方法也可用来约束自编码器重构,如正则自编码器。

正则自编码器不需要使用浅层的编码器和解码器以及小的编码维数来限制模型容量,而是使用损失函数来鼓励模型学习其他特性(除了将输入复制到输出)。这些特性包括稀疏表征、小导数表征、以及对噪声或输入缺失的鲁棒性。

即使模型容量大到足以学习一个无意义的恒等函数,非线性且过完备的正则自编码器仍然能够从数据中学到一些关于数据分布的有用信息。

在实际应用中,常用到两种正则自编码器,分别是稀疏自编码器和降噪自编码器

4.1稀疏自编码器

一种用来约束自动编码器重构的方法,是对其损失函数施加约束。比如,可对损失函数添加一个正则化约束,这样能使自编码器学习到数据的稀疏表征。

要注意,在隐含层中,我们还加入了L1正则化,作为优化阶段中损失函数的惩罚项。与香草自编码器相比,这样操作后的数据表征更为稀疏。

input_size = 784
hidden_size = 64
output_size = 784
 
x = Input(shape=(input_size,))
 
# Encoder
h = Dense(hidden_size, activation='relu', activity_regularizer=regularizers.l1(10e-5))(x)#施加在输出上的L1正则项
 
# Decoder
r = Dense(output_size, activation='sigmoid')(h)
 
autoencoder = Model(input=x, output=r)
autoencoder.compile(optimizer='adam', loss='mse')

四.降噪自编码器

降噪自动编码器就是在自动编码器的基础之上,为了防止过拟合问题而对输入层的输入数据加入噪音,以一定概率分布(通常使用二项分布)去擦除原始input矩阵,即每个值都随机置0,这样看起来部分数据的部分特征是丢失了。向训练数据加入噪声,并使自编码器学会去除这种噪声来获得没有被噪声污染过的真实输入。因此,这就迫使编码器学习提取最重要的特征并学习输入数据中更加鲁棒的表征,这也是它的泛化能力比一般编码器强的原因。

这里不是通过对损失函数施加惩罚项,而是通过改变损失函数的重构误差项来学习一些有用信息。

向训练数据加入噪声(高斯噪声或者随机置0),并使自编码器学会去除这种噪声来获得没有被噪声污染过的真实输入。因此,这就迫使编码器学习提取最重要的特征并学习输入数据中更加鲁棒的表征,这也是它的泛化能力比一般编码器强的原因。
在这里插入图片描述

from keras.datasets import mnist
import numpy as np

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

x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 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_factor = 0.5
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) 
x_test_noisy = x_test + noise_factor * 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.)

# 自编码器
input_img = keras.Input(shape=(28, 28, 1))

x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)
x = layers.MaxPooling2D((2, 2), padding='same')(x)
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
encoded = layers.MaxPooling2D((2, 2), padding='same')(x)

# At this point the representation is (7, 7, 32)

x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(encoded)
x = layers.UpSampling2D((2, 2))(x)
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
x = layers.UpSampling2D((2, 2))(x)
decoded = layers.Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)

autoencoder = keras.Model(input_img, decoded)
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

# 训练自编码器
autoencoder.fit(x_train_noisy, x_train,
                epochs=100,
                batch_size=128,
                shuffle=True,
                validation_data=(x_test_noisy, x_test),
                callbacks=[TensorBoard(log_dir='/tmp/tb', histogram_freq=0, write_graph=False)])

五. 逐层贪婪训练堆叠自编码器

这种方法一定程度上能解决梯度消失梯度爆炸的问题,但现在很少用了。

每次训练都只训练一个自编码器,训练完该自编码器后,抛弃输入层跟输出层,将隐含层作为下一个自编码器的输入层,然后训练下一个自编码器。当训练完多个自编码器后,比如4个,训练了4个隐含层结构,将这4个隐含层结构和参数提取出来作为神经网络的隐含层结构和初始化参数。最后,在神经网络添加输出层预测平均接受信号功率,微调这个网络。

逐层贪婪算法的主要思路是每次只训练网络中的一层,即我们首先训练一个只含一个隐藏层的网络,仅当这层网络训练结束之后才开始训练一个有两个隐藏层的网络,以此类推。在每一步中,我们把已经训练好的前k-1层固定,然后增加第k层(也就是将我们已经训练好的前k-1的输出作为输入)。每一层的训练可以是有监督的(例如,将每一步的分类误差作为目标函数),但更通常使用无监督方法(例如自动编码器,我们会在后边的章节中给出细节)。这些各层单独训练所得到的权重被用来初始化最终(或者说全部)的深度网络的权重,然后对整个网络进行“微调”(即把所有层放在一起来优化有标签训练集上的训练误差)。
在这里插入图片描述

参考

自编码器(AutoEncoder)入门及TensorFlow实现
自编码器(Autoencoders)
keras实现各种自编码器
tensorflow 1.0+版本的代码实现

降噪自编码器是一种常见的无监督学习方法,可以用于特征提取和数据压缩等任务。下面是一个简单的降噪自编码器Python实现: ```python import numpy as np import tensorflow as tf from tensorflow.keras.layers import Input, Dense from tensorflow.keras.models import Model # 加载数据 (x_train, _), (x_test, _) = tf.keras.datasets.mnist.load_data() x_train = x_train.astype('float32') / 255. x_test = x_test.astype('float32') / 255. x_train = np.reshape(x_train, (len(x_train), np.prod(x_train.shape[1:]))) x_test = np.reshape(x_test, (len(x_test), np.prod(x_test.shape[1:]))) # 添加噪声 noise_factor = 0.5 x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) x_test_noisy = x_test + noise_factor * 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.) # 定义模型 input_img = Input(shape=(784,)) encoded = Dense(128, activation='relu')(input_img) decoded = Dense(784, activation='sigmoid')(encoded) autoencoder = Model(input_img, decoded) # 编译模型 autoencoder.compile(optimizer='adam', loss='binary_crossentropy') # 训练模型 autoencoder.fit(x_train_noisy, x_train, epochs=50, batch_size=256, shuffle=True, validation_data=(x_test_noisy, x_test)) # 测试模型 decoded_imgs = autoencoder.predict(x_test_noisy) # 可视化结果 import matplotlib.pyplot as plt 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].reshape(28, 28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # 重构图像 ax = plt.subplot(2, n, i + 1 + n) plt.imshow(decoded_imgs[i].reshape(28, 28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) plt.show() ``` 这个实现使用了MNIST数据集,并添加了高斯噪声。模型使用一个128维的隐藏层,输出层使用sigmoid激活函数。模型使用二元交叉熵作为损失函数,使用Adam优化器进行训练。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值