文章目录
- 摘要
- Abstract
- 1 损失函数与反向传播
- 1.1 L1Loss损失函数
- 1.2 MSELoss损失函数
- 1.3 交叉熵损失函数(CrossEntropyLoss)
- 1.4 反向传播
- 2 优化器
- 3 现有网络模型的使用及修改
- 4 网络模型的保存与读取
- 4.1 保存模型
- 4.2 读取
- 总结
摘要
本次学习对Pytorch中有关常用的损失函数进行了相关学习和实操,并对Pytorch中交叉熵损失函数的原理进行学习和相关公式的推导;并学习了优化器通过计算模型的损失函数进行模型的优化;同时学习了现在训练成熟的网络模型的使用、修改以及网络模型的保存和读取。
Abstract
In this study, the common loss functions in Pytorch are studied and implemented, and the principle of cross-entropy loss function in Pytorch is studied and related formulas are derived. The optimizer can optimize the model by calculating the loss function of the model. At the same time, we learned how to use and modify the network model and how to save and read the network model.
1 损失函数与反向传播
1.1 L1Loss损失函数
import torch
from torch.nn import L1Loss
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
l1 = L1Loss()
result = l1(inputs, targets)
print(result) # tensor(0.6667)
l1 = L1Loss(reduction=‘mean’)
- 默认reduction=‘mean’,求每个数据差的绝对值再取平均
- 当reduction=‘sum’,即求每个数据差的绝对值求和,此时输出为:tensor(2.)
1.2 MSELoss损失函数
torch.nn.MSELoss
import torch
from torch.nn import L1Loss, MSELoss
inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)
# MSELoss损失函数
m1 = MSELoss(reduction='mean') #默认值
result = m1(inputs, targets)
print(result) # tensor(1.3333)
m2 = MSELoss(reduction='sum')
result = m2(inputs, targets)
print(result) # tensor(4.)
1.3 交叉熵损失函数(CrossEntropyLoss)
softmax函数又称归一化指数函数,是基于 sigmoid 二分类函数在多分类任务上的推广;在多分类网络中,常用 Softmax 作为最后一层进行分类。
import torch
import torch.nn as nn
input1 = torch.tensor([-0.5, -0.3, 0, 0.3, 0.5])
input2 = torch.tensor([-3, -1, 0, 1, 3], dtype=torch.float32)
softmax = nn.Softmax(dim=0)
output1 = softmax(input1)
output2 = softmax(input2)
print(output1) # tensor([0.1135, 0.1386, 0.1871, 0.2525, 0.3084])
print(output2) # tensor([0.0021, 0.0152, 0.0413, 0.1122, 0.8292])
- Softmax 可以使正样本(正数)的结果趋近于 1,使负样本(负数)的结果趋近于 0;且样本的绝对值越大,两极化越明显。
- Softmax 可以使数值较大的值获得更大的概率
Pytorch中nn.CrossEntropyLoss,结合了nn.LogSoftmax()和nn.NLLLoss()两个函数,在做分类训练时非常有用
import torch
import torch.nn as nn
input2 = torch.tensor([0.1, 0.2, 0.3])
target2 = torch.tensor([1])
input2 = torch.reshape(input2, (1, 3))
l = crossEntropyLoss(input2, target2)
print(l) # tensor(1.1019)
# 计算公式:
# -0.2 + ln(exp(0.1)+exp(0.2)+exp(0.3))
import torch
import torch.nn as nn
crossEntropyLoss = nn.CrossEntropyLoss()
input = torch.tensor([[-0.1342, -2.5835, -0.9810],
[0.1867, -1.4513, -0.3225],
[0.6272, -0.1120, 0.3048]])
target = torch.tensor([0, 2, 1])
loss = crossEntropyLoss(input, target)
print(loss)
'''
[-(-0.1342)+ln(exp(-0.1342)+exp(-2.5835)+exp(-0.9810))
-(-0.3225)+ln(exp(0.1867)+exp(-1.4513)+exp(-0.3225))
-(-0.1120)+ln(exp(0.6272)+exp(-0.1120)+exp(0.3048))]/3 = 3.03842655071/3 = 1.01280885024
'''
1.4 反向传播
import torch.nn as nn
import torchvision
from torch.nn import Conv2d, MaxPool2d, Sequential, Linear, Flatten, CrossEntropyLoss
from torch.utils.data import DataLoader
#数据集
dataset = torchvision.datasets.CIFAR10("dataset2", train=False, transform= torchvision.transforms.ToTensor())
data_loader = DataLoader(dataset, batch_size=1)
class seq(nn.Module):
def __init__(self):
super(seq, self).__init__()
self.model = 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.model(x)
return x
s = seq()
# 交叉熵
loss = CrossEntropyLoss()
for data in data_loader:
imgs, target = data
output = s(imgs)
# print(output)
# print(target)
result_loss = loss(output, target)
# print(result_loss)
# 反向传播
# 计算出来的 loss 值有 backward 方法属性,
# 反向传播来计算每个节点的更新的参数。
# 这里查看网络的属性 grad 梯度属性刚开始没有,
# 反向传播计算出来后才有,后面优化器会利用梯度优化网络参数。
result_loss.backward()
print('ok')
还未执行反向传播
执行反向传播之后,进行了gradient descent,grad值进行了更新
2 优化器
torch.optim
# 数据集
dataset = torchvision.datasets.CIFAR10("dataset2", train=False, transform= torchvision.transforms.ToTensor())
data_loader = DataLoader(dataset, batch_size=1)
# 定义模型
model=...
#训练模型
for data in data_loader:
imgs, target = data
output = seq(imgs)
result_loss = loss(output, target)
# 优化器先将网络中的每个参数的梯度清零
optim.zero_grad()
# 调用损失函数的反向传播求出每个节点的梯度
result_loss.backward()
# 更新参数
optim.step()
Debug:
将这三行代码打上断点,依次执行观察grad和data的变化
执行第42行代码前跟执行42行代码之后,grad都是没有值的
执行44行反向传播代码之后,grad由none变化,出现参数
执行46行代码前后,data的数值发生了变化
- 训练20个回合(epoch)
训练20个回合,看每个回合的loss值
#训练模型:
for epoch in range(20):
sum_loss = 0
for data in data_loader:
imgs, target = data
output = seq(imgs)
result_loss = loss(output, target)
# 优化器先将网络中的每个参数的梯度清零
optim.zero_grad()
# 调用损失函数的反向传播求出每个节点的梯度
result_loss.backward()
# 更新参数
optim.step()
# print(result_loss)
sum_loss = sum_loss+result_loss
print(sum_loss)
3 现有网络模型的使用及修改
vgg16模型为例,它是以ImageNet数据集进行训练得到的,但是ImageNet数据集不公开并且数据量非常庞大,不下载,仅用于增加和修改该网络模型的学习
import torchvision
vgg16_true = torchvision.models.vgg16()
print(vgg16_true)
可以看到,该网络模型最后是一个线性变化:Linear(4096,1000),现在想该网络模型最后的线性变化改为10输出
方法一:在VGG16后面添加一个线性层
vgg16_true.add_module('add_linear', nn.Linear(1000, 10))
或者
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
方法二:直接修改VGG16的最后一个线性层
vgg16_true.classifier[6] = nn.Linear(4096, 10)
4 网络模型的保存与读取
4.1 保存模型
import torch
import torchvision
vgg16 = torchvision.models.vgg16()
# 保存方式一,模型结构+模型参数
torch.save(vgg16, "vgg16_method1.pth")
# 保存方式二,模型参数(官方推荐)
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
4.2 读取
import torch
import torchvision
# 方式一 -> 保存方式一,加载模型
model = torch.load("vgg16_method1.pth")
print(model)
# 方式二:对应保存方式2
vgg16 = torchvision.models.vgg16()
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
- 方式一保存模型有陷阱
# save.py
# 保存方式一存在陷阱
class modelcc(nn.Module):
def __init__(self):
super(modelcc, self).__init__()
self.conv1 = nn.Conv2d(3, 32, 5, 1)
def forward(self, x):
return self.conv1(x)
cc = modelcc()
torch.save(cc, "cctest.pth")
# load.py
cc = torch.load("cctest.pth")
print(cc)
在实际运用时,一般把model单独写一个python文件,然后通过下面这行代码在使用时进行引入
from model_save import *
总结
本周学习了Pytorch中一些小简单的损失函数的数学公式和使用,搜索相关资料更深刻学习了交叉熵损失函数,学习了网络模型的使用、修改、保存和读取。下周,我将通过学习minist数据集相关任务,加深对CNN原理的学习