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

深度学习day6- 损失函数和BP算法1

六 损失函数

1 线性回归损失函数

1.MAE损失

MAE(Mean Absolute Error,平均绝对误差): L1-Loss,通过对预测值和真实值之间的差取绝对值再平均来衡量他们之间的差异。

$$
\text{MAE} = \frac{1}{n} \sum_{i=1}^{n} \left| y_i - \hat{y}_i \right|
$$

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

特点:1.鲁棒性:与MSE相比,MAE对异常值(outliers)更为鲁棒

2.物理意义直观

应用:需要对误差进行线性度量的情况,数据中可能存在异常值时可以避免对异常值的过度惩罚

torch.nn.L1Loss

import torch
def test01():
    l1_loss_fn=torch.nn.L1Loss()
    y_true=torch.tensor([1,2,3,4,5,6],dtype=torch.float32)
    y_pred=torch.tensor([10,20,30,40,50,60],dtype=torch.float32)
    loss=l1_loss_fn(y_true,y_pred)
    print(loss)
​
if __name__ == "__main__":
    test01()

2.MSE损失-均方差损失

MSE(Mean Squared Error,均方误差)通过对预测值和真实值之间的误差先平方再平均,来衡量预测值与真实值之间的差异。

$$
\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2
$$

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

特点:1.平方误差:MSE对较大误差施加更大惩罚,对异常值更加敏感

2.凸性:MSE是凸函数,具有一个唯一的全局最小值

应用:神经网络

torch.nn.MSELoss

import torch
def test01():
    l1_loss_fn=torch.nn.MSELoss()
    y_true=torch.tensor([1,2,3,4,5,6],dtype=torch.float32)
    y_pred=torch.tensor([2,3,4,5,6,8],dtype=torch.float32)
    loss=l1_loss_fn(y_true,y_pred)
    print(loss)
​
if __name__ == "__main__":
    test01()

3.SmoothL1Loss

SmoothL1Loss(平滑)可以做到在损失较小时表现为 L2 损失,而在损失较大时表现为 L1 损失。

$$
\text{SmoothL1Loss}(x) = \begin{cases} 0.5 \cdot x^2, & \text{if } |x| < 1 \\ |x| - 0.5, & \text{otherwise} \end{cases}
$$

$$
x = y_i - \hat{y}_i,表示预测值和真实值之间的误差
$$

$$
\\L=\frac{1}{n} \sum_{i=1}^{n} L_{i},表示所有样本的平均损失
$$

特点:1.平滑过渡:误差较小时,损失函数表现为L2Loss(平方惩罚);误差较大时,损失函数向L1Loss过渡。保证了对大误差的控制,也不会对异常值过度敏感

2.稳健性:对异常值更加稳健,在小误差范围内提供了较好的优化效果

应用:需要对大误差进行一定控制,又不会完全忽略小误差的回归任务。

目标检测任务中的边界框回归,如Faster R-CNN

import torch
def test01():
    l1_loss_fn=torch.nn.SmoothL1Loss()
    #l1_loss_fn=torch.nn.functional.smooth_l1_loss
    #两种写法
    y_true=torch.tensor([1.0, 2.0, 3.0, 4.0],dtype=torch.float32)
    y_pred=torch.tensor([3.0, 2.5, 3.5, 4.5],dtype=torch.float32)
    loss=l1_loss_fn(y_true,y_pred)
    print(loss)
​
​
if __name__ == "__main__":
    test01()

2 CrossEntropyLoss-交叉熵损失函数

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

$$
\text{CrossEntropyLoss}(y, \hat{y}) = - \sum_{i=1}^{C} y_i \log(\hat{y}_i)
$$

$$
C:类别总数;y:真实标签的one-hot编码向量,表示真实类别;\hat{y} 是模型的输出(经过 softmax 后的概率分布)
$$

$$
y_i是真实类别的第 i 个元素(0 或 1);\hat{y}_i 是预测的类别概率分布中对应类别 i 的概率。
$$

