技术交流QQ群:1027579432,欢迎你的加入!
欢迎关注我的微信公众号:CurryCoder的程序人生
1.简介
- 自编码器是一种特殊的神经网络模型,它可以从无标签的训练集中训练得到,是一种无监督学习算法。它不仅可以用来降维,也可以作为一种生成模型,这表示此模型可以从输入数据中生成新的数据。本文中主要介绍不同类型的自编码器,并用TensorFlow进行实现。
2.原理
- 自编码器将原始的数据作为输入,然后将输入转化成有效的内部表现形式,输出数据看上去和输入是相似的。换句话说,为了生成新的数据来近似表示原始输入,自编码器从原始输入中寻找模型。
- 自编码器的组成:
- (1)编码器或识别网络:将原始的输入转化成简单的内部表示形式;
-
(2)解码器或生成网络:将简单的内部表示形式转化成整个网络的输出;
- 注意:在一个自编码器中,输入层和输出层的神经元数目是相同的;为了让神经网络能够从原始输入数据中学习到最重要的特征,所以隐藏层的神经元数目必须比输入层的神经元数目少。也就是说,自编码器不是简单将输入复制到输出。因为,隐藏层比输出层的维度更低。
3.实战
- (1)降维
- 自编码器的一个应用就是降维,如果神经网络的激活函数使用线性激活函数,代价函数使用均方误差MSE,可以达到像PCA进行数据降维的效果。原始数据可视化如下:
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from sklearn.preprocessing import StandardScaler # 特征缩放 m = 200 w1, w2 = 0.1, 0.3 # 权重初始化 noise = 0.1 # 噪声数据 angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5 data = np.empty((m, 3)) # 原始数据初始化 data[:, 0] = np.cos(angles) + np.sin(angles) / 2 + noise * np.random.rand(m) / 2 data[:, 1] = np.sin(angles) * 0.7 + noise * np.random.rand(m) / 2 data[:, 2] = data[:, 0] * w1 + data[:, 1] * w2 + noise * np.random.rand(m) scaler = StandardScaler() x_train = scaler.fit_transform(data[:100]) x_test = scaler.fit_transform(data[100:]) # 原始数据可视化 fig = plt.figure(figsize=[12, 9]) ax = plt.axes(projection='3d') ax.scatter3D(data[:, 0], data[:, 1], data[:, 2]); plt.show()
- 训练一个自编码器对上述的数据进行降维,达到类似于PCA的效果:
import tensorflow as tf # 训练一个AE进行降维 n_inputs = 3 n_hidden = 2 n_outputs = 3 learning_rate = 0.01 # 定义自编码器的结构 X = tf.placeholder(tf.float32, shape=[None, n_inputs]) hidden = tf.layers.dense(X, n_hidden) outputs = tf.layers.dense(hidden, n_outputs) # 定义损失函数和优化器 loss_fun = tf.reduce_mean(tf.square(outputs - X)) optimizer = tf.train.AdamOptimizer(learning_rate) training_op = optimizer.minimize(loss_fun) init = tf.global_variables_initializer() n_iterations = 1000 codings = hidden # 训练开始 with tf.Session() as sess: init.run() for epoch in range(n_iterations): training_op.run(feed_dict={X:x_train}) codings_val = codings.eval(feed_dict={X:x_test}) # 降维后的结果可视化 fig = plt.figure(figsize=[12, 9]) plt.plot(codings_val[:, 0], codings_val[:, 1], 'k.') plt.xlabel('$z_1$', fontsize=18) plt.ylabel('$z_2$', fontsize=18, rotation=0) plt.show()
- (2)堆自编码器(stacked autoencoders)
-
类似于深层的神经网络,自编器也可以有多个隐藏层,称这一类的自编码器为堆自编码器。多个隐藏层表示神经网络可以学习更加复杂的特征,但是多个隐藏层可以使得自编码器更加有可能对输入数据过拟合,模型的泛化性能不好。堆编码器的结构如图所示:
- 以MNIST手写数字体数据集为例,堆自编码器实战如下:
# stacked AE from tensorflow.examples.tutorials.mnist import input_data from functools import partial import sys mnist = input_data.read_data_sets("E:\\DeepLearning\\jupyter_code\\dataset\\MNIST_data\\") n_inputs = 28 * 28 n_hidden1 = 300 n_hidden2 = 150 n_hidden3 = n_hidden1 n_outputs = n_inputs learning_rate = 0.01 l2_reg = 0.0001 # L2正则化,惩罚因子 X = tf.placeholder(tf.float32, shape=[None, n_inputs]) he_init = tf.contrib.layers.variance_scaling_initializer() l2_regularizer = tf.contrib.layers.l2_regularizer(l2_reg) my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu, kernel_initializer=he_init, kernel_regularizer=l2_regularizer) hidden1 = my_dense_layer(X, n_hidden1) hidden2 = my_dense_layer(hidden1, n_hidden2) hidden3 = my_dense_layer(hidden2, n_hidden3) outputs = my_dense_layer(hidden3, n_outputs, activation=None) reconstruction_loss = tf.reduce_mean(tf.square(outputs - X)) reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) loss = tf.add_n([reconstruction_loss] + reg_losses) optimizer = tf.train.AdamOptimizer(learning_rate) training_op = optimizer.minimize(loss) init = tf.global_variables_initializer() saver = tf.train.Saver() n_epochs = 5 batch_size = 150 # 训练开始 with tf.Session() as sess: init.run() for epoch in range(n_epochs): n_batches = mnist.train.num_examples // batch_size # batch_size的个数 for i in range(n_batches): print("\r{}%".format(100 * i // n_batches), end="") sys.stdout.flush() X_batch, y_batch = mnist.train.next_batch(batch_size) sess.run(training_op, feed_dict={X:X_batch}) loss_train = reconstruction_loss.eval(feed_dict={X:X_batch}) print("\r{}".format(epoch), "Train MSE:", loss_train) saver.save(sess, "./my_model_all_layers.ckpt")
-
- (3) 变分自编码器(variational autoencoders)
- 变分自编码器是一种重要的自编码器类型,2014年首次提出。变分自编码器结构如下图所示,它与传统的自编码器主要有两点不同:
- a.VAE是概率自编码器,意味着输出结果部分是偶然的,即使在训练之后也是如此;
-
b. VAE是生成性自编码器,它们可以生成看起来像输入数据的新数据;
- 解释:上图中,自编码器编码生成一个均值mu和标准差sigma的数据。 然后,从中随机抽样得到均值为mu、标准差为sigma的高斯分布数据。 然后,将其进行解码后输出。代价函数采用latent loss,确保变分自编码器采样得到的数据来自一个简单的高斯分布。
- VAE实战代码如下:
# VAE变分自编码器 n_inputs = 28 * 28 n_hidden1 = 500 n_hidden2 = 500 n_hidden3 = 20 n_hidden4 = n_hidden2 n_hidden5 = n_hidden1 n_outputs = n_inputs learning_rate = 0.001 initializer = tf.contrib.layers.variance_scaling_initializer() my_dense_layer = partial(tf.layers.dense, activation=tf.nn.relu, kernel_initializer=initializer) X = tf.placeholder(tf.float32, shape=[None, n_inputs]) hidden1 = my_dense_layer(X, n_hidden1) hidden2 = my_dense_layer(hidden1, n_hidden2) hidden3_mu = my_dense_layer(hidden2, n_hidden3, activation=None) hidden3_sigma = my_dense_layer(hidden2, n_hidden3, activation=None) noise = tf.random_normal(tf.shape(hidden3_sigma), dtype=tf.float32) hidden3 = hidden3_mu + hidden3_sigma * noise hidden4 = my_dense_layer(hidden3, n_hidden4) hidden5 = my_dense_layer(hidden4, n_hidden5) logits = my_dense_layer(hidden5, n_outputs, activation=None) outputs = tf.sigmoid(logits) x_entropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=X, logits=logits) reconstruction_loss = tf.reduce_sum(x_entropy) eps = 1e-10 # 防止出现log(0) latent_loss = 0.5 * tf.reduce_sum(tf.square(hidden3_sigma) + tf.square(hidden3_mu) - 1 - tf.log(eps + tf.square(hidden3_sigma))) loss = latent_loss + reconstruction_loss optimizer = tf.train.AdamOptimizer(learning_rate) training_op = optimizer.minimize(loss) init = tf.global_variables_initializer() saver = tf.train.Saver() n_digits = 60 n_epochs = 50 batch_size = 150 with tf.Session() as sess: init.run() for epoch in range(n_epochs): n_batches = mnist.train.num_examples // batch_size for i in range(n_batches): print("\r{}%".format(100 * i // n_batches), end="") sys.stdout.flush() X_batch, y_batch = mnist.train.next_batch(batch_size) sess.run(training_op, feed_dict={X:X_batch}) loss_val, reconstruction_loss_val, latent_loss_val = sess.run([loss, reconstruction_loss, latent_loss], feed_dict={X:X_batch}) print("\r{}".format(epoch), "Train total loss:", loss_val, "\tReconstruction loss:", reconstruction_loss_val, "\Latent loss:", latent_loss_val) saver.save(sess, "./my_model_variational.ckpt") codings_rnd = np.random.normal(size=[n_digits, n_hidden3]) outputs_val = outputs.eval(feed_dict={hidden3: codings_rnd})
- 解释:使用变分自编码器,可以得到 非常近似与原始MNIST数据集中的数据,VAE成功的学习到了输入数据中的最重要的特征。
- 变分自编码器是一种重要的自编码器类型,2014年首次提出。变分自编码器结构如下图所示,它与传统的自编码器主要有两点不同: