class_docstr: Applies NetVLAD to the input.
  
  This is a fully-differentiable version of "Vector of Locally Aggregated Descriptors" commonly used in image
  retrieval.
  
  See [NetVLAD: CNN architecture for weakly supervised place recognition](https://arxiv.org/abs/1511.07247), and.
  [Towards Learning a Universal Non-Semantic Representation of Speech](https://arxiv.org/abs/2002.12764)
  
  Args:
      num_clusters: The number of clusters to use.
  Input shape:
      3D tensor with shape: `(batch_size, time, feature_dim)`.
  Output shape:
      2D tensor with shape: `(batch_size, feature_dim * num_clusters)`.
docstr: Apply the NetVLAD module to the given frames.
  
  Args:
      frames: A tensor with shape [batch_size, max_frames, feature_dim].
  
  Returns:
      A tensor with shape [batch_size, feature_dim * num_clusters].
  
  Raises:
      ValueError: If the `feature_dim` of input is not defined.
code: def call(self, frames):
      """Apply the NetVLAD module to the given frames.
  
      Args:
          frames: A tensor with shape [batch_size, max_frames, feature_dim].
  
      Returns:
          A tensor with shape [batch_size, feature_dim * num_clusters].
  
      Raises:
          ValueError: If the `feature_dim` of input is not defined.
      """
      frames.shape.assert_has_rank(3)
      feature_dim = frames.shape.as_list()[-1]
      if feature_dim is None:
          raise ValueError("Last dimension must be defined.")
      max_frames = tf.shape(frames)[-2]
  
      # Compute soft-assignment from frames to clusters.
      # Essentially: softmax(w*x + b), although BN can be used instead of bias.
      frames = tf.reshape(frames, (-1, feature_dim))
      activation = self.fc(frames)
      activation = tf.reshape(
          activation, (-1, max_frames, self.num_clusters)
      )
  
      # Soft-count of number of frames assigned to each cluster.
      # Output shape: [batch_size, 1, num_clusters]
      a_sum = tf.math.reduce_sum(activation, axis=-2, keepdims=True)
  
      # Compute sum_{i=1}^N softmax(w_k * x_i + b_k) * c_k(j),
      # for all clusters and dimensions.
      # Output shape: [batch_size, feature_dim, num_clusters]
      a = a_sum * self.cluster_centers
  
      # Compute sum_{i=1}^N softmax(w_k * x_i + b_k) * x_i(j),
      # for all clusters and dimensions.
      # Output shape: (batch_size, feature_dim, num_clusters)
      frames = tf.reshape(frames, (-1, max_frames, feature_dim))
      b = tf.transpose(
          tf.matmul(tf.transpose(activation, perm=(0, 2, 1)), frames),
          perm=(0, 2, 1),
      )
  
      # Output shape: (batch_size, feature_dim, num_clusters)
      vlad = b - a
  
      # Normalize first across the feature dimensions.
      vlad = tf.nn.l2_normalize(vlad, 1)
  
      # Output shape: [batch_size, feature_dim * num_clusters]
      vlad = tf.reshape(vlad, (-1, feature_dim * self.num_clusters))
  
      # Renormalize across both the feature dimensions (already normalized) and
      # the cluster centers.
      vlad = tf.nn.l2_normalize(vlad, 1)
  
      return vlad
  
file_path: addons/tensorflow_addons/layers/netvlad.py
imports: import math
  import tensorflow
  from typeguard import typechecked
label_desc: Use image node_id 3 for calling the NetVLAD obj's underlying member method code with example usage: obj.call(frames) and returns: vlad