Как определить морфологию слова исключительно по контексту?

Рейтинг: 1Ответов: 1Опубликовано: 09.06.2023

Существует стандартный метод определения морфологии (pos-tagging), на основе lstm, которому на вход передается:

seq: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # vocab idx
out: [0, 0, 1, 2, 0, 1, 3, 2, 0, 1]   # target idx

где 0 — неизвестно и передано параметром в функцию потерь. Точность высокая. Но алгоритм существенно опирается на вектор тестируемого слова. Нужно же определять исключительно по контексту.

Переработал, чтобы исходные данные передавались как срез контекста, с заменой тестируемого слова на <pad> заглушку в seq и обнулением всех слов кроме целевого в out:

seq: [1, 2, 0, 4, 5]
out: [0, 0, 1, 0, 0]

seq: [1, 2, 3, 0, 5, 6]
out: [0, 0, 0, 2, 0, 0]

seq: [1, 2, 3, 4, 5, 0, 7, 8]
out: [0, 0, 0, 0, 0, 1, 0, 0]

...

seq: [5, 6, 7, 8, 9, 0]
out: [0, 0, 0, 0, 0, 1]

Но скорость обучения, соответственно существенно упала. Есть ли способ настроить обучение, чтобы модель обрабатывала предложение целиком, но игнорировала вектора собственно тестируемых слов?

class LSTMTagger(nn.Module):
    def __init__(self, embedding_dim, hidden_dim, tagset_size):
        super(LSTMTagger, self).__init__()
        self.lstm = nn.LSTM(embedding_dim, hidden_dim, bidirectional=True)
        self.hidden2tag = nn.Linear(hidden_dim*2, tagset_size) # *2 — bidirect

    def forward(self, embeds):
        length = len(embeds)
        lstm_out, _ = self.lstm(embeds.view(length, 1, -1))
        tag_space = self.hidden2tag(lstm_out.view(length, -1))
        tag_scores = F.log_softmax(tag_space, dim=1)
        return tag_scores

embedding = nn.Embedding.from_pretrained(weights, padding_idx=0)
model = LSTMTagger(embedding.embedding_dim, HIDDEN_DIM, TAGSET_SIZE)
loss_function = nn.NLLLoss(ignore_index=0)
optimizer = optim.SGD(model.parameters(), lr=0.1)

for source, target in corpus:
    embeds = embedding(source)
    model.zero_grad()
    tag_scores = model(embeds)
    loss = loss_function(tag_scores, target)
    loss.backward()
    optimizer.step()

Ответы

▲ 0

Пришел к такому решению. Пусть имеем данные:

seq: [1, 2, 3, 4, 5]  # vocab idx
out: [3, 2, 1, 3, 2]  # target idx

Для обработки контекста нужно сместить seq относительно out. Расширяем seq до [0, 1, 2, 3, 4, 5, 0] и получаем две последовательности

fwd: [0, 1, 2, 3, 4]  # seq[:-2]
rev: [0, 5, 4, 3, 2]  # reversed(seq[2:])

обрабатываемые двумя однонаправленными сетями с последующим объединением результата. Тогда, например, на out[2]=1 будут влиять именно контексты: [1, 2] прямой и [5, 4] обратный, но не будет влиять собственное значение 3.

Также есть нюанс. Первая позиция результата каждой из сетей не несет смысла, контекста еще нет. Осмысленное значение дает только другая сеть.