当前位置: 首页 > article >正文

深度学习——损失函数与BP算法

一、损失函数

1. 线性回归损失函数

1.1 MAE损失

MAE(Mean Absolute Error,平均绝对误差)通常也被称为 L1-Loss,通过对预测值和真实值之间的绝对差取平均值来衡量他们之间的差异。MAE的公式如下:

其中:

  • n 是样本的总数。
  • y_{i} 是第 i 个样本的真实值。
  • \hat{y}_{i} 是第 i 个样本的预测值
  • \left | y_{i}- \hat{y}_{i} \right | 是真实值和预测值之间的绝对误差。

特点

  1. 鲁棒性:与均方误差(MSE)相比,MAE对异常值(outliers)更为鲁棒,因为它不会像MSE那样对较大误差平方敏感。
  2. 物理意义直观:MAE以与原始数据相同的单位度量误差,使其易于解释。

应用场景: MAE通常用于需要对误差进行线性度量的情况,尤其是当数据中可能存在异常值时,MAE可以避免对异常值的过度惩罚。

1.2 MSE损失

MSE(Mean Squared Error,均方误差)通常也被称为L2Loss。通过对预测值和真实值之间的误差平方取平均值,来衡量预测值与真实值之间的差异。MSE的公式如下:

其中:

  • n 是样本的总数。
  • y_{i} 是第 i 个样本的真实值。
  • \hat{y}_{i} 是第 i 个样本的预测值。
  • \left ( y_{i}- \hat{y}_{i} \right )^{2} 是真实值和预测值之间的误差平方。

特点

  1. 平方惩罚:因为误差平方,MSE 对较大误差施加更大惩罚,所以 MSE 对异常值更为敏感。
  2. 凸性:MSE 是一个凸函数,这意味着它具有一个唯一的全局最小值,有助于优化问题的求解。

应用场景

MSE被广泛应用在神经网络中。

1.3 SmoothL1Loss

SmoothL1Loss可以做到在损失较小时表现为 L2 损失,而在损失较大时表现为 L1 损失。SmoothL1Loss 的公式如下:

其中,x 表示预测值和真实值之间的误差,即 x=y_{i}-\hat{y}_{i}

所有样本的平均损失为:

特点:

  1. 平滑过渡:当误差较小时,损失函数表现为 L2 Loss(平方惩罚);当误差较大时,损失函数逐渐向 L1 Loss过渡。这种平滑过渡既能对大误差有所控制,又不会对异常值过度敏感。
  2. 稳健性:对于异常值更加稳健,同时在小误差范围内提供了较好的优化效果。

应用场景:

SmoothL1Loss常用于需要对大误差进行一定控制但又不希望完全忽略小误差的回归任务。特别适用于目标检测任务中的边界框回归,如 Faster R-CNN 等算法中。

2. CrossEntropyLoss

交叉熵损失函数,使用在输出层使用softmax激活函数进行多分类时,一般都采用交叉熵损失函数。

对于多分类问题,CrossEntropyLoss 公式如下:

其中:

  • C 是类别的总数。
  •  y 是真实标签的one-hot编码向量,表示真实类别。
  • \hat{y} 是模型的输出(经过 softmax 后的概率分布)。
  • y_{i} 是真实类别的第 i个元素(0 或 1)。
  • \hat{y}_{i} 是预测的类别概率分布中对应类别 i 的概率。

特点:

Softmax 直白来说就是将网络输出的 logits 通过 softmax 函数,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。

3. BCELoss

二分类交叉熵损失函数,使用在输出层使用sigmoid激活函数进行二分类时。

对于二分类问题,CrossEntropyLoss 的简化版本称为二元交叉熵(Binary Cross-Entropy Loss),公式为:

log的底数一般默认为e,y是真实类别目标,根据公式可知L是一个分段函数 :  

 

以上损失函数是一个样本的损失值,总样本的损失值是求损失均值即可。  

4. 总结

  • 当输出层使用softmax多分类时,使用交叉熵损失函数;
  • 当输出层使用sigmoid二分类时,使用二分类交叉熵损失函数, 比如在逻辑回归中使用;
  • 当功能为线性回归时,使用smooth L1损失函数或均方差损失-L2 loss;

二、BP算法 

误差反向传播算法(BP)的基本步骤:

  1. 前向传播:正向计算得到预测值。
  2. 计算损失:通过损失函数L\left ( y_{pred},y_{true} \right ) 计算预测值和真实值的差距。
  3. 梯度计算:反向传播的核心是计算损失函数 L 对每个权重和偏置的梯度。
  4. 更新参数:一旦得到每层梯度,就可以使用梯度下降算法来更新每层的权重和偏置,使得损失逐渐减小。
  5. 迭代训练:将前向传播、梯度计算、参数更新的步骤重复多次,直到损失函数收敛或达到预定的停止条件。

1. 前向传播

前向传播(Forward Propagation)把输入数据经过各层神经元的运算并逐层向前传输,一直到输出层为止。

