一:加载预训练模型

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的最后面。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