Read from the value matrix given the attention weights.

        Parameters
        ----------
        F : symbol or ndarray
        att_weights : Symbol or NDArray
            Attention weights.
            For single-head attention,
                Shape (batch_size, query_length, memory_length).
            For multi-head attention,
                Shape (batch_size, num_heads, query_length, memory_length).
        value : Symbol or NDArray
            Value of the memory. Shape (batch_size, memory_length, total_value_dim)

        Returns
        -------
        context_vec: Symbol or NDArray
            Shape (batch_size, query_length, context_vec_dim)

def _read_by_weight(self, F, att_weights, value):
        """Read from the value matrix given the attention weights.

        Parameters
        ----------
        F : symbol or ndarray
        att_weights : Symbol or NDArray
            Attention weights.
            For single-head attention,
                Shape (batch_size, query_length, memory_length).
            For multi-head attention,
                Shape (batch_size, num_heads, query_length, memory_length).
        value : Symbol or NDArray
            Value of the memory. Shape (batch_size, memory_length, total_value_dim)

        Returns
        -------
        context_vec: Symbol or NDArray
            Shape (batch_size, query_length, context_vec_dim)
        """
        output = F.batch_dot(att_weights, value)
        return output