class_docstr: ## U-Net
class_docstr_tok: ["#","#","U-Net"]
docstr: :param in_channels: number of channels in the input image
  :param out_channels: number of channels in the result feature map
docstr_tok: [":","param","in_channels",":","number","of","channels","in","the","input","image",":","param","out_channels",":","number","of","channels","in","the","result","feature","map"]
code: def __init__(self, in_channels: int, out_channels: int):
      """
      :param in_channels: number of channels in the input image
      :param out_channels: number of channels in the result feature map
      """
      super().__init__()
  
      # Double convolution layers for the contracting path.
      # The number of features gets doubled at each step starting from $64$.
      self.down_conv = nn.ModuleList(
          [
              DoubleConvolution(i, o)
              for i, o in [
                  (in_channels, 64),
                  (64, 128),
                  (128, 256),
                  (256, 512),
              ]
          ]
      )
      # Down sampling layers for the contracting path
      self.down_sample = nn.ModuleList([DownSample() for _ in range(4)])
  
      # The two convolution layers at the lowest resolution (the bottom of the U).
      self.middle_conv = DoubleConvolution(512, 1024)
  
      # Up sampling layers for the expansive path.
      # The number of features is halved with up-sampling.
      self.up_sample = nn.ModuleList(
          [
              UpSample(i, o)
              for i, o in [
                  (1024, 512),
                  (512, 256),
                  (256, 128),
                  (128, 64),
              ]
          ]
      )
      # Double convolution layers for the expansive path.
      # Their input is the concatenation of the current feature map and the feature map from the
      # contracting path. Therefore, the number of input features is double the number of features
      # from up-sampling.
      self.up_conv = nn.ModuleList(
          [
              DoubleConvolution(i, o)
              for i, o in [
                  (1024, 512),
                  (512, 256),
                  (256, 128),
                  (128, 64),
              ]
          ]
      )
      # Crop and concatenate layers for the expansive path.
      self.concat = nn.ModuleList([CropAndConcat() for _ in range(4)])
      # Final $1 \times 1$ convolution layer to produce the output
      self.final_conv = nn.Conv2d(64, out_channels, kernel_size=1)
  
imports: import torch
  import torchvision.transforms.functional
  from torch import nn
label_desc: Use image node_id 1 to create a new UNet object from inherited base classes: nn with example: obj = UNet(in_channels, out_channels)