Tabla de Contenidos

6. Redes neuronales

Al principio del curso vimos un esbozo de una red neuronal, en este tema vamos a profundizar en las redes neuronales.

Como vimos una red neuronal no es más que una función matemática de la forma:

$$\vec{y}=f(\vec{x})$$

En el caso de las flores era:

$$tipo \: flor=f(largo \: sépalo,largo \: pétalo)$$

Y la red neuronal que vimos de ejemplo era:

Esta red neuronal, consta de una serie de neuronas que se pasan valores de unas a otras. Son cada uno de los círculos. Las neuronas se organiza en capas:

Mas información:

Neurona

Pasemos a ver que es una neurona. La neurona no es mas que otra función matemáticas que por ahora es solo un polinomio de primer grado.

En su forma mas sencilla una neurona es únicamente lo siguiente:

$$y=wx+b$$

Es decir que a cada neurona le entra el valor de entrada llamado x, y calcula el valor de salida llamado y y para ello usa la fórmula wx+b. Y esa fórmula es la ecuación de una recta.

Si la entrada en vez de ser un único valor de x fueran varios valores, la fórmula quedaría así:

$$y=w_{1}x_{1}+w_{2}x_{2}+w_{3}x_{3}...+w_{n}x_{n}+b$$

que puesto en forma de vectores sería:

$$y=\vec{w}^{ \, \intercal} \cdot \vec{x}+b$$

Por ejemplo si hubiera 3 valores de entrada llamados x1, x2 y x3. La formula sería:

$$y=w_{1}x_{1}+w_{2}x_{2}+w_{3}x_{3}+b$$

El siguiente esquema resume como es una neurona:

Lo siguiente a ver es ¿Que son los w y b que hay en la fórmula? Es lo que se llama pesos y sesgos que vienen del ingles weight y bias. Esos 2 valores es lo que se llaman parámetros de una red neuronal. Esos valores los desconocemos y al crear la red neuronal se eligen aleatoriamente pero al entrenar la red reuronal con el método fit, lo que hace la red neuronal es justamente calcular los parámetros pesos w y sesgos b más adecuados

Normalmente en los apuntes cuando hablemos de los parámetro o los pesos nos estaremos refiriendo tanto a los valores de los weight como a los de bias o w y b

Red Neuronal

Pasemos ahora a explicar que es una red neuronal. No es mas que muchas neuronas conectadas entre ellas.

En la siguiente imagen se pueden ver diversos tipos de redes neuronales

Mas información:

Pasemos ahora a ver un ejemplo simple con 5 neuronas.

Veamos esta red y como son cada una de las neuronas.

Nº Neurona Formula Explicación
1 $$y_1=x$$ Como es una neurona de entrada , realmente no hace nada. Su salida realmente es la entrada ya que solo captura los datos de entrada.
2 $$y_2=w_{2}y_1+b_{2}$$ Esta neurona obtiene su entrada de la salida de la neurona anterior que es $ y_1$
3 $$y_3=w_{3}y_1+b_{3}$$ Esta neurona obtiene su entrada de la salida de la neurona anterior que es $ y_1$
4 $$y_4=w_{4}y_1+b_{4}$$ Esta neurona obtiene su entrada de la salida de la neurona anterior que es $ y_1$
5 $$y_5=w_{5,2}y_2+w_{5,3}y_3+w_{5,4}y_4+b_5$$ Esta neurona obtiene su entrada de las salidas de las 3 neuronas anteriores que son $y_2$ , $y_3$ e $ y_4$

Pero sabiendo que $y_1=x$, podemos substituirlo en las neuronas 2, 3 y 4

Nº Neurona Formula
2 $$y_2=w_{2}\overbrace{x}^{y_1}+b_{2}$$
3 $$y_3=w_{3}\overbrace{x}^{y_1}+b_{3}$$
4 $$y_4=w_{4}\overbrace{x}^{y_1}+b_{4}$$

y finalmente como ya sabemos lo que vale $y_2$ , $y_3$ e $ y_4$ podemos substituirlo en la neurona 5.

Nº Neurona Formula
5 $$y_5=w_{5,2}\overbrace{(w_{2}x+b_{2})}^{y_2}+w_{5,3}\overbrace{(w_{3}x+b_{3})}^{y_3}+w_{5,4}\overbrace{(w_{4}x+b_{4})}^{y_4}+b_5$$

Ahora si decíamos que una red neuronal es solo una función matemática que calcula una y en función de una x ya tenemos la fórmula:

