首页 > 其他分享 >深度学习(十三)——损失函数与反向传播

深度学习(十三)——损失函数与反向传播

时间:2023-08-24 21:23:37浏览次数:43  
标签:loss 函数 nn 十三 sum torch reduction 反向 mean

一、损失函数:Loss Function

官网文档:torch.nn — PyTorch 2.0 documentation

1. Loss Function的作用

  • 每次训练神经网络的时候都会有一个目标,也会有一个输出。目标和输出之间的误差,就是用\(Loss\) \(Function\)来衡量的。所以,误差\(Loss\)是越小越好的。

  • 此外,我们可以根据误差\(Loss\),指导输出\(output\)接近目标\(target\)。即我们可以以\(Loss\)为依据,不断训练神经网络,优化神经网络中各个模块,从而优化\(output\)。

\(Loss\) \(Function\)的作用:

(1)计算实际输出和目标之间的差距

(2)为我们更新输出提供一定的依据,这个提供依据的过程也叫反向传播

2. Loss Function中的函数介绍

(1)nn.L1Loss

计算\(MAE\) (mean absolute error),即假设输入为\(x_i\),目标为\(y_i\),特征数量为\(n\)。在默认情况下,\(nn.L1Loss\)通过下面公式计算误差:

\[\frac{\sum^{n}_{i=1}{|x_i-y_i|}}{n} \]

class torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')

参数说明:

  • reduction:默认为 ‘mean’ ,可选meansum

    • reduction='mean'时,计算误差采用公式:

      \[\frac{\sum^{n}_{i=1}{|x_i-y_i|}}{n} \]

    • reduction='sum'时,计算误差采用公式:

      \[\sum^{n}_{i=1}{|x_i-y_i|} \]

  • 需要注意的是,计算的数据必须为浮点数

代码栗子:

import torch
from torch.nn import L1Loss

input=torch.tensor([1,2,3],dtype=torch.float32)
target=torch.tensor([1,2,5],dtype=torch.float32)

input=torch.reshape(input,(1,1,1,3))
target=torch.reshape(target,(1,1,1,3))

loss1=L1Loss()  #reduction='mean'
loss2=L1Loss(reduction='sum')  #reduction='mean'
result1=loss1(input,target)
result2=loss2(input,target)

print(result1,result2)

(2)nn.MSELoss

计算\(MSE\) (mean squared error),即假设输入为\(x_i\),目标为\(y_i\),特征数量为\(n\)。在默认情况下,\(nn.MSELoss\)通过下面公式计算误差:

\[\frac{\sum^{n}_{i=1}{(x_i-y_i)^2}}{n} \]

class torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

参数说明:

  • reduction:默认为 ‘mean’ ,可选meansum

    • reduction='mean'时,计算误差采用公式:

      \[\frac{\sum^{n}_{i=1}{(x_i-y_i)^2}}{n} \]

    • reduction='sum'时,计算误差采用公式:

      \[\sum^{n}_{i=1}{(x_i-y_i)^2} \]

代码栗子:

import torch
from torch.nn import L1Loss,MSELoss

input=torch.tensor([1,2,3],dtype=torch.float32)
target=torch.tensor([1,2,5],dtype=torch.float32)

input=torch.reshape(input,(1,1,1,3))
target=torch.reshape(target,(1,1,1,3))

loss_mse1=MSELoss()  #reduction='mean'
loss_mse2=MSELoss(reduction='sum')  #reduction='mean'
result_mse1=loss_mse1(input,target)
result_mse2=loss_mse2(input,target)

print(result_mse1,result_mse2)

(3)nn.CrossEntropyLoss(交叉熵)

当训练一个分类问题的时候,假设这个分类问题有\(C\)个类别,那么有:

