{"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":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **IMPORTACIONES**","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nimport tensorflow as tf\nimport tensorflow.keras as keras\nfrom tensorflow.keras import layers\n\nfrom tensorflow.keras.layers.experimental import preprocessing\nfrom tensorflow.keras.preprocessing import image_dataset_from_directory\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nimport matplotlib.pyplot as plt\nimport cv2\n\nfrom sklearn.model_selection import train_test_split     #separación del dataset\n\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau\n\nfrom tensorflow.keras import regularizers        #weight regularization\n\nfrom sys import getsizeof                    #Ver tamaño de las variables en Megabytes \nimport gc                                    #Liberar memoria\n\nimport time\n\nfrom sklearn.utils import class_weight ","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:12:55.906876Z","iopub.execute_input":"2022-08-02T01:12:55.907638Z","iopub.status.idle":"2022-08-02T01:12:55.917070Z","shell.execute_reply.started":"2022-08-02T01:12:55.907597Z","shell.execute_reply":"2022-08-02T01:12:55.916121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **VARIABLES**","metadata":{}},{"cell_type":"code","source":"SEED = 42                  #Semilla para garantizar la reproducibilidad del programa (asignada a parámetros random_state y seed)","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:12:55.921728Z","iopub.execute_input":"2022-08-02T01:12:55.922373Z","iopub.status.idle":"2022-08-02T01:12:55.927818Z","shell.execute_reply.started":"2022-08-02T01:12:55.922340Z","shell.execute_reply":"2022-08-02T01:12:55.926519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **FUNCIONES**","metadata":{}},{"cell_type":"code","source":"# Carga y Ajuste del dataset de entrenamiento\ndef load_traindf():\n    \n    traindf = pd.read_csv('../input/landmark-recognition-2021/train.csv')\n    \n    #Añadir a train.csv la columna con la dirección de cada imagen para su posterior lectura\n    traindf['img_path'] = traindf['id'].apply(lambda r: os.path.join('../input/landmark-recognition-2021/train', r[0], r[1], r[2], r + '.jpg'))\n    \n    #Conversión de columna landmark_id de int64 a int32 para reducir consumo de memoria\n    traindf['landmark_id'] = traindf['landmark_id'].apply(lambda x: np.int32(x))\n    \n    return traindf\n\n\n# Lectura y Redimensionamiento imágenes a partir de su ruta (path)\ndef img_read_resize(img_path): \n    img = plt.imread(img_path)\n    img_redim = cv2.resize(img,(IMG_SIZE,IMG_SIZE))\n    return img_redim\n\n# Rotación solo ortogonal de imágenes empleada en el generador de imágenes de entrenamiento\ndef orthogonal_rot(image):\n    return np.rot90(image, np.random.choice([-1, 0, 1]))\n\n# Obtener la memoria de cualquier objeto en Bytes. Referencia: https://goshippo.com/blog/measure-real-size-any-python-object/\nimport sys\ndef get_size(obj, seen=None):\n    \"\"\"Recursively finds size of objects\"\"\"\n    size = sys.getsizeof(obj)\n    if seen is None:\n        seen = set()\n    obj_id = id(obj)\n    if obj_id in seen:\n        return 0\n    # Important mark as seen *before* entering recursion to gracefully handle\n    # self-referential objects\n    seen.add(obj_id)\n    if isinstance(obj, dict):\n        size += sum([get_size(v, seen) for v in obj.values()])\n        size += sum([get_size(k, seen) for k in obj.keys()])\n    elif hasattr(obj, '__dict__'):\n        size += get_size(obj.__dict__, seen)\n    elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):\n        size += sum([get_size(i, seen) for i in obj])\n    return size","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:12:55.932353Z","iopub.execute_input":"2022-08-02T01:12:55.932915Z","iopub.status.idle":"2022-08-02T01:12:55.944445Z","shell.execute_reply.started":"2022-08-02T01:12:55.932866Z","shell.execute_reply":"2022-08-02T01:12:55.943440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **DATASET DE ENTRENAMIENTO**","metadata":{}},{"cell_type":"markdown","source":"### Carga del dataset de entrenamiento","metadata":{}},{"cell_type":"code","source":"traindf = load_traindf()        #Función previamente definida para cargar el dataset ajustado\n\nlandmark_unique = len(traindf['landmark_id'].unique())    #Clases totales del dataset (monumentos diferentes) \n            # traindf['landmark_id'].nunique()\ntraindf.head()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:12:55.950275Z","iopub.execute_input":"2022-08-02T01:12:55.951191Z","iopub.status.idle":"2022-08-02T01:13:02.250149Z","shell.execute_reply.started":"2022-08-02T01:12:55.951148Z","shell.execute_reply":"2022-08-02T01:13:02.249367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"traindf.info(memory_usage='deep')","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:02.251835Z","iopub.execute_input":"2022-08-02T01:13:02.252296Z","iopub.status.idle":"2022-08-02T01:13:02.652015Z","shell.execute_reply.started":"2022-08-02T01:13:02.252254Z","shell.execute_reply":"2022-08-02T01:13:02.651156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Información numérica del dataset de entrenamiento","metadata":{}},{"cell_type":"code","source":"print('Datos del dataset de entrenamiento \\n')\nprint('Número de imágenes en el dataset a clasificar: ', traindf.shape[0])\nprint('Número de monumentos (clases) diferentes: ', landmark_unique)\nprint('Clase más alta: ', max(traindf['landmark_id']))\nprint('Repeticiones de elementos por clase: Mínimo', min(traindf['landmark_id'].value_counts()),\n      'y Máximo',max(traindf['landmark_id'].value_counts()))","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:02.653404Z","iopub.execute_input":"2022-08-02T01:13:02.653854Z","iopub.status.idle":"2022-08-02T01:13:02.877416Z","shell.execute_reply.started":"2022-08-02T01:13:02.653817Z","shell.execute_reply":"2022-08-02T01:13:02.876640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(traindf['landmark_id'].value_counts())","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:02.880323Z","iopub.execute_input":"2022-08-02T01:13:02.880558Z","iopub.status.idle":"2022-08-02T01:13:02.903854Z","shell.execute_reply.started":"2022-08-02T01:13:02.880529Z","shell.execute_reply":"2022-08-02T01:13:02.903005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Se puede observar que el dataset no está balanceado **(Imbalanced Classification)** por la diferencia entre las frecuencias de las clases. Es necesario realizar oversampling o undersampling. En este programa se realizará **undersampling** en el dataset antes de realizar el split datos entrenamiento-validación, pues es más conveniente disminuir la cantidad de datos de las clases con mayor frecuencia para ahorrar memoria que aumentar los datos de las clases con poca frecuencia dada a la limitación de memoria del sistema. Si esta no fuera un problema, sería recomendable realizar el oversampling, obteniendo así una mayor precisión del modelo.","metadata":{}},{"cell_type":"markdown","source":"### Undersampling del dataset de entrenamiento","metadata":{}},{"cell_type":"code","source":"UMBRAL_UNDERSAMPLING = 200           #Las clases con número de imágenes > umbral serán reducidas a umbral imágenes aleatorias de esa clase\n\ntraindf_unders = traindf.groupby('landmark_id', group_keys=False).apply(lambda x: x.sample(n = min(len(x), UMBRAL_UNDERSAMPLING), random_state= SEED))\ntraindf_unders.reset_index(inplace=True, drop=True)     #Reiniciar índices. drop=True para que los índices antiguos no sean nueva columna\n\n#Frecuencias de las clases tras undersampling en dataset de entrenamiento\ntraindf_unders['landmark_id'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:02.905368Z","iopub.execute_input":"2022-08-02T01:13:02.905663Z","iopub.status.idle":"2022-08-02T01:13:59.299085Z","shell.execute_reply.started":"2022-08-02T01:13:02.905625Z","shell.execute_reply":"2022-08-02T01:13:59.298315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Número de imágenes en el dataset tras undersampling: ', traindf_unders.shape[0])","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:59.300738Z","iopub.execute_input":"2022-08-02T01:13:59.301016Z","iopub.status.idle":"2022-08-02T01:13:59.306063Z","shell.execute_reply.started":"2022-08-02T01:13:59.300973Z","shell.execute_reply":"2022-08-02T01:13:59.305345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Contenido del dataset de entrenamiento y generación de la muestra","metadata":{}},{"cell_type":"code","source":"'''\n#SELECCIONAR MUESTRA SEGÚN NÚMERO DE DATOS (PROBLEMA: una muestra puede acabar en mitad de los datos de una clase; la clase estaría dividida en varias muestras)\n\nIMG_SIZE = 160        #Tamaño/resolución de las imágenes que se empleará en todo el programa\nN_DATOS = 10000       #Número de filas del dataframe tras undersampling que se usarán en la muestra\n\ntraindf_s = traindf_unders.iloc[:N_DATOS,:]             #Muestra del dataset de entrenamiento tras undersampling\n\n#Si se quieren barajar los datos :\n#traindf.sample(frac=N_DATOS/traindf_unders.shape[0], random_state=SEED).reset_index(drop=True)  #Se obtiene un [frac %]  de filas del dataframe original y se barajan\n\n'''","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:59.307564Z","iopub.execute_input":"2022-08-02T01:13:59.308053Z","iopub.status.idle":"2022-08-02T01:13:59.318221Z","shell.execute_reply.started":"2022-08-02T01:13:59.308017Z","shell.execute_reply":"2022-08-02T01:13:59.317498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#SELECCIONAR MUESTRA SEGÚN CLASES MAX Y MIN (TODOS LOS DATOS COMPRENDIDOS ENTRE DICHAS CLASES Y DE DICHAS CLASES)\n\nIMG_SIZE = 220        #Tamaño/resolución de las imágenes que se empleará en todo el programa\n\nCLASE_MIN = 0\nCLASE_MAX = 50\n\ntraindf_s = traindf_unders[(CLASE_MIN<traindf_unders['landmark_id']) & (traindf_unders['landmark_id']<=CLASE_MAX)]\n\nN_DATOS = len(traindf_s['landmark_id'])\n\nN_CLASES = len(traindf_s['landmark_id'].unique())\nCLASE_MIN = min(traindf_s['landmark_id'])      #Clase mínima real de la muestra\nCLASE_MAX = max(traindf_s['landmark_id'])      #Clase máxima real de la muestra ----> En la siguiente muestra, su valor pasaría a CLASE_MIN\n\nprint('Número de datos de la muestra, N_DATOS: '+str(N_DATOS))\nprint('\\nNúmero de clases de la muestra, N_CLASES: '+str(N_CLASES))\nprint('\\nClase más baja de la muestra, CLASE_MIN: '+str(CLASE_MIN))\nprint('\\nClase más alta de la muestra, CLASE_MAX: '+str(CLASE_MAX))","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:59.319669Z","iopub.execute_input":"2022-08-02T01:13:59.320183Z","iopub.status.idle":"2022-08-02T01:13:59.493442Z","shell.execute_reply.started":"2022-08-02T01:13:59.320131Z","shell.execute_reply":"2022-08-02T01:13:59.492711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('ELIMINANDO ' + str(get_size(traindf_unders)/(1024*1024)) +' MB ocupados por traindf_unders')\ndel traindf_unders         #Elimina la relación entre la variable y la memoria a la que apunta\ngc.collect();              #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:13:59.494752Z","iopub.execute_input":"2022-08-02T01:13:59.495437Z","iopub.status.idle":"2022-08-02T01:14:04.201908Z","shell.execute_reply.started":"2022-08-02T01:13:59.495398Z","shell.execute_reply":"2022-08-02T01:14:04.200504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"traindf_s","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:04.204943Z","iopub.execute_input":"2022-08-02T01:14:04.205283Z","iopub.status.idle":"2022-08-02T01:14:04.218997Z","shell.execute_reply.started":"2022-08-02T01:14:04.205249Z","shell.execute_reply":"2022-08-02T01:14:04.218117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Comparación de datos en el mismo intervalo de clases [CLASE_MIN, CLASE_MAX]\n\n# Muestra del dataset SIN undersampling\ntraindf_no_unders = traindf[(CLASE_MIN<traindf['landmark_id']) & (traindf['landmark_id']<=CLASE_MAX)]\nplt.figure(1, figsize = (22, 5))\nplt.title('Histograma de landmark_id en muestra SIN undersampling: '+ str(len(traindf_no_unders))+' datos', fontweight =\"bold\")\nplt.hist(traindf_no_unders['landmark_id'], color = 'red', bins= CLASE_MAX - CLASE_MIN, histtype= 'stepfilled')\nplt.xlabel('landmark_id')\nplt.axhline(y=UMBRAL_UNDERSAMPLING, color='g', linestyle='--', label= 'Umbral Undersampling')      #Línea horizontal indicando umbral\nplt.legend()\nplt.show()\n\n# Muestra del dataset CON undersampling\nplt.figure(2, figsize = (22, 5))\nplt.title('Histograma de landmark_id en la muestra CON undersampling traindf_s: '+ str(N_DATOS)+' datos', fontweight = \"bold\")\nplt.hist(traindf_s['landmark_id'], color = 'blue', bins = CLASE_MAX - CLASE_MIN, histtype= 'stepfilled')\nplt.xlabel('landmark_id')\nplt.ylim([0,max(traindf_no_unders['landmark_id'].value_counts())])\nplt.axhline(y=UMBRAL_UNDERSAMPLING, color='g', linestyle='--', label= 'Umbral Undersampling')      #Línea horizontal indicando umbral\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:04.220327Z","iopub.execute_input":"2022-08-02T01:14:04.221124Z","iopub.status.idle":"2022-08-02T01:14:04.663964Z","shell.execute_reply.started":"2022-08-02T01:14:04.221085Z","shell.execute_reply":"2022-08-02T01:14:04.663269Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Se ha conseguido equilibrar en cierta medida las frecuencias de las clases a costa de disminuir los datos.","metadata":{}},{"cell_type":"code","source":"traindf_s['landmark_id'].value_counts().describe()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:04.665387Z","iopub.execute_input":"2022-08-02T01:14:04.665871Z","iopub.status.idle":"2022-08-02T01:14:04.676118Z","shell.execute_reply.started":"2022-08-02T01:14:04.665836Z","shell.execute_reply":"2022-08-02T01:14:04.675011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Eliminar de memoria el dataset completo; solo se usará la muestra tras subsampling durante la ejecución\nprint('ELIMINANDO ' + str(get_size(traindf)/(1024*1024)) +' MB ocupados por traindf (dataset de entrenamiento completo)')\ndel traindf          #Elimina la relación entre la variable y el espacio de memoria al que apunta\ngc.collect();        #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:04.677507Z","iopub.execute_input":"2022-08-02T01:14:04.677943Z","iopub.status.idle":"2022-08-02T01:14:08.124372Z","shell.execute_reply.started":"2022-08-02T01:14:04.677910Z","shell.execute_reply":"2022-08-02T01:14:08.123354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('ELIMINANDO ' + str(get_size(traindf_no_unders)/(1024*1024)) +' MB ocupados por traindf_no_unders (muestra sin undersampling)')\ndel traindf_no_unders         #Elimina la relación entre la variable y el espacio de memoria al que apunta\ngc.collect();                 #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:08.126144Z","iopub.execute_input":"2022-08-02T01:14:08.126473Z","iopub.status.idle":"2022-08-02T01:14:08.306609Z","shell.execute_reply.started":"2022-08-02T01:14:08.126420Z","shell.execute_reply":"2022-08-02T01:14:08.305654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Mostrar imágenes aleatorias de la muestra\n\nplt.figure(figsize=(25,7))\n\nfor i in range(12):\n    j = np.random.randint(0, N_DATOS)              #Cambiar j por i en las indexaciones si se quieren imágenes aleatorias de la muestra\n    img = img_read_resize(traindf_s['img_path'][j])     #Para que las imágenes tengan el mismo tamaño (= número de píxeles = neuronas de entrada)\n    plt.subplot(2 , 6, i+1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.xlabel('landmark_id: '+ str(traindf_s['landmark_id'][j]), fontweight =\"bold\", fontsize=12)\n    plt.imshow(img)\n    \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:08.308271Z","iopub.execute_input":"2022-08-02T01:14:08.308581Z","iopub.status.idle":"2022-08-02T01:14:09.133125Z","shell.execute_reply.started":"2022-08-02T01:14:08.308545Z","shell.execute_reply":"2022-08-02T01:14:09.132475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Redimensionamiento de las imágenes del dataset de entrenamiento. Almacenamiento de las imágenes en una variable y de las etiquetas en otra","metadata":{}},{"cell_type":"code","source":"start_time = time.time()\n\nX = []   #Imágenes\ny = []   #Clases\n###datos_entrenamiento=[]   #Lista con imágenes y clases\n\nfor i in range(traindf_s.shape[0]):                      #Relleno de una lista con imágenes redimensionadas y otra con sus clases  \n    X.append(img_read_resize(traindf_s['img_path'][i])) \n    y.append(np.array(traindf_s['landmark_id'][i]))      #Cada etiqueta se convierte en numpy.ndarray (por compatibilidad con las imágenes en X, que son ndarrays)\n    ###datos_entrenamiento.append([X[i],y[i]])              #Lista de np.ndarrays  (imágenes y etiquetas)\n\n####df_train = pd.DataFrame(datos_entrenamiento, columns = ['img','landmark_id'])        #Convertimos lista a pd.DataFrame\n\nprint('Tipos de las variables: \\n')\nprint('X: ', type(X))\nprint('Elementos de X: ', type(X[0]))\nprint('\\ny: ', type(y))\nprint('Elementos de y: ', type(y[0]))\n\nprint('\\nDuración del redimensionamiento: %s segundos' % (time.time() - start_time))","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:09.134274Z","iopub.execute_input":"2022-08-02T01:14:09.134962Z","iopub.status.idle":"2022-08-02T01:14:13.054780Z","shell.execute_reply.started":"2022-08-02T01:14:09.134921Z","shell.execute_reply":"2022-08-02T01:14:13.053285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"###print('ELIMINANDO ' + str(get_size(df_train)/(1024*1024)) +' MB ocupados por df_train')\n###del df_train         #Elimina la relación entre la variable y la memoria a la que apunta\n###gc.collect();        #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.056273Z","iopub.execute_input":"2022-08-02T01:14:13.056541Z","iopub.status.idle":"2022-08-02T01:14:13.061387Z","shell.execute_reply.started":"2022-08-02T01:14:13.056505Z","shell.execute_reply":"2022-08-02T01:14:13.060514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('ELIMINANDO ' + str(get_size(traindf_s)/(1024*1024)) +' MB ocupados por traindf_s')\ndel traindf_s         #Elimina la relación entre la variable y la memoria a la que apunta\ngc.collect();         #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.062993Z","iopub.execute_input":"2022-08-02T01:14:13.063640Z","iopub.status.idle":"2022-08-02T01:14:13.265941Z","shell.execute_reply.started":"2022-08-02T01:14:13.063603Z","shell.execute_reply":"2022-08-02T01:14:13.265085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"whos","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.269128Z","iopub.execute_input":"2022-08-02T01:14:13.269757Z","iopub.status.idle":"2022-08-02T01:14:13.313105Z","shell.execute_reply.started":"2022-08-02T01:14:13.269714Z","shell.execute_reply":"2022-08-02T01:14:13.312466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Ajustes a las variables","metadata":{}},{"cell_type":"code","source":"#Conversión de lista X a array + Normalización de las imágenes en X + Cambio de tipo de int8 a float16 para reducir memoria\nX = np.array(X).astype('float16')     #Se normalizan las imágenes para que los valores de los píxeles estén entre [0,1] en vez de [0,255]\n\n#Conversión de lista y a array\ny = np.array(y)","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.314489Z","iopub.execute_input":"2022-08-02T01:14:13.314764Z","iopub.status.idle":"2022-08-02T01:14:13.724020Z","shell.execute_reply.started":"2022-08-02T01:14:13.314733Z","shell.execute_reply":"2022-08-02T01:14:13.722950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Obtención de los datos de validación a partir del dataset de entrenamiento","metadata":{}},{"cell_type":"code","source":"#Separación de los datos en datos para entrenamiento y datos para validación\n\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size = 0.15, random_state=SEED, shuffle=True)  #Mismo tipo que X e y (ndarray formado por ndarrays o int64, respectivamente)\n\nprint('Imágenes en X: ',len(X), ' y etiquetas en y: ', len(y))\nprint('Entrenamiento. Imágenes en X_train: ',len(X_train), 'y etiquetas en y_train: ',len(y_train))\nprint('Validación. Imágenes en X_val: ',len(X_val), 'y etiquetas en y_val: ',len(y_val))","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.725310Z","iopub.execute_input":"2022-08-02T01:14:13.725672Z","iopub.status.idle":"2022-08-02T01:14:13.774331Z","shell.execute_reply.started":"2022-08-02T01:14:13.725635Z","shell.execute_reply":"2022-08-02T01:14:13.773420Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('ELIMINANDO ' + str(get_size(X)/(1024*1024)) +' MB ocupados por X')\ndel X         #Elimina la relación entre la variable y la memoria a la que apunta\ngc.collect();        #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.775917Z","iopub.execute_input":"2022-08-02T01:14:13.776175Z","iopub.status.idle":"2022-08-02T01:14:13.953191Z","shell.execute_reply.started":"2022-08-02T01:14:13.776141Z","shell.execute_reply":"2022-08-02T01:14:13.951904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('ELIMINANDO ' + str(get_size(y)/(1024*1024)) +' MB ocupados por y')\ndel y         #Elimina la relación entre la variable y la memoria a la que apunta\ngc.collect();       #Elimina de memoria y devuelve los objetos a los que ya no se apunta","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:13.954699Z","iopub.execute_input":"2022-08-02T01:14:13.955811Z","iopub.status.idle":"2022-08-02T01:14:14.170176Z","shell.execute_reply.started":"2022-08-02T01:14:13.955773Z","shell.execute_reply":"2022-08-02T01:14:14.169349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Aumento de datos","metadata":{}},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range = 30,            #Grados\n    #preprocessing_function = orthogonal_rot,          #----> Ralentiza muchísimo el entrenamiento\n    width_shift_range = 0.1,\n    height_shift_range = 0.1,\n    #shear_range = 5,               #Grados\n    #zoom_range = 0.3,              #----> Ralentiza mucho el entrenamiento\n    fill_mode = 'nearest',\n    #horizontal_flip = True,\n    #vertical_flip = False,   \n)\n#train_datagen.fit(X_train)\n\nval_datagen = ImageDataGenerator(\n    rescale=1./255,\n)\n\n#Mostrar ejemplos de imágenes transformadas por el generador de entrenamiento\nplt.figure(figsize=(25,7))\nfor img, clase in train_datagen.flow(X_train, y_train, batch_size = 12, seed=SEED, shuffle=True):\n    for i in range(12):\n        plt.subplot(2 , 6, i+1)\n        plt.xticks([])\n        plt.yticks([])\n        plt.xlabel('landmark_id: '+ str(clase[i]), fontweight =\"bold\", fontsize=12)\n        plt.imshow(img[i])\n    plt.show()\n    break;","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:14.175215Z","iopub.execute_input":"2022-08-02T01:14:14.177876Z","iopub.status.idle":"2022-08-02T01:14:15.232804Z","shell.execute_reply.started":"2022-08-02T01:14:14.177835Z","shell.execute_reply":"2022-08-02T01:14:15.231863Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **HIPERPARÁMETROS DEL MODELO**","metadata":{}},{"cell_type":"code","source":"#Parámetros de la red neuronal convolucional\n\nkernelSize = (3,3)            #Tamaño de la plantilla de convolución\npaddingType = 'same'          #Cómo se procede en los bordes de las imágenes ('same' o 'valid')\nactivationF = 'relu'          #Función de activación\npoolSize = (2,2)              #Tamaño de la plantilla de maximum pooling\nstridesSize = (2,2)           #Desplazamiento de plantilla durante maximum pooling\ndropoutRate = 0.5             #Porcentaje de neuronas que se desactivan con la capa Dropout\n#batchSize (tomará valores en un bucle)   #Cantidad de datos con los que se entrena en cada época (tomará valores en un bucle)\nepochsSize= 500               #Número de épocas en las que se entrena\nlr = 0.01                     #Learning Rate\n\nweightDecay = regularizers.L2(0.01) #Regularización de pesos ---> L1: Suma pesos absolutos. L2: Suma pesos cuadrados. L1L2: Suma pesos absolutos y cuadrados","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:15.233995Z","iopub.execute_input":"2022-08-02T01:14:15.234747Z","iopub.status.idle":"2022-08-02T01:14:15.241484Z","shell.execute_reply.started":"2022-08-02T01:14:15.234712Z","shell.execute_reply":"2022-08-02T01:14:15.240362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## En caso de utilizar learning_rate personalizado\n\nLR_START = 0.001\nLR_MAX = 0.005\nLR_MIN = 0.001\nLR_RAMPUP_EPOCHS = 75\nLR_SUSTAIN_EPOCHS = 0\nLR_EXP_DECAY = .9\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n    \nlr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=True)\n\nrng = [i for i in range(epochsSize)]\ny = [lrfn(x) for x in rng]\nplt.plot(rng, y)\nprint(\"Learning rate schedule: {:.3g} to {:.3g} to {:.3g}\".format(y[0], max(y), y[-1]))","metadata":{"execution":{"iopub.status.busy":"2022-04-29T18:36:41.918391Z","iopub.execute_input":"2022-04-29T18:36:41.918676Z","iopub.status.idle":"2022-04-29T18:36:42.1212Z","shell.execute_reply.started":"2022-04-29T18:36:41.91864Z","shell.execute_reply":"2022-04-29T18:36:42.120526Z"}}},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **FUNCIÓN PARA CREACIÓN, COMPILACIÓN Y ENTRENAMIENTO DEL MODELO**","metadata":{}},{"cell_type":"code","source":"def crear_modelo(batchSize):\n    \n    #Creación del modelo\n\n    model = keras.Sequential([\n      \n        #BASE DEL MODELO ---- Extracción de características\n\n        #Bloque convolucional 1  \n        layers.Conv2D(filters=32, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF, input_shape=[IMG_SIZE, IMG_SIZE, 3]),\n        layers.BatchNormalization(),\n        layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n\n        #Bloque convolucional 2  \n        layers.Conv2D(filters=64, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF),\n        layers.BatchNormalization(),\n        layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n\n        #Bloque convolucional 3  \n        layers.Conv2D(filters=128, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF),\n        layers.BatchNormalization(),\n        layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n\n        #Bloque convolucional 4  \n        layers.Conv2D(filters=256, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF),\n        layers.BatchNormalization(),\n        layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n        \n        #Bloque convolucional 5 \n        layers.Conv2D(filters=512, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF),\n        layers.BatchNormalization(),\n        layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n        \n        #Bloque convolucional 6 \n        #layers.Conv2D(filters=512, kernel_size=kernelSize, strides=1, padding=paddingType, activation=activationF),\n        #layers.BatchNormalization(),\n        #layers.MaxPool2D(pool_size=poolSize, strides=stridesSize, padding=paddingType),\n        \n        #CABEZA DEL MODELO ---- Clasificación\n\n        layers.Flatten(),\n        layers.BatchNormalization(),\n        layers.Dropout(rate=dropoutRate),\n        layers.Dense(units = 512, activation=activationF, kernel_regularizer=weightDecay), #Para 1 hidden layer, n_neuronas = sqrt(n_input*n_output) + [1,10]\n\n        layers.BatchNormalization(),\n        layers.Dropout(rate=dropoutRate),\n        layers.Dense(units = landmark_unique, activation='softmax'),         #Número de neuronas = Número de clases TOTALES     \n    ])\n    \n    #OPTIMIZADORES\n    # A menor lr, menos cambios repentinos hay en las métricas como acuracy\n    opt1 = tf.keras.optimizers.RMSprop(learning_rate=lr, rho=0.9, momentum=0.5, epsilon=1e-07)   \n    opt2 = tf.keras.optimizers.Adam(learning_rate=lr, beta_1=0.9, beta_2=0.999, epsilon=1e-07)\n    opt3 = tf.keras.optimizers.SGD(learning_rate=lr, momentum=0.9, nesterov=False)         #momentum--> acelera gradiente y amortigua oscilaciones\n    \n    #Compilación del modelo   \n    model.compile(\n        optimizer = opt3,\n        loss = 'sparse_categorical_crossentropy',\n        metrics = ['sparse_categorical_accuracy']\n    )\n    \n    #Callback 1\n    early_stopping = EarlyStopping(\n        monitor = \"val_loss\",\n        mode = \"auto\",\n        min_delta = 0.0001,         # minimium amount of change to count as an improvement\n        patience = 200,              # how many epochs to wait before stopping\n        restore_best_weights = True,\n    )\n    \n    #Callback 2\n    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.7, patience=50, cooldown=1, min_lr=0.0005, min_delta=0.001, verbose=1)   #patience=7\n    \n    \n    #Entrenamiento del modelo\n    history = model.fit(\n        train_datagen.flow(X_train, y_train, batch_size = batchSize),             #Entrena con X_train datos en cada época, empleando en cada step batch_size datos\n                                                                                  # de X_train modificados aleatoriamente según se indicó en el generador\n        validation_data = val_datagen.flow(X_val, y_val, batch_size = batchSize),\n        class_weight = dic_class_weights,      #Para solucionar Imbalanced Data\n        #shuffle = True,                       #Al trabajar con un generador de datos se ignora\n        batch_size = None,                     #batch_size indicado en generador(train_datagen)\n        steps_per_epoch = len(X_train)//batchSize,\n        epochs = epochsSize,\n        callbacks = [early_stopping, reduce_lr],\n        verbose=1,            # 0: silencio     1: barra de progreso + texto      2: solo texto\n        #use_multiprocessing=True,\n        #max_queue_size = 15,        #Default = 10\n        #workers = 32,\n    ) \n  \n    return model, history","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:15.243065Z","iopub.execute_input":"2022-08-02T01:14:15.243670Z","iopub.status.idle":"2022-08-02T01:14:15.263840Z","shell.execute_reply.started":"2022-08-02T01:14:15.243637Z","shell.execute_reply":"2022-08-02T01:14:15.262910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **GRÁFICAS DE PÉRDIDA Y PRECISIÓN Y EVALUACIÓN DEL MODELO**","metadata":{}},{"cell_type":"code","source":"'''\nfrom tensorflow.compat.v1 import ConfigProto\nfrom tensorflow.compat.v1 import InteractiveSession\n\nconfig = ConfigProto()\nconfig.gpu_options.allow_growth = True\nsession = InteractiveSession(config=config)\n'''","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:15.265587Z","iopub.execute_input":"2022-08-02T01:14:15.266384Z","iopub.status.idle":"2022-08-02T01:14:15.277703Z","shell.execute_reply.started":"2022-08-02T01:14:15.266350Z","shell.execute_reply":"2022-08-02T01:14:15.276992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Cálculo de pesos de cada clase de los datos para entrenamiento de la muestra\n_, freq = np.unique(y_train, return_counts=True)\nmax_freq = np.max(freq)\nprint ('Mayor frecuencia: '+str(np.max(freq)))\n\n##Método no funciona correctamente con este set debido a la numeración de landmark_id\n##classWeights = class_weight.compute_class_weight(class_weight ='balanced',classes = np.unique(y_train), y = y_train)\n##train_classWeights = dict(enumerate(classWeights))\n\n#Se realiza este procedimiento porque con el método compute_class_weight no funciona --> Las keys del diccionario deben coincidir con las etiquetas\ndic_class_weights ={}\ni=0\nfor i in range(max(y_train)+1):                   #Se va a crear un diccionario de max(y_train)+1 elementos, aunque el número de clases sea menor\n    dic_class_weights[i] = 0                      #Inicialmente todos los pesos (valores del diccionario) a 0\n    if i in y_train:                              #Si la clave coincide con el landmark_id (etiqueta) el peso no será 0\n        freq = len(y_train[y_train == i])         #Frecuencia de la etiqueta i\n        dic_class_weights[i] = max_freq/freq","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:15.282337Z","iopub.execute_input":"2022-08-02T01:14:15.283081Z","iopub.status.idle":"2022-08-02T01:14:15.290537Z","shell.execute_reply.started":"2022-08-02T01:14:15.283052Z","shell.execute_reply":"2022-08-02T01:14:15.289761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batchSize = [64]\n#[2, 4, 8, 16, 32, 64, 128]\n\nmin_val_loss = []\nmax_val_acc = []\n\nstart_time = time.time()\n\nfor j in range(len(batchSize)):           #En cada iteración se crea el modelo DESDE CERO con la función crear_modelo()\n\n    model, history = crear_modelo(batchSize[j])\n    \n    history_df = pd.DataFrame(history.history)    \n    \n    print('\\nbatchSize = '+str(batchSize[j]))\n    \n    plt.figure(figsize=(15,5))        #Anchura y Altura de las gráficas, respectivamente\n    \n    plt.subplot(1,2,1)\n    #history_df.loc[0:, ['loss', 'val_loss']].plot()             #Utilizar dataframe y su método plot dan problemas con plt.subplot\n    plt.plot(history.history['loss'], label='train')        \n    plt.plot(history.history['val_loss'], label='test')\n    plt.title('Loss and Validation Loss')\n    plt.xlabel('batchSize = '+str(batchSize[j]))\n    print((\"Minimum Validation Loss: {:0.4f} in epoch {:0.0f} \").format(history_df['val_loss'].min(), history_df['val_loss'].idxmin()))            \n    \n    plt.subplot(1,2,2)\n    #history_df.loc[0:, ['accuracy', 'val_accuracy']].plot()\n    plt.plot(history.history['sparse_categorical_accuracy'], label='train')\n    plt.plot(history.history['val_sparse_categorical_accuracy'], label='test')\n    plt.title('Accuracy and Validation Accuracy')\n    plt.xlabel('batchSize = '+str(batchSize[j]))\n    print((\"Maximum Validation Accuracy: {:0.4f} in epoch {:0.0f} \").format(history_df['val_sparse_categorical_accuracy'].max(), history_df['val_sparse_categorical_accuracy'].idxmax()))\n\n    print(\"\\nEvaluación del modelo con datos de entrenamiento\")\n    score = model.evaluate(X_train, y_train)\n    print(\"Test loss, Test accuracy:\", score[0], score[1])\n\n    print(\"\\nEvaluación del modelo con datos de validación\")\n    score = model.evaluate(X_val, y_val)\n    print(\"Test loss, Test accuracy:\", score[0], score[1])\n\n    plt.show()    #Se muestran las gráficas\n    \n    #Proceso para almacenar los mejores resultados (menor pérdida y mayor precisión)\n    if j==0:\n        min_val_loss= [history_df['val_loss'].min(), history_df['val_loss'].idxmin(), batchSize[j]]\n    else:\n        if min_val_loss[0]> history_df['val_loss'].min():\n            min_val_loss= [history_df['val_loss'].min(), history_df['val_loss'].idxmin(), batchSize[j]]\n            \n    if j==0:\n        max_val_acc= [history_df['val_sparse_categorical_accuracy'].max(), history_df['val_sparse_categorical_accuracy'].idxmax(), batchSize[j]]\n    else:\n        if max_val_acc[0]< history_df['val_sparse_categorical_accuracy'].max():\n            max_val_acc= [history_df['val_sparse_categorical_accuracy'].max(), history_df['val_sparse_categorical_accuracy'].idxmax(), batchSize[j]]\n            \n            \nprint('\\nBest Results:')            \nprint((\"Minimum Validation Loss: {:0.4f} in epoch {:0.0f} with batchSize = {:0.0f}\").format(min_val_loss[0], min_val_loss[1], min_val_loss[2]))\nprint((\"Maximum Validation Accuracy: {:0.4f} in epoch {:0.0f} with batchSize = {:0.0f}\").format(max_val_acc[0], max_val_acc[1], max_val_acc[2]))\nprint('\\nDuración del entrenamiento: %s minutos' % ((time.time() - start_time)/60))","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:14:15.292022Z","iopub.execute_input":"2022-08-02T01:14:15.292825Z","iopub.status.idle":"2022-08-02T01:35:02.513626Z","shell.execute_reply.started":"2022-08-02T01:14:15.292782Z","shell.execute_reply":"2022-08-02T01:35:02.512401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:02.515090Z","iopub.execute_input":"2022-08-02T01:35:02.515904Z","iopub.status.idle":"2022-08-02T01:35:02.532100Z","shell.execute_reply.started":"2022-08-02T01:35:02.515866Z","shell.execute_reply":"2022-08-02T01:35:02.531307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Guardado del modelo completo\n\nmodel.save('./MyModel_h5',save_format='h5')  \n#model.save('./MyModel_tf',save_format='tf')\n\n#Guardado de pesos del modelo\nmodel.save_weights('./Model_Weights_h5', save_format='h5')","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:02.534301Z","iopub.execute_input":"2022-08-02T01:35:02.534850Z","iopub.status.idle":"2022-08-02T01:35:05.919595Z","shell.execute_reply.started":"2022-08-02T01:35:02.534819Z","shell.execute_reply":"2022-08-02T01:35:05.918517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Cargado de un modelo guardado\n\nmodel_cargado = tf.keras.models.load_model('./MyModel_h5')","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:05.926581Z","iopub.execute_input":"2022-08-02T01:35:05.929289Z","iopub.status.idle":"2022-08-02T01:35:08.516089Z","shell.execute_reply.started":"2022-08-02T01:35:05.929238Z","shell.execute_reply":"2022-08-02T01:35:08.515275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\n#En caso de que sea necesario borrar algún archivo en el directorio de salida\n\nfilename = ''\nimport os\nos.remove(filename)\n'''","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:08.522360Z","iopub.execute_input":"2022-08-02T01:35:08.522598Z","iopub.status.idle":"2022-08-02T01:35:08.529164Z","shell.execute_reply.started":"2022-08-02T01:35:08.522570Z","shell.execute_reply":"2022-08-02T01:35:08.528479Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\n#En caso de que sea necesario borrar algún directorio en el directorio de salida\ndirectory = ''\nimport shutil\nshutil.rmtree(directory)\n'''","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:08.530374Z","iopub.execute_input":"2022-08-02T01:35:08.531132Z","iopub.status.idle":"2022-08-02T01:35:10.003657Z","shell.execute_reply.started":"2022-08-02T01:35:08.531098Z","shell.execute_reply":"2022-08-02T01:35:10.002723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model_cargado.get_weights()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:10.006658Z","iopub.execute_input":"2022-08-02T01:35:10.007655Z","iopub.status.idle":"2022-08-02T01:35:10.011675Z","shell.execute_reply.started":"2022-08-02T01:35:10.007618Z","shell.execute_reply":"2022-08-02T01:35:10.010779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Reentrenar el modelo\n\nmodel_cargado.fit(\n        X_train, y_train,\n        validation_data= (X_val, y_val),\n        shuffle = True,\n        batch_size=batchSize,\n        epochs=epochsSize,\n        callbacks=[early_stopping],\n        verbose=1,    # 0: silencio     1: barra de progreso + texto      2: solo texto)\n)","metadata":{"execution":{"iopub.status.busy":"2022-04-26T15:31:16.797454Z","iopub.execute_input":"2022-04-26T15:31:16.798021Z","iopub.status.idle":"2022-04-26T15:31:16.930053Z","shell.execute_reply.started":"2022-04-26T15:31:16.797933Z","shell.execute_reply":"2022-04-26T15:31:16.927279Z"}}},{"cell_type":"markdown","source":"&nbsp;\n&nbsp;\n&nbsp;\n\n# **PREDICCIÓN DE LOS DATOS DE TESTEO**","metadata":{}},{"cell_type":"markdown","source":"### Carga del dataset de testeo y representación de su contenido","metadata":{}},{"cell_type":"code","source":"mainpath_test = '../input/landmark-recognition-2021/test'\n\nsample_subm = pd.read_csv('../input/landmark-recognition-2021/sample_submission.csv')         #Leer archivo .csv con imágenes a clasificar\n\nplt.figure(figsize=(25,7))\n\nfor i in range(12):\n    img_id = sample_subm['id'][i]\n    img_path = os.path.join(mainpath_test, img_id[0], img_id[1], img_id[2], img_id + '.jpg')\n    img = img_read_resize(img_path)  #Se usa la función previamente definida para lograr el mismo tamaño que las imágenes de entrenamiento\n    plt.subplot(2 , 6, i+1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.xlabel('Clase: por determinar')\n    plt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:10.013100Z","iopub.execute_input":"2022-08-02T01:35:10.013495Z","iopub.status.idle":"2022-08-02T01:35:10.931884Z","shell.execute_reply.started":"2022-08-02T01:35:10.013381Z","shell.execute_reply":"2022-08-02T01:35:10.930985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Ajuste de datos y predicción","metadata":{}},{"cell_type":"code","source":"X_pred = []\nX_pred_copia = []\nconfianza = []\n\n#Se rellena una lista con las imágenes de testeo redimensionadas que se vayan a predecir\n\nelem_a_predecir = 30\n\nfor i in range(elem_a_predecir): \n    img_id = sample_subm['id'][i]\n    img_path = os.path.join(mainpath_test, img_id[0], img_id[1], img_id[2], img_id + '.jpg')\n    img = img_read_resize(img_path)               #Se usa la función previamente definida para lograr el mismo tamaño que las imágenes de entrenamiento\n    X_pred.append(img)\n    X_pred_copia.append(img)                            #Copia de las imágenes de testeo redimensionadas y sin normalizar\n\nX_pred = np.array(X_pred).astype('float16')/255         #Normalización de las imágenes de testeo redimensionadas\n\npredic = model.predict(X_pred, verbose=1)       # batch_size=None,        #Se realiza la predicción","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:10.933259Z","iopub.execute_input":"2022-08-02T01:35:10.933733Z","iopub.status.idle":"2022-08-02T01:35:11.416085Z","shell.execute_reply.started":"2022-08-02T01:35:10.933700Z","shell.execute_reply":"2022-08-02T01:35:11.415217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predic","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.417249Z","iopub.execute_input":"2022-08-02T01:35:11.417525Z","iopub.status.idle":"2022-08-02T01:35:11.426649Z","shell.execute_reply.started":"2022-08-02T01:35:11.417484Z","shell.execute_reply":"2022-08-02T01:35:11.425597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predic.shape         #Array de elem_a_predecir filas, neuronas de salida (81313) columnas","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.428325Z","iopub.execute_input":"2022-08-02T01:35:11.428652Z","iopub.status.idle":"2022-08-02T01:35:11.435435Z","shell.execute_reply.started":"2022-08-02T01:35:11.428618Z","shell.execute_reply":"2022-08-02T01:35:11.434497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predic[0]        #Primer elemento predicho","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.437124Z","iopub.execute_input":"2022-08-02T01:35:11.437376Z","iopub.status.idle":"2022-08-02T01:35:11.445512Z","shell.execute_reply.started":"2022-08-02T01:35:11.437344Z","shell.execute_reply":"2022-08-02T01:35:11.444528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_subm.head()      #Archivo .csv antes de introducir resultados","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.447048Z","iopub.execute_input":"2022-08-02T01:35:11.447577Z","iopub.status.idle":"2022-08-02T01:35:11.458315Z","shell.execute_reply.started":"2022-08-02T01:35:11.447543Z","shell.execute_reply":"2022-08-02T01:35:11.457324Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_pred = []\n\nfor i in range(len(predic)):\n    y_pred.append(np.argmax(predic[i]))                                        #Clase (landmark_id) con mayor probabilidad\n    confianza.append(predic[i][y_pred[i]].round(2))                            #Probabilidad de la clase con mayor probabilidad\n    sample_subm['landmarks'][i] = str(y_pred[i]) +' '+ str(confianza[i])       #Se añaden los resultados al archivo .csv","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.459806Z","iopub.execute_input":"2022-08-02T01:35:11.460052Z","iopub.status.idle":"2022-08-02T01:35:11.475416Z","shell.execute_reply.started":"2022-08-02T01:35:11.460020Z","shell.execute_reply":"2022-08-02T01:35:11.474668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_subm.head()         #Archivo .csv después de introducir los resultados","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.476708Z","iopub.execute_input":"2022-08-02T01:35:11.477038Z","iopub.status.idle":"2022-08-02T01:35:11.486304Z","shell.execute_reply.started":"2022-08-02T01:35:11.476996Z","shell.execute_reply":"2022-08-02T01:35:11.485300Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Representación visual de los resultados","metadata":{}},{"cell_type":"code","source":"#Leer de nuevo el dataset de entrenamiento pues se eliminó de la memoria\ntraindf = load_traindf()","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:11.488076Z","iopub.execute_input":"2022-08-02T01:35:11.489111Z","iopub.status.idle":"2022-08-02T01:35:18.642277Z","shell.execute_reply.started":"2022-08-02T01:35:11.489073Z","shell.execute_reply":"2022-08-02T01:35:18.641502Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"col = 5       #Número de columnas del subplot (teniendo en cuenta la propia imagen a predecir)\n\nk=0\nfor k in range(elem_a_predecir):\n    plt.figure(figsize=(16,7))\n    img_pred = X_pred_copia[k]      #Cualquier imagen que se predijo anteriormente (ya está redimensionada)\n    plt.subplot(1, col, 1)\n    plt.xticks([])\n    plt.yticks([])\n    plt.title('Imagen a predecir nº '+ str(k))\n    str1 = 'Clase predicha: ' + str(y_pred[k]) \n    str2 = 'Confianza: ' + str(confianza[k])\n    plt.xlabel(str1 + '\\n' + str2, fontsize = 12, weight = 'bold')             #Para imprimir en 2 líneas distintas\n    plt.imshow(img_pred)\n    \n    i=0\n    img_clase_df = traindf[traindf['landmark_id']==y_pred[k]]            #Dataframe con imágenes de igual 'landmark_id' que la predicción\n    \n    for i in range(len(img_clase_df)):\n        if i < (col-1): \n            img_clase_path = img_clase_df.iloc[i,2] \n            img_clase = img_read_resize(img_clase_path)\n            plt.subplot(1, col, i+2)\n            plt.xticks([])\n            plt.yticks([])\n            plt.title('Imagen de la clase '+ str(y_pred[k]))\n            plt.xlabel(str())\n            plt.imshow(img_clase)\n        else:\n            break;","metadata":{"execution":{"iopub.status.busy":"2022-08-02T01:35:18.643503Z","iopub.execute_input":"2022-08-02T01:35:18.643943Z","iopub.status.idle":"2022-08-02T01:35:25.064730Z","shell.execute_reply.started":"2022-08-02T01:35:18.643906Z","shell.execute_reply":"2022-08-02T01:35:25.063911Z"},"trusted":true},"execution_count":null,"outputs":[]}]}