$$y=f(x)=w_{5,2}(w_{2}x+b_{2})+w_{5,3}(w_{3}x+b_{3})+w_{5,4}(w_{4}x+b_{4})+b_5$$

Y el cálculo de todos esos parámetros ( $w_{2} , w_{3} , w_{4} , w_{5,2} , w_{5,3} , w_{5,4} , b_{4} , b_{2} , b_{3} , b_5$) es lo que se hace al entrenar la red con todos los datos de entrada de todas las posibles x y todos los resultados de y.

¿Como sería esa red programada en Keras?

model=Sequential()
model.add(Dense(3, input_dim=1))
model.add(Dense(1))
model.compile()

Mas información:

Tamaño red neuronal

Pasemos ahora a ver el tamaño una red neuronal. Para ver el tamaño podríamos medirlo por el número de neuronas pero hemos visto que hay neuronas mas simples y mas complejas

La neurona A es mas sencilla que la neurona B. Por ello para determinar el tamaño de la red lo que se cuenta es la suma de los parámetros de todas sus neuronas.

Calculemos ahora cuantos parámetros tiene la red que hemos visto antes.

Es decir que en total son 10 parámetros.

Con Keras podemos saber el Nº de parámetros de cada capa y los totales con el método summary()

model.summary()

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 3)                 6         
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 4         
=================================================================
Total params: 10
Trainable params: 10
Non-trainable params: 0
_________________________________________________________________

El método summary() demás de indicarnos en Nº de parámetros de cada capa , también nos indica el Nº de neuronas de cada capa. Fijaros que no aparece la primera capa ya que nunca tiene ningún parámetro para entrenar.

También podemos ver la estructura de la red neuronal con el método plot_model()

from tensorflow.keras.utils import plot_model

plot_model(model, show_shapes=True)

Podemos ver para cada capa el Nº de entradas y salidas. El Nº de salidas determinará el Nº de neuronas que tendrá esa capa.

Mas información:

Parámetros en Keras

En Keras para ver los valores exactos de cada parámetro de la red neuronal se usa el método .get_weights. Este método retorna tanto los weight como los bias

Sigamos con el ejemplo pero vamos a explicar como Keras numera las neuronas. Lo que hace es organizar las neuronas en capas (layers)y numerar cada neurona con un número dentro de la capa.

La capa de entrada como no hace ningún cálculo es como si no existiera por eso la primera capa oculta es la capa "0" y la de salida al ser la siguiente es la capa "1". Luego dentro de cada capa, las neuronas se enumeran desde el "0" hasta el número de neuronas menos 1.

Para simplificar la obtención de los valores de los parámetros hemos creado dos funciones:

def get_w(model,layer,neuron,index):
    layer=model.layers[layer]
    return layer.get_weights()[0][index,neuron]



def get_b(model,layer,neuron):
    layer=model.layers[layer]
    return layer.get_weights()[1][neuron]

Usando las funciones que acabamos de crear vamos a obtener cada uno de los parámetros

#weights y bias cada neurona de la capa 0 
w_2=get_w(model,0,0,0)
b_2=get_b(model,0,0)

w_3=get_w(model,0,1,0)
b_3=get_b(model,0,1)

w_4=get_w(model,0,2,0)
b_4=get_b(model,0,2)


#weights y bias de la única neurona de la capa 1
w_52=get_w(model,1,0,0)
w_53=get_w(model,1,0,1)
w_54=get_w(model,1,0,2)
b_5 =get_b(model,1,0)



Inicialización de parámetros

Hemos indicado antes que cuando se define la red neuronal con compile, Keras genera valores aleatorios para los parámetros y luego con el método fit se entrena la red neuronal para averiguar los valores mas adecuados.

Cuando decimos que los parámetros de inicializan de forma aleatoria ¿que distribución siguen? ¿distribución Normal ,distribución Uniforme, etc? Pues bien , en ciertos casos es necesario definir exactamente cual es la forma de inicializar los parámetros.

En keras al crear una capa podemos indicar como se inicializan los pesos (weights) y como se inicializan los sesgos (bias), para ello están los siguientes parámetros:

model=Sequential()
model.add(Dense(3, input_dim=1,kernel_initializer="glorot_uniform",bias_initializer="zeros"))
model.add(Dense(1))
model.compile()

En el ejemplo los pesos (weights) se inicializan de la forma "Glorot Uniforme" y los sesgos (bias) mediante "ceros".

Lo normal es modificar el inicializador únicamente de los weights y que los bias se inicialicen a 0. Es decir que el parámetro bias_initializer no se suele usar.

