20  Neurona artificial moderna

Una neurona artificial moderna es una extensión del perceptrón de Rosenblatt la diferencia radica en que ésta utiliza una función de activación contínua y derivable que reemplaza a la función escalón. Por ejemplo, la función sigmoide fue la más utilizada en los primeros años. Teóricamente la función de activación debe ser derivable en todo el dominio sin embargo, como observaremos más adelante esta derivación puede sustituirse por una derivación numérica en los caso que la derivación analítica no está disponible.

En cuanto al contexto histórico, fueron B. Widrow y M. Hoff quienes propusieron dicha extensión al perceptrón. En su artículo original [Widrow et al. (1960)](WIDROW 1960), proponen llamarle a este tipo de neuronales ADALINE, contracción de ADaptive LInear NEuron. En cuanto al método de aprendizaje, se propuso el uso de la llamada regla Delta, que curiosamente era muy similar a la regla de aprendizaje de Rosenblatt pero estaba formalizada bajo la teoría del descenso por gradiente (WIDROW 1995).

A continuación, revisaremos algunas funciones de activación populares.

Diagrama simplificado de una neurona artificial. La capa de entrada esta compuesta de los elementos que componen el vector \(X\) y la capa de salida solo tiene un elemento. Note que a diferencia del diagrama del perceptrón. En este diagrama simplificado se condensan la combinación lineal y la función de activación en un mismo nodo.

20.1 Funciones de activación

Las funciones de activación tienen por objetivo mapear la combinación lineal a un espacio de trabajo diferente. En algunos el objetivo es un mapeo lineal, y en otros es introducir no linealidad en las neuronas. Dependiendo de la tarea que se le encarge a la neurona, deberá ser la selección de su función de activación. Algunas de las funciones de activación más comunes son las que se mencionan a continuación.

20.1.1 Función lineal

La función lineal replica la entrada en la salida. No se suele incluir un escalado de la entrada dado que dicho escalado es realizado previamente por los pesos de la combinación lineal.

La función se escribe de acuerdo a la ecuación (#eq:f_lineal) y su gráfica se presenta en la figura 6.

$$

f_{lineal}(h) = h

$$

Función lineal

Una de las características más relevantes de estas funciones es que al colocar varias neuronas lineales en serie la salida siempre será un valor lineal. Desde una perspectiva más formal, las neuronas lineales únicamente pueden funcionar como regresores lineales (Walpole et al. 1993).

Su implementación en NumPy se presenta a continuación:

def funcion_lineal(x):
    return x

20.1.2 Función sigmoide

La función sigmoide imita el comportamiento de un umbralizado entre cero y uno; tal cual lo realiza la función escalón. Sin embargo, incorpora la propiedad de ser continua y diferenciable, lo que permite el entrenamiento cuando se usa descenso por gradiente.

La función sigmoide se utiliza a menudo como una función de activación en las redes neuronales para introducir no linealidad en la red y esto a su vez permite que las neuronas de la red sean activadas de manera no lineal en función de la entrada, lo que les concede la capacidad de aprender patrones complejos en los datos de entrada. La función de activación sigmoide, graficada en la figura 7, se calcula como:

$$

f_{sigmoid}(h) = $$

Uno de los inconvenientes que puede presentar esta función es que tiende saturarse en sus extremos, lo que puede dificultar el entrenamiento de las redes.

Función sigmoide

A continuación se presenta su implementación en Python.

def sigmoide(h):
    return 1/(1+np.exp(-h))

20.1.3 Función tangente hiperbólica

La función tangente hiperbólica, también conocida como “tanh”, es una función matemática no lineal que toma una entrada (\(h\)) y produce una salida (\(y\)) en el rango de -1 a 1 (ver figura 8). De forma similar que la función sigmoide imita un comportamiento dicotómico, solo que en este caso extiende su rango a los valores -1 y 1. Esto puede ser útil en algunos casos en los que se desea una salida simétrica en torno a cero. La función tanh se define como:

$$

f_{tanh}(h) = $$

Si se usa Python y NumPy la implementación de la función tanh se puede escribir como:

def tanh(x):
    ex = np.exp(x)  # e^x
    e_minus_x = np.exp(-x)  # e^{-x}
    return (ex - e_minus_x) / (ex + e_minus_x)

Función tangente hiperbólica

20.1.4 Función activación lineal rectificada (ReLU)

La función de activación activación lineal rectificada (ReLU, por sus siglas en inglés Rectified Linear Unit) es una función no lineal, que toma una entrada (\(h\)) y produce una salida (\(y\)) de valor cero si la entrada es negativa, y si la entrada es positiva, devuelve la entrada misma, ver figura 9. La función ReLU se define como:

$$

f_{ReLU}(h) = \[\begin{cases} 0 & \text{if } h < 0 \ h & \text{if } h \geq 0 \end{cases}\]

\end{cases}$$

Función ReLU

Existen formas alternativas de definir la función ReLU:

$$

f_{ReLU}(h) = h^{+} = max(0,h)$$

Una de las ventajas es que al no saturarse en sus extremos como la función sigmoide, permitiendo que fluya mayor cantidad de información a través de las neuronas; sin embargo, para los valores negativos esta función no permite que las neuronas se activen, que puede verse como una perdida de información.

def relu(x):
  return np.maximum(0, x)

20.1.5 Función Leaky ReLU

La función Leaky ReLU es una variante de la función ReLU diseñada para solucionar uno de los problemas de ReLU, conocido como neurona muerta. Este problema ocurre cuando, para valores de entrada negativos, la ReLU devuelve cero, lo que puede llevar a que ciertas neuronas nunca se activen ni contribuyan al aprendizaje, ya que sus gradientes se vuelven cero.

En Leaky ReLU, en lugar de devolver cero para entradas negativas, se permite un pequeño valor negativo. La función es:

$$

f_{}(x) = (x, x),$$

donde \(\alpha\) es un pequeño valor positivo (por ejemplo, 0.01) que se multiplica por los valores negativos de la entrada. Esto asegura que siempre haya un pequeño gradiente, incluso cuando la entrada sea negativa, evitando que las neuronas queden inactivas.

Función Leaky ReLU
def leaky_relu(x, alpha=0.01):
  return np.where(x > 0, x, alpha * x)

20.1.6 Función lineal-exponencial (ELU)

La función ELU (Exponential Linear Unit) es una variante de la función Leaky ReLU que introduce una no linealidad suave para los valores negativos. Su objetivo es mejorar el aprendizaje de redes profundas al reducir el problema de neurona muerta y hacer que la red sea más robusta.

La función ELU se define como:

$$

f_{(x)} = \[\begin{cases} x, & \text{si } x > 0 \ \alpha(e^x - 1), & \text{si } x \leq 0 \end{cases}\]
  • 1), & x \end{cases},$$

