机器学习里的自编码器及应用

自动编码机(简称自编码器)是前馈非循环神经网络,是一种无监督机器学习方法,具有非常好的提取数据特征表示的能力,它是深层置信网络的重要组成部分,在图像重构、聚类、机器翻译等方面有着广泛的应用。自动编码机的一个非常好的应用是降维,也可用于特征提取、文档检索、分类和异常检测。

自动编码机的目标是重构一样的输入,其神经元的状态是确定性的

可以将自动编码机看作由两个级联网络组成,第一个网络是一个编码器,负责接收输入 x,并将输入通过函数 h 变换为信号 y,第二个网络将编码的信号 y 作为其输入,通过函数f得到重构的信号 r:

自动编码机可以进行权值共享,即解码器和编码器的权值彼此互为转置,这样可以加快网络学习的速度,因为训练参数的数量减少了,但同时降低了网络的灵活程度。根据隐藏层的大小,自动编码机分为欠完备自动编码机(隐藏层神经元数量小于输入层神经元数量)和过完备自动编码机(隐藏层神经元数量大于输入层神经元数量)。而根据对损失函数的约束条件,又可以分为:稀疏自动编码机、去噪自动编码机和卷积自动编码机。

1.标准自编码器

只有一个隐藏层,隐藏层中神经元的数量少于输入(和输出)层中神经元的数量,这会压缩网络中的信息,因此可以将隐藏层看作是一个压缩层,限定保留的信息。

下面利用 MNIST 数据训练自动编码机,并使用它来重构测试图像。

import numpy as np
import sklearn.preprocessing as prep
import tensorflow as tf
import matplotlib.pyplot as plt 
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets( '../MNIST_data', one_hot = True )

声明 AutoEncoder 类,使用 init 方法初始化自动编码机的权重、偏置和占位符,也可以在 init 方法中构建全部的计算图。还需要定义编码器、解码器,set_session(会话建立)和 fit 方法。此处构建的自动编码机使用简单的均方误差作为损失函数,使用 AdamOptimizer 进行优化,为便于使用,此处还定义了两个辅助函数,reduced_dimension 给出编码器网络的输出,reconstruct 给出重构的测试图像的输出:

class AutoEncoder(object):
    def __init__(self,m,n,eta=0.01):
        self._m=m
        self._n=n
        self.learing_rate=eta
        self._w1=tf.Variable(tf.randon_normal(shape=(self._m,self._n)))
        self._w2 = tf.Variable(tf.randon_normal(shape=(self._n, self._m)))
        self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32))
        self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32))

        self._X=tf.placeholder('float',[None,slef._m])
        self.y=self.encoder(self._X)
        self.r=self.decoder(self.y)
        error=self._X-self.r
        self._loss=tf.reduce_mean(tf.pow(error,2))
        self._opt=tf.train.AdamOPtimizer(self.learning_rate).minimize(self._loss)

    def encoder(self,x):
        h=tf.matmul(x,self._w1)+self._b1
        return tf.nn.sigmiod(h)

    def decoder(self,x):
        h=tf.matmul(x,self._w2)+self._b2
        return tf.nn.sigmoid(h)

    def set_session(self,session):
        self.session=session

    def reduced_dimension(self,x):
        h=self.encoder(x)
        return self.session.run(h,feed_dice={self._X:x})

    def reconstruct(self,x):
        h=self.encoder(x)
        r=self.decoder(h)
        return self.session.run(r,feed_dict={self._X:x})

    def fit(self,X,epochs=1,batch_size=100):
        N,D=X.shape
        num_batches=N

        obj=[]
        for i in range(epochs):
            for j in range(num_batches):
                batch=X[j*batch_size:(j*batch_size+batch_size)]
                _,ob=self.session.run([self._opt,self._loss],feed_dict={self._X:x})
                if j%100==0 and i%100==0:
                    print('traing epoch{0} batch{2} cost{1}'.format(i,ob,j))
                obj.append(ob)
        return obj

2.稀疏自编码器

