/***********************************************************************************************************
2017年7月10日 更新
在cifair-10上使用selu作为激活函数,非常容易使得loss发散,变成nan,不管怎么调整学习率都没有用。我的网络为
conv1-selu-conv2-selu-maxpool-conv3-selu-conv4-selu-maxpool-fc1-selu-fc2-selu
不知道是因为网络太深还是什么原因,selu并没有relu的功能(缓解梯度爆炸或者是消失的情况)
结论:selu作为新提出的激活函数,并没有看到很多项目上有应用,可能还是存在一些问题,所以还是使用bn+relu比较的保险
**********************************************************************************************************/
本文对selu和relu对于加快网络收敛的效果进行了对比。同时也对selu和BatchNormalization进行了对比
arXiv 上公开的一篇 NIPS 投稿论文《Self-Normalizing Neural Networks》提出一种新的函数–缩放指数型线性单元(SELU),能够保证梯度在反向传递的过程中不会出现梯度消失或者是梯度爆炸的问题,同时保证激活输出能够是标准的高斯分布
原理的话不太懂,但是在MNIST上测试了一下,发现效果确实比relu好很多,收敛速度快很多。
我的网络:
relu作为激活函数的网络结构:
input->conv1-relu-maxpool-conv2-relu-maxpool-fcnet1-relu-fcnet2-relu-fcnet3->output
selu作为激活函数的网络结构:
input->conv1-selu-maxpool-conv2-selu-maxpool-fcnet1-selu-fcnet2-selu-fcnet3->output
两者的accuracy的比较:
可以看出selu作为激活函数,收敛速度比relu快很多!
loss的比较:
激活值(输出)比较:
下面是以conv2层的relu/selu的激活输出值对比
但是在全连接层(fc-net)的激活输出就没有那么漂亮,而且感觉也不是很像正态分布,不知道是不是我的实现方法有问题(没有dropout?)。
selu函数的实现很简单:
def selu(x):
alpha = 1.6732632423543772848170429916717
scale = 1.0507009873554804934193349852946
return scale*tf.where(x>0.0, x, alpha*tf.exp(x)-alpha)
BatchNormalization 与selu的对比
既然selu能够使得激活的输出满足标准的高斯分布,那么和BatchNormalization对比的效果是怎么样呢?下面还是在mnist数据集上对两者进行对比
完成200个epoch训练耗时对比
结论:
可以看到就整体的效果来说还是bn得效果最好,但是也要看到bn在每次训练会消耗更多的时间(差不多要3分钟),总的来说bn效果最好,但是耗时(完成每个epoch的时间)。selu效果没有bn好,但是耗时少,不管是selu还是bn都比relu的表现好
在bn的激活函数换成selu表现怎么样
既然bn和selu都能够加快收敛,那就试一试将bn的激活函数换成relu会发生什么呢?
可以看到就bn+selu的效果还不如bn+relu的,而且耗时也要比bn长
看来bn+selu不是好主意
文章结论:
可以看到selu能够加快收敛过程,虽然selu在每个epoch的加速效果不如bn,但是selu完成一个epoch的时间要比bn短,而且实施很方便。
每个epoch的耗时:
relu <selu <bn<bn+selu
对网络收敛的加速效果:
relu <selu<bn
所以可以看到,bn对收敛的加速最明显(用比较少的epoch就能达到比较高的accuracy),但是比较耗时(每个epoch用的时间多),selu的话对收敛的加速效果虽然没有bn好,但是selu的实施很方便,同时每个epoch的耗时也比bn少