Los siguientes son los posibles inicializadores en Keras:

Siendo:

$$fan_{in}=el \: nº \: de \: entradas \: de \: la \: capa$$

$$fan_{out}=el \: nº \: de \: salidas \: de \: la \: capa$$

  • El inicializador glorot también se puede llamar Xavier ya que el autor es Xavier Glorot.
  • El inicializador he también se puede llamar Kaiming ya que el autor es Kaiming He.
  • El inicializador lecun también se puede llamar Yann ya que el autor es Yann LeCun.
No es importante saber las fórmulas de cada inicializador se ha puesto simplemente para explicar las diferencias entre unos y otros

La pregunta ahora es ¿que inicializador usar? Por defecto Keras usa para los pesos (weights) el inicializador glorot_uniform y para los sesgos (bias) el inicializador zeros.

Los inicializadores de los pesos (weights) dependen de la función de activación y se recomiendan los siguientes:

Inicializador Función de Activación
Glorot Normal Ninguna, sigmoide, tanh, softmax
He Uniform ReLu y sus variantes
LeCum Normal SELU
LeCum Uniform GELU

Mas información:

Funciones de activación

Si sabes de matemáticas te puedes haber dado cuenta que falta algo en una neurona. Resulta que si cada neurona es un polinómio de primer grado ( $\; \; y=wx+b \; \;$ ) la unión con otras neuronas acaba siendo también otro polinomio de primer grado, con lo que no serviría para nada hacer una red neuronal.

Recordemos la red neuronal

Cuya función era:

$$y=f(x)=w_{5,2}(w_{2}x+b_{2})+w_{5,3}(w_{3}x+b_{3})+w_{5,4}(w_{4}x+b_{4})+b_5$$

Si agrupamos un poco los términos, se puede queda la expresión de la siguiente forma:

$$y=f(x)=(w_{5,2}w_2+w_{5,3}w_3+w_{5,4}w_4)x+(w_{5,2}b_2+w_{5,3}b_3+w_{5,4}b_4+b_5)$$

Si creamos dos nuevas variable llamas W y B

$$ W=w_{5,2}w_2+w_{5,3}w_3+w_{5,4}w_4 \\ B=w_{5,2}b_2+w_{5,3}b_3+w_{5,4}b_4+b_5 $$

Entonces

$$y=f(x)=Wx+B$$

¡¡¡¡¡¡¡Así que la red se podría haber simplificado a una única neurona de entrada y otra de salida!!!!

Para que realmente funcionen las redes neuronales hay que añadir "algo" que cree una "no linealidad". Ésto se consigue con la función de activación.

Para añadir la función de activación vamos a cambiar un poquito la nomenclatura que hemos usado hasta ahora. La salida de la función lineal a partir de ahora la vamos a llamar z en vez de y ya que la y sigue siendo la salida de la neurona pero después de aplicar la función de activación a z.

$$z = wx+b$$

$$y = \sigma(z)$$

Siendo σ (sigma ) el nombre que le vamos a dar a la función de activación.

Una función de activación sencilla es la llamada "función sigmoide" y es la siguiente fórmula :

$$\sigma(z) = \frac{1}{1 + e^{-z}}$$

Por lo que la neurona queda de la siguiente forma:

$$ \left.\begin{array}{c} z = wx+b \\ y = \sigma(z) \\ \sigma(z) = \frac{1}{1 + e^{-z}} \\ \end{array} \; \; \right\rbrace \; \; y=\frac{1}{1 + e^{-(wx+b)}} $$

Así que vamos a cambiar la fórmula de nuestra red neuronal incluyendo la función de activación sigmoide:

Nº Neurona Formula
1 $$y_1=x$$
2 $$y_2=\sigma(w_{2}y_1+b_{2})=\frac{1}{1 + e^{-( w_{2}y_1+b_{2} )}}$$
3 $$y_3=\sigma(w_{3}y_1+b_{3})=\frac{1}{1 + e^{-( w_{3}y_1+b_{3} )}}$$
4 $$y_4=\sigma(w_{4}y_1+b_{4})=\frac{1}{1 + e^{-( w_{4}y_1+b_{4} )}}$$
5 $$y_5=\sigma(w_{5,2}y_2+w_{5,3}y_3+w_{5,4}y_4+b_5)=\frac{1}{1 + e^{-( w_{5,2}y_2+w_{5,3}y_3+w_{5,4}y_4+b_5 )}}$$
Destacar que la neurona de entrada no tiene función de activación

