注意是一种通常通过查找向量u将一组向量vi聚合为一个向量的方法。通常情况下,v 我uvi要么是输入到模型或先前时间步长的隐藏状态,或者隐藏的状态中的一种电平下降(在层叠LSTMs的情况下)。
结果通常称为上下文向量c,因为它包含与当前时间步长相关的上下文。
此附加上下文向量c然后,也被馈送到RNN / LSTM中(可以简单地与原始输入连接在一起)。因此,上下文可以用于帮助预测。
最简单的方法是计算概率向量p=softmax(VTu)和c=∑ipivi,其中V是所有先前vi的串联。通用查找向量u是当前隐藏状态ht。
对此有很多变体,您可以根据需要使事情变得复杂。例如,代替使用vTiu作为对数,可以选择f(vi,u),其中f是任意神经网络。
序列到序列模型的常见关注机制使用p = softmax (qŤ谭(W1个v一世+ W2HŤ)),其中v是编码器的隐藏状态,ht是当前隐藏状态解码器。q和W均为参数。
一些论文展示了注意力观念的不同变化:
指针网络将注意力集中在参考输入上,以解决组合优化问题。
循环实体网络在读取文本时会为不同的实体(人员/对象)维护单独的内存状态,并通过注意更新正确的内存状态。
变压器模型也充分利用了注意力。他们的注意力制剂是稍微更通用的,并且还涉及密钥矢量ki:注意权重p实际计算出的密钥和查找之间,并在上下文然后与构造vi。
这是一种注意力集中形式的快速实现方法,尽管我不能保证它通过了一些简单的测试后仍然是正确的。
基本RNN:
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 )
完整的代码