4.2 Tensorflow实现自编码器—Tensorflow实战

理解深度学习的特征学习——tensorflow实战(黄文坚)


4.2 Tensorflow实现自编码器—Tensorflow实战_第1张图片

4.2 Tensorflow实现自编码器—Tensorflow实战_第2张图片

好了,部分截图到此,了解更多自己看书去... .. .(然后你会发现后面的内容都是在抄书[捂脸])

下面开始抄代码... .. .

=================================================================

步骤一:导入各种模块

import numpy as n
import sklearn.preprocessing as prep #1
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data #2

① sklearn 中的 preprocessing 模块是一个常用于数据处理的模块,之后我们会死用它的数据标准化功能

②从Tensorflow的MNIST数据模块中导入数据

步骤二:定义自编码器的参数初始化方法

Xavier初始化方法是2010年提出来的一种很有效的神经网络初始化方法,需要深入理解请点击链接Understanding the difficulty of training deep feedforward neural networks

Xavier 就是让权重满足均值为0,同时方差为2/(nin-nout)分布可以用均匀分布和高斯分布,我们通过tf.random_uniform创建一个以下范围内的均匀分布:

                                            4.2 Tensorflow实现自编码器—Tensorflow实战_第3张图片

def xavier_init( fan_in, fan_out, constant = 1 ):
    low  = -constant * np.sqrt( 6.0 / ( fan_in + fan_out ) ) #1
    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 ) #2

① np.sqrt 是numpy科学计算库的开方运算,其中math模块中的math.pow(6.0/(fan_in+fan_out),0.5)也实现类似功能

② tf.random_uniform的第一个参数是shape,紧接着的两个参数表示范围,最后一个是数据类型

步骤三:定义去噪自编码的class

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 # 隐藏层激活函数,默认是softplus
        self.scale = tf.placeholder( tf.float32 ) # class内的scale
        self.training_scale = scale # 训练数据的高斯噪声系数
        network_weights = self._initialize_weights() # 初始化._initialize_weights()方法
        self.weights = network_weights

        self.x = tf.placeholder( tf.float32, [None, self.n_input] ) # tf.placeholder 第一个参数是数据类型,第二个参数是shape
        # 第一层权重 中间隐藏层经过激活函数处理的结果
        # tf.add()实现加法,tf.matmul()矩阵乘法,tf.random_normal()的第一个参数是shape,mean = 0,stddev = 1,从正太分布中输出随机值,
        self.hidden = self.transfer( tf.add( tf.matmul(self.x + scale * tf.random_normal(( n_input, ) ), self.weights['w1'] ), self.weights['b1'] ))
        # 第二层权重 self.reconstruction 就是最后输出的结果
        self.reconstruction = tf.add( tf.matmul( self.hidden, self.weights['w2'] ), self.weights['b2'] )
        # 计算损失函数 tf.reduce_sum()求和 tf.pow()求幂 tf.subtract()相减
        self.cost = 0.5 * tf.reduce_sum( tf.pow( tf.subtract( self.reconstruction, self.x ), 2 ) )
        # optimizer = tf.train.AdamOptimizer()
        self.optimizer = optimizer.minimize( self.cost )
        # 定义初始化所有变量方法
        init = tf.global_variables_initializer()
        self.sess = tf.Session()
        # 执行初始化变量方法
        self.sess.run( init )
        print ("begin to run session...")

步骤四:定义calss的成员函数

# 定义参数初始化函数
def _initialize_weights(self):
    all_weights = dict() #创建字典存储参数
    # 初始化w1采用前面定义的xavier_init函数
    all_weights['w1'] = tf.Variable( xavier_init(self.n_input, self.n_hidden ) )
    # tf.zeros(shape,dtype),shape可以用元组传递也可以用列表传递
    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) )
    # 因为自编码器的输入层和输出层节点是一样的,所以 b2 初始化tf.zeros([self.n_input],dtype = tf.float.32)
    all_weights['b2'] = tf.Variable( tf.zeros( [self.n_input], dtype = tf.float32 ) )
    return all_weights

