code: def create_modules(modules_defs: list, img_size):
      """
      Constructs module list of layer blocks from module configuration in module_defs
      :param modules_defs: 通过.cfg文件解析得到的每个层结构的列表
      :param img_size:
      :return:
      """
  
      img_size = (
          [img_size] * 2 if isinstance(img_size, int) else img_size
      )
      # 删除解析cfg列表中的第一个配置(对应[net]的配置)
      modules_defs.pop(0)  # cfg training hyperparams (unused)
      output_filters = [3]  # input channels
      module_list = nn.ModuleList()
      # 统计哪些特征层的输出会被后续的层使用到(可能是特征融合，也可能是拼接)
      routs = []  # list of layers which rout to deeper layers
      yolo_index = -1
  
      # 遍历搭建每个层结构
      for i, mdef in enumerate(modules_defs):
          modules = nn.Sequential()
  
          if mdef["type"] == "convolutional":
              bn = mdef["batch_normalize"]  # 1 or 0 / use or not
              filters = mdef["filters"]
              k = mdef["size"]  # kernel size
              stride = (
                  mdef["stride"]
                  if "stride" in mdef
                  else (mdef["stride_y"], mdef["stride_x"])
              )
              if isinstance(k, int):
                  modules.add_module(
                      "Conv2d",
                      nn.Conv2d(
                          in_channels=output_filters[-1],
                          out_channels=filters,
                          kernel_size=k,
                          stride=stride,
                          padding=k // 2 if mdef["pad"] else 0,
                          bias=not bn,
                      ),
                  )
              else:
                  raise TypeError(
                      "conv2d filter size must be int type."
                  )
  
              if bn:
                  modules.add_module(
                      "BatchNorm2d", nn.BatchNorm2d(filters)
                  )
              else:
                  # 如果该卷积操作没有bn层，意味着该层为yolo的predictor
                  routs.append(
                      i
                  )  # detection output (goes into yolo layer)
  
              if mdef["activation"] == "leaky":
                  modules.add_module(
                      "activation", nn.LeakyReLU(0.1, inplace=True)
                  )
              else:
                  pass
  
          elif mdef["type"] == "BatchNorm2d":
              pass
  
          elif mdef["type"] == "maxpool":
              k = mdef["size"]  # kernel size
              stride = mdef["stride"]
              modules = nn.MaxPool2d(
                  kernel_size=k, stride=stride, padding=(k - 1) // 2
              )
  
          elif mdef["type"] == "upsample":
              if (
                  ONNX_EXPORT
              ):  # explicitly state size, avoid scale_factor
                  g = (yolo_index + 1) * 2 / 32  # gain
                  modules = nn.Upsample(
                      size=tuple(int(x * g) for x in img_size)
                  )
              else:
                  modules = nn.Upsample(scale_factor=mdef["stride"])
  
          elif (
              mdef["type"] == "route"
          ):  # [-2],  [-1,-3,-5,-6], [-1, 61]
              layers = mdef["layers"]
              filters = sum(
                  [
                      output_filters[l + 1 if l > 0 else l]
                      for l in layers
                  ]
              )
              routs.extend([i + l if l < 0 else l for l in layers])
              modules = FeatureConcat(layers=layers)
  
          elif mdef["type"] == "shortcut":
              layers = mdef["from"]
              filters = output_filters[-1]
              # routs.extend([i + l if l < 0 else l for l in layers])
              routs.append(i + layers[0])
              modules = WeightedFeatureFusion(
                  layers=layers, weight="weights_type" in mdef
              )
  
          elif mdef["type"] == "yolo":
              yolo_index += 1  # 记录是第几个yolo_layer [0, 1, 2]
              stride = [32, 16, 8]  # 预测特征层对应原图的缩放比例
  
              modules = YOLOLayer(
                  anchors=mdef["anchors"][mdef["mask"]],  # anchor list
                  nc=mdef["classes"],  # number of classes
                  img_size=img_size,
                  stride=stride[yolo_index],
              )
  
              # Initialize preceding Conv2d() bias (https://arxiv.org/pdf/1708.02002.pdf section 3.3)
              try:
                  j = -1
                  # bias: shape(255,) 索引0对应Sequential中的Conv2d
                  # view: shape(3, 85)
                  b = module_list[j][0].bias.view(modules.na, -1)
                  b.data[:, 4] += -4.5  # obj
                  b.data[:, 5:] += math.log(
                      0.6 / (modules.nc - 0.99)
                  )  # cls (sigmoid(p) = 1/nc)
                  module_list[j][0].bias = torch.nn.Parameter(
                      b.view(-1), requires_grad=True
                  )
              except Exception as e:
                  print(
                      "WARNING: smart bias initialization failure.", e
                  )
          else:
              print("Warning: Unrecognized Layer Type: " + mdef["type"])
  
          # Register module list and number of output filters
          module_list.append(modules)
          output_filters.append(filters)
  
      routs_binary = [False] * len(modules_defs)
      for i in routs:
          routs_binary[i] = True
      return module_list, routs_binary
  
file_path: deep-learning-for-image-processing/pytorch_object_detection/yolov3_spp/models.py
imports: from build_utils.layers import *
  from build_utils.parse_config import *
label_desc: Use image node_id 1 for calling a global function with example usage: create_modules(modules_defs, img_size) and returns: module_list,  routs_binary