\[loss(x,class)=-log(\frac{exp(x[class])}{\sum_{j}exp(x[j])})=-x[class]+log(\sum_{j}exp(x[j]) \]

*注意:其中的\(log\)在数学中表示的是\(ln\),即以10为底的对数函数

举个栗子:

  • 我们对包含了人、狗、猫的图片进行分类,其标签的索引分别为0、1、2。这时候将一张的图片输入神经网络,即目标(\(target\))为\(1\)(对应标签索引)。输出结果为\([0.1,0.2,0.3]\),该列表中的数字分别代表分类标签对应的概率。

  • 根据上述分类结果,图片为的概率更大,即\(0.3\)。对于该分类的\(Loss\) \(Function\),我们可以通过交叉熵去计算,即:

    \[x=[0.1,0.2,0.3];x[class]=x[1]=0.2 \]

    \[loss(x,class)=-0.2+log[exp(0.1)+exp(0.2)+exp(0.3)] \]

那么如何验证这个公式的合理性呢?根据上面的栗子,分类结果越准确,\(Loss\)应该越小。这条公式由两个部分组成:

  • \(log(\sum_{j}exp(x[j])\):主要作用是控制或限制预测结果的概率分布。比如说,预测出来的人、狗、猫的概率均为0.9,每个结果概率都很高,这显然是不合理的。此时\(log(\sum_{j}exp(x[j])\)的值会变大,误差\(loss(x,class)\)也会随之变大。同时该指标也可以作为分类器性能评判标准。

  • \(-x[class]\):在已知图片类别的情况下,预测出来对应该类别的概率\(x[class]\)越高,其预测结果误差越小。

参数说明:

  • Input: \((N,C)\),其中\(N\)代表batch_size,\(C\)代表分类的数量(或者叫标签数量),即数据要分成几类(或有几个标签)。

  • Target: \((N)\),对于每个数据:\(0\leq{target[i]}\leq{C-1}\)

代码栗子:

  • 仍然以上面图片分类栗子的结果为例,编写程序
import torch
from torch.nn import L1Loss,MSELoss,CrossEntropyLoss

x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])

x=torch.reshape(x,(1,3))

loss_cross=CrossEntropyLoss()
result_cross=loss_cross(x,y)
print(result_cross)
  • 直接用CIFAR 10数据进行实战分类:
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset=torchvision.datasets.CIFAR10("./dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloder=DataLoader(dataset,batch_size=1)

class Demo(nn.Module):
    def __init__(self):
        super(Demo,self).__init__()

        self.model1=Sequential(
            Conv2d(3,32,5,padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )

    def forward(self,x):
        x=self.model1(x)
        return x

demo=Demo()
loss=nn.CrossEntropyLoss()
for data in dataloder:
    imgs,targets=data
    output=demo(imgs)

    # print(output)
    #[Run] 一共输出10个数据,分别代表该图像为各个标签的概率.具体如下:
    # tensor([[-0.0151, -0.0990, 0.0908, 0.0354, 0.0731, -0.0313, -0.0329, 0.1006,
    #          -0.0953, 0.0449]], grad_fn= < AddmmBackward0 >)

    # print(targets)
    #[Run] 输出该图像真实的标签,具体如下:
    # tensor([7])

    result_loss=loss(output,targets)
    print(result_loss)

二、反向传播

如何根据\(Loss\) \(Function\)为更新神经网络数据提供依据?

  • 对于每个卷积核当中的参数,设置一个\(grad\)(梯度)。

  • 当我们进行反向传播的时候,对每一个节点的参数都会求出一个对应的梯度。之后我们根据梯度对每一个参数进行优化,最终达到降低\(Loss\)的一个目的。比较典型的一个方法——梯度下降法

代码举例:

  • 在上面的代码for循环的最后,加上:
result_loss.backward()
  • 上面就是反向传播的使用方法,它的主要作用是计算一个\(grad\)。使用debug功能并删掉上面这行代码,会发现单纯由result_loss=loss(output,targets)计算出来的结果,是没有\(grad\)这个参数的。

标签:loss,函数,nn,十三,sum,torch,reduction,反向,mean
From: https://www.cnblogs.com/zoubilin/p/17655184.html

相关文章

  • C++拷贝构造、赋值函数
    拷贝构造拷贝构造就是一种特殊版本的构造函数,格式:类名(const类名&that){    //执行给每个成员变量进行赋值  }什么时候会调用拷贝构造:当使用旧对象(已new的)给新对象(新new的)初始化时,会自动调用拷贝构造    Testt1;//调用无参构造Testt2=t1......
  • C++this指针、常函数
    this指针this指针的类型:类类型*const。不能被修改和赋值。只能在成员函数的内部使用。全局函数、静态函数都不能使用this.this指针本质上其实是一个成员函数的形参(栈),是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。this指针是成......
  • 每天一个小知识,今日知识-如何设计一个并发请求控制函数
    假如给你一个数组,里面是请求路径,如何设计一个函数去控制这些请求的并发呢?这里我们用的请求路径为https://jsonplaceholder.typicode.com/todos来模拟constreqArr=[];for(leti=1;i<=10;i++){reqArr.push(`https://jsonplaceholder.typicode.c......
  • 一些学习网站和自己写的两个计算周的函数
    toad:https://blog.csdn.net/zzpl139/article/details/127553557风控指标:https://blog.csdn.net/eroswang/article/details/117735703vintage:https://zhuanlan.zhihu.com/p/163206686风控模型:https://falbang.com/?p=350天池:https://tianchi.aliyun.com/competition/entrance/53183......
  • golang init函数、defer函数、匿名函数、错误处理
    1.init每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用,当有全局变量跟main函数init函数同时出现的时候执行的顺序是全局函数==》init函数==》main函数packagemainimport"fmt"functest()string{ r......
  • 定义一个函数,传入一个字典和一个元组,将字典的值(key不变)和元组的值交换,返回交换后的
    知识点:zip()函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。li=[3,4,5]t=(7,8,9)print(list(zip(li,t)))print(dict(zip(li,t)))运行截图:例1:deff(a,b):print(a)print(b)#先获取对应的元素b......
  • C++构造函数、析构函数、初始化列表
    构造函数构造函数就是与类名同名的成员函数,当实例化对象时它会自动执行,当构造函数执行结束后,对象才完成实例化任务:一般负责对类对象进行初始化、资源分配class类名{int*p;public:类名(参数){p=newint;}}......
  • Nginx-配置WebSocket反向代理
    客户环境因开放端口有限,部署Portainer后默认端口无法访问,故使用nginx做转发,按照正常http协议配置nginx,启动后发现portainer默认的进入容器的功能无法使用,排查后发现报错如下。错误信息为websocket连接问题,需要更改nginx配置为websocket。仅修改http块中的内容即可。map$http_......
  • C++内联函数、引用、强制类型转换
    三、内联函数inline1、普通函数普通函数会被编译成二进制指令存储在代码段中,调用语句会生成一条跳转指令,当程序运行到调用语句时,会跳转该函数在代码段中对应的位置执行,执行结束会返回2、什么是内联函数内联函数也会被翻译成二进制指令,但调用语句不会生成跳转指令,而是直接把内......
  • C++虚函数、虚继承:virtual
    ​1.引子在类的继承当中曾经出现过这样一种情况:B、C继承自A,D继承自B和C。 之前提到过,这种情况下,关于类A当中的内容,会被复制成两份给到D,当进行访问的时候,需要指定C或者B,才能够定位到A当中的变量是来自哪里。就像下面这样。​ 代码表示:classA{public:A(int......