# 定义计算损失函数及执行一步训练的函数
# 实现对一个batch数据进行训练并返回cost
def partial_fit(self, X):
    # Session执行两个计算图的节点,分别是cost和训练过程optimizer
    # 输入的feed_dict包括输入数据x,以及噪声系数scale
    cost, opt = self.sess.run( (self.cost, self.optimizer),
                            feed_dict = { self.x : X, self.scale : self.training_scale } )
    return cost
# 定义函数只求cost
# Session只执行一个计算图节点self.cost 传入参数和 partial_fit一致
# 这个函数再自编码器训练完毕后,在测试集上对模型性能进行评测时用到,它不像 partial_fit 触发训练操作
def calc_total_cost( self, X ):
    return self.sess.run( self.cost, feed_dict = { self.x : X, self.scale : self.training_scale } )

# 定义transform 函数返回隐含层激活函数处理后的输出结果
# 它的目的是提供一个接口来获取抽象后的特征,自编码器的隐含层的主要功能就是学习出数据中的高阶特征
def transform( self, X ):
    return self.sess.run( self.hidden, feed_dict = { self.x : X, self.scale : self.training_scale } )

# 定义generate函数,将隐含层输出结果作为输入,通过之后的重建层将提取到的高阶特征恢复为原始数据
# 这个接口和前面的transform正好将自编码器分为两部分,这里的generate接口是后半部分,将高阶特征复原成原始数据
def generate( 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 } )

# 定义reconstruction函数,它整体运行一遍复原过程,包括transform和generate两块,输入原始数据,输出复原后的数据
def reconstruction( self, X ):
# 注意这里的输入数据不是从self.hidden输入了,而是直接由原始数据输入self.x
    return self.sess.run( self.reconstruction, feed_dict = { self.x : X, self.scale : self.training_scale } )

# 获取隐含层的权重w1
def getWeights( self ):
    return self.sess.run( self.weights['w1'] )
# 获取隐含层偏置b1
def getBiases( self ):
    return self.sess.run( self.weights['b1'] )

步骤五:训练前的准备工作

# 使用tensorflow提供的读取示例数据的函数载入MNIST数据集
mnist = input_data.read_data_sets( '../MNIST_data', one_hot = True )

# 定义一个对训练,测试数据进行标准化处理的函数
# 标准化就是让变成数据均值为0,标准差为1的分布 方法:(X-X_mean)/stddev
# 直接使用sklearn.preprocessing的StandardScaler类可以实现,现在训练集进行fit,再将这个Scaler用到训练集和测试集上
# 注意:必须保证训练集和测试集使用相同的Scaler,这样才能保证后面模型处理数据时的一致性,这也是为什么先在训练数据fit出一个共同的caler的原因
def standard_scale( X_train, X_test ):
    # 在训练数据集上fit出一个共同的Scaler
    preprocessor = prep.StandardScaler().fit( X_train )
    X_train = preprocessor.transform( X_train )
    X_test  = preprocessor.transform( X_test )
    return X_train, X_test

# 定义一个获取随机block数据函数:取一个从0到len(data)-batch_size之间的随机整数
# 再以这个随机数作为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_scale函数对训练集和测试集进行标准化变换
X_train, X_test  =standard_scale( mnist.train.images, mnist.test.images )

# 下面定义几个常用的参数,总训练样本数,最大训练的轮数(epoch)设为20,batch_size设为128,并每隔一轮epoch就显示一次cost
# batch_size 一般设置为8的倍数,太小不容易收敛,太大训练吃力,精度不够
n_samples = int( mnist.train.num_examples )
training_epochs = 20
batch_size = 128
display_step = 1

# 创建前面定义去噪声的自编码实例
# 输入节点784(28*28),200个隐藏层节点,激活函数是softplus,优化器学习率0.001,噪声系数0.1
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 ):
    # 初始化平均损失值
    avg_cost = 0
    # 计算迭代次数(iteration)
    total_batch = int( n_samples / batch_size )
    # 每一次i就是将bitch_size扔网络训练一次
    for i in range( total_batch ):
        batch_xs = get_random_block_from_data( X_train, batch_size )

        # 使用类的方法partial_fit获取损失函数值
        cost = autoencoder.partial_fit( batch_xs )
        # 计算平均损失函数值avg_cost
        avg_cost = cost / n_samples * batch_size

    # 每一轮就显示一次cost值,display_step = 1,所以下面表示显示每次epoch的avg_cost值
    if epoch % display_step == 0:
        # %04d 表示输出以为少于4位的数值时,前面补0
        # %.9f保留小数点后面9位
        # 输出示例:epoch : 0001, cost = 39.167500000
        print( "epoch : %04d, cost = %.9f" % ( epoch+1, avg_cost ) )

