新闻资讯
看你所看,想你所想

稀疏自编码器

稀疏自编码器

稀疏自编码器

稀疏自编码器是一种无监督机器学习算法,通过计算自编码的输出与原输入的误差,不断调节自编码器的参数,最终训练出模型。自编码器可以用于压缩输入信息,提取有用的输入特徵。

基本介绍

  • 中文名:稀疏自编码器
  • 外文名:Sparse Autoencoder
  • 领域:深度学习

提出思路

自编码器最初提出是基于降维的思想,但是当隐层节点比输入节点多时,自编码器就会失去自动学习样本特徵的能力,此时就需要对隐层节点进行一定的约束,与降噪自编码器的出发点一样,高维而稀疏的表达是好的,因此提出对隐层节点进行一些稀疏性的限值。稀疏自编码器就是在传统自编码器的基础上通过增加一些稀疏性约束得到的。这个稀疏性是针对自编码器的隐层神经元而言的,通过对隐层神经元的大部分输出进行抑制使网路达到一个稀疏的效果。

算法原理

假设我们只有一个没有带类别标籤的训练样本集合
,其中
。自编码神经网路是一种无监督学习算法,它使用了反向传播算法,并让目标值等于输入值,比如
。下图是一个自编码神经网路(图一)的示例。
图一图一
自编码神经网路尝试学习一个
的函式。换句话说,它尝试逼近一个恆等函式,从而使得输出
接近于输入
。恆等函式虽然看上去不太有学习的意义,但是当我们为自编码神经网路加入某些限制,比如限定隐藏神经元的数量,我们就可以从输入数据中发现一些有趣的结构。举例来说,假设某个自编码神经网路的输入
是一张
图像(共100个像素)的像素灰度值,于是
,其隐藏层
中有50个隐藏神经元。注意,输出也是100维的
。由于只有50个隐藏神经元,我们迫使自编码神经网路去学习输入数据的'''压缩'''表示,也就是说,它必须从50维的隐藏神经元激活度向量
中'''重构'''出100维的像素灰度值输入
。如果网路的输入数据是完全随机的,比如每一个输入
都是一个跟其它特徵完全无关的独立同分布高斯随机变数,那幺这一压缩表示将会非常难学习。但是如果输入数据中隐含着一些特定的结构,比如某些输入特徵是彼此相关的,那幺这一算法就可以发现输入数据中的这些相关性。事实上,这一简单的自编码神经网路通常可以学习出一个跟主元分析(PCA)结果非常相似的输入数据的低维表示。
我们刚才的论述是基于隐藏神经元数量较小的假设。但是即使隐藏神经元的数量较大(可能比输入像素的个数还要多),我们仍然通过给自编码神经网路施加一些其他的限制条件来发现输入数据中的结构。具体来说,如果我们给隐藏神经元加入稀疏性限制,那幺自编码神经网路即使在隐藏神经元数量较多的情况下仍然可以发现输入数据中一些有趣的结构。
稀疏性可以被简单地解释如下。如果当神经元的输出接近于1的时候我们认为它被激活,而输出接近于0的时候认为它被抑制,那幺使得神经元大部分的时间都是被抑制的限制则被称作稀疏性限制。这里我们假设的神经元的激活函式是sigmoid函式。如果你使用tanh作为激活函式的话,当神经元输出为-1的时候,我们认为神经元是被抑制的。
注意到
表示隐藏神经元
的激活度,但是这一表示方法中并未明确指出哪一个输入
带来了这一激活度。所以我们将使用
来表示在给定输入为
情况下,自编码神经网路隐藏神经元
的激活度。
进一步,让
表示隐藏神经元
的平均活跃度(在训练集上取平均)。我们可以近似的加入一条限制
其中,
是'''稀疏性参数''',通常是一个接近于0的较小的值(比如
)。换句话说,我们想要让隐藏神经元
的平均活跃度接近0.05。为了满足这一条件,隐藏神经元的活跃度必须接近于0。
为了实现这一限制,我们将会在我们的最佳化目标函式中加入一个额外的惩罚因子,而这一惩罚因子将惩罚那些
有显着不同的情况从而使得隐藏神经元的平均活跃度保持在较小範围内。惩罚因子的具体形式有很多种合理的选择,我们将会选择以下这一种:
这里,
是隐藏层中隐藏神经元的数量,而索引
依次代表隐藏层中的每一个神经元。如果你对相对熵(KL divergence)比较熟悉,这一惩罚因子实际上是基于它的。于是惩罚因子也可以被表示为
其中
是一个以
为均值和一个以
为均值的两个伯努利随机变数之间的相对熵。相对熵是一种标準的用来测量两个分布之间差异的方法。(如果你没有见过相对熵,不用担心,所有你需要知道的内容都会被包含在这份笔记之中。)
这一惩罚因子有如下性质,当
,并且随着
之间的差异增大而单调递增。举例来说,在下图中,我们设定
并且画出了相对熵值
随着
变化的变化(图二)。
图二图二
我们可以看出,相对熵在
时达到它的最小值0,而当
靠近0或者1的时候,相对熵则变得非常大(其实是趋向于
)。所以,最小化这一惩罚因子具有使得
靠近
的效果。
我们的总体代价函式可以表示为
其中
如之前所定义,而
控制稀疏性惩罚因子的权重。
项则也(间接地)取决于
,因为它是隐藏神经元
的平均激活度,而隐藏层神经元的激活度取决于
为了对相对熵进行导数计算,我们可以使用一个易于实现的技巧,这只需要在你的程式中稍作改动即可。具体来说,前面在后向传播算法中计算第二层(
)更新的时候我们已经计算了
我们将其换成
就可以了。
有一个需要注意的地方就是我们需要知道
来计算这一项更新。所以在计算任何神经元的后向传播之前,你需要对所有的训练样本计算一遍前向传播,从而获取平均激活度。如果你的训练样本可以小到被整个存到记忆体之中(对于编程作业来说,通常如此),你可以方便地在你所有的样本上计算前向传播并将得到的激活度存入记忆体并且计算平均激活度 。然后你就可以使用事先计算好的激活度来对所有的训练样本进行后向传播的计算。如果你的数据量太大,无法全部存入记忆体,你就可以扫过你的训练样本并计算一次前向传播,然后将获得的结果累积起来并计算平均激活度
(当某一个前向传播的结果中的激活度
被用于计算平均激活度
之后就可以将此结果删除)。然后当你完成平均激活度
的计算之后,你需要重新对每一个训练样本做一次前向传播从而可以对其进行后向传播的计算。对于后一种情况,你对每一个训练样本需要计算两次前向传播,所以在计算上的效率会稍低一些。
证明上面算法能达到梯度下降效果的完整推导过程不再本教程的範围之内。不过如果你想要使用经过以上修改的后向传播来实现自编码神经网路,那幺你就会对目标函式
做梯度下降。使用梯度验证方法,你可以自己来验证梯度下降算法是否正确。

