自编码网络
文章目录
自编码网络介绍及应用
自编码网络是非监督学习领域中的一种,可以自动从无标注的数据中学习特征,是一种以重构输入信号为目标的神经网络,可以给出比原始数据更好的特征描述,具有较强的特征学习能力。
醉简单的自编码网络
自编码(Auto-Encoder,AE)网络是输入等于输出的网络,最基本的模型可以视为三层的神经网络,即输入层、隐藏层、输出层。其中,输入层的样本也会充当输出层的标签角色。换句话说,这个神经网络就是一种尽可能复现输入信号的神经网络。
其中从输入到中间状态的过程叫编码,从中间状态再回到输出的过程叫解码,这样构成的自动编码器可以捕捉代表输入数据最重要的因素,类似于PCA,找到可以代表原信息的主要成分。
自编码器要求输出尽可能等于输入,并且其隐藏层必须满足一定的稀疏性,是通过将影藏层中的后一层个数比前一层神经元个数少的方式来实现稀疏效果。相当于隐藏层对输入进行了压缩,并在输出层中解压缩。整个过程中肯定会丢失信息,但训练能够使丢失的信息尽量减少,最大化的保留其主要特征。如果激活函数不使用Sigmoid函数,而使用线性函数,那么便是PCA模型了。
自编码网络的代码实现
提取图片特征并利用特征还原图片
通过构建一个两层降维的自编码网络,将MNIST数据集的数据特征提取出来,并通过这些特征再重建一个MNIST数据集。
下面输入MNIST数据集的图片,将其像素点组成的数据(28x28=784)从784维降维到256,再降到128,最后再以同样的方式经过128再经过256,最终还原到原来的图片。过程如下图所示。
1 | import tensorflow as tf |
图片分为上下两行,第一行显示的是输入图片,第二行显示的输出图片。
线性解码器
上例中使用的激活函数输出范围是[0,1],当对最终提取的特征节点采用该激励函数时,就相当于对输入限制或缩放,是其位于[0,1]范围中,有些数据集如MNIST能方便的将输出缩放到[0,1]中,但是很难满足对输入值的要求。例,PCA白化处理的输入 并不满足[0,1]范围要求。
如果你用一个恒等式来作为激励函数,就可以解决这个问题,即将f(z)=z作为激励函数,即没有激励函数。
这个方法只是对最后的输出层而言,对于神经网络中隐含的神经元依然还要使用S型或其他激活函数。
由多个带有S型激活函数的隐含层及一个线性输出层构成的自编码器,称为线性解码器。
提取图片的二位特征,并利用二维特征还原图片
在自编码网络中使用线性解码器对MNIST数据特征进行再压缩,并将其映射到直角坐标系上。这里使用4层逐渐压缩将784维分别压缩成256、64、16、2这4个特征向量。
然后以直角坐标系的形式将数据点显示出来,这样可以更直观的看到自编码器对于同一类图片的聚类效果。
如果想得到更好的特征提取效果,可以将压缩的层数变得更多,(如512、256、128、64、32、16、2),由于Sigmoid函数天生缺陷,无法使用更深的层,所以这里只能做成4层。
1 | import tensorflow as tf |
实现卷积网络的自编码
1 | import tensorflow as tf |
去噪自编码
去噪自动编码器(Denoising Autoencoder,DA),在自动编码的基础上,训练数据加入噪声,输出的标签仍是原始的样本(没有加噪声),这样自动编码器必须学习去除噪声而获得真正没有被噪声污染过的输入特征。
在实际训练中,人为加入噪声有两种方式:
1)在选择训练数据集时,额外选择一些样本集以外的数据。
2)改变已有的样本数据集中的数据(使样本个体不完整,或通过噪声与样本进行的加减乘除之类的运算,使样本数据发生变化)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("../MNIST_data/", one_hot=True)
train_X = mnist.train.images
train_Y = mnist.train.labels
test_X = mnist.test.images
test_Y = mnist.test.labels
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)
return tf.nn.sigmoid(
tf.add(tf.matmul(layer_2out, weights['out']), biases['out']))
reconstruction = denoise_auto_encoder(x, weights, biases, dropout_keep_prob)
# cost计算
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())
print('开始训练')
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('完成')
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)
# 噪声类型
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))
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()
栈式自编码
栈式自编码介绍
栈式自编码神经网络(Stacked Autoencoder,SA),是对自编码网络的一种使用方法,是一个由多层训练好的自编码器组成的神经网络。由于网络中的每一层都是单独训练而来,相当于都初始化了一个合理的数值。所以,这样的网络会更容易训练,并且有更快的收敛性及更高的准确度。栈式自编码常被用于预训练(初始化)深度神经网络之前的权重预训练步骤。例如,在一个分类问题上,可以按照从前向后的顺序执行每一层通过自编码器来训练,最终将网络中最深层输出作为softmax分类器的输入特征,通过softmax层将其分开。
下面以训练一个包含两个隐含层的栈式自编码网络为例。
1)训练一个自编码器,得到原始输入的一阶特征表示h(1)
2)将上一步输出的特征h(1)作为输入,对其进行再一次的自编码,并同时获取特征h(2).
3)把上一步的特征h(2)连还是哪个softmax分类器,得到了一个图片数字标签分类的模型,结构如下图所示。
4)把这3层结合起来,就构成一个包含两个隐藏层加一个softmax的栈式自编码网络,可以对数字图片分类。
栈式自编码在深度学习中的意义
优点:
- 每一层都可以单独训练,保证将维特征的可控性
- 使用栈式自编码逐层降维,可以将复杂问题简单化。
- 任意深层,理论上是越深层的神经网络对现实的拟合度越高,但是传统的多层神经网络,由于使用的是误差反向传播方式,导致层越深,传播的误差越小。栈式自编码巧妙地绕过这个问题,直接使用降维后的特征值进行二次训练,可以任意层数的加深。
栈式自编码通常能够获取到输入的“层次型分组”或“部分-整体分解”结构,自编码器倾向于学习得到与样本相对应的低维向量,该向量可以更好地表示高维样本的数据特征。
如果网络的输入是图像,第一层会学习去识别边,二层会学习组合边、构成轮廓角等,更高层会学习组合更形象的特征。