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

Machine learning 如何在Pytorch中动态地向优化器添加新参数?,machine-learning,deep-learning,conv-neural-network,pytorch,Machine Learning,Deep Learning,Conv Neural Network,Pytorch,我正在参加pytorch论坛,我也想这样做。最初的帖子删除并添加了图层,但我认为我的情况并没有那么不同。我还想添加层或更多过滤器或单词嵌入。我的主要动机是,人工智能代理并不预先知道整个词汇/词典,因为它很大。我强烈地(目前)不喜欢逐个角色的RNN 因此,对我来说,当代理开始向前传递时,它可能会发现从未见过的新词,需要将它们添加到嵌入表中(或者在开始向前传递之前添加新过滤器) 所以我想确定的是: 嵌入被正确地添加(在正确的时间,当生成新的计算图时),以便优化器可以更新它们 过去参数的存储信息没有问

我正在参加pytorch论坛,我也想这样做。最初的帖子删除并添加了图层,但我认为我的情况并没有那么不同。我还想添加层或更多过滤器或单词嵌入。我的主要动机是,人工智能代理并不预先知道整个词汇/词典,因为它很大。我强烈地(目前)不喜欢逐个角色的RNN

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

所以我想确定的是:

  • 嵌入被正确地添加(在正确的时间,当生成新的计算图时),以便优化器可以更新它们
  • 过去参数的存储信息没有问题,例如,如果使用某种动量
  • 如何做到这一点?有有效的示例代码吗?

    这是一个棘手的问题,因为我认为答案是“取决于”,特别是取决于您希望如何处理优化器

    让我们从您的具体问题开始—嵌入。特别是,您正在询问如何添加嵌入以动态地允许更大的词汇表。我的第一个建议是,如果你对词汇量的上限有很好的认识,那么从一开始就把嵌入的词汇量设置得足够大,这样会更有效,而且最终你还是需要内存的。但这不是你要的。所以-要动态地更改嵌入,您需要用新的覆盖旧的嵌入,并将更改通知优化器。只要您在使用旧嵌入时遇到异常,就可以在
    try。。。除了
    块。这大致应遵循以下理念:

    #从拥有嵌入的模块中
    #记住已经训练过的重量
    旧的嵌入权重=self.embedding.weight.data
    #创建新大小的新嵌入
    self.embedding=nn.embedding(新的声音大小,嵌入尺寸)
    #初始化新嵌入的值。这是随机的,但你可能想用手套之类的东西
    新权重=torch.randn(新的声音大小、嵌入尺寸)
    #由于旧值可能已被更新,您希望检索这些更新值
    新权重[:旧权重]=旧权重
    self.embeding.weights.data.copy_(新的_权重)
    
    但是,您不应该对收到的每个新词都这样做,因为复制需要时间(并且需要大量内存,因为嵌入存在两次,时间很短-如果您几乎没有内存,只需从一开始就让嵌入足够大)。因此,一次动态地增加几百个插槽的大小

    此外,这第一步已经提出了一些问题:

  • 我各自的
    nn.Module
    如何知道新的嵌入参数?
    nn的
    \uuuuu setattr\uuuuuu
    方法。模块
    处理该问题(请参阅)
  • 第二,为什么不简单地更改参数?这已经指向了更改优化器的一些问题:pytorch通过对象ID在内部保留引用。这意味着,如果更改对象,所有这些引用都将指向一个潜在的不兼容对象,因为其属性已更改。因此,我们应该简单地创建一个新参数
  • 其他非嵌入的
    nn.参数
    nn.模块
    呢?这些你都一样对待。您基本上只是实例化它们,并将它们附加到其父模块。
    \uuuu setattr\uuuu
    方法将处理其余部分。所以你可以完全动态地这么做
  • 当然,优化器除外。除了主模型模块之外,优化器是唯一“知道”参数的其他东西。因此,您需要让优化器知道任何更改

    如果你想变得更成熟,这是很棘手的,如果你不在乎保持优化器的状态,这是很容易的。然而,即使你想变得老于世故,也有一个很好的理由说明你无论如何都不应该这样做。更多关于这一点,请参见下文

    不管怎样,如果你不在乎的话,一个简单的

    #只需覆盖旧的优化器
    optimizer=optim.SGD(model.parameters(),lr=0.001)
    
    行。但是,如果您想转移旧状态,您可以使用与存储相同的方法,稍后从磁盘加载参数和优化器状态:使用
    .state\u dict()
    .load\u state\u dict()
    方法。但是,这只适用于扭曲:

    #从旧优化器中提取状态dict
    old\u state\u dict=优化器.state\u dict()
    #创建一个新的优化器
    optimizer=optim.SGD(model.parameters())
    new\u state\u dict=优化器.state\u dict()
    #旧状态dict将在状态_dict['param_groups'][xyz]['params']和状态_dict['state']中引用旧参数
    #现在需要查找新旧statedict之间的参数不匹配
    #如果您的优化器有多个参数组,那么您也需要对它们进行循环(我在这里使用xyz作为占位符。通常情况下,您只有1个参数组,所以只需将xyz替换为0即可
    新参数=[p代表新状态下的p['param_组][xyz]['params']如果旧状态下的p不是[param_组][xyz]['params']
    旧分区=[p代表旧分区['param分区][xyz]['params']中的p,如果不是新分区['param分区][xyz]['params']中的p
    #然后你把所有过时的文件从州目录中删除
    对于旧零件中的pid:
    旧状态命令['state'].pop(pid)
    #并将每个新参数的新状态添加到状态:
    对于新零件中的pid:
    旧状态命令['param\u groups'][xyz]['params']。追加(pid)
    旧状态dict['state'][pid]={…}新状态定义在这里,取决于您的优化
    
    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)