与自动编码器的区别

在自动编码器AutoEncoder的基础上加上L1的正则限制(L1主要是约束每一层中的节点中大部分都要为0,只有少数不为0,这就是Sparse名字的来源),我们就可以得到Sparse AutoEncoder法。
如图三,其实就是限制每次得到的表达code儘量稀疏。因为稀疏的表达往往比其他的表达要有效(人脑好像也是这样的,某个输入只是刺激某些神经元,其他的大部分的神经元是受到抑制的)
图三图三

为什幺要用稀疏自编码器

对于没有带类别标籤的数据,由于为其增加类别标记是一个非常麻烦的过程,因此我们希望机器能够自己学习到样本中的一些重要特徵。通过对隐藏层施加一些限制,能够使得它在恶劣的环境下学习到能最好表达样本的特徵,并能有效地对样本进行降维。这种限制可以是对隐藏层稀疏性的限制。
  如果给定一个神经网路,我们假设其输出与输入是相同的,然后训练调整其参数,得到每一层中的权重。自然地,我们就得到了输入的几种不同表示(每一层代表一种表示),这些表示就是特徵。自动编码器就是一种儘可能复现输入信号的神经网路。为了实现这种复现,自动编码器就必须捕捉可以代表输入数据的最重要的因素,就像PCA那样,找到可以代表原信息的主要成分。
  当然,我们还可以继续加上一些约束条件得到新的Deep Learning方法,如:如果在AutoEncoder的基础上加上L1的Regularity限制(L1主要是约束隐含层中的节点中大部分都要为0,只有少数不为0,这就是Sparse名字的来源),我们就可以得到Sparse AutoEncoder法。
  之所以要将隐含层稀疏化,是由于,如果隐藏神经元的数量较大(可能比输入像素的个数还要多),不稀疏化我们无法得到输入的压缩表示。具体来说,如果我们给隐藏神经元加入稀疏性限制,那幺自编码神经网路即使在隐藏神经元数量较多的情况下仍然可以发现输入数据中一些有趣的结构。

稀疏自编码器的解释

