Я понял ваш вопрос, но немного сложно следовать вашему коду и найти причину, по которой потери не уменьшаются. Кроме того, неясно, почему вы хотите сравнивать последнее скрытое состояние RNN со всеми скрытыми состояниями на каждом временном шаге.
Обратите внимание, что определенный трюк / механизм полезен, если вы используете его правильно. То, как вы пытаетесь использовать механизм внимания, я не уверен, правильно ли это. Так что не ожидайте, что если вы используете уловку внимания в своей модели, вы получите хорошие результаты !! Вы должны подумать, почему механизм внимания принесет пользу вашей желаемой задаче?
Вы четко не упомянули, на какую задачу вы нацелены? Поскольку вы указали репо, которое содержит код для моделирования языка, я предполагаю, что задача заключается в следующем: учитывая последовательность токенов, предсказать следующий токен.
Одна возможная проблема, которую я вижу в вашем коде, заключается в следующем: в цикле for item in emb:
вы всегда будете использовать вложения в качестве входных данных для каждого уровня LSTM, поэтому для меня не имеет смысла иметь составной LSTM.
Теперь позвольте мне сначала ответить на ваш вопрос, а затем шаг за шагом показать, как вы можете построить желаемую архитектуру NN.
Нужно ли мне использовать архитектуру кодировщика-декодера для использования механизма внимания?
Архитектура кодер-декодер более известна как от последовательности к последовательности для обучения и широко используется во многих задачах генерации, например, при машинном переводе. Ответ на ваш вопрос: нет, для использования механизма внимания не требуется использовать какую-либо конкретную архитектуру нейронной сети.
Структура, представленная на рисунке, немного двусмысленна, но ее легко реализовать. Поскольку ваша реализация мне непонятна, я пытаюсь помочь вам найти лучший способ ее реализации. В следующем обсуждении я предполагаю, что мы имеем дело с вводом текста.
Скажем, у нас есть ввод формы 16 x 10
, где 16
- это batch_size
, а 10
- это seq_len
. Мы можем предположить, что у нас есть 16 предложений в мини-пакете, а длина каждого предложения равна 10.
batch_size, vocab_size = 16, 100
mat = np.random.randint(vocab_size, size=(batch_size, 10))
input_var = Variable(torch.from_numpy(mat))
Здесь 100
можно рассматривать как размер словарного запаса. Важно отметить, что на протяжении всего приведенного мной примера я принимаю batch_size
как первое измерение во всех соответствующих тензорах / переменных.
Теперь давайте вставим входную переменную.
embedding = nn.Embedding(100, 50)
embed = embedding(input_var)
После встраивания мы получили переменную формы 16 x 10 x 50
, где 50
- размер встраивания.
Теперь давайте определим двухуровневый однонаправленный LSTM со 100 скрытыми модулями на каждом уровне.
rnns = nn.ModuleList()
nlayers, input_size, hidden_size = 2, 50, 100
for i in range(nlayers):
input_size = input_size if i == 0 else hidden_size
rnns.append(nn.LSTM(input_size, hidden_size, 1, batch_first=True))
Затем мы можем передать наш вход в этот двухуровневый LSTM, чтобы получить результат.
sent_variable = embed
outputs, hid = [], []
for i in range(nlayers):
if i != 0:
sent_variable = F.dropout(sent_variable, p=0.3, training=True)
output, hidden = rnns[i](sent_variable)
outputs.append(output)
hid.append(hidden[0].squeeze(0))
sent_variable = output
rnn_out = torch.cat(outputs, 2)
hid = torch.cat(hid, 1)
Теперь вы можете просто использовать hid
, чтобы предсказать следующее слово. Я бы посоветовал вам это сделать. Здесь форма hid
равна batch_size x (num_layers*hidden_size)
.
Но поскольку вы хотите использовать внимание для вычисления показателя мягкого выравнивания между последними скрытыми состояниями с каждым скрытым состоянием, созданным слоями LSTM, давайте сделаем это.
sent_variable = embed
hid, con = [], []
for i in range(nlayers):
if i != 0:
sent_variable = F.dropout(sent_variable, p=0.3, training=True)
output, hidden = rnns[i](sent_variable)
sent_variable = output
hidden = hidden[0].squeeze(0) # batch_size x hidden_size
hid.append(hidden)
weights = torch.bmm(output[:, 0:-1, :], hidden.unsqueeze(2)).squeeze(2)
soft_weights = F.softmax(weights, 1) # batch_size x seq_len
context = torch.bmm(output[:, 0:-1, :].transpose(1, 2), soft_weights.unsqueeze(2)).squeeze(2)
con.append(context)
hid, con = torch.cat(hid, 1), torch.cat(con, 1)
combined = torch.cat((hid, con), 1)
Здесь мы вычисляем оценку мягкого выравнивания между последним состоянием со всеми состояниями каждого временного шага. Затем мы вычисляем вектор контекста, который представляет собой просто линейную комбинацию всех скрытых состояний. Мы объединяем их в единое представление.
Обратите внимание, я удалил последние скрытые состояния из output
: output[:, 0:-1, :]
, так как вы сравниваете с самим последним скрытым состоянием.
Окончательное combined
представление хранит последние скрытые состояния и векторы контекста, созданные на каждом уровне. Вы можете напрямую использовать это представление для предсказания следующего слова.
Предсказать следующее слово очень просто, и, поскольку вы используете простой линейный слой, это нормально.
Изменить: мы можем сделать следующее, чтобы предсказать следующее слово.
decoder = nn.Linear(nlayers * hidden_size * 2, vocab_size)
dec_out = decoder(combined)
Здесь форма dec_out
равна batch_size x vocab_size
. Теперь мы можем вычислить отрицательную потерю логарифмической вероятности, которая будет использована для обратного распространения позже.
Перед вычислением отрицательной потери логарифма правдоподобия нам нужно применить log_softmax
к выходному сигналу декодера.
dec_out = F.log_softmax(dec_out, 1)
target = np.random.randint(vocab_size, size=(batch_size))
target = Variable(torch.from_numpy(target))
И мы также определили цель, которая требуется для расчета потерь. Подробнее см. NLLLoss. Итак, теперь мы можем вычислить потери следующим образом.
criterion = nn.NLLLoss()
loss = criterion(dec_out, target)
print(loss)
Напечатанная величина потерь:
Variable containing:
4.6278
[torch.FloatTensor of size 1]
Надеюсь, все объяснение вам поможет !!
person
Wasi Ahmad
schedule
12.03.2018