预训练模型的加载,保存模型,模型的修改(迁移学习)
一:加载预训练模型
import torchvision.models as models # 导入models模块,此模块中存有众多预训练好的模型
alexnet = models.alexnet(weights=None) # 加载预训练模型,但是不加载任何预训练的权重,而是从头开始训练模型。这意味着模型的所有参数将被随机初始化
# alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT) # 加载预训练模型,并且加载默认的预训练权重参数
print(alexnet)
打印结果如下:模型由三部分组成,features层、avgpool层和classifier层。每个层中又有一些子层。不清楚如何查看模型结构的朋友,可以查看该文章:查看模型、查看模型参数的方法(主要针对迁移学习)-CSDN博客
AlexNet(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU(inplace=True)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace=True)
(8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(9): ReLU(inplace=True)
(10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
二:保存模型、保存模型+参数
import torchvision.models as models
import torch
# 加载预训练模型,并且加载默认的预训练权重参数
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
# 1. 仅保存模型的参数
# alexnet.state_dict():模型的参数字典
# alexnet_weights.pth:保存模型参数的文件名
torch.save(alexnet.state_dict(), "alexnet_weights.pth")
# 2. 保存 模型 + 模型参数
torch.save(alexnet, "alexnet.pth")
三:加载模型、加载模型+参数
import torchvision.models as models
import torch
# 1. 加载模型 + 参数
net = torch.load("alexnet.pth")
# 2. 已经有了模型,加载自己保存的模型参数
alexnet = models.alexnet(weights=None)
alexnet.load_state_dict(torch.load("alexnet_weights.pth"))
四:模型的修改
先导入Alexnet网络,查看网络的结构,在此基础上进行增、删、改
import torchvision.models as models
import torch
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
print(alexnet)
alexnet网络结构如下:
AlexNet(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU(inplace=True)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace=True)
(8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(9): ReLU(inplace=True)
(10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
torchvision中alexnet的源码如下:
class AlexNet(nn.Module):
def __init__(self, num_classes: int = 1000, dropout: float = 0.5) -> None:
super().__init__()
_log_api_usage_once(self)
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(
nn.Dropout(p=dropout),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(p=dropout),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
1. 删除网络的模块/层
1.1 例1:删除模型的classifier模块中的最后两层
import torchvision.models as models
import torch
import torchinfo
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
# 删除模型的classifier模块中的最后两层
del alexnet.classifier[-2:]
# 测试模型能否跑通,torchinfo.summary()随机生成一个指定维度的张量,然后进行前向传播
# 之所以不使用print(),是因为能打印出来的网络,不一定能跑的通。所以需要进行前向传播的测试。
torchinfo.summary(alexnet, (1, 3, 224, 224))
输出结果如下:
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
AlexNet [1, 4096] --
├─Sequential: 1-1 [1, 256, 6, 6] --
│ └─Conv2d: 2-1 [1, 64, 55, 55] 23,296
│ └─ReLU: 2-2 [1, 64, 55, 55] --
│ └─MaxPool2d: 2-3 [1, 64, 27, 27] --
│ └─Conv2d: 2-4 [1, 192, 27, 27] 307,392
│ └─ReLU: 2-5 [1, 192, 27, 27] --
│ └─MaxPool2d: 2-6 [1, 192, 13, 13] --
│ └─Conv2d: 2-7 [1, 384, 13, 13] 663,936
│ └─ReLU: 2-8 [1, 384, 13, 13] --
│ └─Conv2d: 2-9 [1, 256, 13, 13] 884,992
│ └─ReLU: 2-10 [1, 256, 13, 13] --
│ └─Conv2d: 2-11 [1, 256, 13, 13] 590,080
│ └─ReLU: 2-12 [1, 256, 13, 13] --
│ └─MaxPool2d: 2-13 [1, 256, 6, 6] --
├─AdaptiveAvgPool2d: 1-2 [1, 256, 6, 6] --
├─Sequential: 1-3 [1, 4096] --
│ └─Dropout: 2-14 [1, 9216] --
│ └─Linear: 2-15 [1, 4096] 37,752,832
│ └─ReLU: 2-16 [1, 4096] --
│ └─Dropout: 2-17 [1, 4096] --
│ └─Linear: 2-18 [1, 4096] 16,781,312
==========================================================================================
Total params: 57,003,840
Trainable params: 57,003,840
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 710.59
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 3.95
Params size (MB): 228.02
Estimated Total Size (MB): 232.56
==========================================================================================
从结果中可以看到,之前有7层的classifier,现在只剩下5层了。
# 删除之前
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
# 删除之后
├─Sequential: 1-3 [1, 4096] --
│ └─Dropout: 2-14 [1, 9216] --
│ └─Linear: 2-15 [1, 4096] 37,752,832
│ └─ReLU: 2-16 [1, 4096] --
│ └─Dropout: 2-17 [1, 4096] --
│ └─Linear: 2-18 [1, 4096] 16,781,312
1.2 例2:删除网络中的整个classifier模块
若像之前一样,直接使用del关键字来删除整个classifier模块,则会报错。
import torchvision.models as models
import torch
import torchinfo
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
# 删除模型的classifier模块
del alexnet.classifier
# 测试模型能否跑通
torchinfo.summary(alexnet, (1, 3, 224, 224))
报错信息:
AttributeError: 'AlexNet' object has no attribute 'classifier'
原因分析:之所以会出现这种报错,是因为我们删除的是初始化函数中的 self.classifier 变量,但是在forward函数中,依然执行了 x = self.classifier(x)。但是此时,self.classifier已经被删除,所以会报错。要想解决改问题,就需要重新定义forward函数。
import torchvision.models as models
import torch
import torchinfo
class MyModel(torch.nn.Module):
def __init__(self, alexnet):
super().__init__()
self.alexnet = alexnet
def forward(self, x):
for name, layer in self.alexnet.named_children():
if name != "classifier": # 跳过classifier模块
x = layer(x)
return x
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
my_model = MyModel(alexnet)
# 测试模型能否跑通
torchinfo.summary(my_model, (1, 3, 224, 224))
输出结果如下:
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
MyModel [1, 256, 6, 6] --
├─AlexNet: 1-1 -- 58,631,144
│ └─Sequential: 2-1 [1, 256, 6, 6] --
│ │ └─Conv2d: 3-1 [1, 64, 55, 55] 23,296
│ │ └─ReLU: 3-2 [1, 64, 55, 55] --
│ │ └─MaxPool2d: 3-3 [1, 64, 27, 27] --
│ │ └─Conv2d: 3-4 [1, 192, 27, 27] 307,392
│ │ └─ReLU: 3-5 [1, 192, 27, 27] --
│ │ └─MaxPool2d: 3-6 [1, 192, 13, 13] --
│ │ └─Conv2d: 3-7 [1, 384, 13, 13] 663,936
│ │ └─ReLU: 3-8 [1, 384, 13, 13] --
│ │ └─Conv2d: 3-9 [1, 256, 13, 13] 884,992
│ │ └─ReLU: 3-10 [1, 256, 13, 13] --
│ │ └─Conv2d: 3-11 [1, 256, 13, 13] 590,080
│ │ └─ReLU: 3-12 [1, 256, 13, 13] --
│ │ └─MaxPool2d: 3-13 [1, 256, 6, 6] --
│ └─AdaptiveAvgPool2d: 2-2 [1, 256, 6, 6] --
==========================================================================================
Total params: 61,100,840
Trainable params: 61,100,840
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 656.05
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 3.88
Params size (MB): 9.88
Estimated Total Size (MB): 14.36
=========================================================================================
输出结果中,已经没有classifier的前向传播了,使用这种方法,不需要删除self.classifier这个模块,只需要在forward函数中不使用classifier模块就行。
2. 修改网络的模块/层
2.1 例1:修改classifier中的第六层
将 classifier 模块中的第6层由 Linear(in_features=4096, out_features=1000, bias=True) 修改为 Linear(in_features=4096, out_features=1024, bias=True)。
import torchvision.models as models
import torch.nn as nn
import torchinfo
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
# 修改
alexnet.classifier[6] = nn.Linear(in_features=4096, out_features=1024, bias=True)
# 测试模型能否跑通
torchinfo.summary(alexnet, (1, 3, 224, 224))
输出结果如下:
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
AlexNet [1, 1024] --
├─Sequential: 1-1 [1, 256, 6, 6] --
│ └─Conv2d: 2-1 [1, 64, 55, 55] 23,296
│ └─ReLU: 2-2 [1, 64, 55, 55] --
│ └─MaxPool2d: 2-3 [1, 64, 27, 27] --
│ └─Conv2d: 2-4 [1, 192, 27, 27] 307,392
│ └─ReLU: 2-5 [1, 192, 27, 27] --
│ └─MaxPool2d: 2-6 [1, 192, 13, 13] --
│ └─Conv2d: 2-7 [1, 384, 13, 13] 663,936
│ └─ReLU: 2-8 [1, 384, 13, 13] --
│ └─Conv2d: 2-9 [1, 256, 13, 13] 884,992
│ └─ReLU: 2-10 [1, 256, 13, 13] --
│ └─Conv2d: 2-11 [1, 256, 13, 13] 590,080
│ └─ReLU: 2-12 [1, 256, 13, 13] --
│ └─MaxPool2d: 2-13 [1, 256, 6, 6] --
├─AdaptiveAvgPool2d: 1-2 [1, 256, 6, 6] --
├─Sequential: 1-3 [1, 1024] --
│ └─Dropout: 2-14 [1, 9216] --
│ └─Linear: 2-15 [1, 4096] 37,752,832
│ └─ReLU: 2-16 [1, 4096] --
│ └─Dropout: 2-17 [1, 4096] --
│ └─Linear: 2-18 [1, 4096] 16,781,312
│ └─ReLU: 2-19 [1, 4096] --
│ └─Linear: 2-20 [1, 1024] 4,195,328
==========================================================================================
Total params: 61,199,168
Trainable params: 61,199,168
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 714.78
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 3.95
Params size (MB): 244.80
Estimated Total Size (MB): 249.35
=========================================================================================
3. 向网络中添加模块/层
3.1 例1:添加单层
import torchvision.models as models
import torch.nn as nn
import torchinfo
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
# 添加
alexnet.classifier.add_module('7', nn.ReLU(inplace=True))
alexnet.classifier.add_module('8', nn.Linear(in_features=1000, out_features=20))
# 测试模型能否跑通
torchinfo.summary(alexnet, (1, 3, 224, 224))
输出结果如下:
==========================================================================================
Layer (type:depth-idx) Output Shape Param #
==========================================================================================
AlexNet [1, 20] --
├─Sequential: 1-1 [1, 256, 6, 6] --
│ └─Conv2d: 2-1 [1, 64, 55, 55] 23,296
│ └─ReLU: 2-2 [1, 64, 55, 55] --
│ └─MaxPool2d: 2-3 [1, 64, 27, 27] --
│ └─Conv2d: 2-4 [1, 192, 27, 27] 307,392
│ └─ReLU: 2-5 [1, 192, 27, 27] --
│ └─MaxPool2d: 2-6 [1, 192, 13, 13] --
│ └─Conv2d: 2-7 [1, 384, 13, 13] 663,936
│ └─ReLU: 2-8 [1, 384, 13, 13] --
│ └─Conv2d: 2-9 [1, 256, 13, 13] 884,992
│ └─ReLU: 2-10 [1, 256, 13, 13] --
│ └─Conv2d: 2-11 [1, 256, 13, 13] 590,080
│ └─ReLU: 2-12 [1, 256, 13, 13] --
│ └─MaxPool2d: 2-13 [1, 256, 6, 6] --
├─AdaptiveAvgPool2d: 1-2 [1, 256, 6, 6] --
├─Sequential: 1-3 [1, 20] --
│ └─Dropout: 2-14 [1, 9216] --
│ └─Linear: 2-15 [1, 4096] 37,752,832
│ └─ReLU: 2-16 [1, 4096] --
│ └─Dropout: 2-17 [1, 4096] --
│ └─Linear: 2-18 [1, 4096] 16,781,312
│ └─ReLU: 2-19 [1, 4096] --
│ └─Linear: 2-20 [1, 1000] 4,097,000
│ └─ReLU: 2-21 [1, 1000] --
│ └─Linear: 2-22 [1, 20] 20,020
==========================================================================================
Total params: 61,120,860
Trainable params: 61,120,860
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 714.70
==========================================================================================
Input size (MB): 0.60
Forward/backward pass size (MB): 3.95
Params size (MB): 244.48
Estimated Total Size (MB): 249.04
========================================================================================
3.2 例2 :一次添加多层
通过nn.Sequential构造网络片段,一次性往网络中添加多个层。
import torchvision.models as models
import torch
import torchinfo
import torch.nn as nn
class MyModel(torch.nn.Module):
def __init__(self, alexnet):
super().__init__()
self.alexnet = alexnet
block = nn.Sequential(
nn.ReLU(inplace=True),
nn.Linear(in_features=1000, out_features=20, bias=True)
)
self.alexnet.add_module('block', block)
def forward(self, x):
for name, layer in self.alexnet.named_children():
x = layer(x)
if name == "avgpool":
x = torch.flatten(x, 1)
return x
# 加载模型
alexnet = models.alexnet(weights=models.AlexNet_Weights.DEFAULT)
my_model = MyModel(alexnet)
# 测试模型能否跑通
# torchinfo.summary(my_model, (1, 3, 224, 224))
print(my_model)
输出结果如下:
MyModel(
(alexnet): AlexNet(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2))
(1): ReLU(inplace=True)
(2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU(inplace=True)
(5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(7): ReLU(inplace=True)
(8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(9): ReLU(inplace=True)
(10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(6, 6))
(classifier): Sequential(
(0): Dropout(p=0.5, inplace=False)
(1): Linear(in_features=9216, out_features=4096, bias=True)
(2): ReLU(inplace=True)
(3): Dropout(p=0.5, inplace=False)
(4): Linear(in_features=4096, out_features=4096, bias=True)
(5): ReLU(inplace=True)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
(block): Sequential(
(0): ReLU(inplace=True)
(1): Linear(in_features=1000, out_features=20, bias=True)
)
)
)
从结果中可以看出,已经将block模块添加到alexnet的最后面。
更多推荐
所有评论(0)