Tensorflow——去噪自编码器

TensorFlow实现自编码器

  • 在深度学习中,自编码器是一种非常有用的无监督学习模型。自编码器(AutoEncoder),即可以用自身的高阶特征编码自己。自编码器也是一种神经网络,但它的输入与输出是一致的。自编码器的思想就是借助稀疏编码,使用稀疏的一些高阶特征重新组合来重构自己。
  • 因此,自编码器的特点就是期望输入与输出一致,再就是希望使用高阶特征来重构自己,而不是复制像素点。
  • 自编码器的输入节点和输出节点的数量是一致的,所以,为了使用少量稀疏的高阶特征来重构输入,可以加入几种限制。
    1. 如果限制中间隐含层节点的数量,就相当于一个降维的过程。如果再给中间隐含层的权重加一个L的正则,则可以根据惩罚系数控制隐含系数的稀疏程度,惩罚系数越大,学到的特征组合越稀疏,实际使用的特征数量越少。
    2. 如果给数据加入噪声,那么就是Denoising AutoEncoder(去噪自编码器),将从噪声中学习出数据的特征。因为,完全复制不能去掉添加的噪声,无法完全复原数据,只有学习数据的模式与结构,将无律的噪声略去,才可以复原数据。去噪编码器最常使用的噪声就是加性高斯噪声(Additive Gaussian Noise,AGN),也可以使用Masking Noise,即随机遮挡的噪声。

这次主要是根据《tensorflow实战》一书的指导,自己实现一下去噪自编码器。无噪声的自编码器只需要去掉噪声,并保证隐含层节点小于输入层节点;Masking Noise的自编码器只需要将高斯噪声改为随机遮挡噪声;VAE(Variational AutoEncoder)则相对复杂,关于VAE,接下来肯定是要学习的,到时候具体的过程也会记录下来。

import numpy as np
import sklearn.preprocessing as prep
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

Numpy是常用的库,是Python科学计算的基础包。它提供了多维数组对象、基于数组的各种派生对象(例如,masked Array, 矩阵)。除此之外,还提供了各种各样的加快数组操作的例程,包括数学基本计算、逻辑、图形操作、排序、选择、输入输出,离散傅立叶变换、基础线性代数、基础统计操作、随机仿真等等。还有Scikit-learn中的preprocessing模块,这是一个对数据进行预处理的常用模块,会使用其中的数据标准化的功能。Scikit-learn是一个功能强大的python包,这个星期刚好有模式识别的一个实验,需要用到这个包,到时候进行学习总结。实现中依然使用的MNIST数据集。代码主要来自于TensorFlow的开源实现。

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)

这里使用了一种参数初始化的方法,xavier initialization,fan_in为输入节点的数量,fan_out是输出节点的数量。Xavier初始化器在Caffe的早期版本中被频繁使用,特点是会根据某一层网络的输入,输出节点数量自动调整最合适的分布。在Caffe的学习过程中,如果遇到,会进行记录与学习。从数学的角度,Xavier就是让权重满足0均值,同时方差为2/in+out,分布可以用均匀分布或者高斯分布。这里使用的是均匀分布,根据方差公式可以得出方差为所要求的。因此,这里实现的就是标准的均匀分布的Xavier初始化器。

  • 下面定义去噪自编码的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
        self.scale = tf.placeholder(tf.float32) #将scale参数做成一个placeholder
        self.training_scale = scale
        network_weights = self._initialize_weights() #接下来会定义_initialize_weights()函数
        self.weights = network_weights

