用TensorFlow实现自编码器Autoencoders

技术交流QQ群:1027579432,欢迎你的加入!

欢迎关注我的微信公众号:CurryCoder的程序人生

1.简介

  • 自编码器是一种特殊的神经网络模型,它可以从无标签的训练集中训练得到,是一种无监督学习算法。它不仅可以用来降维,也可以作为一种生成模型,这表示此模型可以从输入数据中生成新的数据。本文中主要介绍不同类型的自编码器,并用TensorFlow进行实现。

2.原理

  • 自编码器将原始的数据作为输入,然后将输入转化成有效的内部表现形式,输出数据看上去和输入是相似的。换句话说,为了生成新的数据来近似表示原始输入,自编码器从原始输入中寻找模型。
  • 自编码器的组成:
    • (1)编码器或识别网络:将原始的输入转化成简单的内部表示形式;
    • (2)解码器或生成网络:将简单的内部表示形式转化成整个网络的输出;


      自编码器的结构.jpg
  • 注意:在一个自编码器中,输入层和输出层的神经元数目是相同的;为了让神经网络能够从原始输入数据中学习到最重要的特征,所以隐藏层的神经元数目必须比输入层的神经元数目少。也就是说,自编码器不是简单将输入复制到输出。因为,隐藏层比输出层的维度更低。

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()
    
    原始数据可视化.jpg
  • 训练一个自编码器对上述的数据进行降维,达到类似于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()
    
降维后的结果可视化.jpg
  • (2)堆自编码器(stacked autoencoders)
    • 类似于深层的神经网络,自编器也可以有多个隐藏层,称这一类的自编码器为堆自编码器。多个隐藏层表示神经网络可以学习更加复杂的特征,但是多个隐藏层可以使得自编码器更加有可能对输入数据过拟合,模型的泛化性能不好。堆编码器的结构如图所示:


      stacked autoencoders.jpg
    • 以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")
      
    stacked autoencoders结果.jpg
  • (3) 变分自编码器(variational autoencoders)
    • 变分自编码器是一种重要的自编码器类型,2014年首次提出。变分自编码器结构如下图所示,它与传统的自编码器主要有两点不同:
      • a.VAE是概率自编码器,意味着输出结果部分是偶然的,即使在训练之后也是如此;
      • b. VAE是生成性自编码器,它们可以生成看起来像输入数据的新数据;


        变分自编码器.jpg
    • 解释:上图中,自编码器编码生成一个均值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})
      
    变分自编器结果.jpg
    • 解释:使用变分自编码器,可以得到 非常近似与原始MNIST数据集中的数据,VAE成功的学习到了输入数据中的最重要的特征。

你可能感兴趣的:(用TensorFlow实现自编码器Autoencoders)