| """ |
| Module contenant la couche de neurones SitiNEUR |
| Syntaxe inspirée de PyTorch mais simplifiée |
| """ |
|
|
| import numpy as np |
|
|
|
|
| class SitiNEUR: |
| """ |
| Couche de neurones simplifiée pour Sitiai |
| |
| Args: |
| input_size: Nombre d'entrées |
| output_size: Nombre de sorties |
| activation: Fonction d'activation ('relu', 'sigmoid', 'tanh', 'linear') |
| |
| Example: |
| >>> from sitiai import SitiNEUR |
| >>> layer = SitiNEUR(input_size=10, output_size=5, activation='relu') |
| >>> output = layer.forward(input_data) |
| """ |
| |
| def __init__(self, input_size: int, output_size: int, activation: str = 'relu'): |
| self.input_size = input_size |
| self.output_size = output_size |
| self.activation = activation |
| |
| |
| if activation == 'relu': |
| self.weights = np.random.randn(input_size, output_size) * np.sqrt(2.0 / input_size) |
| else: |
| self.weights = np.random.randn(input_size, output_size) * np.sqrt(1.0 / input_size) |
| self.bias = np.zeros(output_size) |
| |
| |
| self.last_input = None |
| self.last_output = None |
| self.last_z = None |
| |
| def forward(self, x: np.ndarray) -> np.ndarray: |
| """ |
| Propagation avant |
| |
| Args: |
| x: Données d'entrée (shape: [batch_size, input_size]) |
| |
| Returns: |
| Sortie de la couche (shape: [batch_size, output_size]) |
| """ |
| self.last_input = x |
| |
| |
| self.last_z = np.dot(x, self.weights) + self.bias |
| |
| |
| self.last_output = self._apply_activation(self.last_z) |
| return self.last_output |
| |
| def backward(self, grad_output: np.ndarray, learning_rate: float = 0.01) -> np.ndarray: |
| """ |
| Rétropropagation du gradient |
| |
| Args: |
| grad_output: Gradient de la sortie |
| learning_rate: Taux d'apprentissage |
| |
| Returns: |
| Gradient pour la couche précédente |
| """ |
| |
| grad_output = np.clip(grad_output, -10, 10) |
| |
| |
| if self.activation == 'relu': |
| grad_activation = (self.last_z > 0).astype(float) |
| else: |
| grad_activation = self._activation_gradient(self.last_output) |
| |
| grad_z = grad_output * grad_activation |
| |
| |
| grad_z = np.clip(grad_z, -10, 10) |
| |
| |
| grad_weights = np.dot(self.last_input.T, grad_z) |
| grad_bias = np.sum(grad_z, axis=0) |
| |
| |
| grad_weights = np.clip(grad_weights, -1, 1) |
| grad_bias = np.clip(grad_bias, -1, 1) |
| |
| |
| self.weights -= learning_rate * grad_weights |
| self.bias -= learning_rate * grad_bias |
| |
| |
| grad_input = np.dot(grad_z, self.weights.T) |
| return grad_input |
| |
| def _apply_activation(self, z: np.ndarray) -> np.ndarray: |
| """Applique la fonction d'activation""" |
| if self.activation == 'relu': |
| return np.maximum(0, z) |
| elif self.activation == 'sigmoid': |
| return 1 / (1 + np.exp(-np.clip(z, -500, 500))) |
| elif self.activation == 'tanh': |
| return np.tanh(z) |
| elif self.activation == 'linear': |
| return z |
| else: |
| raise ValueError(f"Activation inconnue: {self.activation}") |
| |
| def _activation_gradient(self, output: np.ndarray) -> np.ndarray: |
| """Calcule le gradient de la fonction d'activation""" |
| if self.activation == 'relu': |
| return (output > 0).astype(float) |
| elif self.activation == 'sigmoid': |
| return output * (1 - output) |
| elif self.activation == 'tanh': |
| return 1 - output ** 2 |
| elif self.activation == 'linear': |
| return np.ones_like(output) |
| else: |
| raise ValueError(f"Activation inconnue: {self.activation}") |
| |
| def get_weights(self): |
| """Retourne les poids et biais de la couche""" |
| return { |
| 'weights': self.weights.copy(), |
| 'bias': self.bias.copy() |
| } |
| |
| def set_weights(self, weights_dict): |
| """Définit les poids et biais de la couche""" |
| self.weights = weights_dict['weights'].copy() |
| self.bias = weights_dict['bias'].copy() |
| |
| def __repr__(self): |
| return f"SitiNEUR(input_size={self.input_size}, output_size={self.output_size}, activation='{self.activation}')" |
|
|