九 损失函数和反向传播
9.1 损失函数
- L1loss- 直接求差
- MSEloss-均方差
- CrossEntropyLoss -交叉熵
我们定义一个分类的项目,要判断的目标为[person,dog,cat],它们组成为class[0,1,2],经过网络输出每个类别的概率x。例如,我们将dog的图片传递给网络,输出x=[0.1,0.2,0.3],对应的class=1,于是我们就可以去算交叉熵为:
l o s s ( x , c l a s s ) = − 0.2 + l o g ( e 0.1 + e 0.2 + e 0.3 ) loss(x, class)=-0.2+log(e^{0.1}+e^{0.2}+e^{0.3}) loss(x,class)=−0.2+log(e0.1+e0.2+e0.3)
其中,loss的第一项代表:dog的概率越大,loss的值越小;loss的第二项表示:整体的概率预测不能都很大。
9.2 实践
import torch
from torch.nn import L1Loss,MSELoss,CrossEntropyLoss
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=L1Loss()
loss_mse=MSELoss()
result=loss(input,target)
result_mse=loss_mse(input,target)
print(result)
print(result_mse)
x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])
x=torch.reshape(x,(1,3))
torch_cross=CrossEntropyLoss()
result_cross=torch_cross(x,y)
print(result_cross)
9.3 反向传播
反向传播:计算每个需要优化的参数的梯度
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d,MaxPool2d,Flatten,Linear,Sequential
from torch.utils.data import DataLoader
data_set=torchvision.datasets.CIFAR10(root="./CIFAR10",train=True,transform=torchvision.transforms.ToTensor(),download=True)
dataloader=DataLoader(data_set,batch_size=1)
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1=Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x=self.model1(x)
return x
loss=nn.CrossEntropyLoss()
tudui=Tudui()
for data in dataloader:
imgs,target=data
output=tudui(imgs)
result_loss=loss(output,target)
result_loss.backward()#对损失函数进行反向传播,计算每个参数的梯度
十 优化器
10.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()#根据梯度进行优化
- 实践
loss=nn.CrossEntropyLoss()
tudui=Tudui()
optim=torch.optim.SGD(tudui.parameters(),lr=0.01)
for epoch in range(20):
running_loss=0.0
for data in dataloader:
imgs,target=data
output=tudui(imgs)
result_loss=loss(output,target)
optim.zero_grad()
result_loss.backward()#对损失函数进行反向传播,计算每个参数的梯度
optim.step()
running_loss+=result_loss
print(running_loss)
十一 网络模型
10.1 导入网络模型
import torchvision
from torch import nn
#导入没有经过预训练的模型
vgg16=torchvision.models.vgg16(pretrained=False)
print(vgg16)
#在现有的模型上增加层数
vgg16.classifier.add_module("add_linear",nn.Linear(1000,100))
vgg16.add_module("add_linear2",nn.Linear(100,10))
print(vgg16)
#修改现有的模型
vgg16.classifier[6]=nn.Linear(4096,10)
print(vgg16)
10.2 模型的保存与加载
注意:在用方式一进行保存自己写的模型时,必须导入自己模型的定义,才能够加载模型。或者使用from model_save import * 将模型的定义导入,model_save 是.py的文件名
import torch
import torchvision
from model_save import *
vgg16=torchvision.models.vgg16(pretrained=False)
#保存方式一:保存模型结构和参数
torch.save(vgg16,"vgg16_method.pth")
#保存方式一的加载
vgg16_loader=torch.load("vgg16_method.pth")
#保存方式二的缺点:在加载自己写的模型时,必须将类的定义一起写上,才能够加载模型
#保存方式二:只保存模型参数(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_method2.pth")#把模型参数保存成字典形式
#保存方式二的加载
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
十二 完整的模型训练套路
- 将我们之前定义好的网络保存成文件,训练的时候方便导入。同时,在写模型时会创建一个1的输入,查看输出的是否为我们需要的格式
import torch
from torch import nn, tensor
from torch.nn import Conv2d,MaxPool2d,Flatten,Linear,Sequential
#搭建神经网络
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1=Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x=self.model1(x)
return x
if __name__ == '__main__':
tudui=Tudui()
intput=torch.ones([64,3,32,32])
output=tudui(intput)
print(output.shape)#输出torch.Size([64, 10])
- 完整训练
import torchvision
import torch
from torch import nn
from torch.utils.data import DataLoader
from torch.nn import Conv2d,MaxPool2d,Flatten,Linear,Sequential
from torch.utils.tensorboard import SummaryWriter
from model_Tudui import *
#准备数据集
train_set=torchvision.datasets.CIFAR10(root="./CIFAR10",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_set=torchvision.datasets.CIFAR10(root="./CIFAR10",train=False,transform=torchvision.transforms.ToTensor(),download=True)
#length
train_length=len(train_set)
test_length=len(test_set)
print("训练集的长度为:{}".format(train_length))
print("测试集的长度为:{}".format(test_length))
#加载数据集
train_dataloder=DataLoader(train_set,batch_size=64)
test_dataloder=DataLoader(test_set,batch_size=64)
#创建网络模型
tudui=Tudui()
#创建损失函数
loss_fn=nn.CrossEntropyLoss()
#定义优化器
learning_rate=1e-2
optimizer=torch.optim.SGD(tudui.parameters(),learning_rate)
#设置训练网络的参数
total_train_step=0
total_test_step=0
epoch=10
#添加tensorboard
writer=SummaryWriter("logs")
for i in range(epoch):
print("----第{}轮开始----".format(i+1))
#训练步骤开始
for data in train_dataloder:
imgs, targets=data
outputs=tudui(imgs)
loss=loss_fn(outputs,targets)
#优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step+=1
if total_train_step %100==0:
print("训练次数:{},LOSS:{}".format(total_train_step,loss.item()))#加item的时候不会出现类型
writer.add_scalar("train_loss",loss.item(),total_train_step)#保存训练的loss
#测试步骤开始
total_test_loss=0
with torch.no_grad():
for data in test_dataloder:
imgs,targets=data
outputs=tudui(imgs)
loss=loss_fn(outputs,targets)
total_test_loss+=loss.item()
print("整体测试集上的loss为:{}".format(total_test_loss))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step+=1
torch.save(tudui,"./Tuduimodel/Tuduimodel{}.pth".format(i))
print("模型已保存")
writer.close()
- 分类问题的正确率
在分类问题上,从网络上输出的是每个类别的概率。例如二分类问题,有两张图片: o u t p u t = [ 0.1 , 0.3 ] , [ 0.2 , 0.4 ] output=[0.1,0.3],[0.2,0.4] output=[0.1,0.3],[0.2,0.4]代表这两张图片是第二类的可能性大,它预测的类别为: p r e d s = [ 1 , 1 ] preds=[1,1] preds=[1,1]而它的实际类别为: t a r g e t = [ 0 , 1 ] target=[0,1] target=[0,1]那么它预测的成功率就是50% p r e d = = t a r g e t − − > [ f a l s e , t r u e ] pred==target -->[false,true] pred==target−−>[false,true]要实现从概率到类别的转换,就要利用 p r e d s = o u t p u t . a r g m a x ( 1 ) preds=output.argmax(1) preds=output.argmax(1)1代表是横向的比较,0代表纵向比较。
#测试步骤开始
total_test_loss=0
total_accuracy=0
with torch.no_grad():
for data in test_dataloder:#一个data是一个batch,一个dataloader是全部的数据
imgs,targets=data
outputs=tudui(imgs)
loss=loss_fn(outputs,targets)
total_test_loss+=loss.item()
accuracy=(outputs.argmax(1)==targets).sum()#accuracy保存一个batch里面配对的个数
total_accuracy+=accuracy#保存全部数据配对的个数
print("整体测试集上的loss为:{}".format(total_test_loss))
print("整体测试集上的正确率为:{}".format(total_accuracy/test_length))
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step+=1
- 利用GPU进行训练一
#网络模型
tudui=Tudui()
tudui.cuda()
#损失函数
loss_fn=loss_fn.cuda()
#数据
imgs,target=data
imgs=imgs.cuda()
target=target.cuda()
- 利用GPU训练二
devcie=torch.device("cuda")
#网络模型
tudui=Tudui()
tudui.to(device)
#损失函数
loss_fn=loss_fn.to(device)
#数据
imgs,target=data
imgs=imgs.to(device)
target=target.to(device)