TensorFlow学习(三)— 自编码器


自编码器的介绍请参考这里

准备

​ 导入所需要的包。

1
2
3
4
import numpy as np
import sklearn.preprocessing as pre
import tensorflow as tf
from tensorflow.example.tutorials.mnist import input_data

定义函数

参数初始化方法 xavier initialization

这里是初始化参数的方法,不是初始化参数!

为什么要参数初始化?

​ 如果神经网络的权重初始化太小,那么我们的输入在经过每层计算后,逐渐缩小而难以产生作用,如果权重初始化太大,则计算后将逐渐增大并导致发散和失效。

​ 不适合的权重似的隐含层的输入方差过大,经过 sigmoid 激活函数时离中心较远,导数接近于零,从而导致提督弥散。

xavier权重初始化

​ xavier权重初始化可以减少梯度弥散,使输入可以传递的更深。

​ xavier根据某一层网络的输入、输出节点数量自动调整最合适的分布,让权重满足0均值,同时方差为 $\frac{2}{n_{in}+n_{out}}$ ,分布可以使用均匀分布或者高斯分布。

​ 下面我们创建一个 $\lgroup -\sqrt{\frac{6}{n_{in}+n_{out}}}, \sqrt{\frac{6}{n_{in}+n_{out}}} \rgroup$ 范围内的均匀分布,而它的方差根据 $D(x)={(max-min)^2/12}$ 正好等于 $\frac{2}{n_{in}+n_{out}}$ 。

1
2
3
4
5
# fan_in:输入节点的数量,fan_out: 输出节点的数量
def xavier_init(fan_in, fan_out, constant=1):
low = -constant * np.sqrt(6.0 / (fan_in + fan_out))
high = constant * np.sqrt(6.0 / (fan_in + fan_out))
return tf.random.unifom((fan_in, fan_out), minval=low, maxval=high, dtype=tf.float32)

定义自编码器类的构建函数

参数解释:

n_input: 输入层个数 n_hidden: 隐含层个数 transfer_function: 隐含层的激活函数,设为softplus

optimizer:优化器,设为Adam scale:高斯噪声系数,设为0.1

class内的scale参数设为一个placeholder 权重初始化使用定义的init_weights函数

这里我们只定义了一层隐含层,可以继续添加!

1
2
3
4
5
6
7
8
9
10
class AdditiveGaussianNoiseAutoencoder(object):
def __init__(self, n_input, n_hidden, transfer_function=tf.nn.softplus,
optimizer=tf.train.AdamOptimizer(), scale=0.1):
self.n_input = n_input
self.n_hidden = n_hidden
self.transfer = transfer_function
self.scale = tf.placeholder(tf.float32)
self.training_scale = scale
weights = self.init_weights()
self.weights = weights

定义权重初始化函数 init_weights

w1经过 xavier_init 函数初始化,传入输入层和隐含层的节点数,xavier会返回一个适合 softplus 等激活函数的权重初始分布,偏置 b1 全部置为0即可。对于输出层,因为没有使用激活函数,w2、b2 全部初始化为0。

1
2
3
4
5
6
7
def init_weights(self):
all_weights = dict()
all_weights['w1'] = tf.Variable(xavier_init(self.n_input, self.n_hidden))
all_weights['b1'] = tf.Variable(tf.zeros([self.n_hidden], dtype=tf.float32))
all_weights['w2'] = tf.Variable(tf.zeros([self.n_hidden, self.n_input], dtype=tf.float32))
sll_weights['b2'] = tf.Variable(tf.zeros([n_input], dtype=tf.float32))
return all_weights

定义网络结构

  • 输入层: self.x
  • 隐含层:首先计算 w1 * x + b ,然后带入激活函数 transfer 中计算。其中 x 为输入加上噪声即 self.x + scale * tf.random_normal({n_input,})
  • 重建层(输出层):将数据尽大的复原为输入的数据
1
2
3
4
5
6
self.x = tf.placeholder(tf.float, [None, self.n_input])
self.hidden = self.transfer(tf.add(tf.matmul(
self.x + scale * tf.random_normal({n_input,}),
self.weights['w1']), self.weights['b1']))
self.reconstruction = tf.add(tf.matmul(
self.hidden, self.weights['w2']), self.weights['b3'])

定义损失函数

使用平方误差函数(SSE)

1
2
3
self.cost = 0.5 * tf.reduce_sum(tf.pow(tf.subtract(
self.reconstruction, self.x), 2.0))
self.optimizer = optimizer.minimize(self.cost)

初始化全部参数

1
2
3
init = tf.global_variables_initializer()
self.sess = tf.Session()
self.sess.run(init)