Y si volvemos a generar la formula completa de la neurona de la salida (es decir y5), la red neuronal queda de la siguiente forma:

$$\LARGE y_5=\frac{1}{1 + e^{-( w_{5,2}\frac{1}{1 + e^{-( w_{2}x+b_{2} )}}+w_{5,3}\frac{1}{1 + e^{-( w_{3}x+b_{3} )}}+w_{5,4}\frac{1}{1 + e^{-( w_{4}x+b_{4} )}}+b_5 )}}$$

Y esa si que es la verdadera función de nuestra red neuronal.

Y en la siguiente imagen se puede ver una animación esquemática de como funciona para predecir datos:

¿Y como sería ahora la programación en Keras de la red neuronal incluyendo la función de activación sigmoide en las neuronas?

model=Sequential()
model.add(Dense(3, input_dim=1,activation="sigmoid"))
model.add(Dense(1,activation="sigmoid"))
model.compile()

Mas información:

Recreando la red neuronal

Acabamos de ver que para la red neuronal

Y usando la función de activación sigmoide, la red neuronal que se obtiene es:

$$\LARGE y_5=\frac{1}{1 + e^{-( w_{5,2}\frac{1}{1 + e^{-( w_{2}x+b_{2} )}}+w_{5,3}\frac{1}{1 + e^{-( w_{3}x+b_{3} )}}+w_{5,4}\frac{1}{1 + e^{-( w_{4}x+b_{4} )}}+b_5 )}}$$

Pues bien, vamos a comprar el resultado de usar una red neuronal con el de la fórmula, para verificar que dar el mismo resultado.

Para ello vamos a crear con Keras la red neuronal, obtener los parámetros , crear la fórmula y comprar lo que retorna la red neuronal y la fórmula.

import numpy as np
import pandas as pd
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets import load_iris

iris=load_iris()
x=iris.data[0:99,2]
y=iris.target[0:99]


np.random.seed(5)
tf.random.set_seed(5)
random.seed(5)  

model=Sequential()
model.add(Dense(3, input_dim=1,activation="sigmoid"))
model.add(Dense(1,activation="sigmoid"))
model.compile(loss='mean_squared_error')

Hemos creado el modelo y ahora vamos a entrenarlo con los datos.

model.fit(x, y,epochs=100)

Hay que destacar que cuando se compila el modelo con compile ya se dan valores aleatorios a los parámetros y luego con el método fit ya se entrena la red neuronal para que aprenda los valores correctos de los parámetros.

Ahora vamos a obtener todos los parámetros como hemos visto antes.

def get_w(model,layer,neuron,index):
    layer=model.layers[layer]
    return layer.get_weights()[0][index,neuron]
 
 
 
def get_b(model,layer,neuron):
    layer=model.layers[layer]
    return layer.get_weights()[1][neuron]


w_2 =get_w(model,0,0,0)
w_3 =get_w(model,0,1,0)
w_4 =get_w(model,0,2,0)
w_52=get_w(model,1,0,0)
w_53=get_w(model,1,0,1)
w_54=get_w(model,1,0,2)
b_2 =get_b(model,0,0)
b_3 =get_b(model,0,1)
b_4 =get_b(model,0,2)
b_5 =get_b(model,1,0)

Por fin vamos a crear la fórmula que hará las predicciones pero usando únicamente los parámetros en vez de la red neuronal, para ello hemos creado la función predict_formula:

def sigmoid(z):
    return 1/(1 + np.exp(-z))

def predict_formula(x):
    part1=w_52*sigmoid(w_2*x+b_2)
    part2=w_53*sigmoid(w_3*x+b_3)
    part3=w_54*sigmoid(w_4*x+b_4)
    part4=b_5
    z=part1+part2+part3+part4

    return sigmoid(z)

Y ahora con todas las x que tenemos vamos a calcular el resultado de la red neuronal y lo mismo con la fórmula para ver si dan lo mismo:

y_predicho_red_neuronal=model.predict([x])
y_predicho_formula=predict_formula(x)

Para compararlos, los ponemos uno al lado del otro y así saldrán en parejas.

np.column_stack((y_predicho_red_neuronal,y_predicho_formula))

El resultado es éste:

