class_docstr: EmbeddingCollection represents a collection of non-pooled embeddings.
  
  It processes sparse data in the form of `KeyedJaggedTensor` of the form [F X B X L]
  where:
  
  * F: features (keys)
  * B: batch size
  * L: length of sparse features (variable)
  
  and outputs `Dict[feature (key), JaggedTensor]`.
  Each `JaggedTensor` contains values of the form (B * L) X D
  where:
  
  * B: batch size
  * L: length of sparse features (jagged)
  * D: each feature's (key's) embedding dimension and lengths are of the form L
  
  Args:
      tables (List[EmbeddingConfig]): list of embedding tables.
      device (Optional[torch.device]): default compute device.
      need_indices (bool): if we need to pass indices to the final lookup dict.
  
  Example::
  
      e1_config = EmbeddingConfig(
          name="t1", embedding_dim=3, num_embeddings=10, feature_names=["f1"]
      )
      e2_config = EmbeddingConfig(
          name="t2", embedding_dim=3, num_embeddings=10, feature_names=["f2"]
      )
  
      ec = EmbeddingCollection(tables=[e1_config, e2_config])
  
      #     0       1        2  <-- batch
      # 0   [0,1] None    [2]
      # 1   [3]    [4]    [5,6,7]
      # ^
      # feature
  
      features = KeyedJaggedTensor.from_offsets_sync(
          keys=["f1", "f2"],
          values=torch.tensor([0, 1, 2, 3, 4, 5, 6, 7]),
          offsets=torch.tensor([0, 2, 2, 3, 4, 5, 8]),
      )
      feature_embeddings = ec(features)
      print(feature_embeddings['f2'].values())
      tensor([[-0.2050,  0.5478,  0.6054],
      [ 0.7352,  0.3210, -3.0399],
      [ 0.1279, -0.1756, -0.4130],
      [ 0.7519, -0.4341, -0.0499],
      [ 0.9329, -1.0697, -0.8095]], grad_fn=<EmbeddingBackward>)
docstr: Args:
      features (KeyedJaggedTensor): KJT of form [F X B X L].
  
  Returns:
      Dict[str, JaggedTensor]
docstr_tok: ["Args",":","features","(","KeyedJaggedTensor",")",":","KJT","of","form","[","F","X","B","X","L","]",".","Returns",":","Dict","[","str",",","JaggedTensor","]"]
code: def forward(
      self,
      features: KeyedJaggedTensor,
  ) -> Dict[str, JaggedTensor]:
      """
      Args:
          features (KeyedJaggedTensor): KJT of form [F X B X L].
  
      Returns:
          Dict[str, JaggedTensor]
      """
  
      feature_embeddings: Dict[str, JaggedTensor] = {}
      jt_dict: Dict[str, JaggedTensor] = features.to_dict()
      for i, emb_module in enumerate(self.embeddings.values()):
          feature_names = self._feature_names[i]
          embedding_names = self._embedding_names_by_table[i]
          for j, embedding_name in enumerate(embedding_names):
              feature_name = feature_names[j]
              f = jt_dict[feature_name]
              lookup = emb_module(
                  input=f.values(),
              ).float()
              feature_embeddings[embedding_name] = JaggedTensor(
                  values=lookup,
                  lengths=f.lengths(),
                  weights=f.values() if self._need_indices else None,
              )
      return feature_embeddings
  
imports: import abc
  from typing import Dict, List, Optional, Tuple, Union
  import torch
  import torch.nn
  from torchrec.modules.embedding_configs import DataType, EmbeddingBagConfig, EmbeddingConfig, pooling_type_to_str
  from torchrec.sparse.jagged_tensor import JaggedTensor, KeyedJaggedTensor, KeyedTensor
label_desc: Use image node_id 2 for calling the EmbeddingCollection obj's underlying member method code with example usage: obj.forward(features) and returns: feature_embeddings