Можно ли заморозить только определенные веса вложения в слое вложения в pytorch?

При использовании встраивания GloVe в задачи НЛП некоторые слова из набора данных могут не существовать в GloVe. Поэтому мы создаем случайные веса для этих неизвестных слов.

Можно ли заморозить веса, полученные от GloVe, и тренировать только новые экземпляры весов?

Я знаю только, что мы можем установить: model.embedding.weight.requires_grad = False

Но это делает новые слова необучаемыми ..

Или есть более эффективные способы извлечения семантики слов ..


person rcshon    schedule 28.02.2019    source источник


Ответы (1)


1. Разделите вложения на два отдельных объекта.

Один из подходов заключается в использовании двух отдельных встраиваний, одно для предварительного обучения, другое для одного для обучения.

Один GloVe должен быть заморожен, а тот, для которого нет предварительно обученного представления, будет взят из обучаемого слоя.

Если вы отформатируете свои данные так, что для предварительно обученных представлений токенов они находятся в меньшем диапазоне, чем токены без представления 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.shape[0]

        # 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.shape[0]
        # 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]])
person Szymon Maszke    schedule 01.03.2019
comment
Во втором подходе, поскольку вы просто обнуляете некоторые из полученных градиентов, разве вычисления не будут такими же тяжелыми, как обучение всех встраиваний с самого начала? - person Andrea Rossi; 06.03.2020
comment
@AndreaRossi Да, поэтому первый подход намного лучше. Только что обозначил еще одну возможность - person Szymon Maszke; 06.03.2020