深度学习算法实践15---堆叠去噪自动编码机(SdA)原理及实现

在上一篇博文中,我们讨论了去噪自动编码机(dA),并讨论了Theano框架实现的细节。在本节中,我们将讨论去噪自动编码机(dA)的主要应用,即组成堆叠自动编码机(SdA),我们将以MNIST手写字母识别为例,用堆叠自动编码机(SdA)来解决这一问题。

堆叠自动编码机(SdA)是由一系列去噪自动编码机堆叠而成,每个去噪自动编码机的中间层(即编码层)作为下一层的输入层,这样一层一层堆叠起来,构成一个深层网络,这些网络组成堆叠去噪自动编码机(SdA)的表示部分。这部分通过无监督学习,逐层进行培训,每一层均可以还原加入随机噪音后的输入信号,而此时在每个去噪自动编码机(dA)中间层即编码层的输出信号,可以视为原始输入信号的某种表示,是对原始输入信号的某种简化表示。

当将所有去噪自动编机(dA)堆叠形成的网络训练完成之后,再把最后一层的中间层即编码接入逻辑回归网络,作为其输入层,这样就形成了一个新的多层BP网络,隐藏层之间的权值,就是前面利用去噪自动编码机(dA)逐层训练时所得到的权值矩阵。然后将这个网络视为一个标准的BP网络,利用我们原来的BP网络算法,进行监督学习,最后达到我们希望的状态。

可能读者会有疑问,为什么直接就用多层BP网络呢?这样先逐层训练去噪自动编码机(SdA),然后再组成BP网络,进行监督学习,好像很麻烦呀。其实BP网络诞生之初,就有人基于这个做具有多个隐藏层的深度网络了。但是人们很快就发现,基于误差反向传播的BP网各,利用随机梯度下降算法来调整权值,但是随着层数的加深,离输出层越远的隐藏层,其权值调整量将递减,最后导致这种深度网络学习速度非常慢,直接限制了其的使用,因此在深度学习崛起之前,深层网络基本没有实际成功的应用案例。
从我们的堆叠自动编码机(SdA)来看,我们首先通过逐层非监督学习方式训练独立的去噪自动编码机,可以视为神经网络自动发现问题域的特征的过程,通过自动特征提取,来找到解决问题的最优特征。而去噪自动编码机(SdA)的训练,可以视为已经对多层BP网络进行了初步训练,最后的监督学习是对网络权值的微调优化。这样可以较好的解决深度BP网各学习收敛速度慢的问题,使其具有实用价值。

