'Computes attention scores and outputs.
Args:
query: The query used to calculate attention scores.
In seq2seq this is typically the current state of the decoder.
A tensor of shape `[B, ...]`
keys: The keys used to calculate attention scores. In seq2seq, these
are typically the outputs of the encoder and equivalent to `values`.
A tensor of shape `[B, T, ...]` where each element in the `T`
dimension corresponds to the key for that value.
values: The elements to compute attention over. In seq2seq, this is
typically the sequence of encoder outputs.
A tensor of shape `[B, T, input_dim]`.
values_length: An int32 tensor of shape `[B]` defining the sequence
length of the attention values.
Returns:
A tuple `(scores, context)`.
`scores` is vector of length `T` where each element is the
normalized "score" of the corresponding `inputs` element.
`context` is the final attention layer output corresponding to
the weighted inputs.
A tensor fo shape `[B, input_dim]`.'

def _build(self, query, keys, values, values_length):

    values_depth = values.get_shape().as_list()[(-1)]
    att_keys = tf.contrib.layers.fully_connected(inputs=keys, num_outputs=self.params[u'num_units'], activation_fn=None, scope=u'att_keys')
    att_query = tf.contrib.layers.fully_connected(inputs=query, num_outputs=self.params[u'num_units'], activation_fn=None, scope=u'att_query')
    scores = self.score_fn(att_keys, att_query)
    num_scores = tf.shape(scores)[1]
    scores_mask = tf.sequence_mask(lengths=tf.to_int32(values_length), maxlen=tf.to_int32(num_scores), dtype=tf.float32)
    scores = ((scores * scores_mask) + ((1.0 - scores_mask) * tf.float32.min))
    scores_normalized = tf.nn.softmax(scores, name=u'scores_normalized')
    context = (tf.expand_dims(scores_normalized, 2) * values)
    context = tf.reduce_sum(context, 1, name=u'context')
    context.set_shape([None, values_depth])
    return (scores_normalized, context)
