ptrblck:

Are you comparing the saved state_dicts or are you saving the model directly?


I have checked again with two ways of saving model directly and only state_dicts. It turns out the size of output file is not so different.  just around 70KB.
But outputs of each models are pretty different . I’ve posted the result of output and each models’s code.
Main:
if __name__ == '__main__':
    import util
    from torchsummary import summary
    u_net = U_net(1,1)
    u_net_p = U_net_P(1,1)
    x = torch.rand(2,1,255, 255)
    x_= x.clone()

    u_net.eval()
    u_net_p.eval()
    # torch.save()
    with torch.no_grad():
        s = u_net.state_dict()
        p = u_net_p.state_dict()
        # torch.save(s, 'sd_n')
        # torch.save(p, 'sd_p')
        # torch.save(u_net, 'sd_n_m')
        # torch.save(u_net_p, 'sd_p_m')
        print(f'x:{x}')
        print(f'x_:{x_}')
        print(f's:{u_net(x)}') # original model
        print(f'p:{u_net_p(x_)}') # model replaced with separate modules
        # summary(u_net, (1,255,255), device='cpu')
        # summary(u_net_p, (1, 255, 255), device='cpu')


Result:
x:tensor([[[[0.9109, 0.3606, 0.3465,  ..., 0.2579, 0.7382, 0.3515],
          [0.7227, 0.6423, 0.3299,  ..., 0.9388, 0.5559, 0.3604],
          [0.1222, 0.3378, 0.2227,  ..., 0.7979, 0.4426, 0.2258],
          ...,
          [0.6314, 0.0019, 0.9010,  ..., 0.5534, 0.1947, 0.2182],
          [0.5756, 0.5397, 0.8308,  ..., 0.5408, 0.5420, 0.2797],
          [0.4340, 0.5136, 0.5940,  ..., 0.2925, 0.3168, 0.2648]]],


        [[[0.6372, 0.9717, 0.3303,  ..., 0.4325, 0.5058, 0.3741],
          [0.5211, 0.3018, 0.7373,  ..., 0.8404, 0.5879, 0.2053],
          [0.5741, 0.5648, 0.2398,  ..., 0.3578, 0.1555, 0.9188],
          ...,
          [0.6338, 0.4263, 0.0118,  ..., 0.4370, 0.0514, 0.6219],
          [0.7636, 0.2459, 0.2059,  ..., 0.1525, 0.0162, 0.4530],
          [0.3828, 0.3840, 0.0025,  ..., 0.5548, 0.8746, 0.8423]]]])
x_:tensor([[[[0.9109, 0.3606, 0.3465,  ..., 0.2579, 0.7382, 0.3515],
          [0.7227, 0.6423, 0.3299,  ..., 0.9388, 0.5559, 0.3604],
          [0.1222, 0.3378, 0.2227,  ..., 0.7979, 0.4426, 0.2258],
          ...,
          [0.6314, 0.0019, 0.9010,  ..., 0.5534, 0.1947, 0.2182],
          [0.5756, 0.5397, 0.8308,  ..., 0.5408, 0.5420, 0.2797],
          [0.4340, 0.5136, 0.5940,  ..., 0.2925, 0.3168, 0.2648]]],


        [[[0.6372, 0.9717, 0.3303,  ..., 0.4325, 0.5058, 0.3741],
          [0.5211, 0.3018, 0.7373,  ..., 0.8404, 0.5879, 0.2053],
          [0.5741, 0.5648, 0.2398,  ..., 0.3578, 0.1555, 0.9188],
          ...,
          [0.6338, 0.4263, 0.0118,  ..., 0.4370, 0.0514, 0.6219],
          [0.7636, 0.2459, 0.2059,  ..., 0.1525, 0.0162, 0.4530],
          [0.3828, 0.3840, 0.0025,  ..., 0.5548, 0.8746, 0.8423]]]])
