Внимание - это метод для агрегирования набора векторов в один вектор, часто через вектор поиска . Обычно - это либо входные данные для модели, либо скрытые состояния предыдущих временных шагов, либо скрытые состояния на один уровень вниз (в случае стековых LSTM).vяUvя
Результат часто называют контекстным вектором , поскольку он содержит контекст, относящийся к текущему временному шагу.с
Этот дополнительный вектор контекста затем также подается в RNN / LSTM (его можно просто объединить с исходным вводом). Следовательно, контекст может быть использован для помощи в прогнозировании.с
Самый простой способ сделать это - вычислить вектор вероятности и где - конкатенация всех предыдущих . Распространенным вектором поиска является текущее скрытое состояние .p = softmax ( VTа )с = ∑япяvяВvяUчасT
Есть много вариантов этого, и вы можете сделать все так сложно, как вы хотите. Например, вместо использования в качестве логитов, можно вместо этого выбрать , где - произвольная нейронная сеть.vTяUе( vя, У )е
Механизм общего внимания для моделей от последовательности к последовательности использует , где - скрытые состояния кодера, а - текущий скрытый состояние декодера. и оба s являются параметрами.p = softmax ( qTTANH( W1vя+ W2часT) )vчасTQW
Некоторые статьи, которые демонстрируют различные варианты идеи внимания:
Сети указателей используют внимание к эталонным данным для решения задач комбинаторной оптимизации.
Рекуррентные сети объектов поддерживают отдельные состояния памяти для разных объектов (людей / объектов) во время чтения текста и обновляют правильное состояние памяти, используя внимание.
Модели трансформаторов также широко используются. Их формулировка внимания немного более общая и также включает ключевые векторы : веса внимания фактически вычисляются между ключами и поиском, и затем контекст строится с помощью .Кяпvя
Вот быстрая реализация одной из форм внимания, хотя я не могу гарантировать правильность, за исключением того факта, что она прошла несколько простых тестов.
Основной РНН:
def rnn(inputs_split):
bias = tf.get_variable('bias', shape = [hidden_dim, 1])
weight_hidden = tf.tile(tf.get_variable('hidden', shape = [1, hidden_dim, hidden_dim]), [batch, 1, 1])
weight_input = tf.tile(tf.get_variable('input', shape = [1, hidden_dim, in_dim]), [batch, 1, 1])
hidden_states = [tf.zeros((batch, hidden_dim, 1), tf.float32)]
for i, input in enumerate(inputs_split):
input = tf.reshape(input, (batch, in_dim, 1))
last_state = hidden_states[-1]
hidden = tf.nn.tanh( tf.matmul(weight_input, input) + tf.matmul(weight_hidden, last_state) + bias )
hidden_states.append(hidden)
return hidden_states[-1]
С вниманием добавляем всего несколько строк, прежде чем вычисляется новое скрытое состояние:
if len(hidden_states) > 1:
logits = tf.transpose(tf.reduce_mean(last_state * hidden_states[:-1], axis = [2, 3]))
probs = tf.nn.softmax(logits)
probs = tf.reshape(probs, (batch, -1, 1, 1))
context = tf.add_n([v * prob for (v, prob) in zip(hidden_states[:-1], tf.unstack(probs, axis = 1))])
else:
context = tf.zeros_like(last_state)
last_state = tf.concat([last_state, context], axis = 1)
hidden = tf.nn.tanh( tf.matmul(weight_input, input) + tf.matmul(weight_hidden, last_state) + bias )
полный код