前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Pytorch】自定义模型、自定义损失函数及模型删除修改层的常用操作

【Pytorch】自定义模型、自定义损失函数及模型删除修改层的常用操作

作者头像
深度学习思考者
发布2023-10-17 16:28:37
5070
发布2023-10-17 16:28:37
举报
问题1:更改模型最后一层,删除最后一层,添加层。

改变模型最后一层

代码语言:javascript
复制
# Load the model
model = models.resnet18(pretrained = False)

# Get number of parameters going in to the last layer. we need this to change the final layer. 
num_final_in = model.fc.in_features

# The final layer of the model is model.fc so we can basically just overwrite it 
#to have the output = number of classes we need. Say, 300 classes.
NUM_CLASSES = 300
model.fc = nn.Linear(num_final_in, NUM_CLASSES)

若有些网络的最后一层不是FC层,那么我们可以先去获取最后一层的层名,再根据层名进行替换

代码语言:javascript
复制
# Load the model
model = models.resnet18(pretrained = False)

# 打印所有层的层名
for name, module in model.named_modules():
    print(name)

删除最后一层

我们可以像以前一样使用 model.children() 来获取层。然后,我们可以通过在其上使用 list() 命令将其转换为列表。然后,我们可以通过索引列表来删除最后一层。最后,我们可以使用 PyTorch 函数 nn.Sequential() 将这个修改后的列表一起堆叠到一个新模型中。可以以任何你想要的方式编辑列表。也就是说,如果你想要倒数第 3 层图像的特征,你可以删除最后 2 层!

甚至可以从模型中间删除层。但很明显,这会导致进入其后层的特征数量不正确,因为大多数层都会改变图像的大小。在这种情况下,你可以索引模型的特定层并覆盖它!

代码语言:javascript
复制
# Load the model
model = models.resnet18(pretrained = False)

new_model = nn.Sequential(*list(model.children())[:-1])

# 获取倒数第3层
new_model_2_removed = nn.Sequential(*list(model.children())[:-2])

添加图层

比如说,想向我们现在拥有的模型添加一个全连接的层。一种明显的方法是编辑我上面讨论的列表并向其附加另一层。然而,通常我们训练了这样一个模型,并想看看我们是否可以加载该模型,并在其之上添加一个新层。如上所述,加载的模型应该与保存的模型具有相同的体系结构,因此我们不能使用列表方法。

我们需要在上面添加层。在 PyTorch 中执行此操作的方法很简单——我们只需要创建一个自定义模型!这将我们带到下一节 - 创建自定义模型!

自定义模型

让我们制作一个自定义模型。如上所述,我们将从预训练网络加载一半模型。这看起来很复杂,对吧?模型的一半是经过训练的,一半是新的。此外,我们希望其中一些被冻结。有些是可更新的。一旦你完成了这个,你就可以在 PyTorch 中对模型架构做任何事情。

代码语言:javascript
复制
# Some imports first
import torch.nn as nn
import math
import torch.utils.model_zoo as model_zoo
import torch
from torch.autograd.variable import Variable
from torchvision import datasets, models, transforms

# New models are defined as classes. Then, when we want to create a model we create an object instantiating this class.
class Resnet_Added_Layers_Half_Frozen(nn.Module):
    def __init__(self, LOAD_VIS_URL=None):
        super(ResnetCombinedFull2, self).__init__()
    
         # Start with half the resnet model, swap out the final layer because that's the model we had defined above. 
        model = models.resnet18(pretrained = False)
        num_final_in = model.fc.in_features
        model.fc = nn.Linear(num_final_in, 300)
        
        # Now that the architecture is defined same as above, let's load the model we would have trained above. 
        checkpoint = torch.load(MODEL_PATH)
        model.load_state_dict(checkpoint)
        
        
        # Let's freeze the same as above. Same code as above without the print statements
        child_counter = 0
        for child in model.children():
            if child_counter < 6:
                for param in child.parameters():
                    param.requires_grad = False
            elif child_counter == 6:
                children_of_child_counter = 0
                for children_of_child in child.children():
                    if children_of_child_counter < 1:
                        for param in children_of_child.parameters():
                            param.requires_grad = False
                    else:
                        children_of_child_counter += 1

            else:
                print("child ",child_counter," was not frozen")

            child_counter += 1
        
        # Now, let's define new layers that we want to add on top. 
        # Basically, these are just objects we define here. The "adding on top" is defined by the forward()
        # function which decides the flow of the input data into the model.
        
        # NOTE - Even the above model needs to be passed to self.
        self.vismodel = nn.Sequential(*list(model.children()))
        self.projective = nn.Linear(512,400)
        self.nonlinearity = nn.ReLU(inplace=True)
        self.projective2 = nn.Linear(400,300)
        
    
    # The forward function defines the flow of the input data and thus decides which layer/chunk goes on top of what.
    def forward(self,x):
        x = self.vismodel(x)
        x = torch.squeeze(x)
        x = self.projective(x)
        x = self.nonlinearity(x)
        x = self.projective2(x)
        return x

自定义损失函数

现在我们已经有了我们的模型,我们可以加载任何东西并创建我们想要的任何架构。这给我们留下了任何管道中的 2 个重要组件 - 加载数据和训练部分。我们来看看训练部分。这一步最重要的两个组成部分是优化器和损失函数。损失函数量化了我们现有模型与我们想要达到的目标之间的距离,优化器决定如何更新参数,以便我们可以最大限度地减少损失。

有时,我们需要定义自己的损失函数。这里有一些事情要知道

  • 自定义损失函数也是使用自定义类定义的。它们像自定义模型一样继承自 torch.nn.Module。
  • 通常,我们需要更改其中一项输入的维度。这可以使用 view() 函数来完成。
  • 如果我们想为张量添加维度,请使用 unsqueeze() 函数。
  • 损失函数最终返回的值必须是标量值。不是矢量/张量。
  • 返回的值必须是一个变量。这样它就可以用于更新参数。最好的方法是确保传入的 x 和 y 都是变量。这样,两者的任何函数也将是一个变量。
  • Pytorch 变量只是一个 Pytorch 张量,但 Pytorch 正在跟踪对其进行的操作,以便它可以反向传播以获得梯度。

这里我展示了一个名为 Regress_Loss 的自定义损失,它将 2 种输入 x 和 y 作为输入。然后将 x 重塑为与 y 相似,最后通过计算重塑后的 x 和 y 之间的 L2 差来返回损失。这是你在训练网络中经常遇到的标准事情。

将 x 视为形状 (5,10),将 y 视为形状 (5,5,10)。所以,我们需要给 x 添加一个维度,然后沿着添加的维度重复它以匹配 y 的维度。然后,(xy) 将是形状 (5,5,10)。我们必须将所有三个维度相加,即三个 torch.sum() 以获得标量。

该操作经常遇到,和numpy中的广播机制一致,需要掌握

代码语言:javascript
复制
# 
class Regress_Loss(torch.nn.Module):
    
    def __init__(self):
        super(Regress_Loss,self).__init__()
        
    def forward(self,x,y):
        y_shape = y.size()[1]
        x_added_dim = x.unsqueeze(1)
        x_stacked_along_dimension1 = x_added_dim.repeat(1, NUM_WORDS, 1)
        diff = torch.sum((y - x_stacked_along_dimension1)**2, 2)
        totloss = torch.sum(torch.sum(torch.sum(diff)))
        return totloss
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 改变模型最后一层
  • 删除最后一层
  • 添加图层
  • 自定义模型
  • 自定义损失函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档