-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.py
195 lines (149 loc) · 6.49 KB
/
model.py
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
# -*- coding: utf-8 -*-
"""
The file for the definition of the SelectiveNet and Classifier models.
Classifier - Class for a EfficientNet Classifier Model.
SelectiveNet - Class for the SelectiveNet Model using a EfficientNet encoder.
"""
# Built-in/Generic Imports
import os
# Library Imports
import torch
import torch.nn as nn
from torch.nn import functional as F
from efficientnet_pytorch import EfficientNet
__author__ = ["Jacob Carse"]
__copyright__ = "Copyright 2020, Selective Dermatology"
__credits__ = ["Jacob Carse", "Stephen Hogg", "Stephen McKenna"]
__license__ = "MIT"
__version__ = "3.0.0"
__maintainer = "Jacob Carse"
__email__ = "j.carse@dundee.ac.uk"
__status__ = "Development"
class Classifier(nn.Module):
"""
Class for the Classifier model that uses an EfficientNet encoder with optional drop out.
"""
def __init__(self, b=0, drop_rate=0.5, pretrained=True):
"""
Initiliser for the model that initialises the models layers.
:param b: The compound coefficient of the EfficientNet model to be loaded.
:param drop_rate: The drop rate for the optional dropout layers.
:param pretrained: Boolean if pretrained weights should be loaded.
"""
# Calls the super for the nn.Module.
super(Classifier, self).__init__()
# Sets the drop rate for the dropout layers.
self.drop_rate = drop_rate
# Loads the EfficientNet encoder.
if pretrained:
self.encoder = EfficientNet.from_pretrained(f"efficientnet-b{str(b)}")
else:
self.encoder = EfficientNet.from_name(f"efficientnet-b{str(b)}")
self.encoder_pool = nn.AdaptiveAvgPool2d(1)
# Defines a hidden layer.
self.hidden = nn.Linear(2560, 512)
# Defines the output layer of the neural network.
self.classifier = nn.Linear(512, 2)
def forward(self, x, dropout=False):
"""
Performs forward propagation with the Classifier.
:param x: Input image batch.
:param dropout: Boolean if dropout should be applied.
:return: A PyTorch Tensor of logits.
"""
# Performs forward propagation with the encoder.
x = self.encoder.extract_features(x)
x = self.encoder_pool(x)
x = x.view(x.shape[0], -1)
# Applies dropout to the model is selected.
if dropout:
x = F.dropout(x, self.drop_rate)
# Performs forward propagation with the hidden layer.
x = self.hidden(x)
# Applies dropout to the model is selected.
if dropout:
x = F.dropout(x, self.drop_rate)
# Gets the output logits from the output layer.
return self.classifier(x)
def save_model(self, path, name, epoch="best"):
"""
Method for saving the model.
:param path: Directory path to save the model.
:param name: The name of the experiment to be saved.
:param epoch: Integer for the current epoch to be included in the save name.
"""
# Checks if the save directory exists and if not creates it.
os.makedirs(path, exist_ok=True)
# Saves the model to the save directory.
torch.save(self.state_dict(), os.path.join(path, f"{name}_cnn_{str(epoch)}.pt"))
class SelectiveNet(nn.Module):
"""
Class for the SelectiveNet model that uses a EfficientNet encoder and optional drop out.
"""
def __init__(self, b=0, drop_rate=0.5, pretrained=True):
"""
Initiliser for the SelectiveNet model that Initialises the models layers.
:param b: The compound coefficient of the EfficientNet model to be loaded.
:param drop_rate: The drop rate for the optional dropout layers.
:param pretrained: Boolean if pretrained weights should be loaded.
"""
# Calls the super for the nn.Module.
super(SelectiveNet, self).__init__()
# Loads the EfficientNet encoder.
if pretrained:
self.encoder = EfficientNet.from_pretrained(f"efficientnet-b{str(b)}")
else:
self.encoder = EfficientNet.from_name(f"efficientnet-b{str(b)}")
self.encoder_pool = nn.AdaptiveAvgPool2d(1)
# Defines a hidden layer.
self.hidden = nn.Linear(2560, 512)
# Initialises the classifier for generating predictions.
self.classifier = nn.Linear(512, 2)
# Initialises the selective branch for generating selection scores.
self.selective_hidden = nn.Linear(512, 512)
self.selective_batch_norm = nn.BatchNorm1d(512)
self.selective_regression = nn.Linear(512, 1)
# Initialises the auxiliary output used by the model during training.
self.auxiliary_output = nn.Linear(512, 2)
# Stores the dropout rate in the object.
self.drop_rate = drop_rate
def forward(self, x, dropout=False):
"""
Performs forward propagation with SelectiveNet.
:param x: The input image batch.
:param dropout: Boolean if dropout should be applied.
:return: Predictions, Selective score and auxiliary output.
"""
# Performs forward propagation with the EfficientNet encoder.
x = self.encoder.extract_features(x)
x = self.encoder_pool(x)
x = x.view(x.shape[0], -1)
# Applies dropout if selected.
if dropout:
x = F.dropout(x, self.drop_rate)
# Uses the hidden layer.
x = self.hidden(x)
# Applies dropout if selected.
if dropout:
x = F.dropout(x, self.drop_rate)
# Gets the predictive output of the model.
x1 = self.classifier(x)
# Gets the selective output of the model.
x2 = F.relu(self.selective_hidden(x))
x2 = self.selective_batch_norm(x2)
x2 = torch.sigmoid(self.selective_regression(x2))
# Gets the auxiliary output of the model.
x3 = self.auxiliary_output(x)
# Returns the outputs of the model.
return x1, x2, x3
def save_model(self, path, name, epoch="best"):
"""
Method for saving the model.
:param path: Directory path to save the model.
:param name: The name of the experiment to be saved.
:param epoch: Integer for the current epoch to be included in the save name.
"""
# Checks if the save directory exists and if not creates it.
os.makedirs(path, exist_ok=True)
# Saves the model to the save directory.
torch.save(self.state_dict(), os.path.join(path, f"{name}_sn_{str(epoch)}.pt"))