首先定义堆叠去噪自动编码机(SdA)类,代码如下所示:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. import os  
  4. import sys  
  5. import timeit  
  6.   
  7. import numpy  
  8.   
  9. import theano  
  10. import theano.tensor as T  
  11. from theano.tensor.shared_randomstreams import RandomStreams  
  12.   
  13. from logistic_regression import LogisticRegression  
  14. from hidden_layer import HiddenLayer  
  15. from denosing_autoencoder import DenosingAutoencoder  
  16.   
  17. class SdA(object):  
  18.     def __init__(  
  19.         self,  
  20.         numpy_rng,  
  21.         theano_rng=None,  
  22.         n_ins=784,  
  23.         hidden_layers_sizes=[500500],  
  24.         n_outs=10,  
  25.         corruption_levels=[0.10.1]  
  26.     ):  
  27.         self.sigmoid_layers = []  
  28.         self.dA_layers = []  
  29.         self.params = []  
  30.         self.n_layers = len(hidden_layers_sizes)  
  31.   
  32.         assert self.n_layers > 0  
  33.   
  34.         if not theano_rng:  
  35.             theano_rng = RandomStreams(numpy_rng.randint(2 ** 30))  
  36.         self.x = T.matrix('x')  # the data is presented as rasterized images  
  37.         self.y = T.ivector('y')  # the labels are presented as 1D vector of  
  38.         for i in range(self.n_layers):  
  39.             if i == 0:  
  40.                 input_size = n_ins  
  41.             else:  
  42.                 input_size = hidden_layers_sizes[i - 1]  
  43.             if i == 0:  
  44.                 layer_input = self.x  
  45.             else:  
  46.                 layer_input = self.sigmoid_layers[-1].output  
  47.   
  48.             sigmoid_layer = HiddenLayer(rng=numpy_rng,  
  49.                                         input=layer_input,  
  50.                                         n_in=input_size,  
  51.                                         n_out=hidden_layers_sizes[i],  
  52.                                         activation=T.nnet.sigmoid)  
  53.             self.sigmoid_layers.append(sigmoid_layer)  
  54.             self.params.extend(sigmoid_layer.params)  
  55.             dA_layer = DenosingAutoencoder(numpy_rng=numpy_rng,  
  56.                           theano_rng=theano_rng,  
  57.                           input=layer_input,  
  58.                           n_visible=input_size,  
  59.                           n_hidden=hidden_layers_sizes[i],  
  60.                           W=sigmoid_layer.W,  
  61.                           bhid=sigmoid_layer.b)  
  62.             self.dA_layers.append(dA_layer)  
  63.         self.logLayer = LogisticRegression(  
  64.             input=self.sigmoid_layers[-1].output,  
  65.             n_in=hidden_layers_sizes[-1],  
  66.             n_out=n_outs  
  67.         )  
  68.         self.params.extend(self.logLayer.params)  
  69.         self.finetune_cost = self.logLayer.negative_log_likelihood(self.y)  
  70.         self.errors = self.logLayer.errors(self.y)  
  71.   
  72.     def pretraining_functions(self, train_set_x, batch_size):  
  73.         index = T.lscalar('index')  # index to a minibatch  
  74.         corruption_level = T.scalar('corruption')  # % of corruption to use  
  75.         learning_rate = T.scalar('lr')  # learning rate to use  
  76.         batch_begin = index * batch_size  
  77.         batch_end = batch_begin + batch_size  
  78.         pretrain_fns = []  
  79.         for dA in self.dA_layers:  
  80.             cost, updates = dA.get_cost_updates(corruption_level,  
  81.                                                 learning_rate)  
  82.             fn = theano.function(  
  83.                 inputs=[  
  84.                     index,  
  85.                     theano.In(corruption_level, value=0.2),  
  86.                     theano.In(learning_rate, value=0.1)  
  87.                 ],  
  88.                 outputs=cost,  
  89.                 updates=updates,  
  90.                 givens={  
  91.                     self.x: train_set_x[batch_begin: batch_end]  
  92.                 }  
  93.             )  
  94.             pretrain_fns.append(fn)  
  95.         return pretrain_fns  
  96.   
  97.     def build_finetune_functions(self, datasets, batch_size, learning_rate):  
  98.         (train_set_x, train_set_y) = datasets[0]  
  99.         (valid_set_x, valid_set_y) = datasets[1]  
  100.         (test_set_x, test_set_y) = datasets[2]  
  101.         n_valid_batches = valid_set_x.get_value(borrow=True).shape[0]  
  102.         n_valid_batches //= batch_size  
  103.         n_test_batches = test_set_x.get_value(borrow=True).shape[0]  
  104.         n_test_batches //= batch_size  
  105.         index = T.lscalar('index')  
  106.         gparams = T.grad(self.finetune_cost, self.params)  
  107.         updates = [  
  108.             (param, param - gparam * learning_rate)  
  109.             for param, gparam in zip(self.params, gparams)  
  110.         ]  
  111.         train_fn = theano.function(  
  112.             inputs=[index],  
  113.             outputs=self.finetune_cost,  
  114.             updates=updates,  
  115.             givens={  
  116.                 self.x: train_set_x[  
  117.                     index * batch_size: (index + 1) * batch_size  
  118.                 ],  
  119.                 self.y: train_set_y[  
  120.                     index * batch_size: (index + 1) * batch_size  
  121.                 ]  
  122.             },  
  123.             name='train'  
  124.         )  
  125.         test_score_i = theano.function(  
  126.             [index],  
  127.             self.errors,  
  128.             givens={  
  129.                 self.x: test_set_x[  
  130.                     index * batch_size: (index + 1) * batch_size  
  131.                 ],  
  132.                 self.y: test_set_y[  
  133.                     index * batch_size: (index + 1) * batch_size  
  134.                 ]  
  135.             },  
  136.             name='test'  
  137.         )  
  138.         valid_score_i = theano.function(  
  139.             [index],  
  140.             self.errors,  
  141.             givens={  
  142.                 self.x: valid_set_x[  
  143.                     index * batch_size: (index + 1) * batch_size  
  144.                 ],  
  145.                 self.y: valid_set_y[  
  146.                     index * batch_size: (index + 1) * batch_size  
  147.                 ]  
  148.             },  
  149.             name='valid'  
  150.         )  
  151.         def valid_score():  
  152.             return [valid_score_i(i) for i in range(n_valid_batches)]  
  153.         def test_score():  
  154.             return [test_score_i(i) for i in range(n_test_batches)]  
  155.         return train_fn, valid_score, test_score  
