美文网首页
神经网络的这几个坑,你都躲过了吗

神经网络的这几个坑,你都躲过了吗

作者: AlchemistMartin | 来源:发表于2018-07-11 22:28 被阅读0次

因为AI这两年的火爆,大家拿着锤子到处找钉子,锤子当然也砸到了我头上,有很多做业务的同事尝试通过AI的方法解决需要一些很复杂的业务逻辑算法,同时需要很多参数组合才能搞定的问题。但因为都是非科班出身也没有系统学习,所以遇到不少问题,所以在这里一一列出来,并且持续更新,希望能够总结出一些经验,在后续的应用中能够跳过这些坑,把更多精力集中在数据和业务问题上。

关于神经网络为什么不工作,有一篇非常实用的指南,在训练过程中遇到问题可以首先参考这个指南。传送门


问题1、没有激活函数,你是认真的吗

真的会发生这样的问题,尤其是直接用tensorflow写模型的时候。同事前几天写了段代码,搭了一个单隐层的模型来近似一个产品中的算法,但是怎么训练都不收敛,按说那个算法是复杂,但也没有必要整一个十好几层的模型来模拟,尝试了种种手段也还是没用,最后仔细一看模型代码,所有层都没有激活函数,相当于费很大劲写了个线性回归还要拟合出产品算法(/摊手)。所以对于大多数的应用,不涉及复杂的网络结构或初始化、loss函数的,就用keras吧,毕竟人生苦短。

当然,无激活函数,也就是单位激活函数,在一些场景下也会使用,通常这种使用能够带来减少训练参数的好处。所以除非是有意的设计来简化网络,否则激活函数可不能忘掉。

问题2、还是激活函数,选对了没有

很多指南上也都说过,一般情况下,分类器隐层的激活函数用relu,输出层用sigmoid或softmax就八九不离十了,但是这里也有坑。许多的例子中使用的数据集是离我们面临的问题比较远的,最近被问到一个回归任务的问题,由于是原型尝试没有做数据规范化,训练的时候发现模型不收敛,或是不管多大的batch都在一个epoch中间波动非常大,打印出来才发现输出值区间和目标值区间都差老远,很明显是激活函数限制了输出范围。所以选激活函数,尤其是回归任务或者autoencoder,在写最后一层的时候停一下,回忆回忆每个激活函数的范围,如果都不合适,那就做一下数据预处理。

激活函数还有一个问题就是sigmoid函数用在隐层的时候,要注意梯度消失的问题,sigmoid在x=±5的附近就基本没有梯度了,如果输入值很大或权重初始化函数比较另类,那妥妥的训练效率会比较差。

问题3、损失函数和输出层激活函数,请尊重原配

对于sigmoid输出单元,如果使用非交叉熵损失函数,损失函数就会在sigmoid饱和时饱和,从而导致梯度消失。而最大似然给出的交叉熵损失函数中的log能够抵消sigmoid中的exp,因此sigmoid要搭配交叉熵给出的损失函数

对数似然之外的许多目标函数对softmax函数不起作用,具体来说,那些不使用对数来抵消softmax中的指数的目标函数,当指数的变量取非常小的负值时会造成梯度消失,从而无法学习。对于softmax函数,当输入值之间的差异变得极端时,输出值会饱和。当softmax饱和时,基于softmax的许多代价函数也会饱和,除非它们能够转化饱和的激活函数,也就是说交叉熵类的损失函数能和输出层的softmax搭配

线性单元不会饱和,所以和relu系列的激活函数,不挑损失函数。另外对于relu,在初始化参数时,可以将b的所有元素设置成一个小小的正值,这使得relu在初始时就对训练集中的大多数输入呈现激活状态,并且允许导数通过。

问题4、学习率,初始化模型时不要改默认参数

学习率基本上是新手最想调整的一个高参,容易理解,改起来也最方便。但是在初始化网络的时候,还是不要动人家的默认值,除非你对这个问题的损失函数空间和形态非常了解(那就不用黑盒算法了),在其他方面不出问题的情况下,学习率默认值基本上能让网络有一个基本靠谱的收敛,在对问题的收敛水平、精度有一定认识之后再去修改默认值也不迟。默认值一般在1e-3到1e-4的范围内,对于MNIST数据集,不同的学习率对应的loss变化趋势如下,从图中也可以看到,使用默认的学习率能得到一个基本够看的结果:

不同学习率下的loss

问题5、网络规模应该如何选择,参考如何将大象装冰箱