第七步:利用测试集进行性能测试

# 对测试集X_test进行测试 如果使用示例中的参数,损失值约为60万[笑哭]
print( "Total cost : ", str( autoencoder.calc_total_cost(X_test)))

输出结果:

4.2 Tensorflow实现自编码器—Tensorflow实战_第4张图片

最后:终于抄完了,为了首尾呼应,截图为终

4.2 Tensorflow实现自编码器—Tensorflow实战_第5张图片

参考: tensorflow实战(黄文坚)

附代码:

import numpy as np
import sklearn.preprocessing as prep
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import time
#
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_uniform((fan_in, fan_out), minval=low, maxval=high, dtype=tf.float32 )

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 # 隐藏层激活函数,默认是softplus
        self.scale = tf.placeholder( tf.float32 ) # class内的scale
        self.training_scale = scale # 训练数据的高斯噪声系数
        network_weights = self._initialize_weights() # 初始化._initialize_weights()方法
        self.weights = network_weights

        self.x = tf.placeholder( tf.float32, [None, self.n_input] ) # tf.placeholder 第一个参数是数据类型,第二个参数是shape
        # 第一层权重 中间隐藏层经过激活函数处理的结果
        # tf.add()实现加法,tf.matmul()矩阵乘法,tf.random_normal()的第一个参数是shape,mean = 0,stddev = 1,从正太分布中输出随机值,
        self.hidden = self.transfer( tf.add( tf.matmul(self.x + scale * tf.random_normal(( n_input, ) ), self.weights['w1'] ), self.weights['b1'] ))
        # 第二层权重 self.reconstruction 就是最后输出的结果
        self.reconstruction = tf.add( tf.matmul( self.hidden, self.weights['w2'] ), self.weights['b2'] )
        # 计算损失函数 tf.reduce_sum()求和 tf.pow()求幂 tf.subtract()详见
        self.cost = 0.5 * tf.reduce_sum( tf.pow( tf.subtract( self.reconstruction, self.x ), 2 ) )
        # optimizer = tf.train.AdamOptimizer()
        self.optimizer = optimizer.minimize( self.cost )
        # 定义初始化所有变量方法
        init = tf.global_variables_initializer()
        self.sess = tf.Session()
        # 执行初始化变量方法
        self.sess.run( init )
        print ("begin to run session...")

    # 定义参数初始化函数
    def _initialize_weights(self):
        all_weights = dict() #创建字典存储参数
        # 初始化w1采用前面定义的xavier_init函数
        all_weights['w1'] = tf.Variable(xavier_init(self.n_input, self.n_hidden ) )
        # tf.zeros(shape,dtype),shape可以用元组传递也可以用列表传递
        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) )
        # 因为自编码器的输入层和输出层节点是一样的,所以 b2 初始化tf.zeros([self.n_input],dtype = tf.float.32)
        # 表示1行有784个数
        all_weights['b2'] = tf.Variable( tf.zeros( [self.n_input], dtype = tf.float32 ) )
        return all_weights

    # 定义计算损失函数及执行一步训练的函数
    # 实现对一个batch数据进行训练并返回cost
    def partial_fit(self, X):
        # Session执行两个计算图的节点,分别是cost和训练过程optimizer
        # 输入的feed_dict包括输入数据x,以及噪声系数scale
        cost, opt = self.sess.run( (self.cost, self.optimizer),
                                feed_dict = { self.x : X, self.scale : self.training_scale } )
        return cost
    # 定义函数只求cost
    # Session只执行一个计算图节点self.cost 传入参数和 partial_fit一致
    # 这个函数再自编码器训练完毕后,在测试集上对模型性能进行评测时用到,它不像 partial_fit 触发训练操作
    def calc_total_cost( self, X ):
        return self.sess.run( self.cost, feed_dict = { self.x : X, self.scale : self.training_scale } )

    # 定义transform 函数返回隐含层激活函数处理后的输出结果
    # 它的目的是提供一个接口来获取抽象后的特征,自编码器的隐含层的主要功能就是学习出数据中的高阶特征
    def transform( self, X ):
        return self.sess.run( self.hidden, feed_dict = { self.x : X, self.scale : self.training_scale } )

    # 定义generate函数,将隐含层输出结果作为输入,通过之后的重建层将提取到的高阶特征恢复为原始数据
    # 这个接口和前面的transform正好将自编码器分为两部分,这里的generate接口是后半部分,将高阶特征复原成原始数据
    def generate( 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 } )

    # 定义reconstruction函数,它整体运行一遍复原过程,包括transform和generate两块,输入原始数据,输出复原后的数据
    def reconstruction( self, X ):
    # 注意这里的输入数据不是从self.hidden输入了,而是直接由原始数据输入self.x
        return self.sess.run( self.reconstruction, feed_dict = { self.x : X, self.scale : self.training_scale } )

    # 获取隐含层的权重w1
    def getWeights( self ):
        return self.sess.run( self.weights['w1'] )
    # 获取隐含层偏置b1
    def getBiases( self ):
        return self.sess.run( self.weights['b1'] )

