Herramientas de usuario

Herramientas del sitio


clase:iabd:pia:2eval:tema07-apendices

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
clase:iabd:pia:2eval:tema07-apendices [2022/03/17 19:58]
admin [Backpropagation]
clase:iabd:pia:2eval:tema07-apendices [2024/03/19 17:04] (actual)
admin [Más métricas]
Línea 1: Línea 1:
-====== 7. Entrenamiento de redes neuronales d) Apéndices ======+====== 7. Entrenamiento de redes neuronales e) Apéndices ======
  
 ===== Tipos de funciones de coste ===== ===== Tipos de funciones de coste =====
Línea 60: Línea 60:
  
 ===== Creación de los gráficos del descenso de gradiente ===== ===== Creación de los gráficos del descenso de gradiente =====
-Pasemos ahora a ver como se programa todo ésto en python.+Durante el tema hemos visto los gráficos que explican el descenso de gradiente. Veamos ahora como se pueden hacer dichos gráficos en Python.
  
-Como ejemplo vamos a seguir la siguiente función:+La función que coste que hemos usado es la siguiente
  
 $$ $$
-loss=3(1-w_0)^2e^{-w_0^2-(w_1+1)^2}-10(\frac{w_0}{5}-w_0^3-w_1^5)e^{-w_0^2-w_1^2}-\frac{1}{3}e^{-(w_0+1)^2-w_1^2}+loss(w_0,w_1)=3(1-w_0)^2e^{-w_0^2-(w_1+1)^2}-10(\frac{w_0}{5}-w_0^3-w_1^5)e^{-w_0^2-w_1^2}-\frac{1}{3}e^{-(w_0+1)^2-w_1^2}
 $$ $$
  
Línea 73: Línea 73:
  
  
-El siguiente código python es el algoritmo que acabamos de ver para saber por donde iríamos en nuestro viaje del descenso de gradiente.+En python la función $loss(w_0,w_1)$ con **NumPy** sería así:
  
 <sxh python> <sxh python>
 def loss(w_0,w_1): def loss(w_0,w_1):
     return  3*(1 - w_0)**2 * np.exp(-w_0**2 - (w_1 + 1)**2)  - 10*(w_0/5 - w_0**3 - w_1**5)*np.exp(-w_0**2 - w_1**2) - 1./3*np.exp(-(w_0 + 1)**2 - w_1**2)      return  3*(1 - w_0)**2 * np.exp(-w_0**2 - (w_1 + 1)**2)  - 10*(w_0/5 - w_0**3 - w_1**5)*np.exp(-w_0**2 - w_1**2) - 1./3*np.exp(-(w_0 + 1)**2 - w_1**2) 
 +</sxh>
  
 +Y el algoritmo que calcula cada uno de los $w_0,w_1$ del descenso de gradiente es el siguiente:
  
-def get_puntos_descenso_gradiente(w_0_init,w_1_init,learning_rate,steps):+<sxh python> 
 +def get_puntos_descenso_gradiente(epochs,learning_rate,w_0_original,w_1_original):
  
-    puntos_descenso_gradiente=np.array([[w_0_init,w_1_init]])+    w_0=w_0_original 
 +    w_1=w_1_original
  
-    w_0=w_0_init +    puntos_descenso_gradiente=np.array([[w_0,w_1]])
-    w_1=w_1_init+
          
-    for in range(steps): +    for epoch in range(epochs): 
         h=0.00001         h=0.00001
-        derivada_w_0=(loss(w_0+h,w_1)-loss(w_0,w_1))/+         
-        derivada_w_1=(loss(w_0,w_1+h)-loss(w_0,w_1))/       +        gradiente_w_0=(loss(w_0+h,w_1)-loss(w_0,w_1))/
 +        gradiente_w_1=(loss(w_0,w_1+h)-loss(w_0,w_1))/       
  
         #Nuevos valores de los pesos         #Nuevos valores de los pesos
