前言
最近学会了如何使用pytorch搭建模型和训练模型,在查找官网资料和博客中也遇到了一些坑,因此写一下关于损失函数等相关问题的研究
一、损失函数
损失函数是算法中比较重要的一部分,通常看论文比较关注的几个点就是网络模型、损失函数、创新点。常用损失函数的有
- 平均绝对误差L1Loss
- 均方误差MSFLoss
- 交叉熵损失CROSSENTROPYLOSS
1.L1Loss,MSELoss
该损失函数常用与回归问题
根据官方文档知道,他是结果与目标值进行相减后取绝对值。
注:input与target必须得有相同的维度
input = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
input = torch.reshape(input, (1, 1, 1, 3))
targets = torch.reshape(targets, (1, 1, 1, 3))
loss = nn.L1Loss(reduction='mean')
#绝对值误差。'none':分别相减 'sum':相减后求和 'mean':对求和的结果再进行取平均值
result = loss(input, targets)
print(result)
--tensor(0.6667)
mse损失是用来比较两个样本相似度的一个程度,mse值越小则模型的性能越好。下述数据均方误差结果:((1-1)2+(2-2)2+(3-5)2)/3
lose_mse = nn.MSELoss()
#平方差误差
result = lose_mse(input,targets)
print(result)
--tensor(1.3333)
2.交叉熵函数
这个在图像分类中使用最多,通常是来处理分类问题。
计算的方法如下:
比如举如下例子,目标检测的标签是0,loss值为
loss = -0.5 + ln(e0.5+e0.36+e0.23)= 0.9680
input = torch.tensor([0.5,0.36,0.23],dtype=torch.float32)
target = torch.tensor([0])
input = torch.reshape(input,shape=(1,3))
loss = nn.CrossEntropyLoss()
result = loss(input,target)
print(result)
--tensor(0.9680)
注:target的范围是从0开始的,其值不能超过当前分类的范围
3.DICE损失
常用于分割任务中的损失函数。
Dice系数是一种集合相似度度量函数,通常用于计算两个样本的相似度(值得范围为[0,1]),公式为:
DiceCoefficient= 2 ∣ X ∩ Y ∣ ∣ x ∣ + ∣ Y ∣ \frac{2\left | X\cap Y \right |}{\left | x \right |+\left | Y \right |} ∣x∣+∣Y∣2∣X∩Y∣
其中, ∣ X ∩ Y ∣ \left | X\cap Y \right | ∣X∩Y∣表示集合X和Y的交集, ∣ X ∣ \left | X \right | ∣X∣和 ∣ Y ∣ \left | Y \right | ∣Y∣表示其元素个数,对于分割任务而言, ∣ X ∣ \left | X \right | ∣X∣和 ∣ Y ∣ \left | Y \right | ∣Y∣分别表示分割的Ground True和Predict mask。
由于Dice损失和Dice系数的关系是:DiceLoss = 1 - DiceCoefficient,由此得到Dice Loss的公式为:
DiceLoss=
1
−
2
∣
X
∩
Y
∣
∣
x
∣
+
∣
Y
∣
1- \frac{2\left | X\cap Y \right |}{\left | x \right |+\left | Y \right |}
1−∣x∣+∣Y∣2∣X∩Y∣
通常为了防止分母为0,通常会在分子和分母添加一个偏移量
二、反向传播、优化器
在上述损失函数中,我们均能得到当前模型的一个训练情况,通过损失值为下一步模型训练的参数进行梯度下降,找到一个合适的值,这个步骤就是反向传播,在模型训练时会用到。
使用损失函数的backward()方法即可进行反向传播,其次通常要使用优化器来优化模型参数。pytorch官网上给出的优化器一般使用模板如下:
2.1优化器模板
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
#optimizer = optim.Adam([var1, var2], lr=0.0001)
for input, target in dataset:
optimizer.zero_grad() #将上一步得到的参数梯度清零
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
在上述模型中使用的是学习率为0.01。在大多数情况下学习率不是一直不变的,特别是在训练的轮数较大时,学习率需要做出调整,一般采用自适应的学习率方法。pytorch采用的学习率方法和及其运算可以采纳这篇博客:
https://blog.csdn.net/XiaoyYidiaodiao/article/details/124678206
以下采用LambdaLR的方法进行自适应学习率算法。算法模板如下:
2.2自适应学习率模板
learning_rate = 1e-2
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
lambda1 = lambda epoch: (epoch) // 2
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda1)
for epoch in range(epochs):
train_data
valid_data
scheduler.step()
通过对mnist数据集进行训练,学习率保持0.01和自适应学习率最终的loss值有不同,使用自适应学习率,该算法的收敛度更快。
三、对MNIST进行模型训练
import time
from turtle import pd
import torch.optim
import torchvision
from torch import nn, optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt
start = time.perf_counter()
train_data = datasets.MNIST(root="./MNIST",
train=True,
transform=transforms.ToTensor(),
download=False)
test_data = datasets.MNIST(root="./MNIST",
train=False,
transform=transforms.ToTensor(),
download=False)
train_loader = DataLoader(dataset=train_data,
batch_size=64,
shuffle=True)
test_loader = DataLoader(dataset=test_data,
batch_size=64,
shuffle=True)
class mymodel(nn.Module):
def __init__(self):
super(mymodel, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3),
nn.MaxPool2d(2),
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3),
nn.MaxPool2d(2, stride=2),
nn.Flatten(),
nn.Linear(in_features=1600, out_features=64),
nn.ReLU(),
nn.Linear(in_features=64, out_features=10)
)
def forward(self, input):
return self.model1(input)
model = mymodel()
loss = nn.CrossEntropyLoss()
iterations = 0
learning_rate = 1e-2
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
lambda1 = lambda epoch: (epoch) // 2
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda1)
for epoch in range(20):
running_loss = 0.0
print(" -----------------the {} number of training epoch --------------".format(epoch))
model.train()
for data in train_loader:
imgs, targets = data
outputs = model(imgs)
result_loss = loss(outputs, targets)
optimizer.zero_grad()
result_loss.backward()
optimizer.step()
running_loss += result_loss
iterations += 1
if iterations % 100 == 0:
print(
"Epoch: {} | Iteration: {} | lr: {} | loss: {} | np.mean(loss): {} "
.format(epoch, iterations, optimizer.param_groups[0]['lr'],result_loss.item(),
np.mean(result_loss.item())))
print("第", epoch, "次", running_loss)
scheduler.step()
if (epoch + 1) % 20 == 0:
print("Save state, iter: {} ".format(iterations))
torch.save(model.state_dict(), "lrmymodel{}.pth".format(epoch))
end = time.perf_counter()
runTime = end - start
print("运行时间:", runTime)
运行结果如图:
总结
本文讲解了常见的一些损失函数,对于交叉熵损失函数进行了重点描述,也讲了反向传播和优化器部分,同时采用自适应学习率算法能够加速loss的收敛情况。