特点:将网络输出的logits通过softmax函数映射到(0,1)区间,概率之和累加为1,选择概率最大(值对应最大的)节点作为预测目标类别

import torch
def test01():
    l1_loss_fn=torch.nn.CrossEntropyLoss()
    #l1_loss_fn=torch.nn.functional.cross_entropy
    one_hot=torch.tensor([[1.5, 2.0, 0.5], [0.5, 1.0, 1.5]])
    y_true=torch.tensor([1,2])
    loss=l1_loss_fn(one_hot,y_true)
    print(loss)
​
if __name__ == "__main__":
    test01()

3 BCELoss-二分类交叉熵损失函数

对于二分类问题,CrossEntropyLoss 的简化版本称为二元交叉熵(Binary Cross-Entropy Loss),输出层使用sigmoid激活函数进行二分类

$$
\text{BinaryCrossEntropy}(y, \hat{y}) = - \left[ y \log(\hat{y}) + (1 - y) \log(1 - \hat{y}) \right]
$$

log底数默认为e,y时真实类别目标,那么L:

$$
L = -log(sigmoid激活值), 当y=1 \\ L = -log(1-sigmoid激活值), 当y=0
$$

#二分类交叉熵损失函数
import torch
def test01():
    #样本
    x=torch.tensor([[0.1,0.2,0.3],
                    [0.4,0.5,0.6],
                    [0.41,0.52,0.63],
                    [0.41,0.51,0.6]])
    #权重
    w=torch.tensor([[0.11,0.22,0.33],
                    [0.44,0.55,0.66],
                    [0.41,0.53,0.6],
                    [0.4,0.1,0.6]])
    #偏置
    b=0.1
    #预测
    y=w*x+b
    #激活
    y_pred=torch.nn.functional.sigmoid(y)
    print(y_pred)
    y_true=torch.tensor([[1,0,0],[1,0,0],[0,0,1],[0,0,1]],dtype=torch.float32)
    loss_fn=torch.nn.BCELoss()
    loss_fn=torch.nn.functional.binary_cross_entropy
    loss=loss_fn(y_pred,y_true)
    print(loss)
    
​
if __name__ == "__main__":
    test01()

4 总结

输出层-softmax多分类,使用交叉熵损失函数

输出层-sigmid二分类,使用二分类交叉熵损失函数

线性回归-smoothL1损失函数/均方差损失L2Loss

七 BP算法

误差反向传播算法(Back Propagation)是训练多层神经网络的学习算法中最杰出的代表。

通常说BP网络时,一般是指用BP算法训练的多层前馈神经网络。

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

1.前向传播:正向计算得到预测值

2.计算损失:通过损失函数L计算预测值和真实值的差距

3.梯度计算:计算损失函数L对每个权重和偏置的梯度(核心)

4.更新参数:得到每层梯度,使用梯度下降算法来更新每层的权重和偏置,使损失逐渐减小

5.迭代训练:将以上步骤重复,直到损失函数收敛或者达到预定的停止条件

1 前向传播

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

一个简单的三层神经网络(输入层、隐藏层、输出层)前向传播的基本步骤:

1.输入层到隐藏层:

$$
给定输入 x 和权重矩阵 W_1 及偏置向量 b_1,隐藏层的输出(激活值)计算如下:z^{(1)} = W_1 \cdot x + b_1
$$

$$
将z^{(1)} 通过激活函数 \sigma进行激活:a^{(1)} = \sigma(z^{(1)})
$$

2.隐藏层到输出层:

$$
隐藏层的输出 a^{(1)} 通过输出层的权重矩阵 W_2和偏置 b_2生成最终的输出:z^{(2)} = W_2 \cdot a^{(1)} + b_2
$$

$$
输出层的激活值 a^{(2)} 是最终的预测结果:y_{\text{pred}} = a^{(2)} = \sigma(z^{(2)})
$$

前向传播的主要作用是:

1.计算神经网络的输出结果,用于预测或计算损失

2.在反向传播中使用,通过计算损失函数相对于每个参数的梯度来优化网络