以通过添加更多的约束确保网络从数据集中学习抽象特征。稀疏自编码器中,重构误差中添加了一个稀疏惩罚,用来限定任何时刻的隐藏层中并不是所有单元都被激活。如果 m 是输入模式的总数,那么可以定义一个参数 ρ_hat,用来表示每个隐藏层单元的行为(平均激活多少次)。基本的想法是让约束值 ρ_hat 等于稀疏参数 ρ。具体实现时在原始损失函数中增加表示稀疏性的正则项,如果 ρ_hat 偏离 ρ,那么正则项将惩罚网络,一个常规的实现方法是衡量 ρ 和 ρ_hat 之间的 Kullback-Leiber(KL) 散度。KL 散度 DKL ,它是衡量两个分布之间差异的非对称度量,本节中,两个分布是 ρ 和 ρ_hat。当 ρ 和 ρ_hat 相等时,KL 散度是零,否则会随着两者差异的增大而单调增加,KL 散度的数学表达式如下:

class SparseAutoEncoder(object):
    def __init__(self,m,n,eta=0.01):
        self._m=m
        self._n=n
        self.learing_rate=eta
        self._w1=tf.Variable(tf.randon_normal(shape=(self._m,self._n)))
        self._w2 = tf.Variable(tf.randon_normal(shape=(self._n, self._m)))
        self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32))
        self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32))

        self._X=tf.placeholder('float',[None,slef._m])
        self.y=self.encoder(self._X)
        self.r=self.decoder(self.y)
        error=self._X-self.r
        self._loss=tf.reduce_mean(tf.pow(error,2))
        alpha=7.5e-5
        k1_div_loss=tf.reduce_sum(self.kl_div(0.02,tf.reduce_mean(self.y,0)))
        loss=self._loss+alpha*k1_div_loss
        self._opt=tf.train.AdamOPtimizer(self.learning_rate).minimize(self._loss)

    def encoder(self,x):
        h=tf.matmul(x,self._w1)+self._b1
        return tf.nn.sigmiod(h)

    def decoder(self,x):
        h=tf.matmul(x,self._w2)+self._b2
        return tf.nn.sigmoid(h)

    def set_session(self,session):
        self.session=session

    def reduced_dimension(self,x):
        h=self.encoder(x)
        return self.session.run(h,feed_dice={self._X:x})

    def reconstruct(self,x):
        h=self.encoder(x)
        r=self.decoder(h)
        return self.session.run(r,feed_dict={self._X:x})

    def k1_div(self,rho,rho_hat):
        term2_num=tf.constant(1.)-rho
        term2_den=tf.constant(1.)-rho_hat
        k1=self.logfunc(rho,rho_hat)+self.logfunc(term2_num,term2_den)
        retyrn k1
        
    def logfunc(self,x1,x2):
        return tf.multiply(x1,tf.log(tf.div(x1,x2)))

    def fit(selfself,X,epochs=1,batch_size=100):
        N,D=X.shape
        num_batches=N

        obj=[]
        for i in range(epochs):
            for j in range(num_batches):
                batch=X[j*batch_size:(j*batch_size+batch_size)]
                _,ob=self.session.run([self._opt,self._loss],feed_dict={self._X:x})
                if j%100==0 and i%100==0:
                    print('traing epoch{0} batch{2} cost{1}'.format(i,ob,j))
                obj.append(ob)
        return obj

3.去燥自编码器

前两个探讨的两个自编码器属于欠完备自动编码机,因为隐藏层的维度比输入(输出)层低。去噪自编码器属于过完备自编码器,隐藏层的维数大于输入层时效果会更好。去噪自编码器从受损(噪声)输入中学习,它向编码器网络提供有噪声的输入,然后将解码器的重构图像与原始输入进行比较,这就会“教会”网络去学习如何对输入去噪。不再只是进行像素比较,为了去噪,它也会学习相邻像素的信息。去噪自编码器也具有 KL 散度惩罚项,它不同于稀疏自编码器的主要有两个方面,首先,隐藏层的单元数 n_hidden 大于输入层的单元数 m,即 n_hidden>m;其次,编码器的输入是受损输入,要做到这一点,这里构造了一个给输入添加噪声的受损函数。

