Softmax, Cross-entropy Loss and Gradient derivation and Implementation

本文详细介绍了机器学习中常用的softmax函数、cross-entropy损失函数及其梯度推导,包括单变量、向量梯度和batch实施,以及它们在深度学习中的应用和Python实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. 概要

2. Sigmoid function

3. Softmax function

3.1 定义

3.2 softmax函数的偏导数

3.3 softmax函数的梯度

3.4 softmax及其梯度的python实现

4. cross-entropy loss

4.2 logistic loss

4.3 logistic loss gradient         

4.4 cross-entropy loss

4.5 cross-entropy loss gradient

5. Gradient of "softmax + cross-entropy" combo

6. batch implementation


1. 概要

        简要介绍机器学习、深度学习中常用的softmax函数,cross-entropy损失函数,以及它们的梯度推导(尤其是softmax和cross-entropy loss级联后的梯度推导)。特别地,从对单个变量的偏导数,到对输入向量的偏导数(即梯度),乃至到对整个batch的梯度的矩阵表示。最后,给出对应的python实现。这些将成为完全DIY用python实现一个分类神经网络的一个基本构成模块。

2. Sigmoid function

        Sigmoid函数(也称为Logistic函数)是一种常用的非线性激活函数,它将输入值映射到一个介于0和1之间的值域。其定义为:

\sigma(z)=\frac{1}{1+e^{-z}}                 (1)

        其中,Sigmoid函数的图像呈S形,常用于神经网络中作为激活函数使用,将输出值转换为概率值。Sigmoid函数的优点在于它有很好的数学特性,单调、连续、可导,且其导函数非常“漂亮”(九可以用Sigmoid函数自己表示,因此非常方便于实现)!Sigmoid函数导数可以表示为:

\sigma'(x)=\sigma(x)(1-\sigma(x))         (2)

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(z):
    '''
    '''
    return 1 / (1 + np.exp(-z))  

x = np.arange(100) * 0.1 - 5
y = sigmoid(x)
y_derivative = y * (1 - y)
plt.plot(x,y, label = 'sigmoid')
plt.plot(x,y_derivative, label = 'derivative of sigmoid')
plt.legend()

 

图1:sigmoid and its derivative 

        但是,从深度学习的角度来看,Sigmoid的缺点也是明显的。如上图所示,当输入数据绝对值较大时,函数的导数接近于0,这容易导致梯度消失的问题,影响神经网络的训练效果。因此,事实上在现代深度学习中,Sigmoid函数在大部分情况下(用作隐藏层的激活函数)都被更有效的激活函数(如ReLU、LeakyReLU等)所取代。通常只用于二值分类神经网络的输出层。

3. Softmax function

3.1 定义

        Sigmoid函数适用于二分类问题,对于多分类问题,需要在Sigmoid函数的基础上进行扩展,这个扩展就得到Softmax函数。softmax函数的输入通常是一个向量,输出是一个相同大小的向量。softmax函数的特点是能够将任意实数向量转换为一个概率分布(严格地来说,是转换为符合概率公理的一组数据,因此可以当作概率值使用)。其输出是一个和为1的向量,其中每个元素表示对应类别的概率。其公式为:

\text{softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum\limits_{j=1}^{n} e^{z_j}}                              (3)

        其中,z表示一个向量,其中有n个分量(对应于n分类问题)。

        考虑n=2的情况,

                                \text{softmax}(\mathbf{z})_1 = \frac{e^{z_1}}{e^{z_1}+e^{z_2}} = \frac{1}{1+e^{z_2-z_1}}                  (4)

                                \text{softmax}(\mathbf{z})_2 = \frac{e^{z_2}}{e^{z_1}+e^{z_2}} = \frac{1}{1+e^{z_1-z_2}}                  (5)

        假定z1和z2分别表示类别1和类别2的评价分数,则显然,z1>z2表示结果应该被判定为类别1,z1<z2表示结果应该被判定为类别2(相等时,可以随意)。现在,令:z=z1-z2。则判决准则变为z>0表示应该被判定为类别1,z<0表示应该被判定为类别2。

                                P(class1 | data) = \frac{1}{1 + e^{z_2-z_1}} = \frac{1}{1 + e^{-z}} = \sigma(z)  (6)

        由此可以看到,在两分类问题(判别是类别1还是类别2的问题可以转换为判别是否为类别1的问题)条件下,Softmax函数就退化为Sigmoid函数了。

