如何在Pytorch中向优化器动态添加新参数?

时间:2019-04-11 20:41:36

标签: machine-learning deep-learning conv-neural-network pytorch

我正在pytorch论坛中浏览this post,我也想这样做。原始帖子删除并添加了图层,但我认为我的情况没有什么不同。我还想添加图层或更多过滤器或单词嵌入。我的主要动机是AI代理不知道全部词汇/词典,因为它很大。我强烈希望(暂时)不要逐个字符地进行RNN。

所以对我来说,发生的事情是,代理开始向前传递时,它可能会找到从未见过的新单词,因此需要将它们添加到嵌入表中(或者可能在开始向前传递之前添加新的过滤器)。

所以我想确定的是:

  1. 正确添加嵌入(在正确的时间,当创建新的计算图时),以便优化程序可以对其进行更新
  2. 过去参数的存储信息没有问题,例如如果它使用某种动量

这是怎么做到的?任何有效的示例代码?

3 个答案:

答案 0 :(得分:2)

这是一个棘手的问题,因为我认为答案是“取决于”的,特别是取决于您要如何使用优化程序。

让我们从您的特定问题开始-嵌入。特别是,您在询问如何添加嵌入以动态地使用更大的词汇。我的第一个建议是,如果您对词汇量的上限有一个很好的了解,请从一开始就使嵌入足够大以应对它,因为这样做效率更高,而且无论如何最终都需要记忆。但这不是您要的。因此-要动态更改嵌入,您需要用新的覆盖旧的嵌入,并将更改通知您的优化器。只要在try ... except块中遇到任何因旧嵌入而引起的异常,就可以简单地做到这一点。这应该大致遵循以下想法:

# from within whichever module owns the embedding
# remember the already trained weights
old_embedding_weights = self.embedding.weight.data
# create a new embedding of the new size
self.embedding = nn.Embedding(new_vocab_size, embedding_dim)
# initialize the values for the new embedding. this does random, but you might want to use something like GloVe
new_weights = torch.randn(new_vocab_size, embedding_dim)
# as your old values may have been updated, you want to retrieve these updates values
new_weights[:old_vocab_size] = old_embedding_weights
self.embedding.weights.data.copy_(new_weights)

但是,您不应为收到的每个新单词执行此操作,因为此复制会花费时间(而且会占用大量内存,因为嵌入会在短时间内存在两次-如果您的内存几乎用完了,从一开始就让您的嵌入足够大)。因此,一次只能动态增加数百个插槽的大小。

此外,第一步已经引发了一些问题:

  1. 我各自的nn.Module如何知道新的嵌入参数? __setattr__的{​​{1}}方法解决了这个问题(请参见here
  2. 第二,为什么我不简单地更改参数?这已经指向更改优化器的一些问题:pytorch在内部按对象ID保留引用。这意味着,如果更改对象,则所有这些引用都将指向可能不兼容的对象,因为其属性已更改。因此,我们应该简单地创建一个新参数。
  3. 不是嵌入的其他nn.Modulenn.Parameters呢?这些你都一样。您基本上只是实例化它们,并将它们附加到其父模块。 nn.Modules方法将处理其余的工作。这样您就可以完全动态地做到这一点...

当然,除了优化器。除了主模型模块外,优化器是唯一“了解”参数的其他东西。因此,您需要让优化器知道任何更改。

这是棘手的,如果您想对其进行完善,并且如果您不关心保持优化器状态,​​则非常容易。但是,即使您想对此做一些复杂的事情,也有很好的理由为什么您不应该这样做。详情请见下文。

无论如何,如果您不在乎,那么简单

__setattr__

可以。但是,如果您希望转移旧状态,可以使用与存储相同的方式进行操作,以后再从磁盘加载参数和优化器状态:使用# simply overwrite your old optimizer optimizer = optim.SGD(model.parameters(), lr=0.001) .state_dict()方法。但是,这仅适用于以下情况:

.load_state_dict()

但是,这就是为什么您可能从不像这样更新优化器,而是应该从头开始重新初始化并仅接受状态信息丢失的原因:当您更改计算图时,您可以沿计算路径更改所有参数的正向和反向计算(如果没有分支架构,则此路径将是您的整个图形)。更具体地说,这意味着,如果您更改了先前应用的某些功能(= layer / # extract the state dict from your old optimizer old_state_dict = optimizer.state_dict() # create a new optimizer optimizer = optim.SGD(model.parameters()) new_state_dict = optimizer.state_dict() # the old state dict will have references to the old parameters, in state_dict['param_groups'][xyz]['params'] and in state_dict['state'] # you now need to find the parameter mismatches between the old and new statedicts # if your optimizer has multiple param groups, you need to loop over them, too (I use xyz as a placeholder here. mostly, you'll only have 1 anyways, so just replace xyz with 0 new_pars = [p for p in new_state_dict['param_groups'][xyz]['params'] if not p in old_state_dict['param_groups'][xyz]['params']] old_pars = [p for p in old_state_dict['param_groups'][xyz]['params'] if not p in new_state_dict['param_groups'][xyz]['params']] # then you remove all the outdated ones from the state dict for pid in old_pars: old_state_dict['state'].pop(pid) # and add a new state for each new parameter to the state: for pid in new_pars: old_state_dict['param_groups'][xyz]['params'].append(pid) old_state_dict['state'][pid] = { ... } # your new state def here, depending on your optimizer ),则函数(= layer / nn.Module)的输入将有所不同;如果更改,则渐变也会更改。稍后应用某些功能(= layer / nn.Module)。反过来,会使优化器的整个状态无效。因此,如果保持优化器的状态不变,它将是为其他计算图计算的状态,如果尝试将其应用于新的计算图,则可能会导致优化器部分出现灾难性行为。 (我去过那里...)

所以-概括起来:我真的建议您尝试使其简单,并尽可能保守地更改参数,而不要接触优化器。

答案 1 :(得分:0)

只需为您的问题的标题添加答案:“如何动态地向Pytorch中的优化程序添加新参数?”

您可以随时将参数添加到优化器:

import torch
import torch.optim as optim

model = torch.nn.Linear(2, 2) 

# Initialize optimizer
optimizer = optim.Adam(model.parameters(), lr=0.001, momentum=0.9)

extra_params = torch.randn(2, 2)
optimizer.param_groups.append({'params': extra_params })

#then you can print your `extra_params`
print("extra params", extra_params)
print("optimizer params", optimizer.param_groups)

答案 2 :(得分:0)

如果你想自定义初始参数:

from itertools import chain

l1 = nn.Linear(3,3)
l2 = nn.Linear(2,3)
optimizer = optim.SGD(chain(l1.parameters(), l2.parameters()), lr=0.01, momentum=0.9)

关键是构造函数的第一个参数接收迭代器。