定义计算损失 cost 及执行进一步训练的函数 partial_fit

功能:触发训练操作,对一个 batch 的数据进行训练并计算当前的损失!

​ 函数里只需让Session执行两个计算图的节点,分别是损失 cost 和训练过程 optimizer ,输入的 feed_dict 包括输入数据 x 、噪声的系数 scale

1
2
3
4
def partial_fit(self, x):
cost, opt = self.sess.run((self.cost, self.optimizer),
feed_Dict={self.x: x, self.scale: self.training_scale})
return cost

定义总损失函数 calc_total_cost

自编码器训练完后,用来在测试集数据上对数据模型进行评测

​ 与 partial_fit 函数不同,这里只让Session执行一个计算图节点 self.cost ,传入的参数和上一个函数一样。

1
2
3
def cal_total_cost(self, x):
return self.sess.run(self.cost, feed_dict={self.x: x,
self.scale: self.training.scale})

定义 transform 函数

功能:返回自编码器隐含层的输出结果。隐含层的主要功能是学习处数据中的高级特征,该函数目的是提供一个接口来得到经过隐含层抽象后的特征!

1
2
3
def transform(self, x):
return self.sess.run(self.hidden, feed_dict={self.x: x,
self.scale: self.training_scale})

定义 generate 函数

功能:返回自编码器重构层的输出结果。重构层的主要功能是将隐含层提取的高阶特征复原为原输入数据。

1
2
3
4
def generaate(self, hidden = None):
if hidden == None:
hidden = np.random.normal(size = self.weights['b1'])
return self.sess.run(self.reconstruction, feed_dict={self.hidden: hidden})

定义reconstruct 函数

功能:完整运行 输入数据 $\to$ 提取高级特征 $\to$ 复原数据 ,即同时包括 transformgenerate 函数。

1
2
3
def reconstruct(self, x):
return self.sess.run(self.reconstruction, feed_dict={self.x: x,
self.scale: self.training_scale})

定义获取参数函数

功能:得到隐含层的权重 w1 和偏置 b1

1
2
3
4
def getWeight(self):
return self.sess.run(self.weights['w1'])
def getBias(self):
return self.sess.run(self.weights['b1'])

去噪编码器类已经定义完成,包括对神经网络的设计、权重的初始化以及几个成员函数。


测试

我们使用TensorFlow提供的 MNIST 数据集进行测试。

1
input_data.read_data_sets('MNIST_data', one_hot=True)

定义数据标准化函数

即将数据变为均值为0, 便准差为1的分布。将数据减去均值,然后除以方差。

​ 使用 sklearn.preprocessingStandardScaler 类现在训练集上进行 fit ,再将这个 Scale 用在训练数据和测试数据上。保证训练数据和测试数据使用同一个 Scale ,使得后面模型处理数据时的一致性。

1
2
3
4
5
def Standard_scale(x_train, x_test):
preprocessor = prep.Stap yndardScale().fit(x_train)
x_train = preprocessor.transform(x_train)
x_test = preprocessor.transform(x_test)
retutn x_train, x_test

定义获取随机 batch_size 大小的数据函数

不放回的每次抽样!

1
2
3
def get_random_block_from_data(data, batch_seze):
start_index = np.random.randint(0, len(data)-batch_size)
return data[start_index : (start_index + batch_size)]

标准化训练集、测试集

1
x_train, x_test = Standard_scale(mnist.train.images, mnist.test.images)

定义常用参数

参数解释:

n_samples:总训练样本数 training_epoches:最大训练轮数 batch_size:批数据样本大小,设为128

1
2
3
4
n_samples = int(mnist.train.num_examples)
training_epoches = 20
batch_size = 128
display_step = 1

训练

实例化自编码器类

1
2
3
4
5
autoencoder = AdditiveGaussianNoiseAutoencoder(n_input=784,
n_hidden=200,
transfer_function = tf.nn.softplus,
optimizer=tf.train.AdamOptimizer(learning_rate=0.001),
scale=0.01)

开始训练

1
2
3
4
5
6
7
8
9
10
for epoch in range(training_epochs):
avg_cost = 0.
total_batch = int(n_samples / batch_size)
for i in range(total_size):
batch_xs = get_random_block_from_data(x_train, batch_size)
cost = autoencoder.partial_fit(batch_xs)
avg_cost += cost / n_sample * batch_size

if epoch % display_step == 0:
print("Epoch:", '%04d' %(epoch + 1), "cost=", ":.9f".format(avg_cost))

评测

​ 使用平方误差和来

1
print("total cost:" + autoencoder.cal_total_cost(x_test))

去噪自编码器的TensorFlow实现完成。

-------------本文结束感谢您的阅读-------------