网络规模的选择是个头疼的问题,没有确定的指导思想或策略,花书中也有具体的篇幅讲这个:

“万能近似定理表明,一个前馈神经网络如果具有线性输出层和至少一层具有任何一种“挤压”性质的激活函数的隐藏层,只要给予网络足够数量的隐藏单元,它可以以任意的精度来近似任何从一个有限空间到另一个有限空间的Borel可测函数。前馈网络的导数也可以任意好地来近似函数的导数”

“具有单层的前馈网络足以表示任何函数,但是网络层可能大得不可实现,并且可能无法正确地学习和泛化”

什么意思呢?理论上单层网络干啥都够,但是随着要拟合的目标函数越来越复杂,需要的神经元和参数是指数级增加的。所以我们要视问题的复杂度来确定网络的层数,问题复杂度是个什么呢,其实就是“要把大象装冰箱,总共分几步”。我们可以结合对于问题的理解,感受下要拟合的这个目标函数都干了些什么事,分了几步,比如大象装冰箱这个问题它就分了三步,三层左右应该就差不多。更深层的网络通常能够对每一层使用更少的单元数和更少的参数,并且经常容易泛化到测试集,但是通常也更难以优化。

问题6、南辕北辙,训练测试集划分要小心

还是同事在拟合产品算法时遇到的问题,拿到的数据是一个持续数小时的记录,系统每隔xxx ms会报一条消息,所以有几万条的样本。他在训练的时候没有做数据的shuffle,仅仅按照时间维度划分了训练集和测试集,结果发现很早就出现了“过拟合”——测试集误差很快就上去了,而且还没收敛到理想的水平就上去了。在这里的问题就在于划分训练集测试集的方式,其实从问题背景出发,这个数小时的记录在时间变化的过程中所反映的环境也肯定是在变化的,所以训练集和测试集所代表的环境不一定会一样。一定要有一个mindset,要保证train-dev-test三个集合都是同分布的,脱离了这个前提,训练就是做样子。所以要shuffle数据,要保证三个集合同分布,或者最好是划分完之后train和dev用k-fold的方法来训练。

不对数据进行shuffle还有一个问题就是,我们拿到的数据很多是顺序数据,在划分batch之后大多数batch对应的目标值很可能都是单一的,这样在训练过程中会出现很大的波动,甚至会导致完全无法收敛

问题7、数据特征的维度够不够

网络是通过输入的特征来学习的,所以要解决一个问题,业务经验在这里十分重要,即使是E2E不考虑特征抽取的神经网络,也需要确保所需要的信息全都输入进来了。在考虑数据特征的时候,可以分为两个阶段:第一个阶段需要业务专家的深度参与,从传统业务的角度看,做这个事情需要哪些输入,能从什么渠道获取这些输入等,这些必须要进行一个完备的分析;第二个阶段就是对这些输入进行必要的处理,以便后续设计网络结构,当然输入形式、网络结构、网络性能这是一个反馈的闭环,需要不断进行更新以达到最好的目标性能。

问题8、性能标准是什么,keras自带的acc合适不

使用keras训练时,一般我们会在compile里定义相关的metrics,有时候犯个懒就直接用系统给的acc了。但是在用了几回吃了几回土之后发现,keras自带的acc,实际上对分类任务更友好一些,对于多维输出的回归问题(我所在的行业涉及的大部分都是这类问题),acc就很容易把人整蒙圈了。有时候训练半天,看着acc一直在0.2左右晃荡,就开始怀疑人生了,但是看着mse loss的值感觉也不应该这么离谱,结果自己结合业务定义一个性能函数,发现已经达到九十好几的精度了。所以对于回归问题,尤其是多维的、稀疏的场景,一开始就要想好性能指标怎么定义,不要犯懒,自己手写一个,这样往后优化模型,目标感也会更强。

问题9、最好把Model的生成封装在一个函数中

还有一个实用的经验是对于模型初始化后调参和ensemble模型优化的场景,把模型的定义、构建等放在一个函数中,每次使用不同参数、不同数据集时调用函数生成模型再进行训练,这样可以避免使用上次训练后遗留的网络权重和偏置等信息,避免因为历史遗留参数而导致收敛问题或无用的性能提升。具体的原因和案例可以参考《为什么Recompile之后你的网络不收敛了》

相关文章

网友评论

      本文标题:神经网络的这几个坑,你都躲过了吗

      本文链接:https://www.haomeiwen.com/subject/uxbkpftx.html