python交叉熵函数 numpy 交叉熵( 二 )


在实验1中 , 随机初始化参数,使得第一次输出值为0.82(该样本对应的实际值为0);经过300次迭代训练后 , 输出值由0.82降到0.09,逼近实际值 。而在实验2中 , 第一次输出值为0.98,同样经过300迭代训练 , 输出值只降到了0.20 。
从两次实验的代价曲线中可以看出: 实验1的代价随着训练次数增加而快速降低,但实验2的代价在一开始下降得非常缓慢;直观上看 , 初始的误差越大,收敛得越缓慢 。
其实,误差大导致训练缓慢的原因在于使用了二次代价函数 。二次代价函数的公式如下:
其中 , 表示代价,表示样本 , 表示实际值 , 表示输出值,表示样本的总数 。为简单起见 , 同样一个样本为例进行说明,此时二次代价函数为:
目前训练ANN最有效的算法是反向传播算法。简而言之,训练ANN就是通过反向传播代价,以减少代价为导向,调整参数 。参数主要有:神经元之间的连接权重,以及每个神经元本身的偏置。调参的方式是采用梯度下降算法(Gradient descent) , 沿着梯度方向调整参数大小 。和的梯度推导如下:
其中,表示神经元的输入,表示激活函数 。从以上公式可以看出,和的梯度跟激活函数的梯度成正比 , 激活函数的梯度越大 , 和的大小调整得越快,训练收敛得就越快 。而神经网络常用的激活函数为sigmoid函数,该函数的曲线如下所示:
如图所示,实验2的初始输出值(0.98)对应的梯度明显小于实验1的输出值(0.82),因此实验2的参数梯度下降得比实验1慢 。这就是初始的代价(误差)越大,导致训练越慢的原因 。与我们的期望不符,即:不能像人一样,错误越大,改正的幅度越大,从而学习得越快 。
可能有人会说,那就选择一个梯度不变化或变化不明显的激活函数不就解决问题了吗?那样虽然简单粗暴地解决了这个问题,但可能会引起其他更多更麻烦的问题 。而且,类似sigmoid这样的函数(比如tanh函数)有很多优点,非常适合用来做激活函数,具体请自行google之 。
换个思路,我们不换激活函数,而是换掉二次代价函数,改用交叉熵代价函数:
其中,表示样本,表示样本的总数 。那么,重新计算参数的梯度:
因此,的梯度公式中原来的被消掉了;另外,该梯度公式中的表示输出值与实际值之间的误差 。所以,当误差越大,梯度就越大,参数调整得越快,训练速度也就越快 。实际情况证明,交叉熵代价函数带来的训练效果往往比二次代价函数要好 。
在实际分类任务中,要先将输出层的输出值经过Softmax函数,再经过log函数 , 最后才用交叉熵损失函数计算损失 。
pytorch中有计算交叉熵损失的接口 , 即 F.cross_entropy() ,不过该接口包含了Softmax函数、log函数、交叉熵损失函数 。也就是说 F.cross_entropy() = F.softmax() + torch.log() + F.nnl_loss()。即使如此 , 也要使用 F.cross_entropy(),不仅是因为它简单 , 更因为它能保证数值稳定 。
机器学习的过程就是希望在训练数据熵模型学到的分布和 真实的分布越近越好 , 我们知道KL散度可以表示两个分布之间的不同 。
但我们没有真实数据的分布,那么只能退而求其次 , 希望模型学到的分布和训练数据的分布,也就是把训练数据当做模型和真实数据之间的代理人。假设训练数据是从总体中独立同步分布采样(Independent and identically distributed sampled)而来,那么我们可以利用最小化训练数据的经验误差来降低模型的泛化误差 。简单说:

推荐阅读