s:tensor([[[[0.0333, 0.0274, 0.0247,  ..., 0.0281, 0.0255, 0.0297],
          [0.0306, 0.0259, 0.0261,  ..., 0.0293, 0.0329, 0.0307],
          [0.0279, 0.0278, 0.0268,  ..., 0.0300, 0.0266, 0.0296],
          ...,
          [0.0288, 0.0286, 0.0277,  ..., 0.0311, 0.0259, 0.0304],
          [0.0283, 0.0317, 0.0312,  ..., 0.0339, 0.0242, 0.0281],
          [0.0272, 0.0257, 0.0278,  ..., 0.0307, 0.0297, 0.0315]]],


        [[[0.0253, 0.0308, 0.0295,  ..., 0.0269, 0.0276, 0.0299],
          [0.0257, 0.0315, 0.0308,  ..., 0.0301, 0.0308, 0.0288],
          [0.0288, 0.0317, 0.0278,  ..., 0.0287, 0.0262, 0.0265],
          ...,
          [0.0275, 0.0278, 0.0278,  ..., 0.0277, 0.0263, 0.0299],
          [0.0308, 0.0276, 0.0320,  ..., 0.0338, 0.0266, 0.0295],
          [0.0303, 0.0276, 0.0324,  ..., 0.0266, 0.0294, 0.0293]]]])
p:tensor([[[[-0.0382, -0.0335, -0.0350,  ..., -0.0347, -0.0347, -0.0348],
          [-0.0374, -0.0351, -0.0392,  ..., -0.0391, -0.0395, -0.0398],
          [-0.0428, -0.0385, -0.0355,  ..., -0.0378, -0.0372, -0.0383],
          ...,
          [-0.0349, -0.0361, -0.0311,  ..., -0.0377, -0.0389, -0.0370],
          [-0.0339, -0.0365, -0.0402,  ..., -0.0395, -0.0393, -0.0347],
          [-0.0370, -0.0346, -0.0365,  ..., -0.0402, -0.0376, -0.0401]]],


        [[[-0.0382, -0.0372, -0.0400,  ..., -0.0364, -0.0372, -0.0379],
          [-0.0324, -0.0357, -0.0414,  ..., -0.0363, -0.0355, -0.0380],
          [-0.0338, -0.0346, -0.0365,  ..., -0.0333, -0.0358, -0.0357],
          ...,
          [-0.0356, -0.0365, -0.0409,  ..., -0.0351, -0.0351, -0.0366],
          [-0.0346, -0.0341, -0.0385,  ..., -0.0386, -0.0395, -0.0373],
          [-0.0356, -0.0334, -0.0378,  ..., -0.0373, -0.0373, -0.0379]]]])

Process finished with exit code 0


