-
Notifications
You must be signed in to change notification settings - Fork 91
Expand file tree
/
Copy pathunet_multilayer.py
More file actions
166 lines (139 loc) · 6.5 KB
/
Copy pathunet_multilayer.py
File metadata and controls
166 lines (139 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# -*- coding: utf-8 -*-
"""
Implementation of UNet
"""
import torch
from torch.nn import ModuleList
from GANDLF.models.seg_modules.DownsamplingModule import DownsamplingModule
from GANDLF.models.seg_modules.EncodingModule import EncodingModule
from GANDLF.models.seg_modules.DecodingModule import DecodingModule
from GANDLF.models.seg_modules.UpsamplingModule import UpsamplingModule
from GANDLF.models.seg_modules.InitialConv import InitialConv
from GANDLF.models.seg_modules.out_conv import out_conv
from .modelBase import ModelBase
class unet_multilayer(ModelBase):
"""
This is the standard U-Net architecture : https://arxiv.org/pdf/1606.06650.pdf. The 'residualConnections' flag controls residual connections, the
Downsampling, Encoding, Decoding modules are defined in the seg_modules file. These smaller modules are basically defined by 2 parameters, the input
channels (filters) and the output channels (filters), and some other hyperparameters, which remain constant all the modules. For more details on the
smaller modules please have a look at the seg_modules file.
"""
def __init__(self, parameters: dict, residualConnections: bool = False):
"""
The constructor for the unet_multilayer class.
Args:
parameters (dict): A dictionary containing the model parameters.
residualConnections (bool, optional): Flag to control residual connections. Defaults to False.
"""
self.network_kwargs = {"res": residualConnections}
super(unet_multilayer, self).__init__(parameters)
# Set the depth of the model based on the input parameters
# If the depth is not set, then set it to 4
parameters["model"]["depth"] = parameters["model"].get("depth", 4)
self.depth = self.model_depth_check(parameters)
# Create the initial convolution layer
self.ins = InitialConv(
input_channels=self.n_channels,
output_channels=self.base_filters,
conv=self.Conv,
dropout=self.Dropout,
norm=self.Norm,
network_kwargs=self.network_kwargs,
)
# Create lists of downsampling, encoding, decoding and upsampling modules
self.ds = ModuleList([])
self.en = ModuleList([])
self.us = ModuleList([])
self.de = ModuleList([])
# Create the required number of downsampling, encoding, decoding and upsampling modules
for i_lay in range(0, self.depth):
self.ds.append(
DownsamplingModule(
input_channels=self.base_filters * 2 ** (i_lay),
output_channels=self.base_filters * 2 ** (i_lay + 1),
conv=self.Conv,
norm=self.Norm,
)
)
self.us.append(
UpsamplingModule(
input_channels=self.base_filters * 2 ** (i_lay + 1),
output_channels=self.base_filters * 2 ** (i_lay),
conv=self.Conv,
interpolation_mode=self.linear_interpolation_mode,
)
)
self.de.append(
DecodingModule(
input_channels=self.base_filters * 2 ** (i_lay + 1),
output_channels=self.base_filters * 2 ** (i_lay),
conv=self.Conv,
norm=self.Norm,
network_kwargs=self.network_kwargs,
)
)
self.en.append(
EncodingModule(
input_channels=self.base_filters * 2 ** (i_lay + 1),
output_channels=self.base_filters * 2 ** (i_lay + 1),
conv=self.Conv,
dropout=self.Dropout,
norm=self.Norm,
network_kwargs=self.network_kwargs,
)
)
# Create the final convolution layer
self.out = out_conv(
input_channels=self.base_filters,
output_channels=self.n_classes,
conv=self.Conv,
norm=self.Norm,
network_kwargs=self.network_kwargs,
final_convolution_layer=self.final_convolution_layer,
sigmoid_input_multiplier=self.sigmoid_input_multiplier,
)
# Check if converter_type is passed in model, generally referring to ACS
if "converter_type" in parameters["model"]:
self.ins = self.converter(self.ins).model
self.out = self.converter(self.out).model
for i_lay in range(0, self.depth):
self.ds[i_lay] = self.converter(self.ds[i_lay]).model
self.us[i_lay] = self.converter(self.us[i_lay]).model
self.de[i_lay] = self.converter(self.de[i_lay]).model
self.en[i_lay] = self.converter(self.en[i_lay]).model
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Forward pass of the U-Net model.
Args:
x (torch.Tensor): The input tensor.
Returns:
torch.Tensor: The output tensor.
"""
# Store intermediate feature maps
y = []
y.append(self.ins(x))
# [downsample --> encode] x num layers
for i in range(0, self.depth):
temp = self.ds[i](y[i])
y.append(self.en[i](temp))
# Get the feature map at the last (deepest) layer
x = y[-1]
# [upsample --> encode] x num layers
for i in range(self.depth - 1, -1, -1):
# Upsample the feature map to match the size of the corresponding feature map in the encoder
x = self.us[i](x)
# Concatenate with the corresponding feature map from the encoder
x = self.de[i](x, y[i])
# Get the final output
x = self.out(x)
return x
class resunet_multilayer(unet_multilayer):
"""
This is the standard U-Net architecture with residual connections : https://arxiv.org/pdf/1606.06650.pdf.
The 'residualConnections' flag controls residual connections, the
Downsampling, Encoding, Decoding modules are defined in the seg_modules file. These smaller modules are basically defined by 2 parameters, the input
channels (filters) and the output channels (filters), and some other hyperparameters, which remain constant all the modules. For more details on the
smaller modules please have a look at the seg_modules file.
"""
def __init__(self, parameters: dict):
super(resunet_multilayer, self).__init__(parameters, residualConnections=True)