array([[0.49011275, 0.49011275],
       [0.49011275, 0.49011275],
       [0.48533145, 0.48533144],
       [0.49485764, 0.49485766],
       [0.49011275, 0.49011275],
       [0.50421923, 0.50421925],
       [0.49011275, 0.49011275],
       [0.49485764, 0.49485766],
       [0.49011275, 0.49011275],
       [0.49485764, 0.49485766],
       [0.49485764, 0.49485766],
       [0.49956134, 0.49956136],
       [0.49011275, 0.49011275],
       [0.47567996, 0.47567996],
       [0.48051879, 0.48051877],
       [0.49485764, 0.49485766],
       [0.48533145, 0.48533144],
       [0.49011275, 0.49011275],
       [0.50421923, 0.50421925],
       [0.49485764, 0.49485766],
       [0.50421923, 0.50421925],
       [0.49485764, 0.49485766],
       [0.4708204 , 0.47082038],
       [0.50421923, 0.50421925],
       [0.51338053, 0.51338055],

......

       [0.56985992, 0.56985994],
       [0.59807748, 0.59807747],
       [0.59807748, 0.59807747],
       [0.59807748, 0.59807747],
       [0.60083133, 0.60083131],
       [0.5590229 , 0.5590229 ]])

¡¡¡¡¡¡Como podemos ver los números son iguales hasta el 7º decimal. Es decir que hemos comprobado que la fórmula coincide con la red neuronal!!!!!

Usando funciones de activación en Keras

Antes de ver los tipos de funciones de activación que hay , vamos a ver como se pueden usar el Keras ya que hay varias formas distintas de usarlas.

Para explicar como indicar la función de activación , vamos a usar como ejemplo usa función de activación llamada "ReLU".

"relu"

tf.keras.activations.relu

tf.keras.layers.ReLU()

Las 3 formas de definir función de activación, se pueden usar en 2 sitios distintos. Como el parámetro activation o añadiendolo una nueva capa.

Parámetro activation Una nueva capa
String
model.add(Dense(3, input_dim=1,activation="relu"))
-
Función de Activación
model.add(Dense(3, input_dim=1,activation=tf.keras.activations.relu))
-
Capa de Activación
model.add(Dense(3, input_dim=1,activation=tf.keras.layers.ReLU()))
model.add(Dense(3, input_dim=1))
model.add(tf.keras.layers.ReLU())

Parece que la forma más normal de definir la función de activación es simplemente poner el nombre pero hay funciones de activación que se pueden parametrizar , es decir que las puedes personalizar con un parámetro. La única forma de definir el parámetro en una capa de activación

model.add(Dense(3, input_dim=1))
model.add(tf.keras.layers.ReLU(negative_slope=0.3))

Función a Capa

Si queremos seguir usando la función de activación pero dentro de una capa de activación podemos usar la clase tf.keras.layers.Activation o tf.keras.layers.Lambda

model.add(Dense(3, input_dim=1))
model.add(tf.keras.layers.Activation(tf.keras.activations.relu))

model.add(Dense(3, input_dim=1))
model.add(tf.keras.layers.Lambda(tf.keras.activations.relu))

Función parametrizada a Capa

Si queremos seguir usando la función de activación pero parametrizarla, podemos hacerlo transformandola en una capa de activación con la clase tf.keras.layers.Lambda

model.add(Dense(3, input_dim=1))
model.add(tf.keras.layers.Lambda(lambda x: tf.keras.activations.relu(x,alpha=0.3)))

Notar que el parámetro se llama distinto en la función que en la capa. En la función se llama alpha y en la capa se llama negative_slope. No se el porqué de dicha discrepancia

La lista competa en Keras de funciones de activaciones y capas de activación es la siguiente:

Tipos de funciones de activación en capas ocultas

Pasemos ahora a ver las distintas funciones de activación que se pueden usar en las capas ocultas. Por cuestiones didácticas, se han agrupado por similitud

Realmente hay mas funciones de activación y en cualquier momento se proponen nuevas pero aquí nos hemos limitado a las que mas se usan o que históricamente se han usado

Mas información:

Sigmoide y Tangente Hiperbólica (tanh)

Son funciones de activación que tiene forma de "S". Se pueden usar en capas ocultas como de salida.

Sigmoide es la primera función de activación que se explica.Si rango de salida es de 0 a 1. Ya no se usa nunca en capas ocultas.

$$sigmoid(x)=\frac{1}{1+e^{-x}}$$

tanh es una mejora de la sigmoide. La ventaja es que su salida está centrada en 0 ya que salida está en el rango de -1 a 1. Las tarjetas gráficas actuales, pueden calcula la tanh en una única instrucción tanh.approx.f32

$$senh(x)=\frac{e^x - e^{-x}}{2}$$ $$cosh(x)=\frac{e^x + e^{-x}}{2}$$

