/* Author: cyh_24 */
/* Date: 2014.10.2 */
/* Email: [email protected] */
/* More: http://blog.csdn.net/cyh_24 */
最近,研究的关注点在以图搜图 这块,近期这块内容的比赛较多,为了不拖师兄的后腿太多,决定潜心研究DeepLearning,主要以Theano官方教程Deep Learning Tutorial为参考。
这个系列的博客应该会持续更新,希望大家多多指点,共同学习!
转载请注明:http://blog.csdn.net/cyh_24/article/details/41827691
1. 下载代码与数据
Theano官方提供了学习用的全部代码和数据集。
你可以从github上clone下来:
git clone git://github.com/lisa-lab/DeepLearningTutorials.git
2. 数据集的使用
2.1 MNIST DataSet
mnist 数据集由手写数字图像组成,分成60,000个训练样本和10,000测试样本。
在许多论文,与这个教程一样,官方的60,000训练样本分成50,000个训练样本和10,000个验证样本
验证样本:用于选择像学习率和模型大小的超参数)
所有手写数字图像大小都归一化在固定的28*28大小的灰度图像中。
这里有一些MNIST手写数字的例子:
2.2 使用数据集
你可以通过下面的代码进行导入数据:
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()
值得注意的是,当我们使用这些数据集的时候,我们通常是把它们分成多个小块(称为minibatcheds)。并且,官方建议我们在读入数据之后,应该把数据存储到共享变量(shared variables)中。使用共享变量的理由与使用GPU有关。当拷贝数据到GPU内存时,从CPU拷贝数据到GPU是个大的效率瓶颈。当代码要做拷贝数据,如果没有使用共享变量,因为这个瓶颈,GPU代码不会比CPU代码快(甚至可能更慢)。
使用共享变量的方法如下:
def shared_dataset(data_xy): 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)) 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 * 500: 3 * 500] label = train_set_y[2 * 500: 3 * 500]
注意:
- 如果你在GPU上运行,使用的数据集超出内存的大小,代码会崩溃。这种情况下,需要把数据保存到共享变量。
- 在训练数据的时候,你可以保存为足够小的数据块到共享变量。一旦你通过数据块获取数据,需要保存更新的值。
- 这个方法能够使CPU和GPU内存之间的数据传输效率最大化。
3. 约定的名称
3.1 数据集名词
分别代表训练集、验证集、测试集。
对于每个数据集,由数据x跟标签y组成:
3.2 数学定义
3.3 符号跟缩略语
4. DeepLearning的监督学习入门
Deep Learning 最令人兴奋的一点事它使用大量的非监督式来学习神经网络。但是,监督式学习仍然在其中扮演了重要的角色。一般来说,无监督学习到的东西,在监督学习的微调之后能够达到更好的效果。这节的监督学习入门主要介绍一下几个方面:
- 简单介绍监督学习在分类模型中的作用
- 介绍 批量随机梯度下降算法
4.1 学习一个分类器
4.1.1 Zero-One Loss
训练分类器的目的是将分类的错误(Zero-One Loss)尽可能减小。
如果预测函数 是:
那么它的错误代价 就是:
在这里,D是训练集,I 的意义如下:
当x为True的时候,它为1;否则为0
在本教程中,f(预测函数) 定义如下:
我们可以用Theano来写出上面的代码:
zero_one_loss = T.sum(T.neq(T.argmax(p_y_given_x), y))
4.1.2 Log-Likelihood Loss(负对数似然代价函数)
因为Zero-One Loss是非可微分的,为了在大模型(成千上万的参数)中优化它,计算量相当惊人。因此,我们用Log-Likelihood Loss来代替:
同样,也是通过最小化这个似然函数来得到优化,最小化的过程我们成为NLL(negative log-likelihood),如下:
同样,用Theano写出的代码仍然很简单:
NLL = -T.sum(T.log(p_y_given_x)[T.arange(y.shape[0]), y])
4.2 Stochastic Gradient Descent (随机梯度下降)
普通的梯度下降法
梯度下降法,就是利用负梯度方向来决定每次迭代的新的搜索方向,使得每次迭代能使待优化的目标函数逐步减小。
普通梯度下降法的伪代码:
# 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
随机梯度下降法
随机梯度下降是每次通过一个样本(而不是整个训练集)来迭代更新一次,如果样本量很大的情况(例如几十万),那么可能只用其中几万条或者几千条的样本,就已经将theta迭代到最优解了。
但是,SGD伴随的一个问题是噪音较多,使得SGD并不是每次迭代都向着整体最优化方向。
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 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
批量随机下降法
Theano深度学习教材中推荐的是随机梯度下降的一个优化的变种,叫做批量随机下降(Minibatch SGD)。批量随机下降的基本工作原理跟随机下降一样,只是,每次使用了更多的训练样本来估计梯度。 这有效的减少了估计梯度的方差,同时更好地利用了计算机内存。
MSGD伪代码如下:
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
注意:
在这里,批的大小B是一个权衡的选择,需要考虑模型、数据集、硬件等;文本选择的是20,当然你可以任意选取(不会对结果正确性些有影响)。
但是,如果你的训练次数是固定的,那么批的大小将会对结果产生影响。
下面,我们用Theano写出一个完整的MSGD:
# 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
4.3 规则化
批量随机梯度下降是一个线性更新的算法,可以使用新的样本来训练已有的模型。这方便,但是会出现一种过拟合的可能。为了防止过拟合的出现,我们可以使用两个方法:
- 规则化的方法;
- 训练到一定时间提早结束;
4.3.1 规则化
L1和L2的规则化方法是通过向代价函数中增加一个新的惩罚项,用来惩罚一些参数配置(规则化也叫做权值衰减)
一般情况下,如果我们的代价函数如下:
那么,经过规则化后的代价函数就是:
我们的例子中,规则化后的代价函数式:
原则上,当添加一个规则化到代价函数中,会使得神经网络的映射更加平滑(通过惩罚值大的参数,这将减少网络模型的非线性)。
看看python中的实现:
# 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
4.3.2 提早结束
验证集: 没有被用于梯度下降训练,也不是测试集的一部分。可以认为它是测试的代表。
我们可以通过验证集来验证模型的性能,来判断是否出现过拟合的情况。如果模型的性能在验证集中没有明显的提升,或者甚至下降了,那么我们应该停止继续迭代。
5 介绍语
python 这么优雅,Theano 这么‘库’,deep-learning 这么潮,不多说了,我也提早结束吧......