Skip to content

Commit 6809987

Browse files
v0.6.10
docs
1 parent f9a246f commit 6809987

6 files changed

Lines changed: 331 additions & 7 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "spotPython"
10-
version = "0.6.8"
10+
version = "0.6.10"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotPython/light/cifar10datamodule.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ def train_dataloader(self) -> DataLoader:
6767
6868
"""
6969
print("train_dataloader: self.batch_size", self.batch_size)
70-
return DataLoader(self.data_train, batch_size=self.batch_size, shuffle=True, num_workers=self.num_workers)
70+
return DataLoader(self.data_train, batch_size=self.batch_size, shuffle=True, drop_last = True,
71+
num_workers=self.num_workers)
7172

7273
def val_dataloader(self) -> DataLoader:
7374
"""
@@ -79,7 +80,8 @@ def val_dataloader(self) -> DataLoader:
7980
8081
"""
8182
print("val_dataloader: self.batch_size", self.batch_size)
82-
return DataLoader(self.data_val, batch_size=self.batch_size, shuffle=False, num_workers=self.num_workers)
83+
return DataLoader(self.data_val, batch_size=self.batch_size, shuffle=False,
84+
drop_last=False, num_workers=self.num_workers)
8385

8486
def test_dataloader(self) -> DataLoader:
8587
"""
@@ -91,4 +93,5 @@ def test_dataloader(self) -> DataLoader:
9193
9294
"""
9395
print("train_data_loader: self.batch_size", self.batch_size)
94-
return DataLoader(self.data_test, batch_size=self.batch_size, shuffle=False, num_workers=self.num_workers)
96+
return DataLoader(self.data_test, batch_size=self.batch_size, shuffle=False,
97+
drop_last=False, num_workers=self.num_workers)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import os
2+
import urllib.request
3+
from types import SimpleNamespace
4+
from urllib.error import HTTPError
5+
6+
import lightning as L
7+
import matplotlib
8+
import matplotlib.pyplot as plt
9+
import matplotlib_inline.backend_inline
10+
import numpy as np
11+
import seaborn as sns
12+
import tabulate
13+
import torch
14+
import torch.nn as nn
15+
import torch.optim as optim
16+
import torch.utils.data as data
17+
import torchvision
18+
19+
%matplotlib inline
20+
from IPython.display import HTML, display
21+
from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint
22+
from PIL import Image
23+
from torchvision import transforms
24+
from torchvision.datasets import CIFAR10
25+
26+
matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export
27+
matplotlib.rcParams["lines.linewidth"] = 2.0
28+
sns.reset_orig()
29+
30+
31+
class GoogleNet(nn.Module):
32+
def __init__(self, num_classes=10, act_fn_name="relu", **kwargs):
33+
super().__init__()
34+
self.hparams = SimpleNamespace(
35+
num_classes=num_classes, act_fn_name=act_fn_name, act_fn=act_fn_by_name[act_fn_name]
36+
)
37+
self._create_network()
38+
self._init_params()
39+
40+
def _create_network(self):
41+
# A first convolution on the original image to scale up the channel size
42+
self.input_net = nn.Sequential(
43+
nn.Conv2d(3, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), self.hparams.act_fn()
44+
)
45+
# Stacking inception blocks
46+
self.inception_blocks = nn.Sequential(
47+
InceptionBlock(
48+
64,
49+
c_red={"3x3": 32, "5x5": 16},
50+
c_out={"1x1": 16, "3x3": 32, "5x5": 8, "max": 8},
51+
act_fn=self.hparams.act_fn,
52+
),
53+
InceptionBlock(
54+
64,
55+
c_red={"3x3": 32, "5x5": 16},
56+
c_out={"1x1": 24, "3x3": 48, "5x5": 12, "max": 12},
57+
act_fn=self.hparams.act_fn,
58+
),
59+
nn.MaxPool2d(3, stride=2, padding=1), # 32x32 => 16x16
60+
InceptionBlock(
61+
96,
62+
c_red={"3x3": 32, "5x5": 16},
63+
c_out={"1x1": 24, "3x3": 48, "5x5": 12, "max": 12},
64+
act_fn=self.hparams.act_fn,
65+
),
66+
InceptionBlock(
67+
96,
68+
c_red={"3x3": 32, "5x5": 16},
69+
c_out={"1x1": 16, "3x3": 48, "5x5": 16, "max": 16},
70+
act_fn=self.hparams.act_fn,
71+
),
72+
InceptionBlock(
73+
96,
74+
c_red={"3x3": 32, "5x5": 16},
75+
c_out={"1x1": 16, "3x3": 48, "5x5": 16, "max": 16},
76+
act_fn=self.hparams.act_fn,
77+
),
78+
InceptionBlock(
79+
96,
80+
c_red={"3x3": 32, "5x5": 16},
81+
c_out={"1x1": 32, "3x3": 48, "5x5": 24, "max": 24},
82+
act_fn=self.hparams.act_fn,
83+
),
84+
nn.MaxPool2d(3, stride=2, padding=1), # 16x16 => 8x8
85+
InceptionBlock(
86+
128,
87+
c_red={"3x3": 48, "5x5": 16},
88+
c_out={"1x1": 32, "3x3": 64, "5x5": 16, "max": 16},
89+
act_fn=self.hparams.act_fn,
90+
),
91+
InceptionBlock(
92+
128,
93+
c_red={"3x3": 48, "5x5": 16},
94+
c_out={"1x1": 32, "3x3": 64, "5x5": 16, "max": 16},
95+
act_fn=self.hparams.act_fn,
96+
),
97+
)
98+
# Mapping to classification output
99+
self.output_net = nn.Sequential(
100+
nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(128, self.hparams.num_classes)
101+
)
102+
103+
def _init_params(self):
104+
# Based on our discussion in Tutorial 4, we should initialize the
105+
# convolutions according to the activation function
106+
for m in self.modules():
107+
if isinstance(m, nn.Conv2d):
108+
nn.init.kaiming_normal_(m.weight, nonlinearity=self.hparams.act_fn_name)
109+
elif isinstance(m, nn.BatchNorm2d):
110+
nn.init.constant_(m.weight, 1)
111+
nn.init.constant_(m.bias, 0)
112+
113+
def forward(self, x):
114+
x = self.input_net(x)
115+
x = self.inception_blocks(x)
116+
x = self.output_net(x)
117+
return x
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os
2+
import urllib.request
3+
from types import SimpleNamespace
4+
from urllib.error import HTTPError
5+
6+
import lightning as L
7+
import matplotlib
8+
import matplotlib.pyplot as plt
9+
import matplotlib_inline.backend_inline
10+
import numpy as np
11+
import seaborn as sns
12+
import tabulate
13+
import torch
14+
import torch.nn as nn
15+
import torch.optim as optim
16+
import torch.utils.data as data
17+
import torchvision
18+
19+
from IPython.display import HTML, display
20+
from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint
21+
from PIL import Image
22+
from torchvision import transforms
23+
from torchvision.datasets import CIFAR10
24+
25+
matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export
26+
matplotlib.rcParams["lines.linewidth"] = 2.0
27+
sns.reset_orig()
28+
29+
# PyTorch
30+
# Torchvision
31+
32+
class InceptionBlock(nn.Module):
33+
def __init__(self, c_in, c_red: dict, c_out: dict, act_fn):
34+
"""
35+
Inputs:
36+
c_in - Number of input feature maps from the previous layers
37+
c_red - Dictionary with keys "3x3" and "5x5" specifying the output of the dimensionality reducing 1x1 convolutions
38+
c_out - Dictionary with keys "1x1", "3x3", "5x5", and "max"
39+
act_fn - Activation class constructor (e.g. nn.ReLU)
40+
"""
41+
super().__init__()
42+
43+
# 1x1 convolution branch
44+
self.conv_1x1 = nn.Sequential(
45+
nn.Conv2d(c_in, c_out["1x1"], kernel_size=1), nn.BatchNorm2d(c_out["1x1"]), act_fn()
46+
)
47+
48+
# 3x3 convolution branch
49+
self.conv_3x3 = nn.Sequential(
50+
nn.Conv2d(c_in, c_red["3x3"], kernel_size=1),
51+
nn.BatchNorm2d(c_red["3x3"]),
52+
act_fn(),
53+
nn.Conv2d(c_red["3x3"], c_out["3x3"], kernel_size=3, padding=1),
54+
nn.BatchNorm2d(c_out["3x3"]),
55+
act_fn(),
56+
)
57+
58+
# 5x5 convolution branch
59+
self.conv_5x5 = nn.Sequential(
60+
nn.Conv2d(c_in, c_red["5x5"], kernel_size=1),
61+
nn.BatchNorm2d(c_red["5x5"]),
62+
act_fn(),
63+
nn.Conv2d(c_red["5x5"], c_out["5x5"], kernel_size=5, padding=2),
64+
nn.BatchNorm2d(c_out["5x5"]),
65+
act_fn(),
66+
)
67+
68+
# Max-pool branch
69+
self.max_pool = nn.Sequential(
70+
nn.MaxPool2d(kernel_size=3, padding=1, stride=1),
71+
nn.Conv2d(c_in, c_out["max"], kernel_size=1),
72+
nn.BatchNorm2d(c_out["max"]),
73+
act_fn(),
74+
)
75+
76+
def forward(self, x):
77+
x_1x1 = self.conv_1x1(x)
78+
x_3x3 = self.conv_3x3(x)
79+
x_5x5 = self.conv_5x5(x)
80+
x_max = self.max_pool(x)
81+
x_out = torch.cat([x_1x1, x_3x3, x_5x5, x_max], dim=1)
82+
return x_out
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import lightning as L
2+
import torch
3+
from torch import nn
4+
5+
# import torchmetrics
6+
import torch.nn.functional as F
7+
from torchmetrics.functional import accuracy
8+
from spotPython.hyperparameters.optimizer import optimizer_handler
9+
10+
11+
import os
12+
import urllib.request
13+
from types import SimpleNamespace
14+
from urllib.error import HTTPError
15+
16+
import lightning as L
17+
import matplotlib
18+
import matplotlib.pyplot as plt
19+
import matplotlib_inline.backend_inline
20+
import numpy as np
21+
import seaborn as sns
22+
import tabulate
23+
import torch
24+
import torch.nn as nn
25+
import torch.optim as optim
26+
import torch.utils.data as data
27+
import torchvision
28+
29+
%matplotlib inline
30+
from IPython.display import HTML, display
31+
from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint
32+
from PIL import Image
33+
from torchvision import transforms
34+
from torchvision.datasets import CIFAR10
35+
36+
matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export
37+
matplotlib.rcParams["lines.linewidth"] = 2.0
38+
sns.reset_orig()
39+
40+
# PyTorch
41+
# Torchvision
42+
43+
44+
class NetCNNBase(L.LightningModule):
45+
def __init__(self, model_name, model_hparams, optimizer_name, optimizer_hparams):
46+
"""
47+
Inputs:
48+
model_name - Name of the model/CNN to run. Used for creating the model (see function below)
49+
model_hparams - Hyperparameters for the model, as dictionary.
50+
optimizer_name - Name of the optimizer to use. Currently supported: Adam, SGD
51+
optimizer_hparams - Hyperparameters for the optimizer, as dictionary. This includes learning rate, weight decay, etc.
52+
"""
53+
super().__init__()
54+
# Exports the hyperparameters to a YAML file, and create "self.hparams" namespace
55+
self.save_hyperparameters()
56+
# Create model
57+
self.model = create_model(model_name, model_hparams)
58+
# Create loss module
59+
self.loss_module = nn.CrossEntropyLoss()
60+
# Example input for visualizing the graph in Tensorboard
61+
self.example_input_array = torch.zeros((1, 3, 32, 32), dtype=torch.float32)
62+
63+
def forward(self, imgs):
64+
# Forward function that is run when visualizing the graph
65+
return self.model(imgs)
66+
67+
def configure_optimizers(self):
68+
# We will support Adam or SGD as optimizers.
69+
if self.hparams.optimizer_name == "Adam":
70+
# AdamW is Adam with a correct implementation of weight decay (see here
71+
# for details: https://arxiv.org/pdf/1711.05101.pdf)
72+
optimizer = optim.AdamW(self.parameters(), **self.hparams.optimizer_hparams)
73+
elif self.hparams.optimizer_name == "SGD":
74+
optimizer = optim.SGD(self.parameters(), **self.hparams.optimizer_hparams)
75+
else:
76+
assert False, f'Unknown optimizer: "{self.hparams.optimizer_name}"'
77+
78+
# We will reduce the learning rate by 0.1 after 100 and 150 epochs
79+
scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[100, 150], gamma=0.1)
80+
return [optimizer], [scheduler]
81+
82+
def training_step(self, batch, batch_idx):
83+
# "batch" is the output of the training data loader.
84+
imgs, labels = batch
85+
preds = self.model(imgs)
86+
loss = self.loss_module(preds, labels)
87+
acc = (preds.argmax(dim=-1) == labels).float().mean()
88+
89+
# Logs the accuracy per epoch to tensorboard (weighted average over batches)
90+
self.log("train_acc", acc, on_step=False, on_epoch=True)
91+
self.log("train_loss", loss)
92+
return loss # Return tensor to call ".backward" on
93+
94+
def validation_step(self, batch, batch_idx):
95+
imgs, labels = batch
96+
preds = self.model(imgs).argmax(dim=-1)
97+
acc = (labels == preds).float().mean()
98+
# By default logs it per epoch (weighted average over batches)
99+
self.log("val_acc", acc)
100+
101+
def test_step(self, batch, batch_idx):
102+
imgs, labels = batch
103+
preds = self.model(imgs).argmax(dim=-1)
104+
acc = (labels == preds).float().mean()
105+
# By default logs it per epoch (weighted average over batches), and returns it afterwards
106+
self.log("test_acc", acc)

src/spotPython/torch/mapk.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,26 @@ def update(self, predicted: torch.Tensor, actual: torch.Tensor):
5757
Returns:
5858
(NoneType): None
5959
60+
Examples:
61+
>>> from spotPython.torch.mapk import MAPK
62+
>>> import torch
63+
>>> mapk = MAPK(k=2)
64+
>>> target = torch.tensor([0, 1, 2, 2])
65+
>>> preds = torch.tensor(
66+
... [
67+
... [0.5, 0.2, 0.2], # 0 is in top 2
68+
... [0.3, 0.4, 0.2], # 1 is in top 2
69+
... [0.2, 0.4, 0.3], # 2 is in top 2
70+
... [0.7, 0.2, 0.1], # 2 isn't in top 2
71+
... ]
72+
... )
73+
>>> mapk.update(preds, target)
74+
>>> print(mapk.compute()) # tensor(0.6250)
75+
6076
Raises:
61-
AssertionError:
62-
If `actual` is not a 1D tensor or if `predicted` is not a 2D tensor
63-
or if `actual` and `predicted` do not have the same number of elements.
77+
AssertionError: If the actual tensor is not 1D or the predicted tensor is not 2D.
78+
AssertionError: If the number of elements in the actual and predicted tensors are not equal.
79+
6480
"""
6581
assert len(actual.shape) == 1, "actual must be a 1D tensor"
6682
assert len(predicted.shape) == 2, "predicted must be a 2D tensor"

0 commit comments

Comments
 (0)