2 反向传播

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

1.原理

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

2.链式法则

通过分层的计算求得损失函数相对于每个参数的梯度

$$
\mathrm{f(x)=\frac{1}{1+e^{-(wx+b)}}}
$$

其中 x 是输入数据,w 是权重,b 是偏置。

①.函数分解

②.链式求导

复合函数 𝑓(𝑥; 𝑤, 𝑏)关于参数 𝑤 和 𝑏的导数可以通过 𝑓(𝑥; 𝑤, 𝑏) 与参数 𝑤 和 𝑏之间所有导数连乘得到

$$
\frac{\partial f(x;w,b)}{\partial w}=\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial h_1}\frac{\partial h_1}{\partial w} \\ \\ \frac{\partial f(x;w,b)}{\partial b}=\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial b}
$$

以w为例,当 𝑥 = 1, 𝑤 = 0, 𝑏 = 0时,可以得到:

$$
\begin{aligned} \frac{\partial f(x;w,b)}{\partial w}|_{x=1,w=0,b=0}& =\frac{\partial f(x;w,b)}{\partial h_6}\frac{\partial h_6}{\partial h_5}\frac{\partial h_5}{\partial h_4}\frac{\partial h_4}{\partial h_3}\frac{\partial h_3}{\partial h_2}\frac{\partial h_2}{\partial h_1}\frac{\partial h_1}{\partial w} \\ &=1\times-0.25\times1\times1\times-1\times1\times1 \\ &=0.25 \end{aligned}
$$

③.代码
import torch
def test01():
    #链式求导
    x=torch.tensor(1.,requires_grad=True)
    w=torch.tensor(0.,requires_grad=True)
    b=torch.tensor(0.,requires_grad=True)
    fx=(1+torch.exp(-(w*x+b)))**(-1)
    fx.backward()#fx对w求导 fx对b求导
    print(w.grad)
    print(b.grad)
​
​
if __name__ == "__main__":
    test01()

3.重要性

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

4.案例

①.数据准备

$$
i_1=0.05, i_2=0.10 代表输入层输入数据的2个特征;w_1=0.15, w_2=0.20代表的是输入数据映射到h_1的权重参数;
$$

$$
w_3=0.25, w_4=0.30代表输入数据映射到h_2的权重参数;b1=0.35,b2=0.60分别代表输入层到隐藏层、隐藏层到输出层的偏置
$$

$$
w_5=0.40, w_6=0.45代表隐藏层的神经元映射到o_1的权重参数;w_7=0.50, w_8=0.55代表的是隐藏层的神经元映射到o_2的权重参数;
$$

$$
o_1下面标注的0.01表示target为0.01,o_2下面标注的0.99表示target为0.99;
$$

②.神经元计算

$$
\mathrm{h}_{1}=\mathrm{w}_{1}*\mathrm{i}_{1}+\mathrm{w}_{2}*\mathrm{i}_{2}+\mathrm{b}_{1}\quad =0.15 * 0.05 + 0.20 * 0.10 + 0.35 =0.3775 \\ k_{1}=sigmoid(h1)=sigmoid(0.3775)=0.5933
$$

$$
\mathrm{h}_{2}=\mathrm{w}_{3}*\mathrm{i}_{1}+\mathrm{w}_{4}*\mathrm{i}_{2}+\mathrm{b}_{1}\quad =0.25 * 0.05 + 0.30 * 0.10 + 0.35 =0.3925 \\ k_{2}=sigmoid(h2)=sigmoid(0.3925)=0.5969
$$

$$
\mathrm{o}_{1}=\mathrm{w}_{5}*\mathrm{k}_{1}+\mathrm{w}_{6}*\mathrm{k}_{2}+\mathrm{b}_{2}\quad =0.40 * 0.5933 + 0.45 * 0.5969 + 0.60 =1.1059 \\ m_{1}=sigmoid(o1)=sigmoid(1.1059)=0.7514
$$

