Skip to content

Commit 94e08b9

Browse files
committed
new funnel regressor network class
1 parent 491afa8 commit 94e08b9

3 files changed

Lines changed: 367 additions & 0 deletions

File tree

src/spotpython/hyperdict/light_hyper_dict.json

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,110 @@
692692
"upper": 2
693693
}
694694
},
695+
"NNFunnelRegressor": {
696+
"l1": {
697+
"type": "int",
698+
"default": 3,
699+
"transform": "transform_power_2_int",
700+
"lower": 3,
701+
"upper": 8
702+
},
703+
"num_layers": {
704+
"type": "int",
705+
"default": 3,
706+
"transform": "None",
707+
"lower": 2,
708+
"upper": 10
709+
},
710+
"epochs": {
711+
"type": "int",
712+
"default": 4,
713+
"transform": "transform_power_2_int",
714+
"lower": 4,
715+
"upper": 9
716+
},
717+
"batch_size": {
718+
"type": "int",
719+
"default": 4,
720+
"transform": "transform_power_2_int",
721+
"lower": 1,
722+
"upper": 4
723+
},
724+
"act_fn": {
725+
"levels": [
726+
"Sigmoid",
727+
"Tanh",
728+
"ReLU",
729+
"LeakyReLU",
730+
"ELU",
731+
"Swish"
732+
],
733+
"type": "factor",
734+
"default": "ReLU",
735+
"transform": "None",
736+
"class_name": "spotpython.torch.activation",
737+
"core_model_parameter_type": "instance()",
738+
"lower": 0,
739+
"upper": 5
740+
},
741+
"optimizer": {
742+
"levels": [
743+
"Adadelta",
744+
"Adagrad",
745+
"Adam",
746+
"AdamW",
747+
"SparseAdam",
748+
"Adamax",
749+
"ASGD",
750+
"NAdam",
751+
"RAdam",
752+
"RMSprop",
753+
"Rprop",
754+
"SGD"
755+
],
756+
"type": "factor",
757+
"default": "SGD",
758+
"transform": "None",
759+
"class_name": "torch.optim",
760+
"core_model_parameter_type": "str",
761+
"lower": 0,
762+
"upper": 11
763+
},
764+
"dropout_prob": {
765+
"type": "float",
766+
"default": 0.01,
767+
"transform": "None",
768+
"lower": 0.0,
769+
"upper": 0.25
770+
},
771+
"lr_mult": {
772+
"type": "float",
773+
"default": 1.0,
774+
"transform": "None",
775+
"lower": 0.1,
776+
"upper": 10.0
777+
},
778+
"patience": {
779+
"type": "int",
780+
"default": 2,
781+
"transform": "transform_power_2_int",
782+
"lower": 2,
783+
"upper": 6
784+
},
785+
"initialization": {
786+
"levels": [
787+
"Default",
788+
"Kaiming",
789+
"Xavier"
790+
],
791+
"type": "factor",
792+
"default": "Default",
793+
"transform": "None",
794+
"core_model_parameter_type": "str",
795+
"lower": 0,
796+
"upper": 2
797+
}
798+
},
695799
"NNLinearRegressor": {
696800
"l1": {
697801
"type": "int",

src/spotpython/light/regression/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .nn_transformer_regressor import NNTransformerRegressor
88
from .nn_linear_regressor import NNLinearRegressor
99
from .netlightregression import NetLightRegression
10+
from .nn_funnel_regressor import NNFunnelRegressor
1011
from .nn_condnet_regressor import NNCondNetRegressor
1112
from .nn_many_to_many_rnn_regressor import ManyToManyRNNRegressor, ManyToManyRNN
1213
from .nn_many_to_many_gru_regressor import ManyToManyGRURegressor
@@ -15,6 +16,7 @@
1516
__all__ = [
1617
"NNLinearRegressor",
1718
"NetLightRegression",
19+
"NNFunnelRegressor",
1820
"NNResNetRegressor",
1921
"NNTransformerRegressor",
2022
"NNCondNetRegressor",
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import lightning as L
2+
import torch
3+
from torch import nn
4+
from spotpython.hyperparameters.optimizer import optimizer_handler
5+
import torchmetrics.functional.regression
6+
7+
8+
class NNFunnelRegressor(L.LightningModule):
9+
"""
10+
A LightningModule class for a regression neural network model.
11+
This is a funnel shape neural network with varying number of layers and neurons per layer. An enhanced version of this class is available
12+
as nn_linear_regression.py in the same directory.
13+
14+
Attributes:
15+
l1 (int):
16+
The number of neurons in the first hidden layer.
17+
num_layers (int):
18+
The number of hidden layers in the model.
19+
epochs (int):
20+
The number of epochs to train the model for.
21+
batch_size (int):
22+
The batch size to use during training.
23+
initialization (str):
24+
The initialization method to use for the weights.
25+
act_fn (nn.Module):
26+
The activation function to use in the hidden layers.
27+
optimizer (str):
28+
The optimizer to use during training.
29+
dropout_prob (float):
30+
The probability of dropping out a neuron during training.
31+
lr_mult (float):
32+
The learning rate multiplier for the optimizer.
33+
patience (int):
34+
The number of epochs to wait before early stopping.
35+
_L_in (int):
36+
The number of input features.
37+
_L_out (int):
38+
The number of output classes.
39+
_torchmetric (str):
40+
The metric to use for the loss function. If `None`,
41+
then "mean_squared_error" is used.
42+
layers (nn.Sequential):
43+
The neural network model.
44+
"""
45+
46+
def __init__(
47+
self,
48+
l1: int,
49+
num_layers: int,
50+
epochs: int,
51+
batch_size: int,
52+
initialization: str,
53+
act_fn: nn.Module,
54+
optimizer: str,
55+
dropout_prob: float,
56+
lr_mult: float,
57+
patience: int,
58+
_L_in: int,
59+
_L_out: int,
60+
_torchmetric: str,
61+
*args,
62+
**kwargs,
63+
):
64+
"""
65+
Initializes the NetLightRegression object.
66+
67+
Args:
68+
l1 (int): The number of neurons in the first hidden layer.
69+
num_layers (int): The number of hidden layers in the model.
70+
epochs (int): The number of epochs to train the model for.
71+
batch_size (int): The batch size to use during training.
72+
initialization (str): The initialization method to use for the weights.
73+
act_fn (nn.Module): The activation function to use in the hidden layers.
74+
optimizer (str): The optimizer to use during training.
75+
dropout_prob (float): The probability of dropping out a neuron during training.
76+
lr_mult (float): The learning rate multiplier for the optimizer.
77+
patience (int): The number of epochs to wait before early stopping.
78+
_L_in (int): The number of input features. Not a hyperparameter, but needed to create the network.
79+
_L_out (int): The number of output classes. Not a hyperparameter, but needed to create the network.
80+
_torchmetric (str):
81+
The metric to use for the loss function. If `None`,
82+
then "mean_squared_error" is used.
83+
84+
Returns:
85+
(NoneType): None
86+
87+
Raises:
88+
ValueError: If l1 is less than 8.
89+
90+
"""
91+
super().__init__()
92+
# Attribute 'act_fn' is an instance of `nn.Module` and is already saved during
93+
# checkpointing. It is recommended to ignore them
94+
# using `self.save_hyperparameters(ignore=['act_fn'])`
95+
# self.save_hyperparameters(ignore=["act_fn"])
96+
#
97+
self._L_in = _L_in
98+
self._L_out = _L_out
99+
if _torchmetric is None:
100+
_torchmetric = "mean_squared_error"
101+
self._torchmetric = _torchmetric
102+
self.metric = getattr(torchmetrics.functional.regression, _torchmetric)
103+
# _L_in and _L_out are not hyperparameters, but are needed to create the network
104+
# _torchmetric is not a hyperparameter, but is needed to calculate the loss
105+
self.save_hyperparameters(ignore=["_L_in", "_L_out", "_torchmetric"])
106+
# set dummy input array for Tensorboard Graphs
107+
# set log_graph=True in Trainer to see the graph (in traintest.py)
108+
self.example_input_array = torch.zeros((batch_size, self._L_in))
109+
if self.hparams.l1 < 8:
110+
raise ValueError("l1 must be at least 8")
111+
112+
113+
layers = []
114+
in_features = self._L_in
115+
hidden_size = self.hparams.l1
116+
output_dim = self._L_out
117+
118+
for i in range(self.hparams.num_layers):
119+
out_features = max(hidden_size // 2, 8) # Enforce minimum of 8 units
120+
layers += [
121+
nn.Linear(in_features, hidden_size),
122+
self.hparams.act_fn,
123+
nn.Dropout(self.hparams.dropout_prob),]
124+
in_features = hidden_size
125+
hidden_size = out_features
126+
127+
layers.append(nn.Linear(in_features, output_dim))
128+
129+
self.layers = nn.Sequential(*layers)
130+
131+
def forward(self, x: torch.Tensor) -> torch.Tensor:
132+
"""
133+
Performs a forward pass through the model.
134+
135+
Args:
136+
x (torch.Tensor): A tensor containing a batch of input data.
137+
138+
Returns:
139+
torch.Tensor: A tensor containing the output of the model.
140+
141+
"""
142+
x = self.layers(x)
143+
return x
144+
145+
def _calculate_loss(self, batch):
146+
"""
147+
Calculate the loss for the given batch.
148+
149+
Args:
150+
batch (tuple): A tuple containing a batch of input data and labels.
151+
mode (str, optional): The mode of the model. Defaults to "train".
152+
153+
Returns:
154+
torch.Tensor: A tensor containing the loss for this batch.
155+
156+
"""
157+
x, y = batch
158+
y = y.view(len(y), 1)
159+
y_hat = self(x)
160+
loss = self.metric(y_hat, y)
161+
return loss
162+
163+
def training_step(self, batch: tuple) -> torch.Tensor:
164+
"""
165+
Performs a single training step.
166+
167+
Args:
168+
batch (tuple): A tuple containing a batch of input data and labels.
169+
170+
Returns:
171+
torch.Tensor: A tensor containing the loss for this batch.
172+
173+
"""
174+
val_loss = self._calculate_loss(batch)
175+
# self.log("train_loss", val_loss, on_step=True, on_epoch=True, prog_bar=True)
176+
# self.log("train_mae_loss", mae_loss, on_step=True, on_epoch=True, prog_bar=True)
177+
return val_loss
178+
179+
def validation_step(self, batch: tuple, batch_idx: int, prog_bar: bool = False) -> torch.Tensor:
180+
"""
181+
Performs a single validation step.
182+
183+
Args:
184+
batch (tuple): A tuple containing a batch of input data and labels.
185+
batch_idx (int): The index of the current batch.
186+
prog_bar (bool, optional): Whether to display the progress bar. Defaults to False.
187+
188+
Returns:
189+
torch.Tensor: A tensor containing the loss for this batch.
190+
191+
"""
192+
val_loss = self._calculate_loss(batch)
193+
# self.log("val_loss", val_loss, on_step=False, on_epoch=True, prog_bar=prog_bar)
194+
self.log("val_loss", val_loss, prog_bar=prog_bar)
195+
self.log("hp_metric", val_loss, prog_bar=prog_bar)
196+
return val_loss
197+
198+
def test_step(self, batch: tuple, batch_idx: int, prog_bar: bool = False) -> torch.Tensor:
199+
"""
200+
Performs a single test step.
201+
202+
Args:
203+
batch (tuple): A tuple containing a batch of input data and labels.
204+
batch_idx (int): The index of the current batch.
205+
prog_bar (bool, optional): Whether to display the progress bar. Defaults to False.
206+
207+
Returns:
208+
torch.Tensor: A tensor containing the loss for this batch.
209+
"""
210+
val_loss = self._calculate_loss(batch)
211+
self.log("val_loss", val_loss, prog_bar=prog_bar)
212+
self.log("hp_metric", val_loss, prog_bar=prog_bar)
213+
return val_loss
214+
215+
def predict_step(self, batch: tuple, batch_idx: int, prog_bar: bool = False) -> torch.Tensor:
216+
"""
217+
Performs a single prediction step.
218+
219+
Args:
220+
batch (tuple): A tuple containing a batch of input data and labels.
221+
batch_idx (int): The index of the current batch.
222+
prog_bar (bool, optional): Whether to display the progress bar. Defaults to False.
223+
224+
Returns:
225+
torch.Tensor: A tensor containing the prediction for this batch.
226+
"""
227+
x, y = batch
228+
yhat = self(x)
229+
y = y.view(len(y), 1)
230+
yhat = yhat.view(len(yhat), 1)
231+
print(f"Predict step x: {x}")
232+
print(f"Predict step y: {y}")
233+
print(f"Predict step y_hat: {yhat}")
234+
# pred_loss = F.mse_loss(y_hat, y)
235+
# pred loss not registered
236+
# self.log("pred_loss", pred_loss, prog_bar=prog_bar)
237+
# self.log("hp_metric", pred_loss, prog_bar=prog_bar)
238+
# MisconfigurationException: You are trying to `self.log()`
239+
# but the loop's result collection is not registered yet.
240+
# This is most likely because you are trying to log in a `predict` hook, but it doesn't support logging.
241+
# If you want to manually log, please consider using `self.log_dict({'pred_loss': pred_loss})` instead.
242+
return (x, y, yhat)
243+
244+
def configure_optimizers(self) -> torch.optim.Optimizer:
245+
"""
246+
Configures the optimizer for the model.
247+
248+
Notes:
249+
The default Lightning way is to define an optimizer as
250+
`optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)`.
251+
spotpython uses an optimizer handler to create the optimizer, which
252+
adapts the learning rate according to the lr_mult hyperparameter as
253+
well as other hyperparameters. See `spotpython.hyperparameters.optimizer.py` for details.
254+
255+
Returns:
256+
torch.optim.Optimizer: The optimizer to use during training.
257+
258+
"""
259+
# optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)
260+
optimizer = optimizer_handler(optimizer_name=self.hparams.optimizer, params=self.parameters(), lr_mult=self.hparams.lr_mult)
261+
return optimizer

0 commit comments

Comments
 (0)