目录
Sequential
一、Sequential模块简介
“Sequential”是PyTorch中的一个容器,用于按顺序包含一系列子模块。
二、Sequential的使用
1. 导入必要的库
首先,需要导入PyTorch及其相关的模块:
import torch | |
import torch.nn as nn | |
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential |
2. 定义模型
接下来,可以使用Sequential来定义模型。以下是一个简单的卷积神经网络(CNN)的例子,该网络用于处理图像数据:
class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model1 = 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.model1(x) return x |
3. 实例化模型并测试
最后,可以实例化模型并传入一些随机数据来测试其输出:
# 实例化模型 | |
tudui = Tudui() print(tudui) input = torch.ones((64, 3, 32, 32)) output = tudui(input) print(output.shape) writer = SummaryWriter('logs_seq') writer.add_graph(tudui,input) writer.close() |
注意:padding理解:如果不padding,5*5的kernel按步长为1走,输出的尺寸就会比之前少4,所以肯定要把这个4用padding补上,所以两边都是2,所以padding就是2。
padding设置成 kernel_size//2 就是保持原通道了=卷积核中心对准边缘。
公式:same padding时,p=(f-1)\2 f是过滤器的尺寸,也是卷积核的尺寸,也就是kernel的大小。
池化:池化的stide默认为kernal_size,padding默认为0
conv2d的stride默认为1,maxpool的才是kernelsize。
参数讲解:
- 初始化卷积层 (
Conv2d
):- 第一个
Conv2d(3, 32, 5, padding=2)
: 输入通道数为3(RGB图像),输出通道数为32,卷积核大小为5x5,使用padding=2来保持输入和输出特征图的空间维度不变。 - 第二个和第三个
Conv2d
类似,但第二个卷积层保持输出通道数为32,第三个卷积层将输出通道数增加到64。
- 第一个
- 池化层 (
MaxPool2d
):- 在每个卷积层之后,都跟着一个
MaxPool2d(2)
池化层,其核大小为2x2,用于降低特征图的空间维度(高度和宽度减半)。
- 在每个卷积层之后,都跟着一个
- 展平层 (
Flatten
):- 在所有卷积和池化层之后,使用
Flatten
层将多维的输入一维化,以便可以传递给全连接层。
- 在所有卷积和池化层之后,使用
- 全连接层 (
Linear
):- 第一个
Linear(1024, 64)
: 输入特征数为1024(64*4*4),输出特征数为64。 - 第二个
Linear(64, 10)
: 输入特征数为64,输出特征数为10。
- 第一个
- Sequential 容器 (
self.model1
):- 将上述所有层封装在
Sequential
容器中,以简化模型的前向传播。
- 将上述所有层封装在
forward
方法- 定义模型的前向传播逻辑,即数据通过模型的方式。它简单地调用
self.model1(x)
,其中x
是输入数据。
- 定义模型的前向传播逻辑,即数据通过模型的方式。它简单地调用
损失函数与反向传播
定义模型之后,通常需要指定一个损失函数(Loss Function)来评估模型预测值与真实值之间的差异,并通过反向传播算法来更新模型的权重,以最小化这个损失。
损失函数
均方误差损失(MSELoss
)、交叉熵损失(CrossEntropyLoss
)。对于分类问题,如果模型的输出层使用了softmax激活函数,则通常使用交叉熵损失。
import torch from torch import nn from torch.nn import L1Loss, MSELoss 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)) loss = L1Loss(reduction='sum') result = loss(inputs, targets) loss_mse = nn.MSELoss() result_mse = loss_mse(inputs, targets) print(result) print(result_mse) x = torch.tensor([0.1,0.2,0.3], dtype = torch.float32) y = torch.tensor([1]) x = torch.reshape(x, (1,3)) loss_cross = nn.CrossEntropyLoss() result_loss = loss_cross() print(result_loss) |
反向传播
调用了损失函数的.backward()
方法,PyTorch就会自动计算图中所有可训练参数的梯度。
import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10(root='dataset', train=False, download=True, transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=1) class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model1 = 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.model1(x) return x tudui = Tudui() loss = nn.CrossEntropyLoss() for data in dataloader: imgs, targets = data outputs = tudui(imgs) print(outputs) # print(targets) result_loss = loss(outputs, targets) print(result_loss)# 计算实际输出和目标之间的差距 result_loss.backward()# 计算图中所有可训练参数的梯度 print("ok") |
注意:
交叉熵公式的输入值是未经过softmax的,经过softmax之后的输入值进行交叉熵函数和这个表达不一样,但最后得到的值和这个公式差别很小,几乎相等。
-log(x)中的x取值范围是0-1, 那么当x越大,损失也就越小。也就是命中概率大,损失小。
科普:反向传播意思就是,尝试如何调整网络过程中的参数才会导致最终的loss变小(因为是从loss开始推导参数,和网络的顺序相反,所以叫反向传播),以及梯度(gradient)的理解可以直接当成“斜率” 反向传播是计算梯度下降的一种方式。
经过softmax之后,概率之和为1,x[class]表示想要预测类的概率,所以x[class]越大,交叉熵越小,表示越准确。
参数介绍:
损失函数:
- L1Loss(L1范数损失):
- 计算输入和目标之间的绝对差值的平均值(当
reduction='mean'
时,默认设置),或者它们的总和(reduction='sum'
)。 - 适用于回归问题,它衡量的是预测值与实际值之间的绝对距离。
- 计算输入和目标之间的绝对差值的平均值(当
- MSELoss(均方误差损失):
- 计算输入和目标之间的平方差的平均值。
- 同样适用于回归问题,但它对较大的误差给予更大的惩罚。
CrossEntropyLoss(交叉熵损失)
- 用途:主要用于分类问题,特别是当输出层使用softmax激活函数时。
- 输入要求:
- logits(网络原始输出):未经softmax处理的分数,形状通常为
[batch_size, num_classes]
。 - targets(真实标签):每个样本的类别索引,形状通常为
[batch_size]
。
- logits(网络原始输出):未经softmax处理的分数,形状通常为
优化器:
用于更新神经网络的权重,以减少损失函数的值。
损失函数衡量了模型的预测值与真实值之间的差异,而优化器的目标则是通过调整模型的权重和偏置来最小化这个差异,从而提高模型的准确性和性能。
import torch import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter dataset = torchvision.datasets.CIFAR10(root='dataset', train=False, download=True, transform=torchvision.transforms.ToTensor()) dataloader = DataLoader(dataset, batch_size=1) class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.model1 = 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.model1(x) return x tudui = Tudui() loss = nn.CrossEntropyLoss() optim = torch.optim.SGD(tudui.parameters(), lr=0.01) for epoch in range(20): running_loss = 0.0 for data in dataloader: imgs, targets = data outputs = tudui(imgs) result_loss = loss(outputs, targets) optim.zero_grad() result_loss.backward() optim.step() print(result_loss) running_loss +=result_loss print(running_loss) |
参数介绍:
nn.CrossEntropyLoss
: 用于多分类问题的交叉熵损失。它结合了nn.LogSoftmax()
和nn.NLLLoss()
在单个类中。torch.optim.SGD
: 随机梯度下降优化器,用于更新网络权重以最小化损失函数。lr=0.01
: 学习率,控制权重更新的步长。tudui.parameters()
:这个方法返回了一个包含Tudui
模型中所有可训练参数的迭代器。
- 在每个epoch中,遍历数据加载器提供的所有样本。
- 对于每个样本,执行前向传播计算输出,计算损失,执行反向传播计算梯度,更新权重。
optim.zero_grad()
: 清除过往梯度。result_loss.backward()
: 计算当前梯度。optim.step()
: 根据梯度更新网络权重。
注意:
rho是人为设定的一个被乘数,episilon是一个保证分母不为0的很小值。
loss函数在其中只是起到了一个提供梯度的作用,而这个梯度就藏在optim中你对optim进行step操作时,step就会把它自身反向传播后的结果用进去。
现有网络模型的使用及修改
加载VGG16模型
-
加载具有默认预训练权重的VGG16模型:
vgg16_true = models.vgg16(weights=VGG16_Weights.DEFAULT)
-
加载无预训练权重的VGG16模型:
vgg16_false = models.vgg16(weights=None)
修改模型结构
在将VGG16模型用于不同的数据集(CIFAR10,它只有10个类别)时,你需要修改模型的分类器部分,以适应新的类别数。
-
修改
vgg16_true
的分类器:vgg16_true.classifier.add_module('add_linear', nn.Linear(1000, 10))
通过添加一个额外的全连接层来修改模型。
-
修改
vgg16_true
的分类器:# 用三个全连接层替换原始的分类器
new_classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 10) # CIFAR10有10个类别
)
vgg16_true.classifier = new_classifier
-
修改
vgg16_false
的分类器:vgg16_false.classifier[6] = nn.Linear(4096, 10)
直接替换
classifier
中的最后一个线性层。特征图的大小与新的全连接层兼容。
打印模型结构
print(vgg16_true) | |
print(vgg16_false) |
注意:
- 当修改模型以用于新的数据集时,如何根据输入特征图的大小来调整全连接层的输入维度。
- 加载CIFAR10数据集时,需要调整图像的大小和归一化参数,以匹配VGG16模型训练时使用的设置(CIFAR10通常不需要调整,因为CIFAR10的图像尺寸较小)。
网络模型的保存与读取
保存模型
保存模型的状态字典:
torch.save(vgg16.state_dict(),"vgg16_method2.pth") |
保存整个模型
torch.save(vgg16, "vgg16_method1.pth") |
陷阱:
#陷阱 class Tudui(nn.Module): def __init__(self): super(Tudui, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=3) def forward(self, x): x = self.conv1(x) return x tudui =Tudui() torch.save(tudui,"tudui_method1.pth") |
读取模型
import torch | |
import torchvision.models as models | |
# 实例化一个VGG16模型,不加载预训练权重 | |
vgg16 = models.vgg16(weights=None) | |
# 加载状态字典 | |
vgg16.load_state_dict(torch.load(r"D:\pycharm\深度学习\learn_torch\tensorboard\vgg16_method2.pth")) | |
# 打印模型结构(打印结构,不会显示参数值) | |
print(vgg16) | |
# 如果需要设置为评估模式 | |
vgg16.eval() |
如果保存了整个模型,可以这样加载:
import torch | |
# 加载整个模型 | |
model = torch.load(r"D:\pycharm\深度学习\learn_torch\tensorboard\vgg16_method1.pth") | |
print(model) | |
# 如果需要设置为评估模式 | |
model.eval() |
陷阱:
# 陷阱 model3 = model_save.torch.load(r"D:\pycharm\深度学习\learn_torch\tensorboard\tudui_method1.pth") print(model3) |