SDAE(stacked denoised autoencoder ,堆栈去噪自编码器)是vincent大神提出的无监督的神经网络模型,论文:Stacked Denoising Autoencoders: Learning Useful Representations ina Deep Network with a Local Denoising Criterion,原文作者从不同角度解释了模型架构设计理念,非常值得一读。原文请戳:http://www.jmlr.org/papers/volume11/vincent10a/vincent10a.pdf
sDAE的思想就是将多个DAE堆叠在一起形成一个深度的架构. 需要注意的是, 只有在训练的时候才会对输入进行腐蚀(加噪), 一旦训练完成, 就不需要在进行腐蚀, 如下图所示
SDAE只是一个特征提取器,并不具有分类功能。为了使SDAE具有分类功能,需在其顶层添加分类器,如SVM、softmax等,并使用带标签的数据对SDAE进行有监督训练,最后使用利用BP算法对整个网络参数进行微调,便得到具有分类功能的SDAE。
具体步骤如下:
来自李宏毅的PPT的图可能更清楚理解其训练过程:
逐层贪婪训练:每层自编码层都单独进行非监督训练,以最小化输入(输入为前一层的隐层输出)与重构结果之间的误差为训练目标。前K层训练好了,就可以训练K+1层,因为已经前向传播求出K层的输出,再用K层的输出当作K+1的输入训练K+1层。
一旦SDAE训练完成, 其高层的特征就可以用做传统的监督算法的输入。当然,也可以在最顶层添加一层logistic regression layer(softmax层),然后使用带label的数据来进一步对网络进行微调(fine-tuning),即用样本进行有监督训练(如上图中最上层是10类分类)。
看代码吧:
##### 设置网络参数 #####
epochs_layer = 10 #每一层DAE训练迭代次数,100
epochs_whole = 20 #SDAE训练迭代次数,200
batch_size = 256
origin_dim = 784 #输入维数
h_dim1 = 256 # SDAE第1隐层的神经元数量
h_dim2 = 64 #
准备数据:
(x_train, _), (x_test, _) = mnist.load_data()
#.....
# 给数据添加噪声
noise_factor = 0.2
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)
单个DAE模型:
##### 构建单个DAE #####
class AutoEncoderLayer():
def __init__(self, input_dim, output_dim):
self.input_dim = input_dim
self.output_dim = output_dim
self.build()
def build(self):
self.input = Input(shape=(self.input_dim,))
self.encode_layer = Dense(self.output_dim, activation='relu') #或者sigmoid
self.encoded = self.encode_layer(self.input)
# 编码器部分
self.encoder = Model(self.input, self.encoded)
self.decode_layer = Dense(self.input_dim, activation='sigmoid')
self.decoded = self.decode_layer(self.encoded)
#DAE模型
self.autoencoder = Model(self.input, self.decoded)
SDAE模型设计(巧)
注意: SDAE是 E-E-D-D的形式,用DAE的编码结果组合而成,最后再fine-tuning
# 构建堆栈去噪自编码模型-SDAE
class StackedAutoEncoder():
def __init__(self, layer_list): #参数是一组DAE
self.layer_list = layer_list
self.build()
def build(self):
out = self.layer_list[0].encoded
for i in range(1, num_layers ): #堆栈过程:用DAE的编码器部分实现
out = self.layer_list[i].encode_layer(out)
self.model = Model(self.layer_list[0].input, out)
SDAE每一层的训练函数,也即,DAE的训练:
# 预训练每一层的DAE
def train_layers(encoder_list=None, layer=None, epochs=None, batch_size=None):
'''
预训练:逐层训练,当训练第layer个ae时,使用前(layer-1)个ae训练好的encoder的参数
:param encoder_list:
:param layer:
:param epochs:
:param batch_size:
:return:
'''
# 对前(layer-1)层用已经训练好的参数进行前向计算,ps:第0层没有前置层
out = x_train_noisy
origin = x_train
if layer != 0:
for i in range(layer):
# print("encoder weight", str(i), ":", encoder_list[i].encoder.get_weights()[0])
out = encoder_list[i].encoder.predict(out)
encoder_list[layer].autoencoder.summary()
encoder_list[layer].autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
# 训练第layer个DAE
encoder_list[layer].autoencoder.fit(
out,
origin if layer == 0 else out,
epochs=epochs,
batch_size=batch_size,
shuffle=True,
verbose=2
)
SDAE的fine_tuning函数:
#fine-tuning SDAE是用的函数
def train_whole(sae=None, epochs=None, batch_size=None):
'''
用预训练好的参数初始化stacked ae的参数,然后进行全局训练优化
:param model:
:param epochs:
:param batch_size:
:return:
'''
sae.model.summary()
sae.model.compile(optimizer='adadelta', loss='binary_crossentropy')
sae.model.fit(
x_train,
x_train,
epochs=epochs,
batch_size=batch_size,
shuffle=True,
validation_data=(x_test_noisy, x_test),
verbose=2
)
DAE的预训练:
# 训练过程
# 4层的stacked ae,实例化4个DAE
num_layers = 4
encoder_1 = AutoEncoderLayer(origin_dim, h_dim1)
encoder_2 = AutoEncoderLayer(h_dim1, h_dim2)
decoder_3 = AutoEncoderLayer(h_dim1, h_dim2)
decoder_4 = AutoEncoderLayer(h_dim1, origin_dim)
autoencoder_list = [encoder_1, encoder_2, decoder_3, decoder_4]
# DAE预训练:按照顺序对每一层进行预训练
print("Pre training:")
for level in range(num_layers - 1):
print("level:", level)
train_layers(encoder_list=autoencoder_list, layer=level, epochs=epochs_layer, batch_size=batch_size)
SDAE的fine-tuning训练:
# 用训练好的4个ae构建stacked dae
stacked_ae = StackedAutoEncoder(autoencoder_list)
print("Whole training:")
# 进行全局训练优化
train_whole(sae=stacked_ae, epochs=epochs_whole, batch_size=batch_size)
结果显示:
##### 显示stacked dae重构后的效果 #####
decoded_imgs = stacked_ae.model.predict(x_test_noisy)
n = 10
plt.figure(figsize=(20, 4))
for i in range(1, n):
# 展示原始图像
ax = plt.subplot(2, n, i)
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 + 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上的结果吧:
第1个DAE结构:
SDAE结构:
复原字符图像(建议增大迭代次数)
补,epoch=100, 效果还不错
SDAE编码结果:
SDAE真的这么神吗,还有什么用?.......欢迎大神交流......
(待续)
2 堆叠式降噪自动编码器(SDA)_Mr_Researcher 的博客-CSDN博客_堆叠降噪自编码器代码
3 https://github.com/MadhumitaSushil/SDAE