donde \(\alpha\) es un parámetro positivo que controla la saturación para entradas negativas (usualmente, \(\alpha = 1\)).

Esta función tiene ventaja sobre ReLU en ciertos escenarios donde las redes profundas son propensas a problemas de gradientes o inestabilidad en las activaciones.

Función ELU (unidad lineal-exponencial).
def elu(x, alpha=1.0):
  return np.where(x > 0, x, alpha * (np.exp(x) - 1))

20.1.7 Unidad lineal de error gaussiano (GELU)

La función GELU (Gaussian Error Linear Unit)(Hendrycks 2016) es una función de activación de alto rendimiento que pondera las entradas por su magnitud, en lugar de bloquearlas por su signo como hace la ReLU. Esta función incorpora la idea de regularización estocástica (similar al Dropout) y permite una no linealidad suave y no monótona, lo cual es crucial para el flujo de gradientes en modelos complejos. Esta función ha demostrado superioridad sobre ReLU y ELU en tareas de visión por computadora y, notablemente, es la activación estándar en modelos de procesamiento de lenguaje natural basados en Transformers (como BERT y GPT).

La función GELU se define formalmente a través de la función de distribución acumulada de la normal estándar \(\Phi(x)\):

$$

f_{(x)} = x (x) = ,$$

donde \(\text{\textbf{erf}}()\) es la función de error gaussiana.

La función GELU se puede aproximar de forma computacionalmente eficiente usando.

$$

_{}(x) 0.5x ( 1 + )

$$

Para propósitos de cálculos manuales o aumentar más la velocidad de cómputo es mejor utilizar la versión SiLU (sigmoid linear unit):

$$

f_{SiLU}(x) = x f_{sigmoid}(1.702x)$$

A continuación se presentan las posibles implementaciones en python usando numpy.

def gelu_exacta(x):
    return 0.5 * x * (1 + math.erf(x / math.sqrt(2)))
Función GELU (unidad lineal de error gaussiano).
def gelu_aproximada(x):
  # Aproximacion comun usando tanh para eficiencia computacional
  return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * np.power(x, 3))))
Ejemplo

A partir del diagrama siguiente:

image

donde \(W = [0.9, -0.5]\), \(b = -0.6\) y la función de activación es una función tangente hiperbólica. Calcule las salidas de la red para el siguiente conjunto de datos:

ID \(x_1\) \(x_2\)
1 0 3
2 1 5
3 7 6

SOLUCION: Para cada uno de los ejemplos en el conjunto se realiza: $$

^i = f_{}(w_1 x_1^i + w_2 x_2^i + b)$$

lo que implica que para el primer ejemplo, \(i=1\), se calcule como: $$

^1 = f_{}(w_1 x_1^1 + w_2 x_2^1 + b)$$

por tanto, $$

^1 = f_{}(0.9 + (-0.5) -0.6)\[ \]

^1 = -0.635$$

Siguiendo la misma metodología obtenemos para los ejemplos restantes: $$

^2 = -0.975\[ \]

^3 = 0.993$$

20.2 Implementación

Una forma de implmentar una neurona simple es mediante el código #cod:neurona_simple que separa los cálculos en funciones para la combinación lineal y la función de activación. Con respecto a la combinación lineal se usa la función dot de NumPy; ésta función realiza un producto escalar entre dos arrays. Es importante destacar que el comportamiento de esta función varía ligeramente dependiendo de la dimensión y la forma de los arrays con los que se trabaja. Para vectores 1D, numpy.dot devuelve el producto escalar (también conocido como producto punto) de los vectores. Por ejemplo, si a y b son dos vectores 1D, numpy.dot(a, b) calculará el producto escalar de a y b. Para matrices 2D (y arrays multidimensionales), numpy.dot realiza una multiplicación de matrices. En este caso, el número de columnas de la primera matriz debe ser igual al número de filas de la segunda matriz. Por ejemplo, si A es una matriz de dimensiones (m×n) y B es una matriz de dimensiones (n×p), entonces numpy.dot(A, B) resultará en una nueva matriz de dimensiones (m×p). Para combinaciones de vectores y matrices, la función dot aplicará las reglas correspondientes de multiplicación vector-matriz o matriz-vector, dependiendo del orden en que se proporcionen los vectores y las matrices.

La función de activación se deja libre en el código #cod:neurona_simple. Finalmente, la implementación se prueba en el código #cod:test_neurona_simple.

def neurona(X, W, b, activacion):
    h = combinacion_lineal(X, W, b)
    return activacion(h)    
import numpy as np
        
def main():
    print("Red neuronal simple")
    
    entradas = np.array([1, 1])
    print("X: ", entradas)
    pesos = np.array([.2, .2])
    print("W: ", pesos)
    sesgo = 1
    print("b: ", sesgo)
    
    # inferencia o pase frontal
    activacion = sigmoide
    output = neurona(entradas, pesos, sesgo, activacion)
    
    print("Resultado: ", output)
    
if __name__ == "__main__":
    main()  

20.3 Ejercicios {#ej:redes simples}

Ejercicio

Usando la neurona artificial del siguiente diagrama: image donde $$

W = [1.0,-0.9,0.4], X = [4.0,6.0,8.0], b=0.1.$$

Responda las siguientes preguntas.

  1. Calcule la salida de la red, \(\hat{y}\), suponiendo como función de activación la función sigmoide, \(\sigma(x)\).

  2. Cambie la función de activación por una función tangente hiperbólica y calcule de nuevo la salida.

  3. Cambie las entradas al siguiente vector y aplique la función ReLU. $$

X = \[\begin{bmatrix} 5.0 \ 6.0 \ 4.0 \end{bmatrix}\]

4.0 \end{bmatrix}$$

Ejercicio

Implemente en python y numpy una neurona artificial que utilice como función de activación:

  1. la función tangente hiperbólica.

  2. la función ELU.

Ejercicio

Modifique el código #cod:test_neurona_simple para que el vector de entrada sea introducido por el usuario (el tamaño y los valores deben ser introducidos por el usuario) y los pesos sean inicializados de forma aleatoria.

Hendrycks, D. 2016. «Gaussian Error Linear Units (Gelus)». arXiv preprint arXiv:1606.08415.
Walpole, Ronald E, Raymond H Myers, Sharon L Myers, y Keying Ye. 1993. Probability and statistics for engineers and scientists. Libro. Vol. 5. Macmillan New York.
WIDROW, B. 1960. «Adaptive switching circuits». En 1960 IRE WESCON Convention Record, Part 4, 96-104.
———. 1995. «Perceptrons, Adalines, and Backpropagation». The Handbook of Brain Theory and Neural Networks.
Widrow, Bernard et al. 1960. Adaptive" adaline" Neuron Using Chemical" memistors.". Stanford.