码迷,mamicode.com
首页 > Web开发 > 详细

(翻译)deeplearning.net/tutorial —— 栈式去噪自编码器(SdA)

时间:2017-02-20 22:53:51      阅读:740      评论:0      收藏:0      [点我收藏+]

标签:[]   init   os.path   nump   reg   const   out   ptr   网络   

前言


 

  栈式去噪自编码器是栈式自动编码器的扩展[Bengio07],并且它在[Vincent08]里有介绍。

  这次教程建立在之前的去噪自编码器Denoising Autoencoders。如果你对自编码器没什么了解,建议你先了解一下。

 

栈式自编码器


 

  通过把上一层去噪自编码器找到的隐藏输入(output code)当作下一层的输入,我们可以把去噪自编码器以栈的形式构成一个深度网络。这种无监督预训练的结构在一层里同时实现。每一层当作一个去噪自编码器,通过重构输入(上一层的输出)最小化损失。一旦前面 技术分享 层被训练好,我们就可以训练第 技术分享 层,因为我们下载可以计算输出的code,或者说是前一层的隐藏表达。

  一旦所有层都预训练好,整个网络进入训练的第二个阶段 fine-tunning。这里我们考虑有监督的 fine-tunning,因为我们想在一个有监督训练中最小化预测错误。首先我们在网络的最顶端加一个logistic回归层(更精确的说,是在输出层的输出code前)。然后我们训练整个网络,正如我们训练一个多层感知机一样。这里,我们只需要考虑每一层自编码器的编码部分。在训练中使用目标分类,这步是有监督的。(详情请看Multilayer Perceptron

  复用之前定义的类,栈式自编码器在Theano里面很容易实现。我们可以看到栈式自编码器有两部分:一系列的自编码器和一个MLP。在预训练的阶段,我们使用第一部分,例如我们使用一系列自编码器加入我们的模型中,然后每个自编码器单独训练。在训练的第二阶段,我们使用第二部分。这两部分是相连的,因为

  • 多个自编码器和MLP中的sigmoid层是共享参数的
  • MLP中间层计算得到的隐藏表达又被当作自动编码器的输入。
 1 class SdA(object):
 2     """栈式去噪自编码器 类
 3 
 4     通过堆积几个dA来组成展示去噪自编码器。第i个dA的隐藏层被当作第i+1层的输入。
 5     第一层dA把SdA的输入当作输入,最后的dA表示输出。在预训练之后,SdA被当作是
 6     普通MLP,dAs只用来初始化权值。
 7     """
 8 
 9     def __init__(
10         self,
11         numpy_rng,
12         theano_rng=None,
13         n_ins=784,
14         hidden_layers_sizes=[500, 500],
15         n_outs=10,
16         corruption_levels=[0.1, 0.1]):
17     self.sigmoid_layers = []
18     self.dA_layers = []
19     self.params = []
20     self.n_layers = len(hidden_layers_sizes)
21 
22     assert self.n_layers > 0
23 
24     if not theano_rng:
25         theano_rng = RandomStreams(numpy_rng.randint(2 ** 30))
26 
27     self.x = T.matrix(x)        # 输入被光栅化的图片
28     self.y = T.ivector(y)        # 1维数组表示[int]标签

self.sigmoid_layers 会储存MLP部分的sigmoid层,self.dA_layers 会储存和MLP有关的去噪自编码。

 

  接下来,我们构造 n_layers 个sigmoid层和 n_layers 去噪自动编码器,其中 n_layers 是我们模型的深度。我们使用Multilayer Perceptron介绍的 HiddenLayer 类,只有一个地方改了,就是我们使用sigmoid 技术分享 代替之前的 tanh 非线性激活函数。我们链接sigmoid层来构建一个MLP,并且构建去噪自编码器,每个的编码部分与对应的sigmoid层共享权值和偏置项。

 1     for i in range(self.n_layers):
 2         # 构建sigmoid层
 3 
 4         # 输入的size,要么是前一层隐藏层的数量
 5         # 要么是SdA的输入
 6         if i == 0:
 7             input_size = n_ins
 8         else:
 9             input_size = hidden_layers_sizes[i-1]
10 
11         # 本层的输入要么是上一层隐藏层的激活
12         # 要么是SdA的输入
13         if i == 0:
14             layer_input = self.x
15         else:
16             layer_input = self.sigmoid_layers[-1].output
17 
18         sigmoid_layer = HiddenLayer(rng=numpy_rng,
19                                     input=layer_input,
20                                     n_in=input_size,
21                                     n_out=hidden_layers_sizes[i],
22                                     activation=T.nnet.sigmoid)
23 
24         # 添加层
25         self.sigmoid_layers.append(sigmoid_layer)
26         
27         self.params.extend(sigmoid_layers.params)
28 
29         dA_layer = dA(numpy_rng=numpy_rng,
30                         theano_rng=theano_rng,
31                         input=layer_input,
32                         n_visible=input_size,
33                         n_hidden=hidden_layers_sizes[i],
34                         W=sigmoid_layer.W,
35                         bhid=sigmoid_layer.b)
36         self.dA_layers.append(dA_layer)

现在我们需要做的就是在sigmoid层之上加一个logistic层。我们使用之前在Classifying MNIST digits using Logistic Regression介绍的 LogisticRegression 类。

 1         self.logLayer = LogisticRegression(
 2             input=self.sigmoid_layers[-1].output,
 3             n_in=hidden_layers_sizes[-1],
 4             n_out=n_outs)
 5 
 6         self.params.extend(self.logLayer.params)
 7         # 构建一个实现单步微调的函数
 8 
 9         # 计算训练第二步需要的损失函数
10         self.finetune_cost = self.logLayer.negative_log_likelihood(self.y)
11         # 计算给定参数的模型梯度
12         # 符号变量指向小批量样本得到的损失
13         self.errors = self.logLayer.errors(self.y)

SdA 类也提供了一个方法在层里为去噪自编码器生成训练函数。他们返回一个list,其中第 i 个元素是一个函数,实现对应 i 层的 dA 的训练。

1         def pretraining_functions(self, train_set_x, batch_size):
2             """生成一列表的函数,每一个实现dA对应层的训练。
3             函数要求输入小批量的下标,训练一个dA只需要迭代,
4             调用对应的函数。
5             """
6 
7             index = T.lscalar(index)

为了可以在训练的时候改变被破坏的等级或学习率,我们使用Theano变量。

 1         def pretraining_functions(self, train_set_x, batch_size):
 2             """生成一列表的函数,每一个实现dA对应层的训练。
 3             函数要求输入小批量的下标,训练一个dA只需要迭代,
 4             调用对应的函数。
 5             """
 6 
 7             index = T.lscalar(index)
 8 
 9             corruption_level = T.scalar(corruption)
10             learning_rate = T.scaler(lr)
11 
12             batch_begin = index * batch_size
13             batch_end = batch_begin + batch_size
14 
15             pretrain_fns = []
16             for dA in self.dA_layers:
17                 cost, updates = dA.get_cost_updates(corruption_level, learning_rate)
18 
19                 fn = theano.function(
20                     inputs=[index,
21                             theano.In(corruption_level, value=0.2),
22                             theano.In(learning_rate, value=0.1)
23                             ],
24                             outputs=cost,
25                             updates=updates,
26                             givens={
27                                 self.x: train_set_x[batch_begin: batch_end]
28                             })
29 
30                 pretrain_fns.append(fn)
31 
32             return pretrain_fns

现在每一个函数 pretrain_fns[i] 以 index,corruption 和 lr 为参数。注意,参数的名字是传给Theano时构造的名字,不是Python的变量(learning_rate, corruption_level)。当我们使用Theano的时候必须记住。

同样地我们为了构建函数而新建在finetuning时候的方法( train_fn, valid_score, test_score)。

 1     def build_finetune_functions(self, datasets, batch_size, learning_rate):
 2 
 3         (train_set_x, train_set_y) = datasets[0]
 4         (valid_set_x, valid_set_t) = datasets[1]
 5         (test_set_x, train_set_y) = datasets[2]
 6 
 7         n_valid_batches = valid_set_x.get_value(borrow=True).shape[0]
 8         n_valid_batches //= batch_size
 9         n_test_batches = test_set_x.get_value(borrow=True).shape[0]
10         n_test_batches //= batch_size
11 
12         index = T.lscalar(index)
13 
14         gparams = T.grad(self.finetune_cost, self.params)
15         updates = [
16             (param, param - gparam * learning_rate)
17             for param, gparam in zip(self.params, gparams)
18         ]
19 
20         train_fn = theano.function(
21             inputs=[index],
22             outputs=self.finetune_cost,
23             updates=updates,
24             givens={
25                 self.x: train_set_x[index * batch_size: (index+1) * batch_size],
26                 self.y: train_set_y[index * batch_size: (index+1) * batch_size]
27             },
28             name=train)
29 
30         test_score_i = theano.function(
31             [index],
32             self.errors,
33             givens={
34                 self.x: test_set_x[
35                     index * batch_size: (index + 1) * batch_size
36                 ],
37                 self.y: test_set_y[
38                     index * batch_size: (index + 1) * batch_size
39                 ]
40             },
41             name=test
42         )
43 
44         valid_score_i = theano.function(
45             [index],
46             self.errors,
47             givens={
48                 self.x: valid_set_x[
49                     index * batch_size: (index + 1) * batch_size
50                 ],
51                 self.y: valid_set_y[
52                     index * batch_size: (index + 1) * batch_size
53                 ]
54             },
55             name=valid
56         )
57 
58         def valid_score():
59             return [valid_score_i(i) for i in range(n_valid_batches)]
60 
61         def test_score():
62             return [test_score_i(i) for i in range(n_test_batches)]
63 
64         return train_fn, valid_score, test_score

注意到 valid_socre 和 test_score 不是一个Theano函数,但是会在验证集和整个测试集循环,构造一个这个数据集上的损失列表。

 

Putting it all together


下面构造一个栈式自编码器

1     numpy_rng = numpy.random.RandomState(89677)
2     print(... building the model)
3     # construct the stacked denoising autoencoder class
4     sda = SdA(
5         numpy_rng=numpy_rng,
6         n_ins=28 * 28,
7         hidden_layers_sizes=[1000, 1000, 1000],
8         n_outs=10
9     )

训练这个网络有两步: 逐层预训练,然后fine-tuning。

 

在预训练阶段,我们会循环所有的层。对于每一层,我们会使用Theano编译的函数来实现SGD的前向反馈实现权值更新,减少重构损失。

 1     #########################
 2     # PRETRAINING THE MODEL #
 3     #########################
 4     print(... getting the pretraining functions)
 5     pretraining_fns = sda.pretraining_functions(train_set_x=train_set_x,
 6                                                 batch_size=batch_size)
 7 
 8     print(... pre-training the model)
 9     start_time = timeit.default_timer()
10     ## Pre-train layer-wise
11     corruption_levels = [.1, .2, .3]
12     for i in range(sda.n_layers):
13         # go through pretraining epochs
14         for epoch in range(pretraining_epochs):
15             # go through the training set
16             c = []
17             for batch_index in range(n_train_batches):
18                 c.append(pretraining_fns[i](index=batch_index,
19                          corruption=corruption_levels[i],
20                          lr=pretrain_lr))
21             print(Pre-training layer %i, epoch %d, cost %f % (i, epoch, numpy.mean(c, dtype=float64)))
22 
23     end_time = timeit.default_timer()
24 
25     print((The pretraining code for file  +
26            os.path.split(__file__)[1] +
27             ran for %.2fm % ((end_time - start_time) / 60.)), file=sys.stderr)

fine-tuning和Multilayer Perceptron类似。唯一的不同就是使用了 build_finetune_functions。

 

Running the Code


 

代码下载。here.

 

Tips and Tricks


 

一个优化运行时间的方法就是(假设你有足够的内存),你在优化第 k 个 dA 的权值的时候,可以同时计算出第 k+1 层的输出,即第 k 层输入可以马上算出来然后和之前的 dA 分开运行。

 

参考


 

1. Stacked Denoising Autoencoders (SdA)-  http://deeplearning.net/tutorial/SdA.html

 

(翻译)deeplearning.net/tutorial —— 栈式去噪自编码器(SdA)

标签:[]   init   os.path   nump   reg   const   out   ptr   网络   

原文地址:http://www.cnblogs.com/mangoyuan/p/6420851.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!