code: def call(self, v, k, q, mask):
      batch_size = tf.shape(q)[0]
      q = self.wq(q)
      k = self.wk(k)
      v = self.wv(v)
      q = self.split_heads(q, batch_size)
      k = self.split_heads(k, batch_size)
      v = self.split_heads(v, batch_size)
      (
          scaled_attention,
          attention_weights,
      ) = scaled_dot_product_attention(q, k, v, mask)
      scaled_attention = tf.transpose(
          scaled_attention, perm=[0, 2, 1, 3]
      )
      concat_attention = tf.reshape(
          scaled_attention, (batch_size, -1, self.embedding_size)
      )
      output = self.dense(concat_attention)
      return output, attention_weights
  
imports: import numpy
  import tensorflow
label_desc: Use image node_id 3 for calling the MultiHeadAttention obj's underlying member method code with example usage: obj.call(v, k, q, mask) and returns: output,  attention_weights