<jupyter_start><jupyter_text>Esta fase de la metdologia consiste en extraer el valor de los datos desarrollando un modelo que aprenda de los patrones en los datos.* El diagrama en cuestion de esta fase esta a continuacion: ![Imagen_mapa](imagen_modelado_general.png)  Selección de tecnicas de ModeladoAl tratarse de un problema de clasificacion de imagenes entre los posibles candidatos tenemos:* MultiLayer Pereptron: Red neuronal de capas densamente conectadas* Convolutional Neuronal Network: Red neuronal convolucional.* Modelos de machine learning clasico (Maquinas de soporte vectorial, arboles de decision e impulso, etc.)Escogi la red neuronal convolucional porque **aprende de patrones locales** como rasgos pequeños y en bloques de informacion, mientras que el **MLP** aprende de patrones especficos, e decir de todo el espacio de entrada en general.  Construcción de la arquitectura del modelo* Para la construccion de la arquitectura aplicaremos Transfer Learning de la arquitectura **MobileNETV2** y la acomplaremos anuestras capas personalizadas para el problema.![imagen_acoplamiento](imagen_modelado_construccion.png)<jupyter_code>import tensorflow as tf
from tensorflow.keras.utils import plot_model<jupyter_output><empty_output><jupyter_text>* Implementamos la arquitectura de MobileNet V2, por ser liviana y consumir menos recursos.<jupyter_code>INPUT_SHAPE=(256,256,3) #256x256  pixeles, y 3 canales de colores(R,G,B)

model_mobilenetv2=tf.keras.applications.MobileNetV2(
                                            input_shape=INPUT_SHAPE,
                                            weights="imagenet", #obtener los pesos del entrenamiento con las imagenes ImageNetV3
                                            include_top=False, #no obtener el clasificador final (LO PERSONALIZAMOS),
                                )<jupyter_output>WARNING:tensorflow:`input_shape` is undefined or non-square, or `rows` is not in [96, 128, 160, 192, 224]. Weights for input shape (224, 224) will be loaded as the default.<jupyter_text>Arquitectura del modelo adaptado* Capa de entrada **Input** para las imagenes.* Capa de Modelo **MobileNetv2** para hacer **Transfer Learning**.* Capa MaxAveragePooling.* Capa Densa personalizada que responda a las clases a predecir.Para esta implementacion usaremos la **API Functional**.<jupyter_code>N_CLASSES=4 #Clases a predecir ->COV
BASE_LEARNING_RATE=0.0001
def build_model_base():
    model_mobilenetv2.trainable=False #las capas del modelo no actualizan su peso.
    inputs=tf.keras.layers.Input(shape=INPUT_SHAPE)
    x=model_mobilenetv2(inputs)
    x=tf.keras.layers.GlobalAveragePooling2D()(x)
    outputs=tf.keras.layers.Dense(units=N_CLASSES,activation="softmax")(x)
    
    model=tf.keras.Model(inputs,outputs)
    
    #compilamos el modelo
    model.compile(loss="categorical_crossentropy",
                  optimizer=tf.keras.optimizers.Adam(lr=BASE_LEARNING_RATE),
                  metrics=["accuracy",tf.keras.metrics.Recall()]
                  )
    return model
model_base=build_model_base()
model_base.summary()<jupyter_output>Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 256, 256, 3)]     0         
_________________________________________________________________
mobilenetv2_1.00_224 (Functi (None, 8, 8, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 4)                 5124      
=================================================================
Total params: 2,263,108
Trainable params: 5,124
Non-trainable params: 2,257,984
_________________________________________________________________<jupyter_text>* Carga y aumento de datos<jupyter_code>from tensorflow.keras.preprocessing.image import ImageDataGenerator #generator de imagenes<jupyter_output><empty_output><jupyter_text>* Configuracion del generador de datasets de entrenamiento, validacion y prueba<jupyter_code>train_datagen=ImageDataGenerator(
            rescale=1.0/127.5, #escalamos los datos en rangos de [-1,1] El modelo espera esta configuracion
            zoom_range=0.2, #agregamos rotacion a las imagenes
            rotation_range=5,
            horizontal_flip=True
        )
#sobre los datos de validacion y test no se hace ningun aumento de datos.
validation_datagen=ImageDataGenerator(rescale=1.0/127.5) #escalamiento de validacion a un rango de [-1,1]
test_datagen=ImageDataGenerator(rescale=1.0/127.5)       #escalamiento de test a un rango de [-1,1]<jupyter_output><empty_output><jupyter_text>* Obtencion de los datos para generar<jupyter_code>#definimos las rutas para el acceso a los datos
train_path="../Datasets/train/"
validation_path="../Datasets/val/"
test_path="../Datasets/test/"

#creamos los generadores de datos a partir de los flujos de informacion
BATCH_SIZE=32 #tamaño del lote que se ira pasando poco a poco
IMAGE_SIZE=(256,256)