$$tanh(x) = \frac{senh(x)}{cosh(x) }=\frac{e^x - e^{-x}}{2} : \frac{e^x + e^{-x}}{2}=\frac{2 \cdot (e^x - e^{-x})}{2 \cdot (e^{x} + e^{-x})}=\frac{e^x - e^{-x}}{e^{x} + e^{-x}}$$

Un problema de estas dos funciones es que para valores muy grandes o muy pequeños dan siempre el mismo valor.

model.add(Dense(3, activation="sigmoid"))
model.add(Dense(3, activation=tf.keras.activations.sigmoid))

model.add(Dense(3, activation="tanh"))
model.add(Dense(3, activation=tf.keras.activations.tanh))

Mas información:

ReLU y Leaky ReLU

ReLU: Es mejor que las anteriores ademas de ser muy rápida ya que prácticamente no hace falta calcular nada. Sin embargo tiene un problema con valores negativos que siempre retorna 0.

$$ ReLu(x)=\left\lbrace \begin{matrix} x>0 & x\\ x\leq 0 & 0 \end{matrix} \right. $$

Leaky ReLU: Es una mejora de ReLU al hacer que los valores negativos tengan un valor menor que cero. Sigue siendo muy rápida de calcular.

$$ LeakyReLU(x)=\left\lbrace \begin{matrix} x>0 & x\\ x\leq 0 & \alpha x \end{matrix} \right. $$

model.add(Dense(3, activation="relu"))
model.add(Dense(3, activation=tf.keras.activations.relu))
model.add(Dense(3, activation=tf.keras.layers.ReLU()))

model.add(Dense(3, activation="LeakyReLU"))
model.add(Dense(3, activation=tf.keras.layers.LeakyReLU()))
model.add(Dense(3, activation=tf.keras.layers.LeakyReLU(alpha=0.2)))

Mas información:

ELU y SELU

Son unas mejora de las anteriores pero tienen que problema de que son muchas lentas de calcular.

ELU:Es similar a ReLU pero cuando cuando la entrada es negativa, hace una caída mas suave para luego estabilizarse. Tiene un parámetro para controlar la caída llamado alfa cuyo valor por defecto $\alpha=1$

$$ ELU(x)=\left\lbrace \begin{matrix} x>0 & x\\ x\leq 0 & \alpha (e^{x}-1) \end{matrix} \right. $$

SELU: Es similar a ELU pero mejor que ella.Lo que hace es modificar ligeramente la formula estableciendo el valor de $\alpha=1.67326324$ y multiplicando la salida por el factor de escala $scale=1.05070098$. Por lo tanto al ser casi como ELU por lo que su velocidad es prácticamente la misma y ser mejor que ELU se recomienda usar SELU. Sin embargo para que SELU sea mejor que ELU se tienen que cumplir una serie de precondiciones:

$$ SELU(x)=1.05070098 \cdot \left\lbrace \begin{matrix} x>0 & x\\ x\leq 0 & 1.67326324 \cdot (e^{x}-1) \end{matrix} \right. $$

model.add(Dense(3, activation="elu"))
model.add(Dense(3, activation=tf.keras.activations.elu))
model.add(Dense(3, activation=tf.keras.layers.ELU(alpha=0.2)))

model.add(Dense(3, activation="selu"))
model.add(Dense(3, activation=tf.keras.activations.selu))

Mas información:

Selección de función de activación

¿Que función de activación deberíamos usar entonces para las capas ocultas?

Según Hands-On Machine Learning with Scikit-Learn and TensorFlow:

$$SELU>ELU>Leaky ReLU>ReLU>Tanh>Sigmoid$$

Sin embargo según Activation functions you might have missed se debería seguir este esquema:

Pero mi opinión es la siguiente:

Tipos de funciones de activación en la capa de salida

En el apartado anterior hemos visto las funciones de activación que se usan en las capas ocultas, ahora vamos a ver las que se pueden usar en la capa de salida.

Los problemas que resuelven las redes neuronales se suelen clasificar en:

Según el tipo de problema se usarán unas funciones u otras:

Lineal

La función de activación lineal es como si realmente no hubiera función de activación.

$$lineal(x)=x$$

La forma de usarla en keras es simplemente no diciendo nada sobre la función de activación en la capa de salida.

La función lineal se usa en problemas de regresión, cuando la salida puede ser cualquier número. Ejemplos de ello son:

model.add(Dense(3, activation="linear"))
model.add(Dense(3, activation=tf.keras.activations.linear))

Sigmoide

Ya hemos visto la función de activación sigmoide

$$sigmoide(x)=\frac{1}{1+e^{-x}}$$