3.2 softmax函数的偏导数

        softmax函数的偏导数(由于有多个输入了,所以是偏导数)也同样有非常优美的形式(与sigmoid一样,这个优美的导函数形式源自于指数函数的的导数性质,以及输出结果归一化【即总和为1】特性)。

        首先,为了简洁,将softmax函数记为以下形式:

                        ​​​​​​​        s(z_i) = \text{softmax}(\bold{z})_i

        ​​​​​​​        ​​​​​​​        ​​​​​​​        s(\bold{z}) = \text{softmax}(\bold{z})             (7)

        s(z_i)的针对变量z_j的偏导函数(需要注意,不管是s(\bold{z})还是s(z_i)都是n个变量\{z_1,z_2,\cdots,z_n\}的函数!)可以推导如下。这里,需要分类讨论。注意,以下为了简洁起见,用\sum 表示 \sum\limits_{j=1}^n{e^{z_j}}

        case1:  i = j

                        \frac{\partial{y_i}}{\partial{z_j}} =\frac{\partial{}}{\partial{z_i}} (\frac{e^{z_i}}{\sum}) =\frac{e^{z_i}\sum - (e^{z_i})^2}{\sum^2}=\frac{e^{z_i}}{\sum} (\frac{\sum - e^{z_i}}{\sum})=y_i(1-y_i)       (8)

                        跟上面的式(2)长得像不像!

        case2: i != j

        ​​​​​​​        ​​​​​​​        \frac{\partial{y_i}}{\partial{z_j}} = -\frac{e^{z_i}e^{z_j}}{\sum^2} = -y_i y_j                 (9)

        合并起来的话,可以写成如下所示:

                         \frac{\partial{y_i}}{\partial{z_j}} = -\frac{e^{z_i}e^{z_j}}{\sum^2} = I(i==j) y_i -y_i y_j   (10)

        其中,I()表示Indicator function,()内条件下成立的话取值为1,否则取值为0.

3.3 softmax函数的梯度

        简而言之,梯度就是将各偏导数表示成向量(或矩阵)的形式。一般来说,把梯度视为行向量。

        首先考虑y的分量y_i的梯度(scalar to vector derivative)(注意,这里用不带下标的字母y,z表示向量,带下标的则表示其中的分量。通常会用字体加粗来着重强调表示向量,但是这里就不拘泥于这个了,有时会加粗有时不加粗,但是不带下标就表示是向量)        

        ​​​​​​​                \nabla_{\bold{z}}{y_i} = [\frac{\partial{y_i}}{\partial{z_1}}, \frac{\partial{y_i}}{\partial{z_2}}, \cdots,\frac{\partial{y_i}}{\partial{z_n}}]                (11)

        整个向量y的梯度(vector to vector derivative)就是将以上各分量的梯度纵向摞起来构成一个矩阵(这个在vector/matrix calculus中叫做所谓的numerator layout。与之相对的还有一种叫做denominator layout。但是前者更常见)

                         \nabla_{\bold{z}}\bold{y} = \left[ {\begin{array}{cccc} \partial_{11} & \partial_{12} & \cdots & \partial_{1n}\\ \partial_{21} & \partial_{22} & \cdots & \partial_{2n}\\ \vdots & \vdots & \ddots & \vdots\\ \partial_{n1} & \partial_{n2} & \cdots & \partial_{nn}\\ \end{array} } \right]                (12)

        其中,采用了缩减表示:\partial_{ij} = \frac{\partial{y_i}}{\partial{z_j}}

3.4 softmax python实现        

import numpy as np

def softmax(logits):
    '''
    Assuming a numpy ndarray (D,m), as input.
    Before doing the division, we must reshape the sums into a one-column matrix,
    otherwise, NumPy complains that it cannot divide a matrix by a one-dimensional array.
    '''    
    tmp = np.exp(logits)    
    tmp = tmp / np.sum(tmp, axis = 1).reshape(-1,1)
    np.testing.assert_almost_equal(np.sum(tmp, axis = 1), 1)
    return tmp


# test of softmax
# logits = np.random.rand(16).reshape(4,4)
logits = np.array([[2,2,2,2]])
s = softmax(logits)
print(logits)
print(s)

Output:
[[2 2 2 2]]
[[0.25 0.25 0.25 0.25]]

