Tensorflow基于minist数据集实现自编码器

自编码器

特征的稀疏表达:使用少量的基本特征组合拼装得到更高层抽象的特征。
如:图像碎片可由少量的基本结构稀疏表达。

自编码器定义:

  • 使用自身的高阶特征编码自己,目标是使用稀疏的一些高阶特征重新组合来重构自己。

自编码器特点:

  • 输入节点和输出节点的数量一致
  • 使用自身的高阶特征编码自己,不只是复制像素点

自编码器作用:

  • 给监督训练做无监督的预训练,提取特征
  • 可直接进行特征提取和分析

自编码器使用少量稀疏的高阶特征来重构输入,所以加入几种限制:

  • 如果限制隐含层的数量,中间隐含层节点数量小于输入输出节点数量,相当于降维的过程,只能学习输入数据最重要的特征,将不太相关的内容去除。
  • 如果给数据加噪声(通常是加性高斯噪声Additive Gaussian Noise,AGN),那么就是Denoising AutoEncoder(去噪自编码器),将从噪声中学习出数据的特征,学习数据频繁出现的模式和结构,将无规律的噪声去除。
    在这里插入图片描述
    在这里插入图片描述

Denoising AutoEncoder(去噪自编码器)

"""
    作者:Heart Sea
    功能:tessorflow实现去噪自编码器Additive Gaussian Noise,AGN
    Model:
        1个输入层, 加噪声
        全连接
        1个隐含层, 激活
        全连接
        1个输出层
    版本:1.0
    日期:10/10/2019
"""

import numpy as np                       # 导入常用库numpy
import sklearn.preprocessing as prep     # 导入sklearn中的preprocessing模块,负责预处理数据标准化
import tensorflow as tf                  # 导入tensorflow
from tensorflow.examples.tutorials.mnist import input_data    # 导入mnist数据集


# 实现标准均匀分布的Xaiver初始化器(根据某一层网络的输入、输出节点数量自动调整最合适的权重分布)
# Xaiver就是让权值满足:均值=0,方差=2/(n_in+n_out)
# 分布可以用均匀分布或者高斯分布,这里采用均匀分布,方差=2/(n_in+n_out)=(max-min)^2/12
def xavier_init_(fan_in, fan_out, constant=1):  # xavier_init_(self.n_input, self.n_hidden)
    """
    :param fan_in: 输入节点的数量, 行数
    :param fan_out: 输出节点的数量, 列数
    :param constant: 1
    :return: 返回一个比较适合softplus等激活函数的权重初始分布w1
    """
    low = -constant*np.sqrt(6.0/(fan_in + fan_out))
    high = constant*np.sqrt(6.0/(fan_in + fan_out))
    return tf.random_uniform((fan_in, fan_out),
                             minval=low, maxval=high,
                             dtype=tf.float32)