Model
class U_net(nn.Module):
    
    def __init__(self, in_channel, n_classes):
        super(U_net, self).__init__()
        
        #input image channels
        self.in_channel = in_channel
        # contractive path conv kernel size:(3x3)
        self.c_kernel_size = 3
        # expansive path conv kernel size:(3x3)
        self.up_kernel_size = 2
        
        self.final_kernel_size = 1
        self.max_pool_kernel_size = 2
        self.max_pool_stride = 2
        
        self.deconv_stride = 2
        
        # activation function
        self.c_activation_f = nn.ReLU(inplace=True)
        
       
        
        # channels
        self.n_out_channels1 = 64
        self.n_out_channels2 = 128
        self.n_out_channels3 = 256
        self.n_out_channels4 = 512
        self.n_out_channels5 = 1024
        self.final_out_channels = n_classes
        
        self.n_skip_channels1 = 128
        self.n_skip_channels2 = 256
        self.n_skip_channels3 = 512
        self.n_skip_channels4 = 1024
        
        self.c_max_pooling =  nn.MaxPool2d(self.max_pool_kernel_size, stride=self.max_pool_stride)
        
         # Batch normalization
        self.c_bn11 = nn.BatchNorm2d(self.n_out_channels1)
        self.c_bn12 = nn.BatchNorm2d(self.n_out_channels1)
        self.c_bn21 = nn.BatchNorm2d(self.n_out_channels2)
        self.c_bn22 = nn.BatchNorm2d(self.n_out_channels2)
        self.c_bn31 = nn.BatchNorm2d(self.n_out_channels3)
        self.c_bn32 = nn.BatchNorm2d(self.n_out_channels3)
        self.c_bn41 = nn.BatchNorm2d(self.n_out_channels4)
        self.c_bn42 = nn.BatchNorm2d(self.n_out_channels4)
        self.c_bn51 = nn.BatchNorm2d(self.n_out_channels5)
        self.c_bn52 = nn.BatchNorm2d(self.n_out_channels5)
        
        ### contractive path layers ###

        self.cont_layer1 = nn.Sequential(OrderedDict([
           ('c_conv11', nn.Conv2d(in_channels=self.in_channel, out_channels=self.n_out_channels1, kernel_size=self.c_kernel_size)),
           ('c_bn_11', nn.BatchNorm2d(self.n_out_channels1)),
           # ('c_bn_11', self.c_bn11),
           # ('c_act_f11', self.c_activation_f),
           ('c_act_f11', nn.ReLU(inplace=True)),
           ('c_conv12', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels1, kernel_size=self.c_kernel_size)),
           ('c_bn_12', nn.BatchNorm2d(self.n_out_channels1)),
           # ('c_bn_12', self.c_bn12),
           # ('c_act_f12', self.c_activation_f)
           ('c_act_f12', nn.ReLU(inplace=True)),
        ]))
    
        # 2th layer
        self.cont_layer2 = nn.Sequential(OrderedDict([
            ('c_conv21', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels2, kernel_size=self.c_kernel_size)),
            ('c_bn_21', nn.BatchNorm2d(self.n_out_channels2)),
            # ('c_bn_21', self.c_bn21),
            # ('c_act_f21', self.c_activation_f),
            ('c_act_f21', nn.ReLU(inplace=True)),
            ('c_conv22', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels2, kernel_size=self.c_kernel_size)),
            ('c_bn_22', nn.BatchNorm2d(self.n_out_channels2)),
            # ('c_bn_22', self.c_bn22),
            # ('c_act_f22', self.c_activation_f)
            ('c_act_f22', nn.ReLU(inplace=True))

        ]))

        # 3th layer
        self.cont_layer3 = nn.Sequential(OrderedDict([
            ('c_conv31', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels3, kernel_size=self.c_kernel_size)),
            ('c_bn_31', nn.BatchNorm2d(self.n_out_channels3)),
            # ('c_bn_31', self.c_bn31),
            # ('c_act_f31', self.c_activation_f),
            ('c_act_f31', nn.ReLU(inplace=True)),
            ('c_bn_32', nn.BatchNorm2d(self.n_out_channels3)),
            # ('c_bn_32', self.c_bn32),
            ('c_conv32', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels3, kernel_size=self.c_kernel_size)),
            # ('c_act_f32', self.c_activation_f)
            ('c_act_f32', nn.ReLU(inplace=True))
        ]))
        # 4th layer
        self.cont_layer4 = nn.Sequential(OrderedDict([
            ('c_conv41', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels4, kernel_size=self.c_kernel_size)),
            ('c_bn_41', nn.BatchNorm2d(self.n_out_channels4)),
            # ('c_bn_41', self.c_bn41),
            # ('c_act_f41', self.c_activation_f),
            ('c_act_f41', nn.ReLU(inplace=True)),
            ('c_bn_42', nn.BatchNorm2d(self.n_out_channels4)),
            # ('c_bn_42', self.c_bn42),
            ('c_conv42', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels4, kernel_size=self.c_kernel_size)),
            # ('c_act_f42', self.c_activation_f),
            ('c_act_f42', nn.ReLU(inplace=True))
        ]))
        # 5th layer
        self.cont_layer5 = nn.Sequential(OrderedDict([
            ('c_conv51', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels5, kernel_size=self.c_kernel_size)),
            ('c_bn_51', nn.BatchNorm2d(self.n_out_channels5)),
            # ('c_bn_51', self.c_bn51),
            # ('c_act_f51', self.c_activation_f),
            ('c_act_f51', nn.ReLU(inplace=True)),
            ('c_conv52', nn.Conv2d(in_channels=self.n_out_channels5, out_channels=self.n_out_channels5, kernel_size=self.c_kernel_size)),
            ('c_bn_52', nn.BatchNorm2d(self.n_out_channels5)),
            # ('c_bn_52', self.c_bn52),
            # ('c_act_f52', self.c_activation_f)
            ('c_act_f52', nn.ReLU(inplace=True))
        ]))
        
        ### expansive path layers ###
        self.exp_layer5 = nn.ConvTranspose2d(in_channels=self.n_out_channels5, out_channels=self.n_out_channels4, 
                                                 kernel_size=self.up_kernel_size, stride=self.deconv_stride)
        
        # 4th layer
        self.exp_layer4 = nn.Sequential(OrderedDict([
            ('e_conv41', nn.Conv2d(in_channels=self.n_skip_channels4, out_channels=self.n_out_channels4, kernel_size=self.c_kernel_size)),
            # ('e_act_f41', self.c_activation_f),
            ('e_act_f41', nn.ReLU(inplace=True)),
            ('e_conv42', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels4, kernel_size=self.c_kernel_size)),
            # ('e_act_f42', self.c_activation_f),
            ('e_act_f42', nn.ReLU(inplace=True)),
            ('e_up_conv4', nn.ConvTranspose2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels3,
                                                 kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))
        
        # 3th layer
        self.exp_layer3 = nn.Sequential(OrderedDict([
            ('e_conv31', nn.Conv2d(in_channels=self.n_skip_channels3, out_channels=self.n_out_channels3, kernel_size=self.c_kernel_size)),
            # ('e_act_f31', self.c_activation_f),
            ('e_act_f31', nn.ReLU(inplace=True)),
            ('e_conv32', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels3, kernel_size=self.c_kernel_size)),
            # ('e_act_f32', self.c_activation_f),
            ('e_act_f32', nn.ReLU(inplace=True)),
            ('e_up_conv3', nn.ConvTranspose2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels2,
                                                kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))
        
        # 2th layer
        self.exp_layer2 = nn.Sequential(OrderedDict([
            ('e_conv21', nn.Conv2d(in_channels=self.n_skip_channels2, out_channels=self.n_out_channels2, kernel_size=self.c_kernel_size)),
            ('e_act_f21', nn.ReLU(inplace=True)),
            # ('e_act_f21', self.c_activation_f),
            ('e_conv22', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels2, kernel_size=self.c_kernel_size)),
            ('e_act_f22', nn.ReLU(inplace=True)),
            # ('e_act_f22', self.c_activation_f),
            ('e_up_conv2', nn.ConvTranspose2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels1,
                                                kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))
        
        # 1th layer
        self.exp_layer1 = nn.Sequential(OrderedDict([
            ('e_conv11', nn.Conv2d(in_channels=self.n_skip_channels1, out_channels=self.n_out_channels1, kernel_size=self.c_kernel_size)),
            ('e_act_f11', nn.ReLU(inplace=True)),
            # ('e_act_f11', self.c_activation_f),
            ('e_conv12', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels1, kernel_size=self.c_kernel_size)),
            ('e_act_f12', nn.ReLU(inplace=True)),
            # ('e_act_f12', self.c_activation_f),
            ('e_conv_f', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.final_out_channels,
                                   kernel_size=self.final_kernel_size))
         ]))
        
        #### without drop-out implemented
        
        # skip operation : skip -> crop and concatenate
        # return concat [n_batch, n_ch, x, h] [n_batch, n_ch, x, h] -> [n_batch, n_ch+n_ch, x, h]
    def skipped_connection(self, cont_maps, exp_maps, height, width):
        cropped_f_maps = self.crop_feature_maps(cont_maps, height, width)
        return torch.cat((cropped_f_maps, exp_maps), 1)
        
    #features = [batchs, n_channels, height , width]
    # h,w = crop후 image size
    def crop_feature_maps(self, features, h, w):
        h_old, w_old = features[0][0].size()
        x = math.ceil((h_old - h) / 2)
        y = math.ceil((w_old - w) / 2)
        return features[:,:, x:(x + h), y:(y + w)]
    
    def contracting_path(self, x):
        self.cont_layer1_out = self.cont_layer1(x)
        
        self.cont_layer2_in = self.c_max_pooling(self.cont_layer1_out)
        self.cont_layer2_out = self.cont_layer2(self.cont_layer2_in)
        
        self.cont_layer3_in = self.c_max_pooling(self.cont_layer2_out)
        self.cont_layer3_out = self.cont_layer3(self.cont_layer3_in)
        
        self.cont_layer4_in = self.c_max_pooling(self.cont_layer3_out)
        self.cont_layer4_out = self.cont_layer4(self.cont_layer4_in)
        
        self.cont_layer5_in = self.c_max_pooling(self.cont_layer4_out)
        self.cont_layer5_out = self.cont_layer5(self.cont_layer5_in)
        
        return self.cont_layer5_out
    
    # x = cont_layer5_out
    def expansive_path(self, x):
        x = self.exp_layer5(x)
        
        x = self.skipped_connection(self.cont_layer4_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer4(x)
        
        x = self.skipped_connection(self.cont_layer3_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer3(x)
        
        x = self.skipped_connection(self.cont_layer2_out, x, x.size()[2], x.size()[3] )
        x = self.exp_layer2(x)
        
        x = self.skipped_connection(self.cont_layer1_out, x, x.size()[2], x.size()[3] )
        x = self.exp_layer1(x)
        return x
   
    # input_x has to be shape of (n_batches, n_channels, height, width) 
    def forward(self, input_x):
        o_x = self.contracting_path(input_x)
        o_x = self.expansive_path(o_x)
        return o_x

Model with replace modules:
class U_net_P(nn.Module):

    def __init__(self, in_channel, n_classes):
        super(U_net_P, self).__init__()

        # input image channels
        self.in_channel = in_channel
        # contractive path conv kernel size:(3x3)
        self.c_kernel_size = 3
        # expansive path conv kernel size:(3x3)
        self.up_kernel_size = 2

        self.final_kernel_size = 1
        self.max_pool_kernel_size = 2
        self.max_pool_stride = 2

        self.deconv_stride = 2

        # activation function
        self.c_activation_f = nn.ReLU(inplace=True)

        # channels
        self.n_out_channels1 = 64
        self.n_out_channels2 = 128
        self.n_out_channels3 = 256
        self.n_out_channels4 = 512
        self.n_out_channels5 = 1024
        self.final_out_channels = n_classes

        self.n_skip_channels1 = 128
        self.n_skip_channels2 = 256
        self.n_skip_channels3 = 512
        self.n_skip_channels4 = 1024

        self.c_max_pooling = nn.MaxPool2d(self.max_pool_kernel_size, stride=self.max_pool_stride)

        # Batch normalization
        self.c_bn11 = nn.BatchNorm2d(self.n_out_channels1)
        self.c_bn12 = nn.BatchNorm2d(self.n_out_channels1)
        self.c_bn21 = nn.BatchNorm2d(self.n_out_channels2)
        self.c_bn22 = nn.BatchNorm2d(self.n_out_channels2)
        self.c_bn31 = nn.BatchNorm2d(self.n_out_channels3)
        self.c_bn32 = nn.BatchNorm2d(self.n_out_channels3)
        self.c_bn41 = nn.BatchNorm2d(self.n_out_channels4)
        self.c_bn42 = nn.BatchNorm2d(self.n_out_channels4)
        self.c_bn51 = nn.BatchNorm2d(self.n_out_channels5)
        self.c_bn52 = nn.BatchNorm2d(self.n_out_channels5)

        ### contractive path layers ###

        self.cont_layer1 = nn.Sequential(OrderedDict([
            ('c_conv11',
             nn.Conv2d(in_channels=self.in_channel, out_channels=self.n_out_channels1, kernel_size=self.c_kernel_size)),
            ('c_bn_11', self.c_bn11),
            ('c_act_f11', self.c_activation_f),
            # ('c_act_f11', nn.ReLU(inplace=True)),
            ('c_conv12', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels1,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_12', self.c_bn12),
            ('c_act_f12', self.c_activation_f)
            # ('c_act_f12', nn.ReLU(inplace=True)),
        ]))

        # 2th layer
        self.cont_layer2 = nn.Sequential(OrderedDict([
            ('c_conv21', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels2,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_21', self.c_bn21),
            ('c_act_f21', self.c_activation_f),
            # ('c_act_f21', nn.ReLU(inplace=True)),
            ('c_conv22', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels2,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_22', self.c_bn22),
            ('c_act_f22', self.c_activation_f)
            # ('c_act_f22', nn.ReLU(inplace=True))

        ]))

        # 3th layer
        self.cont_layer3 = nn.Sequential(OrderedDict([
            ('c_conv31', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels3,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_31', self.c_bn31),
            ('c_act_f31', self.c_activation_f),
            # ('c_act_f31', nn.ReLU(inplace=True)),
            ('c_bn_32', self.c_bn32),
            ('c_conv32', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels3,
                                   kernel_size=self.c_kernel_size)),
            ('c_act_f32', self.c_activation_f)
            # ('c_act_f32', nn.ReLU(inplace=True))
        ]))
        # 4th layer
        self.cont_layer4 = nn.Sequential(OrderedDict([
            ('c_conv41', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels4,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_41', self.c_bn41),
            ('c_act_f41', self.c_activation_f),
            # ('c_act_f41', nn.ReLU(inplace=True)),
            ('c_bn_42', self.c_bn42),
            ('c_conv42', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels4,
                                   kernel_size=self.c_kernel_size)),
            ('c_act_f42', self.c_activation_f),
            # ('c_act_f42', nn.ReLU(inplace=True))
        ]))
        # 5th layer
        self.cont_layer5 = nn.Sequential(OrderedDict([
            ('c_conv51', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels5,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_51', self.c_bn51),
            ('c_act_f51', self.c_activation_f),
            # ('c_act_f51', nn.ReLU(inplace=True)),
            ('c_conv52', nn.Conv2d(in_channels=self.n_out_channels5, out_channels=self.n_out_channels5,
                                   kernel_size=self.c_kernel_size)),
            ('c_bn_52', self.c_bn52),
            ('c_act_f52', self.c_activation_f)
            # ('c_act_f52', nn.ReLU(inplace=True))
        ]))

        ### expansive path layers ###
        self.exp_layer5 = nn.ConvTranspose2d(in_channels=self.n_out_channels5, out_channels=self.n_out_channels4,
                                             kernel_size=self.up_kernel_size, stride=self.deconv_stride)

        # 4th layer
        self.exp_layer4 = nn.Sequential(OrderedDict([
            ('e_conv41', nn.Conv2d(in_channels=self.n_skip_channels4, out_channels=self.n_out_channels4,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f41', self.c_activation_f),
            ('e_conv42', nn.Conv2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels4,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f42', self.c_activation_f),
            ('e_up_conv4', nn.ConvTranspose2d(in_channels=self.n_out_channels4, out_channels=self.n_out_channels3,
                                              kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))

        # 3th layer
        self.exp_layer3 = nn.Sequential(OrderedDict([
            ('e_conv31', nn.Conv2d(in_channels=self.n_skip_channels3, out_channels=self.n_out_channels3,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f31', self.c_activation_f),
            ('e_conv32', nn.Conv2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels3,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f32', self.c_activation_f),
            ('e_up_conv3', nn.ConvTranspose2d(in_channels=self.n_out_channels3, out_channels=self.n_out_channels2,
                                              kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))

        # 2th layer
        self.exp_layer2 = nn.Sequential(OrderedDict([
            ('e_conv21', nn.Conv2d(in_channels=self.n_skip_channels2, out_channels=self.n_out_channels2,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f21', self.c_activation_f),
            ('e_conv22', nn.Conv2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels2,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f22', self.c_activation_f),
            ('e_up_conv2', nn.ConvTranspose2d(in_channels=self.n_out_channels2, out_channels=self.n_out_channels1,
                                              kernel_size=self.up_kernel_size, stride=self.deconv_stride))
        ]))

        # 1th layer
        self.exp_layer1 = nn.Sequential(OrderedDict([
            ('e_conv11', nn.Conv2d(in_channels=self.n_skip_channels1, out_channels=self.n_out_channels1,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f11', self.c_activation_f),
            ('e_conv12', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.n_out_channels1,
                                   kernel_size=self.c_kernel_size)),
            ('e_act_f12', self.c_activation_f),
            ('e_conv_f', nn.Conv2d(in_channels=self.n_out_channels1, out_channels=self.final_out_channels,
                                   kernel_size=self.final_kernel_size))
        ]))

        #### without drop-out implemented

        # skip operation : skip -> crop and concatenate
        # return concat [n_batch, n_ch, x, h] [n_batch, n_ch, x, h] -> [n_batch, n_ch+n_ch, x, h]

    def skipped_connection(self, cont_maps, exp_maps, height, width):
        cropped_f_maps = self.crop_feature_maps(cont_maps, height, width)
        return torch.cat((cropped_f_maps, exp_maps), 1)

    # features = [batchs, n_channels, height , width]
    # h,w = crop후 image size
    def crop_feature_maps(self, features, h, w):
        h_old, w_old = features[0][0].size()
        x = math.ceil((h_old - h) / 2)
        y = math.ceil((w_old - w) / 2)
        return features[:, :, x:(x + h), y:(y + w)]

    def contracting_path(self, x):
        self.cont_layer1_out = self.cont_layer1(x)

        self.cont_layer2_in = self.c_max_pooling(self.cont_layer1_out)
        self.cont_layer2_out = self.cont_layer2(self.cont_layer2_in)

        self.cont_layer3_in = self.c_max_pooling(self.cont_layer2_out)
        self.cont_layer3_out = self.cont_layer3(self.cont_layer3_in)

        self.cont_layer4_in = self.c_max_pooling(self.cont_layer3_out)
        self.cont_layer4_out = self.cont_layer4(self.cont_layer4_in)

        self.cont_layer5_in = self.c_max_pooling(self.cont_layer4_out)
        self.cont_layer5_out = self.cont_layer5(self.cont_layer5_in)

        return self.cont_layer5_out

    # x = cont_layer5_out
    def expansive_path(self, x):
        x = self.exp_layer5(x)

        x = self.skipped_connection(self.cont_layer4_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer4(x)

        x = self.skipped_connection(self.cont_layer3_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer3(x)

        x = self.skipped_connection(self.cont_layer2_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer2(x)

        x = self.skipped_connection(self.cont_layer1_out, x, x.size()[2], x.size()[3])
        x = self.exp_layer1(x)
        return x

    # input_x has to be shape of (n_batches, n_channels, height, width)
    def forward(self, input_x):
        o_x = self.contracting_path(input_x)
        o_x = self.expansive_path(o_x)
        return o_x