首页 > 解决方案 > 在pytorch中构造参数组

问题描述

torch.optim 文档中,指出可以使用不同的优化超参数对模型参数进行分组和优化。它说

例如,当想要指定每层学习率时,这非常有用:

optim.SGD([
                {'params': model.base.parameters()},
                {'params': model.classifier.parameters(), 'lr': 1e-3}
            ], lr=1e-2, momentum=0.9)

这意味着model.base的参数将使用 的默认学习率1e-2model.classifier的参数将使用 的学习率,并且所有参数都将使用1e-3的动量。0.9

我想知道如何定义具有parameters()属性的此类组。我想到的是某种形式的东西

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.base()
        self.classifier()

        self.relu = nn.ReLU()

    def base(self):
        self.fc1 = nn.Linear(1, 512)
        self.fc2 = nn.Linear(512, 264)

    def classifier(self):
        self.fc3 = nn.Linear(264, 128)
        self.fc4 = nn.Linear(128, 964)

    def forward(self, y0):

        y1 = self.relu(self.fc1(y0))
        y2 = self.relu(self.fc2(y1))
        y3 = self.relu(self.fc3(y2))

        return self.fc4(y3)

我应该如何修改上面的代码片段才能得到model.base.parameters()?是定义 ann.ParameterList并明确将所需层的weights 和es 添加到该列表的唯一方法吗?bias最佳做法是什么?

标签: pytorch

解决方案


我将展示解决此问题的三种方法。最后,它归结为个人喜好。


- 用 分组参数nn.ModuleDict

我在这里注意到一个答案,nn.Sequential用于对允许parameters使用nn.Sequential. 事实上base,分类器可能不仅仅是顺序层。我相信更通用的方法是保持模块不变,而是初始化一个附加nn.ModuleDict模块,该模块将包含优化组在单独nn.ModuleList的 s 中排序的所有参数:

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()

        self.fc1 = nn.Linear(1, 512)
        self.fc2 = nn.Linear(512, 264)
        self.fc3 = nn.Linear(264, 128)
        self.fc4 = nn.Linear(128, 964)

        self.params = nn.ModuleDict({
            'base': nn.ModuleList([self.fc1, self.fc2]),
            'classifier': nn.ModuleList([self.fc3, self.fc4])})

    def forward(self, y0):
        y1 = self.relu(self.fc1(y0))
        y2 = self.relu(self.fc2(y1))
        y3 = self.relu(self.fc3(y2))
        return self.fc4(y3)

然后你可以定义你的优化器:

optim.SGD([
    {'params': model.params.base.parameters()},
    {'params': model.params.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)

请注意MyModel'parameters生成器不会包含重复的参数。


- 创建用于访问参数组的界面。

一个不同的解决方案是在 中提供一个接口nn.Module来将参数分成组:

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()

        self.fc1 = nn.Linear(1, 512)
        self.fc2 = nn.Linear(512, 264)
        self.fc3 = nn.Linear(264, 128)
        self.fc4 = nn.Linear(128, 964)

    def forward(self, y0):
        y1 = self.relu(self.fc1(y0))
        y2 = self.relu(self.fc2(y1))
        y3 = self.relu(self.fc3(y2))
        return self.fc4(y3)

    def base_params(self):
        return chain(m.parameters() for m in [self.fc1, self.fc2])

    def classifier_params(self):
        return chain(m.parameters() for m in [self.fc3, self.fc4])

导入itertools.chainchain.

然后定义你的优化器:

optim.SGD([
    {'params': model.base_params()},
    {'params': model.classifier_params(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)

- 使用 child nn.Modules。

最后,您可以将模块部分定义为子模块(这里归结为方法作为nn.Sequential一个,但您可以将其推广到任何子模块)。

class Base(nn.Sequential):
    def __init__(self):
        super().__init__(nn.Linear(1, 512),
                         nn.ReLU(),
                         nn.Linear(512, 264),
                         nn.ReLU())

class Classifier(nn.Sequential):
    def __init__(self):
        super().__init__(nn.Linear(264, 128),
                         nn.ReLU(),
                         nn.Linear(128, 964))

class MyModel(nn.Module):
    def __init__(self):
        super().__init__()

        self.base = Base()
        self.classifier = Classifier()

    def forward(self, y0):
        features = self.base(y0)
        out = self.classifier(features)
        return out

在这里,您可以再次使用与第一种方法相同的接口:

optim.SGD([
    {'params': model.base.parameters()},
    {'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)

我认为这是最佳实践。但是,它会强制您将每个组件定义为单独的nn.Module,这在尝试更复杂的模型时可能会很麻烦。


推荐阅读