Tensorflow建立深度神经网络时出现nan及交叉熵损失函数的探讨

深度学习中NaN问题解析

(作者:陈玓玏)
在使用tensorflow建立深度神经网络的过程中,在几次迭代之后发现所有的权重都变为了nan,导致整个网络都无法正常工作。出现这个问题我知道的可能有以下两个原因。

1) 样本未进行归一化

因为每次迭代都要计算σ(x∗ω+b)\sigma(x*\omega+b)σ(xω+b),而深度神经网络的参数众多,如果样本不进行归一化,很容易出现x∗ω+bx*\omega+bxω+b溢出的情况。

解决方案:
我一般都是先用sklearn中的preprocessing.MinMaxScaler()函数将每一个特征在[最小值,最大值]区间进行尺度缩放,缩放到[0,1]区间。这样处理过后再出现nan就不会特征的问题了。

2)损失函数未进行溢出处理

2.1) 为什么使用交叉熵损失函数?

当输出层使用sigmoid激活函数时,如果在神经网络中使用平方损失函数,即使用
(y−σ)2/2(y-\sigma)^2/2(yσ)2/2
作为损失函数,那么参数更新的公式为(仅考虑一层时,也就是不需要使用到反向传播):
ω=ω−α(y−σ)σ(1−σ)x\omega = \omega-\alpha(y-\sigma)\sigma(1-\sigma)xω=ωα(yσ)σ(1σ)x
其中σ(1−σ)\sigma(1-\sigma)σ(1σ)为sigmoid激活函数导数,使用这个损失函数就因为这一项导数存在一个问题:当我们犯的错误越大时,神经网络更新越慢。考虑二分类问题,y不是0就是1,预测值与0或1差别大,即接近0或1,但与真实值为相反的方向,此时z(σ\sigmaσ的自变量)是离x=0轴非常远的,从sigmoid函数的图像上看也知道,此时导数几乎为0,因为参数更新就会非常慢。

因此我们得出一个结论,sigmoid激活函数不适合与平方损失函数一起用,因为我们希望我们犯错严重时,更新更快。而交叉熵损失函数可以帮助我们达到这个目的。交叉熵损失函数公式如下:
−1/2m∗∑i=1m(yln⁡σ+(1−y)ln⁡(1−σ))-1/2m*\sum_{i=1}^m(y\ln \sigma+(1-y)\ln(1-\sigma))1/2mi=1m(ylnσ+(1y)ln(1σ))
导数自己求就可以,总之就是不再有sigmoid的导数那一项,仅仅是y−σy-\sigmayσ乘以系数,那么预测值和真实差距大则参数更新快,差距小则参数更新慢。

这里有一个有意思的问题,就是交叉熵公式中预测值在对数内,真实值在对数外,能不能互换位置呢?这个问题我稍微想了以下,如果交换过来,那么损失函数的导数波动范围将会非常广,因为导数会是−1/2m∗∑i=1m(σ(1−σ)ln⁡y−σ(1−σ)ln⁡(1−y))x-1/2m*\sum_{i=1}^m(\sigma(1-\sigma)\ln y-\sigma(1-\sigma)\ln(1-y))x1/2mi=1m(σ(1σ)lnyσ(1σ)ln(1y))x
化简一下:
−1/2m∗∑i=1mσ(1−σ)ln⁡y/(1−y)x-1/2m*\sum_{i=1}^m\sigma(1-\sigma)\ln y/(1-y)x1/2mi=1mσ(1σ)lny/(1y)x
这样不仅之前平方损失函数的问题依然存在,还可能有一个新的问题:y/(1−y)y/(1-y)y/(1y)可能会出现分子或分母为0的情况,即使不出现,放到ln⁡\lnln中也可能会出现正负无穷的情况,导数太不稳定,因此不适合交换预测值和真实值的位置。

2.2) 使用交叉熵损失函数要注意的问题

交叉熵损失函数中存在对数,如果ln⁡\lnln作用在0上,则是负无穷,就会溢出,因此需要考虑到这种可能,在代码中加入一个很小的常量,比如np.pow(10,-9),也就是10−910^-9109,避免溢出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值