4. cross-entropy loss

        交叉熵损失(cross-entropy loss),是一种基于对数的损失函数(log loss)。也称为负对数似然(negative log likelihood,NLL)损失,是机器学习和深度学习中用于分类问题的一种常见损失函数。它用于比较模型输出的概率分布与真实标签的概率分布之间的差异。

        在二分类问题中,对应于所使用的logistic函数(即sigmoid函数),此时的交叉熵损失也称为logistic loss。换句话说,logistic loss是cross-entropy loss的在二分类情况下的特例。

4.2 logistic loss

        logistic loss的表达式(单个样本)如下式所示:

        ​​​​​​​        \text{logistic loss} = L = - \{y log (p) + (1-y)log(1-p)\}  (13)

        其中,y是样本的实际标签(0或1),p = \hat{y}是模型预测样本属于正类的概率,是sigmoid函数输出(当然其它激励函数的输出也可以,只要它能够表示概率)。

        显然,当真实标签y为1时,损失函数为$-\log(\hat{y})$,当真实标签y为0时,损失函数为$-\log(1-\hat{y})$。因此,当模型的预测值越接近真实标签,损失函数的值越小,反之,当模型的预测值与真实标签越不一致,损失函数的值越大。 

        一般来说,深度学习中样本数据都是采用(小)批量处理的方式(mini-batch),一个批量数据的的总的损失,就是取各样本的损失的平均值,如下所示:

        ​​​​​​​        \text{batch logistic loss} =\frac{1}{N} \sum\limits_{n=1}^N - \{y^n log (p^n) + (1-y^n)log(1-p^n)\}   (14)

        这里(由于上文说了用下标表示一个向量中的分量)用上标来表示样本编号。 

4.3 logistic loss gradient         

         进行简单的单变量微分运算可得logistic loss gradient(单变量条件下就是普通的导数)如下所示:

        ​​​​​​​        \frac{\partial{L}}{\partial{\hat{y}}} = -(\frac{y}{\hat{y}}-\frac{1-y}{1-\hat{y}})=\frac{\hat{y}-y}{(1-\hat{y})\hat{y}}                                                                         (15)

4.4 cross-entropy loss

        cross-entropy loss的表达式(单个样本)可以表达为logistic loss的直接向多分类情况的扩展(对应于把多分类问题看作是多个二分类问题的合并),如下式所示:

                \text{cross-entropy loss} = L = - \sum\limits_{i=1}^k \{y_i log (p_i) + (1-y_i)log(1-p_i)\}   (16)

        在上式中,求和的每一项相当于是针对每一个类别的logistic loss。其中k代表类别数。p_i = \hat{y}_i表示模型预测样本属于该类的概率。

        cross-entropy loss的更简洁的表达方式为(在多分类中,后半截的信息是冗余的,已经包含在其它各类别项的前半部分中了):

        ​​​​​​​        \text{cross-entropy loss} = L = - \sum\limits_{i=1}^k \{y_i log (p_i) \}       (17)

        同样批量处理的时的一个批量的交叉熵损失取各样本的交叉熵的均值,如下所示:

        ​​​​​​​        $Loss = -\frac{1}{N}\sum_{i=1}^N{\sum_{j=1}^M{y_{j}^i\log{\hat{y}_{j}^i}}}$                                 (18)

        其中,N为样本数量,M为类别数,$y_{j}^i$代表样本i的真实标签为类别j的概率(如果样本i的真实标签为类别j则为1,否则为0. 即一个one-hot vector),$\hat{y}_{j}^i$代表模型预测样本i属于类别j的概率。

        交叉熵损失的直观意义是,模型预测的概率分布与真实标签的概率分布越接近,损失就越小。因此,当模型对样本的分类预测越准确时,交叉熵损失就越小。

4.5 cross-entropy loss gradient

        先看loss对其中某个分量的偏导数(简单到爆):

        ​​​​​​​        \frac{\partial{L}}{\partial{\hat{y}_i}} = - \frac{y_i}{\hat{y}_i}                                               (19)

        cross-entropy loss的梯度就是将针对各分量的偏导数拼起来凑成一个向量而已(注意,如前所述,在数学分析中,通常将梯度视为行向量):

        ​​​​​​​        \frac{\partial{L}}{\partial{\bold{y}}} = [\frac{\partial{y_1}}{\partial{\hat{y}_1}}, \frac{\partial{y_2}}{\partial{\hat{y}_2}},\cdots,\frac{\partial{y_n}}{\partial{\hat{y}_n}}]                      (20)

5. Gradient of "softmax + cross-entropy" combo

