【转载】深度学习笔记——详解损失函数

原文链接: https://blog.csdn.net/weixin_53765658/article/details/136360033
CSDN账号: Purepisces
github账号: purepisces
希望大家可以Star Machine Learning Blog https://github.com/purepisces/Wenqing-Machine_Learning_Blog

损失函数

根据您使用的神经网络类型和数据类型,不同的损失函数可能会变得有用,例如分类问题的交叉熵损失,回归问题的均方误差。了解这些损失函数是如何计算的,以及它们将如何用于更新您的网络是很重要的。

损失类

类属性:

  • A A A:存储模型预测以计算反向传播。
  • Y Y Y:存储期望输出以计算反向传播。

类方法:

  • f o r w a r d forward forward

    • 参数: A A A(模型预测), Y Y Y(期望输出)
    • 返回值:损失值 L L L
    • 描述:计算并返回标量损失值 L L L,量化网络输出和期望输出之间的不匹配。
  • b a c k w a r d backward backward

    • 返回值: d L d A dLdA dLdA(模型输出变化如何影响损失 L L L
    • 描述:计算并返回 d L d A dLdA dLdA,表示模型输出 A A A 的变化如何影响损失 L L L。它使得可以进行反向传播的下游计算。

请考虑以下类结构:

class Loss:def forward(self, A, Y):self.A = Aself.Y = Yself.    # todo(根据需要存储额外的属性)N      = # todo,这是 A 和 Y 的第一维度C      = # todo,这是 A 和 Y 的第二维度# todoreturn Ldef backward(self):dLdA = # 待办事项return dLdA
代码名称数学符号类型形状含义
N N N N标量-批量大小
C C C C标量-类别数目
A A A A矩阵 N × C N \times C N×C模型输出
Y Y Y Y矩阵 N × C N \times C N×C真实值
L L L L标量-损失值
dLdA ∂ L ∂ A \frac{\partial L}{\partial A} AL矩阵 N × C N \times C N×C模型输出变化如何影响损失

注意:在回归任务的上下文中,对应于类别数目的维度 C C C 简化为 1。这是因为回归问题涉及预测单个连续变量,而不是从多个类别中选择。相反,在分类场景中, C C C 代表每个输入可以被分类的不同类别或类别的总数,因此可以根据具体问题而变化。

损失函数拓扑在下图中可视化:

在这里插入图片描述

示例

在此示例中,我们将演示如何在具有3个类别的分类任务中使用 L o s s Loss Loss 类。假设我们的批量大小( N N N)为2,这意味着我们一次处理两个示例。

模型输出(Logits)

模型输出,表示为 A A A,是每个类别的logits。对于我们的示例,批量大小为2且有3个类别, A A A 可能如下所示:

A = [ 5 1 − 1 − 2 6 0 ] A = \begin{bmatrix} 5 & 1 & -1\\ -2 & 6 & 0 \end{bmatrix} A=[521610]

这些logits代表每个类别的原始分数,在应用softmax函数之前。

真实值(Y)

真实值 Y Y Y 以独热编码(one-hot encoded)格式表示:

Y = [ 1 0 0 0 0 1 ] Y = \begin{bmatrix} 1 & 0 & 0\\ 0 & 0 & 1 \end{bmatrix} Y=[100001]

这里,第一行 [ 1 , 0 , 0 ] [1, 0, 0] [1,0,0] 表示第一个示例属于第1类,第二行 [ 0 , 0 , 1 ] [0, 0, 1] [0,0,1] 表示第二个示例属于第3类。

在损失计算中使用 A A A Y Y Y

损失函数使用logits A A A 和真实值 Y Y Y 来计算损失值 L L L。例如,使用交叉熵损失函数会首先对logits应用softmax函数以获得概率,然后通过将这些预测概率与 Y Y Y 中的实际标签进行比较来计算每个单独示例的损失。随后,这些单独的损失在批次中的所有示例上平均,以产生损失的单一标量值, L L L

L o s s Loss Loss 类中的 f o r w a r d forward forward 方法负责使用 A A A(作为logits)和 Y Y Y 来计算这个标量损失值 L L L。接着, b a c k w a r d backward backward 方法计算损失对于logits的梯度,表示为 ∂ L ∂ A \frac{\partial L}{\partial A} AL。这个梯度对于反向传播过程至关重要,使得在训练期间可以更新模型参数。

详解均方误差损失函数:

均方误差(MSE,Mean Squared Error)是回归问题中评估预测误差的一种广泛使用的指标。在回归中,目标是预测连续值,例如根据房屋的特征(如面积、位置、卧室数量等)估计房屋的价格。

MSE 损失前向方程

计算从计算模型预测( A A A)与实际真值( Y Y Y)之间的平方误差( S E SE SE)开始:

S E ( A , Y ) = ( A − Y ) ⊙ ( A − Y ) SE(A, Y) = (A - Y) \odot (A - Y) SE(A,Y)=(AY)(AY)

接下来,我们确定平方误差之和( S S E SSE SSE)。这里, ι N \iota_N ιN ι C \iota_C ιC 分别表示大小为 N N N C C C 的、填充有 1 的列向量:

S S E ( A , Y ) = ι N T ⋅ S E ( A , Y ) ⋅ ι C SSE(A,Y) = \iota_{N}^{T} \cdot SE(A,Y) \cdot \iota_{C} SSE(A,Y)=ιNTSE(A,Y)ιC

这个操作将 S E ( A , Y ) SE(A, Y) SE(A,Y) 矩阵中的所有元素求和,该矩阵的维度为 N × C N \times C N×C。通过 ι N T \iota_{N}^{T} ιNT 的乘法在行间聚合错误,随后通过 ι C \iota_{C} ιC 的乘法将这些在列间求和,产生一个单一标量的总误差。

然后计算每个组件的均方误差( M S E MSE MSE)损失:

M S E L o s s ( A , Y ) = S S E ( A , Y ) N ⋅ C MSELoss(A, Y) = \frac{SSE(A, Y)}{N \cdot C} MSELoss(A,Y)=NCSSE(A,Y)

MSE 损失反向方程

在反向传播过程中,需要计算 MSE 损失相对于模型输出( A A A)的梯度,以更新模型参数:

M S E L o s s . b a c k w a r d ( ) = 2 ⋅ ( A − Y ) N ⋅ C MSELoss.backward() = 2 \cdot \frac{(A - Y)}{N \cdot C} MSELoss.backward()=2NC(AY)

MSE 损失的导数

MSE 损失函数定义为:

M S E L o s s ( A , Y ) = 1 N ⋅ C ∑ i = 1 N ∑ j = 1 C ( A i j − Y i j ) 2 MSELoss(A, Y) = \frac{1}{N \cdot C} \sum\limits_{i=1}^{N} \sum\limits_{j=1}^{C} (A_{ij} - Y_{ij})^2 MSELoss(A,Y)=NC1i=1Nj=1C(AijYij)2

其中:

A A A:模型预测值。

Y Y Y:真实值。

N N N:批次中的样本数量。

C C C:每个样本的输出维度,回归任务中通常为 1。

为了更新模型参数(在这个例子中,通过反向传播),我们需要知道 A A A 的变化如何影响损失。这由损失函数相对于 A A A 的导数给出,表示为 ∂ M S E L o s s ∂ A \frac{\partial MSELoss}{\partial A} AMSELoss

∂ M S E L o s s ∂ A = 2 ⋅ ( A − Y ) N ⋅ C \frac{\partial MSELoss}{\partial A} = 2 \cdot \frac{(A - Y)}{N \cdot C} AMSELoss=2NC(AY)

梯度 ∂ M S E L o s s ∂ A \frac{\partial MSELoss}{\partial A} AMSELoss 指向损失函数最陡增加的方向。通过向相反方向移动(即,从预测 A A A 中减去这个梯度),我们可以减少损失,这是训练模型的目标。

总之, M S E L o s s . b a c k w a r d ( ) MSELoss.backward() MSELoss.backward() 2 ⋅ ( A − Y ) N ⋅ C 2 \cdot \frac{(A - Y)}{N \cdot C} 2NC(AY) 公式是通过对预测 A A A 求 MSE 损失函数的导数得到的,考虑了平方误差和平均操作。这个梯度在优化过程中被用来调整模型参数,以最小化损失。

import numpy as npclass MSELoss:def forward(self, A, Y):# 为反向计算存储预测值(A)和真实值(Y)self.A = Aself.Y = Y# 计算预测值和真实值之间的平方误差se = (A - Y) ** 2# 对平方误差求和以得到总的平方误差sse = np.sum(se)# 通过将总的平方误差除以元素数量来计算均方误差mse = sse / (A.shape[0] * A.shape[1])return msedef backward(self):# 计算损失相对于预测值(A)的梯度dLdA = 2 * (self.A - self.Y) / (self.A.shape[0] * self.A.shape[1])return dLdA

示例

让我们通过一个具体的例子来了解在回归场景中如何应用均方误差(MSE)损失。假设我们正试图基于一些特征来预测房屋价格。为了简单起见,我们将考虑一个案例,其中我们的模型基于多个特征(例如平方英尺面积和卧室数量)来预测两栋房屋的价格,因此我们的批量大小 N N N 为 2,特征数量 C C C 也为 2。

给定数据:

  • 模型输出 ( A A A):预测的两栋房屋的价格和卧室数量。假设模型对每栋房屋的预测如下(价格以美元计,卧室以数量计)。这可以表示为一个 2x2 矩阵(因为 N = 2 N=2 N=2 C = 2 C=2 C=2):

A = [ 300 , 000 3 500 , 000 4 ] A = \begin{bmatrix} 300,000 & 3 \\ 500,000 & 4 \end{bmatrix} A=[300,000500,00034]

这里,第一列代表两栋房屋的预测价格,第二列代表预测的卧室数量。

  • 真实值 ( Y Y Y):两栋房屋的实际价格和卧室数量。这也是一个 2x2 矩阵:

Y = [ 350 , 000 4 450 , 000 3 ] Y = \begin{bmatrix} 350,000 & 4 \\ 450,000 & 3 \end{bmatrix} Y=[350,000450,00043]

正向传播(计算 MSE 损失):

  1. 计算平方误差 ( S E SE SE):

S E ( A , Y ) = ( A − Y ) ⊙ ( A − Y ) = [ ( 300 , 000 − 350 , 000 ) 2 ( 3 − 4 ) 2 ( 500 , 000 − 450 , 000 ) 2 ( 4 − 3 ) 2 ] = [ 2500 × 1 0 6 1 2500 × 1 0 6 1 ] \begin{align} SE(A, Y) &= (A - Y) \odot (A - Y) \\ &= \begin{bmatrix} (300,000 - 350,000)^2 & (3 - 4)^2 \\ (500,000 - 450,000)^2 & (4 - 3)^2 \end{bmatrix} \\ &= \begin{bmatrix} 2500 \times 10^6 & 1 \\ 2500 \times 10^6 & 1 \end{bmatrix} \end{align} SE(A,Y)=(AY)(AY)=[(300,000350,000)2(500,000450,000)2(34)2(43)2]=[2500×1062500×10611]

  1. 平方误差之和 ( S S E SSE SSE):
    S E SE SE 中的所有元素相加:

S S E ( A , Y ) = ∑ S E ( A , Y ) = 2 × ( 2500 × 1 0 6 ) + 2 × 1 = 5000 × 1 0 6 + 2 SSE(A, Y) = \sum SE(A, Y) = 2 \times (2500 \times 10^6) + 2 \times 1 = 5000 \times 10^6 + 2 SSE(A,Y)=SE(A,Y)=2×(2500×106)+2×1=5000×106+2

  1. 均方误差 ( M S E MSE MSE):

M S E L o s s ( A , Y ) = S S E ( A , Y ) N ⋅ C = 5000 × 1 0 6 + 2 2 × 2 = 2500 × 1 0 6 + 1 2 MSELoss(A, Y) = \frac{SSE(A, Y)}{N \cdot C} = \frac{5000 \times 10^6 + 2}{2 \times 2} = \frac{2500 \times 10^6 + 1}{2} MSELoss(A,Y)=NCSSE(A,Y)=2×25000×106+2=22500×106+1

反向传播(计算梯度):

可以计算损失相对于预测值 ( A A A) 的梯度:

∂ M S E L o s s ∂ A = 2 ⋅ ( A − Y ) N ⋅ C = 2 ⋅ [ 300 , 000 − 350 , 000 3 − 4 500 , 000 − 450 , 000 4 − 3 ] 2 × 2 = 1 2 [ − 50 , 000 − 1 50 , 000 1 ] \begin{align} \frac{\partial MSELoss}{\partial A} &= 2 \cdot \frac{(A - Y)}{N \cdot C} \\ &= 2 \cdot \frac{\begin{bmatrix} 300,000 - 350,000 & 3 - 4 \\ 500,000 - 450,000 & 4 - 3 \end{bmatrix}}{2 \times 2} \\ &= \frac{1}{2} \begin{bmatrix} -50,000 & -1 \\ 50,000 & 1 \end{bmatrix} \end{align} AMSELoss=2NC(AY)=22×2[300,000350,000500,000450,0003443]=21[50,00050,00011]

这个梯度矩阵提供了如何调整每个预测(价格和卧室数量)以最小化损失的指导。负值表示需要增加预测值,正值表明需要减少预测值以减少误差。

详解交叉熵损失函数:

交叉熵损失是用于基于概率的分类问题最常用的损失函数之一。

交叉熵损失前向方程

首先,我们使用softmax函数将原始模型输出 A A A转换成由输入数值的指数决定的 C C C类的概率分布。

ι N \iota_N ιN ι C \iota_C ιC是大小为 N N N C C C的列向量,包含全部为1的值。

softmax ( A ) = σ ( A ) = exp ⁡ ( A ) ∑ j = 1 C exp ⁡ ( A i j ) \text{softmax}(A) = \sigma(A) = \frac{\exp(A)}{\sum\limits_{j=1}^{C} \exp(A_{ij})} softmax(A)=σ(A)=j=1Cexp(Aij)exp(A)

现在,A的每一行代表模型对概率分布的预测,而Y的每一行代表一个输入的目标分布。
然后,我们计算分布Ai相对于目标分布Yi的交叉熵H(A,Y),对于i = 1,…,N:

crossentropy = H ( A , Y ) = ( − Y ⊙ log ⁡ ( σ ( A ) ) ) ⋅ ι C \text{crossentropy} = H(A, Y) = (-Y \odot \log(\sigma(A))) \cdot \mathbf{\iota}_C crossentropy=H(A,Y)=(Ylog(σ(A)))ιC

记住,损失函数的输出是一个标量,但现在我们有一个大小为N的列矩阵。要将其转换为标量,我们可以使用所有交叉熵的和或平均值。

这里,我们选择使用平均交叉熵作为交叉熵损失,这也是PyTorch的默认设置:

sumcrossentropyloss : = ι N T ⋅ H ( A , Y ) = S C E ( A , Y ) \text{sumcrossentropyloss} := \mathbf{\iota}_N^T \cdot H(A, Y) = SCE(A, Y) sumcrossentropyloss:=ιNTH(A,Y)=SCE(A,Y)

meancrossentropyloss : = S C E ( A , Y ) N \text{meancrossentropyloss} := \frac{SCE(A, Y)}{N} meancrossentropyloss:=NSCE(A,Y)
在这里插入图片描述

交叉熵损失反向方程

xent.backward ( ) = σ ( A ) − Y N \text{xent.backward}() = \frac{\sigma(A) - Y}{N} xent.backward()=Nσ(A)Y

梯度的推导(我的证明)

要找到交叉熵损失相对于对数几率 A i A_i Ai的梯度,我们需要计算导数 ∂ H ∂ A i \frac{\partial H}{\partial A_i} AiH。这涉及到应用链式法则到对数和softmax函数的复合中。

注意对于一个对数几率 A i c A_{ic} Aic,softmax函数定义为:

σ ( A i c ) = e A i c ∑ j = 1 C e A i j \sigma(A_{ic}) = \frac{e^{A_{ic}}}{\sum\limits_{j=1}^{C} e^{A_{ij}}} σ(Aic)=j=1CeAijeAic

步骤1:应用链式法则

首先,注意我们需要对一个复合函数的导数应用链式法则,这个复合函数是softmax输出的对数:

H ( A i , Y i ) = − ∑ c = 1 C Y i c log ⁡ ( σ ( A i c ) ) H(A_i, Y_i) = -\sum_{c=1}^{C} Y_{ic} \log(\sigma(A_{ic})) H(Ai,Yi)=c=1CYiclog(σ(Aic))

∂ H ∂ A i c = ∂ ( − Y i 1 log ⁡ ( σ ( A i 1 ) ) − Y i 2 log ⁡ ( σ ( A i 2 ) ) − . . . − Y i C log ⁡ ( σ ( A i C ) ) ) ∂ A i c = ∂ ( − Y i 1 log ⁡ ( σ ( A i 1 ) ) ) ∂ A i c + ∂ ( − Y i 2 log ⁡ ( σ ( A i 2 ) ) ) ∂ A i c + . . . + ∂ ( − Y i C log ⁡ ( σ ( A i C ) ) ) ∂ A i c = − Y i 1 ∂ log ⁡ ( σ ( A i 1 ) ) ∂ A i c − Y i 2 ∂ log ⁡ ( σ ( A i 2 ) ) ∂ A i c − . . . − Y i C ∂ log ⁡ ( σ ( A i C ) ) ∂ A i c = − ∑ k = 1 C Y i k ∂ log ⁡ ( σ ( A i k ) ) ∂ A i c \begin{align*} \frac{\partial H}{\partial A_{ic}} &= \frac{\partial (-Y_{i1}\log(\sigma(A_{i1})) -Y_{i2}\log(\sigma(A_{i2}))-...-Y_{iC}\log(\sigma(A_{iC})))}{\partial A_{ic}} \\ &= \frac{\partial (-Y_{i1}\log(\sigma(A_{i1})))}{\partial A_{ic}} + \frac{\partial (-Y_{i2}\log(\sigma(A_{i2})))}{\partial A_{ic}} + ...+ \frac{\partial (-Y_{iC}\log(\sigma(A_{iC})))}{\partial A_{ic}} \\ &=-Y_{i1}\frac{\partial \log(\sigma(A_{i1}))}{\partial A_{ic}} -Y_{i2}\frac{\partial \log(\sigma(A_{i2}))}{\partial A_{ic}} -...-Y_{iC}\frac{\partial \log(\sigma(A_{iC}))}{\partial A_{ic}} \\ &=- \sum_{k=1}^{C} Y_{ik} \frac{\partial \log(\sigma(A_{ik}))}{\partial A_{ic}}\\ \end{align*} AicH=Aic(Yi1log(σ(Ai1))Yi2log(σ(Ai2))...YiClog(σ(AiC)))=Aic(Yi1log(σ(Ai1)))+Aic(Yi2log(σ(Ai2)))+...+Aic(YiClog(σ(AiC)))=Yi1Aiclog(σ(Ai1))Yi2Aiclog(σ(Ai2))...YiCAiclog(σ(AiC))=k=1CYikAiclog(σ(Aik))

∂ H ∂ A i c = − ∑ k = 1 C Y i k ∂ log ⁡ ( σ ( A i k ) ) ∂ A i c \frac{\partial H}{\partial A_{ic}} = - \sum_{k=1}^{C} Y_{ik} \frac{\partial \log(\sigma(A_{ik}))}{\partial A_{ic}} AicH=k=1CYikAiclog(σ(Aik))

步骤2:Softmax的对数的导数

log ⁡ ( σ ( A i k ) ) \log(\sigma(A_{ik})) log(σ(Aik))关于 A i c A_{ic} Aic的导数涉及两种情况:当 k = c k=c k=c和当 k ≠ c k \neq c k=c时。

k = c k=c k=c时,使用对数的导数 ∂ log ⁡ ( x ) ∂ x = 1 x \frac{\partial \log(x)}{\partial x} = \frac{1}{x} xlog(x)=x1和softmax的定义,我们得到:

∂ log ⁡ ( σ ( A i k ) ) ∂ A i c = ∂ log ⁡ ( σ ( A i c ) ) ∂ σ ( A i c ) ⋅ ∂ σ ( A i c ) ∂ A i c = 1 σ ( A i c ) ⋅ σ ( A i c ) ⋅ ( 1 − σ ( A i c ) ) = 1 − σ ( A i c ) \begin{align*} \frac{\partial \log(\sigma(A_{ik}))}{\partial A_{ic}} &= \frac{\partial \log(\sigma(A_{ic}))}{\partial \sigma(A_{ic})} \cdot \frac{\partial \sigma(A_{ic})}{\partial A_{ic}} \\ &= \frac{1}{\sigma(A_{ic})} \cdot \sigma(A_{ic}) \cdot (1 - \sigma(A_{ic})) \\ &= 1 - \sigma(A_{ic}) \end{align*} Aiclog(σ(Aik))=σ(Aic)log(σ(Aic))Aicσ(Aic)=σ(Aic)1σ(Aic)(1σ(Aic))=1σ(Aic)

k ≠ c k\neq c k=c时,导数涉及不同类的softmax函数,结果是:

∂ log ⁡ ( σ ( A i k ) ) ∂ A i c = ∂ log ⁡ ( σ ( A i k ) ) ∂ σ ( A i c ) ⋅ ∂ σ ( A i c ) ∂ A i c = 1 σ ( A i k ) ⋅ − σ ( A i k ) ⋅ σ ( A i c ) = − σ ( A i c ) \begin{align*} \frac{\partial \log(\sigma(A_{ik}))}{\partial A_{ic}} &= \frac{\partial \log(\sigma(A_{ik}))}{\partial \sigma(A_{ic})} \cdot \frac{\partial \sigma(A_{ic})}{\partial A_{ic}} \\ &= \frac{1}{\sigma(A_{ik})} \cdot -\sigma(A_{ik}) \cdot \sigma(A_{ic}) \\ &= -\sigma(A_{ic}) \end{align*} Aiclog(σ(Aik))=σ(Aic)log(σ(Aik))Aicσ(Aic)=σ(Aik)1σ(Aik)σ(Aic)=σ(Aic)

步骤3:合并情况

由于 Y i Y_i Yi对于真实类别只能为1,否则为0,这简化为:

∂ H ∂ A i c = − ∑ k = 1 C Y i k ∂ log ⁡ ( σ ( A i k ) ) ∂ A i c = o r { − 1 ( 1 − σ ( A i c ) ) = σ ( A i c ) − 1 , for  Y i c = 0 − 1 ( − σ ( A i c ) ) = σ ( A i c ) − 0 , for  Y i c = 1 = σ ( A i c ) − Y i c \begin{align*} \frac{\partial H}{\partial A_{ic}} &= - \sum_{k=1}^{C} Y_{ik} \frac{\partial \log(\sigma(A_{ik}))}{\partial A_{ic}} \\ &= or\begin{cases} -1 (1 - \sigma(A_{ic})) = \sigma(A_{ic}) - 1, & \text{for } Y_{ic} = 0 \\ -1(-\sigma(A_{ic})) = \sigma(A_{ic}) - 0, & \text{for } Y_{ic} = 1 \end{cases} \\ &= \sigma(A_{ic}) - Y_{ic} \end{align*} AicH=k=1CYikAiclog(σ(Aik))=or{1(1σ(Aic))=σ(Aic)1,1(σ(Aic))=σ(Aic)0,for Yic=0for Yic=1=σ(Aic)Yic

示例

让我给出一个具体的例子来说明它:

示例 1

考虑这个案例 Y = [ 1 , 0 , 0 ] Y = [1,0,0] Y=[1,0,0] A = [ 2 , 1 , − 1 ] A = [2, 1, -1] A=[2,1,1]

Y 11 = 1 , Y 12 = 0 , Y 13 = 0 Y_{11} = 1, Y_{12} = 0, Y_{13} = 0 Y11=1,Y12=0,Y13=0
A 11 = 2 , A 12 = 1 , A 13 = − 1 A_{11} = 2, A_{12} = 1, A_{13} = -1 A11=2,A12=1,A13=1

然后当计算

∂ H ∂ A 13 = − ∑ k = 1 C Y 1 k ∂ log ⁡ ( σ ( A 1 k ) ) ∂ A 13 = − Y 11 ∂ log ⁡ ( σ ( A 11 ) ) ∂ A 13 − Y 12 ∂ log ⁡ ( σ ( A 12 ) ) ∂ A 13 − Y 13 ∂ log ⁡ ( σ ( A 13 ) ) ∂ A 13 = − 1 ( − σ ( A 13 ) ) − 0 − 0 = σ ( A 13 ) − 0 = σ ( A 13 ) − Y 13 \begin{align*} \frac{\partial H}{\partial A_{13}} &= - \sum\limits_{k=1}^{C} Y_{1k} \frac{\partial \log(\sigma(A_{1k}))}{\partial A_{13}}\\ &=-Y_{11}\frac{\partial \log(\sigma(A_{11}))}{\partial A_{13}}-Y_{12}\frac{\partial \log(\sigma(A_{12}))}{\partial A_{13}}-Y_{13}\frac{\partial \log(\sigma(A_{13}))}{\partial A_{13}}\\ &= -1(-\sigma(A_{13}))-0-0 \\ &= \sigma(A_{13}) - 0 \\ &= \sigma(A_{13}) - Y _{13}\\ \end{align*} A13H=k=1CY1kA13log(σ(A1k))=Y11A13log(σ(A11))Y12A13log(σ(A12))Y13A13log(σ(A13))=1(σ(A13))00=σ(A13)0=σ(A13)Y13

示例 2

考虑这个案例 Y = [ 0 , 0 , 1 ] Y = [0,0,1] Y=[0,0,1] A = [ 2 , 1 , − 1 ] A = [2, 1, -1] A=[2,1,1]

Y 11 = 0 , Y 12 = 0 , Y 13 = 1 Y_{11} = 0, Y_{12} = 0, Y_{13} = 1 Y11=0,Y12=0,Y13=1
A 11 = 2 , A 12 = 1 , A 13 = − 1 A_{11} = 2, A_{12} = 1, A_{13} = -1 A11=2,A12=1,A13=1

然后当计算

∂ H ∂ A 13 = − ∑ k = 1 C Y 1 k ∂ log ⁡ ( σ ( A 1 k ) ) ∂ A 13 = − Y 11 ∂ log ⁡ ( σ ( A 11 ) ) ∂ A 13 − Y 12 ∂ log ⁡ ( σ ( A 12 ) ) ∂ A 13 − Y 13 ∂ log ⁡ ( σ ( A 13 ) ) ∂ A 13 = − 0 − 0 − 1 ( 1 − σ ( A 13 ) ) = σ ( A 13 ) − 1 = σ ( A 13 ) − Y 13 \begin{align*} \frac{\partial H}{\partial A_{13}} &= - \sum\limits_{k=1}^{C} Y_{1k} \frac{\partial \log(\sigma(A_{1k}))}{\partial A_{13}}\\ &=-Y_{11}\frac{\partial \log(\sigma(A_{11}))}{\partial A_{13}}-Y_{12}\frac{\partial \log(\sigma(A_{12}))}{\partial A_{13}}-Y_{13}\frac{\partial \log(\sigma(A_{13}))}{\partial A_{13}}\\ &= -0-0-1(1 - \sigma(A_{13})) \\ &= \sigma(A_{13}) - 1 \\ &= \sigma(A_{13}) - Y _{13}\\ \end{align*} A13H=k=1CY1kA13log(σ(A1k))=Y11A13log(σ(A11))Y12A13log(σ(A12))Y13A13log(σ(A13))=001(1σ(A13))=σ(A13)1=σ(A13)Y13

梯度的推导(YouTube的证明)
  • Softmax函数是一个向量。
  • 每个元素 e z k ∑ c = 1 C e z c \frac{e^{z_k}}{\sum\limits_{c=1}^{C} e^{z_c}} c=1Cezcezk由于分母的原因依赖于所有输入元素。
  • 向量关于向量的梯度是一个矩阵。
  • 为了简化和巩固这个概念,让我们通过查看一个大小为 3 3 3的向量来使其更具体:

( z 1 z 2 z 3 ) → ( e z 1 e z 1 + e z 2 + e z 3 e z 2 e z 1 + e z 2 + e z 3 e z 3 e z 1 + e z 2 + e z 3 ) = ( a 1 a 2 a 3 ) = ( y 1 ^ y 2 ^ y 3 ^ ) \begin{pmatrix} z_1\\ z_2\\ z_3 \end{pmatrix} \rightarrow \begin{pmatrix} \frac{e^{z_1}}{e^{z_1} + e^{z_2} + e^{z_3}}\\ \frac{e^{z_2}}{e^{z_1} + e^{z_2} + e^{z_3}}\\ \frac{e^{z_3}}{e^{z_1} + e^{z_2} + e^{z_3}} \end{pmatrix} = \begin{pmatrix} a_1\\ a_2\\ a_3 \end{pmatrix} = \begin{pmatrix} \hat{y_1}\\ \hat{y_2}\\ \hat{y_3} \end{pmatrix} z1z2z3 ez1+ez2+ez3ez1ez1+ez2+ez3ez2ez1+ez2+ez3ez3 = a1a2a3 = y1^y2^y3^

矩阵的对角元素会发生什么?我们有导数关于分子中相同元素。例如,对于 ∂ a 1 ∂ z 1 \frac{\partial a_1}{\partial z_1} z1a1我们得到:

∂ a 1 ∂ z 1 = e z 1 ( e z 1 + e z 2 + e z 3 ) − e z 1 e z 1 ( e z 1 + e z 2 + e z 3 ) ( e z 1 + e z 2 + e z 3 ) = e z 1 e z 1 + e z 2 + e z 3 ⋅ e z 1 + e z 2 + e z 3 − e z 1 e z 1 + e z 2 + e z 3 = a 1 ( 1 − a 1 ) \frac{\partial a_1}{\partial z_1} = \frac{e^{z_1}(e^{z_1} + e^{z_2} + e^{z_3}) - e^{z_1}e^{z_1}}{(e^{z_1} + e^{z_2} + e^{z_3})(e^{z_1} + e^{z_2} + e^{z_3})} = \frac{e^{z_1}}{e^{z_1} + e^{z_2} + e^{z_3}} \cdot \frac{e^{z_1}+e^{z_2}+e^{z_3}-e^{z_1}}{e^{z_1} + e^{z_2} + e^{z_3}} = a_1(1 - a_1) z1a1=(ez1+ez2+ez3)(ez1+ez2+ez3)ez1(ez1+ez2+ez3)ez1ez1=ez1+ez2+ez3ez1ez1+ez2+ez3ez1+ez2+ez3ez1=a1(1a1)

所以我们得到了非常接近于sigmoid导数的东西。

对角线元素以外的元素会发生什么?例如,对于 ∂ a 1 ∂ z 2 \frac{\partial a_1}{\partial z_2} z2a1我们得到:

∂ a 1 ∂ z 2 = 0 ⋅ ( e z 1 + e z 2 + e z 3 ) − e z 1 e z 2 ( e z 1 + e z 2 + e z 3 ) 2 = − e z 1 e z 1 + e z 2 + e z 3 ⋅ e z 2 e z 1 + e z 2 + e z 3 = − a 1 a 2 \frac{\partial a_1}{\partial z_2} = \frac{0 \cdot (e^{z_1} + e^{z_2} + e^{z_3}) - e^{z_1}e^{z_2}}{(e^{z_1} + e^{z_2} + e^{z_3})^2} = -\frac{e^{z_1}}{e^{z_1} + e^{z_2} + e^{z_3}} \cdot \frac{e^{z_2}}{e^{z_1} + e^{z_2} + e^{z_3}} = -a_1a_2 z2a1=(ez1+ez2+ez3)20(ez1+ez2+ez3)ez1ez2=ez1+ez2+ez3ez1ez1+ez2+ez3ez2=a1a2

对于我们的3x3矩阵,我们将得到:

∂ a ∂ z = ( a 1 ( 1 − a 1 ) − a 1 a 2 − a 1 a 3 − a 2 a 1 a 2 ( 1 − a 2 ) − a 2 a 3 − a 3 a 1 − a 3 a 2 a 3 ( 1 − a 3 ) ) \frac{\partial \mathbf{a}}{\partial \mathbf{z}} = \begin{pmatrix} a_1(1 - a_1) & -a_1a_2 & -a_1a_3 \\ -a_2a_1 & a_2(1 - a_2) & -a_2a_3 \\ -a_3a_1 & -a_3a_2 & a_3(1 - a_3) \end{pmatrix} za= a1(1a1)a2a1a3a1a1a2a2(1a2)a3a2a1a3a2a3a3(1a3)

对于损失相对于最终输出的导数 - 我们有一个标量相对于向量的导数,所以结果也将是一个向量:

∂ L ∂ a L = [ ∂ ∂ a L 1 ( − ∑ c = 1 C y c log ⁡ a L c ) ⋮ ∂ ∂ a L C ( − ∑ c = 1 C y c log ⁡ a L c ) ] = − [ y 1 a L 1 ⋮ y C a L C ] \frac{\partial \mathcal{L}}{\partial a_L} = \begin{bmatrix} \frac{\partial}{\partial a_{L1}} \left(-\sum\limits_{c=1}^{C} y_c \log a_{Lc}\right) \\ \vdots \\ \frac{\partial}{\partial a_{LC}} \left(-\sum\limits_{c=1}^{C} y_c \log a_{Lc}\right) \end{bmatrix} = - \begin{bmatrix} \frac{y_1}{a_{L1}} \\ \vdots \\ \frac{y_C}{a_{LC}} \end{bmatrix} aLL= aL1(c=1CyclogaLc)aLC(c=1CyclogaLc) = aL1y1aLCyC

记住对于每个1-hot向量 y y y,我们只有一个元素等于1,其余都是0。

回到我们具体的 3 × 3 3 \times 3 3×3例子,并把所有东西放在一起,我们得到:

∂ L ∂ z L = ∂ L ∂ a L ∂ a L ∂ z L = − [ y 1 a 1 y 2 a 2 y 3 a 3 ] ( a 1 ( 1 − a 1 ) − a 1 a 2 − a 1 a 3 − a 2 a 1 a 2 ( 1 − a 2 ) − a 2 a 3 − a 3 a 1 − a 3 a 2 a 3 ( 1 − a 3 ) ) = − [ y 1 − a 1 ( y 1 + y 2 + y 3 ) y 2 − a 2 ( y 1 + y 2 + y 3 ) y 3 − a 3 ( y 1 + y 2 + y 3 ) ] = a − y \begin{align} \frac{\partial \mathcal{L}}{\partial z_L} &= \frac{\partial \mathcal{L}}{\partial a_L} \frac{\partial a_L}{\partial z_L} \\ &= -\begin{bmatrix} \frac{y_1}{a_1} \\ \frac{y_2}{a_2} \\ \frac{y_3}{a_3} \end{bmatrix} \begin{pmatrix} a_1(1 - a_1) & -a_1a_2 & -a_1a_3 \\ -a_2a_1 & a_2(1 - a_2) & -a_2a_3 \\ -a_3a_1 & -a_3a_2 & a_3(1 - a_3) \end{pmatrix} \\ &= -\begin{bmatrix} y_1 - a_1(y_1 + y_2 + y_3) & y_2 - a_2(y_1 + y_2 + y_3) & y_3 - a_3(y_1 + y_2 + y_3) \end{bmatrix} \\ &= \mathbf{a} - \mathbf{y} \end{align} zLL=aLLzLaL= a1y1a2y2a3y3 a1(1a1)a2a1a3a1a1a2a2(1a2)a3a2a1a3a2a3a3(1a3) =[y1a1(y1+y2+y3)y2a2(y1+y2+y3)y3a3(y1+y2+y3)]=ay

注意这里的 a \mathbf{a} a y \mathbf{y} y是向量,而不是标量。

交叉熵损失的示例

为了说明交叉熵损失,让我们考虑一个具体的例子,用一个小数据集。假设我们有一个简单的分类问题,有三个类别(C=3),我们正在处理两个样本的批次( N = 2 N=2 N=2)。这两个样本的模型原始输出分数( A A A)和相应的真实标签( Y Y Y)可能如下所示:

  • 两个样本的原始模型输出( A A A):

    • 样本 1: [ 2.0 , 1.0 , 0.1 ] [2.0, 1.0, 0.1] [2.0,1.0,0.1]
    • 样本 2: [ 0.1 , 2.0 , 1.9 ] [0.1, 2.0, 1.9] [0.1,2.0,1.9]
  • 真实类别分布( Y Y Y,独热编码):

    • 样本 1: [ 0 , 1 , 0 ] [0, 1, 0] [0,1,0] (类别 2 是真实类别)
    • 样本 2: [ 1 , 0 , 0 ] [1, 0, 0] [1,0,0] (类别 1 是真实类别)

让我们逐步计算这个例子的交叉熵损失:

1. 应用 Softmax

首先,我们对原始输出应用 softmax 函数,以获得每个类别的预测概率。

对于样本 1,softmax 计算如下:
σ ( A 1 ) = [ e 2.0 e 2.0 + e 1.0 + e 0.1 , e 1.0 e 2.0 + e 1.0 + e 0.1 , e 0.1 e 2.0 + e 1.0 + e 0.1 ] \sigma(A_1) = \left[\frac{e^{2.0}}{e^{2.0} + e^{1.0} + e^{0.1}}, \frac{e^{1.0}}{e^{2.0} + e^{1.0} + e^{0.1}}, \frac{e^{0.1}}{e^{2.0} + e^{1.0} + e^{0.1}}\right] σ(A1)=[e2.0+e1.0+e0.1e2.0,e2.0+e1.0+e0.1e1.0,e2.0+e1.0+e0.1e0.1]

对于样本 2,类似地:
σ ( A 2 ) = [ e 0.1 e 0.1 + e 2.0 + e 1.9 , e 2.0 e 0.1 + e 2.0 + e 1.9 , e 1.9 e 0.1 + e 2.0 + e 1.9 ] \sigma(A_2) = \left[\frac{e^{0.1}}{e^{0.1} + e^{2.0} + e^{1.9}}, \frac{e^{2.0}}{e^{0.1} + e^{2.0} + e^{1.9}}, \frac{e^{1.9}}{e^{0.1} + e^{2.0} + e^{1.9}}\right] σ(A2)=[e0.1+e2.0+e1.9e0.1,e0.1+e2.0+e1.9e2.0,e0.1+e2.0+e1.9e1.9]

2. 计算交叉熵损失

接下来,我们计算每个样本的交叉熵损失。单个样本的损失由下式给出:
H ( A i , Y i ) = − ∑ c = 1 C Y i c log ⁡ ( σ ( A i c ) ) H(A_i, Y_i) = -\sum_{c=1}^{C} Y_{ic} \log(\sigma(A_{ic})) H(Ai,Yi)=c=1CYiclog(σ(Aic))

对于样本 1:
H ( A 1 , Y 1 ) = − [ 0 × log ⁡ ( σ ( A 11 ) ) + 1 × log ⁡ ( σ ( A 12 ) ) + 0 × log ⁡ ( σ ( A 13 ) ) ] H(A_1, Y_1) = -[0 \times \log(\sigma(A_{11})) + 1 \times \log(\sigma(A_{12})) + 0 \times \log(\sigma(A_{13}))] H(A1,Y1)=[0×log(σ(A11))+1×log(σ(A12))+0×log(σ(A13))]

对于样本 2:
H ( A 2 , Y 2 ) = − [ 1 × log ⁡ ( σ ( A 21 ) ) + 0 × log ⁡ ( σ ( A 22 ) ) + 0 × log ⁡ ( σ ( A 23 ) ) ] H(A_2, Y_2) = -[1 \times \log(\sigma(A_{21})) + 0 \times \log(\sigma(A_{22})) + 0 \times \log(\sigma(A_{23}))] H(A2,Y2)=[1×log(σ(A21))+0×log(σ(A22))+0×log(σ(A23))]

3. 计算平均交叉熵损失

最后,我们计算这些损失的平均值,以得到批次的平均交叉熵损失:
meancrossentropyloss = H ( A 1 , Y 1 ) + H ( A 2 , Y 2 ) 2 \text{meancrossentropyloss} = \frac{H(A_1, Y_1) + H(A_2, Y_2)}{2} meancrossentropyloss=2H(A1,Y1)+H(A2,Y2)

4. 交叉熵损失的反向传播

对于反向传播,交叉熵损失相对于应用 softmax 之前的原始模型输出的梯度由下式给出:
∂ Loss ∂ A = σ ( A ) − Y N \frac{\partial \text{Loss}}{\partial A} = \frac{\sigma(A) - Y}{N} ALoss=Nσ(A)Y

对于批次中的每个样本,我们计算:

  • 对于样本 1: σ ( A 1 ) − Y 1 2 \frac{\sigma(A_1) - Y_1}{2} 2σ(A1)Y1
  • 对于样本 2: σ ( A 2 ) − Y 2 2 \frac{\sigma(A_2) - Y_2}{2} 2σ(A2)Y2

这给了我们需要通过网络反向传播的梯度。

基于计算:

  • 两个样本的 softmax 概率大约为:

    • 样本 1: [ 0.659 , 0.242 , 0.099 ] [0.659, 0.242, 0.099] [0.659,0.242,0.099]
    • 样本 2: [ 0.073 , 0.487 , 0.440 ] [0.073, 0.487, 0.440] [0.073,0.487,0.440]
  • 两个样本的交叉熵损失为:

    • 样本 1: 1.417
    • 样本 2: 2.620
  • 这个批次的平均交叉熵损失大约为 2.019。

  • 损失相对于原始模型输出( A A A)的梯度为:

    • 对于样本 1: [ 0.330 , − 0.379 , 0.049 ] [0.330, -0.379, 0.049] [0.330,0.379,0.049]
    • 对于样本 2: [ − 0.464 , 0.243 , 0.220 ] [-0.464, 0.243, 0.220] [0.464,0.243,0.220]

这些结果给出了我们使用 softmax 函数得到的每个类别的预测概率,每个样本的个别交叉熵损失,批次的整体平均交叉熵损失,以及反向传播所需的梯度。梯度中的负值指示了我们应该调整模型参数以减少损失的方向,而正值则建议相反的方向。

class CrossEntropyLoss:def softmax(self, x):# 通过在每个输入向量中减去最大值来改善 softmax 的数值稳定性。# 这通过指数化大的正数来防止潜在的溢出。e_x = np.exp(x - np.max(x, axis=1, keepdims=True))return e_x / e_x.sum(axis=1, keepdims=True)def forward(self, A, Y):self.A = Aself.Y = Yself.softmax = self.softmax(A)crossentropy = -Y * np.log(self.softmax)# 在批次上平均损失L = np.sum(crossentropy) / A.shape[0]return Ldef backward(self):# 计算损失相对于对数(预 softmax 激活)A的梯度# 这个梯度还包括在批次上的平均dLdA = (self.softmax - self.Y) / self.A.shape[0]return dLdA

参考资料:

  • 在YouTube上观看视频
  • CMU_11785_深度学习导论

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2822092.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

力扣SQL50 进店却未进行过交易的顾客 查询

Problem: 1581. 进店却未进行过交易的顾客 文章目录 思路Code 思路 👨‍🏫 山山山林老木 左连接查询筛选 transation_id 为 null 的值group by customer_id Code select v.customer_id ,count(customer_id) count_no_trans from Visits v left jo…

java找工作之JavaWeb(一)

JavaWeb 一个web应用有多部份组成(静态web,动态web) html,css,jsjsp,servletjava程序jar包配置文件(Properties) web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一…

3694-51-7,3,5-Dinitro-1,2-phenylenediamine,合成其他化合物的重要中间体

您好,欢迎来到新研之家 文章关键词:3694-51-7,3,5-Dinitro-1,2-phenylenediamine,3,5-二硝基-1,2-苯二胺;3,5-二硝基苯-1,2-二胺 一、基本信息 【产品简介】:3,5-Dinitro-1,2-phenylenediamine, with the molecular…

一台工控机的能量

使用Docker搭建EPICS的IOC记录 Zstack EPICS Archiver在小课题组的使用经验 以前电子枪调试,用一台工控机跑起束测后台,这次新光源用的电子枪加工回来又是测试,又是用一台工控机做起重复的事,不过生命在于折腾,重复的…

openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划

文章目录 openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划 openGauss学习笔记-232 openGauss性能调优-系统调优-资源负载管理-资源管理准备-资源规划 完成资源负载管理功能配置前,需要先根据业务模型完成租户资源的规划。业…

【白嫖8k买的机构vip教程】Appium自动化(3):Appium-Desktop界面介绍

Appium-Desktop主界面包含三个菜单Simple、Advanced、Presets Simple界面: Host设置Appium server的ip地址,本地调试可以将ip地址修改为127.0.0.1;Port设置端口号,默认是4723不用修改Start Server 启动 Appium serverEdit Confi…

[SUCTF 2019]EasyWeb --不会编程的崽

个人认为&#xff0c;这题还算有些东西。先来看源码 <?php function get_the_flag(){// webadmin will remove your upload file every 20 min!!!! $userdir "upload/tmp_".md5($_SERVER[REMOTE_ADDR]);if(!file_exists($userdir)){mkdir($userdir);}if(!empty…

【力扣白嫖日记】550.游戏玩法分析IV

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 550.游戏玩法分析IV 表&#xff1a;Activity 列名类型player_idintdevice_idintevent_datedategames_played…

leetcode--接雨水(双指针法,动态规划,单调栈)

目录 方法一&#xff1a;双指针法 方法二&#xff1a;动态规划 方法三&#xff1a;单调栈 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 黑色的是柱子&#xff0c;蓝色的是雨水&#xff0c;我们先来观察一下雨水的分布情况: 雨水落在凹槽之间&#xff0c;在一个凹槽的…

第三百七十五回

文章目录 1. 概念介绍2. 使用方法3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"相关的内容&#xff0c;本章回中将介绍如何让Text组件中的文字自动换行.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

《C++进阶--10.多态》

目录 10. 多态 10.1 多态的基本概念 10.2 多态案例一-计算器类 10.3 纯虚函数和抽象类 10.4 多态案例二-制作饮品 10.5 虚析构和纯虚析构 10.6 多态案例三-电脑组装 10. 多态 10.1 多态的基本概念 多态是C面向对象三大特性之一 多态分为两类 静态多态: 函数重载 和 运算…

HTML---Ajax

文章目录 目录 文章目录 前言 一.Ajax概述 二.原生创建Ajax 三,使用Jquery处理Ajax 总结 一.Ajax概述 AJAX&#xff08;Asynchronous Javascript And XML&#xff09;是一种创建交互式网页应用的网页开发技术。它使用Javascript语言与服务器进行异步交互&#xff0c;可以传…

matplotlib.animation 3d姿态动画

目录 演示效果&#xff1a; 演示代码&#xff1a; 保存为gif 演示效果&#xff1a; 演示代码&#xff1a; import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from matplotlib.animation import FuncAnimation# 定义人体关键点…

flutterandroidx支持,【工作经验分享】

基于Linux的pc启动过程 我们都知道&#xff0c;所有的程序软件包括操作系统都是运行在内存中的&#xff0c;然而我们的操作系统一般是存放在硬盘上的&#xff0c;当我们按下开机键的时候&#xff0c;此时内存中什么程序也没有&#xff0c;因此需要借助某种方式&#xff0c;将操…

kubectl 陈述式资源管理方法

目录 陈述式资源管理方法 项目的生命周期 1.创建kubectl create命令 2.发布kubectl expose命令 service的4的基本类型 查看pod网络状态详细信息和 Service暴露的端口 查看关联后端的节点 ​编辑 查看 service 的描述信息 ​编辑在 node01 节点上操作&#xff0c;查看…

Northwestern University-844计算机科学与技术/软件工程-复试注意事项【考研复习】

本文提到的西北大学是位于密歇根湖泊畔的西北大学。西北大学&#xff08;英语&#xff1a;Northwestern University&#xff0c;简称&#xff1a;NU&#xff09;是美国的一所著名私立研究型大学。它由九人于1851年创立&#xff0c;目标是建立一所为西北领地地区的人服务的大学。…

Android13 Audio框架

一、Android 13音频代码结构 1、framework: android/frameworks/base 1.AudioManager.java &#xff1a;音频管理器&#xff0c;音量调节、音量UI、设置和获取参数等控制流的对外API 2.AudioService.java &#xff1a;音频系统服务&#xff08;java层&#xff09;&#xff0c…

在vue2中使用饼状图

1.引入vue2和echarts <script src"https://cdn.jsdelivr.net/npm/vue2.7.14/dist/vue.js"></script> <script src"https://cdn.jsdelivr.net/npm/echarts5.4.0/dist/echarts.min.js"></script> 2.1 补充基本的body内容 <div id…

今年Android面试必问的这些技术面,2024Android常见面试题

都说程序员是在吃青春饭&#xff0c;这一点的确有一点对的成分&#xff0c;以前我不这么认为&#xff0c;但随着年龄的增长&#xff0c;事实告诉我的确是这样的&#xff0c;过了30以后&#xff0c;就会发现身体各方面指标下降&#xff0c;体力和身心上都多少有点跟不上了&#…

仿牛客网项目---私信列表和发送列表功能的实现

这篇文章我们来讲一下我的这个项目的另外一个功能&#xff1a;私信列表和发送列表功能。 先来设计DAO层。 Mapper public interface MessageMapper {// 查询当前用户的会话列表,针对每个会话只返回一条最新的私信.List<Message> selectConversations(int userId, int of…