前向传播的主要作用是:

  1. 计算神经网络的输出结果,用于预测或计算损失。
  2. 在反向传播中使用,通过计算损失函数相对于每个参数的梯度来优化网络。

2. 反向传播

反向传播(Back Propagation,简称BP)通过计算损失函数相对于每个参数的梯度来调整权重,使模型在训练数据上的表现逐渐优化。反向传播结合了链式求导法则和梯度下降算法,是神经网络模型训练过程中更新参数的关键步骤。

2.1 原理

利用链式求导法则对每一层进行求导,直到求出输入层x的导数,然后利用导数值进行梯度更新

2.2. 链式法则

链式求导法则(Chain Rule)是微积分中的一个重要法则,用于求复合函数的导数。在深度学习中,链式法则是反向传播算法的基础,这样就可以通过分层的计算求得损失函数相对于每个参数的梯度。

2.3 重要性

反向传播算法极大地提高了多层神经网络训练的效率,使得训练深度模型成为可能。通过链式法则逐层计算梯度,反向传播可以有效地处理复杂的网络结构,确保每一层的参数都能得到合理的调整。

2.4 案例助解

2.4.1 数据准备

整体网络结构及神经元数据和权重参数如下图所示:

2.4.2 神经元计算

所以,我们可以得到如下数据:

计算h1的相关数据:

计算h2的相关数据:  

计算o1的相关数据:

计算o2的相关数据:

 所以,最终的预测结果分别为: 0.7514、0.7729

2.4.3 损失计算

预测值和真实值(target)进行比较计算损失:

得到损失是:0.2984  

2.4.4 梯度计算

接下来,我们进行梯度计算和参数更新

计算 w5 权重的梯度

计算 w7 权重的梯度

计算 w1 权重的梯度

2.4.5 参数更新

现在就可以进行权重更新了:假设学习率是0.5

2.4.6 代码实现
import torch
import torch.nn as nn
import torch.optim as optim


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.linear1 = nn.Linear(2, 2)
        self.linear2 = nn.Linear(2, 2)

        # 网络参数初始化
        self.linear1.weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]])
        self.linear2.weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]])
        self.linear1.bias.data = torch.tensor([0.35, 0.35])
        self.linear2.bias.data = torch.tensor([0.60, 0.60])

    def forward(self, x):

        x = self.linear1(x)
        x = torch.sigmoid(x)
        x = self.linear2(x)
        x = torch.sigmoid(x)

        return x


if __name__ == "__main__":

    inputs = torch.tensor([[0.05, 0.10]])
    target = torch.tensor([[0.01, 0.99]])

    # 获得网络输出值
    net = Net()
    output = net(inputs)

    # 计算误差
    loss = torch.sum((output - target) ** 2) / 2

    # 优化方法
    optimizer = optim.SGD(net.parameters(), lr=0.5)

    # 梯度清零
    optimizer.zero_grad()

    # 反向传播
    loss.backward()

    # 打印(w1-w8)观察w5、w7、w1 的梯度值是否与手动计算一致
    print(net.linear1.weight.grad.data)
    print(net.linear2.weight.grad.data)

    #更新梯度
    optimizer.step()
   
    # 打印更新后的网络参数
    print(net.state_dict())


http://www.kler.cn/a/421995.html

相关文章:

  • Java虚拟机(JVM)中的元空间(Metaspace)一些关键点的总结
  • 计算S=1!+2!+3!+…+N!的值:JAVA
  • JavaScript 键盘控制移动
  • Narya.ai正在寻找iOS工程师!#Mixlab内推
  • 使用go语言写一个脚本 实现WebSockt连接 用户发送a 得到返回b
  • 常见问题QA的前端代码
  • C#里怎么样快速判断一个数是否为2多少次方的数?
  • 小米-NLP算法工程师面试题
  • PostgreSQL添加PostGIS扩展和存储坐标
  • 【二分查找】
  • 编程语言中什么是框架?什么是Cocoa?Foundation.framework的底层实现?Swift如何引入ObjC框架?
  • ChatGPT和BERT区别和联系
  • PythonQt日常作业
  • 大数据新视界 -- Hive 与其他大数据工具的集成:协同作战的优势(下)(14/ 30)
  • IsaacSim以及IsaacLab的安装
  • 我的基金学习之路,从《解读基金——我的投资观与实践》开始
  • 【23种设计模式】单例模式:理论剖析与 Java 实践
  • Java学习分享
  • 中间件之Elasticsearch
  • 各种类型无人机性能及优缺点技术详解
  • 【乐企文件生成工程】搭建docker环境,使用docker部署工程
  • 【CSS in Depth 2 精译_800】附录A:CSS 选择器的含义及部分用法示例
  • 一次奇妙的getshell之旅
  • 数据结构--二叉树的创建和遍历
  • 28.100ASK_T113-PRO Linux+QT 显示一张照片
  • 6.824/6.5840 Lab 1: MapReduce