$$
\mathrm{o}_{2}=\mathrm{w}_{7}*\mathrm{k}_{1}+\mathrm{w}_{8}*\mathrm{k}_{2}+\mathrm{b}_{2}\quad =0.50 * 0.5933 + 0.55 * 0.5969 + 0.60 =1.2249 \\ m_{2}=sigmoid(o2)=sigmoid(1.2249)=0.7729
$$

③.损失计算

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

$$
MSELoss = \frac{1}{2}((\mathrm{m}_{1}\mathrm{-target}_{1})^{2}+((\mathrm{m}_{2}\mathrm{-target}_{2})^{2}) \\ = \frac{1}{2}((0.7514-0.01)^{2}+((0.7729-0.99)^{2}) =0.2984
$$

④.梯度计算

w5权重的梯度:

$$
\begin{aligned} \frac{\partial\mathrm{L}}{\partial\mathrm{w}_{5}}& =\frac{\partial\mathrm{L}}{\partial\mathrm{o1}_{1}}*\frac{\partial\mathrm{m}_{1}}{\partial\mathrm{o}_{1}}*\frac{\partial\mathrm{o}_{1}}{\partial\mathrm{w}_{5}} \\ &=(\mathrm{m}_{1}-\mathrm{target}_{1})*\mathrm{sigmoid}(\mathrm{o}_{1})*\left(1-\mathrm{sigmoid}(\mathrm{o}_{1})\right)*\mathrm{k}_{1} \\ &=(0.7514-0.01)*sigmoid(1.1059)*\left(1-sigmoid(1.1059)\right)*0.5933 \\ &=0.0822 \end{aligned}
$$

w7权重的梯度:

$$
\begin{aligned} \frac{\partial\mathrm{L}}{\partial\mathrm{w}_7}& =\frac{\partial\mathrm{L}}{\partial\mathrm{m}_2}*\frac{\partial\mathrm{m}_2}{\partial\mathrm{o}_2}*\frac{\partial\mathrm{o}_2}{\partial\mathrm{w}_7} \\ &=(\mathrm{m}_{2}-\mathrm{target}_{2})*\mathrm{sigmoid}(\mathrm{o}_{2})*\left(1-\mathrm{sigmoid}(\mathrm{o}_{2})\right)*\mathrm{k}_{1} \\ &=(0.7729-0.99)*sigmoid(1.2249)*\left(1-sigmoid(1.2249)\right)*0.5933 \\ &=-0.0226 \end{aligned}
$$

w1权重的梯度:

⑤.参数更新

假设学习率是0.5

$$
w_5=0.40-0.5*0.0822=0.3589 \\ w_7=0.50+0.5*0.0226=0.5113 \\ w_1=0.15-0.5*0.0004=0.1498
$$

⑥.代码实现
#反向传播API实现
import torch
def train():
    #前向传播
    i=torch.tensor([[0.05,0.1]])
    model1=torch.nn.Linear(2,2)
    model1.weight.data=torch.tensor([[0.15,0.20],[0.25,0.30]])
    model2=torch.nn.Linear(2,2)
    model2.weight.data=torch.tensor([[0.40,0.45],[0.50,0.55]])
    model1.bias.data=torch.tensor([0.35,0.35])
    model2.bias.data=torch.tensor([0.60,0.60])
​
    l1_l2=model1(i)
    h1_h2=torch.sigmoid(l1_l2)
​
    l3_l4=model2(h1_h2)
    o1_o2=torch.sigmoid(l3_l4)
​
    #反向传播
    o1_o2_true=torch.tensor([[0.01,0.99]])
    #mse=torch.nn.MSELoss()
    loss=torch.sum((o1_o2 - o1_o2_true) ** 2) / 2
​
    loss.backward()
    print(model1.weight.grad)
    print(model2.weight.grad)
​
train()
#反向传播的标准写法1
import torch
class mynet(torch.nn.Module):
    def __init__(self):
       super(mynet,self).__init__()
       #定义网络结构
       self.linear1=torch.nn.Linear(2,2)
       self.linear2=torch.nn.Linear(2,2)
       self.activation=torch.sigmoid
       #初始化参数
       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=self.activation(x)
        x=self.linear2(x)
        x=self.activation(x)
        return x
    
def train():
    model=mynet()
    optimizer=torch.optim.SGD(model.parameters(),lr=0.1)
    input=torch.tensor([[0.05,0.10]])
    target=torch.tensor([0.01,0.99])
    pred=model(input)#torch.nn.Module 父类已经实现了调用forward前向传播函数
    #optimizer=torch.optim.SGD(model.parameters(),lr=0.1)#梯度清零,更新w
    mes=torch.nn.MSELoss()
    loss=mes(pred,target)
    loss.backward()
    print(model.linear1.weight.grad)
    print(model.linear2.weight.grad)
    optimizer.step()
    model.linear1.weight.grad
​
train()
#反向传播的标准写法2
import torch

class mynet(torch.nn.Module):
    def __init__(self):
       super(mynet,self).__init__()
       #定义网络结构
       self.hide1=torch.nn.Sequential(torch.nn.Linear(2,2),torch.nn.Sigmoid())
       self.out=torch.nn.Sequential(torch.nn.Linear(2,2),torch.nn.Sigmoid())
       
       #初始化参数
       self.hide1[0].weight.data=torch.tensor([[0.15,0.20],[0.25,0.30]])
       self.out[0].weight.data=torch.tensor([[0.40,0.45],[0.50,0.55]])
       self.hide1[0].bias.data=torch.tensor([0.35,0.35])
       self.out[0].bias.data=torch.tensor([0.60,0.60])

    def forward(self,x):
        x=self.hide1(x)
        x=self.out(x)
        return x
    
def train():
    model=mynet()
    optimizer=torch.optim.SGD(model.parameters(),lr=0.1)
    input=torch.tensor([[0.05,0.10]])
    target=torch.tensor([0.01,0.99])
    pred=model(input)#torch.nn.Module 父类已经实现了调用forward前向传播函数
    #optimizer=torch.optim.SGD(model.parameters(),lr=0.1)#梯度清零,更新w
    mes=torch.nn.MSELoss()
    loss=mes(pred,target)
    optimizer.zero_grad()
    loss.backward()
    print(model.hide1[0].weight.grad)#0是线性层 1是激活层
    print(model.out[0].weight.grad)
    optimizer.step()
    #model.linear1.weight.grad

train()


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

相关文章:

  • 【Maven】继承和聚合
  • python excel接口自动化测试框架!
  • Leetcode - 周赛424
  • 开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序源码助力品牌共建:价值、策略与实践
  • Ettercap工具使用说明
  • 代码美学2:MATLAB制作渐变色
  • 【从零开始的LeetCode-算法】3304. 找出第 K 个字符 I
  • 分层架构 IM 系统之 Entry 设计实现
  • 跨境电商搭建知识库能带来什么效益?
  • 栈和队列——考研笔记
  • 【论文复现】BERT论文解读及情感分类实战
  • 页面内容下载为pdf
  • 2、Three.js初步认识场景Scene、相机Camera、渲染器Renderer三要素
  • k8s网络服务
  • 【数据仓库 | Data Warehouse】数据仓库的四大特性
  • 为什么redis用跳表不用b+树,而mysql用b+树而不是跳表?
  • CTF之密码学(埃特巴什码 )
  • (0基础保姆教程)-JavaEE开课啦!--12课程(Spring MVC注解 + Vue2.0 + Mybatis)-实验10
  • Python 删除Word中的表格
  • Qt中CMakeLists.txt解释大全
  • Django Admin与Vue前后端分离开发实战教程
  • open-instruct - 训练开放式指令跟随语言模型
  • Docker部署MinIO与Python的结合:探索对象存储的新境界
  • 【AIGC】大模型面试高频考点-RAG中Embedding模型选型
  • 算法训练营day17(二叉树04:平衡二叉树,所有路径,左叶子和)
  • 单片机 WiFi 手机 APP