class DenoisigAutoEncoder(object):
    def __init__(self,m,n,eta=0.01):
        self._m=m
        self._n=n
        self.learing_rate=eta
        self._w1=tf.Variable(tf.randon_normal(shape=(self._m,self._n)))
        self._w2 = tf.Variable(tf.randon_normal(shape=(self._n, self._m)))
        self._b1 = tf.Variable(np.zeros(self._n).astype(np.float32))
        self._b2 = tf.Variable(np.zeros(self._m).astype(np.float32))

        self._X=tf.placeholder('float',[None,self._m])

        self._X_noisy=tf.placeholder('float',[None,self._m])
        self.y=self.encoder(self._X)
        self.r=self.decoder(self.y)
        error=self._X-self.r
        self._loss=tf.reduce_mean(tf.nn.sigmois_crpss_entropy_with_logits(labels=self._X,logits=self.r))
        alpha=0.05
        k1_div_loss=tf.reduce_sum(self.kl_div(0.02,tf.reduce_mean(self.y,0)))
        loss=self._loss+alpha*k1_div_loss
        self._opt=tf.train.AdamOPtimizer(self.learning_rate).minimize(self._loss)

    def encoder(self,x):
        h=tf.matmul(x,self._w1)+self._b1
        return tf.nn.sigmiod(h)

    def decoder(self,x):
        h=tf.matmul(x,self._w2)+self._b2
        return tf.nn.sigmoid(h)

    def set_session(self,session):
        self.session=session

    def reduced_dimension(self,x):
        h=self.encoder(x)
        return self.session.run(h,feed_dice={self._X:x})

    def reconstruct(self,x):
        h=self.encoder(x)
        r=self.decoder(h)
        return self.session.run(r,feed_dict={self._X:x})

    def k1_div(self,rho,rho_hat):
        term2_num=tf.constant(1.)-rho
        term2_den=tf.constant(1.)-rho_hat
        k1=self.logfunc(rho,rho_hat)+self.logfunc(term2_num,term2_den)
        return k1

    def logfunc(self,x1,x2):
        return tf.multiply(x1,tf.log(tf.div(x1,x2)))

    def corrupt(self,x):
        return x*tf.cast(tf.random_uniform(shape=tf.shape(x),minval=0,maxval=2),tf.float32)

    def getWeights(self):
        return self.seesion.run([self._w1,self._w2,self._b1,self._b2])

    def fit(self,X,epochs=1,batch_size=100):
        N,D=X.shape
        num_batches=N

        obj=[]
        for i in range(epochs):
            for j in range(num_batches):
                batch=X[j*batch_size:(j*batch_size+batch_size)]
                _,ob=self.session.run([self._opt,self._loss],feed_dict={self._X:x})
                if j%100==0 and i%100==0:
                    print('traing epoch{0} batch{2} cost{1}'.format(i,ob,j))
                obj.append(ob)
        return obj

4.卷积自编码器

卷积神经网络(CNN)之所以在处理图像上有优势,是因为可以提取隐藏在图像中的空间信息,因此很自然地想到如果可以使用 CNN 构造编码器和解码器网络,会比其他自动编码机工作得更好,因此产生了卷积自编码器(CAE)。
CAE 的编码器和解码器都是 CNN 网络,编码器的卷积网络学习将输入编码为一组信号,然后解码器 CNN 尝试重构来自自动编码机的输入。其中 CNN 作为通用特征提取器进行工作,学习如何最好地捕捉输入特征。我们知道,随着卷积层的添加,传递到下一层的空间尺寸信息在减小,但是在自编码器中,重构图像的大小和深度应与输入图片相同,这意味着解码器应该以某种方式调整图像大小和卷积来重构原始图像。而转置卷积层能够增加空间尺寸和卷积,但是转置卷积层会导致最终图像中出现伪影。Augustus Odena 等人表明使用最近邻或双线性插值(上采样)紧跟着一个卷积层的方式可以避免这些伪影,他们采用最近邻差值实现,最终取得了非常好的结果。

