====== 6. Redes neuronales: Apendices ====== ===== Inicialización de parámetros ===== 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 {{ :clase:iabd:pia:1eval:inicializadores.png?direct |}} 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 {{ :clase:iabd:pia:1eval:inicializadores_de_pesos-resumen.png?direct&600 |}} Mas información: * [[https://www.machinecurve.com/index.php/2019/09/16/he-xavier-initialization-activation-functions-choose-wisely/|He/Xavier initialization & activation functions: choose wisely]] * [[https://towardsdatascience.com/hyper-parameters-in-action-part-ii-weight-initializers-35aee1a28404|Hyper-parameters in Action! Part II — Weight Initializers]]: Demostración sencilla de las fórmulas de //Glorot// y //He//. * [[https://medium.com/@shoray.goel/kaiming-he-initialization-a8d9ed0b5899|Kaiming He initialization]]: Demostración del inicializador //He// * Artículos académicos originales sobre Glorot, He y LeCun: * {{:clase:iabd:pia:1eval:understanding_the_difficulty_of_training_deep_feedforward_neural_networks.pdf|Understanding the difficulty of training deep feedforward neural networks}}: Artículo original de //Xavier Glorot// sobre su inicializador. * {{:clase:iabd:pia:1eval:delving_deep_into_rectifiers.surpassing_human-level_performance_on_imagenet_classification.pdf|Delving Deep into Rectifiers:Surpassing Human-Level Performance on ImageNet Classification}}: Artículo original de //Kaiming He// sobre su inicializador. * {{:clase:iabd:pia:1eval:efficient_backprop.pdf |Efficient BackProp}}: Artículo original de //Yann LeCun// sobre su inicializador. * Artículos académicos sobre la importancia de la inicialización en distintas redes: * {{:clase:iabd:pia:1eval:a_comparison_of_weight_initializers_in_deep.pdf |A Comparison of Weight Initializers in Deep Learning-based Side-channel Analysis}} * {{:clase:iabd:pia:1eval:finding_optimal_hyperparameters_of_feedforward_neural_networks_for_solving_differential_equations_using_a_genetic_algorithm.pdf |Finding optimal hyperparameters of feedforward neural networks for solving differential equations using a genetic algorithm}} ===== Usando funciones de activación en Keras ===== ==== Funciones personalizadas ==== 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 * Usando directamente la función de activación model.add(Dense(3, input_dim=1,activation=my_relu)) * Creando una capa de activación en base a la función de activación con [[https://www.tensorflow.org/api_docs/python/tf/keras/layers/Activation|tf.keras.layers.Activation]] o [[https://www.tensorflow.org/api_docs/python/tf/keras/layers/Lambda|tf.keras.layers.Lambda]] 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))) ==== Funciones personalizadas parametrizadas ==== 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 [[https://www.tensorflow.org/api_docs/python/tf/keras/layers/Lambda|tf.keras.layers.Lambda]] model.add(Dense(3, input_dim=1)) model.add(tf.keras.layers.Lambda(lambda x: my_relu(x,0.3))) ==== Funciones con nombre ==== Para acabar vamos a ver como poder usar el nombre en una capa que nos hayamos creado nosotros usando el método [[https://www.tensorflow.org/api_docs/python/tf/keras/utils/get_custom_objects|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")) ===== Tipos de funciones de activación en capas ocultas ===== ==== sigmoid ==== 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 * Más información * [[https://en.wikipedia.org/wiki/Odds|Odds (Wikipedia)]] * [[https://towardsdatascience.com/https-towardsdatascience-com-what-and-why-of-log-odds-64ba988bf704|WHAT and WHY of Log Odds]] * [[https://medium.com/nerd-for-tech/understanding-logistic-regression-782baa868a54|Understanding Logistic Regression]] * [[https://stats.stackexchange.com/questions/162988/why-sigmoid-function-instead-of-anything-else|Why sigmoid function instead of anything else?]] * [[https://towardsdatascience.com/why-sigmoid-a-probabilistic-perspective-42751d82686|Why Sigmoid: A Probabilistic Perspective]] ==== ReLU y Leaky ReLU ==== 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: * https://github.com/tensorflow/tensorflow/blob/r2.4/tensorflow/python/keras/layers/advanced_activations.py#L74 * https://github.com/tensorflow/tensorflow/blob/r2.4/tensorflow/python/keras/layers/advanced_activations.py#L417 === PReLU === Existe otra capa de activación llamada [[https://keras.io/api/layers/activation_layers/prelu/|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: * [[https://www.machinecurve.com/index.php/2019/12/05/how-to-use-prelu-with-keras/|How to use PReLU with Keras?]] ==== GELU ==== GELU es de la últimas funciones de activación "famosas" que han aparecido. GELU: Se usa con Transformers. La usa Google con [[https://paperswithcode.com/method/bert|BERT]] y OpenAI en GPT-2 y GPT-3.((https://mlfromscratch.com/activation-functions-explained/#gelu)) $$ GELU(x)=0.5x(1+ \frac {2}{ \sqrt {\pi} } \int_{0}^{ \frac {x}{ \sqrt {2}}} e ^ {-t^{2}}dt) $$ {{ :clase:iabd:pia:1eval:funciones_activacion_tipo_gelu.png?direct |}} {{ :clase:iabd:pia:1eval:funciones_activacion_relu_vs_elu_vs_gelu.png?direct |}} * Uso en Keras model.add(Dense(3, input_dim=1,activation=tf.keras.activations.gelu)) model.add(Dense(3, input_dim=1,activation="gelu")) Mas información: * [[https://syncedreview.com/2020/01/03/gaussian-error-linear-unit-activates-neural-networks-beyond-relu/|Gaussian Error Linear Unit Activates Neural Networks Beyond ReLU]] * [[https://www.tensorflow.org/api_docs/python/tf/keras/activations/gelu|tf.keras.activations.gelu]] * {{ :clase:iabd:pia:1eval:gaussian_error_linear_units_gelus_.pdf |Gaussian Error Linear Units (GELUS)}} * ==== Swish ==== 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}}$$ {{ :clase:iabd:pia:1eval:funciones_activacion_tipo_swish.png?direct |}} {{ :clase:iabd:pia:1eval:funciones_activacion_gelu_vs_swish.png?direct |}} * Uso en Keras model.add(Dense(3, activation=tf.keras.activations.swish)) model.add(Dense(3, activation="swish")) * [[https://www.tensorflow.org/api_docs/python/tf/keras/activations/swish|tf.keras.activations.swish(x)]] * [[https://towardsdatascience.com/activation-functions-you-might-have-missed-79d72fc080a5|Activation functions you might have missed]] * [[https://arxiv.org/pdf/1710.05941v1.pdf|Swish: A Self-Gated Activation Function]]: Paper original de Swish ==== Mish ==== 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)) $$ {{ :clase:iabd:pia:1eval:funciones_activacion_mish.png?direct |}} {{ :clase:iabd:pia:1eval:funciones_activacion_swish_vs_mish.png?direct |}} * Uso en Keras model.add(Dense(3, activation=tf.keras.activations.swish)) model.add(Dense(3, activation="mish")) * [https://www.tensorflow.org/api_docs/python/tf/keras/activations/mish|tf.keras.activations.mish(x)]] * [[https://towardsdatascience.com/activation-functions-you-might-have-missed-79d72fc080a5|Activation functions you might have missed]] * [[https://arxiv.org/pdf/1908.08681.pdf|Mish: A Self Regularized Non-Monotonic Activation Function]]: Paper original de Mish ===== Tiempo de cálculo ===== 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. {{:clase:iabd:pia:1eval:tiempos_red_neuronal.csv.scatter.png?direct|}} {{:clase:iabd:pia:1eval:tiempos_red_neuronal.csv.regresion.png?direct|}}