07.1 多分类函数
7.1 多分类函数⚓︎
此函数对线性多分类和非线性多分类都适用。
先回忆一下二分类问题,在线性计算后,使用了Logistic函数计算样本的概率值,从而把样本分成了正负两类。那么对于多分类问题,应该使用什么方法来计算样本属于各个类别的概率值呢?又是如何作用到反向传播过程中的呢?我们这一节主要研究这个问题。
7.1.1 多分类函数定义 - Softmax⚓︎
如何得到多分类问题的分类结果概率值?⚓︎
Logistic函数可以得到诸如0.8、0.3这样的二分类概率值,前者接近1,后者接近0。那么多分类问题如何得到类似的概率值呢?
我们依然假设对于一个样本的分类值是用这个线性公式得到的:
但是,我们要求
具体的说,假设
有的读者可能会有疑问:我们不能训练神经网络让它的z值直接变成概率形式吗?答案是否定的,因为z值是经过线性计算得到的,线性计算能力有限,无法有效地直接变成概率值。
取max值⚓︎
z值是
- 分类结果是
,只保留的非0即1的信息,没有各元素之间相差多少的信息,可以理解是“Hard Max”; - max操作本身不可导,无法用在反向传播中。
引入Softmax⚓︎
Softmax加了个"soft"来模拟max的行为,但同时又保留了相对大小的信息。
上式中:
是对第 项的分类原始值,即矩阵运算的结果 是参与分类计算的每个类别的原始值 是总分类数 是对第 项的计算结果
假设
用图7-5来形象地说明这个过程。
图7-5 Softmax工作过程
当输入的数据
对比MAX运算和Softmax的不同,如表7-2所示。
表7-2 MAX运算和Softmax的不同
输入原始值 | MAX计算 | Softmax计算 |
---|---|---|
也就是说,在(至少)有三个类别时,通过使用Softmax公式计算它们的输出,比较相对大小后,得出该样本属于第一类,因为第一类的值为0.879,在三者中最大。注意这是对一个样本的计算得出的数值,而不是三个样本,亦即Softmax给出了某个样本分别属于三个类别的概率。
它有两个特点:
- 三个类别的概率相加为1
- 每个类别的概率都大于0
Softmax的反向传播工作原理⚓︎
我们仍假设网络输出的预测数据是
这个信息很奇怪:
- 第一项是2,我们已经预测准确了此样本属于第一类,但是反向误差的值是2,即惩罚值是2
- 第二项是1,惩罚值是1,预测对了,仍有惩罚值
- 第三项是-3,惩罚值是-3,意为着奖励值是3,明明预测错误了却给了奖励
所以,如果不使用Softmax这种机制,会存在有个问题:
- z值和y值之间,即预测值和标签值之间不可比,比如
与 是不可比的 - z值中的三个元素之间虽然可比,但只能比大小,不能比差值,比如
,但3和1相差2,1和-3相差4,这些差值是无意义的
在使用Softmax之后,我们得到的值是
再来分析这个信息:
- 第一项-0.121是奖励给该类别0.121,因为它做对了,但是可以让这个概率值更大,最好是1
- 第二项0.119是惩罚,因为它试图给第二类0.119的概率,所以需要这个概率值更小,最好是0
- 第三项0.002是惩罚,因为它试图给第三类0.002的概率,所以需要这个概率值更小,最好是0
这个信息是完全正确的,可以用于反向传播。Softmax先做了归一化,把输出值归一到[0,1]之间,这样就可以与标签值的0或1去比较,并且知道惩罚或奖励的幅度。
从继承关系的角度来说,Softmax函数可以视作Logistic函数扩展,比如一个二分类问题:
是不是和Logistic函数形式非常像?其实Logistic函数也是给出了当前样本的一个概率值,只不过是依靠偏近0或偏近1来判断属于正类还是负类。
7.1.2 正向传播⚓︎
矩阵运算⚓︎
分类计算⚓︎
损失函数计算⚓︎
计算单样本时,m是分类数: $$ loss(w,b)=-\sum_{i=1}^m y_i \ln a_i \tag{3} $$
计算多样本时,m是分类数,n是样本数:
如图7-6示意。
图7-6 Softmax在神经网络结构中的示意图
7.1.3 反向传播⚓︎
实例化推导⚓︎
我们先用实例化的方式来做反向传播公式的推导,然后再扩展到一般性上。假设有三个类别,则:
为了方便书写,我们令:
依次求解公式12中的各项:
把公式13~18组合到12中:
不失一般性,由公式19可得: $$ \frac{\partial loss}{\partial z_i}=a_i-y_i \tag{20} $$
一般性推导⚓︎
- Softmax函数自身的求导
由于Softmax涉及到求和,所以有两种情况:
- 求输出项
对输入项 的导数,此时: ,可以扩展到 为任意相等值 - 求输出项
或 对输入项 的导数,此时: 为 或 , ,可以扩展到 为任意不等值
Softmax函数的分子:因为是计算
Softmax函数的分母: $$ \sum\limits_{i=1}^m e^{z_i} = e^{z_1} + \dots + e^{z_j} + \dots +e^{z_m} \Rightarrow E $$
时(比如输出分类值 对 的求导),求 对 的导数,此时分子上的 要参与求导。参考基本数学导数公式33:
时(比如输出分类值 对 的求导, ), 对 的导数,分子上的 与 没有关系,求导为0,分母的求和项中 要参与求导。同样是公式33,因为分子 对 求导的结果是0:
- 结合损失函数的整体反向传播公式
看上图,我们要求Loss值对Z1的偏导数。和以前的Logistic函数不同,那个函数是一个
先从Loss的公式看,
再从Softmax函数的形式来看:
无论是
当上式中
前面说过了,因为Softmax涉及到各项求和,A的分类结果和Y的标签值分类是否一致,所以需要分情况讨论:
因此,
时,loss通过 对 求导(或者是通过 对 求导):
,loss通过 对 求导:
把两种情况加起来:
因为
我们惊奇地发现,最后的反向计算过程就是:
其含义是,样本预测第一类,但实际是第二类,所以给第一类0.879的惩罚值,给第二类0.881的奖励,给第三类0.002的惩罚,并反向传播给神经网络。
后面对
7.1.4 代码实现⚓︎
第一种,直截了当按照公式写:
def Softmax1(x):
e_x = np.exp(x)
v = np.exp(x) / np.sum(e_x)
return v
np.exp(x)
很容易溢出,因为是指数运算。所以,有了下面这种改进的代码:
def Softmax2(Z):
shift_Z = Z - np.max(Z)
exp_Z = np.exp(shift_Z)
A = exp_Z / np.sum(exp_Z)
return A
Z = np.array([3,0,-3])
print(Softmax1(Z))
print(Softmax2(Z))
[0.95033021 0.04731416 0.00235563]
[0.95033021 0.04731416 0.00235563]
为什么一样呢?从代码上看差好多啊!我们来证明一下:
假设有3个值a,b,c,并且a在三个数中最大,则b所占的Softmax比重应该这样写:
如果减去最大值变成了a-a,b-a,c-a,则b'所占的Softmax比重应该这样写:
Softmax2
的写法对一个一维的向量或者数组是没问题的,如果遇到Z是个np.sum(exp_Z)
这个函数,会把
所以应该这么写:
class Softmax(object):
def forward(self, z):
shift_z = z - np.max(z, axis=1, keepdims=True)
exp_z = np.exp(shift_z)
a = exp_z / np.sum(exp_z, axis=1, keepdims=True)
return a
axis=1
这个参数非常重要,因为如果输入Z是单样本的预测值话,如果是分三类,则应该是个
但是,如果是批量训练,假设每次用两个样本,则:
if __name__ == '__main__':
z = np.array([[3,1,-3],[1,-3,3]]).reshape(2,3)
a = Softmax().forward(z)
print(a)
[[0.87887824 0.11894324 0.00217852]
[0.11894324 0.00217852 0.87887824]]
如果s = np.sum(exp_z)
,不指定axis=1
参数,则:
[[0.43943912 0.05947162 0.00108926]
[0.05947162 0.00108926 0.43943912]]
思考与练习⚓︎
- 有没有可能一个样本的三分类值中,有两个或多个分类值相同呢,比如
? - 遇到这种问题是你打算如何解决?