⾃编码器(AutoEncoder)⼊门及TensorFlow实现
⾃编码器(Autoencoder,AE),是⼀种利⽤反向传播算法使得输出值等于输⼊值的神经⽹络,它先将输⼊压缩成潜在空间表征,然后通过这种表征来重构输出。
⾃编码器由两部分组成:
编码器(encoder):这部分能将输⼊压缩成潜在空间表征,可以⽤编码函数h=f(x)表⽰。
解码器(decoder):这部分重构来⾃潜在空间表征的输⼊,可以⽤解码函数r=g(h)表⽰。
因此,整个⾃编码器可以⽤函数g(f(x)) = r 来描述,其中输出r与原始输⼊x相近。
⼀、为何要⽤输⼊来重构输出?
如果⾃编码器的唯⼀⽬的是让输出值等于输⼊值,那种⼈个算法将毫⽆⽤处。事实上,我们希望通过训练输出值等于输⼊值的⾃编码器,让潜在表征h将具有价值属性。
这可通过在重构任务中构建约束来实现。
从⾃编码器获得有⽤特征的⼀种⽅法是,限制h的维度使其⼩于输⼊x,这种情况下称作有损⾃编码器。通
过训练有损表征,使得⾃编码器能学习到数据中最重要的特征。
如果潜在表征的维度与输⼊相同,或是在完备案例中潜在表征的维度⼤于输⼊,上述结果也会出现。
在这些情况下,即使只使⽤线性编码器和线性解码器,也能很好地利⽤输⼊重构输出,且⽆需了解有关数据分布的任何有⽤信息。
在理想情况下,根据要分配的数据复杂度,来准确选择编码器和解码器的编码维数和容量,就可以成功地训练出任何所需的⾃编码器结构。⼆、⾃编码器⽤来⼲什么?
⽬前,⾃编码器的应⽤主要有两个⽅⾯:
1.第⼀是数据去噪
2.第⼆是为进⾏可视化⽽降维。
设置合适的维度和稀疏约束,⾃编码器可以学习到PCA等技术更有意思的数据投影。
⾃编码器能从数据样本中进⾏⽆监督学习,这意味着可以将这个算法应⽤到某个数据集中,来取得良好的性能,且不需要任何新的特征⼯程,只需要适当地训练数据。
但是,⾃编码器在图像压缩⽅⾯表现的不好。由于在某个给定数据集上训练⾃编码器,因此它在处理与训练集相类似的数据时可达到合理的压缩结果,但是在压缩差异较⼤的其他图像时效果不佳。这⾥,像JPEG这样的压缩技术在通⽤图像压缩⽅⾯会表现得更好。
训练⾃编码器,可以使输⼊通过编码器和解码器后,保留尽可能多的信息,但也可以训练⾃编码器来使新表征具有多种不同的属性。不同类型的⾃编码器旨在实现不同类型的属性。下⾯将重点介绍四中不同的⾃编码器。
三、四种不同的⾃编码器
本⽂将介绍以下四种不同的⾃编码器:
1. vanilla⾃编码器
2. 多层⾃编码器
3. 卷积⾃编码器
4. 正则⾃编码器
vanilla⾃编码器
在这种⾃编码器的最简单结构中,只有三个⽹络层,即只有⼀个隐藏层的神经⽹络。它的输⼊和输出是相同的,可通过使⽤Adam优化器和均⽅误差损失函数,来学习如何重构输⼊。
在这⾥,如果隐含层维数(64)⼩于输⼊维数(784),则称这个编码器是有损的。通过这个约束,来迫使神经⽹络来学习数据的压缩表征。
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)
autoencoderpile(optimizer='adam', loss='mse')
多层⾃编码器
如果⼀个隐含层还不够,显然可以将⾃动编码器的隐含层数⽬进⼀步提⾼。
在这⾥,实现中使⽤了3个隐含层,⽽不是只有⼀个。任意⼀个隐含层都可以作为特征表征,但是为了使⽹络对称,我们使⽤了最中间的⽹络层。
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)
autoencoderpile(optimizer='adam', loss='mse')
卷积⾃编码器
你可能有个疑问,除了全连接层,⾃编码器应⽤到卷积层吗?
答案是肯定的,原理是⼀样的,但是要使⽤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)
autoencoderpile(optimizer='adam', loss='mse')
正则⾃编码器
除了施加⼀个⽐输⼊维度⼩的隐含层,⼀些其他⽅法也可⽤来约束⾃编码器重构,如正则⾃编码器。
正则⾃编码器不需要使⽤浅层的编码器和解码器以及⼩的编码维数来限制模型容量,⽽是使⽤损失函数来⿎励模型学习其他特性(除了将输⼊复制到输出)。这些特性包括稀疏表征、⼩导数表征、以及对噪声或输⼊缺失的鲁棒性。
即使模型容量⼤到⾜以学习⼀个⽆意义的恒等函数,⾮线性且过完备的正则⾃编码器仍然能够从数据中学到⼀些关于数据分布的有⽤信息。
在实际应⽤中,常⽤到两种正则⾃编码器,分别是稀疏⾃编码器和降噪⾃编码器。
稀疏⾃编码器:
⼀般⽤来学习特征,以便⽤于像分类这样的任务。稀疏正则化的⾃编码器必须反映训练数据集的独特统计特征,⽽不是简单地充当恒等函数。以这种⽅式训练,执⾏附带稀疏惩罚的复现任务可以得到能学习有⽤特征的模型。
还有⼀种⽤来约束⾃动编码器重构的⽅法,是对其损失函数施加约束。⽐如,可对损失函数添加⼀个正则化约束,这样能使⾃编码器学习到数据的稀疏表征。
要注意,在隐含层中,我们还加⼊了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正则项
tensorflow入门教程# Decoder
r = Dense(output_size, activation='sigmoid')(h)
autoencoder = Model(input=x, output=r)
autoencoderpile(optimizer='adam', loss='mse')
降噪⾃编码器:
这⾥不是通过对损失函数施加惩罚项,⽽是通过改变损失函数的重构误差项来学习⼀些有⽤信息。
向训练数据加⼊噪声,并使⾃编码器学会去除这种噪声来获得没有被噪声污染过的真实输⼊。因此,这就迫使编码器学习提取最重要的特征并学习输⼊数据中更加鲁棒的表征,这也是它的泛化能⼒⽐⼀般编码器强的原因。
这种结构可以通过梯度下降算法来训练。
x = Input(shape=(28, 28, 1))
# Encoder
conv1_1 = Conv2D(32, (3, 3), activation='relu', padding='same')(x)
pool1 = MaxPooling2D((2, 2), padding='same')(conv1_1)
conv1_2 = Conv2D(32, (3, 3), activation='relu', padding='same')(pool1)
h = MaxPooling2D((2, 2), padding='same')(conv1_2)
# Decoder
conv2_1 = Conv2D(32, (3, 3), activation='relu', padding='same')(h)
up1 = UpSampling2D((2, 2))(conv2_1)
conv2_2 = Conv2D(32, (3, 3), activation='relu', padding='same')(up1)
up2 = UpSampling2D((2, 2))(conv2_2)
r = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(up2)
autoencoder = Model(input=x, output=r)
autoencoderpile(optimizer='adam', loss='mse')
使⽤tensorflow实现对⼿写字(MNIST)的AutoEncoder
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# Import MNIST data
ist import input_data
mnist = ad_data_sets("/tmp/data/", one_hot=False)
# Visualize decoder setting
# Parameters
learning_rate = 0.01
batch_size = 256
display_step = 1
examples_to_show = 10
# Network Parameters
n_input = 784 # 28x28 pix,即 784 Features
# tf Graph input (only pictures)
X = tf.placeholder("float", [None, n_input])
# hidden layer settings
n_hidden_1 = 256 # 经过第⼀个隐藏层压缩⾄256个
n_hidden_2 = 128 # 经过第⼆个压缩⾄128个
#两个隐藏层的 weights 和 biases 的定义
weights = {
'encoder_h1': tf.Variable(tf.random_normal([n_input, n_hidden_1])),
'encoder_h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),
'decoder_h1': tf.Variable(tf.random_normal([n_hidden_2, n_hidden_1])),
'decoder_h2': tf.Variable(tf.random_normal([n_hidden_1, n_input])),
}
biases = {
'encoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'encoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'decoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'decoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'decoder_b2': tf.Variable(tf.random_normal([n_input])),
}
# Building the encoder
def encoder(x):
# Encoder Hidden layer 使⽤的 Activation function 是 sigmoid #1
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['encoder_h1']),
biases['encoder_b1']))
# Decoder Hidden layer with sigmoid activation #2
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['encoder_h2']),
biases['encoder_b2']))
return layer_2
# Building the decoder
def decoder(x):
# Encoder Hidden layer with sigmoid activation #1
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['decoder_h1']),
biases['decoder_b1']))
# Decoder Hidden layer with sigmoid activation #2
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['decoder_h2']),
biases['decoder_b2']))
return layer_2
'''
# Visualize encoder setting
# 只显⽰解压后的数据
learning_rate = 0.01 # 0.01 this learning rate will be better! Tested
training_epochs = 10
batch_size = 256
display_step = 1
# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
# tf Graph input (only pictures)
X = tf.placeholder("float", [None, n_input])
# hidden layer settings
n_hidden_1 = 128
n_hidden_2 = 64
n_hidden_3 = 10
n_hidden_4 = 2 #将原有784Features 的数据压缩成2 Features数据
weights = {
'encoder_h1': tf.uncated_normal([n_input, n_hidden_1],)),
'encoder_h2': tf.uncated_normal([n_hidden_1, n_hidden_2],)),
'encoder_h3': tf.uncated_normal([n_hidden_2, n_hidden_3],)),
'encoder_h4': tf.uncated_normal([n_hidden_3, n_hidden_4],)),
'decoder_h1': tf.uncated_normal([n_hidden_4, n_hidden_3],)),
'decoder_h2': tf.uncated_normal([n_hidden_3, n_hidden_2],)),
'decoder_h3': tf.uncated_normal([n_hidden_2, n_hidden_1],)),
'decoder_h4': tf.uncated_normal([n_hidden_1, n_input],)),
}
biases = {
'encoder_b1': tf.Variable(tf.random_normal([n_hidden_1])),
'encoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'encoder_b3': tf.Variable(tf.random_normal([n_hidden_3])),
'encoder_b4': tf.Variable(tf.random_normal([n_hidden_4])),
'decoder_b1': tf.Variable(tf.random_normal([n_hidden_3])),
'decoder_b2': tf.Variable(tf.random_normal([n_hidden_2])),
'decoder_b3': tf.Variable(tf.random_normal([n_hidden_1])),
'decoder_b4': tf.Variable(tf.random_normal([n_input])),#注意:在第四层时,输出量不再是 [0,1] 范围内的数, #⽽是将数据通过默认的 Linear activation function 调整为 (-∞,∞)
}
def encoder(x):
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['encoder_h1']),
biases['encoder_b1']))
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论