【问题标题】:Is it possible to freeze only certain embedding weights in the embedding layer in pytorch?是否可以在 pytorch 的嵌入层中仅冻结某些嵌入权重?
【发布时间】:2019-07-22 07:16:16
【问题描述】:

在 NLP 任务中使用 GloVe 嵌入时,数据集中的某些单词可能不存在于 GloVe 中。因此,我们为这些未知词实例化随机权重。

是否可以冻结从 GloVe 获得的权重,只训练新实例化的权重?

我只知道我们可以设置: model.embedding.weight.requires_grad = False

但这使得新词无法训练..

或者有没有更好的方法来提取单词的语义..

【问题讨论】:

    标签: python nlp pytorch word-embedding glove


    【解决方案1】:

    1。将嵌入划分为两个单独的对象

    一种方法是使用两个单独的嵌入一个用于预训练,另一个用于待训练

    手套应该被冻结,而没有预训练表示的手套将从可训练层中取出。

    如果您将数据格式化为预训练令牌表示的范围比没有 GloVe 表示的令牌范围更小,则可以这样做。假设您的预训练索引在 [0, 300] 范围内,而没有表示的索引在 [301, 500] 范围内。我会按照这些思路去做:

    import numpy as np
    import torch
    
    
    class YourNetwork(torch.nn.Module):
        def __init__(self, glove_embeddings: np.array, how_many_tokens_not_present: int):
            self.pretrained_embedding = torch.nn.Embedding.from_pretrained(glove_embeddings)
            self.trainable_embedding = torch.nn.Embedding(
                how_many_tokens_not_present, glove_embeddings.shape[1]
            )
            # Rest of your network setup
    
        def forward(self, batch):
            # Which tokens in batch do not have representation, should have indices BIGGER
            # than the pretrained ones, adjust your data creating function accordingly
            mask = batch > self.pretrained_embedding.num_embeddings
    
            # You may want to optimize it, you could probably get away without copy, though
            # I'm not currently sure how
            pretrained_batch = batch.copy()
            pretrained_batch[mask] = 0
    
            embedded_batch = self.pretrained_embedding(pretrained_batch)
    
            # Every token without representation has to be brought into appropriate range
            batch -= self.pretrained_embedding.num_embeddings
            # Zero out the ones which already have pretrained embedding
            batch[~mask] = 0
            non_pretrained_embedded_batch = self.trainable_embedding(batch)
    
            # And finally change appropriate tokens from placeholder embedding created by
            # pretrained into trainable embeddings.
            embedded_batch[mask] = non_pretrained_embedded_batch[mask]
    
            # Rest of your code
            ...
    

    假设您的预训练索引在 [0, 300] 范围内,而没有表示的索引在 [301, 500] 范围内。

    2。指定标记的零梯度。

    这个有点棘手,但我认为它非常简洁且易于实现。因此,如果您获得了没有 GloVe 表示的标记的索引,您可以在反向传播之后显式地将它们的梯度归零,这样这些行就不会被更新。

    import torch
    
    embedding = torch.nn.Embedding(10, 3)
    X = torch.LongTensor([[1, 2, 4, 5], [4, 3, 2, 9]])
    
    values = embedding(X)
    loss = values.mean()
    
    # Use whatever loss you want
    loss.backward()
    
    # Let's say those indices in your embedding are pretrained (have GloVe representation)
    indices = torch.LongTensor([2, 4, 5])
    
    print("Before zeroing out gradient")
    print(embedding.weight.grad)
    
    print("After zeroing out gradient")
    embedding.weight.grad[indices] = 0
    print(embedding.weight.grad)
    

    以及第二种方法的输出:

    Before zeroing out gradient
    tensor([[0.0000, 0.0000, 0.0000],
            [0.0417, 0.0417, 0.0417],
            [0.0833, 0.0833, 0.0833],
            [0.0417, 0.0417, 0.0417],
            [0.0833, 0.0833, 0.0833],
            [0.0417, 0.0417, 0.0417],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0417, 0.0417, 0.0417]])
    After zeroing out gradient
    tensor([[0.0000, 0.0000, 0.0000],
            [0.0417, 0.0417, 0.0417],
            [0.0000, 0.0000, 0.0000],
            [0.0417, 0.0417, 0.0417],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0000, 0.0000, 0.0000],
            [0.0417, 0.0417, 0.0417]])
    

    【讨论】:

    • 在第二种方法中,由于您只是将一些获得的梯度设置为零,所以计算量不会像从头开始训练所有嵌入一样繁重吗?
    • @AndreaRossi 是的,因此第一种方法要好得多。刚刚概述了另一种可能性
    • 几个小调整让这对我有用。我不得不使用self.pretrained_embedding.num_embeddings 而不是self.pretrained_embedding.shape[0]。我还认为embedded_batch = self.pretrained_embedding[pretrained_batch] 行需要稍微更新以使用圆括号-embedded_batch = self.pretrained_embedding(pretrained_batch)。谢谢你的详细回答顺便说一句,这是我发现的唯一真正解释如何实现多重嵌入方法的解决方案。
    • @piedpiper 谢谢,更新了答案。如果您下次发现有问题,请随时编辑我的答案。 :)
    • 没想到真的可以,但是下次一定会的!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 2020-04-19
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多