要想取得好的特征只靠重构输入数据是不够的,在实际应用中,还需要让这些特征具有抗干扰的能力,即当输入数据发生一定程度的扰动时,生成的特征仍然保持不变。这时需要添加噪声来为模型增加更大的困难。在这种情况下训练出来的模型才会有更好的鲁棒性。
去噪自动编码器(Denoising Autoencoder, DA),是在自动编码的基础上,训练数据加入噪声,输出的标签仍是原始的样本,这样自动编码器必须学习去除噪声而获得真正的没有被噪声污染过的输入特征。因此,这就迫使编码器去学习输入信号的更加鲁棒的特征表达,即具有更加强悍的泛化能力。
在实际训练中,人为加入的噪声有两种途径:
(1)在选择训练数据集时,额外选择一些样本集以外的数据。
(2)改变已有的样本数据集中的数据(使样本个体不完整,或通过噪声与样本进行的加减乘除之类的运算,使样本数据发生变化)
实例:使用去噪自编码网络提取MNIST特征,将784维压缩成256维
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("/data/", one_hot=True)
train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels
tf.reset_default_graph()
n_input = 784
n_hidden_1 = 256
x = tf.placeholder("float", [None, n_input])
y = tf.placeholder("float", [None, n_input])
dropout_keep_prob = tf.placeholder("float")
# 学习参数
weights = {
"h1": tf.Variable(tf.random_normal([n_input, n_hidden_1])),
"h2": tf.Variable(tf.random_normal([n_hidden_1, n_hidden_1])),
"out": tf.Variable(tf.random_normal([n_hidden_1, n_input]))
}
biases = {
"b1": tf.Variable(tf.zeros([n_hidden_1])),
"b2": tf.Variable(tf.zeros([n_hidden_1])),
"out": tf.Variable(tf.zeros([n_input]))
}
# 网络模型
def denoise_auto_encoder(_X, _weights, _biases, _keep_prob):
layer_1 = tf.nn.sigmoid(tf.add(tf.matmul(_X, _weights["h1"]), _biases["b1"]))
layer_1out = tf.nn.dropout(layer_1, _keep_prob)
layer_2 = tf.nn.sigmoid(tf.add(tf.matmul(layer_1out, _weights["h2"]), _biases["b2"]))
layer_2out = tf.nn.dropout(layer_2, _keep_prob)
lout = tf.matmul(layer_2out, _weights["out"]) + _biases["out"]
out = tf.nn.sigmoid(lout)
return out
reconstruction = denoise_auto_encoder(x, weights, biases, dropout_keep_prob)
# 使用平方损失
cost = tf.reduce_mean(tf.pow(reconstruction - y, 2))
optm = tf.train.AdamOptimizer(0.01).minimize(cost)
# 训练参数
epochs = 20
batch_size = 256
disp_step = 2
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(epochs):
num_batch = int(mnist.train.num_examples/batch_size)
total_cost = 0.
for i in range(num_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
# 加入噪声
batch_xs_noisy = batch_xs + 0.3 * np.random.randn(batch_size, 784)
feeds = {x: batch_xs_noisy, y: batch_xs, dropout_keep_prob: 1.}
sess.run(optm, feed_dict=feeds)
total_cost += sess.run(cost, feed_dict=feeds)
# 打印日志
if epoch % disp_step == 0:
print ("Epoch %02d/%02d average cost: %.6f"
% (epoch, epochs, total_cost/num_batch))
print("Done")
show_num = 10
test_noisy = mnist.test.images[:show_num] + 0.3*np.random.randn(show_num, 784)
encode_decode = sess.run(
reconstruction, feed_dict={x: test_noisy, dropout_keep_prob: 1.})
f, a = plt.subplots(3, 10, figsize=(10, 3))
for i in range(show_num):
a[0][i].imshow(np.reshape(test_noisy[i], (28, 28)))
a[1][i].imshow(np.reshape(mnist.test.images[i], (28, 28)))
a[2][i].matshow(np.reshape(encode_decode[i], (28, 28)), cmap=plt.get_cmap('gray'))
plt.show()
#换一种噪声测试一个
randidx = np.random.randint(test_X.shape[0], size=1)
orgvec = test_X[randidx, :]
testvec = test_X[randidx, :]
label = np.argmax(test_Y[randidx, :], 1)
print ("label is %d" % (label))
# Noise type
print ("Salt and Pepper Noise")
noisyvec = testvec
rate = 0.15
noiseidx = np.random.randint(test_X.shape[1]
, size=int(test_X.shape[1]*rate))
noisyvec[0, noiseidx] = 1-noisyvec[0, noiseidx]
outvec = sess.run(reconstruction, feed_dict={x: noisyvec, dropout_keep_prob: 1})
outimg = np.reshape(outvec, (28, 28))
# Plot
plt.matshow(np.reshape(orgvec, (28, 28)), cmap=plt.get_cmap('gray'))
plt.title("Original Image")
plt.colorbar()
plt.matshow(np.reshape(noisyvec, (28, 28)), cmap=plt.get_cmap('gray'))
plt.title("Input Image")
plt.colorbar()
plt.matshow(outimg, cmap=plt.get_cmap('gray'))
plt.title("Reconstructed Image")
plt.colorbar()
plt.show()
第一行为加入噪声原图,第二行为原图,第三行为输出的灰度图,可以看出输出图基本上能过滤噪声。
对比第一张原图与第三张输出图,可以看出基本一致,即便加入一定噪声(第二张图)