class_docstr: FPN-like fusion module in Shape Robust Text Detection with Progressive
  Scale Expansion Network.
  
  Args:
      in_channels (list[int]): A list of number of input channels.
          Defaults to [256, 512, 1024, 2048].
      out_channels (int): The number of output channels.
          Defaults to 256.
      fusion_type (str): Type of the final feature fusion layer. Available
          options are "concat" and "add". Defaults to "concat".
      init_cfg (dict or list[dict], optional): Initialization configs.
          Defaults to
          dict(type='Xavier', layer='Conv2d', distribution='uniform')
docstr: Args:
      inputs (list[Tensor]): Each tensor has the shape of
          :math:`(N, C_i, H_i, W_i)`. It usually expects 4 tensors
          (C2-C5 features) from ResNet.
  
  Returns:
      Tensor: A tensor of shape :math:`(N, C_{out}, H_0, W_0)` where
      :math:`C_{out}` is ``out_channels``.
docstr_tok: ["Args",":","inputs","(","list","[","Tensor","]",")",":","Each","tensor","has","the","shape","of",":","math",":","`","(","N",",","C_i",",","H_i",",","W_i",")","`",".","It","usually","expects","4","tensors","(","C2-C5","features",")","from","ResNet",".","Returns",":","Tensor",":","A","tensor","of","shape",":","math",":","`","(","N",",","C_","{","out","}",",","H_0",",","W_0",")","`","where",":","math",":","`","C_","{","out","}","`","is","``","out_channels","``","."]
code: def forward(self, inputs: List[Tensor]) -> Tensor:
      """
      Args:
          inputs (list[Tensor]): Each tensor has the shape of
              :math:`(N, C_i, H_i, W_i)`. It usually expects 4 tensors
              (C2-C5 features) from ResNet.
  
      Returns:
          Tensor: A tensor of shape :math:`(N, C_{out}, H_0, W_0)` where
          :math:`C_{out}` is ``out_channels``.
      """
      assert len(inputs) == len(self.in_channels)
  
      # build laterals
      laterals = [
          lateral_conv(inputs[i])
          for i, lateral_conv in enumerate(self.lateral_convs)
      ]
  
      # build top-down path
      used_backbone_levels = len(laterals)
      for i in range(used_backbone_levels - 1, 0, -1):
          # step 1: upsample to level i-1 size and add level i-1
          prev_shape = laterals[i - 1].shape[2:]
          laterals[i - 1] = laterals[i - 1] + F.interpolate(
              laterals[i], size=prev_shape, mode="nearest"
          )
          # step 2: smooth level i-1
          laterals[i - 1] = self.fpn_convs[i - 1](laterals[i - 1])
  
      # upsample and cat
      bottom_shape = laterals[0].shape[2:]
      for i in range(1, used_backbone_levels):
          laterals[i] = F.interpolate(
              laterals[i], size=bottom_shape, mode="nearest"
          )
  
      if self.fusion_type == "concat":
          out = torch.cat(laterals, 1)
      elif self.fusion_type == "add":
          out = laterals[0]
          for i in range(1, used_backbone_levels):
              out += laterals[i]
      else:
          raise NotImplementedError
      out = self.output_convs(out)
  
      return out
  
file_path: mmocr/mmocr/models/textdet/necks/fpnf.py
imports: from typing import Dict, List, Optional, Union
  import torch
  import torch.nn.functional
  from mmcv.cnn import ConvModule
  from mmengine.model import BaseModule, ModuleList
  from torch import Tensor
  from mmocr.registry import MODELS
label_desc: Use image node_id 2 for calling the FPNF obj's underlying member method code with example usage: obj.forward(inputs) and returns: out