Source code for biolearn.model.bcm

import numpy as np

from biolearn.model._base import Base
from biolearn.utils.weights import Normal
from biolearn.utils.activations import Logistic
from biolearn.utils.optimizer import SGD

__author__  = ['Nico Curti', 'SimoneGasperini']
__email__ = ['nico.curit2@unibo.it', 'simone.gasperini2@studio.unibo.it']


[docs]class BCM (Base): ''' Parameters ---------- inputs : int (default=None) Number of input units outputs : int (default=100) Number of hidden units num_epochs : int (default=100) Maximum number of epochs for model convergency batch_size : int (default=10) Size of the minibatch weights_init : BaseWeights (default=Normal) Weights initialization strategy object activation : Activations (default=Logistic) Activation function object optimizer : Optimizer (default=SGD) Optimizer object orthogonalization : bool (default=False) Turn on/off the synaptic weights orthogonalization algorithm interaction_strength : float (default=0.) Set the lateral interaction strenght between weights precision : float (default=1e-30) Parameter that controls numerical precision of the weight updates epochs_for_convergency : int (default=None) Number of stable epochs requested for the convergency. If None the training proceeds up to the maximum number of epochs (num_epochs) convergency_atol : float (default=0.01) Absolute tolerance requested for the convergency random_state : int (default=None) Random seed for weights generation verbose : bool (default=True) Turn on/off the verbosity ''' def __init__(self, inputs=None, outputs=100, num_epochs=100, batch_size=100, weights_init=Normal(), activation=Logistic(), optimizer=SGD(), orthogonalization=False, interaction_strength=0., precision=1e-30, epochs_for_convergency=None, convergency_atol=0.01, random_state=None, verbose=True): self.orthogonalization = orthogonalization self._interaction_matrix = self._weights_interaction(interaction_strength, outputs) self.interaction_strength = interaction_strength super (BCM, self).__init__(inputs=inputs, outputs=outputs, num_epochs=num_epochs, batch_size=batch_size, weights_init=weights_init, activation=activation, optimizer=optimizer, precision=precision, epochs_for_convergency=epochs_for_convergency, convergency_atol=convergency_atol, random_state=random_state, verbose=verbose) def _weights_interaction (self, strength, outputs): ''' Set the interaction matrix between weights' connections Parameters ---------- strength : float Interaction strength between weights outputs : int Number of hidden units Returns ------- interaction_matrix : array-like Matrix of interactions between weights ''' if not -1. < strength < 1.: raise ValueError('Incorrect value of interaction_strength. It must be in the interval ]-1,1[') if strength != 0.: L = np.full(fill_value=-strength, shape=(outputs, outputs)) L[np.eye(*L.shape, dtype=bool)] = 1 return np.linalg.inv(L) else: return np.eye(M=outputs, N=outputs) def _weights_update (self, X, output): ''' Compute the weights update using the BCM learning rule. Parameters ---------- X : array-like (2D) Input array of data output : array-like (2D) Output of the model estimated by the predict function Returns ------- weight_update : array-like (2D) Weight updates matrix to apply theta : array-like (1D) Array of learning progress ''' theta = np.mean(output**2, axis=1, keepdims=True) phi = output * (output - theta) * (1. / (theta + self.precision)) #dw = phi @ X dw = np.einsum('ij, jk -> ik', phi, X, optimize=True) nc = np.max(np.abs(dw)) nc = 1. / max(nc, self.precision) return dw * nc, theta def _fit (self, X): ''' Core function for the fit member ''' if self.orthogonalization and self.outputs > self.inputs: raise ValueError('The orthogonalization cannot be performed because the number of ' 'outputs is greater than the number of inputs') return super(BCM, self)._fit(X=X, norm=False, ortho=self.orthogonalization) def _predict (self, X): ''' Core function for the predict member ''' # return self.activation.activation( self._interaction_matrix @ self.weights @ X.T, copy=True) return self.activation.activate(np.einsum('ij, jk, lk -> il', self._interaction_matrix, self.weights, X, optimize=True), copy=True)