text: import torch
  import torch.nn as nn
  import torch.nn.functional as F
  
  
  def get_upsampling_weight(in_channels, out_channels, kernel_size):
      """Make a 2D bilinear kernel suitable for upsampling.
  
      https://github.com/shelhamer/fcn.berkeleyvision.org/blob/master/surgery.py
      """
  
      import numpy as np
      factor = (kernel_size + 1) // 2
      if kernel_size % 2 == 1:
          center = factor - 1
      else:
          center = factor - 0.5
      og = np.ogrid[:kernel_size, :kernel_size]
      filt = (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)
      weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size), dtype=np.float64)
      weight[range(in_channels), range(out_channels), :, :] = filt
      return torch.from_numpy(weight).float()
  
  
  class FCN(nn.Module):
      """FCN(Fully Convolutional Neural Network) implement.
  
      https://github.com/wkentaro/pytorch-fcn/blob/master/torchfcn/models/fcn16s.py
      """
  
      def __init__(self, n_class=21):
          super(FCN, self).__init__()
          # conv1
          self.conv1_1 = nn.Conv2d(3, 64, 3, padding=100)
          self.relu1_1 = nn.ReLU(inplace=True)
          self.conv1_2 = nn.Conv2d(64, 64, 3, padding=1)
          self.relu1_2 = nn.ReLU(inplace=True)
          self.pool1 = nn.MaxPool2d(2, stride=2, ceil_mode=True)    # 1/2
  
          # conv2
          self.conv2_1 = nn.Conv2d(64, 128, 3, padding=1)
          self.relu2_1 = nn.ReLU(inplace=True)
          self.conv2_2 = nn.Conv2d(128, 128, 3, padding=1)
          self.relu2_2 = nn.ReLU(inplace=True)
          self.pool2 = nn.MaxPool2d(2, stride=2, ceil_mode=True)    # 1/4
  
          # conv3
          self.conv3_1 = nn.Conv2d(128, 256, 3, padding=1)
          self.relu3_1 = nn.ReLU(inplace=True)
          self.conv3_2 = nn.Conv2d(256, 256, 3, padding=1)
          self.relu3_2 = nn.ReLU(inplace=True)
          self.conv3_3 = nn.Conv2d(256, 256, 3, padding=1)
          self.relu3_3 = nn.ReLU(inplace=True)
          self.pool3 = nn.MaxPool2d(2, stride=2, ceil_mode=True)    # 1/8
  
          # conv4
          self.conv4_1 = nn.Conv2d(256, 512, 3, padding=1)
          self.relu4_1 = nn.ReLU(inplace=True)
          self.conv4_2 = nn.Conv2d(512, 512, 3, padding=1)
          self.relu4_2 = nn.ReLU(inplace=True)
          self.conv4_3 = nn.Conv2d(512, 512, 3, padding=1)
          self.relu4_3 = nn.ReLU(inplace=True)
          self.pool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True)    # 1/16
  
          # conv5
          self.conv5_1 = nn.Conv2d(512, 512, 3, padding=1)
          self.relu5_1 = nn.ReLU(inplace=True)
          self.conv5_2 = nn.Conv2d(512, 512, 3, padding=1)
          self.relu5_2 = nn.ReLU(inplace=True)
          self.conv5_3 = nn.Conv2d(512, 512, 3, padding=1)
          self.relu5_3 = nn.ReLU(inplace=True)
          self.pool5 = nn.MaxPool2d(2, stride=2, ceil_mode=True)    # 1/32
  
          # fc6
          self.fc6 = nn.Conv2d(512, 4096, 7)
          self.relu6 = nn.ReLU(inplace=True)
          self.drop6 = nn.Dropout2d()
  
          # fc7
          self.fc7 = nn.Conv2d(4096, 4096, 1)
          self.relu7 = nn.ReLU(inplace=True)
          self.drop7 = nn.Dropout2d()
  
          self.score_fr = nn.Conv2d(4096, n_class, 1)
          self.score_pool4 = nn.Conv2d(512, n_class, 1)
  
          self.upscore2 = nn.ConvTranspose2d(n_class, n_class, 4, stride=2, bias=False)
          self.upscore16 = nn.ConvTranspose2d(n_class, n_class, 32, stride=16, bias=False)
  
          self._initialize_weights()
  
      def _initialize_weights(self):
          for m in self.modules():
              if isinstance(m, nn.Conv2d):
                  m.weight.data.zero_()
                  if m.bias is not None:
                      m.bias.data.zero_()
              if isinstance(m, nn.ConvTranspose2d):
                  assert m.kernel_size[0] == m.kernel_size[1]
                  initial_weight = get_upsampling_weight(m.in_channels, m.out_channels, m.kernel_size[0])
                  m.weight.data.copy_(initial_weight)
  
      def forward(self, x):
  
          h = x
          h = self.relu1_1(self.conv1_1(h))
          h = self.relu1_2(self.conv1_2(h))
          h = self.pool1(h)
  
          h = self.relu2_1(self.conv2_1(h))
          h = self.relu2_2(self.conv2_2(h))
          h = self.pool2(h)
  
          h = self.relu3_1(self.conv3_1(h))
          h = self.relu3_2(self.conv3_2(h))
          h = self.relu3_3(self.conv3_3(h))
          h = self.pool3(h)
  
          h = self.relu4_1(self.conv4_1(h))
          h = self.relu4_2(self.conv4_2(h))
          h = self.relu4_3(self.conv4_3(h))
          h = self.pool4(h)
          pool4 = h    # 1/16
  
          h = self.relu5_1(self.conv5_1(h))
          h = self.relu5_2(self.conv5_2(h))
          h = self.relu5_3(self.conv5_3(h))
          h = self.pool5(h)
  
          h = self.relu6(self.fc6(h))
          h = self.drop6(h)
  
          h = self.relu7(self.fc7(h))
          h = self.drop7(h)
  
          h = self.score_fr(h)
          h = self.upscore2(h)
          upscore2 = h    # 1/16
  
          h = self.score_pool4(pool4)
          h = h[:, :, 5:5 + upscore2.size()[2], 5:5 + upscore2.size()[3]]
          score_pool4c = h    # 1/16
  
          h = upscore2 + score_pool4c
  
          h = self.upscore16(h)
          h = h[:, :, 27:27 + x.size()[2], 27:27 + x.size()[3]].contiguous()
  
          return h
  