# 定义一个去噪自编码的class,此类包括一个构建函数_init_(),还有一些成员函数
class AdditiveGaussianNoiseAutoencoder(object):
    def __init__(self, n_input, n_hidden, transfer_function=tf.nn.softplus,
                 optimizer=tf.train.AdadeltaOptimizer(), scale=0.1):
        """
        :param n_input: 输入变量数
        :param n_hidden: 隐含层节点数
        :param transfer_function: 隐含层激活函数,默认为softplus
        :param optimizer: 默认为Adam
        :param 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
        network_weights = self._initialize_weights()  # 使用_initialize_weights()函数初始化参数,后面会定义
        self.weights = network_weights

    # 定义网格结构:输入层,隐含层,输出层
        # x每行是一个样本,列是特征
        self.x = tf.placeholder(tf.float32, [None, self.n_input])
        # 建立能提取特征的隐含层,給输入添加维度为(n_input,)的正态分布的噪声
        # tf.random_normal((n_input,))里的小括号可换成中括号,一行n_input列个正态随机个数,没有设置随机种子
        # self.x + scale * tf.random_normal((n_input,))相当于每个样本的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['b2'])

    # 定义自编码器的损失函数,用平方误差作为cost,输出与输入之差再平方,求和,0.5
        self.cost = 0.5 * tf.reduce_sum(tf.pow(tf.subtract(
            self.reconstruction, self.x), 2.0))         # 使用平方误差作为cost
        self.optimizer = optimizer.minimize(self.cost)  # 优化器为损失进行优化

        init = tf.global_variables_initializer()        # 初始化全部模型参数
        self.sess = tf.Session()                        # 建立Session
        self.sess.run(init)

    # 定义参数初始化函数_initialize_weights
    # 输出层权重和偏置不含激活函数,直接初始化为0
    def _initialize_weights(self):
        all_weights = dict()  # 创建字典dict
        all_weights['w1'] = tf.Variable(xavier_init_(self.n_input, self.n_hidden))   # 返回一个比较适合softplus等激活函数的权重初始分布
        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))
        all_weights['b2'] = tf.Variable(tf.zeros([self.n_input], dtype=tf.float32))
        return all_weights

    def partial_fit(self, X):
        """
        aim: 定义计算损失cost及执行一步训练的函数partial_fit
        :param X: 一个batch数据
        :return: 当前损失
        """
        cost, opt = self.sess.run((self.cost, self.optimizer),
                                  feed_dict={self.x: X, self.scale: self.training_scale})
        return cost

    def calc_total_cost(self, X):
        """
        aim: 定义一个只计算cost的函数,主要是在训练完毕后,在测试集上对模型性能进行测评,不会触发训练操作
        :param X: 测试集数据
        :return: 平方误差cost
        """
        return self.sess.run(self.cost, feed_dict={self.x: X,
                                                   self.scale: self.training_scale})

    def transform(self, X):
        """
        aim: 定义transform函数(自编码器的前半部分),目的是提供一个接口来获取高阶特征
        :param X:
        :return: 返回自编码器隐含层的输出结果 hidden
        """
        return self.sess.run(self.hidden, feed_dict={self.x: X,
                                                     self.scale: self.training_scale})

    def generate(self, hidden=None):
        """
        定义generate函数(自编码器的后半部分),将transform提取的高阶特征经过重建层复原为原始数据
        :param hidden: 隐含层的输出结果
        :return: 高阶特征经过重建层复原为原始数据
        """
        if hidden is None:
            hidden = np.random.normal(size=self.weights["b1"])
        return self.sess.run(self.reconstruction, feed_dict={self.hidden: hidden})

    def reconstruct(self, X):
        """
        整体运行transform和generate两个过程
        :param X: 原始数据
        :return: 复原的原始数据
        """
        return self.sess.run(self.reconstruction, feed_dict={self.x: X,
                                                             self.scale: self.training_scale})

    # 定义getWeights函数,获取隐含层的权重W1
    def getWeights(self):
        return self.sess.run(self.weights['w1'])

    # 定义getBiases函数,获取隐含层偏置系数b1
    def getBiases(self):
        return self.sess.run(self.weights['b1'])

# 去噪自编码器的类已经定义完,包括神经网络的设计、权重的初始化


mnist = input_data.read_data_sets('MNIST_data', one_hot=True)   # 载入minist数据集


# 定义standard_scale函数,对训练、测试数据标准化,
# 标准化即让数据变成0均值,且标准差为1的分布。方法是先减去均值,再除以标准差
# 为保证训练、测试数据使用完全相同的Scaler,需要先在训练数据上fit出一个共用的Scaler,再将这个Scaler用到训练数据和测试数据上
def standard_scale(X_train, X_test):
    preprocessor = prep.StandardScaler().fit(X_train)
    X_train = preprocessor.transform(X_train)
    X_test = preprocessor.transform(X_test)
    return X_train, X_test


# 定义一个随机取block数据的函数
# random.randint(a, b),用于生成一个指定范围a <= n <= b的整数
# 将这个随机数作为block的起始位置,顺序取到一个batch size的数据。这属于放回原样,可以提高数据利用率
def get_random_block_from_data(data, batch_size):
    start_index = np.random.randint(0, len(data) - batch_size)
    return data[start_index:(start_index + batch_size)]


# 使用standard_index函数标准化训练集,测试集
X_train, X_test = standard_scale(mnist.train.images, mnist.test.images)

# 定义常用参数
n_samples = int(mnist.train.num_examples)  # 总训练样本数
training_epochs = 20                       # 最大训练轮数
batch_size = 128                           # 批数据,即多少个样本作为一个batch
display_step = 1                           # 每隔一轮epoch显示一次损失cost

# 创建一个AGN自编码器的实例,并设置输入层节点数、隐含层节点数、隐含层激活函数、优化器、噪声系数
autoencoder = AdditiveGaussianNoiseAutoencoder(n_input=784,
                                               n_hidden=200,
                                               transfer_function=tf.nn.softplus,
                                               optimizer=tf.train.AdamOptimizer(learning_rate=0.001),
                                               scale=0.01)

# 开始训练
for epoch in range(training_epochs):                # 迭代训练轮数
    # print(epoch)
    avg_cost = 0.                                   # 每一轮开始时,平均损失=0
    total_batch = int(n_samples / batch_size)       # 样本总数/batch大小=需要的batch数
    for i in range(total_batch):                    # 迭代一轮中所有的批数据
        # print(i)
        batch_xs = get_random_block_from_data(X_train, batch_size)       # 随机抽取block数据

        cost = autoencoder.partial_fit(batch_xs)      # 使用partial_fit训练这个当前的batch数据的平方误差
        avg_cost += cost / n_samples * batch_size     # 将当前的cost整合到avg_cost,计算平均cost

# 每一轮迭代后,显示当前的迭代数和这一轮迭代的acg_cost
    if epoch % display_step == 0:
        print("Epoch:", '%04d' % (epoch + 1),
              "cost =", "{:.9f}".format(avg_cost))

# 对测试集进行测试,评价指标仍然是平方误差
print("Total cost: " + str(autoencoder.calc_total_cost(X_test)))

Epoch: 0001 cost= 19708.600550000
Epoch: 0002 cost= 13143.029138636


从实现的角度而言,自编码器其实和一个单隐含层的神经网络差不多,只是自编码器在数据输入时做了标准化处理,且加上了一个高斯噪声,同时我们的输出结果不是数字分类结果,而是复原的数据,因此不需要用标注过的数据进行监督训练。自编码器作为一种无监督学习方法,它与其他无监督学习的区别主要在于:它不是对数据进行聚类,而是把数据中最有用、最频繁的高阶特征提取出来,然后根据这些高阶特征进行重构数据。在深度学习发展早期非常流行的DBN,也是依靠这种思想,先对数据进行无监督学习,提取到一些有用的特征,将神经网络权重初始化到一个较好的分布,然后再使用有标注的数据进行监督训练,即对权重进行fine-tune。

现实生活中,大部分数据是没有标准信息的,但人脑比较擅长处理这些数据,会提取出其中的高阶抽象特征,并使用在别的地方。自编码器作为深度学习在无监督领域的应用的确非常成功,同时无监督学习也将成为深度学习一个重要发展方向。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值