我正在pytorch论坛中浏览this post,我也想这样做。原始帖子删除并添加了图层,但我认为我的情况没有什么不同。我还想添加图层或更多过滤器或单词嵌入。我的主要动机是AI代理不知道全部词汇/词典,因为它很大。我强烈希望(暂时)不要逐个字符地进行RNN。
所以对我来说,发生的事情是,代理开始向前传递时,它可能会找到从未见过的新单词,因此需要将它们添加到嵌入表中(或者可能在开始向前传递之前添加新的过滤器)。
所以我想确定的是:
这是怎么做到的?任何有效的示例代码?
答案 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)
但是,您不应为收到的每个新单词执行此操作,因为此复制会花费时间(而且会占用大量内存,因为嵌入会在短时间内存在两次-如果您的内存几乎用完了,从一开始就让您的嵌入足够大)。因此,一次只能动态增加数百个插槽的大小。
此外,第一步已经引发了一些问题:
nn.Module
如何知道新的嵌入参数?
__setattr__
的{{1}}方法解决了这个问题(请参见here)nn.Module
或nn.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)
关键是构造函数的第一个参数接收迭代器。