Пример #1
0
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()
Пример #2
0
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()
Пример #3
0
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
Пример #4
0
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