一、自编码网络

自编码,又称自编码器(autoencoder),也是神经网络的一种,经过训练后能尝试将输入复制到输出。自编码器内部有隐藏层,通过编码和解码来还原输入数据。该网络可以看作由两部分组成:一个函数h=f(x)表示编码器和一个解码器r=g(h)。

自编码器是一个3层或大于3层的神经网络,它的作用是将输入样本压缩到隐藏层,然后解压,在输出端还原输入样本。最终输出层神经元数量等于输入层神经元数量。自编码器属于无监督学习,训练自编码网络不需要类标。

自编码网络主要由两个部分构成:压缩和解压。压缩依靠的是输入数据(图片、文字、声音)本身存在不同程度的冗余信息,自编码网络通过学习来去掉这些冗余信息,提取出有用的特征,来实现压缩。压缩过程与机器学习算法中的PCA(主成分分析)有些类似。在自编码网络中,如果激活函数不使用sigmoid等非线性函数,而使用线性函数,就是PCA模型。压缩是通过限制隐藏层神经元的数量,使得隐藏层输出神经元的数量小于输入神经元的数量,来提取有意义的特征。解压网络输入和输出的神经元数目与压缩的输入和输出神经元数目相反。

神经网络编码层的作用 神经网络自编码_数据

二、如何来构造一个自编码网络

想要设计一个神经网络,我们首先需要明确的就是代价函数如何定义。对于自编码网络,我们的目的是使得输出尽可能与输入相同,当然不是单纯的复制,那样整个网络就没有任何意义。所以,我们定义输出与输入平方误差的均值作为代价函数

在设计自编码网络的时候,我们可以考虑使用多个隐藏层。如果有多个隐藏层,网络就能更好的学习更高层的特征。对于输入数据为图片时,低层的网络提取的特征可能是直线、菱角、边缘、颜色等,而高层的网络则可以学习如何去组合这些特征。

三、实现一个自编码网络

整个自编码网络是通过MNIST手写数字数据集来训练的,在自编码网络训练完成之后通过从测试集中取出10张图片测试,从结果来看通过对输入图片进行压缩和解压之后基本还原了输入图片。

1、网络参数设置

import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt

'''
编码器解码器参数设置
'''
class Config(object):
    #设置学习率
    learning_rate = 0.001
    #训练轮数
    training_epochs = 20
    #设置batch size
    batch_size = 256
    #设置每多少轮输出一次训练结果
    print_epoch = 1
    #从测试集选择多少张图片验证结果
    test_to_show = 10
    #设置第一层隐藏层神经元的个数
    n_hidden1 = 256
    #设置第二层隐藏层神经元的个数
    n_hidden2 = 128
    #设置输入神经元的个数
    n_input = 784

2、自编码网络结构

'''
设置编码器和解码器的结构
'''
class Encoder_Decoder(object):
    def __init__(self,config):
        self.config = config
        #定义输入
        self.input_x = tf.placeholder("float",[None,config.n_input])
        self.weights = {
            #设置第一层编码器权重
            "encoder_w1":tf.Variable(tf.random_normal([self.config.n_input,self.config.n_hidden1])),
            #设置第二层编码器权重
            "encoder_w2":tf.Variable(tf.random_normal([self.config.n_hidden1,self.config.n_hidden2])),
            #设置第一层解码器权重
            "decoder_w1":tf.Variable(tf.random_normal([self.config.n_hidden2,self.config.n_hidden1])),
            #设置第二层解码器权重
            "decoder_w2":tf.Variable(tf.random_normal([self.config.n_hidden1,self.config.n_input]))
        }
        self.biases = {
            #设置第一层编码器偏置
            "encoder_b1":tf.Variable(tf.random_normal([self.config.n_hidden1])),
            #设置第二层编码器偏置
            "encoder_b2":tf.Variable(tf.random_normal([self.config.n_hidden2])),
            #设置第一层解码器偏置
            "decoder_b1":tf.Variable(tf.random_normal([self.config.n_hidden1])),
            #设置第二层解码器偏置
            "decoder_b2":tf.Variable(tf.random_normal([self.config.n_input]))
        }
        #编码
        self.encoder()
        #解码
        self.decoder()
        #定义平方差损失函数
        self.loss = tf.reduce_mean(tf.pow(self.input_x - self.decoder_layer2,2))
        #最小化损失函数
        self.Adam = tf.train.AdamOptimizer(config.learning_rate).minimize(self.loss)
    #编码器模型
    def encoder(self):
        #第一层编码器
        self.encoder_layer1 = tf.nn.sigmoid(
            tf.matmul(self.input_x,self.weights["encoder_w1"])+self.biases["encoder_b1"])
        #第二层编码器
        self.encoder_layer2 = tf.nn.sigmoid(
            tf.matmul(self.encoder_layer1,self.weights["encoder_w2"])+self.biases["encoder_b2"])

    #解码器模型
    def decoder(self):
        #第一层解码器
        self.decoder_layer1 = tf.nn.sigmoid(tf.matmul(
            self.encoder_layer2,self.weights["decoder_w1"])+self.biases["decoder_b1"])
        #第二层解码器
        self.decoder_layer2 = tf.nn.sigmoid(tf.matmul(
            self.decoder_layer1,self.weights["decoder_w2"])+self.biases["decoder_b2"])

3、训练模型

def training():
    #获取数据集
    mnist_data = input_data.read_data_sets("/MNIST_data")
    #初始化网络设置参数
    config = Config()
    #初始化网络
    encode_decode = Encoder_Decoder(config)
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        #计算总的迭代次数
        total_batch = (mnist_data.train.num_examples-1) // config.batch_size + 1
        for epoch in range(config.training_epochs):
            for i in range(total_batch):
                batch_xs,batch_ys = mnist_data.train.next_batch(config.batch_size)
                _,loss = sess.run([encode_decode.Adam,encode_decode.loss],
                                  feed_dict={encode_decode.input_x:batch_xs})
            if epoch % config.print_epoch == 0:
                print("epoch:%d,loss:%.3f"%(epoch+1,loss))
        #对测试集的进行编码
        test_encode_decode = sess.run(encode_decode.decoder_layer2,
                        feed_dict={encode_decode.input_x:mnist_data.test.images[:config.test_to_show]})
        #比较测试集和编码解码后的结果
        f,a = plt.subplots(2,10,figsize=(10,2))
        for i in range(config.test_to_show):
            #测试集的原始图片
            a[0][i].imshow(np.reshape(mnist_data.test.images[i],(28,28)))
            #编码解码后的图片
            a[1][i].imshow(np.reshape(test_encode_decode[i],(28,28)))
        f.show()
        plt.draw()
        plt.waitforbuttonpress()

4、输出结果

神经网络编码层的作用 神经网络自编码_数据_02

神经网络编码层的作用 神经网络自编码_权重_03

四、自编码器应用

从上面的例子来看,我们先将输入的图片进行压缩再解压,好像自编码器没有很多的作用。实则不然,通过编码网络我们可以将输入数据从784维压缩到128维,然后通过解码网络将编码后的数据从128维还原到784维,基本还原了输入数据。自编码器的主要应用是降维信息检索。通过自编码器,我们可以提取信息的主要特征,来减少信息量,提高信息检索的效率。通过自编码器,可以对数据进行压缩,来减少数据量。