#!/usr/bin/env python
# coding: utf-8

# # Problema clasificación con RNA y Keras

# In[103]:


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


# In[52]:


# Para obtener todos el mismo resultado debemos añadir una semilla
from numpy.random import seed
seed(1)
from tensorflow import random
random.set_seed(1)


# In[53]:


# Cargamos el conjunto de datos
df = pd.read_csv("./datos/diabetes.csv", sep=",", header=None)


# In[54]:


df.info()


# In[55]:


df.head()


# Significado de las columnas:
# 
#  0. Number of times pregnant
#  1. Plasma glucose concentration a 2 hours in an oral glucose tolerance test
#  2. Diastolic blood pressure (mm Hg)
#  3. Triceps skin fold thickness (mm)
#  4. 2-Hour serum insulin (mu U/ml)
#  5. Body mass index (weight in kg/(height in m)^2)
#  6. Diabetes pedigree function
#  7. Age (years)
#  8. Variable clase (Tiene diabetes)

# In[135]:


# Dividimos los datos en X e y
X = df.iloc[:,0:8]
y = df.iloc[:,8]


# In[136]:


from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
                                        X,
                                        y,
                                        train_size   = 0.7,
                                        random_state = 123,
                                        shuffle      = True
                                    )


# ### Definimos el modelo Keras

# In[137]:


# Definiremos el modelo como una secuencia de capas.
# Usaremos el modelo secuencial de manera que podamos ir añadiendo capas hasta estar contentos con la arquitectura desarrollada.
model = Sequential()

# Partimos de un sistema con 8 variables por lo que nuestra primera capa acomodará dichas variables
# En la primera capa oculta usaremos 12 neuronas y una función de activación ReLU
# En la segunda capa oculta usaremos 8 neuronas y una función de activación ReLU
# Finalmente en la de salida una neurona y función de activación sigmoide
model.add(Dense(12, input_dim=8, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

# Nota: Fíjate que el total de neuronas de entrada, lo definimos en la primera capa con input_dim


# ### Compilamos el modelo

# In[144]:


# La compilación usa (internamente) librerías numéricas muy eficientes como TensorFlow además de comprobar si tenemos GPU o sólo CPU
# Es necesario definir la función de pérdida que vamos a minimizar (optimizar).  Para este caso minimizaremos Binary Cross Entropy puesto
# que funciona bien para problemas binarios de clasificación.
# Como métrica (al ser clasificación) usaremos la precisión
# Como optimizador, usaremos el algoritmo "adam" ya que ofrece buenos resultados en un amplio abanico de problemas y además de manera rápida

model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
              #loss="binary_crossentropy",
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=False, # para valores de predicción [0,1]
                                                      label_smoothing=0.0,
                                                      axis=-1,
                                                      reduction="auto",
                                                      name="binary_crossentropy"),
              metrics=['accuracy'])


# ### Entrenamos el modelo

# In[145]:


seed(1)
random.set_seed(1)
# El argumento batch_size, nos permite definir el número de filas que se considerarán, antes de que los pesos del
# modelo se reajusten dentro de cada ciclo.
model.fit(X_train, y_train, epochs=150, batch_size=10)


# ### Evaluamos el modelo

# In[146]:


# Con la red neuronal entrenada, ahora debemos evaluar cómo ha funcionado.
_, accuracy = model.evaluate(X_train, y_train)
print('Accuracy: %.2f' % (accuracy*100))


# In[74]:


# No es un mal resultado, tenemos una precisión de más del 80%


# ### Predicciones

# In[147]:


predicciones = model.predict(X_test)

# La función sigmoide nos devueve los resultados en formato probabilidad.
# Convertimos los mismos a casos, tomando como umbral 0.5
y_pred = (predicciones > 0.5).astype(int)
y_pred


# In[148]:


from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_pred)


# ## Ejercicios

# 1.Configura la red neuronal para que trabaje con 4 capas. 
# - La primera con 15 neuronas y función de activación sigmoide. 
# - La segunda con 10 neuronas y función de activación sigmoide.
# - La tercera con 8 neuronas y función de activación ReLU.
# - La cuarta con 1 neurona y función de activación sigmoide.

# 2.Configura la red neuronal para que trabaje con las 3 mismas capas del ejemplo inicial, pero esta vez usa como función de activación de la última capa 'softmax'

# 3.En compile, cambia la configuración del optimizer, de manera que en vez de Adam, usemos esta vez SGD:
# optimizer=tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.1, nesterov=False, name='SGD')

# 4.En compile, cambia la configuración del loss para usar como función de pérdida CategoriclCrossentropy.
# 
# tf.keras.losses.CategoricalCrossentropy(
#     from_logits=False,
#     label_smoothing=0.0,
#     axis=-1,
#     reduction="auto",
#     name="categorical_crossentropy",
# )

# 4.Prueba diferentes configuraciones a ver si consigues mejorar el resultado inicial.
