Cha*_*ker 8 machine-learning deep-learning conv-neural-network pytorch
我正在pytorch论坛中浏览这篇文章,我也想这样做。原始帖子删除并添加了图层,但我认为我的情况没有什么不同。我还想添加图层或更多过滤器或单词嵌入。我的主要动机是AI代理不知道全部词汇/词典,因为它很大。我强烈希望(暂时)不要按字符进行RNN。
所以对我来说,发生的事情是,当代理开始向前传递时,它可能会找到从未见过的新单词,并且需要将它们添加到嵌入表中(或者可能在开始向前传递之前添加新的过滤器)。
所以我想确定的是:
如何做到这一点?任何有效的示例代码?
只是为您的问题标题添加一个答案:“如何在 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)
Run Code Online (Sandbox Code Playgroud)
这是一个棘手的问题,因为我认为答案是“取决于”,特别是取决于您想如何处理优化器。
让我们从您的具体问题开始 - 嵌入。特别是,您询问如何添加嵌入以动态地允许更大的词汇量。我的第一个建议是,如果你对词汇量的上限有一个很好的认识,那么从一开始就让嵌入足够大以应对它,因为这更有效率,而且无论如何你最终都会需要记忆。但这不是你问的。因此 - 要动态更改您的嵌入,您需要用新的覆盖旧的嵌入,并将更改通知您的优化器。每当您在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)
Run Code Online (Sandbox Code Playgroud)
但是,您不应该对收到的每个新单词都这样做,因为这种复制需要时间(并且需要大量内存,因为嵌入在短时间内存在两次 - 如果您几乎没有内存,只需进行嵌入从一开始就足够大)。因此,改为一次动态增加几百个插槽的大小。
此外,这第一步已经提出了一些问题:
nn.Module知道新的嵌入参数?处理这个问题的__setattr__方法nn.Module(见这里)nn.Parameters或者nn.Modules不是嵌入的呢?这些你一样对待。您基本上只是实例化它们,并将它们附加到它们的父模块。该__setattr__方法将处理其余部分。所以你可以完全动态地这样做......当然,优化器除外。除了主模型模块之外,优化器是唯一“知道”参数的其他东西。所以你需要让优化器知道任何变化。
这很棘手,如果你想更复杂一点,如果你不关心保持优化器状态,这很容易。但是,即使您想对其进行复杂处理,也有充分的理由说明您无论如何都不应该这样做。更多关于下面的内容。
不管怎样,如果你不在乎,一个简单的
# simply overwrite your old optimizer
optimizer = optim.SGD(model.parameters(), lr=0.001)
Run Code Online (Sandbox Code Playgroud)
会做。但是,如果您想转移旧状态,可以使用与存储和稍后从磁盘加载参数和优化器状态相同的方式:使用.state_dict()和.load_state_dict()方法。然而,这仅适用于扭曲:
# 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
Run Code Online (Sandbox Code Playgroud)
但是,这就是为什么您可能永远不应该像这样更新优化器,而是应该从头开始重新初始化,并且只接受状态信息丢失的原因:当您更改计算图时,您会更改所有参数的前向和后向计算沿着你的计算路径(如果你没有分支架构,这条路径将是你的整个图)。这更具体地意味着,nn.Module如果您更改之前应用的某个函数 (=layer/ nn.Module) ,则您的函数 (=layer/ )的输入将有所不同,如果您更改稍后应用的某个函数 (=layer/ ),则梯度也会发生变化nn.Module。这反过来会使优化器的整个状态无效. 因此,如果您保留优化器的状态,它将是为不同的计算图计算的状态,并且如果您尝试将其应用于新的计算图,则可能会导致优化器的一部分出现灾难性行为。(我去过那儿 ...)
所以 - 总结一下:我真的建议尽量保持简单,并且尽可能保守地更改参数,而不是触及优化器。
| 归档时间: |
|
| 查看次数: |
947 次 |
| 最近记录: |