El siguiente código en Python nos muestra las funciones de distribución de cada uno de los inicializadores. El ejemplo no tiene utilidad real mas allá de mostrar los datos y las gráficas de los inicializadores.
import tensorflow as tf import keras as keras from keras.models import Sequential from keras.layers import Dense import pandas as pd initializers=["random_uniform","random_normal","glorot_uniform","glorot_normal","he_uniform","he_normal","lecun_uniform","lecun_normal","zeros","ones"] model=Sequential() for initializer in initializers: model.add(Dense(1000, input_dim=1000,kernel_initializer=initializer)) model.add(Dense(1000)) model.compile() df=pd.DataFrame() for index,initializer in enumerate(initializers): pesos=(model.layers[index].get_weights()[0]).reshape(-1) df[initializer]=pesos df.describe() df.iloc[:,0:8].plot(kind = 'density', subplots = True, layout = (5,2),figsize=(15,20),sharex = False)
random_uniform random_normal glorot_uniform glorot_normal he_uniform he_normal lecun_uniform lecun_normal zeros ones count 1000000.000000 1000000.000000 1000000.000000 1000000.000000 1000000.000000 1000000.000000 1000000.000000 1000000.000000 1000000.0 1000000.0 mean -0.000022 -0.000005 -0.000010 0.000020 0.000054 0.000019 -0.000008 -0.000040 0.0 1.0 std 0.028850 0.049970 0.031639 0.031590 0.044715 0.044714 0.031611 0.031635 0.0 0.0 min -0.050000 -0.238984 -0.054772 -0.071899 -0.077459 -0.101682 -0.054772 -0.071899 0.0 1.0 25% -0.025000 -0.033713 -0.027437 -0.022938 -0.038669 -0.032442 -0.027381 -0.023074 0.0 1.0 50% -0.000047 0.000047 -0.000014 0.000056 0.000099 0.000064 0.000006 -0.000038 0.0 1.0 75% 0.024980 0.033729 0.027406 0.022980 0.038725 0.032528 0.027364 0.022952 0.0 1.0 max 0.050000 0.232416 0.054772 0.071900 0.077460 0.101682 0.054772 0.071900 0.0 1.0
Ahora vamos a ver como se comportan 4 redes distintas según el número de capas, el inicializador y la función de activación usada
Mas información:
Podemos crear nuestra propia función de activación
from tensorflow.keras import backend as K def my_relu(x): return K.maximum(0.3*x, x)
Y usarla de las siguientes formas
model.add(Dense(3, input_dim=1,activation=my_relu))
model.add(Dense(3, input_dim=1)) model.add(Dense(3, input_dim=1,activation=tf.keras.layers.Activation(my_relu)))
model.add(Dense(3, input_dim=1)) model.add(Dense(3, input_dim=1,activation=tf.keras.layers.Lambda(my_relu)))
Pero si nuestra función tiene un parámetro
from tensorflow.keras import backend as K def my_relu(x,alpha): return K.maximum(alpha*x, x)
Solo podremos usar tf.keras.layers.Lambda
model.add(Dense(3, input_dim=1)) model.add(tf.keras.layers.Lambda(lambda x: my_relu(x,0.3)))
Para acabar vamos a ver como poder usar el nombre en una capa que nos hayamos creado nosotros usando el método tf.keras.utils.get_custom_objects
Vamos a definir un nombre de función de activación llamado relu_0_3
usando get_custom_objects()
from tensorflow.keras import backend as K from tensorflow.keras.utils import get_custom_objects def my_relu(x,alpha): return K.maximum(alpha*x, x) get_custom_objects()['relu_0_3']=tf.keras.layers.Lambda(lambda x: my_relu(x,0.3))
Y simplemente lo usamos indicando el nombre de relu_0_3
model.add(Dense(3, input_dim=1,activation="relu_0_3"))
La fórmula de la sigmoide se obtiene a partir de la función $logit$ o $log odds$.
$$odds(p) = \frac{p}{1-p} $$ $$logit(p) = log(odds(p))=log(\frac{p}{1-p}) $$
Ahora:
$$logit(p) = ax+b $$ $$log(\frac{p}{1-p}) = ax+b $$
Si despejamos $p$ de la anterior fórmula:
$$ log(\frac{p}{1-p}) = ax+b \\ e^{log(\frac{p}{1-p})} = e^{ax+b} \\ \frac{p}{1-p}=e^{ax+b} \\ p=e^{ax+b} \cdot (1-p) \\ p=e^{ax+b}-pe^{ax+b} \\ p+pe^{ax+b}=e^{ax+b} \\ p(1+e^{ax+b})=e^{ax+b} \\ p=\frac{e^{ax+b}}{1+e^{ax+b}} \\ p=\frac{ \frac{e^{ax+b}}{e^{ax+b}} }{ \frac{1+e^{ax+b}}{e^{ax+b}} } \\ p=\frac{ 1 }{ 1+\frac{1}{e^{ax+b}} } \\ p=\frac{ 1 }{ 1+e^{-(ax+b)} } \\ $$
Que es exactamente la función sigmoide
Ahora vamos a complicarlo un poco. Resulta que la función de activación tf.keras.activations.relu
tiene un parámetro llamado alpha
que hace que comporte como la función Leaky ReLU. Y también la capa tf.keras.layers.ReLU()
tiene el mismo parámetro pero llamado negative_slope
y obviamente hace exactamente lo mismo. Por lo tanto es igual a Leaky ReLU
model.add(Dense(3, input_dim=1,activation=tf.keras.layers.ReLU(negative_slope=0.3))) model.add(Dense(3, input_dim=1,activation=tf.keras.layers.Lambda(lambda x: tf.keras.activations.relu(x,alpha=0.3))))
Con el siguiente código podemos ver el resultado:
class_relu=tf.keras.layers.ReLU(negative_slope=0.3) class_leaky_relu=tf.keras.layers.LeakyReLU() x=np.linspace(-5,5,500) y_class_relu=class_relu(x) y_relu=tf.keras.activations.relu(x, alpha=0.3).numpy() y_class_leaky_relu=class_leaky_relu(x) np.set_printoptions(threshold=np.inf) np.set_printoptions(suppress=True) np.column_stack((y_relu,y_class_leaky_relu,y_class_relu,y_relu-y_class_leaky_relu,y_class_relu-y_class_leaky_relu))
Además en el código cuente de TensorFlow podemos ver como realmente son iguales:
Existe otra capa de activación llamada PReLU que es similar a Leaky ReLU pero el valor de $\alpha$ se calcula automáticamente durante el entrenamiento, es decir que es como un parámetro mas de la red.
Mas información:
GELU es de la últimas funciones de activación "famosas" que han aparecido. GELU: Se usa con Transformers. La usa Google con BERT y OpenAI en GPT-2 y GPT-3.1)
$$ GELU(x)=0.5x(1+ \frac {2}{ \sqrt {\pi} } \int_{0}^{ \frac {x}{ \sqrt {2}}} e ^ {-t^{2}}dt) $$
model.add(Dense(3, input_dim=1,activation=tf.keras.activations.gelu)) model.add(Dense(3, input_dim=1,activation="gelu"))
Mas información:
Se usa cuando ReLU pero es un poco más lenta pero es mejor.
$$swish(x)=x \cdot sigmoid(x)=x \cdot \frac{1}{1+e^{-x}}=\frac{x}{1+e^{-x}}$$
model.add(Dense(3, activation=tf.keras.activations.swish)) model.add(Dense(3, activation="swish"))
Se usa cuando ReLU pero aunque es un poco más lenta es mejor. Es muy similar a Swish
$$ mish(x)=x \cdot tanh(softplus(x)) $$
model.add(Dense(3, activation=tf.keras.activations.swish)) model.add(Dense(3, activation="mish"))
Para cada problema puede que una función sea mejor que otra, es decir que cosiga entrenar en un menor número de épocas. Aun así a veces no puede preocupar el tiempo de CPU/GPU que usa cada función de activación.
En la siguiente gráfica se puede ver el tiempo de entrenamiento de una red neuronal con 14 capas y 1521 neuronas usando cada una de las funciones de activación.