在构造函数中,n_ins为输入信号维数,hidden_layer_sizes是一个列表,其中每个元素代表一个隐藏层的神经元数量,可以定义多层,例如在上例中,缺省情况下即为两层,n_outs为输出神经元个数,由于是手写数字识别,因此该值为10,corruption_levels是去噪自动编码机(dA)随机噪音级别,上例中分别为10%的随机噪音。

在构造网络过程中,首先建立BP网络的隐藏层,然后权值和Bias与去噪自动编码机(dA)共享,按照缺省参数,会组成一个输入层有584个神经元,第一隐藏层500个神经元,第二个隐藏层500个神经元,输出层为10个神经元,代码中循环部分具体操作如下所示:

i=0时:

    input_size = 584, layer_input = x即为原始输入信号

    BP隐藏层定义:input=x(原始输入信号)n_in=584(28*28),n_out=hidden_layer_sizes[0]=500,激活函数为Sigmoid函数

    dA定义:input=原始输入信号,n_visible=584, n_hidden=hidden_layer_sizes[0]=500,权值与上面定义的隐藏层共享,Bias与上面定义的隐藏层共享

i=1时:

    input_size=500

    layer_input=上一层输出

    BP隐藏层:input=上一层输出,n_in=500,n_out=hidden_layer_sizes[1]=500,激活函数为Sigmoid函数

    dA定义:input=上一层输出,n_visible=500,n_hidden=hidden_layer_sizes[0]=500,权值与上面定义的隐藏层共享,Bias与上面定义的隐藏层共享

至此循环结束,接着定义最后的逻辑回归层:输入层为上面最后一层的输出,输入层节点数为500,输出层节点数为10。

当创建好网络结构之后,SdA类定义了两阶段的训练方法,pretraining_functions用于逐层训练去噪自动编码机(dA),而build_finetune_functions则用于训练BP网络,由于上面的代码与DenosingAutoencoder和MLP类相类似,这里就不再重复介绍了。