图2:combo of  softmax and cross-entropy loss

         在机器学习或者深度学习中,softmax和cross-entropy通常是组合起来使用的。所以,可以把两者的组合看作是一个组件/模块,即:

        ​​​​​​​        \text{SML}(l) = L(\hat{y}) = L(\text{softmax}(l))     (21)

        这里,用SML表示combo of softmax and cross-entropy loss,l表示softmax的logits输入(如前所述它是一个向量),\hat{y}为softmax的输出。

        SML的梯度是指最终的loss针对softmax的输入logits的梯度。由于,SML由两个函数级联而成,即所谓的复合函数,求偏导数需要用到链式法则(chain rule)。而且,如下所示,由于l\hat{y}都是向量,所以需要用到全微分版本的链式法则。

        先看针对logits的分量i的偏导数。

        step1:       \frac{\partial{L}}{\partial{l_i}} = \sum\limits_{j=1}^n \frac{\partial{L}}{\partial{\hat{y}_j}} \frac{\partial{\hat{y}_j}}{\partial{l_i}}                                        (22-1)

        首先,L是\hat{y} = {...,\hat{y}_j,...}的函数,而各\hat{y}_j又分别是l_i的函数,所以求L对l_i的偏导数需要考虑各\hat{y}_j作为中间媒介的贡献。这是所谓的全微分,具体细节请参考高等数学教材。

        step2: 参见4-5.        

        ​​​​​​​        ​​​​​​​        \frac{\partial{L}}{\partial{\hat{y}_j}} = - \frac{y_j}{\hat{y}_j}                                                   (22-2)

        step3: 参见3-2.       

                        \frac{\partial{\hat{y}_j}}{\partial{l_i}} = -\frac{e^{l_i}e^{l_j}}{\sum^2} = I(i==j) \hat{y}_j -\hat{y}_j \hat{y}_i     (22-3)

        step4: 把(22-1~3)串起来可以得到:

图3:SML vs logits derivative 

         so beautiful! 这么眼花缭乱的一大堆东西,最后竟然得出如此简洁的一个结果!

        进一步,可以得到L的梯度表示(就是将各分量排排队凑成一个向量)如下所示 :

        ​​​​​​​        ​​​​​​​        ​​​​​​​\nabla{L} \\= [\frac{\partial{L}}{\partial{l}_1},\frac{\partial{L}}{\partial{l}_2},\cdots,\frac{\partial{L}}{\partial{l}_n}] \\= [y_1-\hat{y}_1,y_2-\hat{y}_2,\cdots,y_n-\hat{y}_n] \\= \bold{y} - \bold{\hat{y}}               (22-4)

6. batch implementation

        coming soon

        进一步更完整的关于backpropagation的数学推导参考:Tutorial: Mathmatical Derivation of Backpropagation

reference:

[1] Killer Combo: Softmax and Cross Entropy

### 关于交叉熵损失函数 #### 交叉熵损失函数定义 交叉熵损失函数是一种衡量实际输出与期望输出之间差异的方法,在机器学习领域特别是分类问题中广泛应用。该方法通过比较预测概率分布和真实标签的概率分布之间的距离来进行评估。 #### 公式表达 对于二分类问题中的单个样本而言,其交叉熵损失可以由下述公式给出: \[ L(y, \hat{y}) = -[ y \log(\hat{y}) + (1-y)\log(1-\hat{y}) ] \] 其中 \(y\) 表示真实的类别标签(0 或者 1),而 \(\hat{y}\) 则代表模型对该类别的预测概率[^2]。 当处理多分类情况时,则会采用更一般的软最大似然估计(Softmax)配合交叉熵的形式: \[ C=-\sum_{i=1}^{N}{t_i log(p_i)} \] 这里\(p_i\) 是经过 Softmax 转换后的第 i 类的预测概率;\(t_i\) 是对应的真实标签向量中的元素值,通常是一个 one-hot 编码形式的数据集的一部分[^3]。 #### 应用场景 - **逻辑回归** 和其他线性判别分析算法经常使用二元交叉熵作为目标函数。 -深度学习框架内,无论是卷积神经网络还是循环神经网络都可能涉及到 softmax 层加上交叉熵损失组合来完成最终决策任务。 - 对于自然语言处理(NLP),图像识别等领域来说,这种类型的损失函数有助于提高模型性能并加速收敛过程。 ```python import torch.nn as nn # 创建一个简单的二分类交叉熵损失实例 criterion = nn.BCELoss() output = torch.tensor([0.7], requires_grad=True) target = torch.tensor([1.]) loss = criterion(output, target) print(f'Binary Cross Entropy Loss: {loss.item()}') ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值