目录
搭建小实战和Sequential的使用
以下面的神经网络为例,进行一个简单的搭建实战,并说明sequential的用法。
未使用sequential时神经网络的代码如下:
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear
class Ceshi(nn.Module):
def __init__(self):
super(Ceshi, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2)
self.maxpool1 = MaxPool2d(2)
self.conv2 = Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2)
self.maxpool2 = MaxPool2d(2)
self.conv3 = Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2)
self.maxpool3 = MaxPool2d(2)
self.flatten = Flatten()
self.linear1 = Linear(1024, 64)
self.linear2 = 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)
return x
ceshi = Ceshi()
# 在实际中,可以创建一个输入去检查网络正确性,比如下面创建一个input
# torch提供了一些比较简单的方法去创建数据。我们可以指定我们想要创建的数据的形状,其中的数可以指定为都是0、都是1或者是随机数
# 下面创造一个数据作为输入,其中的数都指定为1,通过这个输入去检查网络的正确性
input = torch.ones((64, 3, 32, 32))
output = ceshi(input)
print(output.shape)
使用sequential后神经网络的代码如下,相比未使用时代码简洁了很多:
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
class Ceshi(nn.Module):
def __init__(self):
super(Ceshi, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
ceshi = Ceshi()
# 在实际中,可以创建一个输入去检查网络正确性,比如下面创建一个input
# torch提供了一些比较简单的方法去创建数据。我们可以指定我们想要创建的数据的形状,其中的数可以指定为都是0、都是1或者是随机数
# 下面创造一个数据作为输入,其中的数都指定为1,通过这个输入去检查网络的正确性
input = torch.ones((64, 3, 32, 32))
output = ceshi(input)
print(output.shape)
# 输出结果:torch.Size([64, 10])
使用tensorboard工具对上面的ceshi网络进行显示:
writer = SummaryWriter("logs")
writer.add_graph(ceshi, input)
writer.close()
把ceshi的方框点开可以进一步查看网络架构:
进一步打开这些层:
损失函数与反向传播
Loss Functions:
-
- 计算实际输出和目标之间的差距
- 为我们更新输出提供一定的依据(反向传播)
下面介绍三个Loss Function:
① nn.L1Loss
——Creates a criterion that measures the mean absolute error (MAE) between each element in the input x and target y.
② nn.MSELoss
——Creates a criterion that measures the mean squared error (squared L2 norm) between each element in the input x and target y.
③ nn.CrossEntropyLoss
——This criterion computes the cross entropy loss between input logits and target.
代码实例如下:
import torch
from torch import nn
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))
loss1 = nn.L1Loss()
loss_L1 = loss1(inputs, targets)
print("loss_L1 =", loss_L1)
loss2 = nn.MSELoss()
Loss_MSE = loss2(inputs, targets)
print("Loss_MSE =", Loss_MSE)
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss3 = nn.CrossEntropyLoss()
Loss_CrossEntropy = loss3(x, y)
print("Loss_CrossEntropy =", Loss_CrossEntropy)
下面使用小实战搭建的网络结构+CIFAR10数据集进行CrossEntropyLoss的计算:
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
testset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(testset, batch_size=32)
class Ceshi(nn.Module):
def __init__(self):
super(Ceshi, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
ceshi = Ceshi()
loss = nn.CrossEntropyLoss()
for data in dataloader:
imgs, targets = data
outputs = ceshi(imgs)
Loss_CrossEntropy = loss(outputs, targets)
Loss_CrossEntropy.backward()
print("Loss_CrossEntropy =", Loss_CrossEntropy)
断点打在33行,进行Debugger,可看到部分参数如下:
继续往下运行一步,用backward去做一个反向传播,计算出各个节点的参数,grad参数如下图,有关梯度和梯度下降法可参见李宏毅老师的课程:
优化器
各类优化器原理在李宏毅老师机器学习的课程中也有详细讲述,下面代码以SGD为例:
import torch.optim
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
testset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(testset, batch_size=32)
class Ceshi(nn.Module):
def __init__(self):
super(Ceshi, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
ceshi = Ceshi()
loss = nn.CrossEntropyLoss()
optim = torch.optim.SGD(ceshi.parameters(), lr=0.001)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = ceshi(imgs)
Loss_CrossEntropy = loss(outputs, targets)
optim.zero_grad()
Loss_CrossEntropy.backward()
optim.step()
# print("Loss_CrossEntropy =", Loss_CrossEntropy)
running_loss += Loss_CrossEntropy
print("running_loss =", running_loss)
现有网络模型的使用及修改
以torchvision Classification中的VGG为例:
VGG有在ImageNet上预训练的版本和没有经过预训练的版本:
代码实例:
import torchvision
# 没有预训练
from torch import nn
vgg16_false = torchvision.models.vgg16(pretrained=False)
# 有预训练
vgg16_true = torchvision.models.vgg16(pretrained=True)
print(vgg16_true)
# vgg最后的out_features是1000,ImageNet数据集就有1000个类别
# 以CIFAR10为例,对vgg网络进行增加module和修改module的操作
# CIFAR10有10个类别,最后的out_features需要变成10
# 增加module操作如下:
vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
# 不加.classifier就是把linear的module加在classifier的外面
# vgg16_true.add_module('add_linear', nn.linear(1000, 10))
print(vgg16_true)
# 修改module操作如下
vgg16_false.classifier[6] = nn.Linear(4096, 10)
print(vgg16_false)
VGG网络原始的classifier如下图:
增加Linear module后的classifier如下图:
修改module后的classifier如下图:
网络模型的保存与读取
save模块代码实例:
import torch
import torchvision.models
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1 模型结构+模型参数
torch.save(vgg16, "vgg_method1.pth")
# 保存方式2 模型参数(官方推荐)
torch.save(vgg16.state_dict(), "vgg16_method2.pth")
load模块代码实例:
import torch
# 加载通过方式1保存的模型
import torchvision
model = torch.load("vgg_method1.pth")
print(model)
# 加载通过方式2保存的模型
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
print(vgg16)
输出结果是两个vgg网络的模型。
注:如果保存的是自己写的网络模型,那么在加载模型的py文件中,需要import保存模型结构的那个文件。