这里定义了一个构建函数 _ init _(),包含这样几个输入:n_input输入变量数,n_hidden隐含层节点数,transfer_function隐含层激活函数(默认为softplus),optimizer优化器(默认为Adam),scale高斯噪声系数(默认为0.1)。

  • 接下来定义网络结构
        self.x = tf.placeholder(tf.float32,[None,self.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']) #在输出层进行数据复原,重建操作,即建立reconstruction层

在隐含层,现将输入x加上了噪声,然后用tf.matmul将加入噪声的输入与隐含层的权重w1相乘,并使用tf.add加上隐含层的偏置b1,最后使用self.transfer(即softpuls)对结果进行激活函数处理。在reconstruction层不需要激活函数。

        self.cost = 0.5 * tf.reduce_sum(tf.pow(tf.subtract(self.reconstruction,self.x),2.0))
        self.optimizer = optimizer.minimize(self.cost)  

        init = tf.global_variables_initializer() #初始化自编码器的全部模型参数
        self.sess = tf.Session()
        self.sess.run(init)

定义自编码器的损失函数,这里使用的是平方误差(squared error)作为cost,tf.subtract是计算输出与输入之差。最后创建了Session,初始化自编码器的全部模型参数。

  • 定义参数初始化函数_initialize_weights
    def _initialize_weights(self):
        all_weights = dict() #创建一个字典,存入w1,b1,w2,b2
        all_weights['w1'] = tf.Variable(xavier_init(self.n_input,self.n_hidden)) #使用xavier_init函数初始化,返回一个比较适合于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
  • 定义计算损失cost和执行一步训练的函数partial_fit
    def partial_fit(self,X):
        cost,opt = self.sess.run((self.cost,self.optimizer),feed_dict = {self.x:X,self.scale:self.training_scale})
        return cost

函数让Session执行了两个计算图的节点,分别是损失cost,训练过程optimizer。函数partial_fit做的就是用一个batch数据进行训练并返回当前的损失cost。

  • 定义只求cost的函数calc_total_cost
    def calc_total_cost(self,X):
        return self.sess.run(self.cost,feed_ditc = {self.x:X,self.scale:self.training_scale})

这里只让Session执行一个计算图节点self.cost。这个函数实在自编码器训练完毕后,在测试集上对模型性能进行评测时会用到的,不会像partial_fit那样触发训练操作。

  • 定义transform函数,返回自编码器隐含层的输出结果
    def transform(self,X):
    return self.sess.run(self.hidden)

transform函数的目的是提供一个接口来获取抽象后的特征,自编码器的隐含层的最主要功能就是学习数据中的高级特征。

  • 定义generate函数
    def genreate(self,hidden = None):
        if hidden is None:
            hidden = np.random.normal(size = self.weights["b1"]
        return self.sess.run(self.reconstruction,feed_dict = {self.hidden: hidden})

generate函数将隐含层的输出结果作为输入,通过之后的重建层将提取到的高阶特征复原为原始数据。这个接口和前面的transform正好将整个自编码器拆分为两部分,这里的generate接口是后半部分,将高阶特征复原为初始数据。

  • 定义reconstruct函数
    def reconstruct(self,X):
        return self.sess.run(self.reconstruction,feed_dict = {self.x:X,self.scale:self.training_scale})

reconstruct函数整体运行了一遍复原过程,即包括了transform和generate两块。

  • getWeights函数
    def getWeights(self):
        return self.sess.run(self.weights['w1']) #作用是获取隐含层的权重w1
  • getBiases函数
    def getBiases(self):
        return self.sess.run(sefl.weights['b1']) #作用是获取隐含层的偏执b1

到这里,去噪自编码器的class就定义完了,回顾一下,包括了构建函数,神经网络结构,损失函数,权重的初始化,以及一些成员函数。
下面就是用定义好的AGN自编码器在MNIST数据集上的一些测试。

mnist = input_data.read_data_sets('MNIST_data',ont_hot = True)
#载入MNIST数据集
  • 定义一个对训练,测试数据进行标准化处理的函数
def standard_scale(X_train,X_test):
    preprocessor = prep.StandarScaler().fit(X_train) #StandarScaler是sklearn.preprossing工具包里面的类,先在训练集上fit
    X_train = preprocessor.transform(X_train) #transform是返回隐含层的输出结果,所以X_train就完成了标准化处理
    X_test = preprocessor.transform(X_test)
    return X_train,X_test
  • 再定义一个获取随机block数据的函数
def get_random_block_from_data(data,batch_size):
    start_index = np.random.randint(o,len(data) - batch_size)#从0到len(data) - batch_size之间取一个随机数
    return data[start_index:(start_index + batch_size)]#从随机数的位置开始,顺序取一个 batch size的数据。这里是不放回的抽样
X_train,X_test = standard_scale(mnist.train.images,mnist.test.images) #使用定义好的函数,对训练集和测试集进行标准化变换
  • 定义一些常用参数
n_samples = int(mnist.train.num_samples) #总训练样本数
training_epochs = 20 #最大训练的轮数
batch_size = 128
display_step = 1 #每次显示一次损失cost的轮数
  • 创建一个AGN自编码器的实例
autoencoder  = AdditiveGaussianNoiseAutoencoder(n_input = 784,n_hidden = 200,transfer_function = tf.nn.softplus,optimizer = tf.train.AdamOptimizer(learining_rate = 0.001),scale = 0.01)
  • 下面开始训练过程
for epoch in range(training_epochs):
    avg_cost = 0.
    total_batch = int(n_samples/batch_size)
    for i in range(total_batch):
        batch_xs = get_random_block_from_data(X_train,batch_size)
        cost = autoencoder.partial_fit(batch_xs)
        avg_cost += cost/n_samples * batch_size
    if epoch % display_step == 0:
        print("Epoch:",'%04d' % (epoch+1),"cost = ","{:.9f}".format(avg_cost))
  • 最后对训练完的模型进行性能测试,对测试集X_test进行测试
print("Total cost:" + str(autoencoder.cal_total_cost(X_test))) #使用成员函数cal_total_cost计算平方误差

可以发现,实现自编码器和实现一个单隐层的神经网络差不多,只不过是在数据输入时做了标准化,并加上了一个高斯噪声,同时我们的输出结果不是数字分类结果,而是复原的数据,不需要标注过的数据进行监督训练。
自编码器作为一种无监督学习的方法,它与其他无监督学习的主要不同在于,它不是对数据进行聚类,而是提取其中最有用,最频繁出现的高阶特征,根据这些高阶特征重构数据。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用TensorFlow实现去噪自编码器(DAE)的示例代码: ```python import tensorflow as tf import numpy as np # 加载数据集 (x_train, _), (x_test, _) = tf.keras.datasets.mnist.load_data() x_train = x_train.astype('float32') / 255. x_test = x_test.astype('float32') / 255. x_train = np.reshape(x_train, (len(x_train), np.prod(x_train.shape[1:]))) x_test = np.reshape(x_test, (len(x_test), np.prod(x_test.shape[1:]))) # 添加噪声 noise_factor = 0.5 x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape) x_train_noisy = np.clip(x_train_noisy, 0., 1.) x_test_noisy = np.clip(x_test_noisy, 0., 1.) # 定义模型 input_size = 784 hidden_size = 128 output_size = 784 input_data = tf.keras.layers.Input(shape=(input_size,)) hidden_layer = tf.keras.layers.Dense(hidden_size, activation='relu')(input_data) output_layer = tf.keras.layers.Dense(output_size, activation='sigmoid')(hidden_layer) model = tf.keras.models.Model(input_data, output_layer) model.compile(optimizer='adam', loss='binary_crossentropy') # 训练模型 model.fit(x_train_noisy, x_train, epochs=50, batch_size=128, shuffle=True, validation_data=(x_test_noisy, x_test)) # 测试模型 decoded_imgs = model.predict(x_test_noisy) # 显示结果 import matplotlib.pyplot as plt n = 10 # 显示10个数字 plt.figure(figsize=(20, 4)) for i in range(n): # 原始图像 ax = plt.subplot(2, n, i + 1) plt.imshow(x_test[i].reshape(28, 28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # 加噪后的图像 ax = plt.subplot(2, n, i + 1 + n) plt.imshow(x_test_noisy[i].reshape(28, 28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # 解码后的图像 ax = plt.subplot(2, n, i + 1 + n * 2) plt.imshow(decoded_imgs[i].reshape(28, 28)) plt.gray() ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) plt.show() ``` 在这个例子中,我们使用了MNIST数据集,并将每个图像添加了50%的高斯噪声。模型使用一个128个隐藏节点的隐层和一个784个输出节点的输出层。训练过程中使用了Adam优化器和二元交叉熵损失函数。最终,我们将显示10个数字的原始图像、添加噪声后的图像和去噪后的图像。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值