基于java的BP神经网络-初步调超参的体会

本文讲述了作者在使用Java实现BP神经网络过程中遇到的问题和解决方法,包括输入标准化、世代(epoch)调整、偏斜更新、训练样本数量、学习率、网络结构和小批量数(mini-batch)以及正则化参数(lambda)的超参数调优,以提高手写数字识别的准确率。
摘要由CSDN通过智能技术生成

  最初实现BP神经网络的时候,我只是简单地用梯度检验法检验了一下,验证自己的求导计算应该是没有问题的。

  采用的是标准minist手写数字数据集,就是60000个训练样本和10000个测试样本那个。真的上实际数据测试,一跑,正确率居然只有10%左右!而我在读的Michael A. Nielsen的教程中,他通过Python构建的那个简单的网络,只运行一个世代就有90%以上的正确率,这个结果让我一度十分沮丧。如果我写一个简单的程序,每次都猜这个数字是0,正确率大概也会是10%吧。这真的是个很悲惨的正确率,否定了我的全部劳动成果!

  那个时候我对“超参”这个概念还没有特别深入的体会,因为是自己造的轮子,我首先依然是从bug检查,首先发现的问题是,W2矩阵,也就是第一层隐藏层前的权重矩阵内的值居然都近乎为0,我想起看过但还没开始学习的概念“导数消失”。当时我的理解是,如果输入层到第一个隐藏层的权重几乎都是0,这不就意味着输入对最终结果的影响不大吗?

  0.1.标准化(normalization)
  我第一个想到的点是输入的取值范围为0~255,但事实上网络接受的值最好还是0~1之间或者-1到+1之间的吧?如果你采用的是随机(-1~+1)初始化参数,所以假如你不做标准化,这部分的运算就会纳入参数迭代的过程中,通过梯度更新的方式来迭代变化。我做出的第一个改变是对输入作了标准化,特征值载入前将其除以255,使得特征值的取值范围在0~1之间。

  然而,结果并没有太大的改变。不过这样的尝试还是在我心中构建了一个标准化的概念,同时还让我产生了这样的思考,认为之所以图像识别用计算机来处理这么方便,其实就是因为图像很容易描述成一串量纲等价的向量。目前成熟的机器学习形式最认可的输入也正是这种天然就具有标准、独立、量纲等价的向量。

  0.2.世代(epoch)

  即样本集遍历次数。显然,世代与运行时间呈线性关系。

  这个概念是吴恩达的机器学习视频课里没有提及的。他讲mini-batch的时候,并没有说世代。以至于我最开始的学习一直都是以“世代=1”的方式进行的,在Nielsen的教程中对这个概念也没说得很详细。我认为世代这个概念体现的是一个样本要利用几次。尤其是当你的训练样本比较少的时候,如果一个样本只用了一次,很可能会出现未收敛的情况。

  其实,原先世代这个概念之所以会让我感到困惑,还是因为它跟mini-batch的矛盾。mini-batch这个概念好像天然地暗示了一个样本只用一次,世代却把这种概念推翻了。现在想来,这个问题应该这么看,mini-batch的定义,是为了在有限的世代内尽可能多的迭代。更多关于mini-batch的讨论留到下面说。

  0.3.偏斜更新(bias update)

  在吴恩达的教学视频中,偏斜永远是1不需要更新,Nielsen的笔记则不然,仅只是在正则化的时候忽略偏斜。

  从数学角度上讲,即使偏斜始终为1,仅仅通过权重矩阵W自身的变化,也可以实现相同的学习效果,这个问题可能跟标准化一样,将偏斜的更新任务移交给了W,个人倾向于更新偏斜,这样可以最大化利用BP算法计算出的残差。实际测试表明,在充分训练(收敛)的情况下,是否更新偏斜对结果影响不大。

  1.训练样本数量m

  最没有悬念的放在第一个说,已经有成熟的理论表明,基于大量真实样本的前提下,就连最简单的感知机都可以做得很好。就本数据集而言,事实上4000个训练数据就可以得到差不多90%的正确率,如果把训练数据提升10倍,正确率大概能有2%的提升,不算特别明显。当然,这全部都是基于MINIST训练集和测试集而言的。

  从感性上来说,训练样本应该有一种多样性的定义。所以衡量样本,更多的应该从“多样性”的层面去分析。如果再说得功利和具体一点,应该是针对测试样本的多样性。简言之,如果我预先知道或者可以大概估计到需求的“多样性”,那么理论上我只需要能满足这些“多样性”最具代表性的样本进行训练即可,事实上这也是最理想的学习方式。也就是说,最好的样本,应该是最接近实际需求“多样性”的样本。而绝不是一味地增加数量。

  说个题外话,前几天我看到因人工智能催生的新产业——人工标注。就是在第三世界国家雇佣一些廉价劳动力给图片做标注,以此扩大训练集数量。足以见得带标签的样本是多么宝贵的资源。本人对这种大规模流水线式的人工智能持一种悲观态度,这种方式跟马拉火车一样荒唐。

  样本数量的增加跟学习时间基本呈一个线性增加的关系。另外还有一点值得注意的是,如果用了较小的训练集和测试集,一定记得随机抽取,不要每次都只取前一部分,这样很没有代表性。

  2.学习率n 

  这个超参大概也是比较容易确定的,这可能是少数几个类似mini-batch或世代这样可以通过牺牲运行速度来达到最佳效果的超参,它影响的是迭代的速度。如果设得及其小,就可以保证每次迭代都一定会相应地降低损失,但这样会增加收敛需要的迭代次数。如果设置得大了,就可能无法收敛。

  学习率可以很容易从每次迭代后的正确率看出来,一般最初的几次迭代对正确率的提升都很大,随后提升越来越慢。如果波动巨大,就证明学习率太大了。学习率还有一点值得声明的事情是,它是一个迭代速度的系数,原则上来说,它只跟模型参数的取值范围有关,当函数逼近最优解时,由于梯度的下降,收敛速度会自动下降,所以并不需要特意将它设成一个动态收缩的值。

  学习率不会直接影响最终的正确率,基本不受其他超参的影响,它也不会影响程序的执行速度。它直接影响的是收敛次数,在定义学习率前,首先应该考虑的是你的运算能力,允许迭代多少次?(由mini-batch,世代数,样本数共同决定)学习率的设置应该满足在迭代次数内使函数收敛,即正确率不再进一步增大,而是在某个小范围内波动。

  我认为学习率的调整应该基于这样的原则:首先选择较大的学习率,如1.0,根据学习曲线的波动情况进行小范围向下微调,如果学习曲线能基本保持线形,则可确定这就是要找的学习率了。

  总之,学习率不要取得太小,因为时间很宝贵。

  3.网络结构(net structure)

  对网络结构的架构,比较直观的考虑是,以问题的复杂度为出发点,或者前文中谈到的样本的“多样性”。这里需要定义的是,网络有多少层,每层有多少个节点?(按照吴恩达的视频课程,隐藏层的节点数原则上一般相等)

  这个问题可以套用一个已有的结论——只要有充足的样本,感知机也可以做得很好。实际的测试表明,网络的复杂性应该是同测试样本的复杂性相关的,当网络的复杂度尚未满足问题复杂度时,增加网络复杂度(最简单的方式就是增加节点数)可以提升正确率。

  设定网络结构的策略可以是,首先从一个较小的节点数开始,比如8。逐步增加节点个数(可以是16、32、64),直到正确率不再明显上升为止。在本问题中,增加层数对正确率的提升并不明显,学习时间却相应地要增加一倍。我最终的网络结构选择是{784,64,10}。

  网络结构与算法运行的时间基本成线性关系,所以应该尽量选择简单的网络。

  4.小批量数(mini_batch)

  即每次学习实际参与的样本数量,这个参数的定义是为了调和随机梯度下降法与完全梯度下降法之间的优缺点。如果这个值取得很小,比如2,那就意味着一个世代中,每2个样本就可以让参数更新一次,这样做会明显增加迭代次数,但显然也会让参数变得不稳定。

  原则上来说,这个值会随着增高逐渐达到饱和(再提升也基本不会增加正确率了),有一点需要注意的是,样本数一定的情况下,增大批量数,会线性减少迭代次数这个隐性参数。

  另外一点值得注意的是,一般样本都不可能完整整除批量数,比如样本数1000,批量数32,1000%32=8,出于公平原则,最后8个样本个人认为应该抛弃。另外,每个世代开始时都应该对样本重新洗牌然后按顺序提取训练数据。

  5.正则化参数(lambda)

  十分微妙的一个参数,它的数学意义是在每次更新权重前,先对其进行等比例衰减,L2正则化的衰减率为:                           (1 - n*lambda/mini_batch)它的性质跟学习率有点像,如果取0代表完全不衰减,取一个较小的非0值则会为正确率带来意想不到的提升,只需要一点点就行,一定不能取得太大,太大的正则化参数就跟太大的学习率一样,会让学习效果变得十分不稳定,而且无法收敛。

  这个超参需要注意的点是,如果调整了学习率或批量数,衰减率也会相应发生变化,这个时候需要相应地调整衰减率。

  正则化参数并不会影响程序的执行效率。

 

 

 
 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值