教学视频:损失函数与反向传播_哔哩哔哩_bilibili
损失函数(Loss Function)
损失函数是衡量模型预测输出与实际目标之间差距的函数。在监督学习任务中,我们通常希望模型的预测尽可能接近真实的目标值。损失函数就是用来量化模型预测的误差大小的一种方法。
作用:
- 衡量模型性能: 损失函数的值越小,表示模型在训练集上的预测结果与实际标签越接近,即模型的性能越好。
- 指导模型优化(反向传播): 通过最小化损失函数来调整模型的参数,使得模型能够更准确地预测目标值。优化过程就是通过调整模型参数来减小损失函数的过程。可通过这个过程得到梯度。
常见的损失函数
常见的损失函数包括均方误差(Mean Squared Error, MSE)、交叉熵损失(Cross Entropy Loss)、对数损失(Log Loss)等,具体选择哪种损失函数取决于问题的类型和输出的形式。
pytorch官方网址:torch.nn — PyTorch 2.4 documentation
接下来看几个损失函数:
L1Loss
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
如图所示,是这个函数所使用的的公式。看起来很复杂,其实就是对所有的损失求平均或求和。
另外我们需要注意输入和输出格式:
使用案例:
inputs=torch.tensor([1,2,3],dtype=torch.float32)
targets=torch.tensor([1,2,5],dtype=torch.float32)
inputs=torch.reshape(inputs,(1,1,1,3))
targets=torch.reshape(targets,(1,1,1,3))
#没有指定参数reduction,则默认按均值方式计算,除此之外,还可以使用reduction='sum',使其求和
loss=nn.L1Loss()
#计算公式:(|1-1|+|2-2|+|5-3|)/3≈0.667
result=loss(inputs,targets)
print(result)
MSELoss
平方差损失函数。
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
与上面的使用方法基本相同。
loss=nn.MSELoss()
result=loss(inputs,targets)
print(result)
#输出结果:tensor(1.3333)
CrossEntropyLoss
交叉熵损失函数,特别适用于多分类任务和输出为概率分布的情况。这个我首次接触是在机器学习中的逻辑回归那部分。如果想要了解更多可以看我另一篇文章:0_(机器学习)逻辑回归介绍-CSDN博客
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100,
reduce=None, reduction='mean', label_smoothing=0.0)
注意此函数的输入和输出与前面两种略有不同:
x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])
x=torch.reshape(x,(1,3))
loss_cross=nn.CrossEntropyLoss()
result_cross=loss_cross(x,y)
print(result_cross)
输出结果为:
tensor(1.1019)
反向传播(Backpropagation)
反向传播是一种有效的训练神经网络的方法,它利用链式法则计算损失函数对每个模型参数的梯度,并根据梯度更新参数。它是损失函数优化过程中的关键步骤。
作用:
- 计算梯度: 反向传播算法通过将损失函数的梯度从网络的输出层向输入层传播,计算每个参数对损失函数的影响程度。
- 参数更新: 计算得到的梯度可以用来更新模型的参数,使得损失函数值减小,从而提高模型的预测性能。
反向传播利用了链式法则来计算复杂的导数,高效地更新神经网络中的参数。这种算法使得深度学习模型可以在大量数据上进行训练,并从数据中学习到复杂的模式和关系。
测试代码:
from torch import nn
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset=torchvision.datasets.CIFAR10("../dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader=DataLoader(dataset,batch_size=64,drop_last=True)
class MyNn(nn.Module):
def __init__(self) :
super().__init__()
self.conv1=nn.Conv2d(3,32,5,padding=2)
self.maxpool1=nn.MaxPool2d(2)
self.conv2=nn.Conv2d(32,32,5,padding=2)
self.maxpool2=nn.MaxPool2d(2)
self.conv3=nn.Conv2d(32,64,5,padding=2)
self.maxpool3=nn.MaxPool2d(2)
self.flatten=nn.Flatten()
self.linear1=nn.Linear(1024,64)
self.linear2=nn.Linear(64,10)
'''使用Sequential可以简化代码'''
# self.model1=nn.Sequential(
# nn.Conv2d(3,32,5,padding=2),
# nn.MaxPool2d(2),
# nn.Conv2d(32,32,5,padding=2),
# nn.MaxPool2d(2),
# nn.Conv2d(32,64,5,padding=2),
# nn.MaxPool2d(2),
# nn.Flatten(),
# nn.Linear(1024,64),
# nn.Linear(64,10)
# )
def forward(self,x):
x=self.conv1(x)
x=self.maxpool1(x)
x=self.conv2(x)
x=self.maxpool2(x)
x=self.conv3(x)
x=self.maxpool3(x)
x=self.flatten(x)
x=self.linear1(x)
x=self.linear2(x)
# x=self.model1(x)
return x
mynn=MyNn()
loss=nn.CrossEntropyLoss()
for data in dataloader:
imgs,targets=data
outputs=mynn(imgs)
result_loss=loss(outputs,targets)
result_loss.backward()
print(result_loss)
在其中打上断点,如图:
然后debug运行查看参数,就可以看到在没有运行backward()函数之前,图中所指参数为空:
那么现在往下运行一行试试:
以上就是我们得到的梯度(梯度下降法中的梯度)。接下来选择合适的优化器,我们就可以对模型进行优化了。
优化器
官方文档:torch.optim — PyTorch 2.4 documentation
用于优化神经网络模型的库,它的主要作用是实现各种优化算法,帮助模型在训练过程中更新参数以最小化损失函数。
有许许多多不同的优化器,入门阶段就不一一介绍。有需要可以自己查用法。
from torch import nn
import torch
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset=torchvision.datasets.CIFAR10("../dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader=DataLoader(dataset,batch_size=64,drop_last=True)
class MyNn(nn.Module):
def __init__(self) :
super().__init__()
self.conv1=nn.Conv2d(3,32,5,padding=2)
self.maxpool1=nn.MaxPool2d(2)
self.conv2=nn.Conv2d(32,32,5,padding=2)
self.maxpool2=nn.MaxPool2d(2)
self.conv3=nn.Conv2d(32,64,5,padding=2)
self.maxpool3=nn.MaxPool2d(2)
self.flatten=nn.Flatten()
self.linear1=nn.Linear(1024,64)
self.linear2=nn.Linear(64,10)
'''使用Sequential可以简化代码'''
# self.model1=nn.Sequential(
# nn.Conv2d(3,32,5,padding=2),
# nn.MaxPool2d(2),
# nn.Conv2d(32,32,5,padding=2),
# nn.MaxPool2d(2),
# nn.Conv2d(32,64,5,padding=2),
# nn.MaxPool2d(2),
# nn.Flatten(),
# nn.Linear(1024,64),
# nn.Linear(64,10)
# )
def forward(self,x):
x=self.conv1(x)
x=self.maxpool1(x)
x=self.conv2(x)
x=self.maxpool2(x)
x=self.conv3(x)
x=self.maxpool3(x)
x=self.flatten(x)
x=self.linear1(x)
x=self.linear2(x)
# x=self.model1(x)
return x
mynn=MyNn()
loss=nn.CrossEntropyLoss()
optim=torch.optim.SGD(mynn.parameters(),lr=0.01)
for data in dataloader:
imgs,targets=data
outputs=mynn(imgs)
result_loss=loss(outputs,targets)
optim.zero_grad()
result_loss.backward()
optim.step()
优化输出结果,可以看到cost在逐渐变小:
tensor(358.2124, grad_fn=<AddBackward0>)
tensor(351.9559, grad_fn=<AddBackward0>)
tensor(328.9600, grad_fn=<AddBackward0>)
tensor(314.0949, grad_fn=<AddBackward0>)
tensor(306.4304, grad_fn=<AddBackward0>)
tensor(297.4454, grad_fn=<AddBackward0>)
tensor(288.6151, grad_fn=<AddBackward0>)
tensor(280.8827, grad_fn=<AddBackward0>)
tensor(273.8977, grad_fn=<AddBackward0>)
tensor(267.9252, grad_fn=<AddBackward0>)
tensor(262.4760, grad_fn=<AddBackward0>)
tensor(257.2650, grad_fn=<AddBackward0>)
tensor(252.2356, grad_fn=<AddBackward0>)
tensor(247.5495, grad_fn=<AddBackward0>)
tensor(243.2856, grad_fn=<AddBackward0>)
tensor(239.3630, grad_fn=<AddBackward0>)
tensor(235.6944, grad_fn=<AddBackward0>)
tensor(232.2151, grad_fn=<AddBackward0>)
tensor(228.8934, grad_fn=<AddBackward0>)
tensor(225.7084, grad_fn=<AddBackward0>)
截图: