En el amplio universo de la inteligencia artificial, las redes neuronales artificiales han transformado la forma en que enfrentamos problemas complejos en múltiples disciplinas, desde la visión por computadora y el reconocimiento de imágenes, hasta el procesamiento del lenguaje natural y los sistemas de recomendación. Estas redes, inspiradas en el funcionamiento del cerebro humano, son capaces de aprender patrones, tomar decisiones y generalizar a partir de grandes cantidades de datos. Sin embargo, para que estas redes puedan ir más allá de simples relaciones lineales, es necesario incorporar un elemento esencial que permite capturar la complejidad de los datos reales: las funciones de activación. A lo largo de este post se revisarán 5 funciones que aparecen en arquitecturas ampliamente utilizadas, desde redes neuronales densas tradicionales hasta redes convolucionales, recurrentes, LSTM y modelos más recientes como los Transformers.
Una función de activación es una función matemática que se aplica a una neurona o a un nodo de una red neuronal (NN). Su función principal es determinar la salida de esa neurona en función de sus entradas ponderadas. En otras palabras, decide si una neurona debe “activarse” o “dispararse” y, en caso afirmativo, cuál debe ser la intensidad de su señal al pasar a la siguiente capa. Este mecanismo es crucial para introducir la no linealidad en la red y permitirle aprender patrones y relaciones complejas a partir de los datos.
Una buena función de activación debe cumplir ciertas propiedades que
favorecen un entrenamiento eficiente y estable en redes
neuronales:
- No linealidad: Permite que la red aprenda relaciones
complejas y no se reduzca a una simple transformación lineal.
- Diferenciabilidad: Es esencial para aplicar descenso
de gradiente y ajustar los pesos mediante retropropagación.
- Derivada computacionalmente eficiente: Su cálculo
debe ser rápido, ya que se evalúa millones de veces durante el
entrenamiento.
- Centrada en cero: Favorece actualizaciones de pesos
más equilibradas y estables, evitando sesgos en una sola
dirección.
- Evitar el gradiente que se desvanece o explota: Una
buena función debe mantener los gradientes dentro de un rango razonable
para asegurar el aprendizaje en todas las capas.
- Evitar zonas muertas: Debe minimizar regiones donde
el gradiente sea nulo, lo que impediría que ciertas neuronas sigan
aprendiendo.
Existen muchos tipos de funciones de activación, cada una con propiedades únicas. La elección de la función puede afectar significativamente al rendimiento de un modelo y a la eficacia del entrenamiento.
Dado un vector de entrada \(\mathbf{x} = [x_1, x_2, \dots, x_n]\), la función Softmax se define como:
\[ \text{Softmax}(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{K} e^{x_j}} \quad \text{para } i = 1, 2, \dots, K \]
Dónde:
- \(x_i\): Es el valor de entrada
(logit) para la clase \(i\).
- \(e^{x_i}\): Es la función
exponencial aplicada a \(x_i\).
- \(K\): Es el número de clases en el
clasificador multiclase.
- \(\sum_{j=1}^{K} e^{x_j}\): Sumatoria
de la función exponencial estándar del vector de salida.
# Definir función softmax
softmax <- function(x) {
exp_x <- exp(x)
exp_x / sum(exp_x)
}
# Secuencia de valores para el primer elemento del vector
x <- seq(-6, 6, length.out = 200)
# Evaluar softmax para vectores de la forma (x, 1, 2)
softmax_vals <- sapply(x, function(xi) softmax(c(xi, 1, 2))[1])
# Crear gráfico
plot(x, softmax_vals, type = "l", lwd = 2, col = "orange",
main = "Función Softmax", ylab = "Softmax(x)", xlab = "x")
# Agregar cuadrícula
grid()
La función Softmax convierte un vector de valores reales en un vector de probabilidades. Es decir, toma un conjunto de puntuaciones arbitrarias (logits) y las transforma en valores entre 0 y 1, asegurándose de que la suma de todas las salidas sea igual a 1. Esto permite interpretar cada salida como la probabilidad de pertenecer a una clase específica.
Softmax no se aplica a valores individuales, sino a vectores completos, por lo que es comúnmente utilizada en la capa de salida de redes neuronales para clasificación multiclase. A diferencia de funciones como la sigmoide o la ReLU, que se aplican elemento por elemento, Softmax actúa globalmente sobre todos los elementos de la capa.
Softmax es la elección estándar cuando nuestro modelo necesita
clasificar entradas en una de varias categorías mutuamente excluyentes.
Algunos ejemplos comunes son:
- Clasificación de imágenes (identificar objetos en fotos)
- Categorización de documentos (asignar temas al texto)
- Reconocimiento de entidades con nombre (identificación de nombres de
personas, lugares, etc.)
- Reconocimiento de voz (mapeo de audio a texto)
La derivada de la función softmax no es una derivada escalar, sino una derivada matricial (jacobiana), ya que la softmax transforma un vector en otro vector. La derivada parcial de la componente \(i\)-ésima de la softmax con respecto a la entrada \(x_j\) está dada por:
\[ \frac{\partial\ \mathrm{softmax}(x_i)}{\partial x_j} = \begin{cases}\mathrm{softmax}(x_i) \cdot (1 - \mathrm{softmax}(x_i)) & \text{si } i = j \\-\mathrm{softmax}(x_i) \cdot \mathrm{softmax}(x_j) & \text{si } i \neq j\end{cases} \]
Para un vector \(\mathbf{z} \in \mathbb{R}^n\), la derivada de la función Softmax es una matriz Jacobiana de dimensión \(n \times n\), con componentes:
Donde \(s_i = \text{Softmax}(z_i)\). - Si \(i = j\): representa la derivada de la componente respecto a sí misma. - Si \(i \neq j\): muestra cómo cambia la componente \(i\) cuando cambia la entrada \(j\) (por la dependencia global).
# Función softmax comparando x contra 0
softmax <- function(x) {
exp(x) / (exp(x) + exp(0))
}
# Derivada cuando i = j
softmax_deriv_diag <- function(x) {
s <- softmax(x)
s * (1 - s)
}
# Derivada cuando i ≠ j
softmax_deriv_offdiag <- function(x) {
s_i <- softmax(x)
s_j <- softmax(0) # comparando con 0
-s_i * s_j
}
# Vector de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluamos derivadas
diag_vals <- softmax_deriv_diag(x_vals)
offdiag_vals <- softmax_deriv_offdiag(x_vals)
# Graficar
plot(x_vals, diag_vals, type = "l", col = "blue", lwd = 2,
ylim = c(min(offdiag_vals), max(diag_vals)),
xlab = "x", ylab = "Derivada parcial",
main = "Derivada de la Función Softmax")
lines(x_vals, offdiag_vals, col = "red", lwd = 2, lty = 2)
legend("topright", legend = c("i = j",
"i ≠ j"),
col = c("blue", "red"), lty = c(1, 2), lwd = 2)
grid()
La función Swish, propuesta por investigadores de Google, se ha convertido en una alternativa prometedora a funciones clásicas como ReLU. Está definida como:
\[ \text{Swish}(x) = x \cdot \sigma(x) = x \cdot \frac{1}{1 + e^{-x}} \]
donde \(\sigma(x)\) es la función sigmoide.
Esta función de activación es relativamente nueva (2017), y supera a ReLU para redes CNN más profundas. La ecuación que define esta función describe una Sigmoid(x), pero no tiene el problema de desvanecimiento del gradiente, el factor x hace que esta función sea importante nuevamente. Con respecto a su comparativa con ReLU, ReLU tenía el problema de producir salida 0 para valores negativos que no pueden añadirse a backpropagation, Swish maneja este problema parcialmente; sin embargo, el costo computacional de Swish es mayor y es un tema que debe evaluarse para su decisión ya que este costo será llevado al proceso de backpropagation.
Swish tiene un comportamiento similar a ReLU cuando \(x\) es muy grande, pero suaviza la transición en valores cercanos a 0.
# Función sigmoide
sigmoid <- function(x) {
1 / (1 + exp(-x))
}
# Función Swish
swish <- function(x) {
x * sigmoid(x)
}
# Valores de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluación
y_swish <- swish(x_vals)
# Gráfica de Swish
plot(x_vals, y_swish, type = "l", lwd = 2, col = "orange",
main = "Función de Activación Swish", xlab = "x", ylab = "Swish(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La derivada de la función Swish con respecto a \(x\) es:
\[ \frac{d}{dx} \mathrm{swish}(x) = \sigma(x) + x \cdot \sigma(x) \cdot (1 - \sigma(x)) \]
Esta derivada puede descomponerse como la suma de:
- el valor de la sigmoide \(\sigma(x)\),
- más un término de corrección que incluye la pendiente de la sigmoide y
el valor de \(x\).
# Derivada de Swish
swish_deriv <- function(x) {
s <- sigmoid(x)
s + x * s * (1 - s)
}
# Valores de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluación
y_deriv <- swish_deriv(x_vals)
# Gráfica de la derivada
plot(x_vals, y_deriv, type = "l", lwd = 2, col = "blue",
main = "Derivada de la Función Swish", xlab = "x", ylab = "Swish'(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La función ReLU simplemente rectifica los datos (x) negativos y los vuelve cero a la salida. Las entradas con valores positivos no sufren modificación alguna a la salida. Se define matemáticamente como:
\[
ReLU(x) = max(0, x)
\] Es una función por partes:
\[
ReLU(x) = \begin{cases}
x & \text{si } x > 0 \\
0 & \text{si } x \leq 0
\end{cases}
\] La función de activación ReLU se ha convertido en la más usada
en los modelos Deep Learning durante los últimos años, lo cual se debe
principalmente a:
- La no existencia de saturación, como sí ocurre en las funciones
sigmoidal y tanh. Lo anterior hace que el algoritmo del gradiente
descendente converja mucho más rápidamente, facilitando así el
entrenamiento.
- Es más fácil de implementar computacionalmente en comparación con las
otras dos funciones, que requieren el cálculo de funciones matemáticas
más complejas como la exponencial.
# Función ReLU
relu <- function(x) {
pmax(0, x)
}
# Valores de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluación
y_relu <- relu(x_vals)
# Gráfica de ReLU
plot(x_vals, y_relu, type = "l", lwd = 2, col = "orange",
main = "Función ReLU", xlab = "x", ylab = "ReLU(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La derivada de la función ReLU con respecto a \(x\) es:
\[
\frac{d}{dx} ReLU(x) = \begin{cases}
1 & \text{si } x > 0 \\
0 & \text{si } x < 0 \\
\text{indefinida} & \text{si } x = 0
\end{cases}
\]
Esto significa:
- Para entradas positivas (x > 0), la función es lineal y su derivada
es 1.
- Para entradas negativas (x < 0), la salida se aplana y la derivada
es 0, lo que implica que los gradientes no fluyen y el nodo deja de
aprender.
- En x = 0 la derivada es técnicamente indefinida, pero en la
práctica,se define como 0 o 1 para simplificar los cálculos.
drelu <- function(x) {
ifelse(x > 0, 1, 0) # Definimos 0 en x = 0 por simplicidad
}
x_vals <- seq(-6, 6, length.out = 300)
y_drelu <- drelu(x_vals)
plot(x_vals, y_drelu, type = "l", lwd = 2, col = "blue",
main = "Derivada de la función ReLU",
xlab = "x", ylab = "dReLU/dx")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La función Softplus es una versión suavizada de la función ReLU, usada como función de activación en redes neuronales. Se define como:
\[
\mathrm{softplus}(x) = \ln(1 + e^x)
\] Donde:
- El término \(\ln(1 + e^x)\) suaviza
la operación de activación.
La función Softplus suele considerarse una versión más suave de la función ReLU. Si bien ReLU es simple y eficaz, puede presentar problemas, como provocar la “muerte” de neuronas si siempre generan cero para entradas negativas. Softplus evita este problema proporcionando una salida suave y continua tanto para entradas positivas como negativas. Además, Softplus es una función diferenciable en todo su dominio, a diferencia de ReLU, que presenta una discontinuidad en cero. Asimismo, la naturaleza continua y diferenciable de Softplus facilita el funcionamiento eficaz de los algoritmos de optimización basados en gradientes, garantizando un aprendizaje fluido durante el entrenamiento.
La función Softplus ofrece mayor estabilidad numérica que otras funciones de activación, ya que evita los problemas derivados de valores muy grandes o muy pequeños. Presenta una salida suave y, para valores de entrada muy grandes o muy pequeños, se comporta de forma predecible, lo que reduce el riesgo de desbordamiento o subdesbordamiento en los cálculos.
Softplus es útil cuando:
- Necesita una función de activación suave y continua.
- Quiere evitar el problema de neuronas moribundas que ocurre con
ReLU.
- La red maneja entradas tanto positivas como negativas, y desea que la
salida permanezca no negativa.
- Prefieres una función diferenciable en toda la red para una
optimización basada en gradientes más suave.
# Función Softplus
softplus <- function(x) {
log(1 + exp(x))
}
# Valores de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluación
y_softplus <- softplus(x_vals)
# Gráfica de Softplus
plot(x_vals, y_softplus, type = "l", lwd = 2, col = "orange",
main = "Función Softplus", xlab = "x", ylab = "Softplus(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La derivada de la función Softplus con respecto a \(x\) es:
\[ \frac{d}{dx} \mathrm{softplus}(x) = \frac{1}{1 + e^{-x}} = \sigma(x) \] Se observa que la derivada de la función Softplus es exactamente la función sigmoidea. Esta propiedad hace que Softplus sea útil cuando se desea controlar la suavidad del gradiente, ya que tiene una derivada continua y suave.
Esto significa que:
- Para valores grandes de \(x\), la
derivada se aproxima a 1 (como ReLU).
- Para valores negativos, la pendiente disminuye suavemente, pero nunca
se vuelve cero (a diferencia de ReLU).
# Función sigmoide (derivada de la Softplus)
sigmoid <- function(x) {
1 / (1 + exp(-x))
}
# Valores de entrada
x_vals <- seq(-6, 6, length.out = 300)
# Evaluación de la derivada
y_derivada_softplus <- sigmoid(x_vals)
# Gráfica de la derivada de Softplus
plot(x_vals, y_derivada_softplus, type = "l", lwd = 2, col = "blue",
main = "Derivada de la Función Softplus", xlab = "x", ylab = "d(Softplus)/dx")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La función de activación Tanh se asemeja a la función Sigmoide, pero proporciona resultados en el rango de -1 a 1 en lugar de 0 a 1. Al igual que la Sigmoide, presenta una curva característica en forma de “S” y es diferenciable, lo que la hace adecuada para redes neuronales. Se define matemáticamente como:
\[ \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} \] Debido a que las salidas de Tanh están centradas en cero, conduce a mejores actualizaciones de peso y una convergencia más rápida en el entrenamiento basado en gradientes en comparación con Sigmoid, que está restringido al rango de 0 a 1 .
# Función tanh
tanh_func <- function(x) {
tanh(x)
}
# Valores de entrada
x_vals <- seq(-5, 5, length.out = 400)
# Evaluación
y_tanh <- tanh_func(x_vals)
# Gráfica de tanh
plot(x_vals, y_tanh, type = "l", lwd = 2, col = "orange",
main = "Función tanh(x)", xlab = "x", ylab = "tanh(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
La derivada de la función Tanh con respecto a \(x\) es:
\[
\frac{d}{dx} ReLU(x) = \begin{cases}
1 & \text{si } x > 0 \\
0 & \text{si } x < 0 \\
\text{indefinida} & \text{si } x = 0
\end{cases}
\]
Esto significa:
- Para entradas positivas (x > 0), la función es lineal y su derivada
es 1.
- Para entradas negativas (x < 0), la salida se aplana y la derivada
es 0, lo que implica que los gradientes no fluyen y el nodo deja de
aprender.
- En x = 0 la derivada es técnicamente indefinida, pero en la
práctica,se define como 0 o 1 para simplificar los cálculos.
# Derivada de tanh
dtanh_func <- function(x) {
1 - tanh(x)^2
}
y_dtanh <- dtanh_func(x_vals)
# Gráfica de su derivada
plot(x_vals, y_dtanh, type = "l", lwd = 2, col = "blue",
main = "Derivada de la función tanh(x)", xlab = "x", ylab = "d/dx tanh(x)")
abline(h = 0, v = 0, col = "gray", lty = 2)
grid()
https://www.mygreatlearning.com/blog/activation-functions/
https://www.geeksforgeeks.org/deep-learning/softplus-function-in-neural-network/
https://www.innovatiana.com/es/post/activation-function-in-ai
https://jahazielponce.com/funciones-de-activacion-y-como-puedes-crear-la-tuya-usando-python-r-y-tensorflow/
https://www.alexisalulema.com/es/2022/09/23/funciones-de-activacion-en-tensorflow
https://medium.com/@sushmita2310/12-types-of-activation-functions-in-neural-networks-a-comprehensive-guide-a441ecefb439