下面定义SdAEngine类,用于完成具体的模型训练工作,代码如下所示:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. import os  
  4. import sys  
  5. import timeit  
  6.   
  7. import numpy  
  8.   
  9. import theano  
  10. import theano.tensor as T  
  11. from theano.tensor.shared_randomstreams import RandomStreams  
  12.   
  13. from mnist_loader import MnistLoader  
  14. from mlp import HiddenLayer  
  15. from sda import SdA  
  16.   
  17.   
  18. class SdAEngine(object):  
  19.     def __init__(self):  
  20.         print('create SdAEngine')  
  21.   
  22.     def train(finetune_lr=0.1, pretraining_epochs=15,  
  23.                  pretrain_lr=0.001, training_epochs=1000,  
  24.                  dataset='mnist.pkl.gz', batch_size=1):  
  25.         loader = MnistLoader()  
  26.         datasets = loader.load_data(dataset)  
  27.         train_set_x, train_set_y = datasets[0]  
  28.         valid_set_x, valid_set_y = datasets[1]  
  29.         test_set_x, test_set_y = datasets[2]  
  30.         n_train_batches = train_set_x.get_value(borrow=True).shape[0]  
  31.         n_train_batches //= batch_size  
  32.         numpy_rng = numpy.random.RandomState(89677)  
  33.         print('... building the model')  
  34.         sda = SdA(  
  35.             numpy_rng=numpy_rng,  
  36.             n_ins=28 * 28,  
  37.             hidden_layers_sizes=[100010001000],  
  38.             n_outs=10  
  39.         )  
  40.         print('... getting the pretraining functions')  
  41.         pretraining_fns = sda.pretraining_functions(train_set_x=train_set_x,  
  42.                                                     batch_size=batch_size)  
  43.         print('... pre-training the model')  
  44.         start_time = timeit.default_timer()  
  45.         corruption_levels = [.1, .2, .3]  
  46.         for i in range(sda.n_layers):  
  47.             for epoch in range(pretraining_epochs):  
  48.                 c = []  
  49.                 for batch_index in range(n_train_batches):  
  50.                     c.append(pretraining_fns[i](index=batch_index,  
  51.                              corruption=corruption_levels[i],  
  52.                              lr=pretrain_lr))  
  53.                 print('Pre-training layer %i, epoch %d, cost %f' % (i, epoch, numpy.mean(c)))  
  54.         end_time = timeit.default_timer()  
  55.         print(('The pretraining code for file ' +  
  56.                os.path.split(__file__)[1] +  
  57.                ' ran for %.2fm' % ((end_time - start_time) / 60.)), file=sys.stderr)  
  58.         print('... getting the finetuning functions')  
  59.         train_fn, validate_model, test_model = sda.build_finetune_functions(  
  60.             datasets=datasets,  
  61.             batch_size=batch_size,  
  62.             learning_rate=finetune_lr  
  63.         )  
  64.         print('... finetunning the model')  
  65.         patience = 10 * n_train_batches  # look as this many examples regardless  
  66.         patience_increase = 2.  # wait this much longer when a new best is  
  67.                                 # found  
  68.         improvement_threshold = 0.995  # a relative improvement of this much is  
  69.                                        # considered significant  
  70.         validation_frequency = min(n_train_batches, patience // 2)  
  71.         best_validation_loss = numpy.inf  
  72.         test_score = 0.  
  73.         start_time = timeit.default_timer()  
  74.         done_looping = False  
  75.         epoch = 0  
  76.         while (epoch < training_epochs) and (not done_looping):  
  77.             epoch = epoch + 1  
  78.             for minibatch_index in range(n_train_batches):  
  79.                 minibatch_avg_cost = train_fn(minibatch_index)  
  80.                 iter = (epoch - 1) * n_train_batches + minibatch_index  
  81.                 if (iter + 1) % validation_frequency == 0:  
  82.                     validation_losses = validate_model()  
  83.                     this_validation_loss = numpy.mean(validation_losses)  
  84.                     print('epoch %i, minibatch %i/%i, validation error %f %%' %  
  85.                           (epoch, minibatch_index + 1, n_train_batches,  
  86.                            this_validation_loss * 100.))  
  87.                     if this_validation_loss < best_validation_loss:  
  88.                         if (  
  89.                             this_validation_loss < best_validation_loss *  
  90.                             improvement_threshold  
  91.                         ):  
  92.                             patience = max(patience, iter * patience_increase)  
  93.                         best_validation_loss = this_validation_loss  
  94.                         best_iter = iter  
  95.                         test_losses = test_model()  
  96.                         test_score = numpy.mean(test_losses)  
  97.                         print(('     epoch %i, minibatch %i/%i, test error of '  
  98.                                'best model %f %%') %  
  99.                               (epoch, minibatch_index + 1, n_train_batches,  
  100.                                test_score * 100.))  
  101.                 if patience <= iter:  
  102.                     done_looping = True  
  103.                     break  
  104.         end_time = timeit.default_timer()  
  105.         print(  
  106.             (  
  107.                 'Optimization complete with best validation score of %f %%, '  
  108.                 'on iteration %i, '  
  109.                 'with test performance %f %%'  
  110.             )  
  111.             % (best_validation_loss * 100., best_iter + 1, test_score * 100.)  
  112.         )  
  113.         print(('The training code for file ' +  
  114.                os.path.split(__file__)[1] +  
  115.                ' ran for %.2fm' % ((end_time - start_time) / 60.)), file=sys.stderr)  
上面的代码基本上是DenosingAutoencoder和MLP训练算法的合成,没有太多可以介绍的部分。

将上面的代码,结合之间介绍的LogisticRegression、HIddenLayer、MnistLoader等类,就可以构成一个完整的堆叠自动编码机(SdA)了。下面是训练网络的代码:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. from sda_engine import SdAEngine  
  2.   
  3. if __name__ == '__main__':  
  4.     engine = SdAEngine()  
  5.     engine.train()  
运行上述代码,在我的Mac笔记本上需要跑一个晚上,可以得到识别错误率为1%左右。

大家可以看到,堆叠去噪自动编码机(SdA)训练速度和识别精度方面,与之前介绍的卷积神经网络(CNN)相比,都会有些差距,这就说明不同的网络,适合不同的任务。图像识别领域,首选是卷积神经网络(CNN),而在图像搜索等领域,堆叠去噪自动编码机(SdA)的应用效果更佳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值