-
Notifications
You must be signed in to change notification settings - Fork 91
Expand file tree
/
Copy pathdensenet.py
More file actions
309 lines (259 loc) · 11.6 KB
/
Copy pathdensenet.py
File metadata and controls
309 lines (259 loc) · 11.6 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# adapted from https://github.com/kenshohara/3D-ResNets-PyTorch
import sys
import torch
import torch.nn as nn
import torch.nn.functional as F
from collections import OrderedDict
from .modelBase import ModelBase
class _DenseLayer(nn.Sequential):
def __init__(self, num_input_features, growth_rate, bn_size, drop_rate, Norm, Conv):
"""
Constructor for _DenseLayer class.
Args:
num_input_features (int): Number of input channels to the layer.
growth_rate (int): Number of output channels of each convolution operation in the layer.
bn_size (int): Factor to scale the number of intermediate channels between the 1x1 and 3x3 convolutions.
drop_rate (float): Probability of an element to be zeroed in the Dropout layer.
Norm (torch.nn.Module): A normalization module from torch.nn, such as BatchNorm2d or InstanceNorm2d.
Conv (torch.nn.Module): A convolution module from torch.nn, such as Conv2d or ConvTranspose2d.
"""
# Call the constructor of the parent class
super().__init__()
# Add a batch normalization layer followed by a ReLU activation function and a 1x1 convolution layer
self.add_module("norm1", Norm(num_input_features))
self.add_module("relu1", nn.ReLU(inplace=True))
self.add_module(
"conv1",
Conv(
num_input_features,
bn_size * growth_rate,
kernel_size=1,
stride=1,
bias=False,
),
)
# Add another batch normalization layer followed by a ReLU activation function and a 3x3 convolution layer
self.add_module("norm2", Norm(bn_size * growth_rate))
self.add_module("relu2", nn.ReLU(inplace=True))
self.add_module(
"conv2",
Conv(
bn_size * growth_rate,
growth_rate,
kernel_size=3,
stride=1,
padding=1,
bias=False,
),
)
# Set the dropout rate
self.drop_rate = drop_rate
def forward(self, x):
"""
Forward pass through the _DenseLayer.
Args:
x (torch.Tensor): Input tensor.
Returns:
torch.Tensor: Output tensor obtained after concatenating the input tensor and the new features obtained after the convolution operations and dropout.
"""
# Perform forward pass through the layers
new_features = super().forward(x)
# If dropout rate is greater than 0, apply dropout to the new features
if self.drop_rate > 0:
new_features = F.dropout(
new_features, p=self.drop_rate, training=self.training
)
# Concatenate the input tensor with the new features and return the result
return torch.cat([x, new_features], 1)
class _DenseBlock(nn.Sequential):
def __init__(
self,
num_layers,
num_input_features,
bn_size,
growth_rate,
drop_rate,
norm,
conv,
):
"""
Constructor for _DenseBlock class.
Args:
num_layers (int): Number of dense layers to be added to the block.
num_input_features (int): Number of input channels to the block.
bn_size (int): Factor to scale the number of intermediate channels between the 1x1 and 3x3 convolutions in each dense layer.
growth_rate (int): Number of output channels of each convolution operation in each dense layer.
drop_rate (float): Probability of an element to be zeroed in the Dropout layer of each dense layer.
norm (torch.nn.Module): A normalization module from torch.nn, such as BatchNorm2d or InstanceNorm2d, to be used in each dense layer.
conv (torch.nn.Module): A convolution module from torch.nn, such as Conv2d or ConvTranspose2d, to be used in each dense layer.
"""
# Call the constructor of the parent class
super().__init__()
# Add num_layers _DenseLayer objects to the block
for i in range(num_layers):
# Calculate the number of input features for the i-th dense layer
num_input_features_i = num_input_features + i * growth_rate
# Create an instance of _DenseLayer with the calculated number of input features and other parameters
layer = _DenseLayer(
num_input_features_i, growth_rate, bn_size, drop_rate, norm, conv
)
# Add the _DenseLayer object to the block
self.add_module("denselayer{}".format(i + 1), layer)
class _Transition(nn.Sequential):
def __init__(self, num_input_features, num_output_features, Norm, Conv, AvgPool):
super().__init__()
self.add_module("norm", Norm(num_input_features))
self.add_module("relu", nn.ReLU(inplace=True))
self.add_module(
"conv",
Conv(
num_input_features,
num_output_features,
kernel_size=1,
stride=1,
bias=False,
),
)
self.add_module("pool", AvgPool(kernel_size=2, stride=2))
class DenseNet(ModelBase):
def __init__(self, parameters: dict, block_config=(6, 12, 24, 16)):
"""
Densenet-BC model class
Args:
block_config (list of 4 ints): how many layers in each pooling block
parameters (dict): overall parameters dictionary; parameters specific for DenseNet:
- growth_rate (int): how many filters to add each layer (k in paper)
- num_init_features (int): the number of filters to learn in the first convolution layer
- bn_size (int): multiplicative factor for number of bottle neck layers
(i.e. bn_size * k features in the bottleneck layer)
- drop_rate (float): dropout rate after each dense layer
- num_classes (int): number of classification classes
- final_convolution_layer (str): the final convolutional layer to use
- norm_type (str): the normalization type to use
"""
super(DenseNet, self).__init__(parameters)
# defining some defaults
if not ("num_init_features" in parameters):
parameters["num_init_features"] = 64
if not ("growth_rate" in parameters):
parameters["growth_rate"] = 32
if not ("bn_size" in parameters):
parameters["bn_size"] = 4
if not ("drop_rate" in parameters):
parameters["drop_rate"] = 0
if not ("conv1_t_stride" in parameters):
parameters["conv1_t_stride"] = 1
if not ("conv1_t_size" in parameters):
parameters["conv1_t_size"] = 7
if not ("no_max_pool" in parameters):
parameters["no_max_pool"] = False
if self.Norm is None:
sys.stderr.write(
"Warning: densenet is not defined without a normalization layer"
)
self.Norm = self.BatchNorm
if self.n_dimensions == 2:
self.output_size = (1, 1)
self.conv_stride = (parameters["conv1_t_stride"], 2)
elif self.n_dimensions == 3:
self.output_size = (1, 1, 1)
self.conv_stride = (parameters["conv1_t_stride"], 2, 2)
# First convolution
self.features = [
(
"conv1",
self.Conv(
self.n_channels,
parameters["num_init_features"],
kernel_size=parameters["conv1_t_size"],
stride=self.conv_stride,
padding=parameters["conv1_t_size"] // 2,
bias=False,
),
),
("norm1", self.Norm(parameters["num_init_features"])),
("relu1", nn.ReLU(inplace=True)),
]
if not parameters["no_max_pool"]:
self.features.append(
("pool1", self.MaxPool(kernel_size=3, stride=2, padding=1))
)
self.features = nn.Sequential(OrderedDict(self.features))
# Each denseblock
num_features = parameters["num_init_features"]
for i, num_layers in enumerate(block_config):
block = _DenseBlock(
num_layers=num_layers,
num_input_features=num_features,
bn_size=parameters["bn_size"],
growth_rate=parameters["growth_rate"],
drop_rate=parameters["drop_rate"],
norm=self.Norm,
conv=self.Conv,
)
self.features.add_module("denseblock{}".format(i + 1), block)
num_features = num_features + num_layers * parameters["growth_rate"]
if i != len(block_config) - 1:
trans = _Transition(
num_input_features=num_features,
num_output_features=num_features // 2,
Norm=self.Norm,
Conv=self.Conv,
AvgPool=self.AvgPool,
)
self.features.add_module("transition{}".format(i + 1), trans)
num_features = num_features // 2
# Final batch norm
self.features.add_module("norm5", self.Norm(num_features))
for m in self.modules():
if isinstance(m, self.Conv):
m.weight = nn.init.kaiming_normal_(m.weight, mode="fan_out")
elif isinstance(m, self.Norm):
if m.weight is not None:
m.weight.data.fill_(1)
if m.bias is not None:
m.bias.data.zero_()
# Linear layer
self.classifier = nn.Linear(num_features, self.n_classes)
for m in self.modules():
if isinstance(m, self.Conv):
nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
elif isinstance(m, self.Norm):
if m.weight is not None:
m.weight.data.fill_(1)
if m.bias is not None:
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
nn.init.constant_(m.bias, 0)
def forward(self, x):
"""
Forward pass of the network
Args:
x (torch.Tensor) - the input tensor
Returns:
out (torch.Tensor) - the output tensor
"""
# Pass the input tensor through the convolutional layers of the model
features = self.features(x)
# Apply a ReLU activation function to the feature maps
out = F.relu(features, inplace=True)
# Apply adaptive average pooling to the feature maps and flatten the resulting tensor
out = self.AdaptiveAvgPool(self.output_size)(out).view(features.size(0), -1)
# Pass the flattened tensor through the fully connected layers of the model
out = self.classifier(out)
# Apply the final convolutional operation, if specified
if not self.final_convolution_layer is None:
if self.final_convolution_layer == F.softmax:
out = self.final_convolution_layer(out, dim=1)
else:
out = self.final_convolution_layer(out)
# Return the output tensor
return out
def densenet121(parameters):
return DenseNet(parameters, block_config=(6, 12, 24, 16))
def densenet169(parameters):
return DenseNet(parameters, block_config=(6, 12, 32, 32))
def densenet201(parameters):
return DenseNet(parameters, block_config=(6, 12, 48, 32))
def densenet264(parameters):
return DenseNet(parameters, block_config=(6, 12, 64, 48))