简介
这个教程涵盖了深度学习(Deep Learning)的一些重要概念,是一个快速入门的大纲教程,包含了三个部分:
- 第一部分-数据集:介绍了MNIST数据集和使用方法;
- 第二部分-标记法:介绍了主要概念的符号标记方法;
- 第三部分-监督优化入门:介绍了一些深度学习的重要概念;
1.数据集
1.MNIST数据集
这个数据集(mnist.pkl.gz)可以在CSDN下载中免费下载:http://download.csdn.net/detail/ws_20100/9224993
MNIST数据集包含手写字符图像,其中有60000个是训练样本,而10000个是测试样本。对于很多论文,包含这篇教程,60000个训练样本被分为50000个样本作训练集,10000个样本作验证集(用于选择超参数,例如学习率和模型大小)。所有的数字图像在大小上都已经规范化并且处于28
这些是一些MNIST数据集样本:
为了方便我们在python中使用MNIST数据集,我们对MNIST数据集进行处理。处理后的数据集有3个列表:训练集,验证集和测试集。每个列表包含多个二元组,每个二元组包含一个图像和对应的标签。图像被表示为一个1
import cPickle, gzip, numpy
# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = cPickle.load(f)
f.close()
当使用数据集的时候,我们通常将其分成多个minibatch(详细的请看下面的随机梯度下降法)。在实际使用时,最好使用共享变量,因为共享变量和GPU相关。如果不使用共享变量,使用GPU运算可能不比CPU快多少,可能更慢。存取数据,读取minibatch的代码如下:
def shared_dataset(data_xy):
""" Function that loads the dataset into shared variables
The reason we store our dataset in shared variables is to allow
Theano to copy it into the GPU memory (when code is run on GPU).
Since copying data into the GPU is slow, copying a minibatch everytime
is needed (the default behaviour if the data is not in a shared
variable) would lead to a large decrease in performance.
"""
data_x, data_y = data_xy
shared_x = theano.shared(numpy.asarray(data_x, dtype=theano.config.floatX))
shared_y = theano.shared(numpy.asarray(data_y, dtype=theano.config.floatX))
# When storing data on the GPU it has to be stored as floats
# therefore we will store the labels as ``floatX`` as well
# (``shared_y`` does exactly that). But during our computations
# we need them as ints (we use labels as index, and if they are
# floats it doesn't make sense) therefore instead of returning
# ``shared_y`` we will have to cast it to int. This little hack
# lets us get around this issue
return shared_x, T.cast(shared_y, 'int32')
test_set_x, test_set_y = shared_dataset(test_set)
valid_set_x, valid_set_y = shared_dataset(valid_set)
train_set_x, train_set_y = shared_dataset(train_set)
batch_size = 500 # size of the minibatch
# accessing the third minibatch of the training set
data = train_set_x[2 * batch_size: 3 * batch_size]
label = train_set_y[2 * batch_size: 3 * batch_size]
If you are running your code on the GPU and the dataset you are using is too large to fit in memory the code will crash. In such a case you should store the data in a shared variable. You can however store a sufficiently small chunk of your data (several minibatches) in a shared variable and use that during training. Once you got through the chunk, update the values it stores. This way you minimize the number of data transfers between CPU memory and GPU memory.
2.标记法
1.数据集标记法
我们将数据集标记为
这个教程多数算法都是用于处理分类问题,所以数据集
2.数学约定
W :大写符号代表一个矩阵(除非特殊指定)。Wij :矩阵W 的第i 行第j 列元素。Wi⋅,Wi :矩阵W 的第i 行。W⋅j :矩阵W 的第j 列。b :小写符号代表一个向量(除非特殊指定)。bi :向量b 的第i 个元素。
3.符号和缩写列表
D :输入向量的维数。D(i)h :第i 层隐节点数量。fθ(x),f(x) :与模型P(Y|x,θ) 相关的分类函数,定义为argmaxkP(Y=k|x,θ) 。注意通常省略下标θ 。L :标签的数量。L(θ,D) :含有参数θ 关于D 的对数似然。ℓ(θ,D) :在数据集D 上,含有参数θ 的预测函数f 的经验损失。NLL :负对数似然。θ :一个给定模型的所有参数组成的集。
4.Python命名空间
教程的代码通常使用以下的命名空间:
import theano
import theano.tensor as T
import numpy
3.监督优化入门
深度学习(Deep Learning)中最大的特点,就是大量使用深度网络的无监督学习(unsupervised learning)。但是监督学习仍然扮演着非常重要的角色。非监督预学习(pre-training)的作用在于,评估(在监督精细迭代(fine-tuning)之后)网络可以达到的性能。这节回顾了分类模型中监督学习的理论基础,并且包含了多数模型中精细迭代所需要的小批量数据的随机梯度下降算法(minibatch stochastic gradient descent algorithm)。
1.学习一个分类器
1.)0-1损失
在这个深度学习教程中出现的模型大多是用于分类器设计。训练分类器的目的在于,对于未见过的样本,最小化错误分类(0-1损失)的数量。如果
# zero_one_loss is a Theano variable representing a symbolic
# expression of the zero one loss ; to get the actual value this
# symbolic expression has to be compiled into a Theano function (see
# the Theano tutorial for more details)
zero_one_loss = T.sum(T.neq(T.argmax(p_y_given_x), y))
2.)负对数似然损失
由于0-1损失是不可微的,所以在大模型中使用它,会存在成千上万的系数,会不可避免地增加繁重的计算量。所以,对于训练集对应的标签,我们最大化分类器的对数似然。
由于我们通常要最小化一个损失函数,学习过程试图最小化负对数似然(negative log-likelihood,NLL)定义如下:
可以通过以下代码计算:
# NLL is a symbolic variable ; to get the actual value of NLL, this symbolic
# expression has to be compiled into a Theano function (see the Theano
# tutorial for more details)
NLL = -T.sum(T.log(p_y_given_x)[T.arange(y.shape[0]), y])
# note on syntax: T.arange(y.shape[0]) is a vector of integers [0,1,2,...,len(y)].
# Indexing a matrix M by the two vectors [0,1,...,K], [a,b,...,k] returns the
# elements M[0,a], M[1,b], ..., M[K,k] as a vector. Here, we use this
# syntax to retrieve the log-probability of the correct labels, y.
2.随机梯度下降法
什么是普通的梯度下降法呢?它是一个简单的算法,在含参损失函数的误差曲面上,向着误差更小的方向一步一步的调整。为了达到这个目标,我们需要将训练数据引入损失函数。该算法的伪代码如下所示:
# GRADIENT DESCENT
while True:
loss = f(params)
d_loss_wrt_params = ... # compute gradient
params -= learning_rate * d_loss_wrt_params
if <stopping condition is met>:
return params
随机梯度下降法(Stochastic gradient descent, SGD)遵循着和一般梯度下降法一样的准则。但是随机梯度下降法具有更快的速度,它一次只对一小部分样本估计梯度,而不是全部的样本集。为了更加纯粹的形式,我们每次只对一个样本进行梯度估计。
# STOCHASTIC GRADIENT DESCENT
for (x_i,y_i) in training_set:
# imagine an infinite generator
# that may repeat examples (if there is only a finite training set)
loss = f(params, x_i, y_i)
d_loss_wrt_params = ... # compute gradient
params -= learning_rate * d_loss_wrt_params
if <stopping condition is met>:
return params
用于深度学习的梯度下降法,是一种随机梯度下降法的变体,我们叫它”minibatches”。称为Minibatch SGD(MSGD),它和SGD工作原理相同,只是在一次估计中使用多个样本,而不仅仅是一个。这种方法减少了估计梯度的方差,并且能够在现代计算机中更好的组织内存。
for (x_batch,y_batch) in train_batches:
# imagine an infinite generator
# that may repeat examples
loss = f(params, x_batch, y_batch)
d_loss_wrt_params = ... # compute gradient using theano
params -= learning_rate * d_loss_wrt_params
if <stopping condition is met>:
return params
在选择minibatch大小(记为
如果你的训练的迭代次数是固定的,minibatch的大小就会变得至关重要,因为它控制着参数更新的次数。迭代次数为10,minibatch为1的迭代结果显然和迭代次数为10,但是minibatch为20的迭代结果不同。在调整minibatch大小时,需要谨记这点。
以上的代码都是伪代码格式的,真实的实用代码如下:
# Minibatch Stochastic Gradient Descent
# assume loss is a symbolic description of the loss function given
# the symbolic variables params (shared variable), x_batch, y_batch;
# compute gradient of loss with respect to params
d_loss_wrt_params = T.grad(loss, params)
# compile the MSGD step into a theano function
updates = [(params, params - learning_rate * d_loss_wrt_params)]
MSGD = theano.function([x_batch,y_batch], loss, updates=updates)
for (x_batch, y_batch) in train_batches:
# here x_batch and y_batch are elements of train_batches and
# therefore numpy arrays; function MSGD also updates the params
print('Current loss is ', MSGD(x_batch, y_batch))
if stopping_condition_is_met:
return params
3.正则化
最优化并不是机器学习的全部内容。在训练中,除了我们给定的完美样本以外,模型还会遇到它从来没有见过的样本。MSGD的训练过程不会考虑到这些,因此可能会对训练样本过拟合。一个用于抵抗过拟合的方法就是:正则化。正则化的方法有很多,在这里我们仅仅介绍
1.)ℓ1 和ℓ2 正则化
从原理上来说,在损失项之后增加正则化约束项,可以获得更加平滑的网络映射(因为通过惩罚参数中的大值,可以减少网络模型中非线性的数量)。更加直观的是,两项(
注意,我们寻找到的最“简单“的解,并不意味着这个解具有非常好的泛化能力。从经验上看,在神经网络中加入这种正则化约束可以增强泛化能力,特别是在小数据集的情况下。以下代码,阐述了如何在pyhton中计算具有
# symbolic Theano variable that represents the L1 regularization term
L1 = T.sum(abs(param))
# symbolic Theano variable that represents the squared L2 term
L2_sqr = T.sum(param ** 2)
# the loss
loss = NLL + lambda_1 * L1 + lambda_2 * L2
2.)提前退出
提前退出(Early-stopping),通过在一个验证集(validate set)上监控模型性能,来防止过拟合的发生。验证集的样本,没有用于梯度下降法(训练阶段),但也不是测试集的一部分。验证集样本被视为未来测试样本的一个典型代表。我们可以将它用于训练,因为它并不是测试集的一部分。如果在验证集上,模型的性能已经没有提高了,甚至在某些情况下,模型的性能已经随着训练有所下降时,那么训练程序会提前退出。
提前退出的条件有很多种,我们在此使用基于patience的增加数量的退出策略。
# early-stopping parameters
patience = 5000 # look as this many examples regardless
patience_increase = 2 # wait this much longer when a new best is
# found
improvement_threshold = 0.995 # a relative improvement of this much is
# considered significant
validation_frequency = min(n_train_batches, patience/2)
# go through this many
# minibatches before checking the network
# on the validation set; in this case we
# check every epoch
best_params = None
best_validation_loss = numpy.inf
test_score = 0.
start_time = time.clock()
done_looping = False
epoch = 0
while (epoch < n_epochs) and (not done_looping):
# Report "1" for first epoch, "n_epochs" for last epoch
epoch = epoch + 1
for minibatch_index in xrange(n_train_batches):
d_loss_wrt_params = ... # compute gradient
params -= learning_rate * d_loss_wrt_params # gradient descent
# iteration number. We want it to start at 0.
iter = (epoch - 1) * n_train_batches + minibatch_index
# note that if we do `iter % validation_frequency` it will be
# true for iter = 0 which we do not want. We want it true for
# iter = validation_frequency - 1.
if (iter + 1) % validation_frequency == 0:
this_validation_loss = ... # compute zero-one loss on validation set
if this_validation_loss < best_validation_loss:
# improve patience if loss improvement is good enough
if this_validation_loss < best_validation_loss * improvement_threshold:
patience = max(patience, iter * patience_increase)
best_params = copy.deepcopy(params)
best_validation_loss = this_validation_loss
if patience <= iter:
done_looping = True
break
# POSTCONDITION:
# best_params refers to the best out-of-sample parameters observed during the optimization
如果我们在达到终止条件(耗尽patience)之前,耗尽了所有的训练样本minibatch,那么我们就从最初的训练样本开始,重复训练。
注意:
validation_frequency
永远都要小于patience
。在耗尽patience之前,代码需要至少两次检查模型性能。这就是为什么我们使用公式:validation_frequency = min( value, patience/2.)
注意: 在决定是否增加patience时,不使用简单的比较,而使用统计显著性的一个测试,有可能可以增强算法的性能。