train_generator=train_datagen.flow_from_directory(
    train_path,
    target_size=IMAGE_SIZE,
    batch_size=BATCH_SIZE,
    class_mode="categorical"
)

validation_generator=train_datagen.flow_from_directory(
    validation_path,
    target_size=IMAGE_SIZE,
    batch_size=BATCH_SIZE,
    class_mode="categorical"
)

test_generator=train_datagen.flow_from_directory(
    test_path,
    target_size=IMAGE_SIZE,
    batch_size=BATCH_SIZE,
    class_mode="categorical"
)<jupyter_output>Found 15238 images belonging to 4 classes.
Found 1694 images belonging to 4 classes.
Found 4233 images belonging to 4 classes.<jupyter_text>* Generando **Callbacks** para detener el entrenamiento cuando no se tienen buenos resultados<jupyter_code>early_stopping=tf.keras.callbacks.EarlyStopping(monitor="val_loss",patience=3) #cuando la funcion de perdida ya no mejora
n_epochs=10<jupyter_output><empty_output><jupyter_text>Entrenamiento el modelo BASE<jupyter_code>model_base=build_model_base()
model_history=model_base.fit_generator(
                    train_generator,
                    epochs=n_epochs,
                    validation_data=validation_generator,
                    validation_steps=validation_generator.samples//BATCH_SIZE,
                    callbacks=[early_stopping]
            )
#deseo guardar el History devuelto del entrenamiento
dfhist=pd.DataFrame(model_history.history)
dfhist.to_csv("model_historyV1.csv",index=False)<jupyter_output><empty_output><jupyter_text>Mostramos los resultados encontrados<jupyter_code>import pandas as pd
import matplotlib.pyplot as plt
def plot_results(history,columns=[],ax=None):
    if ax is None:
        fig,(ax)=plt.subplots(1,1)
    df=pd.DataFrame(history)[columns]
    df.plot(kind="line",ax=ax,style="o-")
    ax.set_title(" VS ".join(columns))<jupyter_output><empty_output><jupyter_text>Trazamos la curva de la accuracy y funcion de perdida<jupyter_code>#la curva de precision
history=model_history.history
fig,(ax1,ax2)=plt.subplots(1,2)
fig.set_size_inches(15,5)
plot_results(history,["accuracy","val_accuracy"],ax=ax1)
plot_results(history,["loss","val_loss"],ax=ax2)
#plt.title("Accuracy & Loss function evolution")
plt.show()<jupyter_output><empty_output><jupyter_text>* Para **recall**<jupyter_code>plot_results(history,["recall_1","val_recall_1"])
#guardamos el modelo base con 10 epochs
tf.saved_model.save(model_base,"baseline")
model_base.save("baseline.h5")<jupyter_output>INFO:tensorflow:Assets written to: baseline\assets<jupyter_text>2da fase del entrenamiento (2/3)<jupyter_code>#sesion de entramiento restaurada
#El primer entramiento tuvo solo 10 epocas, por temas de tiempo
#El 2do entrenamiento retoma los datos y establece 10 epocas mas, dando lugar asi a 20 epocas en total.
model_base=tf.keras.models.load_model("baseline.h5")
n_epochs=20
model_base.fit_generator(
        train_generator,
        epochs=n_epochs,
        initial_epoch=10,
        validation_data=validation_generator,
        validation_steps=validation_generator.samples//BATCH_SIZE,
        callbacks=[early_stopping]
    )
import pandas as pd
#deseo guardar el History devuelto del entrenamiento
model_history=_
dfhist=pd.DataFrame(model_history.history)
dfhist.to_csv("model_historyV2.csv",index=False)<jupyter_output><empty_output><jupyter_text>* Graficar las dos sesiones de entrenamiento<jupyter_code>df1=pd.read_csv("model_historyV1.csv",sep=",")
df2=pd.read_csv("model_historyV2.csv",sep=",")
df_history=pd.concat([df1,df2],axis=0)
#la curva de precision
history=df_history.to_dict("list")
fig,(ax1,ax2)=plt.subplots(1,2)
fig.set_size_inches(15,5)
plot_results(history,["accuracy","val_accuracy"],ax=ax1)
plot_results(history,["loss","val_loss"],ax=ax2)
#plt.title("Accuracy & Loss function evolution")
plt.show()
plot_results(history,["recall_1","val_recall_1"])<jupyter_output><empty_output><jupyter_text>* Guardamos el modelo base final. sesion (2/3)<jupyter_code>model_base.save("baselinev2.h5")<jupyter_output><empty_output><jupyter_text>* Evaluamos el puntaje en el **conjunto de entrenamiento**.<jupyter_code>model_base.evaluate(train_generator)<jupyter_output>477/477 [==============================] - 650s 1s/step - loss: 0.4047 - accuracy: 0.8498 - recall_1: 0.8218<jupyter_text>* Evaluamos el puntaje en el **conjunto de validacion**.<jupyter_code>model_base.evaluate(validation_generator)<jupyter_output>53/53 [==============================] - 77s 1s/step - loss: 0.4248 - accuracy: 0.8430 - recall_1: 0.8117<jupyter_text>* **RESULTADOS PARCIALES**: El accuracy para el conjunto de entrenamiento es de **ACC=84.98%** y **Recall=82.18%**.El accuracy para el conjunto de validación es de **ACC=84.29%** y **Recall=81.16%**.  3era fase de entrenamiento (3/3)* Retomamos el entrenamiento pasado y continuamos el entrenamiento<jupyter_code>epochs=30
early_stopping=tf.keras.callbacks.EarlyStopping(monitor="val_loss",patience=3) #cuando la funcion de perdida ya no mejora
BATCH_SIZE=32
model_history=model_base.fit_generator(
                        train_generator,
                        epochs=epochs,
                        initial_epoch=20,
                        validation_data=validation_generator,
                        validation_steps=validation_generator.samples//BATCH_SIZE,
                        callbacks=[early_stopping]
                )
