code: def forward(
      self,
      features: torch.Tensor,  # (N, C, H, W)
      hidden_state: torch.Tensor,  # (N, C)
  ) -> torch.Tensor:
      H_f, W_f = features.shape[2:]
  
      # (N, feat_chans, H, W) --> (N, attention_units, H, W)
      feat_projection = self.feat_conv(features)
      # (N, state_chans, 1, 1) --> (N, attention_units, 1, 1)
      hidden_state = hidden_state.view(
          hidden_state.size(0), hidden_state.size(1), 1, 1
      )
      state_projection = self.state_conv(hidden_state)
      state_projection = state_projection.expand(-1, -1, H_f, W_f)
      # (N, attention_units, 1, 1) --> (N, attention_units, H_f, W_f)
      attention_weights = torch.tanh(feat_projection + state_projection)
      # (N, attention_units, H_f, W_f) --> (N, 1, H_f, W_f)
      attention_weights = self.attention_projector(attention_weights)
      B, C, H, W = attention_weights.size()
  
      # (N, H, W) --> (N, 1, H, W)
      attention_weights = torch.softmax(
          attention_weights.view(B, -1), dim=-1
      ).view(B, C, H, W)
      # fuse features and attention weights (N, C)
      return (features * attention_weights).sum(dim=(2, 3))
  
imports: from copy import deepcopy
  from typing import Any, Callable, Dict, List, Optional, Tuple
  import torch
  from torch import nn
  from torch.nn import functional
  from torchvision.models._utils import IntermediateLayerGetter
  from doctr.datasets import VOCABS
  from ...classification import resnet31
  from ...utils.pytorch import _bf16_to_float32, load_pretrained_params
  from ..core import RecognitionModel, RecognitionPostProcessor
label_desc: Use image node_id 2 for calling the AttentionModule obj's underlying member method code with example usage: obj.forward(features, hidden_state) and returns: unknown