{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":30761,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 0 - Inicio\n\nEste es el código que proporciona Kaggle al unirse al reto Digit Recognizer. ¡Vamos allá!","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-09-13T08:29:00.219376Z","iopub.execute_input":"2024-09-13T08:29:00.220622Z","iopub.status.idle":"2024-09-13T08:29:00.230292Z","shell.execute_reply.started":"2024-09-13T08:29:00.220573Z","shell.execute_reply":"2024-09-13T08:29:00.229144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1 - Visualizar los datos\n\nVisualizaré algunos datos de los csv para comprender el formato y la información que nos está aportando","metadata":{}},{"cell_type":"code","source":"# Cargar los archivos CSV\ntrain_df = pd.read_csv('/kaggle/input/digit-recognizer/train.csv')\ntest_df = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')\nsample_submission_df = pd.read_csv('/kaggle/input/digit-recognizer/sample_submission.csv')\n\n# Mostrar las primeras 5 filas de cada archivo\nprint(\"Primeras filas del archivo train.csv:\")\nprint(train_df.head())\n\nprint(\"\\nPrimeras filas del archivo test.csv:\")\nprint(test_df.head())\n\nprint(\"\\nPrimeras filas del archivo sample_submission.csv:\")\nprint(sample_submission_df.head())","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:00.232659Z","iopub.execute_input":"2024-09-13T08:29:00.233039Z","iopub.status.idle":"2024-09-13T08:29:04.570916Z","shell.execute_reply.started":"2024-09-13T08:29:00.233001Z","shell.execute_reply":"2024-09-13T08:29:04.569691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*  Podemos observar que los archivos train y test contienen por cada fila información de 784 píxeles (0 - 783), que entendemos corresponden a los valores de imágenes.\n* El archivo train tiene una columna llamada label, que parece indicar el número al que corresponde la imagen.\n* De momento, todos los valores que se muestran bajo la columna pixel son ceros, por lo que parece que tendremos que mostrar valores intermedios de esas filas para tener una visión más clara de lo que contienen\n* sample_submission contiene un ejemplo de cómo debemos presentar los resultados: id, imagenId, Label\n\nVamos a mostrar de otra forma los valores de train:","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Función para visualizar las imágenes en el dataset\ndef visualizar_imagen(fila):\n    # Extraer la etiqueta y los datos de píxeles de una fila\n    etiqueta = train_df.iloc[fila, 0]\n    pixeles = train_df.iloc[fila, 1:].values.reshape(28, 28)\n    \n    # Mostrar la imagen con matplotlib\n    plt.imshow(pixeles, cmap='gray')\n    plt.title(f'Etiqueta: {etiqueta}')\n    plt.axis('off')  # Ocultar los ejes\n    plt.show()\n\n# Visualizar las primeras 5 imágenes del conjunto de entrenamiento\nfor i in range(5):\n    visualizar_imagen(i)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:04.573256Z","iopub.execute_input":"2024-09-13T08:29:04.573767Z","iopub.status.idle":"2024-09-13T08:29:05.321781Z","shell.execute_reply.started":"2024-09-13T08:29:04.573718Z","shell.execute_reply":"2024-09-13T08:29:05.319490Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"La función visualizar_imagen separa etiqueta y píxeles de una fila del archivo y reorganiza los píxeles en una matriz de 28x28 (ya que la raíz de 784 es 28).\n\nHaciendo uso de la librería Matpotlib hemos convertido la matriz en imagen.\n\nViendo el resultado, podemos interpretar que los píxeles contienen valores en cierto rango de intensidad, ya que vemos blancos negros y grises. Interpretamos que el 0 es el negro. Vamos a comprobarlo:","metadata":{}},{"cell_type":"code","source":"# Número de columnas de píxeles (sin incluir la columna 'label')\nnum_pixeles = train_df.shape[1] - 1  # Restamos 1 por la columna 'label'\n\n# Seleccionar aleatoriamente 10 columnas de píxeles (entre 'pixel0' y 'pixel783')\ncolumnas_aleatorias = np.random.choice(range(1, num_pixeles + 1), size=10, replace=False)\n\n# Añadir la columna 'label' para referencia\ncolumnas_seleccionadas = ['label'] + [f'pixel{col}' for col in columnas_aleatorias]\n\n# Mostrar las primeras filas de las columnas seleccionadas\nprint(f\"Mostrando valores de las columnas: {columnas_seleccionadas}\")\nprint(train_df[columnas_seleccionadas].head())","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:05.324374Z","iopub.execute_input":"2024-09-13T08:29:05.324800Z","iopub.status.idle":"2024-09-13T08:29:05.337998Z","shell.execute_reply.started":"2024-09-13T08:29:05.324760Z","shell.execute_reply":"2024-09-13T08:29:05.336477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Efectivamente, hay un rango que parece ir del 0 al 255 (Valores RGB):\n* El 0 es el negro (0 intensidad luminosa)\n* El 255 representa el blanco (máxima intensidad)\n* Los valores intermedios serán distintos tonos de gris","metadata":{}},{"cell_type":"markdown","source":"# 2 - Preparar los datos para entrenamiento\n\nEn el archivo de entrenamiento, necesitaremos separar la columna label de los píxeles. \n\nAdemás, necesitamos escalar los datos de los píxeles de 0-255 a 0-1 (ya que muchos modelos de marchine learning trabajan mejor en este rango).\n\nComo ya hicimos antes, tenemos que pasar los datos a matrices de 28x28.\n\nPor último, tendremos que dividir los datos del entremaniento para separar entrenamiento y validación.\n\n¡Vamos allá!","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# 1. Separar características (pixeles) y etiquetas (label)\nX = train_df.drop('label', axis=1).values  # Características (pixeles)\ny = train_df['label'].values  # Etiquetas (números de 0 a 9)\n\n# 2. Normalización de los datos (dividir por 255 para tener valores entre 0 y 1)\nX = X / 255.0\n\n# 3. Dividir los datos en conjunto de entrenamiento y validación (80% entrenamiento, 20% validación)\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n\n# 4. Redimensionar los datos para poder usar una red neuronal convolucional (CNN)\n# Cambiamos la forma de los datos para tener una dimensión adicional (28x28x1)\nX_train = X_train.reshape(-1, 28, 28, 1)\nX_val = X_val.reshape(-1, 28, 28, 1)\n\nprint(f\"Datos de entrenamiento: {X_train.shape}\")\nprint(f\"Datos de validación: {X_val.shape}\")","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:05.341461Z","iopub.execute_input":"2024-09-13T08:29:05.341940Z","iopub.status.idle":"2024-09-13T08:29:05.986975Z","shell.execute_reply.started":"2024-09-13T08:29:05.341831Z","shell.execute_reply":"2024-09-13T08:29:05.985580Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3 - Implementar una CNN\n\nUsaremos una red neuronal convolucional. Las CNN son muy buenas en tareas de visión por computadora, como es este caso (necesitamos reconocer dígitos en imágenes).\n\nPara ello, vamos a necesitar utilizar bibliotecas como TensorFlow o Keras.","metadata":{}},{"cell_type":"markdown","source":"### 3.1 - Importar las librerías","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:05.988260Z","iopub.execute_input":"2024-09-13T08:29:05.988641Z","iopub.status.idle":"2024-09-13T08:29:05.995119Z","shell.execute_reply.started":"2024-09-13T08:29:05.988604Z","shell.execute_reply":"2024-09-13T08:29:05.993837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.2 - Definir la arquitectura\n\nConstruiremos una CNN simple con varias capas de convolución, reducción de dimensión (poolong) y capa densa para clasificación.\n\n* Capas convolucionales: Estas extraen características espaciales de las imágenes.\n* MaxPooling: Reduce la dimensionalidad y ayuda a evitar el sobreajuste.\n* Capas densas: Estas son las capas completamente conectadas al final, que toman las características extraídas y realizan la clasificación final.","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, Dropout, BatchNormalization\n\n# Definir la arquitectura de la CNN con ajustes adicionales\nmodel = Sequential()\n\nmodel.add(Input(shape=(28, 28, 1)))\n\nmodel.add(Conv2D(32, (3, 3), activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Conv2D(64, (3, 3), activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Conv2D(128, (3, 3), activation='relu'))\nmodel.add(BatchNormalization())\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\nmodel.add(Dropout(0.25))\n\nmodel.add(Flatten())\n\nmodel.add(Dense(128, activation='relu'))\nmodel.add(Dropout(0.5))\n\nmodel.add(Dense(10, activation='softmax'))\n","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:05.997125Z","iopub.execute_input":"2024-09-13T08:29:05.997513Z","iopub.status.idle":"2024-09-13T08:29:06.105579Z","shell.execute_reply.started":"2024-09-13T08:29:05.997468Z","shell.execute_reply":"2024-09-13T08:29:06.104385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.3 - Compilar el modelo","metadata":{}},{"cell_type":"code","source":"# Compilar el modelo con un optimizador ajustado\nmodel.compile(optimizer=Adam(learning_rate=0.001), \n              loss='sparse_categorical_crossentropy', \n              metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:06.107151Z","iopub.execute_input":"2024-09-13T08:29:06.107913Z","iopub.status.idle":"2024-09-13T08:29:06.122214Z","shell.execute_reply.started":"2024-09-13T08:29:06.107869Z","shell.execute_reply":"2024-09-13T08:29:06.119228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.4 - Entrenamiento","metadata":{}},{"cell_type":"code","source":"# Definir EarlyStopping para evitar sobreajuste\nearly_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)\n\n# Entrenar el modelo\nhistory = model.fit(X_train, y_train, \n                    validation_data=(X_val, y_val), \n                    epochs=20, \n                    batch_size=64,\n                    callbacks=[early_stopping])","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:29:06.124738Z","iopub.execute_input":"2024-09-13T08:29:06.125254Z","iopub.status.idle":"2024-09-13T08:31:33.139052Z","shell.execute_reply.started":"2024-09-13T08:29:06.125202Z","shell.execute_reply":"2024-09-13T08:31:33.137808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Cambios en versión 7:\n\n* BatchNormalization(): Normaliza las activaciones, ayudando a estabilizar y acelerar el entrenamiento.\n\n* Dropout(): Añadido en varias capas para prevenir el sobreajuste.\n\n* Adam(learning_rate=0.001): Tasa de aprendizaje ajustada.\n\n* Más epochs y paciencia en EarlyStopping: Permite más tiempo para que el modelo aprenda y evita el sobreajuste.","metadata":{}},{"cell_type":"markdown","source":"### 3.5 - Evaluación","metadata":{}},{"cell_type":"code","source":"# Evaluar el rendimiento en los datos de validación\nval_loss, val_acc = model.evaluate(X_val, y_val)\nprint(f\"Precisión en el conjunto de validación: {val_acc:.4f}\")","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:31:33.140962Z","iopub.execute_input":"2024-09-13T08:31:33.141467Z","iopub.status.idle":"2024-09-13T08:31:34.781783Z","shell.execute_reply.started":"2024-09-13T08:31:33.141417Z","shell.execute_reply":"2024-09-13T08:31:34.780579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.6 - Realizar predicciones\n\nRealizamos predicciones sobre el conjunto de pruebas","metadata":{}},{"cell_type":"code","source":"# Cargar y normalizar los datos de prueba\ntest_df = pd.read_csv('/kaggle/input/digit-recognizer/test.csv')\nX_test = test_df.values / 255.0\nX_test = X_test.reshape(-1, 28, 28, 1)\n\n# Hacer predicciones\npredicciones = model.predict(X_test)\n\n# Convertir las probabilidades a etiquetas (dígito más probable)\npredicciones_finales = predicciones.argmax(axis=1)","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:33:41.085268Z","iopub.execute_input":"2024-09-13T08:33:41.085740Z","iopub.status.idle":"2024-09-13T08:33:48.385695Z","shell.execute_reply.started":"2024-09-13T08:33:41.085699Z","shell.execute_reply":"2024-09-13T08:33:48.384089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Visualicemos las predicciones para ver si son o no acertadas:","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n#Constante num images\nnum = 25\n\n# Función para mostrar imágenes y sus predicciones\ndef mostrar_imagenes_con_predicciones(X_test, predicciones, num_imagenes=num):\n    plt.figure(figsize=(10, 10))\n    \n    for i in range(num_imagenes):\n        # Seleccionar una imagen aleatoria\n        indice = np.random.randint(0, X_test.shape[0])\n        imagen = X_test[indice].reshape(28, 28)  # Convertir a 28x28 para visualizar\n        prediccion = predicciones[indice]\n        \n        # Mostrar la imagen\n        plt.subplot(5, 5, i+1)  # Crear una cuadrícula de 5x5 para mostrar varias imágenes\n        plt.imshow(imagen, cmap='gray')\n        plt.title(f\"Pred: {prediccion}\")\n        plt.axis('off')  # Ocultar los ejes\n    \n    plt.tight_layout()\n    plt.show()\n\n# Mostrar las imágenes del conjunto de test junto con sus predicciones\nmostrar_imagenes_con_predicciones(X_test, predicciones_finales, num_imagenes=num)\n","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:38:30.874660Z","iopub.execute_input":"2024-09-13T08:38:30.875110Z","iopub.status.idle":"2024-09-13T08:38:32.608147Z","shell.execute_reply.started":"2024-09-13T08:38:30.875071Z","shell.execute_reply":"2024-09-13T08:38:32.606811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"¡Parece que tiene muy muy buena pinta!","metadata":{}},{"cell_type":"markdown","source":"# 4 - Generar archivo de resultados","metadata":{}},{"cell_type":"code","source":"# Crear el DataFrame para la presentación\nsubmission_df = pd.DataFrame({\n    'ImageId': range(1, len(predicciones_finales) + 1),\n    'Label': predicciones_finales\n})\n\n# Guardar el DataFrame como archivo CSV\nsubmission_df.to_csv('/kaggle/working/submission.csv', index=False)\nprint(\"Archivo generado con éxito.\")","metadata":{"execution":{"iopub.status.busy":"2024-09-13T08:58:26.081189Z","iopub.execute_input":"2024-09-13T08:58:26.081921Z","iopub.status.idle":"2024-09-13T08:58:26.114358Z","shell.execute_reply.started":"2024-09-13T08:58:26.081870Z","shell.execute_reply":"2024-09-13T08:58:26.113025Z"},"trusted":true},"execution_count":null,"outputs":[]}]}