class_docstr: Base class for multi-modal image-text data
code: def forward(self, input: Dict, *args, **kwargs) -> Dict:
      images = input.get("image", None)
      text_tokens = input.get("text", None)
      padding_mask = input.get("padding_mask", None)
  
      # [B, C, H, W] --> [B, d]
      # where B is the batch size, C is number of image channels, H is height and W is Width
      image_encoder_out = self.image_encoder(images)
      augmented_tensor = None
      if isinstance(image_encoder_out, Dict):
          if not {"augmented_tensor", "logits"}.issubset(
              image_encoder_out.keys()
          ):
              logger.error(
                  "Output of image classifier must contain logits and augmented_tensor"
                  " as keys. Got keys: {}".format(
                      image_encoder_out.keys()
                  )
              )
          image_embeddings = image_encoder_out["logits"]
          augmented_tensor = image_encoder_out["augmented_tensor"]
      elif isinstance(image_encoder_out, Tensor):
          image_embeddings = image_encoder_out
      else:
          logger.error(
              "The output of image encoder should be either Dict or Tensor"
          )
  
      # [B, N] --> [B, d] (for single-caption per image)
      # or [B, Cl, M, N] --> [d, Cl] (for zero-shot)
      # where N in caption len, M is number of captions per image, and Cl is number of classes per image
  
      if self.cache_text_features_zero_shot and not self.training:
          # For zero-shot image classification, we can cache text features as they are the same for all images
          if self.cached_text_features is None:
              text_embeddings = self.text_encoder(
                  text_tokens=text_tokens, key_padding_mask=padding_mask
              )
              self.cached_text_features = text_embeddings
          text_embeddings = self.cached_text_features
      else:
          text_embeddings = self.text_encoder(
              text_tokens=text_tokens, key_padding_mask=padding_mask
          )
  
      if (
          not self.training
          and (
              text_embeddings.shape[0] == image_embeddings.shape[1]
          )  # d_text == d_image
          and (
              text_embeddings.shape[1] != image_embeddings.shape[0]
          )  # N_classes != Batch_image
      ):
          # This means that we are running a zero-shot set-up.
          # [B x d] x [d x N] --> [B, N]
          zero_shot_image_logits = (
              100.0 * image_embeddings @ text_embeddings
          )
          return {
              "image": None,
              "text": None,
              "logit_scale": self._exponentiate_and_clip_logits(),
              "zero_shot_image_logits": zero_shot_image_logits,
              "augmented_tensor": None,
          }
      else:
          return {
              "image": image_embeddings,
              "text": text_embeddings,
              "logit_scale": self._exponentiate_and_clip_logits(),
              "zero_shot_image_logits": None,
              "augmented_tensor": augmented_tensor,
          }
  
imports: import argparse
  import math
  from typing import Dict, Optional
  import torch
  from torch import Tensor, nn
  from cvnets.image_projection_layers import build_image_projection_head
  from cvnets.image_projection_layers.base_image_projection import get_in_feature_dimension
  from cvnets.models import MODEL_REGISTRY, BaseAnyNNModel, get_model
  from cvnets.models.classification.base_image_encoder import BaseImageEncoder
  from cvnets.models.multi_modal_img_text.base_multi_modal_img_text import BaseMultiModalImageText
  from cvnets.text_encoders import BaseTextEncoder, build_text_encoder
  from utils import logger
label_desc: Use image node_id 7 for calling the CLIP obj's underlying member method code with example usage: obj.forward(input) and returns: dict, dict