-        w_0=w_0-learning_rate*derivada_w_0 +        w_0=w_0-learning_rate*gradiente_w_0 
-        w_1=w_1-learning_rate*derivada_w_1+        w_1=w_1-learning_rate*gradiente_w_1
  
         puntos_descenso_gradiente=np.append(puntos_descenso_gradiente,[[w_0,w_1]], axis=0)                    puntos_descenso_gradiente=np.append(puntos_descenso_gradiente,[[w_0,w_1]], axis=0)           
Línea 103: Línea 107:
  
  
-Si lo ejecutamos con los siguientes datos +Si ejecutamos lo siguiente:
- +
-\begin{align} +
-w_0 &= -0.35 \\  +
-w_1 &= -0.67 \\ +
-learning \: \: rate &= 0.03 \\ +
-steps &= 5 +
-\end{align} +
- +
- +
-<note tip>La variable ''steps'' dices cuantas veces se ejecuta el algoritmo o cuantos //pasos// debemos dar antes de pararnos. +
-En Keras los ''steps'' lo hemos visto como el parámetro ''epochs'' del método ''fit''. No es exactamente lo mismo pero su objetivo es el mismo</note> +
- +
-Es decir llamando a la función así: +
 <sxh python> <sxh python>
-get_puntos_descenso_gradiente(-0.35,-0.67,0.03,5)+get_puntos_descenso_gradiente(5,0.03,-0.35,-0.67)
 </sxh> </sxh>
  
-Saldría la siguiente imagen:+El resultado es: 
 +<sxh base> 
 +array([[-0.35      , -0.67      ], 
 +       [-0.26931594, -0.72470447], 
 +       [-0.13292324, -0.838827  ], 
 +       [ 0.0654496 , -1.06459902], 
 +       [ 0.24087009, -1.40749396], 
 +       [ 0.2657862 , -1.59573582]]) 
 +</sxh>
  
-{{ :clase:iabd:pia:2eval:descenso_gradiente_algoritmo_vanilla_1.png?direct |}}+Que son cada uno de los valores de $w_0,w_1$ que empiezan en ''-0.35, -0.67'' y acaban en ''0.2657862, -1.59573582$''
  
- +Pasamos ahora a mostrar los valores dentro de la gráfica de la funciónpara ello hemos creado 2 funciones:
-<note> +
-  * La estrella roja es desde donde empieza el algoritmo. +
-  * La estrella azul es donde acaba el algoritmo +
-  * Los puntos amarillo son los //pasos// por lo que se va moviendo el algoritmo +
-</note> +
- +
-Para poder hacer estas gráficas, hemos creado 2 funciones:+
   * ''plot_loss_function'': Que dibuja la superficie con todos los colores   * ''plot_loss_function'': Que dibuja la superficie con todos los colores
-  * ''plot_descenso_gradiente'': Dibuja por donde va pasando el algoritmo del descenso de gradiente+  * ''plot_descenso_gradiente'': Dibuja los puntos por donde va pasando el algoritmo del descenso de gradiente 
  
 <sxh python> <sxh python>