La función Sigmoide se usa en problemas de clasificación, cuando la salida es una probabilidad entre 0 y 1. Ejemplos de ello:

Es decir se puede usar para clasificar entre 2 opciones pero también se puede usar cuando son más de dos opciones pero que no sean excluyentes entre ellas.

Antes vimos que como capa oculta, sigmoide era la peor pero en una capa de salida es muy útil.
Destacar que cuando tenemos varias salidas , los datos de entrada, es decir la y_true deben estar preparados para ello. Por ejemplo en la red de las flores, cada flor es un número. Pero deberíamos usar la función OneHotEncoder para transformarlo en columnas distintas para cada tipo de flor

model.add(Dense(3, activation="sigmoid"))
model.add(Dense(3, activation=tf.keras.activations.sigmoid))

Softmax

Se usa en problemas de clasificación. La función Softmax no es fácil de representar gráficamente pero vamos a intentar explicarla.

En vez de aplicarse a una única neurona se aplica a todas las neuronas de una capa. Es similar a la sigmoide pero lo que hace es que la probabilidad de todas las salidas de la capa debe sumar 1.

$$ softmax(x_i)=\frac{e^{x_i}}{\sum\limits_{j=0}^{n}e^{x_j}} $$

Siendo $x_i$ la entrada la la neurona i-esioma y siendo $x_j$ todas las entradas a todas las neuronas de la capa habiendo $n$ neuronas en la capa. Por lo que $x_i$ corresponde con uno de los valores de $x_j$

Ejemplos de ello:

La explicación es similar a la sigmoide pero vamos a ver con ejemplos las diferencias.

En este caso la red neuronal tendría 3 neuronas en la capa de salida. Una para cada uno de los objetos que puede detectar.

¿Pueden las 3 neuronas sacar cada una un 1 porque están los 3 objetos? En ese caso debemos usar la función sigmoide.

En este caso la red neuronal también tendría 3 neuronas en la capa de salida. Una para cada uno de los objetos que puede detectar.

¿Pueden las 3 neuronas sacar cada una un 1 porque están los 3 objetos? No porque solo puede ser uno de los 3 objetos, por lo tanto hay que usar la función de activación softmax ya que de esa forma indicamos cual de los 3 objetos es mas probable pero la suma de las 3 probabilidades debe sumar 1.

Es decir que si las neuronas de la capa de salida son resultados independientes de las otras neuronas , usamos sigmoide pero si sus salidas están relacionadas con las otras neuronas, usaremos softmax

Destacar que cuando tenemos varias salidas , los datos de entrada, es decir la y_true deben estar preparados para ello. Por ejemplo en la red de las flores, cada flor es un número. Pero deberíamos usar la función LabelBinarizer para transformarlo en columnas distintas para cada tipo de flor

from sklearn.preprocessing import LabelBinarizer

y=iris.target
label_binarizer = LabelBinarizer()
label_binarizer.fit(range(max(y)+1))
y = label_binarizer.transform(y)

model.add(Dense(3, activation="softmax"))
model.add(Dense(3, activation=tf.keras.activations.softmax))

Mas información:

Guardando modelos a disco

Una vez tenemos la red neuronal entrenada, la podemos guardar a disco para poder usarla en otro programa.


model=Sequential()
model.add(Dense(10, activation="sigmoid",input_dim=2))
model.compile(loss="mse")
history=model.fit(x_train,y_train,validation_data=(x_test,y_test),epochs=10,verbose=False)


model.save('my_red_neuronal.keras') 

model=tf.keras.models.load_model('my_red_neuronal.keras')

model=tf.keras.models.load_model('my_red_neuronal.keras',custom_objects={"specificity": specificity})

Más información:

Redes Neuronales Famosas

Antes hemos vito el tamaño de una red neuronal, ¿Que tamaño tienen las redes neuronales mas famosas? Lo podemos ver en el siguiente gráfico:

Año Red Nº Parámetros Creador
2004 Lira 100.000
2007 λ-WASP 490.000
2008 Semantic Hashing 2.600.000
2010 NORB 16.000.000
2012 AlexNet 60.000.000
2014 Seq2Seq 380.000.000
2017 MoE 8.700.000.000
2019 DLRM-2020 100.000.000.000
2020 GPT-3 175.000.000.000 OpenAI
2021 Gopher 280.000.000.000 DeepMind (Google)
2021 Megatron-Turing NLG 530.000.000.000 Microsoft & NVIDIA
2021 DLRM-12T 12.000.000.000.000