# 使用tensorflow提供的读取示例数据的函数载入MNIST数据集
mnist = input_data.read_data_sets( '../MNIST_data', one_hot = True )

# 定义一个对训练,测试数据进行标准化处理的函数
# 标准化就是让变成数据均值为0,标准差为1的分布 方法:(X-X_mean)/stddev
# 直接使用sklearn.preprocessing的StandardScaler类可以实现,现在训练集进行fit,再将这个Scaler用到训练集和测试集上
# 注意:必须保证训练集和测试集使用相同的Scaler,这样才能保证后面模型处理数据时的一致性,这也是为什么先在训练数据fit出一个共同的caler的原因
def standard_scale( X_train, X_test ):
    # 在训练数据集上fit出一个共同的Scaler
    preprocessor = prep.StandardScaler().fit( X_train )
    X_train = preprocessor.transform( X_train )
    X_test  = preprocessor.transform( X_test )
    return X_train, X_test

# 定义一个获取随机block数据函数:取一个从0到len(data)-batch_size之间的随机整数
# 再以这个随机数作为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_scale函数对训练集和测试集进行标准化变换
X_train, X_test  =standard_scale( mnist.train.images, mnist.test.images )

# 下面定义几个常用的参数,总训练样本数,最大训练的轮数(epoch)设为20,batch_size设为128,并每隔一轮epoch就显示一次cost
# batch_size 一般设置为8的倍数,太小不容易收敛,太大训练吃力,精度不够
n_samples = int( mnist.train.num_examples )
training_epochs = 20
batch_size = 128
display_step = 1

# 创建前面定义去噪声的自编码实例
# 输入节点784(28*28),200个隐藏层节点,激活函数是softplus,优化器学习率0.001,噪声系数0.1
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 ):
    # 初始化平均损失值
    avg_cost = 0
    # 计算迭代次数(iteration)
    total_batch = int( n_samples / batch_size )
    # 每一次i就是将bitch_size扔网络训练一次
    for i in range( total_batch ):
        batch_xs = get_random_block_from_data( X_train, batch_size )

        # 使用类的方法partial_fit获取损失函数值
        cost = autoencoder.partial_fit( batch_xs )
        # 计算平均损失函数值avg_cost
        avg_cost = cost / n_samples * batch_size

    # 每一轮就显示一次cost值,display_step = 1,所以下面表示显示每次epoch的avg_cost值
    if epoch % display_step == 0:
        # %04d 表示输出以为少于4位的数值时,前面补0
        # %.9f保留小数点后面9位
        # 输出示例:epoch : 0001, cost = 39.167500000
        print( "epoch : %04d, cost = %.9f at %s" % ( epoch+1, avg_cost,time.ctime() ) )

# 对测试集X_test进行测试 如果使用示例中的参数,损失值约为60万[笑哭]
print( "Total cost : ", str( autoencoder.calc_total_cost(X_test)))


你可能感兴趣的:(Tensorflow实战)