feat: ActivationFunc classes ReLU and LeakyReLU

This commit is contained in:
Lenoctambule
2026-03-29 19:20:05 +02:00
parent 44bf4c0286
commit 53c7f73055
5 changed files with 37 additions and 21 deletions

27
activations.py Normal file
View File

@@ -0,0 +1,27 @@
import numpy as np
from abc import ABC, abstractmethod
class ActivationFunc(ABC):
@abstractmethod
def derivative(v: np.ndarray) -> np.ndarray:
pass
class ReLU(ActivationFunc):
def __call__(self, x):
return x * (x > 0)
def derivative(self, x):
return x > 0
class LeakyReLU(ActivationFunc):
def __init__(self, k=0.01):
self.k = k
def __call__(self, x):
return x * (x > 0) + self.k * x * (x <= 0)
def derivative(self, x):
return (x > 0) + self.k * (x <= 0)

View File

@@ -4,6 +4,7 @@ from utils import (dynamic_loss_plot_init,
dynamic_loss_plot_finish) dynamic_loss_plot_finish)
from tqdm import tqdm from tqdm import tqdm
from layers import DeepNNLayer from layers import DeepNNLayer
from activations import ActivationFunc
LOADER = ['', '', '', '', '', '', '', ''] LOADER = ['', '', '', '', '', '', '', '']
@@ -13,7 +14,7 @@ class Autoencoder:
encoder_layers: list[int], encoder_layers: list[int],
decoder_layers: list[int], decoder_layers: list[int],
lr: float, lr: float,
activation_func): activation_func: ActivationFunc):
self.encoder = DeepNNLayer(encoder_layers, lr, activation_func) self.encoder = DeepNNLayer(encoder_layers, lr, activation_func)
self.decoder = DeepNNLayer(decoder_layers, lr, activation_func) self.decoder = DeepNNLayer(decoder_layers, lr, activation_func)
@@ -49,7 +50,7 @@ class Autoencoder:
with tqdm(bar_format="{desc} {elapsed} {rate_fmt}") as lbar: with tqdm(bar_format="{desc} {elapsed} {rate_fmt}") as lbar:
while True: while True:
lbar.set_description( lbar.set_description(
f"{LOADER[epoch % len(LOADER)]} Training ({epoch=} error={float(prev_error):.6f}", # noqa f"{LOADER[epoch % len(LOADER)]} Training ({epoch=} error={float(prev_error):.6f})", # noqa
) )
lbar.update() lbar.update()
error = 0 error = 0

View File

@@ -1,6 +1,6 @@
import numpy as np import numpy as np
import types
from utils import normalize from utils import normalize
from activations import ActivationFunc
class NNLayer: class NNLayer:
@@ -8,7 +8,7 @@ class NNLayer:
in_size: int, in_size: int,
out_size: int, out_size: int,
lr: float, lr: float,
activation_func: types.FunctionType): activation_func: ActivationFunc):
self.W = np.random.uniform(-1, 1, (in_size, out_size)) self.W = np.random.uniform(-1, 1, (in_size, out_size))
self.B = np.zeros((out_size)) self.B = np.zeros((out_size))
self.lr = lr self.lr = lr
@@ -18,7 +18,7 @@ class NNLayer:
self.activation_func = activation_func self.activation_func = activation_func
def __str__(self): def __str__(self):
return f'[ {self.W.shape[0]} => {self.W.shape[1]}\tlr:{self.lr}\tactivation:{self.activation_func.__name__} ]' # noqa return f'[ {self.W.shape[0]} => {self.W.shape[1]}\tlr:{self.lr}\tactivation:{self.activation_func.__class__.__name__} ]' # noqa
def forward(self, V: np.ndarray) -> np.ndarray: def forward(self, V: np.ndarray) -> np.ndarray:
self.input = normalize(V) self.input = normalize(V)
@@ -29,7 +29,7 @@ class NNLayer:
return self.output return self.output
def backprop(self, error: np.ndarray) -> np.ndarray: def backprop(self, error: np.ndarray) -> np.ndarray:
error *= self.activation_func(self.output_linear, True) error *= self.activation_func.derivative(self.output_linear)
ret = self.W @ error ret = self.W @ error
dW = np.outer(self.input, error) * self.lr dW = np.outer(self.input, error) * self.lr
dB = error * self.lr dB = error * self.lr
@@ -42,7 +42,7 @@ class DeepNNLayer:
def __init__(self, def __init__(self,
layers: list[int], layers: list[int],
lr: float, lr: float,
activation_func: types.FunctionType): activation_func: ActivationFunc):
self.layers: list[NNLayer] = [] self.layers: list[NNLayer] = []
for i in range(len(layers) - 1): for i in range(len(layers) - 1):
self.layers.append( self.layers.append(

View File

@@ -1,7 +1,7 @@
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from autoencoder import Autoencoder from autoencoder import Autoencoder
from utils import leaky_relu from activations import LeakyReLU
def load_mnist() -> list[np.ndarray]: def load_mnist() -> list[np.ndarray]:
@@ -32,7 +32,7 @@ def mnist_train(
[in_len, 64, 16], [in_len, 64, 16],
[16, 64, in_len], [16, 64, in_len],
0.01, 0.01,
leaky_relu LeakyReLU()
) )
autoencoder.train_dataset( autoencoder.train_dataset(
x_train, x_train,

View File

@@ -9,18 +9,6 @@ def softmax(v: np.ndarray) -> np.ndarray:
return exp_v / np.sum(exp_v) return exp_v / np.sum(exp_v)
def relu(x: np.ndarray, derivative=False) -> np.ndarray:
if derivative:
return x > 0
return x * (x > 0)
def leaky_relu(x: np.ndarray, derivative=False, k=0.01) -> np.ndarray:
if derivative:
return 1 * (x > 0) + k * (x <= 0)
return x * (x > 0) + x * 0.01 * (x <= 0)
def normalize(v: np.ndarray) -> np.ndarray: def normalize(v: np.ndarray) -> np.ndarray:
return v / (np.linalg.norm(v) + 1e-8) return v / (np.linalg.norm(v) + 1e-8)