¿Y con que hardware se hace todo eso? La red neuronal StyleGAN3 que genera imágenes de caras , tiene 530 mil millones de parámetros. NVIDIA uso 560 ordenadores DGX A100, teniendo cada uno de ellos con 8 tarjetas gráficas A100 con 80GB de RAM cada una. Por lo tanto se usaron 4.480 gráficas y un total de 358.400 GB de memoria.

¿Y cuales son las empresas punteras en inteligencia artificial?: Artificial intelligence researchers rank the top A.I. labs worldwide

ChatGPT forma parte de la familia de modelos basados en GPT-3 pero especializado en conversaciones como se puede ver en la siguiente imagen:

Además ChatGPT no es de los modelos con más parámetros como podemos ver a continuación:

Por último vemos el tamaño que tendrá GPT4:

Mas información:

Ejercicios

Ejercicio 1.A

Dada la siguiente red neuronal:

Ejercicio 1.B

Modifica la red anterior pero ahora.

Ejercicio 1.C

Modifica la red anterior pero ahora.

Ejercicio 2.A

Dada la siguiente red neuronal:

Ejercicio 2.B

Modifica la red anterior pero ahora.

Ejercicio 2.C

Modifica la red anterior pero ahora.

Ejercicio 3

Dada la siguiente red neuronal:

Ejercicio 4.A

Dada la siguiente red neuronal:

¡¡¡Acabas de comprobar como funciona internamente una red neuronal!!!

Ejercicio 4.B

Repite el ejercicio anterior pero antes de obtener los parámetros, entrena la red con los valora del largo del sépalo y el largo del pétalo.

Y comprueba si los resultados coinciden con todas las flores de tipo 0 y tipo 1, para ello deberás hacer un pequeño programa que calcule el resultado de la fórmula.

Ejercicio 5.A

Con los datos de las flores y la configuración de neuronas [2,4,8,4,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 5.B

Con los datos de las flores y la configuración de neuronas [2,4,8,4,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 5.C

Con los datos de las flores y la configuración de neuronas [2,4,8,4,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 5.D

Con los datos de las flores y la configuración de neuronas [2,4,8,4,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 5.E

Con los datos de las flores y la configuración de neuronas [2,4,8,4,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 5.F

Con los datos de las flores y la configuración de neuronas [2,4,8,4,2,1] crea una red neuronal cuyas capas ocultas:

Indica además que inicializador usarías con ella

Ejercicio 6.A

Con los datos del cáncer de mama crea una red neuronal de forma que

¿Que dice la teoría de cual se debería haber usado? ¿Ha habido alguna diferencia?

Ejercicio 6.B

Repita el ejercicio anterior pero ahora con las siguientes capas:

[5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,70,65,60,55,50,45,40,35,30,25,20,15,10,5,1]

¿Que dice la teoría de cual se debería haber usado? ¿Ha habido alguna diferencia?

Ejercicio 7

Con los datos del cáncer de mama crea una red neuronal con las siguientes capas [16,32,64,127,64,32,16,8,1] variando cada vez la función de activación y mostrando una gráfica con su función de perdida. el nº de épocas será de 100. Y se usará el inicializador glorot_uniform

Responde:

Ejercicio 8

Para cada uno de los problemas siguientes de redes neuronales:

Indica:

Ejercicio 9.A

Crea y entrena una red neuronal con los datos de las flores pero ahora ya deberás tener en cuenta los 3 tipos de flor.

Deberás:

Ejercicio 9.B

Crea y entrena una red neuronal que averigüe el tipo de un vino. Los datos los obtendrás con la función load_wine.

from sklearn.datasets import load_wine

Deberás:

Ejercicio 9.C

Crea y entrena una red neuronal que averigüe el dígito que ha escrito una persona. Los datos los obtendrás con la función load_digits.

from sklearn.datasets import load_digits

Deberás:

Ejercicio 9.D

Crea y entrena una red neuronal que averigüe la "cantidad" (es un número) de diabetes que tendrá un paciente. Los datos los obtendrás con la función load_diabetes.

from sklearn.datasets import load_diabetes

Deberás:

Ejercicio 10

Crea y entrena una red neuronal que averigüe el tipo de vino. Los datos los obtendrás con la función load_wine.

from sklearn.datasets import load_wine

Deberás:

Nº Neuronas en cada capa
2,4,3
4,8,3
8,16,8,3
4,8,4,3
8,16,8,3
16,32,16,8,3
32,64,32,8,3
64,128,64,8,3
8,16,32,64,32,16,8,3