稀疏性可以被简单地解释如下。如果当神经元的输出接近于1的时候我们认为它被激活,而输出接近于0的时候认为它被抑制,那幺使得神经元大部分的时间都是被抑制的限制则被称作稀疏性限制。这里我们假设的神经元的激活函式是sigmoid函式。如果你使用tanh作为激活函式的话,当神经元输出为-1的时候,我们认为神经元是被抑制的。

通过tensorflow实现的稀疏自编码器

初始化参数

input_nodes = 8*8 //输入节点数hidden_size = 100//隐藏节点数output_nodes = 8*8//输出节点数

初始化训练集数据

从mat档案中读取图像块,随机生成10000个8*8的图像块。
def sampleImage():    mat = scipy.io.loadmat('F:/ml/code/IMAGES.mat')    pic = mat['IMAGES']    shape = pic.shape    patchsize = 8    numpatches = 1000    patches = []    i = np.random.randint(0, shape[0]-patchsize,numpatches)    j = np.random.randint(0, shape[1]-patchsize, numpatches)    k = np.random.randint(0, shape[2], numpatches)    for l in range(numpatches):        temp = pic[i[l]:(i[l]+patchsize), j[l]:(j[l]+patchsize), k[l]]        temp = temp.reshape(patchsize*patchsize)        patches.append(temp)    return patches

通过xvaier初始化第一层的权重值

def xvaier_init(input_size, output_size):    low = -np.sqrt(6.0/(input_nodes+output_nodes))    high = -low    return tf.random_uniform((input_size, output_size), low, high, dtype = tf.float32)

计算代价函式

代价函式由三部分组成,均方差项,权重衰减项,以及稀疏因子项。
def computecost(w,b,x,w1,b1):    p = 0.1    beta = 3    lamda = 0.00001        hidden_output = tf.sigmoid(tf.matmul(x,w) + b)    pj = tf.reduce_mean(hidden_output, 0)    sparse_cost = tf.reduce_sum(p*tf.log(p/pj)+(1-p)*tf.log((1-p)/(1-pj)))    output = tf.sigmoid(tf.matmul(hidden_output,w1)+b1)    regular = lamda*(tf.reduce_sum(w*w)+tf.reduce_sum(w1*w1))/2    cross_entropy = tf.reduce_mean(tf.pow(output - x, 2))/2 +sparse_cost*beta +     regular #+ regular+sparse_cost*beta     return cross_entropy, hidden_output, output

可视化自编码器

为了使隐藏单元得到最大激励(隐藏单元需要什幺样的特徵输入),将这些特徵输入显示出来。
def show_image(w):    sum = np.sqrt(np.sum(w**2, 0))    changedw = w/sum    a,b = changedw.shape    c = np.sqrt(a*b)    d = int(np.sqrt(a))    e = int(c/d)    buf = 1    newimage = -np.ones((buf+(d+buf)*e,buf+(d+buf)*e))    k = 0    for i in range(e):        for j in range(e):            maxvalue = np.amax(changedw[:,k])            if(maxvalue<0):                maxvalue = -maxvalue            newimage[(buf+i*(d+buf)):(buf+i*(d+buf)+d),(buf+j*(d+buf)):(buf+j*(d+buf)+d)] =             np.reshape(changedw[:,k],(d,d))/maxvalue            k+=1        plt.figure("beauty")    plt.imshow(newimage)    plt.axis('off')    plt.show()   

主函式

通过AdamOptimizer下降误差,调节参数。
def main():    w = tf.Variable(xvaier_init(input_nodes, hidden_size))    b = tf.Variable(tf.truncated_normal([hidden_size],0.1))       x = tf.placeholder(tf.float32, shape = [None, input_nodes])    w1 = tf.Variable(tf.truncated_normal([hidden_size,input_nodes], -0.1, 0.1))    b1 = tf.Variable(tf.truncated_normal([output_nodes],0.1))    cost, hidden_output, output = computecost(w,b,x,w1,b1)    train_step = tf.train.AdamOptimizer().minimize(cost)    train_x = sampleImage()    sess = tf.Session()    sess.run(tf.global_variables_initializer())        for i in range(100000):        _,hidden_output_, output_,cost_,w_= sess.run([train_step, hidden_output, output,cost,w],         feed_dict = {x : train_x})        if i%1000 == 0:            print(hidden_output_)            print(output_)            print(cost_)    np.save("weights1.npy", w_)    show_image(w_)

转载请注明出处海之美文 » 稀疏自编码器

相关推荐

    声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:ailianmeng11@163.com