dtf=pd.DataFrame(model_history.history)
dtf.to_csv("model_historyV3.csv",index=False)<jupyter_output><empty_output><jupyter_text>* Volvemos a ejecutar la secuencia de pasos de verificacion de resultados<jupyter_code>df1=pd.read_csv("model_historyV1.csv",sep=",")
df2=pd.read_csv("model_historyV2.csv",sep=",")
df3=pd.read_csv("model_historyV3.csv",sep=",")
df_history=pd.concat([df1,df2,df3],axis=0)<jupyter_output><empty_output><jupyter_text>* Creamos una funcion para reutilizar la funcion que genera el grafico, para los modelos que se porbaran mas adelante.<jupyter_code>def plot_metrics(history,metrics=[]): #retorna una lista de tuplas
    fig,axes=plt.subplots(1,len(metrics))
    fig.set_size_inches(15,5)
    graph=pd.DataFrame(history)
    for i,ax in enumerate(axes.flat):
        graph[list(metrics[i])].plot(kind="line",style="o-",ax=ax)
        ax.set_title(" VS ".join(list(metrics[i])))
        ax.grid(True)
    plt.show()
model_history=df_history.to_dict("list")
metrics=[("accuracy","val_accuracy"),("loss","val_loss")]
plot_metrics(model_history,metrics=metrics)
plot_results(model_history,["recall_1","val_recall_1"])
plt.grid(True)<jupyter_output><empty_output><jupyter_text>* Guardamos el modelo final (3/3) para validarlo.<jupyter_code>model_base.save("baselinev3.h5")<jupyter_output><empty_output><jupyter_text>* El entrenamiento del modelo se detuvo en 28 epochs lo que nos dice que la funcion de perdida en la data de validacion no mejoro por 3 epochs consecutivos, probablemente, ya no mejore para futuras epocas.Ahora veamos el rendiemiento del modelo base en los datos de entrenamiento y validacion<jupyter_code>model_base.evaluate(train_generator)
model_base.evaluate(validation_generator)<jupyter_output>53/53 [==============================] - 81s 2s/step - loss: 0.4038 - accuracy: 0.8512 - recall_1: 0.8253<jupyter_text>* Mostramos la matriz de confusion en los datos de entrenamiento y validacionPara el conjunto de entrenamiento<jupyter_code>import numpy as np,seaborn as sns; sns.set()

y_true=train_generator.classes
y_pred=np.argmax(model_base.predict(train_generator),axis=1)
train_generator.class_indices
#obtenemos la matriz de confusion de sklearn
from sklearn.metrics import confusion_matrix

classes=train_generator.class_indices.keys()
mat_train=confusion_matrix(y_true,y_pred)
sns.heatmap(mat_train,square=True,annot=True,fmt="0.2f",cmap="coolwarm",xticklabels=classes,yticklabels=classes)
plt.xticks(rotation=60)
plt.title("Matriz de confusion entrenamiento")
plt.show()<jupyter_output><empty_output><jupyter_text>* Para el conjunto de validacion<jupyter_code>y_true=validation_generator.classes
y_pred=np.argmax(model_base.predict(validation_generator),axis=1)
classes=validation_generator.class_indices.keys()
mat_val=confusion_matrix(y_true,y_pred)
sns.heatmap(mat_val,square=True,annot=True,fmt="0.2f",cmap="coolwarm",xticklabels=classes,yticklabels=classes)
plt.xticks(rotation=60)
plt.title("Matriz de confusion validacion")
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11373184934031105, 'fraction_numerical': 0.02565047541829439, 'mean_word_length': 5.448669201520913, 'pattern_counts': {'":': 0, '<': 86, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '658663', 'license': 'Apache-2.0', 'n_tokens_mistral': 4698, 'n_tokens_neox': 4417, 'n_words': 935, 'path': 'Notebooks/Modelado.ipynb', 'repo_name': 'Jovamih/Modelo-Deteccion-COVID19', 'size': '13567'}