{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<div style=\"width: 100%; clear: both;\">\n<div style=\"float: left; width: 50%;\">\n<img src=\"http://www.uoc.edu/portal/_resources/common/imatges/marca_UOC/UOC_Masterbrand.jpg\", align=\"left\">\n</div>\n<div style=\"float: right; width: 50%;\">\n<p style=\"margin: 0; padding-top: 22px; text-align:right;\">M2.875·Deep Learning · Práctica: Implementación de un algoritmo para la clasificación de enfermedades foliares del árbol del manzano</p>\n<p style=\"margin: 0; text-align:right;\">2020-2 · Máster Universitario en Ciencia de Datos (MUDS)</p>\n<p style=\"margin: 0; text-align:right; padding-button: 100px;\">Estudios de Informática, Multimedia y Telecomunicación</p>\n<p style=\"margin: 0; text-align:right; padding-button: 100px;\">Iñaki Galech Amillano-MSDS 2020/21 <a href=\"mailto:igalech@uoc.edu\">igalech@uoc.edu</a></p>\n</div>\n</div>\n<div style=\"width:100%;\">&nbsp;</div>","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-05-21T23:50:30.481272Z","iopub.execute_input":"2021-05-21T23:50:30.481872Z","iopub.status.idle":"2021-05-21T23:50:36.90064Z","shell.execute_reply.started":"2021-05-21T23:50:30.481758Z","shell.execute_reply":"2021-05-21T23:50:36.899794Z"}}},{"cell_type":"markdown","source":"<div style=\"background-color: #EDF7FF; border-color: #7C9DBF; border-left: 5px solid #7C9DBF; padding: 0.5em;\">\n<strong>Nombre y apellidos:</strong> Iñaki Galech Amillano - MSDS 2020/21  <a href=\"mailto:igalech@uoc.edu\">igalech@uoc.edu</a>\n</div>","metadata":{}},{"cell_type":"markdown","source":"Importación de las librerías necesarias. **Nota:** En Colab de google es necesario importar la librería Kaggle por separado.","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\nimport cv2\nimport glob\nimport os\nimport matplotlib.pylab as plt\nimport seaborn as sns\nimport pandas as pd\nimport numpy as np\nimport os\nimport tensorflow as tf\nimport keras\nimport keras_preprocessing\nfrom keras.preprocessing.image import ImageDataGenerator  \nfrom keras.layers import (\n    GlobalAveragePooling2D, Multiply, Flatten,\n     Dense, Dropout, Conv2D, BatchNormalization, MaxPooling2D)\nfrom keras.layers.experimental.preprocessing import Resizing, Rescaling\nfrom keras.callbacks import EarlyStopping\nfrom keras.optimizers import Adam\nfrom keras import Sequential, Model\nfrom sklearn.model_selection import train_test_split\nfrom tqdm.notebook import tqdm\nimport tensorflow_addons as tfa\nimport albumentations\nimport shutil","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-05T16:44:42.487206Z","iopub.execute_input":"2021-06-05T16:44:42.487537Z","iopub.status.idle":"2021-06-05T16:44:42.499036Z","shell.execute_reply.started":"2021-06-05T16:44:42.487505Z","shell.execute_reply":"2021-06-05T16:44:42.498062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <u>Preprocesado de los datos y análisis preliminar</u>","metadata":{}},{"cell_type":"markdown","source":"Situamos el path en nuestro set de datos, en concreto en el subidectorio /train.","metadata":{}},{"cell_type":"code","source":"pathimagenestrain = '/kaggle/input/kaggle-plant-pathology-2021-modificat/train/'\n\nprint(\"El directorio base entrenamiento es {}\".format(pathimagenestrain))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:44:39.987809Z","iopub.execute_input":"2021-06-05T16:44:39.988135Z","iopub.status.idle":"2021-06-05T16:44:39.992332Z","shell.execute_reply.started":"2021-06-05T16:44:39.988104Z","shell.execute_reply":"2021-06-05T16:44:39.991479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Conteo inicial por subdirectorios\nRecorremos los subdirectorios y contamos el número de imágenes por directorio para obtener el conteo por clases.","metadata":{}},{"cell_type":"code","source":"# Recorremos los subdirectorios y contamos el número de imágenes por clase\n\nsubdirectorios = [x[1] for x in os.walk(pathimagenestrain)]\nsubdirectorios = subdirectorios[0]\n\n# Diccionario para la cuenta\nnumeroimagenes = {}\n\n# Recorremos y contamos:\nfor subpath in subdirectorios:\n    jpegCounter = len(glob.glob1(pathimagenestrain+'/'+subpath+'/',\"*.jp*g\"))\n    numeroimagenes[subpath] = jpegCounter\n\nprint('Número total de clases es:', len(subdirectorios))    \nprint('Número total de imágenes (jpg/jpeg) por clase es :{}'.format(numeroimagenes))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:44:47.351517Z","iopub.execute_input":"2021-06-05T16:44:47.351884Z","iopub.status.idle":"2021-06-05T16:44:50.485665Z","shell.execute_reply.started":"2021-06-05T16:44:47.35185Z","shell.execute_reply":"2021-06-05T16:44:50.484774Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Guardamos en una variable el nombre da cada fichero.","metadata":{}},{"cell_type":"code","source":"# Guardamos el path completo a cada fichero\nficheros = [name for name in glob.glob(pathimagenestrain+'/*/*', recursive=True)]","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:44:58.713422Z","iopub.execute_input":"2021-06-05T16:44:58.713769Z","iopub.status.idle":"2021-06-05T16:44:58.759928Z","shell.execute_reply.started":"2021-06-05T16:44:58.713736Z","shell.execute_reply":"2021-06-05T16:44:58.759129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Cálculo y chequeo del rango dinámico, canales de color/grises y data-types. Aunque no es imprescindible, sí es una buena idea contrastar los data-types, ya que los data types unicode 8, 16 y 32 son más rápidos de procesar en Tensorflow.","metadata":{}},{"cell_type":"code","source":"maximo = 0\nminimo = 1024\n#256\ncanales = 0\n\nimagenes = []\ntipos = []\n\n# Recorremos los ficheros y vamos guardando máximos y mínimos por canal\nfor i in ficheros: \n    img = cv2.imread(i)\n    # Extraemos max y min por colores\n    max_channels = np.amax([np.amax(img[:,:,0]), np.amax(img[:,:,1]), np.amax(img[:,:,2])])\n    min_channels = np.amax([np.amin(img[:,:,0]), np.amin(img[:,:,1]), np.amin(img[:,:,2])])\n    if max_channels > maximo:\n        maximo = max_channels\n    if min_channels < minimo:\n        minimo = min_channels\n    # Guardamos los canales (el máximo encontrado)\n    if len(img.shape) > canales:\n        canales = len(img.shape)\n\n    # Guardamos los data types que encontramos.\n    if img.dtype not in tipos:\n        tipos.append(img.dtype)\n\nprint(\"Número de imagenes leídas en /train(entrenamiento): {}\\n\".format(len(ficheros)))\n\nprint(\"Tipos de datos de los ficheros leídos en /train(entrenamiento): {}\\n\".format(tipos))\n\nprint(\"Número de canales de color de los ficheros: {}\\n\".format(canales))\n\nprint(\"Max. y Min de todas las imágenes en /train son : Max={}, Min={}\\n\".format(maximo, minimo))\n\nprint(\"El rango dinámico es: {}\".format(((maximo - minimo) * 100) / 255))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T11:57:01.623401Z","iopub.execute_input":"2021-06-05T11:57:01.623784Z","iopub.status.idle":"2021-06-05T11:59:03.032306Z","shell.execute_reply.started":"2021-06-05T11:57:01.623756Z","shell.execute_reply":"2021-06-05T11:59:03.030225Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <u>Gráficos con el conteo de etiquetas y de las frecuencias relativas</u>","metadata":{}},{"cell_type":"code","source":"#Gráfico inicial del conteo de etiquetas\n\nplt.figure(figsize=(10,8))\nlabels = sns.barplot(x=list(numeroimagenes.keys()), y=list(numeroimagenes.values()));\n\n# Para imprimir los valores por columnas\nfor i, v in enumerate(list(numeroimagenes.values())):\n    #print (i, v)\n    labels.text(i-0.15, v , str(v), color='darkblue', fontsize=10, fontweight='bold')\nplt.title('Número de imágenes por clase', fontsize =14, color = 'darkblue');","metadata":{"execution":{"iopub.status.busy":"2021-06-05T11:59:03.034336Z","iopub.execute_input":"2021-06-05T11:59:03.034839Z","iopub.status.idle":"2021-06-05T11:59:03.296059Z","shell.execute_reply.started":"2021-06-05T11:59:03.034796Z","shell.execute_reply":"2021-06-05T11:59:03.295015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"A continuación se muestra la gráfica de las <u>frecuencias relativas</u> de las distintas etiquetas.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(6,6))\nplt.title(\"Frecuencias relativas en % para las clases (labels)\", fontsize = 14, color = 'darkblue')\n# Contamos los valores por clases y vamos calculando sus frecuencias relativas.\nsorted_counts = list(np.array(list(numeroimagenes.values()))/sum(list(numeroimagenes.values())))\nplt.pie(sorted_counts, labels = list(numeroimagenes.keys()), startangle = 30,\n        counterclock = False, autopct='%.2f%%', textprops={'fontsize':'14'})\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T11:59:09.827973Z","iopub.execute_input":"2021-06-05T11:59:09.828375Z","iopub.status.idle":"2021-06-05T11:59:09.981885Z","shell.execute_reply.started":"2021-06-05T11:59:09.828314Z","shell.execute_reply":"2021-06-05T11:59:09.980583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <u>Gráfico mostrando cinco imágenes por clase</u>","metadata":{}},{"cell_type":"code","source":"from PIL import Image\nimport random\n\npathimagenes = '/kaggle/input/kaggle-plant-pathology-2021-modificat/train/'\nimagenes = []\netiquetas = []\n\nfor subpath in subdirectorios:\n    ficheros = random.sample([name for name in glob.glob1(pathimagenes + subpath + '/',\"*.jpg\")],5)\n    for i in ficheros:\n        imagen = pathimagenes + subpath + '/' + i\n        img = cv2.imread(imagen, 1)\n        imagenes.append(img)\n        etiquetas.append(subpath)\n\nfig = plt.figure(figsize=(24, 24))\nfig.suptitle(\"5 imágenes de cada clase (label)\", fontsize = 24);\nfor i in range(len(imagenes)):\n    ax = plt.subplot(6, 5, i+1)\n    plt.imshow(imagenes[i])\n    plt.title(etiquetas[i], fontsize = 18)\n    plt.axis(\"off\")\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T11:59:14.651603Z","iopub.execute_input":"2021-06-05T11:59:14.65197Z","iopub.status.idle":"2021-06-05T11:59:18.678437Z","shell.execute_reply.started":"2021-06-05T11:59:14.651938Z","shell.execute_reply":"2021-06-05T11:59:18.677275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Esta parte del códico no se utiliza en nuestro problema, pero sí en el problema original de Kaggle. Se utiliza para 'binarizar' (OneHotEncoder) de MultiLabels, ya qe el problema original tienen multilabels no excluyentes.","metadata":{}},{"cell_type":"code","source":"#from sklearn.preprocessing import MultiLabelBinarizer\n\n## Convertimos el array a una lista\n#s = list(p_train['labels'])\n#mlb = MultiLabelBinarizer()\n## Construimos un dataframe con el resultado de aplicar el MLB con las nuevas clases como columnas con el mismo índice que p_train.\n#trainx = pd.DataFrame(mlb.fit_transform(s), columns=mlb.classes_, index=p_train.index)\n#listadoetiquetas = list(mlb.classes_)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Enriquecimiento y transformación de los datos (Data Augmentation):\nGeneramos dos generadores de imágenes de Keras, uno para el set de entrenamiento y validación (train_datagen) y otro para el set de test. En los generadores incluimos los siguientes parámetros que enriquecerán las imágenes, lo cual beneficiará la generalización del modelo:\n* Normalización\n* Rotación\n* Ensanchado\n* Alargado\n* Zoom o focalización\n* Se le da la vuelta a la imagen sobre el eje de abscisas","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator,load_img\n\ntrain_datagen = keras.preprocessing.image.ImageDataGenerator(\n    rescale = 1/255., # Normalización\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    horizontal_flip=True,\n    validation_split = 0.2,\n    zoom_range = 0.2,\n    shear_range = 0.2,\n    vertical_flip = False)\n\ntest_datagen = keras.preprocessing.image.ImageDataGenerator(\n    rescale = 1/255.,  # Normalización\n    #rotation_range=20,\n    #width_shift_range=0.2,\n    #height_shift_range=0.2,\n    #horizontal_flip=True,\n    #validation_split = 0.2,\n    #zoom_range = 0.2,\n    #shear_range = 0.2,\n    vertical_flip = False)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:45:09.689755Z","iopub.execute_input":"2021-06-05T16:45:09.690093Z","iopub.status.idle":"2021-06-05T16:45:09.698197Z","shell.execute_reply.started":"2021-06-05T16:45:09.690063Z","shell.execute_reply":"2021-06-05T16:45:09.69741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Creación de los sets de entrenamiento, validación y test basados en los generadores anteriores. Nótese la división 'exacta' de las imágenes resultantes para que los batches sean uniformes.","metadata":{}},{"cell_type":"code","source":"# Creamos los sets de entreno, validación y otro específico para evaluación (directorio /test y sin labels).\nset_entreno = train_datagen.flow_from_directory(\n    directory= '/kaggle/input/kaggle-plant-pathology-2021-modificat/train/',\n    target_size=(256, 256),\n    color_mode=\"rgb\",\n    class_mode=\"categorical\",\n    classes = numeroimagenes.keys(),\n    batch_size=30, ## 7800/30 = 260\n    shuffle=True,\n    seed=27,\n    save_to_dir=None,\n    follow_links=False,\n    subset='training',\n    interpolation=\"nearest\"\n)\n\nset_validacion = train_datagen.flow_from_directory(\n    directory='/kaggle/input/kaggle-plant-pathology-2021-modificat/train/',\n    target_size=(256, 256),\n    color_mode=\"rgb\",\n    class_mode=\"categorical\",\n    classes = numeroimagenes.keys(),\n    batch_size=30, # Divide 1950/30 a 65 batches\n    shuffle=True,\n    seed=27,\n    save_to_dir=None,\n    follow_links=False,\n    subset='validation',\n    interpolation=\"nearest\",\n)\n\n# Aquí hay un pequeño 'truco'. Si pasamos como clase 'test' el generador toma esto como el subdirectorio de 'clases', aunque realmente no hay tales.\nset_test = test_datagen.flow_from_directory(\n    directory = '/kaggle/input/kaggle-plant-pathology-2021-modificat/',\n    target_size =(256, 256),\n    color_mode = \"rgb\",\n    classes = ['test'],\n    shuffle = False,\n    seed = 27,\n    batch_size = 39, # 39 divide al set de test perfectamente (7527/39 = 193 batches)\n    class_mode = None,\n)\n\n# Este último set no implementa shuffle (shuffle=False) y se usará para las \n# predicciones y así poder comparar las clases de las predicciones vs. clases \n# verdaderas.\nset_validacion_ordenado = train_datagen.flow_from_directory(\n    directory='/kaggle/input/kaggle-plant-pathology-2021-modificat/train/',\n    target_size=(256, 256),\n    color_mode=\"rgb\",\n    class_mode=\"categorical\",\n    classes = numeroimagenes.keys(),\n    batch_size=30, # Divide validación 1950/30 en 65 batches\n    shuffle=False,\n    seed=27,\n    save_to_dir=None,\n    follow_links=False,\n    subset='validation',\n    interpolation=\"nearest\",\n)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:45:14.0794Z","iopub.execute_input":"2021-06-05T16:45:14.07973Z","iopub.status.idle":"2021-06-05T16:45:19.718614Z","shell.execute_reply.started":"2021-06-05T16:45:14.079696Z","shell.execute_reply":"2021-06-05T16:45:19.717817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Guardamos las etiquetas según vienen en el conjunto de validación, sin Shuffle. Se utilizará al final para evaluar el f1-score en el entrenamiento.","metadata":{}},{"cell_type":"code","source":"labelslista  = list(set_entreno.class_indices.keys())","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:45:44.712138Z","iopub.execute_input":"2021-06-05T16:45:44.712448Z","iopub.status.idle":"2021-06-05T16:45:44.716719Z","shell.execute_reply.started":"2021-06-05T16:45:44.712418Z","shell.execute_reply":"2021-06-05T16:45:44.715764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Orden 'real' tras la binarización (a tener en cuenta en predicciones): {}\".format(labelslista))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:45:47.759811Z","iopub.execute_input":"2021-06-05T16:45:47.760134Z","iopub.status.idle":"2021-06-05T16:45:47.764948Z","shell.execute_reply.started":"2021-06-05T16:45:47.760102Z","shell.execute_reply":"2021-06-05T16:45:47.76389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"A continuación se muestra una imagen por clase con los datos 'aumentados', es decir, tras las funciones de aumento de datos implementadas en las celdas anteriores. Esto nos da una idea de cómo se entrenarán nuestras imágenes.","metadata":{}},{"cell_type":"code","source":"# Grafico con una imagen por clase tras el data augmentation.\nindices = []\nimagenes=[]\netiquetas = []\nclass_names = []\n\nwhile len(imagenes) < 6:\n  for i in range(len(class_names)):\n    indice = np.where(labels[i] == 1)[0][0]\n    #class_names = list(set_entreno.class_indices.keys())\n    if ((indice in indices) == False):\n      indices.append(indice)\n      imagenes.append(images[i])\n      etiquetas.append(class_names[indice])\n      #print(etiquetas)\n\n  images, labels = set_entreno.next()\n  class_names = list(set_entreno.class_indices.keys())\n\nfig = plt.figure(figsize=(24, 24))\nfor i in range(len(imagenes)):\n  ax = plt.subplot(6, 6, i+1)\n  plt.imshow(imagenes[i])\n  plt.title(etiquetas[i])\n  plt.axis(\"off\")\nfig.suptitle(\"Una imagen 'aumentada' de cada clase (label) tras las transformaciones\", fontsize = 24);","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:00:26.441457Z","iopub.execute_input":"2021-06-05T12:00:26.441812Z","iopub.status.idle":"2021-06-05T12:00:29.300919Z","shell.execute_reply.started":"2021-06-05T12:00:26.441782Z","shell.execute_reply":"2021-06-05T12:00:29.299634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <u>Determinación de la estrategia de entrenamiento y validación</u>","metadata":{}},{"cell_type":"markdown","source":"#### Creación de las Métricas, callbacks y optimizadores. Nótese cómo vamos guardando el mejor modelo (fichero .h5 que se entrega como un activo a nuestro cliente en el repositorio público de Kaggle que incluimos con la entrega.\nAdemás del checkpoint (guardar el mejor modelo), se implementa un Early Stopper de 5 épocas que monitoriza nuestra función f1-score en validación y también una función `ReduceOnPlateau()` que monitoriza también f1-score en validación y disminuirá el learning rate en un 10% si no se presentan mejoras en los últimas 3 épocas. De esta forma se pretende incentivar un aprendizaje más granular en estos puntos de 'no aprendizaje' para tratar de recuperar aprendizaje. ","metadata":{}},{"cell_type":"code","source":"from datetime import datetime\ntimestampmodelo = datetime.now()\ntimestampmodelo = timestampmodelo.strftime(\"%d-%b-%Y_%H%M\")\n\nMETRIC = \"val_f1_score\"\n\ndef create_callbacks(model, metric = METRIC):\n    \n    guardar_path = './mejor_modelo_' + model.name + '_' + timestampmodelo + '.h5'\n    \n    checkpoint = tf.keras.callbacks.ModelCheckpoint(\n        filepath = guardar_path,\n        monitor = metric,\n        mode ='max',\n        save_best_only=True,\n        verbose=1,\n    )\n\n    reducccionlr = tf.keras.callbacks.ReduceLROnPlateau(\n        monitor = metric,\n        mode = 'max',\n        factor = 0.1,\n        patience = 3,\n        verbose = 0\n    )\n\n    earlystop = tf.keras.callbacks.EarlyStopping(\n        monitor = metric,\n        mode ='max',\n        patience = 5, \n        verbose = 1\n    )\n    \n    callbacks = [checkpoint, reducccionlr, earlystop]         \n    \n    return callbacks","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:46:31.123592Z","iopub.execute_input":"2021-06-05T16:46:31.123967Z","iopub.status.idle":"2021-06-05T16:46:31.130343Z","shell.execute_reply.started":"2021-06-05T16:46:31.123934Z","shell.execute_reply":"2021-06-05T16:46:31.129512Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Función general de compilación de los modelos, introduciendo como optimizador un descenso del gradiente con 'momentum' tipo Nesterov y como métrica la funcion f1-score 'macro'.","metadata":{}},{"cell_type":"code","source":"def compile_model(model, lr=0.03):\n    \n    optimizer = tf.keras.optimizers.SGD(lr=0.03, decay=1e-4, momentum=0.8, nesterov=True)\n    \n    loss = tf.keras.losses.CategoricalCrossentropy()\n    \n    metrics = [\n    tfa.metrics.F1Score(num_classes = 6, average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:46:34.737141Z","iopub.execute_input":"2021-06-05T16:46:34.737529Z","iopub.status.idle":"2021-06-05T16:46:34.744305Z","shell.execute_reply.started":"2021-06-05T16:46:34.737488Z","shell.execute_reply":"2021-06-05T16:46:34.743333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Creación de modelos y entrenamientos iniciales: DenseNet121 y ResNet50 con/sin entrenamiento de todas las capas.</u>","metadata":{}},{"cell_type":"markdown","source":"### <u>Modelo DenseNet121</u>","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.densenet import DenseNet121\nfrom tensorflow.keras.applications.densenet import preprocess_input\n\nseed = 37\ntf.random.set_seed(seed)\n\nbase_model = tf.keras.applications.DenseNet121(input_shape=(256,256,3),\n                                               include_top=False,\n                                               weights='imagenet',\n                                               pooling='avg')\n\n## Descomentar si no queremos entrenal la primera parte del DenseNet121\n#for layer in base_model.layers:\n#    layer.trainable = False\n\nx = base_model.output\n# fully connected layer D64 a D16 y por último a 6 clases.\nx = tf.keras.layers.Dense(64, activation='relu')(x)\nx = tf.keras.layers.Dense(16, activation='relu')(x)\n# Output final con función sigmoid (potencialmente más de una, asumimos clases no excluyentes) \npredicciones = tf.keras.layers.Dense(6, activation='softmax')(x)\nmodeloDenseNet121 = tf.keras.Model(base_model.inputs, predicciones, name='DenseNet121')","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:00:58.843197Z","iopub.execute_input":"2021-06-05T12:00:58.843583Z","iopub.status.idle":"2021-06-05T12:01:06.207625Z","shell.execute_reply.started":"2021-06-05T12:00:58.843552Z","shell.execute_reply":"2021-06-05T12:01:06.206568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS= 10\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    #model = create_model()\n    modeloDenseNet121 = compile_model(modeloDenseNet121)\n    callbacks = create_callbacks(modeloDenseNet121)\n    historymodeloDenseNet121 = modeloDenseNet121.fit(\n                        set_entreno,\n                        epochs=EPOCHS,\n                        callbacks=callbacks,\n                        validation_data = set_validacion,\n                        verbose=VERBOSE\n                       )","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:02:06.105874Z","iopub.execute_input":"2021-06-05T12:02:06.106497Z","iopub.status.idle":"2021-06-05T12:35:44.343257Z","shell.execute_reply.started":"2021-06-05T12:02:06.106429Z","shell.execute_reply":"2021-06-05T12:35:44.342206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Podemos ver la diferencia en cuanto al número de parámetros si descomentamos la siguiente celda.","metadata":{}},{"cell_type":"code","source":"#modeloDenseNet121.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:35:59.253659Z","iopub.execute_input":"2021-06-05T12:35:59.254081Z","iopub.status.idle":"2021-06-05T12:35:59.258743Z","shell.execute_reply.started":"2021-06-05T12:35:59.254049Z","shell.execute_reply":"2021-06-05T12:35:59.257436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Recalculamos la pérdida y el f1-score por si hubiera diferencias.","metadata":{}},{"cell_type":"code","source":"loss, f1score = modeloDenseNet121.evaluate(set_validacion,verbose=1)\nprint('Pérdida y f1-score del modelo {}: pérdida={} y f1-score(macro)={}'.format(modeloDenseNet121.name, round(loss,3), round(f1score,3)))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:36:03.849725Z","iopub.execute_input":"2021-06-05T12:36:03.850098Z","iopub.status.idle":"2021-06-05T12:36:41.717456Z","shell.execute_reply.started":"2021-06-05T12:36:03.850062Z","shell.execute_reply":"2021-06-05T12:36:41.716122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosDenseNet121 = pd.DataFrame(historymodeloDenseNet121.history)\nmodeloDenseNet121 = 'modeloDenseNet121_history06052021_con.csv'\nwith open(modeloDenseNet121, mode='w') as f:\n    resultadosDenseNet121.to_csv(f)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:36:44.276004Z","iopub.execute_input":"2021-06-05T12:36:44.276387Z","iopub.status.idle":"2021-06-05T12:36:44.287692Z","shell.execute_reply.started":"2021-06-05T12:36:44.27634Z","shell.execute_reply":"2021-06-05T12:36:44.286235Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <u>Modelo ResNet50</u>\nSe construye y prueba un modelo basado en ResNet50 con dos variantes: entrenando todo el modelo o únicamente las capas fully connected finales (D64->D16->D6).","metadata":{}},{"cell_type":"code","source":"from keras.applications.resnet50 import ResNet50\nfrom keras.applications.resnet50 import preprocess_input\n\nseed = 43\ntf.random.set_seed(seed)\n\n#base_model = tf.keras.applications.DenseNet121(weights=weight_path, include_top=False, pooling='avg')\nbase_model = tf.keras.applications.ResNet50(input_shape=(256,256,3),\n                                               include_top=False,\n                                               weights='imagenet',\n                                               pooling='avg')\n\n# Descomentar para no entrenar la primera parte del ResNet50, solo las capas finales FC\n#for layer in base_model.layers:\n#    layer.trainable = False\n\nx = base_model.output\n# fully connected layer del Resnet a D64, a D16 y por último a D con 6 clases.\nx = tf.keras.layers.Dense(64, activation='relu')(x)\nx = tf.keras.layers.Dense(16, activation='relu')(x)\n# Output final con función softmax (asumimos clases excluyentes) \npredicciones = tf.keras.layers.Dense(6, activation='softmax')(x)\nmodeloResNet50 = tf.keras.Model(base_model.inputs, predicciones, name='ResNet50Prac1Final')","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:47:17.124528Z","iopub.execute_input":"2021-06-05T16:47:17.125006Z","iopub.status.idle":"2021-06-05T16:47:20.00551Z","shell.execute_reply.started":"2021-06-05T16:47:17.124968Z","shell.execute_reply":"2021-06-05T16:47:20.004668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS= 10\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\n# Forzamos entrenar en la GPU\nwith tf.device('/device:GPU:0'):\n    \n    #model = create_model()\n    modeloResNet50 = compile_model(modeloResNet50)\n    callbacks = create_callbacks(modeloResNet50)\n    historymodeloResNet50 = modeloResNet50.fit(\n                        set_entreno,\n                        epochs = EPOCHS,\n                        callbacks = callbacks,\n                        validation_data = set_validacion,\n                        verbose = VERBOSE\n                       )","metadata":{"execution":{"iopub.status.busy":"2021-06-05T12:40:49.615886Z","iopub.execute_input":"2021-06-05T12:40:49.616258Z","iopub.status.idle":"2021-06-05T13:13:59.568729Z","shell.execute_reply.started":"2021-06-05T12:40:49.616228Z","shell.execute_reply":"2021-06-05T13:13:59.567694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosmodeloResNet50 = pd.DataFrame(historymodeloResNet50.history)\nmodeloResNesnet50fichero = 'modeloResNet50_history06052021_con.csv'\nwith open(modeloResNesnet50fichero, mode='w') as f:\n    resultadosmodeloResNet50.to_csv(f)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T13:14:01.828881Z","iopub.execute_input":"2021-06-05T13:14:01.829271Z","iopub.status.idle":"2021-06-05T13:14:01.837753Z","shell.execute_reply.started":"2021-06-05T13:14:01.829241Z","shell.execute_reply":"2021-06-05T13:14:01.836353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss, f1score = modeloResNet50.evaluate(set_validacion,verbose=1)\nprint('La pérdida y f1-score del modelo {} en validación son: pérdida={} y f1-score(macro)={}'.format(modeloResNet50.name, round(loss,3), round(f1score,3)))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T13:14:05.009929Z","iopub.execute_input":"2021-06-05T13:14:05.010322Z","iopub.status.idle":"2021-06-05T13:14:42.644052Z","shell.execute_reply.started":"2021-06-05T13:14:05.01029Z","shell.execute_reply":"2021-06-05T13:14:42.642961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Gráficas compartivas ResNet50 vs. DenseNet121 con/sin entrenamiento de todas las capas</u>:\nEl siguiente gráfico muestra el resumen comparativo en cuanto a pérdida y f1-score en training y validación de ambos modelos preseleccionados y entrenados tanto con todas las capas como con las 3 últimas capas (capas fully connected añadidas: D64->D16->D6). Los resultados agrupados los tenemos en un fichero CSV dentro del repositorio de datos de la entrega: `/kaggle/input/resultados-modelos-prac1-gpu/modelos_Resultados_ResNet50_vs_DenseNet121.csv`","metadata":{}},{"cell_type":"code","source":"resultados_1 = pd.read_csv('/kaggle/input/resultados-modelos-prac1-gpu/modelos_Resultados_ResNet50_vs_DenseNet121.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-05T13:14:47.77127Z","iopub.execute_input":"2021-06-05T13:14:47.771669Z","iopub.status.idle":"2021-06-05T13:14:47.789129Z","shell.execute_reply.started":"2021-06-05T13:14:47.771637Z","shell.execute_reply":"2021-06-05T13:14:47.788034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ((ax1,ax2, ax3, ax4), (ax5, ax6, ax7, ax8)) = plt.subplots(2,4, figsize=(24,8))\nplt.suptitle(\"Gráficas de pérdida y f1-score para los modelos \\n (Sets entrenamientor y validación)\", color='darkblue')\nplt.style.use('seaborn')\n\nax1.plot(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['loss'], color='blue')\nax1.plot(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['val_loss'], color='orange')\nax1.set_xticks(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['epoch'])\nax1.legend(['pérdida entren.', 'perdida val.'])\nax1.set_xlabel('Epochs')\nax1.set_ylabel('Pérdida (loss)')\nax1.set_title(\"Modelo ResNet50 solo últimas capas\")\n               \nax2.plot(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['f1_score'].to_list(),color='maroon')\nax2.plot(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['val_f1_score'].to_list(),color='lime')\nax2.set_xticks(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['epoch'].to_list())\nax2.set_xticklabels(resultados_1[resultados_1['modelo']=='ResNet50_ultimacapa']['epoch'].to_list())\nax2.set_xlabel('Epochs')\nax2.set_ylabel('Pérdida (loss)')\nax2.legend(['f1-score entren.', 'f1-score valid.'])\nax2.set_title(\"Pérdida Modelo ResNet50 últimas capas\")\n\n\nax3.plot(resultados_1[resultados_1['modelo']=='ResNet50_todo']['loss'].to_list(), color='blue')\nax3.plot(resultados_1[resultados_1['modelo']=='ResNet50_todo']['val_loss'].to_list(), color='orange')\nax3.set_xticks(resultados_1[resultados_1['modelo']=='ResNet50_todo']['epoch'].to_list())\nax3.set_xticklabels(resultados_1[resultados_1['modelo']=='ResNet50_todo']['epoch'].to_list())\nax3.legend(['pérdida entren.', 'perdida val.'])\nax3.set_xlabel('Epochs')\nax3.set_ylabel('Pérdida (loss)')\nax3.set_title(\"Modelo ResNet50 todas las capas\")\n               \nax4.plot(resultados_1[resultados_1['modelo']=='ResNet50_todo']['f1_score'].to_list(),color='maroon')\nax4.plot(resultados_1[resultados_1['modelo']=='ResNet50_todo']['val_f1_score'].to_list(),color='lime')\nax4.set_xticks(resultados_1[resultados_1['modelo']=='ResNet50_todo']['epoch'].to_list())\nax4.set_xticklabels(resultados_1[resultados_1['modelo']=='ResNet50_todo']['epoch'].to_list())\nax4.set_yticks(resultados_1[resultados_1['modelo']=='ResNet50_todo']['val_f1_score'].to_list())\nax4.set_xlabel('Epochs')\nax4.set_ylabel('Pérdida (loss)')\nax4.legend(['f1-score entren.', 'f1-score valid.'])\nax4.set_title(\"f1-score Modelo ResNet50 todas las capas\")\n\n\nax5.plot(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['loss'].to_list(), color='blue')\nax5.plot(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['val_loss'].to_list(), color='orange')\nax5.set_xticks(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['epoch'].to_list())\nax5.set_xticklabels(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['epoch'].to_list())\nax5.legend(['pérdida entren.', 'perdida val.'])\nax5.set_xlabel('Epochs')\nax5.set_ylabel('Pérdida (loss)')\nax5.set_title(\"Modelo DenseNet50 solo últimas capas\")\n               \nax6.plot(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['f1_score'].to_list(),color='maroon')\nax6.plot(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['val_f1_score'].to_list(),color='lime')\nax6.set_xticks(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['epoch'].to_list())\nax6.set_xticklabels(resultados_1[resultados_1['modelo']=='DenseNet121_ultimacapa']['epoch'].to_list())\nax6.set_xlabel('Epochs')\nax6.set_ylabel('Pérdida (loss)')\nax6.legend(['f1-score entren.', 'f1-score valid.'])\nax6.set_title(\"f1-score Modelo DesNet121 (últimas capas)\")\n\n\nax7.plot(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['loss'].to_list(), color='blue')\nax7.plot(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['val_loss'].to_list(), color='orange')\nax7.set_xticks(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['epoch'].to_list())\nax7.legend(['pérdida entren.', 'perdida val.'])\nax7.set_xlabel('Epochs')\nax7.set_ylabel('Pérdida (loss)')\nax7.set_title(\"Modelo DenseNet121 todas las capas\")\n               \nax8.plot(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['f1_score'].to_list(),color='maroon')\nax8.plot(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['val_f1_score'].to_list(),color='lime')\nax8.set_xticks(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['epoch'].to_list())\nax8.set_xticklabels(resultados_1[resultados_1['modelo']=='DenseNet121_todo']['epoch'].to_list())\nax8.set_xlabel('Epochs')\nax8.set_ylabel('Pérdida (loss)')\nax8.legend(['f1-score entren.', 'f1-score valid.'])\nax8.set_title(\"f1-score Modelo DenseNet121 todas las capas\")\n\nplt.tight_layout()\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T13:14:51.059154Z","iopub.execute_input":"2021-06-05T13:14:51.059534Z","iopub.status.idle":"2021-06-05T13:14:53.545373Z","shell.execute_reply.started":"2021-06-05T13:14:51.059505Z","shell.execute_reply":"2021-06-05T13:14:53.544374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Selección y entrenamiento final del modelo ResNet50->D64->D16-D6 elegido</u>","metadata":{}},{"cell_type":"markdown","source":"A continuación, ya elegido el modelo ResNet50 entrenado en su totalidad,, construimos una función que lo compilará e incluirá las métricas adicionales establecidas con nuestro cliente: f1-score, accuracy, precision y recall. Además, estas métricas facilitarán la construcción de la matriz de confusión resultante de nuestro entrenamiento.","metadata":{}},{"cell_type":"code","source":"def compile_model_final(model, learning_rate=0.03):\n    \n    optimizer = tf.keras.optimizers.SGD(learning_rate=0.03, decay=1e-4, momentum=0.8, nesterov=True)\n    \n    loss = tf.keras.losses.CategoricalCrossentropy()\n    \n    # tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n        tfa.metrics.F1Score(num_classes = 6, average = \"macro\",name = \"f1_score\"),       \n        #keras.metrics.TruePositives(name='tp'),\n        #keras.metrics.FalsePositives(name='fp'),\n        #keras.metrics.TrueNegatives(name='tn'),\n        #keras.metrics.FalseNegatives(name='fn'), \n        keras.metrics.CategoricalAccuracy(name='accuracy'),\n        keras.metrics.Precision(name='precision'),\n        keras.metrics.Recall(name='recall')\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:46:56.269483Z","iopub.execute_input":"2021-06-05T16:46:56.26982Z","iopub.status.idle":"2021-06-05T16:46:56.278137Z","shell.execute_reply.started":"2021-06-05T16:46:56.269787Z","shell.execute_reply":"2021-06-05T16:46:56.277353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Entrenamiento final del ResNet50 con 30 épocas y las nuevas métricas.","metadata":{}},{"cell_type":"code","source":"EPOCHS= 30\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\n# Forzamos entrenar en la GPU\nwith tf.device('/device:GPU:0'):\n    \n    modeloResNet50 = compile_model_final(modeloResNet50)\n    callbacks = create_callbacks(modeloResNet50)\n    historymodeloResNet50 = modeloResNet50.fit(\n                        set_entreno,\n                        epochs = EPOCHS,\n                        callbacks = callbacks,\n                        validation_data = set_validacion,\n                        verbose = VERBOSE\n                       )","metadata":{"execution":{"iopub.status.busy":"2021-06-05T16:47:29.54886Z","iopub.execute_input":"2021-06-05T16:47:29.549182Z","iopub.status.idle":"2021-06-05T18:11:08.745336Z","shell.execute_reply.started":"2021-06-05T16:47:29.549151Z","shell.execute_reply":"2021-06-05T18:11:08.744143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Predicciones sobre el conjunto de imágenes de test y creación del fichero test.csv</u>\nCalculamos las predicciones de nuestro set de test que hemos creado al principio. Lo podemos hacer si hemos ejecutado la celda anterior (entrenando 30 épocas) o también si subimos el modelo que hemos guardado previamente en el repositorio Kaggle: '/kaggle/input/resultados-modelos-prac1-gpu/mejor_modelo_ResNet50Prac1Final.h5'","metadata":{}},{"cell_type":"code","source":"# Descomentar esta celda si queremos subir y utilizar el modelo **ya entrenado** con un f1-score de 0.93409\n#from keras.models import load_model\n\n#modeloResNet50 = load_model('/kaggle/input/resultados-modelos-prac1-gpu/mejor_modelo_ResNet50Prac1Final_06052021.h5')\n##modeloResNet50 = load_model('/kaggle/input/resultados-modelos-prac1-gpu/mejor_modelo_ResNet50Prac1Final_05-Jun-2021_1240.h5')","metadata":{"execution":{"iopub.status.busy":"2021-06-05T14:49:57.510666Z","iopub.execute_input":"2021-06-05T14:49:57.511028Z","iopub.status.idle":"2021-06-05T14:49:59.58147Z","shell.execute_reply.started":"2021-06-05T14:49:57.510998Z","shell.execute_reply":"2021-06-05T14:49:59.580368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Se efectúan las predicciones del modelo sobre las imágenes de test (set_test).","metadata":{}},{"cell_type":"code","source":"set_test.reset()\npredicciones = modeloResNet50.predict(set_test)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:11:16.689849Z","iopub.execute_input":"2021-06-05T18:11:16.690516Z","iopub.status.idle":"2021-06-05T18:12:31.052222Z","shell.execute_reply.started":"2021-06-05T18:11:16.690459Z","shell.execute_reply":"2021-06-05T18:12:31.051327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tomamos nota del orden en el que vienen las etiquetas en el generador de imágenes del conjunto de entrenamiento-validación ya que la predicción se efectuará de acuerdo a este orden.","metadata":{}},{"cell_type":"code","source":"print('Orden de las etiquetas: {}'.format(set_entreno.class_indices.keys()))\n#set_test.class_indices.keys()","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:13:59.625216Z","iopub.execute_input":"2021-06-05T18:13:59.625527Z","iopub.status.idle":"2021-06-05T18:13:59.630283Z","shell.execute_reply.started":"2021-06-05T18:13:59.625497Z","shell.execute_reply":"2021-06-05T18:13:59.629195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Orden de las etiquetas: {}'.format(set_validacion_ordenado.class_indices.keys()))\n#set_test.class_indices.keys()","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:02.728993Z","iopub.execute_input":"2021-06-05T18:14:02.729318Z","iopub.status.idle":"2021-06-05T18:14:02.733279Z","shell.execute_reply.started":"2021-06-05T18:14:02.72929Z","shell.execute_reply":"2021-06-05T18:14:02.732495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Para confirmar\n#set_entreno.class_indices.items()","metadata":{"execution":{"iopub.status.busy":"2021-05-31T15:16:57.229902Z","iopub.execute_input":"2021-05-31T15:16:57.230234Z","iopub.status.idle":"2021-05-31T15:16:57.235301Z","shell.execute_reply.started":"2021-05-31T15:16:57.230203Z","shell.execute_reply":"2021-05-31T15:16:57.234472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Guardamos en un array las predicciones con una mayor probabilidad en cada imagen del conjunto de test.","metadata":{}},{"cell_type":"code","source":"preds_clases_indices = predicciones.argmax(axis=-1)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:11.299312Z","iopub.execute_input":"2021-06-05T18:14:11.299675Z","iopub.status.idle":"2021-06-05T18:14:11.306092Z","shell.execute_reply.started":"2021-06-05T18:14:11.299624Z","shell.execute_reply":"2021-06-05T18:14:11.305169Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Eliminamos el prefijo  'test/' del nombre de los ficheros.","metadata":{}},{"cell_type":"code","source":"ficheros_test = [str(i).replace('test/', '')  for i in set_test.filenames]","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:19.067951Z","iopub.execute_input":"2021-06-05T18:14:19.068274Z","iopub.status.idle":"2021-06-05T18:14:19.075471Z","shell.execute_reply.started":"2021-06-05T18:14:19.068244Z","shell.execute_reply":"2021-06-05T18:14:19.07438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Construimos una tupla (ficheros, clase predicha), a partir de los arrays anteriores. Una vez construido, lo guardamos en un DataFrame y lo exportamos como test.csv.","metadata":{}},{"cell_type":"code","source":"indices_de_clases = {v: k for k, v in set_entreno.class_indices.items()}\npreds_clases = np.vectorize(indices_de_clases.get)(preds_clases_indices)\nfilenames_to_clases = list(zip(ficheros_test, preds_clases))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:24.57003Z","iopub.execute_input":"2021-06-05T18:14:24.570376Z","iopub.status.idle":"2021-06-05T18:14:24.58113Z","shell.execute_reply.started":"2021-06-05T18:14:24.570333Z","shell.execute_reply":"2021-06-05T18:14:24.580272Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Resumen (conteo) de las predicciones por clases:","metadata":{}},{"cell_type":"code","source":"from collections import Counter\nprint(\"conteo de predicciones: {}\".format(Counter(preds_clases)))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:32.830546Z","iopub.execute_input":"2021-06-05T18:14:32.830902Z","iopub.status.idle":"2021-06-05T18:14:32.839725Z","shell.execute_reply.started":"2021-06-05T18:14:32.830871Z","shell.execute_reply":"2021-06-05T18:14:32.83862Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Obtenemos una gráfica de nuestras predicciones en porcentaje (frecuencias relativas).","metadata":{}},{"cell_type":"code","source":"# Grafica del % de etiquetas en las predicciones.\nplt.figure(figsize=(6,6))\nplt.title(\"Frecuencias relativas en % de las predicciones\", fontsize = 14, color = 'darkblue')\n# Contamos los valores por clases y vamos calculando sus frecuencias relativas.\nsorted_counts_pred = list(np.array(list(Counter(preds_clases).values()))/sum(list(Counter(preds_clases).values())))\nplt.pie(sorted_counts_pred, labels = list(Counter(preds_clases).keys()), startangle = 30,\n        counterclock = False, autopct='%.2f%%', textprops={'fontsize':'14'})\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T18:14:42.901999Z","iopub.execute_input":"2021-06-05T18:14:42.902385Z","iopub.status.idle":"2021-06-05T18:14:43.043427Z","shell.execute_reply.started":"2021-06-05T18:14:42.902343Z","shell.execute_reply":"2021-06-05T18:14:43.042535Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Observamos que hay un mayor número de imágenes con enfermedades respecto a las sanas (C0), lo cual no está muy en línea con los sets de entrenamiento y validación. En cualquier caso es posible que el set de test sea completamente diferente en cuanto a sus clases.","metadata":{}},{"cell_type":"code","source":"prediccionesResNet50 = pd.DataFrame(filenames_to_clases)\npredicciones_fichero = 'test.csv'\nwith open(predicciones_fichero, mode='w') as f:\n    prediccionesResNet50.to_csv(f)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:04:52.120942Z","iopub.execute_input":"2021-06-05T15:04:52.121339Z","iopub.status.idle":"2021-06-05T15:04:52.155689Z","shell.execute_reply.started":"2021-06-05T15:04:52.121306Z","shell.execute_reply":"2021-06-05T15:04:52.154654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Por últmos obtenemos una imagen por clase predicha y la visualizamos para 'comprobar' los resultados.","metadata":{}},{"cell_type":"code","source":"path_test = '/kaggle/input/kaggle-plant-pathology-2021-modificat/test/'\n\n# Grafico con una imagen por clase tras la  prediccion.\nindices = []\nimagenes=[]\netiquetas = []\nclass_names = []\n\nwhile len(imagenes) < 6:\n    for i in filenames_to_clases:\n    \n        etiqueta = i[1]\n        if ((etiqueta in etiquetas) == False):\n            imagenes.append(path_test+str(i[0]))\n            etiquetas.append(i[1])\n            #print(etiquetas)\n\n    #images = set_test.next() \n    #labels = preds_clases_indices\n  \nfig = plt.figure(figsize=(24, 24))\nfor i in range(len(imagenes)):\n    ax = plt.subplot(6, 6, i+1)\n    img = cv2.imread(imagenes[i], 1)\n    plt.imshow(img)\n    plt.title('Clase predicha: ' + etiquetas[i])\n    plt.axis(\"off\")\nfig.suptitle(\"Una imagen predicha de cada clase (label) con nuestro modelo ResNet50->D64->D16->D6\", fontsize = 24);\nfig.tight_layout()","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:05:39.501416Z","iopub.execute_input":"2021-06-05T15:05:39.50199Z","iopub.status.idle":"2021-06-05T15:05:40.243801Z","shell.execute_reply.started":"2021-06-05T15:05:39.50196Z","shell.execute_reply":"2021-06-05T15:05:40.242464Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Calculamos las predicciones en el conjunto de validación para corroborar el f1-score, el cual calculamos nuevamente.","metadata":{}},{"cell_type":"code","source":"predicciones_val_1 = modeloResNet50.predict(set_validacion_ordenado)\npredicciones_val = np.argmax(predicciones_val_1, axis=-1)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:05:57.058607Z","iopub.execute_input":"2021-06-05T15:05:57.059002Z","iopub.status.idle":"2021-06-05T15:06:34.222244Z","shell.execute_reply.started":"2021-06-05T15:05:57.05897Z","shell.execute_reply":"2021-06-05T15:06:34.220975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\nprint(\"Precision f1-score 'macro': {}\".format(precision_score(set_validacion_ordenado.classes, predicciones_val , average=\"macro\")))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:06:37.96312Z","iopub.execute_input":"2021-06-05T15:06:37.963497Z","iopub.status.idle":"2021-06-05T15:06:37.974433Z","shell.execute_reply.started":"2021-06-05T15:06:37.963456Z","shell.execute_reply":"2021-06-05T15:06:37.972919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Gráficas del modelo elegido: Accuracy, f1-score, precision y recall</u>\nA continuación se obtienen las gráficas que incluyen accuracy, f1-score, precision y recall <u>**ponderadas**</u> (`average = 'weighted'`) del entrenamiento del modelo ResNet50 elegido.","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\n\n# Se calculan las métricas al final para el conjunto de validación\nprint('Precisión (ponderada=weighted) en el conjunto de validación: {}'.format(precision_score(set_validacion_ordenado.classes, predicciones_val , average=\"weighted\")))\nprint('Recall (ponderada=weighted) en el conjunto de validación:    {}'.format(recall_score(set_validacion_ordenado.classes, predicciones_val , average=\"weighted\")))\nprint('f1-score (ponderada=weighted) en el conjunto de validación:  {}'.format(f1_score(set_validacion_ordenado.classes, predicciones_val , average=\"weighted\")))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:06:42.737393Z","iopub.execute_input":"2021-06-05T15:06:42.737763Z","iopub.status.idle":"2021-06-05T15:06:42.754503Z","shell.execute_reply.started":"2021-06-05T15:06:42.737734Z","shell.execute_reply":"2021-06-05T15:06:42.75308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#fig, ((ax1,ax2, ax3, ax4), (ax5, ax6, ax7, ax8)) = plt.subplots(2,4, figsize=(24,8))\nfig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(2,3, figsize=(18,6))\nplt.suptitle(\"Gráficas de pérdida, f1-score, accuracy, recall y precision \\n para el modelo ResNet50->D64->D16->D6 (entren. y valid.)\", color='darkblue')\nplt.style.use('seaborn')\n\nax1.plot(historymodeloResNet50.history['loss'], color='blue')\nax1.plot(historymodeloResNet50.history['val_loss'], color='orange')\nax1.set_xticks(np.arange(len(historymodeloResNet50.history['loss'])))\nax1.legend(['pérdida entren.', 'perdida val.'])\nax1.set_xlabel('Epochs')\nax1.set_ylabel('Pérdida (loss)')\nax1.set_title(\"Pérdida Modelo ResNet50 final D64->D16->D6\")\n\nax2.plot(historymodeloResNet50.history['f1_score'], color='maroon')\nax2.plot(historymodeloResNet50.history['val_f1_score'], color='lime')\nax2.set_xticks(np.arange(len(historymodeloResNet50.history['loss'])))\nax2.legend(['f1-score entren.', 'f1-score valid.'])\nax2.set_xlabel('Epochs')\nax2.set_ylabel('F1-Score')\nax2.set_title(\"f1-score(macro) Modelo ResNet50 final D64->D16->D6\")\n\nax3.plot(historymodeloResNet50.history['accuracy'], color='purple')\nax3.plot(historymodeloResNet50.history['val_accuracy'], color='red')\nax3.set_xticks(np.arange(len(historymodeloResNet50.history['loss'])))\nax3.legend(['Accuracy entren.', 'Accuracy valid.'])\nax3.set_xlabel('Epochs')\nax3.set_ylabel('Accuracy')\nax3.set_title(\"Accuracy (cross-entropy) Modelo ResNet50 final D64->D16->D6\")\n\nax4.plot(historymodeloResNet50.history['precision'], color='grey')\nax4.plot(historymodeloResNet50.history['val_precision'], color='green')\nax4.set_xticks(np.arange(len(historymodeloResNet50.history['loss'])))\nax4.legend(['Precision entren.', 'Precision valid.'])\nax4.set_xlabel('Epochs')\nax4.set_ylabel('Precision')\nax4.set_title(\"Precision Modelo ResNet50 final D64->D16->D6\")\n\nax5.plot(historymodeloResNet50.history['recall'], color='gold')\nax5.plot(historymodeloResNet50.history['val_recall'], color='darkblue')\nax5.set_xticks(np.arange(len(historymodeloResNet50.history['loss'])))\nax5.legend(['Recall entren.', 'Recall valid.'])\nax5.set_xlabel('Epochs')\nax5.set_ylabel('Recall')\nax5.set_title(\"Recall Modelo ResNet50 final D64->D16->D6\")\n\nplt.tight_layout()\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:06:47.553307Z","iopub.execute_input":"2021-06-05T15:06:47.553699Z","iopub.status.idle":"2021-06-05T15:06:49.93389Z","shell.execute_reply.started":"2021-06-05T15:06:47.553661Z","shell.execute_reply":"2021-06-05T15:06:49.932782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, f1_score, recall_score, multilabel_confusion_matrix\n\nclases_val_ord = set_validacion_ordenado.class_indices.keys()\n# Calculamos la matriz de confusion\n\ncfmatrix  = confusion_matrix(set_validacion_ordenado.classes, predicciones_val)\ncfmatrixmulti = multilabel_confusion_matrix(set_validacion_ordenado.classes, predicciones_val)","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:06:55.837401Z","iopub.execute_input":"2021-06-05T15:06:55.837895Z","iopub.status.idle":"2021-06-05T15:06:55.857962Z","shell.execute_reply.started":"2021-06-05T15:06:55.837847Z","shell.execute_reply":"2021-06-05T15:06:55.856617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Graficamos con seaborn y un heatmap para visualizar\nimport seaborn as sns\nplt.rcParams['figure.figsize'] = (12.0, 8.0)\nplt.rcParams['font.family'] = \"serif\"\nplt.rcParams['font.size'] = \"12\"\nsns.heatmap(cfmatrix, annot=True, cmap=\"BuPu\", fmt=\".0f\", xticklabels=clases_val_ord, yticklabels=clases_val_ord)\nplt.tight_layout()\nplt.title(\"Matriz de confusión modelo RestNet50: \\n (RestNet50->D64->D16->D6)\\n\")\nplt.ylabel('Clase verdadera')\nplt.xlabel('Clase predicha')\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:07:08.442011Z","iopub.execute_input":"2021-06-05T15:07:08.442396Z","iopub.status.idle":"2021-06-05T15:07:09.004617Z","shell.execute_reply.started":"2021-06-05T15:07:08.442365Z","shell.execute_reply":"2021-06-05T15:07:09.003605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Classification Report/Informe sobre la clasificación de acuerdo a las 6 enfermedades del manzano')\nprint(classification_report(set_validacion_ordenado.classes, predicciones_val,target_names=clases_val_ord))","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:07:13.547979Z","iopub.execute_input":"2021-06-05T15:07:13.548368Z","iopub.status.idle":"2021-06-05T15:07:13.566676Z","shell.execute_reply.started":"2021-06-05T15:07:13.548338Z","shell.execute_reply":"2021-06-05T15:07:13.565075Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <u>Apéndice</u>: Modelos entrenados en el problema original Kaggle y en nuestro problema para la selección previa de modelos a utilizar.\nA continuación se presentan los 6 modelos probados tanto para el desafío original en Kaggle como para nuestro problema. Los modelos son:  \n1. Resnet50v2  \n2. InceptionV3  \n3. EfficientNetB0  \n4. Resnet50  \n5. IncepcionResNet50V2  \n6. DenseNet121  \n \nPara estos modelos dentro del desafío original, es necesario construir unos generadores algo diferentes que toman como input los subdirectorios de las imágenes reducidas en tamaño (256), ya que si utilizamos las imágenes originales éstas son demasiado grandes en tamaño y el entrenamiento se ralentiza mucho. En cualquier caso estos generadores requieren de un dataframe diferente, donde las clases se consideran multilabels y la binarizacion (OneHotEncoder) se implementa con un método diferente MultiLabelBinarizer() de sklearn.preprocessing.\n","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator,load_img\n\ntrain_data = train_datagen.flow_from_dataframe(\n    df_entreno2,\n    directory= '/kaggle/input/resized-plant2021/img_sz_256/', # originales en 'plant-pathology-2021-fgvc8/train_images/'\n    x_col = \"image\",\n    y_col=  labelslista,\n    color_mode=\"rgb\",\n    #target_size = (256,256),\n    #class_mode=\"categorical\",\n    batch_size=32,\n    subset = \"training\",\n    shuffle=True,\n    seed=27,\n    class_mode='raw'\n)\n\nvalidacion_data = train_datagen.flow_from_dataframe(\n    df_entreno2,\n    directory= '/kaggle/input/resized-plant2021/img_sz_256/', # originales en 'plant-pathology-2021-fgvc8/train_images/'\n    x_col = \"image\",\n    y_col=  labelslista,\n    color_mode=\"rgb\",\n    #target_size = (256,256),\n    #class_mode=\"categorical\",\n    batch_size=32,\n    subset = \"validation\",\n    shuffle=True,\n    seed=27,\n    class_mode='raw'\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Construcción y entrenamiento de 6 modelos (preselección)</u>:\nSe construyen y entrenan los siguientes modelos para determinar cuáles de ellos son más prometedores respecto a la métrica f1-score(macro) utilizada:  \n\n    1.-Resnet50v2  \n    2.-InceptionV3  \n    3.-EfficientNetB0  \n    4.-Resnet50  \n    5.-IncepcionResNet50V2  \n    6.-DenseNet121  ","metadata":{}},{"cell_type":"markdown","source":"### <u>Modelo ResNet50V2:</u>","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, Lambda, Dense, Flatten\nfrom tensorflow.keras.models import Model\n#from tensorflow.keras.applications.inception_resnet_v2 import InceptionResNetV2\n#from tensorflow.keras.applications.inception_resnet_v2 import preprocess_input\nfrom tensorflow.keras.applications.resnet_v2 import ResNet50V2\nfrom tensorflow.keras.applications.resnet_v2 import preprocess_input\nfrom tensorflow.keras.models import Sequential\n\ndef create_model():\n    \n    pretrained = ResNet50V2(include_top=False, weights='imagenet',input_shape=[256,256, 3])\n    # pretrained = InceptionResNetV2(include_top=False, weights='imagenet',input_shape=[256, 256, 3])\n            \n    x = pretrained.output\n    x = tf.keras.layers.GlobalAveragePooling2D()(x)\n    outputs = tf.keras.layers.Dense(6, activation=\"sigmoid\", dtype='float32')(x)\n        \n    model = tf.keras.Model(pretrained.input, outputs, name='modeloResNetV2')\n    return model\n\nmodel = create_model()","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:20:51.153064Z","iopub.execute_input":"2021-05-30T14:20:51.153376Z","iopub.status.idle":"2021-05-30T14:20:54.887361Z","shell.execute_reply.started":"2021-05-30T14:20:51.153346Z","shell.execute_reply":"2021-05-30T14:20:54.886064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.name","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:21:36.440522Z","iopub.execute_input":"2021-05-30T14:21:36.440852Z","iopub.status.idle":"2021-05-30T14:21:36.446935Z","shell.execute_reply.started":"2021-05-30T14:21:36.440822Z","shell.execute_reply":"2021-05-30T14:21:36.445931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Función que compilará el modelo con Adam y la learning rate como parámetro. \n# También introducimos nuestra medidad como F1-score 'macro'\n\ndef compile_model(model, lr=0.03):\n    \n    optimizer = tf.keras.optimizers.SGD(lr=0.03, decay=1e-4, momentum=0.8, nesterov=True)\n    \n    loss = tf.keras.losses.CategoricalCrossentropy()\n    \n    # tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n    tfa.metrics.F1Score(num_classes = 6, average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:20:58.766587Z","iopub.execute_input":"2021-05-30T14:20:58.766937Z","iopub.status.idle":"2021-05-30T14:20:58.774077Z","shell.execute_reply.started":"2021-05-30T14:20:58.766901Z","shell.execute_reply":"2021-05-30T14:20:58.77171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Los siguientes parámetros son comunes a todos los modelos. Introducen un 'checkpoint' para guardar los mejores modelos según se van obteniendo máximos en nuestra función (f1-score en validación). También definimos un EarlyStop con 5 epochs de paciencia y un parámetro que actúa como reductor del learning rate si se producen situaciones de estancamiento ([`ReduceLROnPlateau()`](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/ReduceLROnPlateau))","metadata":{}},{"cell_type":"code","source":"METRIC = \"val_f1_score\"\n\ndef create_callbacks(metric = METRIC):\n    \n    guardar_path = './mejor_modelo_' + model.name + '_' + timestampmodelo + '.h5'\n    \n    checkpoint = tf.keras.callbacks.ModelCheckpoint(\n        filepath = guardar_path,\n        monitor = metric,\n        mode ='max',\n        save_best_only=True,\n        verbose=1,\n    )\n\n    reducccionlr = tf.keras.callbacks.ReduceLROnPlateau(\n        monitor = metric,\n        mode = 'max',\n        factor = 0.1,\n        patience = 3,\n        verbose = 0\n    )\n\n    earlystop = tf.keras.callbacks.EarlyStopping(\n        monitor = metric,\n        mode ='max',\n        patience = 5, \n        verbose = 1\n    )\n    \n    callbacks = [checkpoint, reducccionlr, earlystop]         \n    \n    return callbacks","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS= 10\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    model = create_model()\n    model = compile_model(model, lr=0.001)\n   \n    callbacks = create_callbacks()\n    \n    history = model.fit(\n                        train_data,\n                        epochs=EPOCHS,\n                        callbacks=callbacks,\n                        validation_data = validacion_data,\n                        verbose=VERBOSE\n                       )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultados = pd.DataFrame(history.history)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"modeloResNetV2 = 'modeloResNetV2_history.csv'\nwith open(modeloResNetV2, mode='w') as f:\n    resultados.to_csv(f)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <u>Modelo EfficientNet80 (problema original):</u>","metadata":{}},{"cell_type":"markdown","source":"Aquí utilizamos tanto los datos de nuestro caso como los **datos originales** del desafío Kaggle `/kaggle/input/plant-pathology-2021-fgvc8/train.csv'` para ir probando diferentes modelos y su performance e idoneidad para nuestro problema.","metadata":{}},{"cell_type":"code","source":"p_train = pd.read_csv('/kaggle/input/plant-pathology-2021-fgvc8/train.csv')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Individualizamos cada etiqueta de cada imagen, ya que cada una trae varias.","metadata":{}},{"cell_type":"code","source":"p_train['labels'] = p_train['labels'].apply(lambda string: string.split(' '))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Para la construcción de este dataframe (df_entreno) hay que referirse a un segundo [notebook](https://www.kaggle.com/nameeman/practica-1-dl-galech-amillano-probl-originalkaggle/edit) donde se trata el problema Kaggle original (multiclase-multilabel).  \nhttps://www.kaggle.com/nameeman/practica-1-dl-galech-amillano-probl-originalkaggle/edit","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator,load_img\n\ntrain_data = train_datagen.flow_from_dataframe(\n    df_entreno,\n    directory= '/kaggle/input/resized-plant2021/img_sz_256/', # originales en 'plant-pathology-2021-fgvc8/train_images/'\n    x_col = \"image\",\n    y_col=  \"labels\",\n    color_mode=\"rgb\",\n    #target_size = (256,256),\n    #class_mode=\"categorical\",\n    batch_size=32,\n    subset = \"training\",\n    shuffle=True,\n    seed=27,\n    class_mode='raw'\n)\n\nvalidacion_data = train_datagen.flow_from_dataframe(\n    df_entreno,\n    directory = '/kaggle/input/resized-plant2021/img_sz_256/',\n    x_col = \"image\",\n    y_col = \"labels\",\n    color_mode=\"rgb\",\n    #target_size = (256,256),\n    #class_mode=\"categorical\",\n    batch_size=32,\n    shuffle=True,\n    subset = \"validation\",\n    seed=27,\n    class_mode='raw'\n)\n\n# Este último set no implementa shuffle (shuffle=False) y se usará para las \n# predicciones y así poder comparar las clases de las predicciones vs. clases \n# verdaderas.\nset_validacion_ordenado = train_datagen.flow_from_dataframe(\n    df_entreno,\n    directory = '/kaggle/input/resized-plant2021/img_sz_256/',\n    x_col=\"image\",\n    y_col = \"labels\",\n    #target_size=(256, 256),\n    color_mode=\"rgb\",\n    #class_mode=\"categorical\",\n    batch_size=32,\n    shuffle=False,\n    seed=27,\n    subset='validation',\n    interpolation=\"nearest\",\n    class_mode='raw'\n)\n\nset_test = test_datagen.flow_from_directory(\n    directory = '/kaggle/input/plant-pathology-2021-fgvc8/test_images/',\n    batch_size=1,\n    # No tenemos y_col porque son las que tenemos que predecir\n    target_size=(256, 256),\n    color_mode=\"rgb\",\n    class_mode=None,\n    shuffle=False,\n    seed=27,\n#    interpolation=\"nearest\",\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import EfficientNetB0\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\ndef create_EffNetB0model():\n    \n    pretrained = EfficientNetB0(include_top=False, weights='imagenet',input_shape=[256, 256, 3], pooling = 'avg')\n    x = pretrained.output\n    x = tf.keras.layers.Dense(64, activation='relu')(x)\n    x = tf.keras.layers.Dense(16, activation='relu')(x)\n    # finally, the softmax for the classifier \n    #predictions = tf.keras.layers.Dense(6, activation='sigmoid')(x)\n    \n    outputs = tf.keras.layers.Dense(6,activation=\"sigmoid\", dtype='float32')(x)\n    model = tf.keras.Model(pretrained.input, outputs, name = 'modeloEffNetB0')\n    return model\n\nmodel = create_EffNetB0model()\n\n\ndef compile_model(model, lr=0.0001):\n    \n    optimizer = tf.keras.optimizers.Adam(lr=lr)\n    \n    loss = tf.keras.losses.BinaryCrossentropy()\n    \n    #tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n    tfa.metrics.F1Score(num_classes = 6, average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:35:04.008563Z","iopub.execute_input":"2021-06-05T15:35:04.008978Z","iopub.status.idle":"2021-06-05T15:35:05.982686Z","shell.execute_reply.started":"2021-06-05T15:35:04.008948Z","shell.execute_reply":"2021-06-05T15:35:05.981618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS= 10\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    #model = create_model()\n    modeloEffNetB0 = compile_model(model)\n    callbacks = create_callbacks(modeloEffNetB0)\n    historymodeloDenseNet121 = modeloEffNetB0.fit(\n                        set_entreno,\n                        epochs = EPOCHS,\n                        callbacks = callbacks,\n                        validation_data = set_validacion,\n                        verbose = VERBOSE\n                       )","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:35:57.623893Z","iopub.execute_input":"2021-06-05T15:35:57.624257Z","iopub.status.idle":"2021-06-05T15:35:57.634806Z","shell.execute_reply.started":"2021-06-05T15:35:57.624227Z","shell.execute_reply":"2021-06-05T15:35:57.633467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Modelo InceptionV3:</u>","metadata":{}},{"cell_type":"code","source":"try: # TPUs, si tenemos habilitado el cluster y generamos archivos tipo .tfrec\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n\nexcept ValueError: # GPUs\n    strategy = tf.distribute.MirroredStrategy() # for GPU or multi-GPU machines\n    #strategy = tf.distribute.get_strategy() # una CPU y una GPU\n    #strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy() # for clusters of multi-GPU machines\n\nprint(\"Number of accelerators: \", strategy.num_replicas_in_sync)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.applications.inception_v3 import InceptionV3\nfrom tensorflow.keras.applications.inception_v3 import preprocess_input\n\ndef create_InceptionV3():\n    \n    pretrained = InceptionV3(include_top=False, weights='imagenet',input_shape=[256, 256, 3], pooling = 'avg')\n    x = pretrained.output\n    #x = tf.keras.layers.GlobalAveragePooling2D()(x) # No necesitamos pooling porque lo ponemos en la salida del EfficientNetB0\n    outputs = tf.keras.layers.Dense(len(validacion_data.class_indices),activation=\"sigmoid\", dtype='float32')(x)\n    model = tf.keras.Model(pretrained.input, outputs, name = 'modeloInceptionV3')\n    return model\n\nmodel = create_InceptionV3()\n\n\ndef compile_model(model, lr=0.0001):\n    \n    optimizer = tf.keras.optimizers.Adam(lr=lr)\n    \n    loss = tf.keras.losses.CategoricalCrossentropy()\n    \n    #tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n    tfa.metrics.F1Score(num_classes = len(validacion_data.class_indices), average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.name","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS= 10\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    #model = create_model()\n    model = compile_model(model, lr=0.0001)\n   \n    callbacks = create_callbacks()\n    \n    historyIncepcionV3 = model.fit(\n                        train_data,\n                        epochs=EPOCHS,\n                        callbacks=callbacks,\n                        validation_data = validacion_data,\n                        verbose=VERBOSE\n                       )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosInceptionV3 = pd.DataFrame(historyIncepcionV3.history)\nmodeloInceptionV3_cvs_file = 'resultadosInceptionV3_history.csv'\nwith open(modeloInceptionV3_cvs_file, mode='w') as f:\n    resultadosInceptionV3.to_csv(f)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Modelo EfficientNetB0:</u>","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import EfficientNetB0\nfrom tensorflow.keras.applications.efficientnet import preprocess_input\n\ndef create_EffNetB0model():\n    \n    pretrained = EfficientNetB0(include_top=False, weights='imagenet',input_shape=[256, 256, 3], pooling = 'avg')\n    x = pretrained.output\n    x = tf.keras.layers.Dense(64, activation='relu')(x)\n    x = tf.keras.layers.Dense(16, activation='relu')(x)\n    \n    #x = tf.keras.layers.GlobalAveragePooling2D()(x) # No necesitamos pooling porque lo ponemos en la salida del EfficientNetB0\n    outputs = tf.keras.layers.Dense(6,activation=\"sigmoid\")(x)\n    model = tf.keras.Model(pretrained.input, outputs, name = 'modeloEffNetB0')\n    return model\n\nmodel = create_EffNetB0model()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 10\nVERBOSE = 1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    #model = create_EffNetB0model()\n    model = compile_model(model)\n \n    callbacks = create_callbacks(model)\n    \n    historyModeloEffNetB0 = model.fit(\n                    set_entreno,\n                    epochs=EPOCHS,\n                    callbacks=callbacks,\n                    validation_data = set_validacion,\n                    verbose = VERBOSE\n                   )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosEffNetB0 = pd.DataFrame(historyModeloEffNetB0.history)\nresultadosEffNetB0_csv_file = 'resultadosEffNetB0_history2.csv'\nwith open(resultadosEffNetB0_csv_file, mode='w') as f:\n    resultadosEffNetB0.to_csv(f)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Modelo RestNet50:</u> \nCreamos y entrenamos el modelo RestNet50 con el mismo set de datos, mismos parámetros y guardamos tanto el modelo como los resultados del entrenamiento.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.resnet import ResNet50\nfrom tensorflow.keras.applications.resnet import preprocess_input\n\ndef create_RestNet50():\n    \n    pretrained = ResNet50(include_top=False, weights='imagenet',input_shape=[256, 256, 3])\n    x = pretrained.output\n    x = tf.keras.layers.GlobalAveragePooling2D()(x) \n    # Para RestNet50 debemos usar softmax si utilizamos los pesos pre-entrenados.\n    outputs = tf.keras.layers.Dense(6,activation=\"sigmoid\", dtype='float32')(x)\n    model = tf.keras.Model(pretrained.input, outputs, name = 'modeloRestNet50')\n    return model\n\nmodel = create_RestNet50()\n\n\ndef compile_model(model):\n    \n    #optimizer = tf.keras.optimizers.Adam(lr=lr)\n    optimizer = tf.keras.optimizers.SGD(lr=0.03, decay=1e-4, momentum=0.8, nesterov=True)\n\n    \n    loss = tf.keras.losses.BinaryCrossentropy()\n    \n    #tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n    tfa.metrics.F1Score(num_classes = 6, average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Entrenamos el modelo RestNet50","metadata":{}},{"cell_type":"code","source":"EPOCHS = 10\nVERBOSE = 1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    callbacks = create_callbacks()\n    model = compile_model(model)\n    historyResNet50 = model.fit(\n                    train_data,\n                    epochs=EPOCHS,\n                    callbacks=callbacks,\n                    validation_data = validacion_data,\n                    verbose = VERBOSE\n                   )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosRestNet50 = pd.DataFrame(historyResNet50.history)\nmodeloResNet50 = 'modeloResNet50_history2.csv'\nwith open(modeloResNet50, mode='w') as f:\n    resultadosRestNet50.to_csv(f)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <u>Modelo DenseNet121:</u> \nCreamos y entrenamos el modelo DenseNet121 con el mismo set de datos, mismos parámetros y guardamos tanto el modelo como los resultados del entrenamiento.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.densenet import DenseNet121\nfrom tensorflow.keras.applications.densenet import preprocess_input\n#tf.keras.applications.densenet.DenseNet121\n\nseed = 1200\ntf.random.set_seed(seed)\n\n#base_model = tf.keras.applications.DenseNet121(weights=weight_path, include_top=False, pooling='avg')\nbase_model = tf.keras.applications.DenseNet121(input_shape=(256,256,3),\n                                               include_top=False,\n                                               weights='imagenet',\n                                               pooling='avg')\n#pesos_entrenados = '../input/keras-pretrained-models/'\n#model = keras.applications.InceptionResNetV2(weights=pesos_entrenados, include_top=False, input_shape=(256, 256, 3))\nbase_model.trainable = False\n\nx = base_model.output\n#fully connected layer\nx = tf.keras.layers.Dense(64, activation='relu')(x)\nx = tf.keras.layers.Dense(16, activation='relu')(x)\n# finally, the softmax for the classifier \npredictions = tf.keras.layers.Dense(6, activation='sigmoid')(x)\nmodeloDenseNet121 = tf.keras.Model(base_model.inputs, predictions, name='DenseNet121')\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <u>Gráficas del entrenamiento de los modelos</u>:\n\nSe muestra a continuación la evolución de la pérdida y de F1-Score para los conjuntos de entrenamiento y de validación para nuestros cuatro modelos.","metadata":{}},{"cell_type":"code","source":"resultados = pd.read_csv('/kaggle/input/resultados-modelos-prac1-gpu/modelos_Resultados_original_Kaggle.csv')","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:12:21.546379Z","iopub.execute_input":"2021-06-05T15:12:21.546852Z","iopub.status.idle":"2021-06-05T15:12:21.56755Z","shell.execute_reply.started":"2021-06-05T15:12:21.546821Z","shell.execute_reply":"2021-06-05T15:12:21.566228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"modelos = list(resultados.modelo.unique())\n\nfig = plt.figure(figsize=(18, 4))\nfig.suptitle(\"Pérdida en los 6 modelos\", fontsize = 20);\nplt.style.use('seaborn')\n\nfor i, j in enumerate(modelos):\n    ax = plt.subplot(1, 6, i+1)\n    ax.plot(resultados[resultados['modelo']==str(j)]['loss'].to_list(), color='blue')\n    ax.plot(resultados[resultados['modelo']==str(j)]['val_loss'].to_list(), color='orange')\n    ax.set_xticks(resultados[resultados['modelo']==str(j)]['epoch'].to_list())\n    ax.set_xticklabels(resultados[resultados['modelo']==str(j)]['epoch'].to_list())\n    ax.legend(['pérdida entren.', 'perdida val.'])\n    ax.set_xlabel('Epochs')\n    ax.set_ylabel('Pérdida (loss)')\n    ax.set_title(\"Pérdida Modelo \"+str(j))\nplt.tight_layout()\nplt.show();\n\nfig2 = plt.figure(figsize=(18, 4))\nfig2.suptitle(\"f1-score en los 6 modelos\", fontsize = 20);\nplt.style.use('seaborn')\n\nfor i, j in enumerate(modelos):\n\n    ax = plt.subplot(1, 6, i+1)\n    ax.plot(resultados[resultados['modelo']==str(j)]['f1_score'].to_list(), color='maroon')\n    ax.plot(resultados[resultados['modelo']==str(j)]['val_f1_score'].to_list(), color='lime')\n    ax.set_xticks(resultados[resultados['modelo']==str(j)]['epoch'].to_list())\n    ax.set_xticklabels(resultados[resultados['modelo']==str(j)]['epoch'].to_list())\n    ax.legend(['f1-score entren.', 'f1-score val.'])\n    ax.set_xlabel('Epochs')\n    ax.set_ylabel('f1-score (macro)')\n    ax.set_title(\"F1-score Modelo \"+str(j))\n\nplt.tight_layout()\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-05T15:12:23.747989Z","iopub.execute_input":"2021-06-05T15:12:23.748634Z","iopub.status.idle":"2021-06-05T15:12:26.74552Z","shell.execute_reply.started":"2021-06-05T15:12:23.748582Z","shell.execute_reply":"2021-06-05T15:12:26.74448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### <u> Modelo InceptionResNetv2</u>","metadata":{}},{"cell_type":"code","source":"seed = 1200\ntf.random.set_seed(seed)\n\npesos_entrenados = '../input/keras-pretrained-models/inception_resnet_v2_weights_tf_dim_ordering_tf_kernels_notop.h5'\nmodel = keras.applications.InceptionResNetV2(weights=pesos_entrenados, include_top=False, input_shape=(256, 256, 3))\n","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:24:48.375635Z","iopub.execute_input":"2021-05-30T14:24:48.375986Z","iopub.status.idle":"2021-05-30T14:24:56.753824Z","shell.execute_reply.started":"2021-05-30T14:24:48.375952Z","shell.execute_reply":"2021-05-30T14:24:56.752789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"modelo_sobre_IncRSNet50V2 = tf.keras.Sequential([\n    model,\n    keras.layers.GlobalAveragePooling2D(),\n    keras.layers.Dense(6, kernel_initializer=keras.initializers.RandomUniform(seed=seed),\n                        bias_initializer=keras.initializers.Zeros(), name='dense_top', activation='sigmoid')\n])\n\n# Freezing the weights\nfor layer in modelo_sobre_IncRSNet50V2.layers[:-1]:\n    layer.trainable=False\n    \nmodelo_sobre_IncRSNet50V2.summary()","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:25:39.561422Z","iopub.execute_input":"2021-05-30T14:25:39.561768Z","iopub.status.idle":"2021-05-30T14:25:41.011725Z","shell.execute_reply.started":"2021-05-30T14:25:39.561735Z","shell.execute_reply":"2021-05-30T14:25:41.01089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 10\nVERBOSE = 1\nf1 = tfa.metrics.F1Score(num_classes=6, average='macro')\n\n#callbacks = keras.callbacks.EarlyStopping(monitor=f1, patience=3, mode='max', restore_best_weights=True)\n\n\nmodelo_sobre_IncRSNet50V2.compile(loss=tf.keras.losses.CategoricalCrossentropy(), optimizer=keras.optimizers.SGD(lr=0.03, decay=1e-4, momentum=0.8, nesterov=True), \n              metrics= [f1])\n\nwith tf.device('/device:GPU:0'):\n    \n    callbacks = create_callbacks(modelo_sobre_IncRSNet50V2)\n    #model = compile_model(model, lr=0.0001)\n    history_IncRSNet50V2 = modelo_sobre_IncRSNet50V2.fit(\n                    set_entreno,\n                    epochs=EPOCHS,\n                    callbacks=callbacks,\n                    validation_data = set_validacion,\n                    verbose = VERBOSE\n                   )\n\n#modelo_sobre_IncRSNet50V2.fit(train_data, epochs=20, callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:29:48.17634Z","iopub.execute_input":"2021-05-30T14:29:48.176684Z","iopub.status.idle":"2021-05-30T14:55:59.157387Z","shell.execute_reply.started":"2021-05-30T14:29:48.176645Z","shell.execute_reply":"2021-05-30T14:55:59.156561Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosIncepRSNet50V2 = pd.DataFrame(history_IncRSNet50V2.history)\nmodeloIncepResNet50v2 = 'modeloIncepRSNet50V2_history.csv'\nwith open(modeloIncepResNet50v2, mode='w') as f:\n    resultadosIncepRSNet50V2.to_csv(f)","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:56:04.575428Z","iopub.execute_input":"2021-05-30T14:56:04.575764Z","iopub.status.idle":"2021-05-30T14:56:04.584685Z","shell.execute_reply.started":"2021-05-30T14:56:04.575732Z","shell.execute_reply":"2021-05-30T14:56:04.583706Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Modelo DenseNet121","metadata":{}},{"cell_type":"code","source":"# Probamos un DenseNet121\nfrom tensorflow.keras.applications.densenet import DenseNet121\nfrom tensorflow.keras.applications.densenet import preprocess_input\n#tf.keras.applications.densenet.DenseNet121\n\nseed = 43\ntf.random.set_seed(seed)\n\n#base_model = tf.keras.applications.DenseNet121(weights=weight_path, include_top=False, pooling='avg')\nbase_model = tf.keras.applications.DenseNet121(input_shape=(256,256,3),\n                                               include_top=False,\n                                               weights='imagenet',\n                                               pooling='avg')\nx = base_model.output\n#fully connected layer\n#x = tf.keras.layers.Dense(64, activation='relu')(x)\n#x = tf.keras.layers.Dense(16, activation='relu')(x)\n# finally, the softmax for the classifier \noutput = tf.keras.layers.Dense(6, activation='sigmoid')(x)\nmodeloDenseNet121orig = tf.keras.Model(base_model.inputs, output, name='DenseNet121')\n","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:58:56.626671Z","iopub.execute_input":"2021-05-30T14:58:56.627242Z","iopub.status.idle":"2021-05-30T14:58:59.024808Z","shell.execute_reply.started":"2021-05-30T14:58:56.627192Z","shell.execute_reply":"2021-05-30T14:58:59.023981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 10\nVERBOSE = 1\nf1 = tfa.metrics.F1Score(num_classes=6, average='macro')\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n\n    callbacks = create_callbacks(modeloDenseNet121orig)\n    modeloDenseNet121orig = compile_model(modeloDenseNet121orig)\n    history_DenseNet121 = modeloDenseNet121orig.fit(\n                    #train_data,\n                    set_entreno,\n                    epochs=EPOCHS,\n                    callbacks=callbacks,\n                    validation_data = set_validacion, \n                    #validation_data = validacion_data,\n                    verbose = VERBOSE\n                   )","metadata":{"execution":{"iopub.status.busy":"2021-05-30T14:59:10.499342Z","iopub.execute_input":"2021-05-30T14:59:10.499682Z","iopub.status.idle":"2021-05-30T15:26:18.174541Z","shell.execute_reply.started":"2021-05-30T14:59:10.499652Z","shell.execute_reply":"2021-05-30T15:26:18.173733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resultadosDenseNet121= pd.DataFrame(history_DenseNet121.history)\nmodeloDenseNet121 = 'modeloDenseNet121_history2.csv'\nwith open(modeloDenseNet121, mode='w') as f:\n    resultadosDenseNet121.to_csv(f)","metadata":{"execution":{"iopub.status.busy":"2021-05-30T15:26:24.130672Z","iopub.execute_input":"2021-05-30T15:26:24.131024Z","iopub.status.idle":"2021-05-30T15:26:24.13746Z","shell.execute_reply.started":"2021-05-30T15:26:24.13099Z","shell.execute_reply":"2021-05-30T15:26:24.136584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_generator = test_datagen.flow_from_directory()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_generator.reset()\npredicciones = model.predict_generator(test_generator, verbose=1)","metadata":{},"execution_count":null,"outputs":[]}]}