@@ -244,9 +244,280 @@ class dA(object):
244244 }
245245 )
246246```
247- 假设没有最小化重构误差的限制,一个有n个输入的自动编码机
247+ 假设除了最小化重构误差外没有别的的限制,一个有n个输入和n(或者更大)维编码学习能力的自动编码机定义函数,将倾向于去映射出它的输入副本。这种自动编码机将无法从训练样本的分布中区分任何测试样例。(无效编码机:输出与输入完全相同)。
248248
249+ 让人惊讶地,在[ Bengio07] ( http://deeplearning.net/tutorial/references.html#bengio07 ) 的实验指出,在实践中,当通过随机梯度下降训练时,有比输入更多的隐藏单元(称为超完备)的非线性的自动编码机可以产生有效的表达。(这里,有效指的是编码作为网络的输入获得了更低的分类误差)。
249250
251+ 一个简单的解释是,使用early-stopping的随机梯度下降是与参数的L2正则化相似的。为了去实现对连续性输入有更好的重建,包含非线性隐藏单元的单隐藏层的自动编码机需要非常小的权值在第一(编码)层,以使得将非线性隐藏单元进入他们的线性区域(参考sigmoid函数),然后在第二(解码)层有更大的权值。对于二进制输入,非常大的权值也需要彻底的最小化重构误差。因为隐性的或者显性的正则化将使得获得大权值的解决方案变得困难,这个最优化算法发现在训练样本中表现好的编码。这意味着,表达是利用训练集的统计规律来实现的,而不仅仅是复制输入。
252+
253+ 这里有其他方法,使得一个有比输入有更多隐藏单元的自动编码机,去避免只学习它本身,而是在输入的隐藏表达中捕捉到有用的东西。一个是添加稀疏性(迫使许多隐单元是0或者接近0)。稀疏性已经被很成功的发挥了[ Ranzato07] ( http://deeplearning.net/tutorial/references.html#ranzato07 ) [ Lee08] ( http://deeplearning.net/tutorial/references.html#lee08 ) 。另一个是,在输入到重建过程中,增加从输入到重建的转换中的随机性。这个技术在受限玻尔兹曼机中被使用(Restricted Boltzmann Machines,在后面的章节中讨论),还有降噪自动编码机,在后面讨论。
254+
255+ ###降噪自动编码机
256+
257+ 降噪自动编码机的思想是很简单饿。为了迫使隐藏层去发现更加鲁棒性的特征,避免它只是去简单的学习定义,我们训练自动编码机去重建被破坏的输入版本的数据。
258+
259+ 这个降噪自动编码机是自动编码机的随机版本。直观上讲,一个降噪自动编码机做2件事情:尝试对输入进行编码(保护输入信息),然后尝试去消除输入中的随机差错产生的影响。后者可以通过捕捉输入间的统计相关性来实现。降噪自动编码机可以从不到的角度来理解(流行学习角度,随机操作角度,自下而上的信息论角度,自上而下的生成模型角度),所有的这些在[ Vincent08] ( http://deeplearning.net/tutorial/references.html#vincent08 ) 中被解释。在[ Bengio09] ( http://deeplearning.net/tutorial/references.html#bengio09 ) 的第7.2节有自动编码机的综述。
260+
261+ 在[ Vincent08] ( http://deeplearning.net/tutorial/references.html#vincent08 ) 中,随机差错进程随机的设定部分(也可以是一半)输入为0。因此降噪自动编码机尝试去从未被污染的值中去预测被污染的(丢失)的值,通过随机的选择丢失模式下的仔鸡。注意如何能预测从剩下的变量的任意子集是一个充分条件,去完全捕获一组变量之间的联合分布(这是Gibbs采样工作)。
262+
263+ 从自动编码机的类转换为降噪自动编码机,我们需要去增加一个随机误差步骤去应用到输入中。这个输入可以通过许多方法来污染,但在这个教程中,我们将支持以输入的随机性来腐化原始数据,使它趋向于0。代码如下:
264+
265+ ``` Python
266+ from theano.tensor.shared_randomstreams import RandomStreams
267+
268+ def get_corrupted_input (self , input , corruption_level ):
269+ """ This function keeps ``1-corruption_level`` entries of the inputs the same
270+ and zero-out randomly selected subset of size ``coruption_level``
271+ Note : first argument of theano.rng.binomial is the shape(size) of
272+ random numbers that it should produce
273+ second argument is the number of trials
274+ third argument is the probability of success of any trial
275+
276+ this will produce an array of 0s and 1s where 1 has a probability of
277+ 1 - ``corruption_level`` and 0 with ``corruption_level``
278+ """
279+ return self .theano_rng.binomial(size = input .shape, n = 1 , p = 1 - corruption_level) * input
280+ ```
281+ 在层叠自动编码机类([ 层叠自动编码机] ( http://deeplearning.net/tutorial/SdA.html#stacked-autoencoders ) )中,` dA ` 类中的权值不得不和相应的sigmoid层共享。因为这个原因,dA的构建也将Theano变量指向了共享参数。假如这些参数被设置为` None ` ,新的参数会被构建。
282+
283+ 最后的降噪自动编码机类就变成了这样:
284+
285+ ``` Python
286+ class dA (object ):
287+ """ Denoising Auto-Encoder class (dA)
288+
289+ A denoising autoencoders tries to reconstruct the input from a corrupted
290+ version of it by projecting it first in a latent space and reprojecting
291+ it afterwards back in the input space. Please refer to Vincent et al.,2008
292+ for more details. If x is the input then equation (1) computes a partially
293+ destroyed version of x by means of a stochastic mapping q_D. Equation (2)
294+ computes the projection of the input into the latent space. Equation (3)
295+ computes the reconstruction of the input, while equation (4) computes the
296+ reconstruction error.
297+
298+ .. math::
299+
300+ \t ilde{x} ~ q_D(\t ilde{x}|x) (1)
301+
302+ y = s(W \t ilde{x} + b) (2)
303+
304+ x = s(W' y + b') (3)
305+
306+ L(x,z) = -sum_{k=1}^d [x_k \log z_k + (1-x_k) \log( 1-z_k)] (4)
307+
308+ """
309+
310+ def __init__ (self , numpy_rng , theano_rng = None , input = None , n_visible = 784 , n_hidden = 500 ,
311+ W = None , bhid = None , bvis = None ):
312+ """
313+ Initialize the dA class by specifying the number of visible units (the
314+ dimension d of the input ), the number of hidden units ( the dimension
315+ d' of the latent or hidden space ) and the corruption level. The
316+ constructor also receives symbolic variables for the input, weights and
317+ bias. Such a symbolic variables are useful when, for example the input is
318+ the result of some computations, or when weights are shared between the
319+ dA and an MLP layer. When dealing with SdAs this always happens,
320+ the dA on layer 2 gets as input the output of the dA on layer 1,
321+ and the weights of the dA are used in the second stage of training
322+ to construct an MLP.
323+
324+ :type numpy_rng: numpy.random.RandomState
325+ :param numpy_rng: number random generator used to generate weights
326+
327+ :type theano_rng: theano.tensor.shared_randomstreams.RandomStreams
328+ :param theano_rng: Theano random generator; if None is given one is generated
329+ based on a seed drawn from `rng`
330+
331+ :type input: theano.tensor.TensorType
332+ :paran input: a symbolic description of the input or None for standalone
333+ dA
334+
335+ :type n_visible: int
336+ :param n_visible: number of visible units
337+
338+ :type n_hidden: int
339+ :param n_hidden: number of hidden units
340+
341+ :type W: theano.tensor.TensorType
342+ :param W: Theano variable pointing to a set of weights that should be
343+ shared belong the dA and another architecture; if dA should
344+ be standalone set this to None
345+
346+ :type bhid: theano.tensor.TensorType
347+ :param bhid: Theano variable pointing to a set of biases values (for
348+ hidden units) that should be shared belong dA and another
349+ architecture; if dA should be standalone set this to None
350+
351+ :type bvis: theano.tensor.TensorType
352+ :param bvis: Theano variable pointing to a set of biases values (for
353+ visible units) that should be shared belong dA and another
354+ architecture; if dA should be standalone set this to None
355+
356+
357+ """
358+ self .n_visible = n_visible
359+ self .n_hidden = n_hidden
360+
361+ # create a Theano random generator that gives symbolic random values
362+ if not theano_rng :
363+ theano_rng = RandomStreams(rng.randint(2 ** 30 ))
364+
365+ # note : W' was written as `W_prime` and b' as `b_prime`
366+ if not W:
367+ # W is initialized with `initial_W` which is uniformely sampled
368+ # from -4.*sqrt(6./(n_visible+n_hidden)) and 4.*sqrt(6./(n_hidden+n_visible))
369+ # the output of uniform if converted using asarray to dtype
370+ # theano.config.floatX so that the code is runable on GPU
371+ initial_W = numpy.asarray(numpy_rng.uniform(
372+ low = - 4 * numpy.sqrt(6 . / (n_hidden + n_visible)),
373+ high = 4 * numpy.sqrt(6 . / (n_hidden + n_visible)),
374+ size = (n_visible, n_hidden)), dtype = theano.config.floatX)
375+ W = theano.shared(value = initial_W, name = ' W' )
376+
377+ if not bvis:
378+ bvis = theano.shared(value = numpy.zeros(n_visible,
379+ dtype = theano.config.floatX), name = ' bvis' )
380+
381+ if not bhid:
382+ bhid = theano.shared(value = numpy.zeros(n_hidden,
383+ dtype = theano.config.floatX), name = ' bhid' )
384+
385+ self .W = W
386+ # b corresponds to the bias of the hidden
387+ self .b = bhid
388+ # b_prime corresponds to the bias of the visible
389+ self .b_prime = bvis
390+ # tied weights, therefore W_prime is W transpose
391+ self .W_prime = self .W.T
392+ self .theano_rng = theano_rng
393+ # if no input is given, generate a variable representing the input
394+ if input == None :
395+ # we use a matrix because we expect a minibatch of several examples,
396+ # each example being a row
397+ self .x = T.dmatrix(name = ' input' )
398+ else :
399+ self .x = input
400+
401+ self .params = [self .W, self .b, self .b_prime]
402+
403+ def get_corrupted_input (self , input , corruption_level ):
404+ """ This function keeps ``1-corruption_level`` entries of the inputs the same
405+ and zero-out randomly selected subset of size ``coruption_level``
406+ Note : first argument of theano.rng.binomial is the shape(size) of
407+ random numbers that it should produce
408+ second argument is the number of trials
409+ third argument is the probability of success of any trial
410+
411+ this will produce an array of 0s and 1s where 1 has a probability of
412+ 1 - ``corruption_level`` and 0 with ``corruption_level``
413+ """
414+ return self .theano_rng.binomial(size = input .shape, n = 1 , p = 1 - corruption_level) * input
415+
416+
417+ def get_hidden_values (self , input ):
418+ """ Computes the values of the hidden layer """
419+ return T.nnet.sigmoid(T.dot(input , self .W) + self .b)
420+
421+ def get_reconstructed_input (self , hidden ):
422+ """ Computes the reconstructed input given the values of the hidden layer """
423+ return T.nnet.sigmoid(T.dot(hidden, self .W_prime) + self .b_prime)
424+
425+ def get_cost_updates (self , corruption_level , learning_rate ):
426+ """ This function computes the cost and the updates for one trainng
427+ step of the dA """
428+
429+ tilde_x = self .get_corrupted_input(self .x, corruption_level)
430+ y = self .get_hidden_values( tilde_x)
431+ z = self .get_reconstructed_input(y)
432+ # note : we sum over the size of a datapoint; if we are using minibatches,
433+ # L will be a vector, with one entry per example in minibatch
434+ L = - T.sum(self .x * T.log(z) + (1 - self .x) * T.log(1 - z), axis = 1 )
435+ # note : L is now a vector, where each element is the cross-entropy cost
436+ # of the reconstruction of the corresponding example of the
437+ # minibatch. We need to compute the average of all these to get
438+ # the cost of the minibatch
439+ cost = T.mean(L)
440+
441+ # compute the gradients of the cost of the `dA` with respect
442+ # to its parameters
443+ gparams = T.grad(cost, self .params)
444+ # generate the list of updates
445+ updates = []
446+ for param, gparam in zip (self .params, gparams):
447+ updates.append((param, param - learning_rate * gparam))
448+
449+ return (cost, updates)
450+ ```
451+
452+
453+ ###将它组合起来
454+
455+ 现在去构建一个` dA ` 类和训练它变得很简单了。
456+
457+ ``` Python
458+ # allocate symbolic variables for the data
459+ index = T.lscalar() # index to a [mini]batch
460+ x = T.matrix(' x' ) # the data is presented as rasterized images
461+
462+ # #####################
463+ # BUILDING THE MODEL #
464+ # #####################
465+
466+ rng = numpy.random.RandomState(123 )
467+ theano_rng = RandomStreams(rng.randint(2 ** 30 ))
468+
469+ da = dA(numpy_rng = rng, theano_rng = theano_rng, input = x,
470+ n_visible = 28 * 28 , n_hidden = 500 )
471+
472+ cost, updates = da.get_cost_updates(corruption_level = 0.2 ,
473+ learning_rate = learning_rate)
474+
475+
476+ train_da = theano.function([index], cost, updates = updates,
477+ givens = {x: train_set_x[index * batch_size: (index + 1 ) * batch_size]})
478+
479+ start_time = time.clock()
480+
481+ # ###########
482+ # TRAINING #
483+ # ###########
484+
485+ # go through training epochs
486+ for epoch in xrange (training_epochs):
487+ # go through trainng set
488+ c = []
489+ for batch_index in xrange (n_train_batches):
490+ c.append(train_da(batch_index))
491+
492+ print ' Training epoch %d , cost ' % epoch, numpy.mean(c)
493+
494+ end_time = time.clock
495+
496+ training_time = (end_time - start_time)
497+
498+ print (' Training took %f minutes' % (pretraining_time / 60 .))
499+ ```
500+
501+ 为了了解网络学习了什么东西,我们将会描述出滤波器(通过权值矩阵来定义)。记住,事实上它没有提供完整的情况,因为我们忽略了偏置,并且画出的权值被乘以了常数(权值被转换到了0-1之间)。
502+
503+ 去画出我们的滤波器,我们需要` title_raster_images ` (看[ Plotting Samples and Filters] ( http://deeplearning.net/tutorial/utilities.html#how-to-plot ) ),所以我们强烈建议读者去了解它。当然,也在PIL(python image library)的帮助下,下面行的代码将把滤波器保存为图像:
504+
505+ ``` Python
506+ image = Image.fromarray(tile_raster_images(X = da.W.get_value(borrow = True ).T,
507+ img_shape = (28 , 28 ), tile_shape = (10 , 10 ),
508+ tile_spacing = (1 , 1 )))
509+ image.save(' filters_corruption_30.png' )
510+ ```
511+
512+ ###运行这个代码
513+
514+ 当我们不使用任何噪声的时候,获得的滤波器如下:
515+
516+ ![ filter_not_nosie] ( /images/5_running_code_1.png )
517+
518+ 有30%噪声的时候,滤波器如下:
519+
520+ ![ filter_with_nosie] ( /images/5_running_code_2.png )
250521
251522
252523
0 commit comments