def _init_acummulators(self): """ Inicializo acumuladores usados para la optimizacion :return: """ self.step_w = [] self.step_b = [] for layer in self.model.list_layers: shape_w = layer.get_weights().shape shape_b = layer.get_bias().shape self.step_w.append(LocalNeurons(np.zeros(shape_w), shape_w)) self.step_b.append(LocalNeurons(np.zeros(shape_b), shape_b))
def __init__(self, n_in, n_out, activation='ReLU', distributed=False, w=None, b=None, rng=None): self.n_out = n_out self.n_in = n_in self.activation = act.fun_activation[activation] self.activation_d = act.fun_activation_d[activation] distributed = False # TODO completar esta funcionalidad if rng is None: rng = np.random.RandomState(123) self.rng = rng self.rnd_state = self.rng.get_state() self.shape_w = n_out, n_in self.shape_b = n_out, 1 # Recomendaciones de http://cs231n.github.io/neural-networks-2/ y http://deeplearning.net/tutorial/mlp.html#mlp # TODO: ver si conviene dejar acá la inicializ de pesos, o en core.neurons (en términos de legibilidad) if w is None: if activation is "Tanh": w = np.asarray( self.rng.uniform( low=-np.sqrt(6.0 / (n_in + n_out)), high=+np.sqrt(6.0 / (n_in + n_out)), size=self.shape_w), dtype=np.dtype(float) ) elif activation is "Sigmoid": w = np.asarray( self.rng.uniform( low=-np.sqrt(6.0 / (n_in + n_out))*4.0, high=+np.sqrt(6.0 / (n_in + n_out))*4.0, size=self.shape_w), dtype=np.dtype(float) ) else: w = self.rng.randn(*self.shape_w) * np.sqrt(2.0/n_in) if b is None: b = np.zeros(self.shape_b, dtype=np.dtype(float)) # TODO weights_T era p/ poder hacer operaciones distribuidas, pero se deja como TBC la class DistributedNeurons assert distributed is False, logger.error("DistributedNeurons will be implemented soon ...") self.weights = LocalNeurons(w, self.shape_w) #self.weights_T = LocalNeurons(w.transpose(), self.shape_w[::-1]) self.bias = LocalNeurons(b, self.shape_b)
def test_activation(self): logger.info("Testeando la integración de funciones de activación...") activation = 'Tanh' fun = fun_activation[activation] res = self.matrix_neurons.activation(fun) fun_d = fun_activation_d[activation] res_d = self.matrix_neurons.activation(fun_d) assert np.array_equiv(res.matrix, fun(np.ones(self.matrix_neurons.shape))) assert np.array_equiv(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape))) # Test de softmax como funcion de activacion N = self.matrix_neurons.rows shape = (N,) x = LocalNeurons(sc.parallelize(range(N)), shape) # Aprovecho para testear con RDD res = x.softmax() res = map(lambda e: e[0], res.matrix) exp_x = np.exp(range(N)) y = exp_x / float(sum(exp_x)) assert np.allclose(res, y) logger.info("OK")
def test_activation(self): logger.info("Testeando la integración de funciones de activación...") activation = 'Tanh' fun = fun_activation[activation] res = self.matrix_neurons.activation(fun) fun_d = fun_activation_d[activation] res_d = self.matrix_neurons.activation(fun_d) assert np.array_equiv(res.matrix, fun(np.ones(self.matrix_neurons.shape))) assert np.array_equiv(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape))) # Test de softmax como funcion de activacion N = self.matrix_neurons.rows shape = (N, ) x = LocalNeurons(sc.parallelize(range(N)), shape) # Aprovecho para testear con RDD res = x.softmax() res = map(lambda e: e[0], res.matrix) exp_x = np.exp(range(N)) y = exp_x / float(sum(exp_x)) assert np.allclose(res, y) logger.info("OK")
def test_operators(self): logger.info("Testeando operadores...") other = np.ones(self.matrix_neurons.shape) # Return: LocalNeurons res = self.matrix_neurons * other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons / other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons - other assert np.array_equiv(res.matrix, np.zeros(self.matrix_neurons.shape)) res = self.matrix_neurons + other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 2) res **= 2 # LocalNeurons assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 4) assert self.matrix_neurons == LocalNeurons(self.matrix_neurons.matrix, self.matrix_neurons.shape) logger.info("OK")
def __init__(self, shape=(100,100)): mat = np.ones(shape) self.matrix_neurons = LocalNeurons(mat, shape)
class TestLocalNeurons(object): def __init__(self, shape=(100,100)): mat = np.ones(shape) self.matrix_neurons = LocalNeurons(mat, shape) def test_operators(self): logger.info("Testeando operadores...") other = np.ones(self.matrix_neurons.shape) # Return: LocalNeurons res = self.matrix_neurons * other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons / other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons - other assert np.array_equiv(res.matrix, np.zeros(self.matrix_neurons.shape)) res = self.matrix_neurons + other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 2) res **= 2 # LocalNeurons assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 4) assert self.matrix_neurons == LocalNeurons(self.matrix_neurons.matrix, self.matrix_neurons.shape) logger.info("OK") def test_linalg(self): logger.info("Testeando operaciones algebraicas...") N = self.matrix_neurons.rows other = np.array(range(N)) # Producto matricial res = self.matrix_neurons.mul_array(other) sum_range = sum(range(N)) assert np.array_equiv(res.matrix, np.array([sum_range]*N)) # Producto punto #res = res.transpose().dot(range(N)) #assert res == (sum_range ** 2) * N # Producto entre elementos assert self.matrix_neurons.mul_elemwise(self.matrix_neurons) == self.matrix_neurons.collect() # Suma entre arreglos assert np.array_equiv(self.matrix_neurons.sum_array(self.matrix_neurons), self.matrix_neurons * 2) assert self.matrix_neurons.sum() == self.matrix_neurons.count() logger.info("OK") def test_activation(self): logger.info("Testeando la integración de funciones de activación...") activation = 'Tanh' fun = fun_activation[activation] res = self.matrix_neurons.activation(fun) fun_d = fun_activation_d[activation] res_d = self.matrix_neurons.activation(fun_d) assert np.array_equiv(res.matrix, fun(np.ones(self.matrix_neurons.shape))) assert np.array_equiv(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape))) # Test de softmax como funcion de activacion N = self.matrix_neurons.rows shape = (N,) x = LocalNeurons(sc.parallelize(range(N)), shape) # Aprovecho para testear con RDD res = x.softmax() res = map(lambda e: e[0], res.matrix) exp_x = np.exp(range(N)) y = exp_x / float(sum(exp_x)) assert np.allclose(res, y) logger.info("OK") def test_loss(self): logger.info("Testeando la integración de funciones de activación...") loss = 'MSE' y = np.array(range(self.matrix_neurons.shape[0])) fun = fun_loss[loss] res = self.matrix_neurons.loss(fun, y) fun_d = fun_loss_d[loss] res_d = self.matrix_neurons.loss_d(fun_d, y) assert res == fun(np.ones(self.matrix_neurons.shape), y) assert np.allclose(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape), y)) logger.info("OK") # TODO: test regularization stuff (i.e. l1, l2, dropout) def test_all(self): logger.info("Test de funcionalidades para la clase LocalNeurons") self.test_operators() self.test_linalg() self.test_activation() self.test_loss()
class NeuralLayer(object): """ Clase básica para modelar una capa de neuronas que compone una red neuronal. Contiene sus "neuronas" representadas por pesos sinápticos **w** y **b**, además de una función de activación asociada para dichos pesos. Una correcta inicialización de los pesos sinápticos está muy ligada a la función de activación elegida: * Por defecto, los pesos sinápticos se inicializan con una distribución uniforme \ con media *0* y varianza :math:`\\tfrac{2.0}{\sqrt{n_{in}}}`, \ lo cual da buenos resultados especialmente usando ReLUs. * Para la función *Tanh* se muestrea sobre una distribución uniforme \ en el rango :math:`\pm \sqrt{\\frac{6}{n_{in}+n_{out}}}`. * Para la *Sigmoid* en el rango :math:`\pm 4.0 \sqrt{\\frac{6}{n_{in}+n_{out}}}`. :param n_in: int, dimensión de la entrada. :param n_out: int, dimensión de la salida. :param activation: string, key de alguna función de activación soportada en :mod:`~learninspy.core.activations`. :param distributed: si es True, indica que se utilicen arreglos distribuidos para **w** y **b**. :param w: :class:`.LocalNeurons`, matriz de pesos sinápticos. Si es *None*, se crea por defecto. :param b: :class:`.LocalNeurons`, vector de pesos bias. Si es *None*, se crea por defecto. :param rng: si es *None*, se crea un generador de números aleatorios mediante una instancia **numpy.random.RandomState**. .. note:: el parámetro *distributed* no tiene efecto, ya que el uso de arreglos distribuidos se deja para un próximo release. >>> n_in, n_out = (10, 5) >>> layer = NeuralLayer(n_in, n_out, activation='Tanh') >>> x = np.random.rand(n_in) >>> out = layer.output(x) >>> len(out) 5 """ def __init__(self, n_in, n_out, activation='ReLU', distributed=False, w=None, b=None, rng=None): self.n_out = n_out self.n_in = n_in self.activation = act.fun_activation[activation] self.activation_d = act.fun_activation_d[activation] distributed = False # TODO completar esta funcionalidad if rng is None: rng = np.random.RandomState(123) self.rng = rng self.rnd_state = self.rng.get_state() self.shape_w = n_out, n_in self.shape_b = n_out, 1 # Recomendaciones de http://cs231n.github.io/neural-networks-2/ y http://deeplearning.net/tutorial/mlp.html#mlp # TODO: ver si conviene dejar acá la inicializ de pesos, o en core.neurons (en términos de legibilidad) if w is None: if activation is "Tanh": w = np.asarray( self.rng.uniform( low=-np.sqrt(6.0 / (n_in + n_out)), high=+np.sqrt(6.0 / (n_in + n_out)), size=self.shape_w), dtype=np.dtype(float) ) elif activation is "Sigmoid": w = np.asarray( self.rng.uniform( low=-np.sqrt(6.0 / (n_in + n_out))*4.0, high=+np.sqrt(6.0 / (n_in + n_out))*4.0, size=self.shape_w), dtype=np.dtype(float) ) else: w = self.rng.randn(*self.shape_w) * np.sqrt(2.0/n_in) if b is None: b = np.zeros(self.shape_b, dtype=np.dtype(float)) # TODO weights_T era p/ poder hacer operaciones distribuidas, pero se deja como TBC la class DistributedNeurons assert distributed is False, logger.error("DistributedNeurons will be implemented soon ...") self.weights = LocalNeurons(w, self.shape_w) #self.weights_T = LocalNeurons(w.transpose(), self.shape_w[::-1]) self.bias = LocalNeurons(b, self.shape_b) def __div__(self, other): self.weights /= other self.bias /= other return self def __mul__(self, other): self.weights *= other self.bias *= other return self def l1(self): """ Norma **L1** sobre la matriz **w** de pesos sinápticos, utilizando la funcion :func:`~learninspy.core.neurons.LocalNeurons.l1`. Por lo tanto, se retorna el resultado de aplicar la norma y el gradiente de la misma. :return: tuple de float, :class:`~learninspy.core.neurons.LocalNeurons` """ return self.weights.l1() def l2(self): """ Norma **L2** sobre la matriz **w** de pesos sinápticos, utilizando la funcion :func:`~learninspy.core.neurons.LocalNeurons.l2`. Por lo tanto, se retorna el resultado de aplicar la norma y el gradiente de la misma. :return: tuple de float, :class:`~learninspy.core.neurons.LocalNeurons` """ return self.weights.l2() def output(self, x, grad=False): """ Salida de la capa neuronal. Se toma una entrada :math:`x \in \Re^{n_{in}}`, se pondera con los pesos sinápticos **W** y el bias **b**, y luego se aplica la función de activación **f** para retornar como resultado: :math:`a = f(Wx + b), \quad a' = f'(Wx + b)` :param x: **numpy.ndarray**, vector de entrada :param grad: Si es *True*, se retorna además el gradiente de la salida. :return: **numpy.ndarray**, o tupla de ellos si *grad* es True. """ wx = self.weights.mul_array(x) z = wx.sum_array(self.bias) a = z.activation(self.activation) if grad is True: d_a = z.activation(self.activation_d) a = (a, d_a) return a # Basado en http://cs231n.github.io/neural-networks-2/ def dropoutput(self, x, p, grad=True): """ Salida de la capa neuronal, luego de aplicar la regularización de los pesos sinápticos por Dropout utilizando la funcion :func:`~learninspy.core.neurons.LocalNeurons.dropout`. :param x: numpy.ndarray, vector de entrada :param p: float, tal que :math:`0<p<1` :param grad: Si es *True*, se retorna además el gradiente de la salida. :return: numpy.ndarray, (o tuple de numpy.ndarray, numpy.ndarray si *grad* es *True*), numpy.ndarray correspondiente a la máscara binaria utilizada en el DropOut. .. note:: En las predicciones de la red no se debe efectuar Dropout. """ self.rng.set_state(self.rnd_state) # Para que sea reproducible out = self.output(x, grad) if grad is True: a, d_a = out a, mask = a.dropout(p, self.rng.randint(500)) # randint es para generar un nuevo seed (reproducible) out = a, d_a else: out, mask = out.dropout(p, self.rng.randint(500)) # TODO: en que caso no se necesitaria el grad? return out, mask # Devuelvo ademas la mascara utilizada, para el backprop def update(self, step_w, step_b): # Actualiza sumando los argumentos w y b a los respectivos pesos """ Se actualizan los arreglos **w** y **b** sumando respectivamente los incrementos dados por los parámetros recibidos. :param step_w: :class:`.LocalNeurons` :param step_b: :class:`.LocalNeurons` """ self.weights += step_w # self.weights_T += step_w.transpose() self.bias += step_b return def get_weights(self): """ Se devuelve la matriz de pesos sinápticos **w**. :return: numpy.ndarray. """ return self.weights def get_bias(self): """ Se devuelve el vector de bias **b**. :return: numpy.ndarray. """ return self.bias
def __init__(self, shape=(100, 100)): mat = np.ones(shape) self.matrix_neurons = LocalNeurons(mat, shape)
class TestLocalNeurons(object): def __init__(self, shape=(100, 100)): mat = np.ones(shape) self.matrix_neurons = LocalNeurons(mat, shape) def test_operators(self): logger.info("Testeando operadores...") other = np.ones(self.matrix_neurons.shape) # Return: LocalNeurons res = self.matrix_neurons * other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons / other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape)) res = self.matrix_neurons - other assert np.array_equiv(res.matrix, np.zeros(self.matrix_neurons.shape)) res = self.matrix_neurons + other assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 2) res **= 2 # LocalNeurons assert np.array_equiv(res.matrix, np.ones(self.matrix_neurons.shape) * 4) assert self.matrix_neurons == LocalNeurons(self.matrix_neurons.matrix, self.matrix_neurons.shape) logger.info("OK") def test_linalg(self): logger.info("Testeando operaciones algebraicas...") N = self.matrix_neurons.rows other = np.array(range(N)) # Producto matricial res = self.matrix_neurons.mul_array(other) sum_range = sum(range(N)) assert np.array_equiv(res.matrix, np.array([sum_range] * N)) # Producto punto #res = res.transpose().dot(range(N)) #assert res == (sum_range ** 2) * N # Producto entre elementos assert self.matrix_neurons.mul_elemwise( self.matrix_neurons) == self.matrix_neurons.collect() # Suma entre arreglos assert np.array_equiv( self.matrix_neurons.sum_array(self.matrix_neurons), self.matrix_neurons * 2) assert self.matrix_neurons.sum() == self.matrix_neurons.count() logger.info("OK") def test_activation(self): logger.info("Testeando la integración de funciones de activación...") activation = 'Tanh' fun = fun_activation[activation] res = self.matrix_neurons.activation(fun) fun_d = fun_activation_d[activation] res_d = self.matrix_neurons.activation(fun_d) assert np.array_equiv(res.matrix, fun(np.ones(self.matrix_neurons.shape))) assert np.array_equiv(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape))) # Test de softmax como funcion de activacion N = self.matrix_neurons.rows shape = (N, ) x = LocalNeurons(sc.parallelize(range(N)), shape) # Aprovecho para testear con RDD res = x.softmax() res = map(lambda e: e[0], res.matrix) exp_x = np.exp(range(N)) y = exp_x / float(sum(exp_x)) assert np.allclose(res, y) logger.info("OK") def test_loss(self): logger.info("Testeando la integración de funciones de activación...") loss = 'MSE' y = np.array(range(self.matrix_neurons.shape[0])) fun = fun_loss[loss] res = self.matrix_neurons.loss(fun, y) fun_d = fun_loss_d[loss] res_d = self.matrix_neurons.loss_d(fun_d, y) assert res == fun(np.ones(self.matrix_neurons.shape), y) assert np.allclose(res_d.matrix, fun_d(np.ones(self.matrix_neurons.shape), y)) logger.info("OK") # TODO: test regularization stuff (i.e. l1, l2, dropout) def test_all(self): logger.info("Test de funcionalidades para la clase LocalNeurons") self.test_operators() self.test_linalg() self.test_activation() self.test_loss()
def test_all(self): # 1) Test de assert_features_label # 1.a) Sin exception # Dummy data features = np.array(range(10)) label = 1 data = (features, label) test_ok = True try: self.test_features_label(data) test_ok = True except: test_ok = False assert test_ok # 1.b) Con exception try: self.test_features_label(features) test_ok = False # Tendría que arrojar un Exception por recibir un arreg de dim 10 except: test_ok = True assert test_ok # 1.c) No debe actuar con None try: self.test_features_label(None) test_ok = True except: test_ok = False assert test_ok # 2) Test de assert_samedimension # 2.a) Sin exception try: self.test_samedimension(features, features) test_ok = True except: test_ok = False assert test_ok # 2.b) Con exception try: self.test_samedimension(features, features[:5]) # Dimensiones diferentes test_ok = False except: test_ok = True assert test_ok # 3) Test de assert_matchdimension # Matrices de neuronas shape = (5, 10) matrix1 = LocalNeurons(np.zeros(shape), shape=shape) shape = (10, 3) matrix2 = LocalNeurons(np.zeros(shape), shape=shape) # 3.a) Sin exception try: self.test_matchdimension(matrix1, matrix2) test_ok = True except: test_ok = False assert test_ok # 3.b) Con exception try: self.test_matchdimension(matrix2, matrix1) # Dimensiones no compatibles test_ok = False except: test_ok = True assert test_ok # 4) Test de assert_sametype try: self.test_sametype(features, features) test_ok = True except: test_ok = False assert test_ok try: self.test_sametype(features, matrix2) test_ok = False except: test_ok = True assert test_ok