【Python实现卷积神经网络】:神经网络的Loss函数:Softmax+Cross Entropy前向传播原理+python实现代码
1.交叉熵的作用
通过神经网络解决多分类问题时,最常用的一种方式就是在最后一层设置n个输出节点,无论在浅层神经网络还是在CNN中都是如此,比如,在AlexNet中最后的输出层有1000个节点:

一般情况下,最后一个输出层的节点个数与分类任务的目标数相等。假设最后的节点数为N,那么对于每一个样例,神经网络可以得到一个N维的数组作为输出结果,数组中每一个维度会对应一个类别。在最理想的情况下,如果一个样本属于k,那么这个类别所对应的的输出节点的输出值应该为1,而其他节点的输出都为0,即[0,0,1,0,….0,0],这个数组也就是样本的Label,是神经网络最期望的输出结果,交叉熵就是用来判定实际的输出与期望的输出的接近程度!
2. 为什么使用交叉熵?
一文搞懂交叉熵在机器学习中的使用,透彻理解交叉熵背后的直觉这篇文字章介绍了什么是交叉熵?交叉熵在机器学习中的应用的例子。
看完交叉熵的概念之后,相信你对交叉熵有一个大致的了解。至于为什么使用交叉熵代价函数,而不使用方差代价函数作为深度学习反向传播的代价函数?下面首先简单介绍一下深度学习反向传播算法。
**反向传播算法(Back propagation)**是目前用来训练人工神经网络(Artificial Neural Network,ANN)的最常用且最有效的算法。其主要思想是:
(1)将训练集数据输入到ANN的输入层,经过隐藏层,最后达到输出层并输出结果,这是ANN的前向传播过程;
(2)由于ANN的输出结果与实际结果有误差,则计算估计值与实际值之间的误差,并将该误差从输出层向隐藏层反向传播,直至传播到输入层;
(3)在反向传播的过程中,根据误差调整各种参数的值;不断迭代上述过程,直至收敛。
更多反向传播算法介绍可以访问:反向传播算法(过程及公式推导)。看完反向传播算法之后,你大概可以知道代价函数的用处了。那么代价函数在这里有两种计算方式:
1.方差代价函数
2.交叉熵代价函数
那么为什么大家都使用交叉熵代价函数,而不使用方差代价函数呢?
答:它可以克服方差代价函数更新权重过慢的问题
我们可以从下图看到方差代价函数的梯度含有sigmoid函数的导数。
而从下图是交叉熵代价函数的梯度,可以看到没有导数项,所以在计算的时候速度很快。
具体的原因,可以看:
交叉熵代价函数(cross-entropy cost function)
交叉熵代价函数(作用及公式推导)
这两篇文章都是介绍二次代价函数的不足,以及为什么使用交叉熵代价函数。
3.Softmax回归处理
神经网络的原始输出不是一个概率值,实质上只是输入的数值做了复杂的加权和与非线性处理之后的一个值而已,那么如何将这个输出变为概率分布?
这就是Softmax层的作用,假设神经网络的原始输出为y1,y2,….,yn,那么经过Softmax回归处理之后的输出为:
很显然的是:
Softmax回归处理就是:使单个节点的输出变成的一个概率值,经过Softmax处理后结果作为神经网络最后的输出。
4.交叉熵的原理
交叉熵刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵,则:
这个公式如何表征距离呢,举个例子:
假设N=3,期望输出为p=(1,0,0),实际输出q1=(0.5,0.2,0.3),q2=(0.8,0.1,0.1),那么:
很显然,q2与p更为接近,它的交叉熵也更小。
除此之外,交叉熵还有另一种表达形式,还是使用上面的假设条件:
其结果为:
以上的所有说明针对的都是单个样例的情况,而在实际的使用训练过程中,数据往往是组合成为一个batch来使用,所以对用的神经网络的输出应该是一个m*n的二维矩阵,其中m为batch的个数,n为分类数目,而对应的Label也是一个二维矩阵,还是拿上面的数据,组合成一个batch=2的矩阵:
所以交叉熵的结果应该是一个列向量(根据第一种方法):
而对于一个batch,最后取平均为0.2。1
5.代码
代码见我的github仓库:csdn/ Softmax-Cross Entropy
5.1.softmax
# import numpy as np
a = np.array([[-0.69314718 ,-1.60943791, -1.2039728],[-0.22314355, -2.30258509, -2.30258509]])
print ("数组a的值为:\n" + str(a))
print ("找出a中每行最大值:")
print (np.max(a, axis=1).reshape(-1,1))
b = a - np.max(a, axis=1).reshape(-1, 1)
print ("a中每行均减去本行最大值后的数组b:")
print (b)
a_softmax = np.exp(a) / np.sum(np.exp(a), axis=1).reshape(-1, 1)
print ("对数组a进行softmax:")
print (a_softmax)
b_softmax = np.exp(b) / np.sum(np.exp(b), axis=1).reshape(-1, 1)
print ("对去掉最大值的进行softmax:")
print (b_softmax)
5.2.cross entropy
print ("我们上次得到的softmax为:")
print (b_softmax)
d = np.log10(b_softmax) # log下什么都不写默认是自然对数e为底 ,np.log10()是以10为底
print ("对softmax取ln:")
print (d)
print ("找出softmax中每行我们标签概率最大的两个数,也就是第一行的第0个,第二行的第0个:")
print (b_softmax[range(2), list([0,0])])
c = np.log10(b_softmax[range(2), list([0,0])])
print ("分别对这两个数进行ln:")
print (c)
print ("最后,因为这两行是一个batch的两个,所以,加和去平均,得到的就是Loss:")
print (-np.sum(np.log10(b_softmax[range(2), list([0,0])]))*(1/2))
问题:为什么需要 b_softmax ?
a_softmax 和 b_softmax 的区别
1. 定义上的区别
-
a_softmax:- 直接对数组
a进行 Softmax 操作。 - 公式为:
[
a_{softmax} = \frac{\exp(a)}{\sum_{j} \exp(a_j)}
] - 计算时,直接使用数组
a的原始值。
- 直接对数组
-
b_softmax:- 对数组
b进行 Softmax 操作,而b是通过将数组a的每一行减去该行的最大值后得到的。 - 公式为:
[
b = a - \max(a, \text{axis}=1)
]
[
b_{softmax} = \frac{\exp(b)}{\sum_{j} \exp(b_j)}
]
- 对数组
2. 数值稳定性上的区别
-
a_softmax:- 如果数组
a中的值较大(正数较大)或较小(负数较大),在计算exp(a)时可能会导致数值溢出或下溢。 - 例如,当
a中的值很大时,exp(a)可能会超出浮点数的表示范围,导致结果为无穷大(inf)。 - 当
a中的值很小时,exp(a)可能接近于零,导致精度损失。
- 如果数组
-
b_softmax:- 在计算
b时,每一行都减去了该行的最大值。这样可以确保b中的值不会过大或过小,从而避免了exp(b)的数值溢出或下溢问题。 - 数值稳定性更好,尤其在处理大规模数据或深度学习模型中非常常见。
- 在计算
3. 输出结果的区别
尽管 a_softmax 和 b_softmax 的计算方式不同,但它们的最终输出结果是相同的。这是因为:
[
\exp(a) / \sum_{j} \exp(a_j) = \exp(a - c) / \sum_{j} \exp(a_j - c)
]
其中 (c) 是每行的最大值。减去最大值的操作并不会改变 Softmax 的结果,只是提高了数值计算的稳定性。
结合代码分析
# a_softmax 的计算
a_softmax = np.exp(a) / np.sum(np.exp(a), axis=1).reshape(-1, 1)
# b 的计算:每一行减去该行的最大值
b = a - np.max(a, axis=1).reshape(-1, 1)
# b_softmax 的计算
b_softmax = np.exp(b) / np.sum(np.exp(b), axis=1).reshape(-1, 1)
-
a_softmax:- 使用原始数组
a计算 Softmax。 - 可能存在数值不稳定的问题。
- 使用原始数组
-
b_softmax:- 使用调整后的数组
b(每一行减去最大值)计算 Softmax。 - 数值更稳定,适合实际应用。
- 使用调整后的数组
总结
a_softmax和b_softmax的结果相同,但在计算过程中,b_softmax更加数值稳定。- 在实际应用中,通常推荐使用
b_softmax的方式来计算 Softmax,以避免数值溢出或下溢问题。
更多推荐
所有评论(0)