homa 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {homa-0.2.0 → homa-0.2.1}/PKG-INFO +1 -1
- {homa-0.2.0 → homa-0.2.1}/pyproject.toml +1 -1
- homa-0.2.1/src/homa/activations/classes/APLU.py +53 -0
- homa-0.2.1/src/homa/activations/classes/GALU.py +61 -0
- homa-0.2.1/src/homa/activations/classes/MELU.py +63 -0
- homa-0.2.1/src/homa/activations/classes/PDELU.py +44 -0
- homa-0.2.1/src/homa/activations/classes/SReLU.py +50 -0
- homa-0.2.1/src/homa/activations/classes/SmallGALU.py +48 -0
- homa-0.2.1/src/homa/activations/classes/WideMELU.py +68 -0
- homa-0.2.1/src/homa/activations/utils.py +94 -0
- homa-0.2.1/src/homa/vision/StochasticClassifier.py +50 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/PKG-INFO +1 -1
- homa-0.2.0/src/homa/activations/classes/APLU.py +0 -86
- homa-0.2.0/src/homa/activations/classes/GALU.py +0 -67
- homa-0.2.0/src/homa/activations/classes/MELU.py +0 -70
- homa-0.2.0/src/homa/activations/classes/PDELU.py +0 -54
- homa-0.2.0/src/homa/activations/classes/SReLU.py +0 -69
- homa-0.2.0/src/homa/activations/classes/SmallGALU.py +0 -58
- homa-0.2.0/src/homa/activations/classes/WideMELU.py +0 -90
- homa-0.2.0/src/homa/activations/utils.py +0 -27
- homa-0.2.0/src/homa/vision/StochasticClassifier.py +0 -28
- {homa-0.2.0 → homa-0.2.1}/README.md +0 -0
- {homa-0.2.0 → homa-0.2.1}/setup.cfg +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/activations/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/activations/classes/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/cli/HomaCommand.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/cli/namespaces/CacheNamespace.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/cli/namespaces/MakeNamespace.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/cli/namespaces/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/device.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/Ensemble.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/CalculatesMetricNecessities.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/PredictsProbabilities.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsClassificationMetrics.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsEnsembleAccuracy.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsEnsembleF1.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsEnsembleKappa.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsLogits.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/ReportsSize.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/StoresModels.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/ensemble/concerns/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/loss/LogitNormLoss.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/loss/Loss.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/loss/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/settings.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/torch/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/torch/helpers.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/utils.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/ClassificationModel.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/Model.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/Resnet.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/StochasticResnet.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/StochasticSwin.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/Swin.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/HasLabels.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/HasLogits.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/HasProbabilities.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/ReportsAccuracy.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/ReportsMetrics.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/Trainable.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/concerns/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/modules/ResnetModule.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/modules/SwinModule.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa/vision/modules/__init__.py +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/SOURCES.txt +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/dependency_links.txt +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/entry_points.txt +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/requires.txt +0 -0
- {homa-0.2.0 → homa-0.2.1}/src/homa.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "homa"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "A curated list of machine learning and deep learning helpers."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Taha Shieenavaz", email="tahashieenavaz@gmail.com" },
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class APLU(nn.Module):
|
|
6
|
+
def __init__(self, channels: int, max_input: float = 1.0):
|
|
7
|
+
super().__init__()
|
|
8
|
+
if channels <= 0:
|
|
9
|
+
raise ValueError(f"Number of channels must be positive, got {channels}.")
|
|
10
|
+
self.channels = int(channels)
|
|
11
|
+
self.max_input = float(max_input)
|
|
12
|
+
self.alpha = nn.Parameter(torch.empty(self.channels))
|
|
13
|
+
self.beta = nn.Parameter(torch.empty(self.channels))
|
|
14
|
+
self.gamma = nn.Parameter(torch.empty(self.channels))
|
|
15
|
+
self.xi = nn.Parameter(torch.empty(self.channels))
|
|
16
|
+
self.psi = nn.Parameter(torch.empty(self.channels))
|
|
17
|
+
self.mu = nn.Parameter(torch.empty(self.channels))
|
|
18
|
+
self.reset_parameters()
|
|
19
|
+
|
|
20
|
+
def reset_parameters(self) -> None:
|
|
21
|
+
with torch.no_grad():
|
|
22
|
+
self.alpha.zero_()
|
|
23
|
+
self.beta.zero_()
|
|
24
|
+
self.gamma.zero_()
|
|
25
|
+
self.xi.uniform_(0.0, self.max_input)
|
|
26
|
+
self.psi.uniform_(0.0, self.max_input)
|
|
27
|
+
self.mu.uniform_(0.0, self.max_input)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _reshape_for_broadcast(param: torch.Tensor, ref: torch.Tensor) -> torch.Tensor:
|
|
31
|
+
return param.view(1, param.shape[0], *([1] * (ref.ndim - 2)))
|
|
32
|
+
|
|
33
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
34
|
+
if x.ndim < 2:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"APLU expects inputs with at least two dimensions (N, C, ...), got {tuple(x.shape)}."
|
|
37
|
+
)
|
|
38
|
+
if int(x.shape[1]) != self.channels:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"APLU was initialized with {self.channels} channels but received input with {int(x.shape[1])}."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
alpha = self._reshape_for_broadcast(self.alpha, x)
|
|
44
|
+
beta = self._reshape_for_broadcast(self.beta, x)
|
|
45
|
+
gamma = self._reshape_for_broadcast(self.gamma, x)
|
|
46
|
+
xi = self._reshape_for_broadcast(self.xi, x)
|
|
47
|
+
psi = self._reshape_for_broadcast(self.psi, x)
|
|
48
|
+
mu = self._reshape_for_broadcast(self.mu, x)
|
|
49
|
+
output = torch.relu(x)
|
|
50
|
+
output = output + alpha * torch.relu(-x + xi)
|
|
51
|
+
output = output + beta * torch.relu(-x + psi)
|
|
52
|
+
output = output + gamma * torch.relu(-x + mu)
|
|
53
|
+
return output
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
from torch.nn.parameter import Parameter
|
|
4
|
+
import torch.nn.functional as F
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GALU(nn.Module):
|
|
8
|
+
def __init__(self, channels: int, max_input: float = 1.0):
|
|
9
|
+
super().__init__()
|
|
10
|
+
if max_input <= 0:
|
|
11
|
+
raise ValueError("max_input must be positive.")
|
|
12
|
+
if channels <= 0:
|
|
13
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
14
|
+
self.channels = int(channels)
|
|
15
|
+
self.max_input = float(max_input)
|
|
16
|
+
self.alpha = Parameter(torch.empty(self.channels))
|
|
17
|
+
self.beta = Parameter(torch.empty(self.channels))
|
|
18
|
+
self.gamma = Parameter(torch.empty(self.channels))
|
|
19
|
+
self.delta = Parameter(torch.empty(self.channels))
|
|
20
|
+
self.reset_parameters()
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def _reshape(param: torch.Tensor, ref: torch.Tensor) -> torch.Tensor:
|
|
24
|
+
return param.view(1, param.shape[0], *([1] * (ref.ndim - 2)))
|
|
25
|
+
|
|
26
|
+
def reset_parameters(self):
|
|
27
|
+
with torch.no_grad():
|
|
28
|
+
for param in (self.alpha, self.beta, self.gamma, self.delta):
|
|
29
|
+
param.zero_()
|
|
30
|
+
|
|
31
|
+
def forward(self, x: torch.Tensor):
|
|
32
|
+
if x.ndim < 2:
|
|
33
|
+
raise ValueError(
|
|
34
|
+
f"Input tensor must have at least 2 dimensions (N, C), but got shape {tuple(x.shape)}"
|
|
35
|
+
)
|
|
36
|
+
if int(x.shape[1]) != self.channels:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"GALU was initialized with C={self.channels} but received input with C={int(x.shape[1])}."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
x_norm = x / self.max_input
|
|
42
|
+
zero = torch.zeros(1, dtype=x.dtype, device=x.device)
|
|
43
|
+
alpha = self._reshape(self.alpha, x_norm)
|
|
44
|
+
beta = self._reshape(self.beta, x_norm)
|
|
45
|
+
gamma = self._reshape(self.gamma, x_norm)
|
|
46
|
+
delta = self._reshape(self.delta, x_norm)
|
|
47
|
+
part_prelu = F.relu(x_norm) + alpha * torch.minimum(x_norm, zero)
|
|
48
|
+
part_beta = beta * (
|
|
49
|
+
F.relu(1.0 - torch.abs(x_norm - 1.0))
|
|
50
|
+
+ torch.minimum(torch.abs(x_norm - 3.0) - 1.0, zero)
|
|
51
|
+
)
|
|
52
|
+
part_gamma = gamma * (
|
|
53
|
+
F.relu(0.5 - torch.abs(x_norm - 0.5))
|
|
54
|
+
+ torch.minimum(torch.abs(x_norm - 1.5) - 0.5, zero)
|
|
55
|
+
)
|
|
56
|
+
part_delta = delta * (
|
|
57
|
+
F.relu(0.5 - torch.abs(x_norm - 2.5))
|
|
58
|
+
+ torch.minimum(torch.abs(x_norm - 3.5) - 0.5, zero)
|
|
59
|
+
)
|
|
60
|
+
z = part_prelu + part_beta + part_gamma + part_delta
|
|
61
|
+
return z * self.max_input
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MELU(nn.Module):
|
|
7
|
+
def __init__(self, channels: int, max_input: float = 1.0):
|
|
8
|
+
super().__init__()
|
|
9
|
+
if channels <= 0:
|
|
10
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
11
|
+
self.channels = int(channels)
|
|
12
|
+
self.max_input = float(max_input)
|
|
13
|
+
shape = (1, self.channels, 1, 1)
|
|
14
|
+
self.alpha = nn.Parameter(torch.empty(shape))
|
|
15
|
+
self.beta = nn.Parameter(torch.empty(shape))
|
|
16
|
+
self.gamma = nn.Parameter(torch.empty(shape))
|
|
17
|
+
self.delta = nn.Parameter(torch.empty(shape))
|
|
18
|
+
self.xi = nn.Parameter(torch.empty(shape))
|
|
19
|
+
self.psi = nn.Parameter(torch.empty(shape))
|
|
20
|
+
self.reset_parameters()
|
|
21
|
+
|
|
22
|
+
def reset_parameters(self):
|
|
23
|
+
with torch.no_grad():
|
|
24
|
+
for param in (
|
|
25
|
+
self.alpha,
|
|
26
|
+
self.beta,
|
|
27
|
+
self.gamma,
|
|
28
|
+
self.delta,
|
|
29
|
+
self.xi,
|
|
30
|
+
self.psi,
|
|
31
|
+
):
|
|
32
|
+
param.zero_()
|
|
33
|
+
|
|
34
|
+
def forward(self, X: torch.Tensor) -> torch.Tensor:
|
|
35
|
+
if X.dim() != 4:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Expected 4D input (N, C, H, W), got {X.dim()}D with shape {tuple(X.shape)}"
|
|
38
|
+
)
|
|
39
|
+
if int(X.shape[1]) != self.channels:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"MELU was initialized with C={self.channels} but received input with C={int(X.shape[1])}."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
X_norm = X / self.max_input
|
|
45
|
+
Y = torch.roll(X_norm, shifts=-1, dims=1)
|
|
46
|
+
|
|
47
|
+
term1 = F.relu(X_norm)
|
|
48
|
+
term2 = self.alpha * torch.clamp(X_norm, max=0)
|
|
49
|
+
|
|
50
|
+
dist_sq_beta = (X_norm - 2) ** 2 + (Y - 2) ** 2
|
|
51
|
+
dist_sq_gamma = (X_norm - 1) ** 2 + (Y - 1) ** 2
|
|
52
|
+
dist_sq_delta = (X_norm - 1) ** 2 + (Y - 3) ** 2
|
|
53
|
+
dist_sq_xi = (X_norm - 3) ** 2 + (Y - 1) ** 2
|
|
54
|
+
dist_sq_psi = (X_norm - 3) ** 2 + (Y - 3) ** 2
|
|
55
|
+
|
|
56
|
+
term3 = self.beta * torch.sqrt(F.relu(2 - dist_sq_beta))
|
|
57
|
+
term4 = self.gamma * torch.sqrt(F.relu(1 - dist_sq_gamma))
|
|
58
|
+
term5 = self.delta * torch.sqrt(F.relu(1 - dist_sq_delta))
|
|
59
|
+
term6 = self.xi * torch.sqrt(F.relu(1 - dist_sq_xi))
|
|
60
|
+
term7 = self.psi * torch.sqrt(F.relu(1 - dist_sq_psi))
|
|
61
|
+
|
|
62
|
+
Z_norm = term1 + term2 + term3 + term4 + term5 + term6 + term7
|
|
63
|
+
return Z_norm * self.max_input
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PDELU(nn.Module):
|
|
7
|
+
def __init__(self, channels: int, theta: float = 0.5):
|
|
8
|
+
super().__init__()
|
|
9
|
+
if theta == 1.0:
|
|
10
|
+
raise ValueError("theta cannot be 1.0, as it would cause a division by zero.")
|
|
11
|
+
if channels <= 0:
|
|
12
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
13
|
+
self.channels = int(channels)
|
|
14
|
+
self.theta = float(theta)
|
|
15
|
+
self._power_val = 1.0 / (1.0 - self.theta)
|
|
16
|
+
self.alpha = nn.Parameter(torch.empty(self.channels))
|
|
17
|
+
self.reset_parameters()
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _reshape(param: torch.Tensor, ref: torch.Tensor) -> torch.Tensor:
|
|
21
|
+
return param.view(1, param.shape[0], *([1] * (ref.ndim - 2)))
|
|
22
|
+
|
|
23
|
+
def reset_parameters(self):
|
|
24
|
+
with torch.no_grad():
|
|
25
|
+
self.alpha.fill_(0.1)
|
|
26
|
+
|
|
27
|
+
def forward(self, x: torch.Tensor):
|
|
28
|
+
if x.ndim < 2:
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"Input tensor must have at least 2 dimensions (N, C), but got shape {tuple(x.shape)}"
|
|
31
|
+
)
|
|
32
|
+
if int(x.shape[1]) != self.channels:
|
|
33
|
+
raise ValueError(
|
|
34
|
+
f"PDELU was initialized with C={self.channels} but received input with C={int(x.shape[1])}."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
positive_part = F.relu(x)
|
|
38
|
+
inner_term = F.relu(1.0 + (1.0 - self.theta) * x)
|
|
39
|
+
powered_term = torch.pow(inner_term, self._power_val)
|
|
40
|
+
subtracted_term = powered_term - 1.0
|
|
41
|
+
zero = torch.zeros(1, dtype=x.dtype, device=x.device)
|
|
42
|
+
alpha = self._reshape(self.alpha, x)
|
|
43
|
+
negative_part = alpha * torch.minimum(subtracted_term, zero)
|
|
44
|
+
return positive_part + negative_part
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SReLU(nn.Module):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
channels: int,
|
|
9
|
+
alpha_init: float = 0.0,
|
|
10
|
+
beta_init: float = 0.0,
|
|
11
|
+
gamma_init: float = 1.0,
|
|
12
|
+
delta_init: float = 1.0,
|
|
13
|
+
):
|
|
14
|
+
super().__init__()
|
|
15
|
+
if channels <= 0:
|
|
16
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
17
|
+
self.channels = int(channels)
|
|
18
|
+
self.alpha_init_val = float(alpha_init)
|
|
19
|
+
self.beta_init_val = float(beta_init)
|
|
20
|
+
self.gamma_init_val = float(gamma_init)
|
|
21
|
+
self.delta_init_val = float(delta_init)
|
|
22
|
+
shape = (1, self.channels, 1, 1)
|
|
23
|
+
self.alpha = nn.Parameter(torch.empty(shape))
|
|
24
|
+
self.beta = nn.Parameter(torch.empty(shape))
|
|
25
|
+
self.gamma = nn.Parameter(torch.empty(shape))
|
|
26
|
+
self.delta = nn.Parameter(torch.empty(shape))
|
|
27
|
+
self.reset_parameters()
|
|
28
|
+
|
|
29
|
+
def reset_parameters(self):
|
|
30
|
+
with torch.no_grad():
|
|
31
|
+
self.alpha.fill_(self.alpha_init_val)
|
|
32
|
+
self.beta.fill_(self.beta_init_val)
|
|
33
|
+
self.gamma.fill_(self.gamma_init_val)
|
|
34
|
+
self.delta.fill_(self.delta_init_val)
|
|
35
|
+
|
|
36
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
37
|
+
if x.dim() != 4:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"Expected 4D input (N, C, H, W), got {x.dim()}D with shape {tuple(x.shape)}"
|
|
40
|
+
)
|
|
41
|
+
if int(x.shape[1]) != self.channels:
|
|
42
|
+
raise ValueError(
|
|
43
|
+
f"SReLU was initialized with C={self.channels} but received input with C={int(x.shape[1])}."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
start = self.beta + self.alpha * (x - self.beta)
|
|
47
|
+
finish = self.delta + self.gamma * (x - self.delta)
|
|
48
|
+
out = torch.where(x < self.beta, start, x)
|
|
49
|
+
out = torch.where(x > self.delta, finish, out)
|
|
50
|
+
return out
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SmallGALU(nn.Module):
|
|
7
|
+
def __init__(self, channels: int, max_input: float = 1.0):
|
|
8
|
+
super().__init__()
|
|
9
|
+
if max_input <= 0:
|
|
10
|
+
raise ValueError("max_input must be positive.")
|
|
11
|
+
if channels <= 0:
|
|
12
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
13
|
+
self.channels = int(channels)
|
|
14
|
+
self.max_input = float(max_input)
|
|
15
|
+
self.alpha = nn.Parameter(torch.empty(self.channels))
|
|
16
|
+
self.beta = nn.Parameter(torch.empty(self.channels))
|
|
17
|
+
self.reset_parameters()
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _reshape(param: torch.Tensor, ref: torch.Tensor) -> torch.Tensor:
|
|
21
|
+
return param.view(1, param.shape[0], *([1] * (ref.ndim - 2)))
|
|
22
|
+
|
|
23
|
+
def reset_parameters(self):
|
|
24
|
+
with torch.no_grad():
|
|
25
|
+
self.alpha.zero_()
|
|
26
|
+
self.beta.zero_()
|
|
27
|
+
|
|
28
|
+
def forward(self, x: torch.Tensor):
|
|
29
|
+
if x.ndim < 2:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"Input tensor must have at least 2 dimensions (N, C), but got shape {tuple(x.shape)}"
|
|
32
|
+
)
|
|
33
|
+
if int(x.shape[1]) != self.channels:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"SmallGALU was initialized with C={self.channels} but received input with C={int(x.shape[1])}."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
x_norm = x / self.max_input
|
|
39
|
+
zero = torch.zeros(1, dtype=x.dtype, device=x.device)
|
|
40
|
+
alpha = self._reshape(self.alpha, x_norm)
|
|
41
|
+
beta = self._reshape(self.beta, x_norm)
|
|
42
|
+
part_prelu = F.relu(x_norm) + alpha * torch.minimum(x_norm, zero)
|
|
43
|
+
part_beta = beta * (
|
|
44
|
+
F.relu(1.0 - torch.abs(x_norm - 1.0))
|
|
45
|
+
+ torch.minimum(torch.abs(x_norm - 3.0) - 1.0, zero)
|
|
46
|
+
)
|
|
47
|
+
z = part_prelu + part_beta
|
|
48
|
+
return z * self.max_input
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from torch import nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WideMELU(nn.Module):
|
|
7
|
+
def __init__(self, channels: int, max_input: float = 1.0):
|
|
8
|
+
super().__init__()
|
|
9
|
+
if channels <= 0:
|
|
10
|
+
raise ValueError(f"channels must be positive, got {channels}.")
|
|
11
|
+
self.channels = int(channels)
|
|
12
|
+
self.max_input = float(max_input)
|
|
13
|
+
shape = (1, self.channels, 1, 1)
|
|
14
|
+
self.alpha = nn.Parameter(torch.empty(shape))
|
|
15
|
+
self.beta = nn.Parameter(torch.empty(shape))
|
|
16
|
+
self.gamma = nn.Parameter(torch.empty(shape))
|
|
17
|
+
self.delta = nn.Parameter(torch.empty(shape))
|
|
18
|
+
self.xi = nn.Parameter(torch.empty(shape))
|
|
19
|
+
self.psi = nn.Parameter(torch.empty(shape))
|
|
20
|
+
self.theta = nn.Parameter(torch.empty(shape))
|
|
21
|
+
self.lam = nn.Parameter(torch.empty(shape))
|
|
22
|
+
self.reset_parameters()
|
|
23
|
+
|
|
24
|
+
def reset_parameters(self):
|
|
25
|
+
with torch.no_grad():
|
|
26
|
+
for param in (
|
|
27
|
+
self.alpha,
|
|
28
|
+
self.beta,
|
|
29
|
+
self.gamma,
|
|
30
|
+
self.delta,
|
|
31
|
+
self.xi,
|
|
32
|
+
self.psi,
|
|
33
|
+
self.theta,
|
|
34
|
+
self.lam,
|
|
35
|
+
):
|
|
36
|
+
param.zero_()
|
|
37
|
+
|
|
38
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
39
|
+
if x.dim() != 4:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"Expected 4D input (N, C, H, W), got {x.dim()}D with shape {tuple(x.shape)}"
|
|
42
|
+
)
|
|
43
|
+
if int(x.shape[1]) != self.channels:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"WideMELU was initialized with C={self.channels} but received input with C={int(x.shape[1])}."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
X_norm = x / self.max_input
|
|
49
|
+
Y = torch.roll(X_norm, shifts=-1, dims=1)
|
|
50
|
+
|
|
51
|
+
term1 = F.relu(X_norm)
|
|
52
|
+
term2 = self.alpha * torch.clamp(X_norm, max=0)
|
|
53
|
+
dist_sq_beta = (X_norm - 2) ** 2 + (Y - 2) ** 2
|
|
54
|
+
dist_sq_gamma = (X_norm - 1) ** 2 + (Y - 1) ** 2
|
|
55
|
+
dist_sq_delta = (X_norm - 1) ** 2 + (Y - 3) ** 2
|
|
56
|
+
dist_sq_xi = (X_norm - 3) ** 2 + (Y - 1) ** 2
|
|
57
|
+
dist_sq_psi = (X_norm - 3) ** 2 + (Y - 3) ** 2
|
|
58
|
+
dist_sq_theta = (X_norm - 1) ** 2 + (Y - 2) ** 2
|
|
59
|
+
dist_sq_lambda = (X_norm - 3) ** 2 + (Y - 2) ** 2
|
|
60
|
+
term3 = self.beta * torch.sqrt(F.relu(2 - dist_sq_beta))
|
|
61
|
+
term4 = self.gamma * torch.sqrt(F.relu(1 - dist_sq_gamma))
|
|
62
|
+
term5 = self.delta * torch.sqrt(F.relu(1 - dist_sq_delta))
|
|
63
|
+
term6 = self.xi * torch.sqrt(F.relu(1 - dist_sq_xi))
|
|
64
|
+
term7 = self.psi * torch.sqrt(F.relu(1 - dist_sq_psi))
|
|
65
|
+
term8 = self.theta * torch.sqrt(F.relu(1 - dist_sq_theta))
|
|
66
|
+
term9 = self.lam * torch.sqrt(F.relu(1 - dist_sq_lambda))
|
|
67
|
+
Z_norm = term1 + term2 + term3 + term4 + term5 + term6 + term7 + term8 + term9
|
|
68
|
+
return Z_norm * self.max_input
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Optional, Sequence, Tuple, Type
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from torch import nn
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def negative_part(x):
|
|
8
|
+
return torch.minimum(x, torch.zeros_like(x))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def positive_part(x):
|
|
12
|
+
return torch.maximum(x, torch.zeros_like(x))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def as_channel_parameters(parameter: torch.Tensor, x: torch.Tensor):
|
|
16
|
+
shape = [1] * x.dim()
|
|
17
|
+
shape[1] = -1
|
|
18
|
+
return parameter.view(*shape)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def device_compatibility_check(model, x: torch.Tensor):
|
|
22
|
+
for p in model.parameters():
|
|
23
|
+
if p.device != x.device or p.dtype != x.dtype:
|
|
24
|
+
p.data = p.data.to(device=x.device, dtype=x.dtype)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def phi_hat(x, a, lam):
|
|
28
|
+
term_pos = torch.maximum(lam - torch.abs(x - a), torch.zeros_like(x))
|
|
29
|
+
term_neg = torch.minimum(torch.abs(x - (a + 2 * lam)) - lam, torch.zeros_like(x))
|
|
30
|
+
return term_pos + term_neg
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _channels_from_module(module: nn.Module) -> Optional[int]:
|
|
34
|
+
for attr in ("channels", "out_channels", "out_features", "num_features"):
|
|
35
|
+
value = getattr(module, attr, None)
|
|
36
|
+
if isinstance(value, int) and value > 0:
|
|
37
|
+
return int(value)
|
|
38
|
+
weight = getattr(module, "weight", None)
|
|
39
|
+
if isinstance(weight, torch.Tensor) and weight.ndim >= 1:
|
|
40
|
+
return int(weight.shape[0])
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _infer_channels(parent: nn.Module, target_name: str, child: nn.Module) -> int:
|
|
45
|
+
value = _channels_from_module(child)
|
|
46
|
+
if value is not None:
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
children: Sequence[Tuple[str, nn.Module]] = list(parent.named_children())
|
|
50
|
+
for index, (name, _) in enumerate(children):
|
|
51
|
+
if name == target_name:
|
|
52
|
+
break
|
|
53
|
+
else:
|
|
54
|
+
raise ValueError(f"Child {target_name} not found in parent {parent.__class__.__name__}.")
|
|
55
|
+
|
|
56
|
+
for _, module in reversed(children[:index]):
|
|
57
|
+
value = _channels_from_module(module)
|
|
58
|
+
if value is not None:
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
value = _channels_from_module(parent)
|
|
62
|
+
if value is not None:
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
raise ValueError(
|
|
66
|
+
f"Could not infer channel count for activation {target_name!r} under parent {parent.__class__.__name__}."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def infer_activation_channels(parent: nn.Module, child_name: str, child: nn.Module) -> int:
|
|
71
|
+
return _infer_channels(parent, child_name, child)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def replace_activation(
|
|
75
|
+
model: nn.Module,
|
|
76
|
+
activation: Type[nn.Module],
|
|
77
|
+
needles: Optional[Sequence[Type[nn.Module]]] = None,
|
|
78
|
+
**activation_kwargs,
|
|
79
|
+
) -> None:
|
|
80
|
+
if needles is None:
|
|
81
|
+
needles = (nn.ReLU,)
|
|
82
|
+
needle_types = tuple(needles)
|
|
83
|
+
|
|
84
|
+
for parent in model.modules():
|
|
85
|
+
for name, child in list(parent.named_children()):
|
|
86
|
+
if isinstance(child, needle_types):
|
|
87
|
+
channels = _infer_channels(parent, name, child)
|
|
88
|
+
try:
|
|
89
|
+
new_module = activation(channels=channels, **activation_kwargs)
|
|
90
|
+
except TypeError as exc: # pragma: no cover - defensive branch
|
|
91
|
+
raise TypeError(
|
|
92
|
+
f"{activation.__name__} must accept a `channels` keyword argument."
|
|
93
|
+
) from exc
|
|
94
|
+
setattr(parent, name, new_module)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import torch
|
|
3
|
+
from ..activations import (
|
|
4
|
+
APLU,
|
|
5
|
+
GALU,
|
|
6
|
+
SmallGALU,
|
|
7
|
+
MELU,
|
|
8
|
+
WideMELU,
|
|
9
|
+
PDELU,
|
|
10
|
+
SReLU,
|
|
11
|
+
infer_activation_channels,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StochasticClassifier:
|
|
16
|
+
def __init__(self, *args, **kwargs):
|
|
17
|
+
super().__init__(*args, **kwargs)
|
|
18
|
+
self._activation_pool = [
|
|
19
|
+
APLU,
|
|
20
|
+
GALU,
|
|
21
|
+
SmallGALU,
|
|
22
|
+
MELU,
|
|
23
|
+
WideMELU,
|
|
24
|
+
PDELU,
|
|
25
|
+
SReLU,
|
|
26
|
+
torch.nn.ReLU,
|
|
27
|
+
torch.nn.PReLU,
|
|
28
|
+
torch.nn.LeakyReLU,
|
|
29
|
+
torch.nn.ELU,
|
|
30
|
+
]
|
|
31
|
+
self._requires_channels = {
|
|
32
|
+
APLU,
|
|
33
|
+
GALU,
|
|
34
|
+
SmallGALU,
|
|
35
|
+
MELU,
|
|
36
|
+
WideMELU,
|
|
37
|
+
PDELU,
|
|
38
|
+
SReLU,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def replace_activations(self, needle: torch.nn.Module) -> None:
|
|
42
|
+
for parent in self.network.modules():
|
|
43
|
+
for name, child in list(parent.named_children()):
|
|
44
|
+
if isinstance(child, needle):
|
|
45
|
+
replacement = random.choice(self._activation_pool)
|
|
46
|
+
if replacement in self._requires_channels:
|
|
47
|
+
channels = infer_activation_channels(parent, name, child)
|
|
48
|
+
setattr(parent, name, replacement(channels=channels))
|
|
49
|
+
else:
|
|
50
|
+
setattr(parent, name, replacement())
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import torch
|
|
2
|
-
from torch import nn
|
|
3
|
-
from torch.nn.parameter import Parameter, UninitializedParameter
|
|
4
|
-
import torch.nn.functional as F
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class APLU(nn.Module):
|
|
8
|
-
def __init__(self, max_input: float = 1.0):
|
|
9
|
-
super().__init__()
|
|
10
|
-
self.max_input = float(max_input)
|
|
11
|
-
self.alpha = UninitializedParameter()
|
|
12
|
-
self.beta = UninitializedParameter()
|
|
13
|
-
self.gamma = UninitializedParameter()
|
|
14
|
-
self.xi = UninitializedParameter()
|
|
15
|
-
self.psi = UninitializedParameter()
|
|
16
|
-
self.mu = UninitializedParameter()
|
|
17
|
-
self._num_channels = None
|
|
18
|
-
|
|
19
|
-
def _initialize_parameters(self, x: torch.Tensor):
|
|
20
|
-
if x.ndim < 2:
|
|
21
|
-
raise ValueError(
|
|
22
|
-
f"Input tensor must have at least 2 dimensions (N, C), but got shape {tuple(x.shape)}"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
channels = int(x.shape[1])
|
|
26
|
-
self._num_channels = channels
|
|
27
|
-
param_shape = [1] * x.ndim
|
|
28
|
-
param_shape[1] = channels
|
|
29
|
-
|
|
30
|
-
with torch.no_grad():
|
|
31
|
-
self.alpha = Parameter(
|
|
32
|
-
torch.zeros(param_shape, dtype=x.dtype, device=x.device)
|
|
33
|
-
)
|
|
34
|
-
self.beta = Parameter(
|
|
35
|
-
torch.zeros(param_shape, dtype=x.dtype, device=x.device)
|
|
36
|
-
)
|
|
37
|
-
self.gamma = Parameter(
|
|
38
|
-
torch.zeros(param_shape, dtype=x.dtype, device=x.device)
|
|
39
|
-
)
|
|
40
|
-
self.xi = Parameter(
|
|
41
|
-
torch.empty(param_shape, dtype=x.dtype, device=x.device).uniform_(
|
|
42
|
-
0.0, self.max_input
|
|
43
|
-
)
|
|
44
|
-
)
|
|
45
|
-
self.psi = Parameter(
|
|
46
|
-
torch.empty(param_shape, dtype=x.dtype, device=x.device).uniform_(
|
|
47
|
-
0.0, self.max_input
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
self.mu = Parameter(
|
|
51
|
-
torch.empty(param_shape, dtype=x.dtype, device=x.device).uniform_(
|
|
52
|
-
0.0, self.max_input
|
|
53
|
-
)
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
def reset_parameters(self):
|
|
57
|
-
if isinstance(self.alpha, UninitializedParameter):
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
with torch.no_grad():
|
|
61
|
-
self.alpha.zero_()
|
|
62
|
-
self.beta.zero_()
|
|
63
|
-
self.gamma.zero_()
|
|
64
|
-
self.xi.uniform_(0.0, self.max_input)
|
|
65
|
-
self.psi.uniform_(0.0, self.max_input)
|
|
66
|
-
self.mu.uniform_(0.0, self.max_input)
|
|
67
|
-
|
|
68
|
-
def forward(self, x: torch.Tensor):
|
|
69
|
-
if isinstance(self.alpha, UninitializedParameter):
|
|
70
|
-
self._initialize_parameters(x)
|
|
71
|
-
|
|
72
|
-
if x.ndim < 2:
|
|
73
|
-
raise ValueError(
|
|
74
|
-
f"Input tensor must have at least 2 dimensions (N, C), but got shape {tuple(x.shape)}"
|
|
75
|
-
)
|
|
76
|
-
if self._num_channels is not None and x.shape[1] != self._num_channels:
|
|
77
|
-
raise RuntimeError(
|
|
78
|
-
f"APLU was initialized with C={self._num_channels} but got C={x.shape[1]}. "
|
|
79
|
-
"Create a new APLU for a different channel size."
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
a = F.relu(x)
|
|
83
|
-
b = self.alpha * F.relu(-x + self.xi)
|
|
84
|
-
c = self.beta * F.relu(-x + self.psi)
|
|
85
|
-
d = self.gamma * F.relu(-x + self.mu)
|
|
86
|
-
return a + b + c + d
|