5.堆叠自编码器

编码器和解码器网络也可能有多层,使用更深的编码器和解码器网络可以使自编码器表示更复杂的特征,将一个编码器提取的特征作为输入传递到下一个编码器,这种结构被称为堆叠自编码器(或者深度自编码器)。堆叠自编码器可以作为一个网络进行训练,训练目标是最小化重构误差;也可以首先使用之前学习的无监督方法对每个编码器/解码器网络进行预训练,然后对整个网络进行微调。有人指出,通过预训练(逐层贪婪训练),效果会更好。

定义类 StackedAutoencoder。__init__ 方法包括一个列表,其中包含每个自编码器中的诸多神经元,从第一个输入自编码器和学习率开始。由于每层都有不同的输入和输出维度,因此选择一个字典数据结构来表示每层的权重、偏置和输入:

class StackedAutoEncoder(object):
    def __init__(self,list1,eta=0.02):
        N=len(list1)-1
        self._m=list1[0]
        self.learing_rate=eta
        
        self._w={}
        self._b={}
        self._X={}
        self._X['0']=tf.placeholder('float',[None,list1[0]])
        
        for i in range(N):
            layer='{0}'.format(i+1)
            self._w['E'+layer]=tf.Variable(tf.random_normal(shape=(list1[i],list1[i+1])),name='WtsEncoder'+layer)
            self._b['E' + layer] = tf.Variable(np.zeros(list1[i+1]).astype(np.float32),name='BiasEncoder'+layer)
            self._X[layer] = tf.placeholder('float', [None, list1[i + 1]])
            
            self._w['D'+layer]=tf.transpose(self._w['E'+layer])  #shared weights
            self._b['D' + layer] = tf.Variable(np.zeros(list1[i + 1]).astype(np.float32), name='BiasEncoder' + layer)
            self._X_noisy=tf.placeholder('float',[None,self._m])

建立计算图,在预训练时为每个自编码器定义优化参数,当上一个自编码器的输出作为当前自编码器的输入时,为其定义重构损失,为此定义了方法 pretrain 和 one_pass,分别为每个自编码器返回编码器的训练操作和输出:

在计算图中对整个堆叠自编码器进行微调,这里使用类方法 encoder 和 decoder 来实现:

定义类方法 fit,对每个自编码器执行批量预训练,然后进行微调。在训练时使用正常的输入;在微调时使用受损输入。这使得可以用堆叠自编码器从噪声输入中进行重构:

    def encoder(self,x,N):
        x=x
        for i in range(N):
            hiddenD=tf.nn.sigmiod(tf.matmul(x,self._w['E'+layer])+self._b['E' + layer])
        x=hiddenD
        return x

    def decoder(self,x,N):
        x=x
        for i in range(N):
            hiddenD=tf.nn.sigmiod(tf.matmul(x,self._w['D'+layer])+self._b['D' + layer])
        x=hiddenD
        return x

    def set_session(self,session):
        self.session=session

    def reduced_dimension(self,x):
        h=self.encoder(x)
        return self.session.run(h,feed_dice={self._X:x})

    def reconstruct(self,x,n_layers):
        h=self.encoder(x,n_layers)
        r=self.decoder(h,n_layers)
        return self.session.run(r,feed_dict={self._X['0']:x})

    def pretrain(self,x,layer):
        y=tf.nn.sigmiod(tf.matmul(x,self._w['E'+layer])+self._b['E' + layer])
        r=tf.nn.sigmiod(tf.matmul(y,self._w['D'+layer])+self._b['D' + layer])

    def one_pass(self,x,w,b,c):
        h=tf.nn.sigmiod(tf.matmul(x,w)+b)
        return h

    def corrupt(self,x):
        return x*tf.cast(tf.random_uniform(shape=tf.shape(x),minval=0,maxval=2),tf.float32)

    def getWeights(self):
        return self.seesion.run([self._w1,self._w2,self._b1,self._b2])

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值