Línea 158: Línea 150:
 </sxh> </sxh>
  
 +Si ejecutamos el código:
  
 +<sxh python>
 +figure=plt.figure(figsize=(16,15))
 +axes = figure.add_subplot()
 +plot_loss_function(axes)
 +
 +plot_descenso_gradiente(axes,get_puntos_descenso_gradiente(5,0.03,-0.35,-0.67))
 +</sxh>
 +
 +Vemos la siguiente gráfica donde se muestran los puntos que hemos obtenido de la función ''get_puntos_descenso_gradiente''
 +
 +{{ :clase:iabd:pia:2eval:descenso_gradiente_algoritmo_vanilla_1.png?direct |}}
 +
 +
 +<note>
 +  * La estrella roja es desde donde empieza el algoritmo.Es decir, el valor inicial de los parámetros $w_0,w_1$
 +  * La estrella azul es donde acaba el algoritmo.Es decir, el valor tras el entrenamiento de los parámetros $w_0,w_1$
 +  * Los puntos amarillo son los //pasos// por lo que se va moviendo el algoritmo. Cada uno de los valores intermedios de los parámetros durante el entrenamiento.
 +</note>
 +
 +
 +==== Optimizadores de Keras ====
 + Podemos mejorar nuestro código en Python haciendo que podamos usar directamente los [[https://keras.io/api/optimizers/|Optimizers]] de Keras y de esa forma ver como funciona cada uno de ellos. Para poder usarlos directamente vamos a creas las nuevas funciones ''loss_tf'' y ''get_puntos_descenso_gradiente_optimizer'' adecuadas a TensorFlow y Keras.
  
-Y ahora el programa que usa las 4 funciones y que  dibuja la gráfica es el siguiente: 
  
 +<sxh python>
 +def loss_tf(w_0,w_1):
 +    return  3*(1 - w_0)**2 * tf.exp(-w_0**2 - (w_1 + 1)**2)  - 10*(w_0/5 - w_0**3 - w_1**5)*tf.exp(-w_0**2 - w_1**2) - 1./3*tf.exp(-(w_0 + 1)**2 - w_1**2) 
 +
 +def get_puntos_descenso_gradiente_optimizer(epochs,optimizer_function,w_0_init,w_1_init):
 +
 +    puntos_descenso_gradiente=np.array([[w_0_init,w_1_init]])
 +
 +    w_0=w_0_init
 +    w_1=w_1_init
 +    for epoch in range(epochs): 
 +        var_w_0=tf.Variable(w_0)
 +        var_w_1=tf.Variable(w_1)
 +
 +        optimizer_function.minimize(lambda: loss_tf(var_w_0,w_1),  var_list=[var_w_0])
 +        optimizer_function.minimize(lambda: loss_tf(w_0,var_w_1),  var_list=[var_w_1])
 +
 +        w_0=var_w_0.numpy()
 +        w_1=var_w_1.numpy()      
 +
 +        puntos_descenso_gradiente=np.append(puntos_descenso_gradiente,[[w_0,w_1]], axis=0)           
 +
 +    return puntos_descenso_gradiente
 +</sxh>
 +
 +Lo que ha cambiado principalmente es la función ''get_puntos_descenso_gradiente_optimizer''  no hace cálculo  del gradiente (derivada) ni actualiza los parámetros, sino que llama a la función de Keras de optimización.
 +
 +
 +Si usamos ''get_puntos_descenso_gradiente_optimizer'' ahora con ''tf.keras.optimizers.SGD''
 +<sxh python>
 +get_puntos_descenso_gradiente_optimizer(5,tf.keras.optimizers.SGD(learning_rate=0.03),-0.35,-0.67)
 +</sxh>
 +<sxh base>
 +array([[-0.35      , -0.67      ],
 +       [-0.269319  , -0.72470832],
 +       [-0.13292952, -0.83883744],
 +       [ 0.06544246, -1.06462073],
 +       [ 0.24086528, -1.40752137],
 +       [ 0.26578248, -1.59573841]])
 +</sxh>
 +Vamos que el resultado es prácticamente el mismo que cuando lo hicimos manualmente.
 +
 +Y podemos generar de la misma forma el gráfico:
 <sxh python> <sxh python>
 figure=plt.figure(figsize=(16,15)) figure=plt.figure(figsize=(16,15))
Línea 167: Línea 224:
 plot_loss_function(axes) plot_loss_function(axes)
  
-plot_descenso_gradiente(axes,get_puntos_descenso_gradiente(-0.35,-0.67,0.03,5))+plot_descenso_gradiente(axes,get_puntos_descenso_gradiente_optimizer(5,tf.keras.optimizers.SGD(learning_rate=0.03),-0.35,-0.67))
 </sxh> </sxh>
 +
 +
 +{{ :clase:iabd:pia:2eval:optimizer_sgd.png?direct |}}
 +
 +Y obviamente el resultado es el mismo
 +
 +===== Métricas =====
 +Las métricas que ya las hemos explicado en el tema de métricas son:
 +    * Métricas básicas (Sensibilidad, Especificidad, FNR y FPR)
 +    * Métricas derivadas según el teorema de bayes (PPV,NPV, FDR y FOR)
 +
 +Las métricas que ahora vamos a ver son métricas que hacen la media entre alguna de las dos métricas que acabamos de indicar.
 +
 +Para organizar las métricas según 2 criterios:
 +  * Según que métricas juntan
 +    * Métricas básicas (Sensibilidad, Especificidad, FNR y FPR)
 +    * Métricas derivadas (PPV,NPV, FDR y FOR)
 +    * Métricas mixtas, que usa una básica y otra derivada.
 +  * Según la fórmula que usan:
 +    * Media aritmética
 +    * Media armónica
 +    * Media geométrica
 +    * L1-Norma
 +    * L2-Norma
 +    * Ratio
 +==== Juntado dos Métricas Básicas ====
 +^  ^  Fórmula que usan  ^^^^^^
 +^  Métricas básicas que usan  ^  Media aritmética  ^  Media armónica  ^  Media geométrica  ^  L1-Norma  ^  L2-Norma  ^  Ratio  ^
 +| Sensibilidad (TPR) y Especificidad (TNR)  |    |    |    |  $Informedness=TPR+TNR-1$  |     |
 +| Sensibilidad (TPR) y FPR  |    |    |    |    |    $Positive \; likelihood \; ratio=\frac{TPR}{FPR}$ |
 +| Especificidad (TNR) y FNR  |    |    |    |    |    $Negative \; likelihood \; ratio=\frac{FNR}{TNR}$ |
 +| FPR y FNR  |    |    |     |
 +
 +
 +==== Juntado dos Métricas derivadas ====
 +
 +^  ^  Fórmula que usan  ^^^^^^
 +^  Métricas básicas que usan  ^  Media aritmética  ^  Media armónica  ^  Media geométrica  ^  L1-Norma  ^  L2-Norma  ^  Ratio  ^
 +| PPV y NPV  |    |    |    |  $Markdness=PPV+NPV-1$ |    |   |
 +| PPV y FOR  |    |    |    |        |
 +| NPV y FDR  |    |    |    |        |
 +| FDR y FOR  |    |    |    |        |
 +
 +
 +==== Métricas mixtas ====
 +Son métricas que juntan una métrica básica con una métrica derivada. Debido a que existen 16 combinaciones no vamos a mostrar todas las que existen, sino solo las que he considerado interesantes.
 +
 +  * La siguiente tabla son métricas que existen (Tienen nombre)
 +
 +^  ^  Fórmula que usan  ^^^^^^
 +^  Métricas básicas que usan  ^  Media aritmética  ^  Media armónica  ^  Media geométrica  ^  L1-Norma  ^  L2-Norma  ^  Ratio  ^
 +| PPV y Sensibilidad (TPR)  |    |  $F_{1}score=\frac{2}{\frac{1}{PPV}+\frac{1}{TPR}}$  |  $Fowlkes-Mallows \; index=\sqrt{PPV*TPR}$  |        |
 +| NPV y Especificidad (TNR)  |    |    |    |        |
 +
 +===== Más métricas =====
 +
 +
 +==== Indice Jaccard ====
 +Este índice es la división entre 2 probabilidades:
 +
 +$$
 +Indice \; Jaccard=\frac{P(Positivo \cap Enfermo)}{P(Positivo \cup Enfermo)}=\frac{TP}{TP+FP+FN}
 +$$
 +
 +  * Se deduce de la siguiente forma:
 +
 +$$
 +\frac{P(Positivo \cap Enfermo)}{P(Positivo \cup Enfermo)}=
 +$$
 +
 +$$
 +\frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Positivo)+P(Enfermo)-P(Positivo \cap Enfermo)}=\frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Positivo)+P(Enfermo)-P(Positivo|Enfermo)*P(Enfermo)}
 +$$
 +
 +  * Sabiendo que:
 +
 +$$
 +
 +\begin{array}
 +\\
 +P(Enfermo)&=&\frac{TP+FN}{TP+FN+FP+TN}
 +\\
 +
 +
 +
 +P(Sano)&=&\frac{FP+TN}{TP+FN+FP+TN}
 +\\
 +P(Positivo)&=&\frac{TP+FP}{TP+FN+FP+TN}
 +\\
 +P(Negativo)&=&\frac{FN+TN}{TP+FN+FP+TN}
 +\\
 +P(Positivo|Enfermo)&=&\frac{TP}{TP+FN}
 +\end{array}
 +$$
 +
 +  * Entonces:
 +
 +$$
 +\frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Positivo)+P(Enfermo)-P(Positivo|Enfermo)*P(Enfermo)}=
 +$$
 +
 +$$
 +\left ( \frac{TP}{TP+FN}*\frac{TP+FN}{TP+FN+FP+TN} \right ) \div    \left (\frac{TP+FP}{TP+FN+FP+TN}+\frac{TP+FN}{TP+FN+FP+TN}-\frac{TP}{TP+FN}*\frac{TP+FN}{TP+FN+FP+TN} \right )=
 +$$
 +
 +$$
 +\left ( \frac{TP}{TP+FN+FP+TN} \right ) \div    \left (\frac{TP+FP}{TP+FN+FP+TN}+\frac{TP+FN}{TP+FN+FP+TN}-\frac{TP}{TP+FN+FP+TN} \right )=
 +$$
 +
 +$$
 +\left ( \frac{TP}{TP+FN+FP+TN} \right ) \div    \left (\frac{TP+FP+TP+FN-TP}{TP+FN+FP+TN} \right )=\left ( \frac{TP}{TP+FN+FP+TN} \right ) \div    \left (\frac{TP+FP+FN}{TP+FN+FP+TN} \right )=
 +$$
 +
 +$$
 +\frac{TP}{TP+FP+FN}=Indice \; Jaccard
 +$$
 +
 +  * Sin embargo también podemos definir el Indice Jaccard en función de la sensibilidad, la especificidad y la prevalencia.Usando el teorema de bayes podemos definir P(Positivo) de la siguiente forma:
 +
 +$$
 +P(Positivo)=\frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Enfermo|Positivo)}=
 +$$
 +
 +$$
 +\frac{P(Positivo|Enfermo)*P(Enfermo)}{1} \div \frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Positivo|Enfermo)*P(Enfermo)+P(Positivo|Sano)*P(Sano)}=
 +$$
 +
 +$$
 +Sensibilidad*Prevalencia+(1-Especificidad)*(1-Prevalencia)
 +$$
 +
 +
 +  * Y ahora usamos la formula de P(Positivo) en la definición del Indice Jaccard
 +
 +$$
 +Indice \; Jaccard=\frac{P(Positivo|Enfermo)*P(Enfermo)}{P(Positivo)+P(Enfermo)-P(Positivo|Enfermo)*P(Enfermo)}=
 +$$
 +
 +$$
 +\frac{Sensibilidad*Prevalencia}{Sensibilidad*Prevalencia+(1-Especificidad)*(1-Prevalencia)+Prevalencia-Sensibilidad*Prevalencia}=
 +$$
 +
 +$$
 +\frac{Sensibilidad*Prevalencia}{(1-Especificidad)*(1-Prevalencia)+Prevalencia}
 +$$
 +
 +  * Por lo tanto
 +
 +$$
 +Indice \; Jaccard=\frac{Sensibilidad*Prevalencia}{(1-Especificidad)*(1-Prevalencia)+Prevalencia}
 +$$
 +
 +
 +
 +==== Prevalence threshold ====
 +La métrica de Prevalence threshold está explicada en [[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7540853/|Prevalence threshold (ϕe) and the geometry of screening curves]].
 +
 +Lo único que diremos respecto a la formula es que en el artículo aparece como:
 +
 +$$
 +Prevalence \; threshold=\frac{\sqrt{Sensibilidad(1-Especificidad)}+(Especificidad-1)}{Sensibilidad+Especificidad+1}
 +$$
 +Que jugando un poco con los signos se obtiene la formula equivalente que aparece en Wikipedia:
 +$$
 +Prevalence \; threshold=\frac{\sqrt{Sensibilidad*FPR}-FPR}{Sensibilidad-FPR}
 +$$
 +
 +==== Diagnostic odds ratio ====
 +Se define como la división entre //Positive likelihood ratio (LR+)// y  //Negative likelihood ratio (LR-)//
 +
 +$$
 +DOR=\frac{LR+}{LR-}=\frac{TP*TN}{FP*FN}
 +$$
 +
 +  * Aunque también se puede definir en función de la sensibilidad y la especificidad
 +
 +$$
 +DOR=\frac{LR+}{LR-}=\frac{\frac{TPR}{1-TNR}}{\frac{1-TPR}{TNR}}=\frac{Sensibilidad*Especificidad}{(1-Sensibilidad)(1-Especificidad)}
 +$$
 +
 +==== Matthews Correlation Coefficient o MMC ====
 +Es otra métrica pero que tiene en cuenta que los datos no estén balanceados. 
 +
 +El MMC tiene un valor entre -1 a 1. Siendo:
 +  * 1 : El clasificador funciona perfectamente
 +  * 0 : El clasificador acierta aleatoriamente
 +  * -1 : El clasificador acierta peor que aleatoriamente, es decir que clasifica al revés "perfectamente"
 +
 +$$MCC = \frac{ \mathit{TP} \times \mathit{TN} - \mathit{FP} \times \mathit{FN} } {\sqrt{ (\mathit{TP} + \mathit{FP}) ( \mathit{TP} + \mathit{FN} ) ( \mathit{TN} + \mathit{FP} ) ( \mathit{TN} + \mathit{FN} ) } }$$
 +
 +
 +Podemos hacer uso de la métrica con la función ''sklearn.metrics.matthews_corrcoef'' de sklearn
 +
 +Ejemplo de uso:
 +<sxh python>
 +from sklearn.metrics import matthews_corrcoef
 +
 +y_true = [1,1,1,1,0,0,0,0]
 +y_pred = [1,1,1,1,0,0,0,0]
 +print("Valor para una predicción que acierta siempre=",matthews_corrcoef(y_true,y_pred))
 +
 +y_true = [1,1,1,1,0,0,0,0]
 +y_pred = [1,1,0,0,1,1,0,0]
 +print("Valor para una predicción que acierta la mitad=",matthews_corrcoef(y_true,y_pred))
 +
 +y_true = [1,1,1,1,0,0,0,0]
 +y_pred = [0,0,0,0,1,1,1,1]
 +print("Valor para una predicción que nunca acierta=",matthews_corrcoef(y_true,y_pred))
 +</sxh>
 +
 +
 +<sxh base>
 +Valor para una predicción que acierta siempre= 1.0
 +Valor para una predicción que acierta la mitad= 0.0
 +Valor para una predicción que nunca acierta= -1.0
 +</sxh>
 +
 +
 +
 +
 +
 +Mas información:
 +  * [[https://scikit-learn.org/stable/modules/generated/sklearn.metrics.matthews_corrcoef.html|sklearn.metrics.matthews_corrcoef]]
 +  * [[https://towardsdatascience.com/the-best-classification-metric-youve-never-heard-of-the-matthews-correlation-coefficient-3bf50a2f3e9a|Matthews Correlation Coefficient is The Best Classification Metric You’ve Never Heard Of]]
 +  * [[https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5721660/|Ten quick tips for machine learning in computational biology]]
 +  * [[https://medium.com/@cdefaux/phi-coefficient-a-k-a-matthews-correlation-coefficient-binary-classification-11e2c29db91e|Phi Coefficient A.K.A Matthews Correlation Coefficient (Binary Classification)]]
 +  * {{ :clase:iabd:pia:2eval:the_advantages_of_the_matthews_correlation_coeffic.pdf |The advantages of the Matthews correlation coefficient (MCC) over F1 score and accuracy in binary classification evaluation}}
 +  * [[https://en.wikipedia.org/wiki/Phi_coefficient|Phi coefficient-Wikipedia]]
 +
  
  
clase/iabd/pia/2eval/tema07-apendices.1647543528.txt.gz · Última modificación: 2022/03/17 19:58 por admin