11.27 深度学习-损失函数+BP算法

发布于:2024-11-29 ⋅ 阅读:(26) ⋅ 点赞:(0)

# MAE损失 L1Loss 曼哈顿距离的 误差

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

# torch.nn.L1Loss

# 1. **鲁棒性**:与均方误差(MSE)相比,MAE对异常值(outliers)更为鲁棒,因为它不会像MSE那样对较大误差平方敏感。 均方误差有个平方

# 2. **物理意义直观**:MAE以与原始数据相同的单位度量误差,使其易于解释。

# **应用场景**:

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

# MSE 均方误差

# torch.nn.MSELoss()

# SmoothL1Loss torch.nn.SmoothL1Loss

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

# # **特点:**

# 1. 平滑过渡:当误差较小时,损失函数表现为 L2 Loss(平方惩罚);当误差较大时,损失函数逐渐向 L1 Loss过渡。这种平滑过渡既能对大误差有所控制,又不会对异常值过度敏感。

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

# 损失小于1时 损失平方的一半

# 其他情况 损失绝对值-0.5  大的值不平方

# **应用场景:**

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

# 交叉熵误差 用于多分类 softmax

# ## CrossEntropyLoss  nn.CrossEntropyLoss()

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

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

# softmax预测结果概率分布 和真实的概率分布(只有一个1其他全为0的) 对应位置

#  BCELoss CrossEntropyLoss的特殊情况 只有两种 算法都一样 nn.BCELoss()

# 二分类交叉熵损失函数

# 输出层是sigmoid的

# bp

# bp 误差反向传播算法

# 多层神经网络的学习能力比单层网络强得多。想要训练多层网络,需要更强大的学习算法。误差反向传播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神经网络学习算法。现实任务使用神经网络时,

# 大多是在使用 BP 算法进行训练,值得指出的是 BP 算法不仅可用于多层前馈神经网络,还可以用于其他类型的神经网络。通常说 BP 网络时,一般是指用 BP 算法训练的多层前馈神经网络。

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

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

# 2. 计算损失:通过损失函数$$ L(y_{\text{pred}}, y_{\text{true}}) $$ 计算预测值和真实值的差距。

# 3. 梯度计算:反向传播的核心是计算损失函数 $$L$$ 对每个权重和偏置的梯度。

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

# 5. 迭代训练:将前向传播、梯度计算、参数更新的步骤重复多次,直到损失函数收敛或达到预定的停止条件。

# 前向传播

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

#

# 每一层都有自己w和激活函数 算出一个预测值 用来做误差函数

# 反向传播

# 反向传播(Back Propagation,简称BP)通过计算损失函数相对于每个参数的梯度来调整权重,

# 使模型在训练数据上的表现逐渐优化。反向传播结合了链式求导法则和梯度下降算法,是神经网络**模型训练**过程中更新参数的关键步骤。

# ### 2.1 原理

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

# 每一层都有自己的损失函数和W 每一层都可以做w新=w旧-lr*g   求每一层的w的时候上面层的w都是知道的 所以求这一层的导函数就行 链式到输入层

# bp代码 两种写法

# 创建一个类这个类继承 nn.Model类

# 类的构造方法要定义网络界沟 神经元 层一个 Linear表示一层  Linear传出表示下一层有多少个神经元 传入表示上层有多少个输出 也可以在构造函数中定义激活函数 forward调用 还有初始化W和b 用linear的weight的data

# 重写forward方法 传入self和数据 前向传播一次 要注意激活函数 父类已经实现了一下forward函数的一些功能 forward 返回前向传播的值

import torch.nn as nn

import torch

from torch import optim

def demo1():

    class mynn(nn.Module):

        def __init__(self, *args, **kwargs):

            super().__init__(*args, **kwargs)

            # 定义网络结构

            self.linear1=nn.Linear(4,3)# 隐藏层

            self.linear2=nn.Linear(3,2)# 隐藏层

            self.linear3=nn.Linear(2,1)# 输出层

            self.activation=torch.nn.Sigmoid() # 激活函数

            # 初始化每一层的W 和截距b

            # nn的方法初始化 不包括b 传入的是linear的weight

            nn.init.xavier_normal_(self.linear1.weight)

            nn.init.xavier_normal_(self.linear2.weight)

            nn.init.xavier_normal_(self.linear3.weight)

            self.linear1.bias.data=torch.tensor(5.5,dtype=torch.float32)

            self.linear2.bias.data=torch.tensor(5.5,dtype=torch.float32)

            self.linear3.bias.data=torch.tensor(5.5,dtype=torch.float32)

        def forward(self,x):

            x=self.linear1(x)

            x=self.activation(x)

            x=self.linear2(x)

            x=self.activation(x)

            x=self.linear3(x)

            x=self.activation(x)

            return x # 前向传播的结果

    # 实例化类

    model1=mynn()

    # 假数据

    torch.manual_seed(666)

    x=torch.randn(100,4)

    torch.manual_seed(6)

    y=torch.rand(100,1)

    # 定义优化器 w更新用

    sgd1=optim.SGD(model1.parameters(),lr=0.1)

    # 得到反向传播的值

    # 训练轮次

    epoch=50

    # 开始训练

    for i in range(epoch):

        # 前向传播放在for里面 每次训练都前向传播一次

        y_pre=model1.forward(x)

        # 均方误差

        loss_func=nn.MSELoss()

        loss=loss_func(y_pre,y)

        # 清空梯度

        sgd1.zero_grad()

        # 反向传播

        loss.backward()

        # 更新梯度

        sgd1.step()

    print(model1.linear1.weight.data)

    print(model1.linear2.weight.data)

    print(model1.linear3.weight.data)

    print(y[0])

def demo2():# 神经网络第二中写法 用nn.Sequential 组合每一层的linear和激活函数

    class mynn(nn.Module):

        def __init__(self, *args, **kwargs):

            super().__init__(*args, **kwargs)

            self.hidden1=nn.Sequential(nn.Linear(4,3),nn.Sigmoid())

            self.hidden2=nn.Sequential(nn.Linear(3,2),nn.Sigmoid())

            self.out=nn.Sequential(nn.Linear(2,1),nn.Sigmoid())

            # 初始化方法从Sequential元组里面去下标

            nn.init.xavier_normal_(self.hidden1[0].weight)

            nn.init.xavier_normal_(self.hidden2[0].weight)

            nn.init.xavier_normal_(self.out[0].weight)

            self.hidden1[0].bias.data=torch.tensor(5.5,dtype=torch.float32)

            self.hidden2[0].bias.data=torch.tensor(5.5,dtype=torch.float32)

            self.out[0].bias.data=torch.tensor(5.5,dtype=torch.float32)

        def forward(self,x):

            # 直接用层传一次参就行

            x=self.hidden1(x)

            x=self.hidden2(x)

            x=self.out(x)

            return x

    # 假数据

    torch.manual_seed(666)

    x=torch.randn(100,4)

    torch.manual_seed(6)

    y=torch.rand(100,1)

    model1=mynn()

    # 定义优化器

    sgd1=optim.SGD(model1.parameters(),lr=0.1)

    epoch=50

    for i in range(epoch):

        y_pre=model1.forward(x)

        loss_func=nn.MSELoss()

        loss=loss_func(y_pre,y)

        # 清空梯度

        sgd1.zero_grad()

        loss.backward()

        sgd1.step()

    print(model1.hidden1[0].weight.data)

    print(model1.hidden1[0].weight.data)

    print(model1.out[0].weight.data)

    print(y[0])

if __name__=="__main__":

    demo1()

    demo2()