Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama
Abstract
En este Trabajo de Fin de Máster se aborda el problema de la detección de cáncer de mama, propuesto por la RSNA en la plataforma de Kaggle, mediante el uso de redes neuronales convolucionales para clasificación binaria. El proceso llevado a cabo hasta encontrar una solución se presenta de forma cronológica, abarcando éste la recopilación, preprocesamiento y organización de los conjuntos de datos de mamografías; el diseño y evaluación de diversas arquitecturas de red personalizadas; y la implementación y ajuste de modelos preentrenados. Una vez obtenido, el modelo más prometedor es sometido a una optimización exhaustiva para maximizar la métrica de puntuación de la evaluación.
Full text
Equation Chapter 1 Section 1 Proyecto Fin de Máster Máster Universitario en Ingeniería en Electrónica, Robótica y Automática Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Autor: Raúl Fernández Valcárcel Tutor: Francisco José Simois Tirado Dpto. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2024
Proyecto Fin de Máster Máster Universitario en Ingeniería en Electrónica, Robótica y Automática Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Autor: Raúl Fernández Valcárcel Tutor: Francisco José Simois Tirado Profesor Contratado Doctor Dpto. de Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2024
Proyecto Fin de Máster: Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Autor: Raúl Fernández Valcárcel Tutor: Francisco José Simois Tirado El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2024
El Secretario del Tribunal
A mi familia A mis amigos A mis maestros
Agradecimientos Me gustaría dedicar este espacio a agradecer a todas las personas que me han acompañado y apoyado este año, haciendo posible la realización de este trabajo. Primero de todo, a mi familia, especialmente a mis padres y a mi hermana; quienes por un año más habéis depositado vuestra fe en mí y me habéis dado palabras de aliento cuando lo necesitaba. A mis amigos, por estar siempre a mi lado y ser un pilar fundamental en mi día a día. Este largo camino se ha hecho mucho más llevadero gracias a vuestra compañía y vuestro apoyo, que me han dado las fuerzas necesarias para continuar. A la Universidad de Sevilla y a sus profesores, específicamente al que ha sido mi tutor en este trabajo, Don Francisco José Simois Tirado; por haberme prestado su tiempo y sus conocimientos durante la elaboración de este proyecto. A todas aquellas personas que, de una u otra forma, han contribuido a que complete la que ha sido hasta el momento la etapa más importante de mi vida, les agradezco de todo corazón. Este trabajo de fin de máster es el resultado de vuestro apoyo. Raúl Fernández Valcárcel Sevilla, 2024
4.4.2 Submuestreo de datos negativos 35 5 Desarrollo de la red neuronal 37 5.1. Primera propuesta 37 5.1.1 Primer modelo de red 37 5.1.2 Segundo modelo de red 38 5.1.3 Motivos del descarte 39 5.2. Segunda propuesta 40 5.2.1 Implementación de nuevas métricas 40 5.2.2 Pruebas con los modelos previos 41 5.2.3 Motivos del descarte 43 5.3. Tercera propuesta 44 5.3.1 Aprendizaje por transferencia 44 5.3.2 Elección del modelo preentrenado 45 5.3.3 Pruebas con los modelos preentrenados 46 5.3.4 Ajuste de la red neuronal 50 5.3.5 Búsqueda del modelo óptimo 52 6 Evaluación del modelo 61 6.1. Resultados de la evaluación 61 6.2. Resultados de la evaluación con hard thresholding 63 6.3. Análisis del funcionamiento interno del modelo 65 6.3.1 Primeros bloques 65 6.3.2 Bloques intermedios 66 6.3.3 Bloques finales 68 7 Conclusiones 71 7.1. Importancia del conjunto de datos 71 7.2. Ventajas del aprendizaje por transferencia 71 7.3. Influencia del umbral de predicción 71 7.4. Necesidad de validación externa 72 7.5. Líneas de ampliación futuras 72 7.5.1 Técnicas de optimización y búsqueda de hiperparámetros 72 7.5.2 Generación sintética de mamografías 72 7.5.3 Implementación de modelos especializados 72 Referencias 75 Anexo: Códigos 79 Anexo: Modelos de red 121
Índice Agradecimientos 9 Resumen 11 Abstract 13 Índice abreviado 15 Índice 17 Índice de códigos 21 Índice de figuras 23 Índice de modelos 27 Índice de tablas 29 1 Introducción 1 1.1. Kaggle 1 1.2. El cáncer de mama en la actualidad 2 1.3. Integración de las nuevas tecnologías en medicina 3 1.4. Redes neuronales para la detección de anomalías clínicas 3 2 Redes neuronales artificiales 5 2.1. Aprendizaje automático 5 2.1.1. Formas de aprendizaje automático 5 2.1.1.1 Aprendizaje supervisado 5 2.1.1.2 Aprendizaje sin supervisión 6 2.1.1.3 Aprendizaje semisupervisado 6 2.1.1.4 Aprendizaje por refuerzo 6 2.2. Aprendizaje profundo 6 2.3. Estructura de una red neuronal artificial 7 2.3.1 Funciones de activación 7 2.4. Entrenamiento de una red neuronal 8 2.4.1 Función de coste 9 2.4.2 Descenso de gradiente 9 2.4.2.1 Mejoras del descenso de gradiente 10 2.5. Validación de una red neuronal 12 2.5.1 Problemas de ajuste del modelo 12 2.5.1.1 Subajuste de una red neuronal 13 2.5.1.2 Sobreajuste de una red neuronal 13 2.6. Evaluación de una red neuronal 14 2.6.1 Métricas de evaluación 14 2.6.1.1 Métricas probabilísticas 16 3 Redes neuronales convolucionales 19 3.1. Estructura de una red neuronal convolucional 19 3.1.1 Capas convolucionales 19 3.1.1.1 Profundidad de la convolución 20 3.1.1.2 Paso de convolución 21
3.1.1.3 Relleno 21 3.1.1.4 Función de activación 22 3.1.2 Capas de pooling 23 3.1.2.1 Tipos de pooling 24 3.1.3 Capas densas 25 3.1.4 Otras capas 26 4 Creación del conjunto de datos 27 4.1. Formato DICOM 27 4.1.1 Formato PNG como alternativa 27 4.2. Descripción del conjunto de datos 27 4.3. Importación de los conjuntos de datos 30 4.4. Distribución de los subconjuntos 31 4.4.1 Aumento de datos de entrenamiento 32 4.4.1.1 Traslaciones y rotaciones 32 4.4.1.2 Shearing y zooming 32 4.4.1.3 Volteo horizontal y vertical 33 4.4.1.4 Recortes aleatorios en la imagen 34 4.4.2 Submuestreo de datos negativos 35 5 Desarrollo de la red neuronal 37 5.1. Primera propuesta 37 5.1.1 Primer modelo de red 37 5.1.2 Segundo modelo de red 38 5.1.3 Motivos del descarte 39 5.2. Segunda propuesta 40 5.2.1 Implementación de nuevas métricas 40 5.2.1.1 Definición del F1 Score 40 5.2.1.2 Definición de las métricas probabilísticas 40 5.2.2 Pruebas con los modelos previos 41 5.2.3 Motivos del descarte 43 5.3. Tercera propuesta 44 5.3.1 Aprendizaje por transferencia 44 5.3.2 Elección del modelo preentrenado 45 5.3.3 Pruebas con los modelos preentrenados 46 5.3.3.1 Adición de capas de salida 46 5.3.3.2 Modelo híbrido 47 5.3.3.3 Entrenamiento parcial 48 5.3.4 Ajuste de la red neuronal 50 5.3.4.1 Cambio de optimizador 50 5.3.4.2 Programación de la tasa de aprendizaje 51 5.3.4.3 Actualización de parámetros en los bloques de salida 52 5.3.5 Búsqueda del modelo óptimo 52 5.3.5.1 Primera versión 52 5.3.5.2 Segunda versión 54 5.3.5.3 Tercera versión 55 5.3.5.4 Cuarta versión 56 5.3.5.5 Quinta versión 58 5.3.5.6 Versión final 59 6 Evaluación del modelo 61 6.1. Resultados de la evaluación 61 6.2. Resultados de la evaluación con hard thresholding 63 6.3. Análisis del funcionamiento interno del modelo 65 6.3.1 Primeros bloques 65 6.3.2 Bloques intermedios 66
6.3.3 Bloques finales 68 7 Conclusiones 71 7.1. Importancia del conjunto de datos 71 7.2. Ventajas del aprendizaje por transferencia 71 7.3. Influencia del umbral de predicción 71 7.4. Necesidad de validación externa 72 7.5. Líneas de ampliación futuras 72 7.5.1 Técnicas de optimización y búsqueda de hiperparámetros 72 7.5.2 Generación sintética de mamografías 72 7.5.3 Implementación de modelos especializados 72 Referencias 75 Anexo: Códigos 79 Anexo: Modelos de red 121
Índice de códigos Código 1. Escalado y reorganización (para el conjunto de regiones de interés) 79 Código 2. Distribución de subconjuntos 82 Código 3. Separación de muestras positivas y negativas 85 Código 4. Aumento de datos 88 Código 5. Submuestreo 91 Código 6. Entrenamiento del modelo de red final 93 Código 7. Evaluación del rendimiento de la red 105 Código 8. Visualización de los mapas de características 117
Índice de figuras Figura 1-1. Logo de Kaggle (3) 1 Figura 1-2. Logo de la Sociedad Radiológica de Norteamérica (4) 2 Figura 1-3. Dificultad de detección del cáncer de mama según la densidad del tejido mamario (6) 2 Figura 2-1. Formas de aprendizaje automático 5 Figura 2-2. Estructura básica de una red neuronal artificial (14) 7 Figura 2-3. Ejemplos de las funciones de activación más utilizadas en redes neuronales (15) 8 Figura 2-4. Ejemplos de un sistema subajustado, bien ajustado y sobreajustado (16) 13 Figura 2-5. Diferencia entre la precisión y la sensibilidad (17) 15 Figura 2-6. Interpretación de la curva ROC (18) 16 Figura 2-7. Interpretación de la curva PR (19) 16 Figura 3-1. Estructura convencional de una red neuronal convolucional para clasificación (21) 19 Figura 3-2. Operación de convolución de una matriz con una máscara 3x3 (22) 20 Figura 3-3. Representación gráfica de la influencia de la profundidad en la convolución (23) 21 Figura 3-4. Efectos de aumentar el paso de convolución (24) 21 Figura 3-5. Mapas de características generados por una red de clasificación de números (25) 22 Figura 3-6. Ejemplo de aplicación de max pooling (28) 24 Figura 3-7. Ejemplo de aplicación de average pooling (29) 24 Figura 3-8. Diferencias entre el uso de una capa densa y de Global Average Pooling (31) 25 Figura 3-9. Flattening de una array multidimensional 3x2x2 (1) 25 Figura 4-1. Distribución de mamografías cáncer en el conjunto de datos 28 Figura 4-2. Metadatos del archivo de valores separados por comas (32) 28 Figura 4-3. Representación de los cuatro tipos de mamografías de un paciente 29 Figura 4-4. Diferencias entre niveles de densidad del tejido mamario 29 Figura 4-5. Comparación de las dimensiones de las imágenes de los conjuntos de datos 30 Figura 4-6. Mamografía del conjunto de datos de regiones de interés escalada a 512x512 31 Figura 4-7. Resultado de aplicar traslación y rotación a una mamografía 32 Figura 4-8. Resultado de aplicar shearing y zoom in a una mamografía 33 Figura 4-9. Resultado de voltear horizontal y verticalmente una mamografía 34 Figura 4-10. Resultado de recortar partes aleatorias de una mamografía 34 Figura 4-11. Distribución final de los subconjuntos de las regiones de interés escaladas a 512x512 35 Figura 4-12. Distribución final de los subconjuntos de las regiones de interés escaladas a 512x1024 36 Figura 5-1. Evolución de las pérdidas durante el entrenamiento del primer modelo 38 Figura 5-2. Número de parámetros del segundo modelo de red propuesto 38
Figura 5-3. Evolución de las pérdidas durante el entrenamiento del segundo modelo 39 Figura 5-4. Resultados de la evaluación del primer modelo de red propuesto (TH = 0.5) 39 Figura 5-5. Declaración de la precisión probabilística como métrica personalizada 41 Figura 5-6. Evolución mejorada de las pérdidas del primer modelo 42 Figura 5-7. Evolución similar de las pérdidas del segundo modelo 42 Figura 5-8. Resultados de la evaluación del segundo modelo propuesto 43 Figura 5-9. Comparación de las métricas de evaluación en sus dos formatos 43 Figura 5-10. Ejemplo de transfer learning para una red de clasificación de imágenes médicas (39) 44 Figura 5-11. Representación gráfica de la arquitectura Inception-V3 (42) 45 Figura 5-12. Representación gráfica de la arquitectura EfficientNetB4 (43) 45 Figura 5-13. Evolución de las pérdidas y del pF1 Score en el entrenamiento del modelo Inception-V3 46 Figura 5-14. Ejemplo de capa de referencia donde realizar el corte en el modelo Inception-V3 47 Figura 5-15. Ejemplo de capa de referencia donde realizar el corte en el modelo EfficientNEtB4 47 Figura 5-16. Evolución de las pérdidas en el entrenamiento del modelo híbrido con Inception-V3 48 Figura 5-17. Evolución de las métricas en el entrenamiento del modelo híbrido con Inception-V3 48 Figura 5-18. Evolución del entrenamiento y resultados de la evaluación del modelo Inception-V3 49 Figura 5-19. Evolución del entrenamiento y resultados de la evaluación del modelo EfficientNetB4 50 Figura 5-20. Funcionamiento de la política de un ciclo para la tasa de aprendizaje 51 Figura 5-21. Evolución del entrenamiento y resultados de la evaluación tras el primer intento de ajuste 53 Figura 5-22. Matriz de confusión obtenida en la evaluación tras el primer intento de ajuste 54 Figura 5-23. Evolución del entrenamiento tras el segundo intento de ajuste 55 Figura 5-24. Evolución del entrenamiento tras el tercer intento de ajuste 56 Figura 5-25. Evolución del entrenamiento y resultados de la evaluación tras el cuarto intento de ajuste 57 Figura 5-26. Matriz de confusión obtenida en la evaluación tras el cuarto intento de ajuste 57 Figura 5-27. Evolución del entrenamiento y resultados de la evaluación tras el quinto intento de ajuste 58 Figura 5-28. Matriz de confusión obtenida en la evaluación tras el quinto intento de ajuste 59 Figura 5-29. Evolución del entrenamiento tras el último intento de ajuste 59 Figura 5-30. Evolución de la tasa de aprendizaje durante el entrenamiento 60 Figura 6-1. Curva ROC obtenida en la evaluación final del modelo 61 Figura 6-2. Matriz de confusión obtenida en la evaluación final del modelo 62 Figura 6-3. Métricas obtenidas en la evaluación final del modelo 62 Figura 6-4. Aplicación de hard thresholding con los resultados de las predicciones 63 Figura 6-5. Efecto del hard thresholding en la matriz de confusión 64 Figura 6-6. Efecto del hard thresholding en las métricas de evaluación 64 Figura 6-7. Mamografía con cáncer utilizada (ID: 953022973) 65 Figura 6-8. Mapas de características generados por la capa block1a_project_conv 66 Figura 6-9. Mapas de características generados por la capa block2a_expand_conv 66 Figura 6-10. Mapas de características generados por la capa block5a_expand_conv 67 Figura 6-11. Mapas de características generados por la capa block5c_expand_conv 67 Figura 6-12. Mapas de características generados por la capa block7a_expand_conv 68
Figura 6-13. Mapas de características generados por la capa top_conv 69
Introducción 2 2 Figura 1-2. Logo de la Sociedad Radiológica de Norteamérica (4) La RSNA ofrece a los participantes un extenso conjunto de datos con mamografías junto a sus diagnósticos para que estos creen un algoritmo de detección y clasificación de imágenes de alta precisión con total libertad. 1.2. El cáncer de mama en la actualidad El cáncer de mama es una de las formas de cáncer más comunes entre las mujeres a nivel mundial, además de ser una de las principales a nivel general. Según la Organización Mundial de la Salud (5), en el año 2022 se diagnosticaron 2.3 millones de casos de cáncer de mama a lo largo de todo el mundo, acentuándose la aparición en personas de avanzada edad. La detección temprana es muy importante a la hora de mejorar las tasas de supervivencia. Las mamografías son el método más utilizado para la detección, ya que permiten detectar tumores antes de que estos presenten síntomas o sean palpables. A pesar de que gracias a las mamografías la eficacia en la detección ha mejorado considerablemente, la precisión de los diagnósticos puede verse afectada por factores ajenos a estas, como la densidad del tejido mamario, que puede llegar a dificultar en gran medida la captación de anomalías. Figura 1-3. Dificultad de detección del cáncer de mama según la densidad del tejido mamario (6)
3 3 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama De este tipo de problemas nace la necesidad de crear y desarrollar nuevas tecnologías para facilitar la detección y diagnóstico de tumores en etapas tempranas, con el fin de reducir la tasa de mortalidad de esta enfermedad. 1.3. Integración de las nuevas tecnologías en medicina En los últimos años, la integración de las tecnologías avanzadas de inteligencia artificial y de aprendizaje automático en el ámbito médico ha seguido un proceso progresivo y de notable éxito. Ambas están transformando la medicina en varios frentes, desde la parte administrativa hasta la parte clínica. Por ejemplo, los algoritmos de inteligencia artificial han permitido a los hospitales mejorar la gestión de los registros médicos de sus bases de datos. Inteligencias artificiales de uso general como Watson de IBM (7) ayudan a los profesionales de la salud a tener un monitoreo constante de las consultas médicas y los datos sanitarios de sus pacientes, lo cual permite predecir posibles complicaciones a futuro y actuar de forma preventiva. Gracias a la inteligencia artificial también se pueden diseñar tratamientos específicos basados en los requisitos individuales de cada paciente, puesto que estas son capaces de gestionar todos sus datos clínicos de forma simultánea para ofrecer la mejor solución posible a su problema. Recientemente, la creación de modelos de aprendizaje automático ha jugado un papel clave en el devenir de la salud mundial. Durante la pandemia de COVID-19, estos modelos permitieron a los países y a sus sistemas sanitarios realizar un seguimiento de la evolución y propagación del virus a lo largo de sus territorios, facilitando así la gestión de recursos sanitarios por parte de los gobiernos (8). En el ámbito farmacéutico, plataformas como Atomwise (9) son utilizadas para analizar la estructura molecular de ciertos compuestos médicos con el fin de predecir su eficacia como posibles tratamientos, reduciendo el tiempo y costo correspondientes al método de investigación farmacéutica convencional. 1.4. Redes neuronales para la detección de anomalías clínicas El uso de redes neuronales convolucionales ha revolucionado el diagnóstico clínico de imágenes. La capacidad de estas redes de reconocer patrones y características específicas de forma visual las convierte en una herramienta particularmente efectiva para el análisis de imágenes médicas como tomografías, resonancias magnéticas o, como es en el caso de este trabajo, mamografías. Las redes neuronales convolucionales pueden ser entrenadas de forma que incluso sean capaces de identificar signos muy sutiles y prematuros de enfermedades que pueden ser pasados por alto por el ojo humano. Entre todas las aplicaciones exitosas, podemos destacar el uso de redes neuronales para la clasificación dermatológica de cánceres de piel, donde se ha obtenido un rendimiento comparable al presentado por dermatólogos profesionales (10). En el ámbito del cáncer de mama, el uso de redes convolucionales no solo mejora la precisión del diagnóstico, sino que también reduce el número de falsos positivos y negativos, dos circunstancias que pueden llevar a aplicar tratamientos innecesarios o a retrasarlos, respectivamente. A pesar de que estas técnicas tienen poco recorrido en el campo de la salud, las redes convolucionales están respaldadas por numerosos estudios que demuestran su eficacia a la hora de diagnosticar imágenes médicas. Entre todos ellos, podemos destacar un estudio publicado en 2020 (11) acerca del diagnóstico de cáncer de mama en mamografías, donde la inteligencia artificial desarrollada ofrecía predicciones que superaban en precisión a las de varios radiólogos expertos. El avance y la aplicación de redes neuronales en el diagnóstico de imágenes clínicas está evolucionando hasta llegar a puntos donde ya no solo sirven como una herramienta para optimizar las decisiones de los profesionales, sino que también están presentando unas cotas de rendimiento tan altas que pueden llegar a cambiar por completo el ámbito de la medicina. La mejora en la velocidad y la precisión de los algoritmos facilita su integración en cada vez más áreas de la
Introducción 4 4 medicina, lo que está generando un enorme impacto positivo en la salud pública a nivel mundial.
5 2 REDES NEURONALES ARTIFICIALES as redes neuronales artificiales son el principio de funcionamiento de los algoritmos de aprendizaje profundo. Nacieron inspiradas en el concepto de neurona del cerebro humano, de ahí el nombre del gran número de nodos interconectados que se utilizan durante el entrenamiento para el aprendizaje de la red. 2.1. Aprendizaje automático El aprendizaje automático o machine learning es un campo dentro de la inteligencia artificial cuyo objetivo es crear un modelo de aprendizaje capaz de otorgar soluciones particulares a un problema partiendo de ejemplos y soluciones generales (12). 2.1.1. Formas de aprendizaje automático Dentro de un campo tan amplio como es el del aprendizaje automático, existen cuatro grandes tipos de aprendizaje: el aprendizaje supervisado, el aprendizaje no supervisado, el aprendizaje semisupervisado y el aprendizaje por refuerzo. Figura 2-1. Formas de aprendizaje automático 2.1.1.1 Aprendizaje supervisado En el aprendizaje supervisado el modelo aprende a través de ejemplos, es decir, se introducen una serie de datos de los que se conocen las salidas deseadas y el algoritmo se encarga de adaptarse de tal forma que sea capaz de establecer una relación entre cada entrada y su salida correspondiente. Durante este proceso se buscan patrones entre entradas y salidas, y es el usuario el encargado de ir corrigiendo las relaciones establecidas para que el modelo funcione correctamente. Esta forma de aprendizaje automático se divide en dos grandes tipos de algoritmos, dependiendo de la tarea que se quiera realizar: • Algoritmos de clasificación. En ellos el modelo recibe un dato a su entrada y, en base a una serie de salidas predefinidas por el usuario, se le otorga un valor probabilístico para cada una de ellas, siendo la clase estimada la que mayor valor presente. Este tipo de algoritmo es, como bien hemos mencionado antes, el típico utilizado para clasificación de imágenes médicas. • Algoritmos de regresión. Los algoritmos de regresión establecen una relación entre todos los componentes de los datos de entrada para otorgar un valor estimado de salida. Un algoritmo de este tipo es muy útil para sacar aproximaciones, por ejemplo, a la hora de estimar el número de multas que se pondrán en verano en base al número de desplazamientos registrados en años anteriores. Tipos de aprendizaje automático Aprendizaje supervisado Aprendizaje sin supervisión Aprendizaje semisupervisado Aprendizaje por refuerzo L
Redes neuronales artificiales 6 6 2.1.1.2 Aprendizaje sin supervisión En este tipo de aprendizaje el modelo se encarga de encontrar los patrones dentro del conjunto de datos introducido sin el establecimiento previo de referencias de salida. Los algoritmos sin supervisión tienen dos formas de establecer un orden en los datos de entrada: el clustering y la asociación. • Algoritmos de clustering. Los datos se dividen dependiendo de sus diferencias y sus parecidos. El objetivo es encontrar datos de entrada similares y agruparlos en cúmulos de datos, también conocidos como clústeres. Este tipo de algoritmos es muy recurrente a la hora de crear promociones personalizadas en base a los perfiles de cliente de una empresa. • Algoritmos de asociación. La diferencia principal con el algoritmo anterior es que los algoritmos de asociación se encargan de establecer relaciones entre los datos del conjunto de entrada, intentando buscar una regla capaz de indicar la implicación de un dato en la presencia de otro (13). Siguiendo con el ejemplo anterior, un algoritmo asociativo podría utilizarse para identificar patrones de consumo de los clientes y relaciones entre diferentes productos de una empresa. 2.1.1.3 Aprendizaje semisupervisado El aprendizaje semisupervisado es una mezcla entre el aprendizaje supervisado y el aprendizaje sin supervisión. Mientras que unos datos de entrada están dotados de etiquetas con los patrones necesarios para llegar a la salida que les corresponde, otros no poseen ningún tipo de referencia. Con este aprendizaje se consigue la capacidad de clasificar etiquetas en base a las directrices del entrenamiento. 2.1.1.4 Aprendizaje por refuerzo En este caso, el principio de este aprendizaje está fundamentado en un algoritmo de premios y castigos. Cuando el modelo funciona correctamente, puede seguir avanzando en el entrenamiento sin problema; pero cuando este realiza una decisión incorrecta se le castiga, haciéndole perder el progreso. Este funcionamiento se asemejaría al de un algoritmo de prueba y error donde se trata de optimizar la recompensa del modelo al máximo, y es el que suele utilizarse en los motores de búsqueda para personalizar los anuncios en Internet. 2.2. Aprendizaje profundo El aprendizaje profundo o deep learning forma parte del aprendizaje automático. Su función es la de organizar, procesar y extraer de forma automática la información más importante dentro de un conjunto de datos de entrada completamente desestructurado. La ventaja del aprendizaje profundo frente al automático es que este no necesita de un proceso de etiquetado o una comprobación constante del proceso de aprendizaje de los modelos. Esto permite a los modelos entrenarse automáticamente, a costa de un considerable aumento en el coste computacional.
7 7 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 2.3. Estructura de una red neuronal artificial Las redes neuronales artificiales están formadas por neuronas que se distribuyen en capas. Dentro de estas capas siempre hay una capa de entrada para los datos, una o varias capas ocultas encargadas de la extracción de información, y una capa de salida que ofrece los resultados. Figura 2-2. Estructura básica de una red neuronal artificial (14) Las capas y, más específicamente, las neuronas, están interconectadas entre sí. Las neuronas están formadas por una serie de pesos, sesgos y umbrales que definen el flujo de información dentro de la red. A mayores sean los pesos que conectan una serie de neuronas, mayor importancia tendrán la característica que captan a la hora de definir la salida de la red. Los sesgos y los umbrales dotan a la red de flexibilidad en la activación, decidiendo si las salidas de determinadas neuronas se transmiten o no a capas posteriores. A mayor sea el número de capas ocultas y de neuronas de una red, mayor coste computacional requerirá entrenarla, aunque también habrá más parámetros que permitirán refinar y ajustar más la red a los datos de entrenamiento. Más adelante veremos que esto no siempre es algo positivo, ya que puede afectar al rendimiento final del modelo. 2.3.1 Funciones de activación Las funciones de activación de una neurona son una parte fundamental dentro del entrenamiento de una red neuronal. Estas se encargan de moldear la información que se maneja a lo largo de la red, permitiendo añadir efectos de no linealidad que permiten aumentar la complejidad del modelo y su adaptabilidad ante nuevos datos de entrada. Por otro lado, su función es clave para denotar el formato de la información que queremos obtener en las neuronas de salida. Diferentes tipos de funciones de activación en la capa de salida se utilizan para diferentes aplicaciones. Por ejemplo, en una red para clasificación utilizaríamos una función sigmoide para acotar los valores de salida entre 0 y 1 con el fin de obtener una probabilidad de que el dato de entrada pertenezca a una determinada clase. Por otro lado, si tuviéramos una red para regresión que puede devolver valores mayores que la unidad, pero no menores que cero, podríamos utilizar una función ReLU. En la Figura 2-3 se pueden observar algunos de los tipos de función de activación utilizados en redes neuronales.
Redes neuronales artificiales 8 8 Figura 2-3. Ejemplos de las funciones de activación más utilizadas en redes neuronales (15) 2.4. Entrenamiento de una red neuronal El entrenamiento de una red neuronal es el proceso que permite a la red aprender a realizar tareas específicas en función de los datos de entrada. La recolección de un gran conjunto de datos representativos del problema es clave a la hora de que el entrenamiento devuelva resultados de calidad. Estos, además, han de ser preprocesados correctamente para eliminar toda la información que no tenga utilidad y puedea afectar a los resultados finales. Durante el entrenamiento. los diferentes pesos que conforman la red van actualizándose poco a poco para ir mejorando la salida. La actualización se hace en base a una métrica previamente definida denominada función de coste, y cada ciclo en el que ésta se lleva a cabo se denomina época de entrenamiento o epoch. El trabajo del entrenamiento pues es ir iterando entre diferentes valores de los pesos hasta alcanzar el mínimo valor posible en la función de coste en un número de epochs definido previamente. Un valor alto que no se reduce durante el entrenamiento indicaría un mal aprendizaje al no ser la red capaz de adaptarse a la información de entrada.
9 9 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 2.4.1 Función de coste Como ya hemos mencionado, la función de coste es la métrica por minimizar durante el entrenamiento. Esta indica el nivel de discordancia de los datos de entrenamiento entre las predicciones realizadas por la red y sus valores reales. Algunas de las funciones de coste más recurrentes son: • Error cuadrático medio. Es probablemente la función de coste más utilizada en problemas de regresión. Se calcula como la media de los cuadrados de las diferencias entre predicciones y valores reales: 𝐸𝐶𝑀=1 𝑁∑(𝑦𝑖−𝑦𝑖)2 𝑁 𝑖=1 donde N es el total de datos del entrenamiento, 𝑦𝑖 es el valor real de la predicción, e 𝑦𝑖 el valor predicho por el modelo. • Entropía cruzada. Es una función de coste utilizada comúnmente en problemas de clasificación binaria. Para estos casos, esta se define como: 𝐿(𝑦,𝑦)=−1 𝑁∑[𝑦𝑖·𝑙𝑜𝑔(𝑦𝑖)−(1−𝑦𝑖)·log(1−𝑦𝑖)] 𝑁 𝑖=1 donde 𝑦𝑖 es la etiqueta verdadera (0 o 1, por norma general), e 𝑦𝑖 la probabilidad predicha para la clase correcta. En el caso de que la clasificación no sea binaria, se utiliza la entropía cruzada categórica: 𝐿(𝑦,𝑦)=−∑∑𝑦𝑖𝑐·log(𝑦𝑖𝑐) 𝐶 𝑐=1 𝑁 𝑖=1 donde C es el número de clases, 𝑦𝑖𝑐 es un indicador binario que es 1 si la clase correcta es c para el dato i, e 𝑦𝑖𝑐 es la probabilidad predicha para la clase c. • Error absoluto medio. Esta función de coste mide las diferencias absolutas entre las predicciones y los valores reales, siendo sus resultados algo más robustos que los del error cuadrático medio ante valores atípicos: 𝑀𝐴𝐸=1 𝑁∑|𝑦𝑖−𝑦𝑖| 𝑁 𝑖=1 2.4.2 Descenso de gradiente El descenso de gradiente es un algoritmo de optimización encargado de minimizar la función de coste y, por tanto, de mejorar el rendimiento del entrenamiento. Su función es ajustar los pesos y sesgos de la red neuronal de forma iterativa, moviéndose en dirección opuesta al gradiente de la función de coste con respecto a dichos parámetros para alcanzar el mínimo valor posible de la función. El gradiente de la función de coste con respecto a los pesos (w) es un vector que indica la dirección y la tasa de (2.1) (2.2) (2.3) (2.4)
Redes neuronales artificiales 10 10 cambio más pronunciada de la función de coste, siendo este definido como un vector de derivadas parciales: ∇𝐿(𝑤)=[𝜕𝐿 𝜕𝑤1,𝜕𝐿 𝜕𝑤2,…, 𝜕𝐿 𝜕𝑤𝑛] donde L(w) es la función de coste. Como ya hemos mencionado anteriormente, los parámetros se actualizan en la dirección opuesta al gradiente, escalada por un factor llamado tasa de aprendizaje (η) según: 𝑤≜𝑤−𝜂·𝛻𝐿(𝑤) Este proceso es repetido iterativamente hasta que los parámetros converjan a valores que permitan alcanzar un mínimo en la función de coste. Aunque esta aplicación del descenso de gradiente es perfectamente aplicable a cualquier tipo de red neuronal, existen alternativas y modificaciones que calculan la actualización de los parámetros de otras formas, pudiendo ser interesantes bajo ciertas circunstancias. Entre las alternativas disponibles podemos destacar: • Descenso de gradiente estocástico. El descenso de gradiente estocástico utiliza únicamente un dato del conjunto de entrenamiento a la vez siguiendo la fórmula anterior, lo cual reduce el coste computacional, pero introduce ruido en las actualizaciones y puede inestabilizar la convergencia: 𝑤≜𝑤−𝜂·𝛻𝐿𝑖(𝑤) donde 𝐿𝑖(𝑤) es la función de coste del i-ésimo dato. • Descenso de gradiente por lotes. Calcula el gradiente de la función de coste utilizando todo el conjunto del entrenamiento. Con esto se consigue estabilidad a la hora de converger, aunque los requisitos computacionales y temporales aumentan considerablemente si el conjunto de datos es muy grande: 𝑤≜𝑤−𝜂·1 𝑁∑𝛻𝐿𝑖(𝑤) 𝑁 𝑖=1 • Descenso de gradiente por mini-lotes. Combina los dos enfoques anteriores al utilizar pequeños lotes de datos del conjunto de entrenamiento para actualizar los parámetros en cada iteración: 𝑤≜𝑤−𝜂·1 𝑚∑𝛻𝐿𝑖(𝑤) 𝑚 𝑖=1 donde m es el tamaño del mini-lote. Suele ser la opción más utilizada. 2.4.2.1 Mejoras del descenso de gradiente El descenso de gradiente estándar puede ser mejorado al utilizarse junto a algoritmos de optimización avanzados, los cuales permiten ajustar de forma dinámica parámetros como la tasa de aprendizaje con el (2.5) (2.6) (2.7) (2.8) (2.9)
11 11 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama objetivo de hacer más rápida la convergencia y mejorar la estabilidad del modelo. Entre algunos de los más utilizados tenemos: • Descenso de gradiente con Momentum. Introduce un término de Momentum que varía dependiendo de valores anteriores del gradiente: 𝑣𝑡=𝛾·𝑣𝑡−1+𝜂·𝛻𝐿(𝑤) 𝑤≜𝑤−𝑣𝑡 donde 𝑣𝑡 es la velocidad en el tiempo t y 𝛾 es el coeficiente de Momentum. • RMSProp (Root Mean Square Propagation). Adapta individualmente la tasa de aprendizaje para cada uno de los parámetros del modelo: 𝐸[𝑔2]𝑡=𝛽·𝐸[𝑔2]𝑡−1+(1−𝛽)·𝑔𝑡2 𝑤≜𝑤− 𝜂 √𝐸[𝑔2]𝑡+∈·𝑔𝑡 donde 𝐸[𝑔2]𝑡 es la media móvil de los cuadrados de los gradientes, 𝛽 es un parámetro de decaimiento, y ∈ es un término de estabilidad para evitar divisiones por cero. • Adam (Adaptive Moment Estimation). Combina las ventajas de los dos métodos anteriores en un único algoritmo: 𝑚𝑡=𝛽1·𝑚𝑡−1+(1−𝛽1)·𝑔𝑡 𝑣𝑡=𝛽2·𝑣𝑡−1+(1−𝛽2)·𝑔𝑡2 Los parámetros se actualizan utilizando: 𝑚𝑡=𝑚𝑡 1−𝛽1𝑡 𝑣𝑡=𝑣𝑡 1−𝛽2 𝑡 𝑤≜𝑤−𝜂· 𝑚𝑡 √𝑣𝑡+∈ donde 𝑚𝑡 y 𝑣𝑡 son las estimaciones sesgadas de los momentos. • AdamW. La implementación del optimizador Adam con regularización L2 sigue las siguientes ecuaciones: (2.10) (2.11) (2.12) (2.13) (2.14) (2.15) (2.16) (2.17) (2.18)
Redes neuronales artificiales 18 18
19 3 REDES NEURONALES CONVOLUCIONALES as redes neuronales convolucionales son un tipo de red neuronal artificial especializada en el procesamiento de imágenes. Son especialmente útiles para reconocer características y patrones debido a su capacidad de capturar información tanto espacial como temporal. Utilizan filtros convolucionales y comparten pesos para diferentes partes de la entrada, lo que las hace especialmente útiles para tareas como la detección de anomalías o la clasificación de imágenes. 3.1. Estructura de una red neuronal convolucional Ya hemos mencionado que las redes neuronales convolucionales se centran en el procesamiento de imágenes. Una imagen se puede definir como un conjunto de píxeles ordenados en forma de array con tantos canales como información independiente presente: colores, trasparencia… En una red neuronal convolucional las neuronas de una misma capa no están interconectadas, sino que, por norma general, estas están conectadas directamente con la capa posterior, por lo que el flujo de la información es unidireccional. La estructura básica de este tipo de red incluye tres tipos de capa complementadas entre sí: las capas convolucionales, las capas de pooling y las capas densas o completamente conectadas. Figura 3-1. Estructura convencional de una red neuronal convolucional para clasificación (21) 3.1.1 Capas convolucionales Las capas convolucionales son la base de las redes neuronales convolucionales. Diseñadas para procesar datos estructurados en forma de cuadrícula, utilizan operaciones de convolución para extraer características de los datos de entrada. Se fundamentan en la operación de convolución, donde un filtro o máscara de tamaño reducido se va desplazando a través de la imagen calculando los productos entre los pesos del filtro y los valores de los píxeles de la imagen correspondientes a cada posición de la máscara. Como resultado se obtiene un mapa de características: una imagen de menor resolución a la original que resalta la información que un filtro en particular ha considerado de mayor importancia. L
Redes neuronales convolucionales 20 20 Figura 3-2. Operación de convolución de una matriz con una máscara 3x3 (22) Siguiendo con el ejemplo de la imagen, las máscaras que conforman la primera capa de la red se encargarían de extraer las características más sencillas como contornos, colores o texturas; mientras que los filtros de las capas más profundas estarían más orientados a la extracción de información compleja, presentando unos niveles de abstracción imposibles de interpretar a simple vista. 3.1.1.1 Profundidad de la convolución Es importante tener en cuenta que, los filtros de una capa de convolución han de tener la misma cantidad de canales que la imagen o mapa de características a procesar. Por ejemplo, los filtros de la primera capa de convolución de un modelo que recibe imágenes en escala de grises deberán tener un canal de profundidad. Si dicha imagen fuera RGB, los filtros pasarían a necesitar tres canales de profundidad. Si se aplica más de un filtro en una capa de convolución, la salida será un array que contendrá tantos mapas de características como filtros se hayan utilizado. En la Figura 3-3 se puede observar visualmente el concepto de profundidad y de su efecto en la salida de una capa convolucional.
21 21 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Figura 3-3. Representación gráfica de la influencia de la profundidad en la convolución (23) 3.1.1.2 Paso de convolución Normalmente, el proceso de convolución es un proceso lento, ya que se realiza píxel a píxel; pero esto no es siempre así. El paso de convolución o stride define el salto en píxeles que realizan las máscaras de convolución durante el recorrido de la imagen. Figura 3-4. Efectos de aumentar el paso de convolución (24) Es interesante aplicar un paso grande cuando la información se encuentra de forma periódica y abundante en una imagen, aunque esto conlleva el riesgo correspondiente de poder pasar por alto información relevante. Como es de esperar, a mayor sea el paso utilizado, menores dimensiones tendrá el mapa de características resultante. 3.1.1.3 Relleno Otro de los parámetros más importantes de las capas convolucionales es el relleno o padding. Cuando llevamos a cabo la convolución, los bordes no son considerados, ya que no tienen información más allá de ellos con la que aplicar correctamente la operación. Ante esta situación, se pueden definir tres métodos distintos de actuación: • No hacer padding (valid padding). Este es el caso más simple, ya que simplemente no aplica la
Redes neuronales convolucionales 22 22 convolución en las zonas donde no se puede. Como resultado, se pierde la información de los bordes y los mapas de características resultantes ven su dimensión reducida. • Sustituir por cero (zero padding). Si interesa mantener las dimensiones de la imagen, se pueden sustituir por cero todos los bordes donde no se puede aplicar la convolución. La imagen resultante mantendrá las dimensiones, pero tendrá un característico borde negro alrededor. • Añadir ceros alrededor (same padding). Esta alternativa también permite mantener los bordes de la imagen. Añade tantas ceros como sea necesario más allá de los bordes para poder aplicar una convolución parcial y evitar el borde negro asociado añ método anterior. 3.1.1.4 Función de activación Tras haber obtenido un mapa de características como los de la Figura 3-5, la capa convolucional aplica una función de activación para añadir no linealidades a la red. Con esto se busca aprender características y patrones complejos que no podrían ser representados con meras operaciones lineales. Figura 3-5. Mapas de características generados por una red de clasificación de números (25) Las funciones de activación también ayudan a la propagación de gradientes y a evitar la saturación de neuronas, es decir, ayudan a mitigar el problema de que los gradientes se acaben volviendo extremadamente pequeños y ralenticen el entrenamiento. Las funciones de la Figura 2-3 más utilizadas para la convolución son las recogidas a continuación (26): • ReLU (Rectified Liner Unit). Es la función de activación que más suele utilizarse en las capas ocultas de una red neuronal convolucional: 𝑓(𝑥)=max(0,𝑥) Es eficiente a nivel computacional y ayuda a evitar el desvanecimiento de gradiente, pero tiene el problema de que a veces produce neuronas “muertas”, que son aquellas que nunca se activan porque solo reciben valores negativos. • Leaky ReLU. Alternativa a la ReLU convencional que resuelve el problema de las neuronas “muertas” al permitir el flujo de gradiente a entradas negativas: (3.1)
23 23 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 𝑓(𝑥)=max(0.1𝑥,𝑥) El hecho de permitir entradas negativas aumenta la complejidad del cálculo y, por tanto, la duración del entrenamiento. • Parametric ReLU. Es igual que la alternativa anterior solo que el término constante pasa a ser una variable 𝛼≤1 que se puede entrenar: 𝑓(𝑥)=max(𝛼·𝑥,𝑥) Suele utilizarse cuando es necesaria una mayor flexibilidad en la función de activación, pero ralentiza aún más el entrenamiento. • ELU (Exponential Linear Unit). Se utiliza en capas ocultas para no saturar la salida de la activación y para poder manejar valores negativos: 𝑓(𝑥)={𝑥, 𝑠𝑖𝑥≥0 𝛼·(𝑒𝑥−1), 𝑠𝑖𝑥<0 Esta función también evita el problema de las neuronas “muertas” y permite obtener una salida negativa, pero sigue manteniendo el problema del coste computacional al tener que realizar un cálculo exponencial. • SiLU (Sigmoid-weighted Linear Unit). El uso de esta función en las capas ocultas de la red es relativamente novedoso (27) y ha demostrado mejor rendimiento que sus competidoras en ciertas tareas: 𝑓(𝑥)=𝑥·𝜎(𝑥) donde 𝜎(𝑥) es la función sigmoide: 𝜎(𝑥)=1 1+𝑒−𝑥 Como bien se puede ver por su definición, esta función también es bastante costosa computacionalmente, pero con ella se evitan los problemas de las neuronas “muertas” y del desvanecimiento de gradiente. La selección de una u otra función para las capas ocultas de un modelo dependerá de su aplicación final y de los datos de entrada que se le suministrarán. Por norma general, la función que mejor relación rendimiento/coste ofrece es la ReLU y, como ya hemos comentado, es la que casi siempre se utiliza. 3.1.2 Capas de pooling Las capas de pooling, también conocidas como capas de submuestreo, son otro de los componentes esenciales de las redes neuronales convolucionales. Su principal cometido es el de reducir las dimensiones espaciales, es decir, el ancho y el alto de los mapas de características extraídos por las capas convolucionales; conservando la información más importante. (3.2) (3.3) (3.4) (3.5) (3.6)
Redes neuronales convolucionales 24 24 Al reducir el tamaño de los mapas de características, se reduce también el número de parámetros de la red, consiguiendo que esta se vuelva menos costosa de entrenar y reduciendo con ello las probabilidades de que se sobreajuste. Trabajan de forma similar a las capas convolucionales, pero, en vez de realizar la operación de convolución, aplican una función de agregación a los valores dentro del área de influencia de la máscara. Los parámetros de esta capa coinciden con los de la capa convolucional: tamaño de la máscara, paso y relleno. El principio de funcionamiento de estas capas también aumenta la robustez ante entradas de entrenamiento de variabilidad moderada, ya que son capaces de mostrar resultados similares bajo un cierto grado de variación provocado por traslaciones, rotaciones, distorsiones... 3.1.2.1 Tipos de pooling Existen varias formas de aplicar el pooling, aunque las más utilizadas actualmente son: • Max Pooling. La entrada se divide en regiones del tamaño del filtro (las cuales pueden o no estar superpuestas dependiendo del paso elegido) y, para cada una de estas regiones, se toma el valor máximo. Figura 3-6. Ejemplo de aplicación de max pooling (28) Es el método más interesante si lo que se busca es extraer los patrones más importantes del mapa de características. • Average Pooling. Funciona exactamente igual que el método anterior solo que, en este caso, se hace la media de todos los valores dentro de la región sobre la que se aplica la máscara. Figura 3-7. Ejemplo de aplicación de average pooling (29) Suele ser la opción elegida cuando se busca una representación más equilibrada de las características
25 25 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama del mapa. • Global Average Pooling (GAP). Este método es algo diferente a los dos mencionados, ya que se utiliza con el fin de sustituir las capas densas de la red neuronal. Realiza un promedio de los valores de cada mapa de características de un array y devuelve un vector unidimensional con los resultados (30). Figura 3-8. Diferencias entre el uso de una capa densa y de Global Average Pooling (31) Al ser estos valores producto de una operación matemática, no cuentan como parámetros a entrenar de la red, por lo que se evita el sobreajuste en la capa resultante. Por otro lado, utilizando este método se pierde mucha menos información espacial, por lo que la red aumenta aún más su robustez ante traslaciones. 3.1.3 Capas densas También conocidas como capas completamente conectadas, las capas densas suelen utilizarse en las últimas etapas de una red neuronal convolucional. Su función se podría comparar a la de un clasificador para los mapas de características, donde las neuronas se activan al encontrar una característica específica en los mapa. Los filtros de las capas densas se conectan con todas las salidas de las capas anteriores y con todas las entradas de las capas posteriores, funcionando de forma similar a las redes neuronales artificiales más básicas. Esta capa no puede trabajar con estructuras de carácter multidimensional, por lo que los datos de entrada necesitan un preprocesado previo para representarlos en una dimensión. Este proceso es conocido como flattening, y puede verse su funcionamiento en la Figura 3-9. Figura 3-9. Flattening de una array multidimensional 3x2x2 (1)
Redes neuronales convolucionales 26 26 Las conexiones de cada una de las neuronas de una capa densa se representan como una matriz de pesos y un vector de sesgos, y su salida se calcula como la suma ponderada de sus entradas a la que se le aplica una función de activación: 𝑦=𝑓(∑(𝑤𝑖·𝑥𝑖+𝑏) 𝑛 𝑖=1 ) donde y es la salida de la neurona, wi son los pesos, xi son las entradas, b es el sesgo y f es la función de activación. Por norma general, todas las funciones de activación de las capas densas que funcionan en la red a modo de capa oculta utilizan la función de activación ReLU. Sin embargo, la capa de salida de las redes, conformada por neuronas completamente conectadas, suele variar su función de activación dependiendo de la función final de la red: • Clasificación. Si lo que se busca es resolver un problema de clasificación, las dos funciones de activación que suelen utilizarse son la sigmoide, para clasificación binaria, y la softmax, para clasificación multiclase. La diferencia entre ambas es que en clasificación binaria se utiliza una única neurona y se devuelve un valor entre 0 y 1 que definirá la clase a la que pertenece la entrada en base a un umbral predefinido; y en clasificación multiclase hay tantas neuronas como clases, por lo que el valor devuelto es la probabilidad de pertenecer a cada clase, sumando uno entre todas las probabilidades. • Regresión. Para un problema de regresión la función utilizada suele ser la función identidad, también conocida como función lineal, debido a que genera una salida en el rango continuo de los números reales. Existen aplicaciones donde también se puede utilizar la ReLU como función de activación, principalmente aquellas en las que no interesan los valores negativos. 3.1.4 Otras capas Además de las mencionadas, hay muchos otros tipos de capas que pueden formar parte de una red neuronal convolucional, pero que no son estrictamente necesarias para su correcto funcionamiento. Entre las más destacadas están las capas de normalización, las de regularización, las residuales, las de concatenación… Cada una de ellas aportan sus propias funcionalidades con el fin de mejorar el desempeño de la red neuronal en tareas complejas. (3.7)
27 4 CREACIÓN DEL CONJUNTO DE DATOS a RSNA ofrece en el concurso de Kaggle (32) un conjunto de datos de 54.706 mamografías de aproximadamente 12.000 pacientes distintos en formato DICOM, para que cada uno de los participantes genere su conjunto personalizado y experimente con diferentes distribuciones. Todos los códigos mencionados en este apartado y en los posteriores están programados en Python y utilizan librerías como Pandas, Pillow o Matplotlib, entre otras. 4.1. Formato DICOM El formato DICOM (Digital Imaging and Communications in Medicine) es un estándar internacional utilizado en el manejo y transmisión de información de imágenes médicas (33). Se caracteriza por almacenar la información contenida en la imagen junto con otra gran cantidad de metadatos que pueden ser relevantes para su análisis como, por ejemplo, información acerca del paciente estudiado o los parámetros de la máquina que ha extraído la imagen. Al ser un estándar, el formato DICOM facilita la interoperabilidad entre dispositivos médicos de diferentes fabricantes, garantizando que no se pierda ninguna traza de información entre diferentes plataformas. Debido a la complejidad del formato, el uso de imágenes DICOM como datos de entrenamiento para una red neuronal convolucional queda descartado. Esto se debe a la variabilidad de las dimensiones de los archivos (no todos contienen la misma cantidad de metadatos) y a que estos, a su vez, presentan demasiada información irrelevante para el aprendizaje que requeriría de una enorme potencia computacional para ser procesada. 4.1.1 Formato PNG como alternativa Debido a las dificultades que presenta el uso de las imágenes en su formato original, se ha optado por utilizar un formato ampliamente soportado en el ámbito de las redes neuronales convolucionales, el formato PNG. Este formato tiene la característica de que no presenta pérdidas, por lo que asegura que la calidad de las imágenes DICOM se conservan en la conversión. Además, se deshace de todos la información adicional del formato original para quedarse únicamente con los valores de los píxeles de la imagen, por lo que se consigue reducir el tamaño de las mamografías tanto en dimensión como en espacio en el disco. 4.2. Descripción del conjunto de datos De las casi 55.000 mamografías del conjunto de datos original, únicamente un 2% presentan cáncer. Un conjunto de datos tan desfavorecido en la clase positiva puede provocar problemas en el entrenamiento y desempeño de la red neuronal, aunque de ello hablaremos más adelante. En la Figura 4-1 se puede observar una representación gráfica del balance de clases obtenida mediante la librería Matplotlib de Python. L
Creación del conjunto de datos 34 34 Figura 4-9. Resultado de voltear horizontal y verticalmente una mamografía 4.4.1.4 Recortes aleatorios en la imagen El recorte de parches de forma aleatoria ayuda a prevenir el sobreajuste, puesto que obliga a la red a fijarse en la información de toda la imagen al no a saber nunca qué partes de ésta estarán presentes y qué partes no, de forma parecida a como lo hace una capa de dropout en un modelo de red convolucional. El tamaño de los parches es de 30x30 píxeles, y el total de recortes a realizar será un número aleatorio entre 6 y 8 parches. Al principio del Código 4 está definido el funcionamiento de esta transformación personalizada. Figura 4-10. Resultado de recortar partes aleatorias de una mamografía Existe la posibilidad de que alguno de estos recortes se realice encima de información relevante para la clasificación, aunque esta pérdida de información se compensa con el resto de las imágenes generadas.
35 35 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 4.4.2 Submuestreo de datos negativos Para hacer frente al desbalance de clases y mantener una proporción más apta para el aprendizaje, se puede o aumentar el número de muestras positivas o reducir el de muestras negativas. Debido a que ya se ha recurrido al aumento de datos de la clase positiva, el próximo paso es reducir el número de imágenes de la clase negativa. Para el conjunto de regiones de interés de 512x512 píxeles, la relación buscada es de una mamografía positiva por cada cinco mamografías en el conjunto de entrenamiento. Para el conjunto de 512x1024 píxeles, la relación es de una muestra positiva por cada dos. La diferencia entre las relaciones de las mamografías positivas y negativas en cada conjunto es producto del ensayo tanto con modelos creados desde cero, como con modelos preentrenados. Se han acabado escogiendo estas dos relaciones porque son las que mejores resultados han mostrado durante el entrenamiento. El número total de muestras de cada subconjunto de entrenamiento ha sido definido en base la cantidad de píxeles que hay que procesar en cada caso y que, por ende, afectan al tiempo de aprendizaje. Esta es la razón por la que el conjunto de imágenes de 512x1024 píxeles, que tiene más información que procesar por dato, ha de reducir el total de muestras de entrenamiento para no extender en exceso su duración. El algoritmo de reducción de muestras (Código 5) utilizado para el subconjunto de entrenamiento será también utilizado sobre los subconjuntos de validación y de evaluación para que, como se mencionaba con anterioridad, todos ellos puedan dividirse entre potencias de dos. Finalmente, las nuevas organizaciones de los subconjuntos de datos quedan tal y como se recogen en la Figura 4-11 y en la Figura 4-12. En ambos caso se observa que los conjuntos de validación y de prueba presentan una relación prácticamente idéntica a la del conjunto de datos original. Figura 4-11. Distribución final de los subconjuntos de las regiones de interés escaladas a 512x512
Creación del conjunto de datos 36 36 Figura 4-12. Distribución final de los subconjuntos de las regiones de interés escaladas a 512x1024 Cabe destacar que durante el desarrollo de este trabajo se han considerado múltiples distribuciones diferentes para los subconjuntos de datos. En este capítulo únicamente se recoge el proceso de creación de los dos conjuntos de datos que más se han utilizado y que, además, han servido para evaluar las últimas diferentes redes propuestas.
37 5 DESARROLLO DE LA RED NEURONAL l desarrollo de la red neuronal convolucional ha sido un proceso de ensayo y error progresivo entre varias ideas y configuraciones que ha ido dando forma al modelo definitivo utilizado en la evaluación final. En este capítulo se recorrerán por orden cronológico los diferentes planteamientos propuestos durante la realización de este trabajo. Al tratarse de un problema de clasificación binaria, todos los modelos utilizarán la entropía cruzada como función de coste y tendrán una capa de salida con única neurona completamente conectada con función de coste sigmoidal. Por ahora, el optimizador seleccionado para el descenso de gradiente será el Adam. La referencia de puntuación para el modelo será la media de las puntuaciones obtenidas por los usuarios del concurso (~0.23). La puntuación más alta obtenida en el concurso ha sido de 0.55. 5.1. Primera propuesta En un principio, se planteó crear una red neuronal convolucional partiendo desde cero, utilizando el primer conjunto de entrenamiento importado (imágenes en formato DICOM escaladas a 512x512 píxeles y convertidas a PNG) (34). Para el balance de clases, se redujo el número de muestras de las clases negativa en los tres subconjuntos de datos, hasta llegar a una relación de, aproximadamente, una mamografía positiva cada cuatro muestras. En esta primera parte se han realizado numerosas pruebas con distintas arquitecturas de red para intentar obtener los mejores resultados posibles, pero nos centraremos solo en los dos modelos que se utilizarán como referencia en propuestas posteriores. 5.1.1 Primer modelo de red La arquitectura de la primera red neuronal propuesta para el entrenamiento se puede observar en el Modelo 1. A pesar de tener una cantidad de parámetros considerable, la estructuración de las capas era bastante sencilla, ya que solo consistía en capas de convolución, capas de pooling y capas densas. Incluso con el efecto de la regularización de las capas de dropout, la red se sobreajustaba muy rápidamente al conjunto de entrenamiento, haciendo que las función de coste en el conjunto de validación se disparara (Figura 5-1). E
Desarrollo de la red neuronal 38 38 Figura 5-1. Evolución de las pérdidas durante el entrenamiento del primer modelo Se intentó acentuar el efecto del dropout, agregar más capas de regularización L1/L2, cambiar la tasa de aprendizaje y el tamaño del lote, aumentar la cantidad de muestras positivas mediante aumento de datos… Pero la escasa complejidad de la red sumada a la gran cantidad de parámetros por entrenar hacía imposible eliminar el sobreajuste. 5.1.2 Segundo modelo de red Para el segundo modelo, se tomó como referencia una arquitectura propuesta por Keras (36) para la clasificación binaria de neumonías (Modelo 2), la cual fue adaptada para su uso en este proyecto, cambiándose algunas capas y reduciéndose el número de neuronas. Consiste en una serie de bloques de convolución formados por pares de capas de convolución 2D separadas para cada canal de entrada, y en bloques de capas densas en serie. La adaptación propuesta ha sustituido la convolución 2D por canales por la convolución clásica, ya que las imágenes se cargaban en escala de grises y, por ello, solo contaban con un canal. Aunque se aumentó el número de capas totales, el número de parámetros resultante fue bastante similar al del caso anterior. Figura 5-2. Número de parámetros del segundo modelo de red propuesto La nueva configuración aumentó en gran medida el tiempo de entrenamiento y, a pesar de reducir considerablemente las pérdidas en el subconjunto de validación, no pudo solventar el problema de sobreajuste:
39 39 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Figura 5-3. Evolución de las pérdidas durante el entrenamiento del segundo modelo 5.1.3 Motivos del descarte A pesar del mal rendimiento en el entrenamiento, se evaluó la primera red con el conjunto de prueba, obteniéndose resultados muy lejos de lo aceptable: Figura 5-4. Resultados de la evaluación del primer modelo de red propuesto (TH = 0.5) Como se puede observar en la Figura 5-4, el modelo funcionaba prácticamente como un clasificador aleatorio. Además, el conjunto de evaluación en los primeros ensayos no se había equilibrado de forma que siguiera una distribución realista, por lo que los valores reflejados por las métricas de precisión y sensibilidad no eran nada
Desarrollo de la red neuronal 40 40 fiables a la hora de reflejar el rendimiento real de la red. Por esta razón, se decidió no entrenar el segundo modelo propuesto hasta llevar a cabo los cambios de la segunda propuesta. 5.2. Segunda propuesta Como solución a los problemas mencionados, se plantearon los siguientes cambios: • Nuevo conjunto de datos. Teniendo en cuenta los resultados, se optó por descartar el conjunto de datos utilizado en la anterior propuesta por un conjunto de datos que filtrara las mamografías para extraer únicamente las regiones de interés (35). Estas fueron escaladas a 512x512 píxeles con la intención de que, en las mismas dimensiones de imagen, hubiera más información útil de la que extraer características (Figura 4-5 y Figura 4-6). • Distribución realista de los subconjuntos. Como bien mencionamos en capítulos anteriores, seguir una distribución realista es clave a la hora de evaluar el rendimiento del modelo tanto en la validación como en el proceso de prueba. A partir de esta propuesta, estos dos conjuntos siempre estarán desbalanceados con el fin de que los resultados obtenidos sean extrapolables a una posible aplicación real. Esto, como consecuencia, tendrá un impacto significativo en el entrenamiento. • Implementación de nuevas métricas. Hasta el momento, el rendimiento del proceso de entrenamiento tenía en cuenta la exactitud, la precisión, la sensibilidad, el F1 Score y el valor de la función de coste. De aquí en adelante, cada vez que se evalúe el entrenamiento de la red se tendrán también en cuenta las versiones probabilísticas de la precisión, la sensibilidad y el F1 Score; y el valor del área bajo la curva ROC. Una vez añadidos estos cambios, se volvió a entrenar sobre los dos modelos de red de la primera propuesta. La distribución del nuevo conjunto de datos es la referida en la Figura 4-11. 5.2.1 Implementación de nuevas métricas Debido a que ni el F1 Score ni las versiones probabilísticas de éste, de la precisión y de la sensibilidad están definidos en la librería de métricas de Keras, ha sido necesario implementarlas desde cero teniendo como referencia el tutorial que ofrece la propia API para la creación de métricas personalizadas (37). Es necesario recordar que las métricas probabilísticas son las que Kaggle requiere para puntuar el modelo de predicción. En concreto, la métrica de puntuación es el F1 Score probabilístico. 5.2.1.1 Definición del F1 Score Para definir el F1 Score, primero se declaran las métricas que se utilizarán en el cálculo (Precision y Recall, que sí vienen definidas en Keras), a las que se les pasará los valores binarios de las predicciones y de las etiquetas reales para que actualicen su valor. Luego, se utilizarán dichos valores para calcular el F1 Score correspondiente según la Ecuación 2.25. Al principio del Código 6 se recoge la implementación personalizada de esta métrica. La épsilon del denominador en el cálculo del resultado se utiliza para evitar posibles divisiones por cero. 5.2.1.2 Definición de las métricas probabilísticas Para el cálculo de las métricas en su forma probabilística es necesario conservar el valor de las predicciones sin binarizar. Para el cálculo de la precisión probabilística y del F1 Score probabilístico, será necesario extraer la probabilidad asociada a los falsos positivos y a los verdaderos positivos, además del total de muestras positivas; mientras que para el cálculo de la sensibilidad, los últimos dos últimos valores son suficiente. Al estar trabajando con arrays personalizados de Keras, es necesario utilizar sus funciones para operar con los
41 41 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama valores de entrada de las métricas. Figura 5-5. Declaración de la precisión probabilística como métrica personalizada A continuación, se profundizará en el trabajo realizado por cada una de ellas: • Cast. Transforma los arrays de valores enteros a floats para poder operar con ellos sin perder precisión. • Clip by value. Es una función de seguridad que limita los valores de las predicciones al rango [0, 1]. Teniendo en cuenta cómo se obtienen los predicciones, no es estrictamente necesaria. • Where. Funciona como un if – else que crea un array que va recogiendo los valores de cada posición de un vector de predicciones o de un vector lleno de ceros en función de si la predicción es mayor que un umbral previamente definido o no. El array obtenido solo contendrá probabilidades en las posiciones donde, según el umbral, la predicción se considere positiva. • Reduce sum. Esta función suma todos los valores de un array en uno solo. Sirve para obtener el número total de muestras positivas. Si se utiliza en el array obtenido por la función where filtrado en base al vector de etiquetas verdaderas, también se puede obtener la suma total de las probabilidades de los verdaderos positivos y de los falsos positivos. 5.2.2 Pruebas con los modelos previos El cambio del conjunto de datos tuvo un impacto positivo en la minimización de la función de coste, obteniéndose valores mucho menores en el primer modelo:
Desarrollo de la red neuronal 42 42 Figura 5-6. Evolución mejorada de las pérdidas del primer modelo Esto no se tradujo al segundo modelo, ya que la cantidad de parámetros a entrenar desde cero seguía siendo demasiado grande y la complejidad de su arquitectura no tenía el suficiente impacto como para minimizar las pérdidas, siendo estas muy parecidas a las obtenidas en la propuesta anterior. Figura 5-7. Evolución similar de las pérdidas del segundo modelo
43 43 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 5.2.3 Motivos del descarte A pesar de haber mejorado un poco los resultados del entrenamiento con respecto a la propuesta inicial, el sobreajuste seguía siendo un problema. Las predicciones seguían sin ser satisfactorias ya que los modelos tendían a clasificar todas las muestras como negativas al ser incapaces de extraer las características necesarias para la clasificación: Figura 5-8. Resultados de la evaluación del segundo modelo propuesto Figura 5-9. Comparación de las métricas de evaluación en sus dos formatos Los cambios propuestos sirvieron para evaluar la red en unas circunstancias mucho más realistas, pero estos tuvieron un impacto negativo en las métricas de la evaluación. Por un lado, la acentuada desproporción entre las muestras sin cáncer y las muestras con cáncer afectaba directamente al cálculo de la precisión y del F1 Score, puesto que al haber más muestras en la clase negativa se incrementaba la posibilidad de predecir un falso positivo. Por otro lado, el cálculo de las métricas en su versión probabilística penaliza aquellas predicciones acertadas de forma aleatoria con poca seguridad, es decir, con probabilidades muy cercanas al umbral de decisión; aunque para este caso el efecto es prácticamente nulo. Al igual que en la propuesta anterior, los malos resultados del segundo modelo, que en principio debería funcionar mejor que el primero, hicieron que no fuera necesario evaluar la primera arquitectura propuesta. Finalmente, se acabó descartando la creación de un modelo de red desde cero debido a que intentar conseguir un buen equilibrio entre complejidad de la red y número de parámetros era una tarea muy lenta y complicada que, además, no garantizaba un buen rendimiento en la detección de cáncer de mama.
Desarrollo de la red neuronal 50 50 Figura 5-19. Evolución del entrenamiento y resultados de la evaluación del modelo EfficientNetB4 5.3.4 Ajuste de la red neuronal Una vez seleccionada la arquitectura EfficientNetB4 para el aprendizaje por transferencia, es necesario ajustar los hiperparámetros de la red en un proceso que se conoce como fine tuning, con el objeto de obtener los mejores resultados posibles. El ajuste del modelo en este trabajo está fundamentado en tres ideas: cambiar el optimizador y probar diferentes parámetros de entrada; implementar una política de programación de la tasa de aprendizaje para testar múltiples tasas de una vez; y ensayar con diferentes configuraciones de capas congeladas. También forma parte del ajuste de la red la modificación de sus hiperparámetros (tamaño de los lotes, número de épocas, tasa de aprendizaje máxima…) En el anexo (Código 6) se encuentra el código utilizado en el entrenamiento para la versión final de la red, el cual contiene las implementaciones mencionadas junto con los hiperparámetros definitivos. 5.3.4.1 Cambio de optimizador Como ya comentábamos con anterioridad, el optimizador Adam, que es el que se ha estado utilizando hasta el momento; es una opción más que viable a la hora de elegir un método de descenso de gradiente, puesto que permite adaptar la tasa de aprendizaje de forma dinámica durante el entrenamiento, haciéndolo mucho más rápido y eficiente. El problema que presenta este optimizador es la dependencia entre la actualización de los pesos y la regularización L2 que se les aplica para evitar que tomen valores excesivamente grandes. Como alternativa se propone el uso del optimizador AdamW (44), que separa los dos procesos, rompiendo la dependencia. Este desacoplamiento permite obtener una mayor estabilidad en el entrenamiento y mejora la capacidad de generalización del modelo.
51 51 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama 5.3.4.2 Programación de la tasa de aprendizaje Agregar un programador para la tasa de aprendizaje al entrenamiento es una buena forma de converger más rápidamente a una solución óptima y de evitar mínimos locales de la función de coste. La regulación de la tasa de aprendizaje también ayuda a reducir las posibilidades de sobreajuste si se aplica correctamente. Para los entrenamientos, la política del programador elegida es la de variación en un único ciclo (One Cycle policy) (45). Esta divide las épocas de entrenamiento en iteraciones en función del número de pasos de entrenamiento (división del total de muestras del conjunto de entrenamiento entre el tamaño de los lotes multiplicada por el número de épocas). Consta de una primera fase de calentamiento donde la tasa de aprendizaje va aumentando desde un valor mínimo hasta un valor máximo de forma lineal durante algo menos de la mitad de las iteraciones. Una vez alcanzado el máximo, la segunda etapa, denominada fase de enfriamiento, reduce linealmente el valor de la tasa de aprendizaje hasta llegar al valor mínimo del principio en el mismo número de iteraciones que la fase anterior. Finalmente, en las iteraciones finales se reduce aún más la tasa de aprendizaje hasta llegar a un valor final preestablecido. En la Figura 5-20 se puede observar el funcionamiento de la política de un ciclo. En el Código 6 se encuentra definido el callback para su implementación, ya que Keras no incluye esta política en sus librerías. En la definición de dicho callback puede observarse que solo es necesario pasar al programador la tasa de aprendizaje máxima, la cual, en caso de no introducirse el resto de los parámetros de la llamada, se utilizará como base para dar valor a los parámetros necesarios para ejecutar la política. Figura 5-20. Funcionamiento de la política de un ciclo para la tasa de aprendizaje El aumento lineal de la tasa de aprendizaje en las primeras fases del entrenamiento permite acercarse más rápidamente al punto de convergencia, por lo que aumenta la velocidad del aprendizaje. Además, cuando la tasa de aprendizaje se encuentra cerca del máximo esta funciona como método de regularización para evitar el sobreajuste. El posterior descenso progresivo de la tasa de aprendizaje permite a la modelo llegar de forma estable y precisa al mínimo encontrado para la función de coste.
Desarrollo de la red neuronal 52 52 5.3.4.3 Actualización de parámetros en los bloques de salida Con el fin de encontrar la mejor configuración posible para el modelo preentrenado, se han realizado diferentes pruebas descongelando distintos bloques de capas de la arquitectura EfficientNetB4. Se ha probado a entrenar solo las top layers del modelo y, también, estas junto con varios bloques de extracción de características previos: el séptimo, el sexto y el quinto; específicamente (Modelo 4). Este proceso busca reentrenar las últimas capas de la red para adaptar la extracción de características al problema de clasificación de cáncer de mama. En el siguiente apartado se analizarán las diferentes pruebas realizadas sobre el modelo preentrenado. 5.3.5 Búsqueda del modelo óptimo Para obtener el mejor F1 Score probabilístico posible (la métrica de puntuación de Kaggle), se ha ido iterando entre distintas versiones de entrenamiento. En esta sección se incluirán tantos apartados como diferentes versiones del Código 6 se han ido testando, con el objetivo de documentar en su totalidad el proceso de fine tuning. El código mencionado también incluye callbacks para guardar los pesos asociados a los mejores valores registrados por otras métricas de entrenamiento (sensibilidad probabilística, F1 Score probabilístico…) 5.3.5.1 Primera versión En la primera versión se incluyen la política de programación de la tasa de aprendizaje y el optimizador AdamW. Solo se entrenaron las tres últimas capas de la EfficientNetB4, es decir, sus top layers. Las capas colocadas a la salida del modelo mantuvieron la estructura de las pruebas de entrenamiento parcial previas: una capa de GAP, una capa densa de 1024 neuronas con dropout del 50%, y una neurona completamente conectada como salida con función de activación sigmoidal (Modelo 9). Los valores de los hiperparámetros de la primera versión vienen recogidos en la siguiente tabla: Tabla 5-1. Hiperparámetros de la primera versión Hiperparámetro Valor Función EPOCHS 10 Duración total del entrenamiento BATCH_SIZE 32 Número de muestras por iteración MAX_LR 0.001 Tasa de aprendizaje máxima para la política de un ciclo weight_decay 0.001 Intensidad de la regularización del optimizador AdamW Se ha reducido considerablemente el número total de épocas de entrenamiento para evitar que el aprendizaje se prolongue innecesariamente. Esto también ayuda a que el cambio en la tasa de aprendizaje entre iteraciones impuesto por el programador no sea excesivamente pequeño. El tamaño de los lotes de entrenamiento está limitado a 32 imágenes para evitar problemas con los recursos de Kaggle. Como bien es sabido, esto no solo influye al coste computacional, sino que también afecta a la velocidad de convergencia y a la probabilidad de sobreajuste. La tasa de aprendizaje máxima al ajustar modelos preentrenados se define del orden de diez veces más pequeña que la utilizada para un entrenamiento desde cero, que suele ser de 0.001. En esta primera versión se estaba evaluando por primera vez la programación de tasas de aprendizaje y el optimizador, así que esto no se tuvo en cuenta. Los resultados obtenidos no han sido favorables. A pesar de que la curva ROC denota una leve mejora en el
53 53 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama modelo, las métricas obtenidas en la evaluación no cumplen los mínimos requeridos (Figura 5-21). La matriz de confusión de la predicción verifica el mal rendimiento, como se puede observar en el bajo número de casos positivos detectados (Figura 5-22). Figura 5-21. Evolución del entrenamiento y resultados de la evaluación tras el primer intento de ajuste
Desarrollo de la red neuronal 54 54 Figura 5-22. Matriz de confusión obtenida en la evaluación tras el primer intento de ajuste 5.3.5.2 Segunda versión En esta versión se ha probado a utilizar nuevamente el conjunto de datos de 512x512 con el objetivo de reducir el tiempo de entrenamiento lo máximo posible. Los nuevos hiperparámetros están definidos en la siguiente tabla: Tabla 5-2. Cambios en los hiperparámetros con respecto a la primera versión Hiperparámetro Valor Función BATCH_SIZE 16 Número de muestras por iteración weight_decay 0.00001 Intensidad de la regularización del optimizador AdamW Se ha reducido el tamaño de los lotes para estudiar su efecto en la capacidad de generalización del modelo. También se ha minimizado el efecto de la regularización del optimizador AdamW. Los resultados de entrenamiento obtenidos no han sido satisfactorios, puesto que se han visto afectados negativamente por las medidas tomadas:
55 55 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Figura 5-23. Evolución del entrenamiento tras el segundo intento de ajuste Debido al empeoramiento de las métricas de entrenamiento, esta versión fue descartada sin que su rendimiento se evaluara con el conjunto de prueba. 5.3.5.3 Tercera versión Tras los malos resultados de la versión anterior, en esta tercera versión se recuperó el conjunto de datos de 512x1024, que era el que mejor había funcionado hasta el momento. También. se ha descongelado el último bloque de extracción de características (el séptimo) junto con las top layers (Modelo 10). Es necesario señalar que, de aquí en adelante, todas las versiones seguirán el consejo de Keras (46) de congelar los parámetros de las capas de normalización de lotes de los bloques de la arquitectura EfficientNetB4 que se vayan a entrenar, para así evitar corromper el aprendizaje previo del modelo. Los hiperparámetros de esta versión se recogen en la tabla a continuación: Tabla 5-3. Cambios en los hiperparámetros con respecto a la segunda versión Hiperparámetro Valor Función MAX_LR 0.01 Tasa de aprendizaje máxima para la política de un ciclo weight_decay 0.001 Intensidad de la regularización del optimizador AdamW drop_connect_rate 0.3 Tasa de dropout interna de la arquitectura EfficientNetB4 Se ha cambiado la tasa de aprendizaje máxima de la política de un ciclo para ver su efecto en el entrenamiento, y se ha mantenido el tamaño de lote de 16 imágenes para comprobar si los problemas de aprendizaje de la segunda versión estaban relacionados con este hiperparámetro. También se ha recuperado el weight_decay de la primera versión, puesto que su efecto estaba siendo mínimo en la versión anterior. Con la inclusión de bloques completos de la arquitectura EfficientNetB4, se ha añadido el parámetro “drop_connect_rate”, que define la tasa de dropout de las capas internas de la red cuando estas están habilitadas para el entrenamiento.
Desarrollo de la red neuronal 56 56 Figura 5-24. Evolución del entrenamiento tras el tercer intento de ajuste Como se observa en la Figura 5-24, los cambios de esta versión han hecho que el entrenamiento no cumpla con los requisitos mínimos esperados, por lo que, en este caso, tampoco se ha evaluado el modelo obtenido. 5.3.5.4 Cuarta versión En esta nueva versión se entrenarán las mismas capas que en la versión anterior, pero la capa densa a la salida del modelo preentrenado será sustituida por una capa de normalización de lote con una tasa de dropout del 50%, siguiendo como referencia los ejemplos de fine tuning (46) propuestos por Keras (Modelo 11). Se han reestablecido todos los valores de los hiperparámetros de la primera versión (Tabla 5-1) con el fin de evaluar exclusivamente el efecto de sustituir la capa densa. Si comparamos los resultados de la primera versión (Figura 5-21 y Figura 5-22) con los obtenidos (Figura 5-25 y Figura 5-26), podemos observar una mejora importante en el filtrado de los falsos positivos, que no ha aportado una mejora notoria a las métricas de evaluación debido a la pérdida de algunos verdaderos positivos.
57 57 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Figura 5-25. Evolución del entrenamiento y resultados de la evaluación tras el cuarto intento de ajuste Figura 5-26. Matriz de confusión obtenida en la evaluación tras el cuarto intento de ajuste Lo que puede destacarse es la evolución del F1 Score probabilístico en el conjunto de validación, que muestra una mejora sustancial en esta versión. Teniendo esto en cuenta y sabiendo que en versiones futuras se van a
Desarrollo de la red neuronal 58 58 descongelar más bloques y, por tanto, va a aumentar el número de parámetros de entrenamiento totales; se ha decidido seguir trabajando con la capa de normalización de lote. 5.3.5.5 Quinta versión En la quinta versión del código de entrenamiento se incluyó el sexto bloque de extracción de características del modelo EfficientNetB4 al conjunto bloques ya descongelados en versiones anteriores (Modelo 12), y se aumentó a quince el número de épocas para ver su efecto en el entrenamiento. Con esta configuración, las métricas en tiempo real del entrenamiento reflejaban que la red no estaba aprendiendo correctamente, por lo que se optó por recuperar las diez épocas de la versión anterior y por reducir la tasa de aprendizaje máxima en base a lo mencionado en la primera versión: Tabla 5-4. Cambios en los hiperparámetros con respecto a la cuarta versión Hiperparámetro Valor Función MAX_LR 0.0001 Tasa de aprendizaje máxima para la política de un ciclo Los resultados obtenidos han mejorado con respecto a la versión anterior. Aunque el área bajo la curva ROC es prácticamente igual, las detección de muestras positivas ha aumentado considerablemente, como bien muestra la sensibilidad probabilística. Teniendo en cuenta que la caída de la precisión ha sido mínima, el F1 Score probabilístico también ha aumentado (Figura 5-27). Figura 5-27. Evolución del entrenamiento y resultados de la evaluación tras el quinto intento de ajuste
59 59 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama La distribución de las predicciones en la matriz de confusión (Figura 5-28) confirma lo que se ha estado comentando. Figura 5-28. Matriz de confusión obtenida en la evaluación tras el quinto intento de ajuste 5.3.5.6 Versión final El código de entrenamiento y ajuste utilizado para obtener esta versión viene referido en el anexo (Código 6). En este caso, se han incluido al entrenamiento las capas que conforman el quinto bloque de extracción de características del modelo EfficientNetB4 (Modelo 13). Los valores de los hiperparámetros no han cambiado con respecto a los de la versión anterior. Como se puede observar en la Figura 5-29, el valor máximo del F1 Score probabilístico del conjunto de validación es el mejor obtenido hasta el momento: Figura 5-29. Evolución del entrenamiento tras el último intento de ajuste
Evaluación del modelo 66 66 En la Figura 6-8 y la Figura 6-9 se puede observar cómo, a pesar de que no se extrae información demasiado compleja de la imagen, estas capas destacan el tumor en los mapas de características que generan en base a su color y su textura, lo que será de mucha utilidad en bloques posteriores. Figura 6-8. Mapas de características generados por la capa block1a_project_conv Figura 6-9. Mapas de características generados por la capa block2a_expand_conv 6.3.2 Bloques intermedios En estos bloques, los mapas de características empiezan a abstraerse, y las características de la imagen original se vuelven algo más complicadas de identificar. A pesar de esto, en algunos mapas se sigue conservando la
67 67 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama información acerca de la situación del tumor, por lo que se puede afirmar que la red está identificando correctamente cuál es la anomalía relevante (Figura 6-10). Figura 6-10. Mapas de características generados por la capa block5a_expand_conv Como se puede ver en la (Figura 6-11), la información que se pasan las capas entre sí no siempre se conserva de forma clara ante el ojo humano, aunque en el próximo apartado se verá cómo la red sí que es capaz de interpretarla a su manera. Figura 6-11. Mapas de características generados por la capa block5c_expand_conv
Evaluación del modelo 68 68 6.3.3 Bloques finales Conforme la imagen llega a las capas finales de la red, la abstracción se va haciendo mayor. Como se comentaba en el apartado anterior, en la Figura 6-12 se puede ver que algunos mapas que anteriormente no mostraban características relevantes a simple vista han sido procesados por las capas de la red de tal forma que los nuevos mapas generados presentan la información importante de forma algo más visual. Figura 6-12. Mapas de características generados por la capa block7a_expand_conv Por último, en la Figura 6-13, se representan los mapas generados por la capa “top_conv”, que son mínimamente procesados y salen del modelo EfficientNetB4 hacia la capa de GAP. En estos mapas la información contenida es mínima, pero es destacable como en los seis casos representados se puede ver que en la zona aproximada donde se situaba el tumor hay un patrón en forma de cruz que muy probablemente sea clave a la hora de clasificar la muestra como positiva.
69 69 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Figura 6-13. Mapas de características generados por la capa top_conv
Evaluación del modelo 70 70
71 7 CONCLUSIONES n este capítulo se presentan las conclusiones derivadas de los resultados obtenidos a lo largo de todo el proyecto. También se incluirán algunas posibles líneas de ampliación futuras con las que estudiar alternativas para mejorar el modelo. 7.1. Importancia del conjunto de datos La selección de las dimensiones adecuadas de las mamografías, así como el preprocesamiento y aumento artificial de imágenes juegan un papel fundamental en el rendimiento final del modelo. Queda demostrado que a mayor sea el tamaño de las imágenes, así como la cantidad de información relevante que se presenta en ellas, mejor capacidad de aprendizaje y mayor generalización tendrá el modelo y, por tanto, mejores predicciones se obtendrán. También se ha comprobado que para evaluar de forma correcta el rendimiento real de la red de clasificación es necesario mantener el desbalance de clases del conjunto de datos original en los subconjuntos de validación y de evaluación. Cualquier intento de balancear las clases supondría un aumento artificial de las métricas de evaluación que no debería utilizarse para evaluar el rendimiento del modelo. 7.2. Ventajas del aprendizaje por transferencia Las arquitecturas propuestas al principio del proyecto no han presentado resultados favorables debido a que su escasa complejidad les impedía extraer las características necesarias para el problema de clasificación. El uso del modelo preentrenado EfficientNetB4 ha sido la propuesta que ha definido el éxito de este proyecto. Los bloques de esta red, junto a los pesos previamente entrenados para la clasificación de imágenes de ImageNet, han permitido captar con mayor eficacia las características que favorecen la detección del cáncer de mama. Además, gracias a la implementación del aprendizaje por transferencia se ha reducido considerablemente el tiempo de aprendizaje, favoreciendo la realización de un mayor número de iteraciones de entrenamiento en las que buscar los hiperparámetros de entrenamiento óptimos. 7.3. Influencia del umbral de predicción El umbral de decisión es un elemento crucial en las aplicaciones médicas, donde es necesario encontrar un equilibrio adecuado entre falsos positivos y falsos negativos. Como ya hemos mencionado en varias ocasiones, la métrica que define dicho equilibrio, el F1 Score probabilístico, es la misma que se utiliza para puntuar el rendimiento del modelo en el concurso de Kaggle. Utilizar hard thresholding para buscar el umbral de predicción óptimo que maximice el F1 Score probabilístico de la red ha supuesto un aumento notable en la puntuación final del modelo. Con un umbral de 0.9, se ha alcanzado una puntuación de, aproximadamente, 0.22; un valor muy cercano a la puntuación media del resto de participantes del concurso, ~0.23. Es necesario aclarar que el umbral obtenido por hard thresholding sirve principalmente para mejorar la puntuación del modelo en el concurso, y no tiene por qué utilizarse en una futura aplicación real. Dependiendo del contexto clínico, el umbral de decisión se ajustará para priorizar la detección de verdaderos positivos o el descarte de falsos positivos, considerándose en todo momento el impacto que esto podría tener en los pacientes. E
Conclusiones 72 72 7.4. Necesidad de validación externa Los resultados obtenidos son prometedores y muestran el potencial de las redes neuronales convolucionales como herramientas de apoyo médico en la detección del cáncer de mama. A pesar de ello, antes de utilizar el modelo en un contexto clínico real, sería necesario obtener validación externa en distintos entornos médicos. Involucrar la opinión de profesionales de la salud en la evaluación del modelo ayudará a garantizar su funcionamiento en un nivel más pragmático. En esta línea, también sería interesante recurrir a otros conjuntos de datos para verificar la capacidad de generalización del modelo. 7.5. Líneas de ampliación futuras Existen múltiples oportunidades de optimización y ampliación del modelo utilizado. En este apartado se describirán diferentes propuestas de investigación para intentar abordar los problemas encontrados durante el desarrollo del proyecto y perfeccionar los modelos propuestos para la detección de cáncer de mama. 7.5.1 Técnicas de optimización y búsqueda de hiperparámetros Los hiperparámetros utilizados en el entrenamiento del modelo final de la red han sido elegidos tras varios ensayos sobre el modelo final. Se propone realizar una exploración más exhaustiva sobre la configuración de la red elegida con técnicas más avanzadas como la optimización bayesiana, que construye un modelo probabilístico en base la función de coste y el espacio de hiperparámetros para proponer puntos de evaluación; o los algoritmos evolutivos, que utilizan mecanismos genéticos para optimizar los hiperparámetros y para proponer cambios en la estructura de red. 7.5.2 Generación sintética de mamografías El aumento de datos tradicional es una parte clave a la hora de balancear el conjunto de entrenamiento para que la red pueda extraer información por igual de la clase positiva y la negativa, pero tiene el problema de que las imágenes generadas de la clase desfavorecida presentan artefactos creados de forma artificial de los que la red puede aprender. Dicho aprendizaje afectaría directamente al rendimiento del modelo, pues estas características no se encuentran en los conjuntos de validación y de prueba. Como alternativa explorable se propone utilizar redes generativas antagónicas (GANs) para el aumento de datos con el objeto de comparar los resultados que se obtengan con los de este trabajo. Las redes generativas antagónicas son un par de redes neuronales utilizadas para generar información de forma sintética. La primera red genera imágenes artificiales en base a muestras reales, y la segunda trata de identificarlas y discriminarlas. En un principio, las nuevas imágenes generadas serían prácticamente indiferenciables de las reales en el proceso de aprendizaje de la red, consiguiéndose un mejor rendimiento en el entrenamiento, que se vería menos afectado por la introducción de artefactos en las mamografías; y, por ende, una mejor puntuación del modelo. 7.5.3 Implementación de modelos especializados En este trabajo se ha utilizado el modelo EfficientNetB4, cuyos pesos preentrenados se han obtenido mediante la base de datos de ImageNet. El uso de esta base de datos está bastante extendido a la hora de entrenar redes neuronales para la clasificación multiclase de imágenes genéricas, aunque la carencia de información médica hace que su rendimiento sin ajuste en el problema estudiado sea bastante pobre. Sería interesante comprobar el rendimiento del modelo utilizando unos pesos adaptados a la detección de cáncer de mama en mamografías o, en ausencia de ellos, unos pesos con una función algo más genérica como la detección de anomalías en tomografías. Por otro lado, también se podría recurrir a ajustar modelos especializados en la evaluación de imágenes médicas como CheXNet, utilizado en la detección de enfermedades pulmonares mediante radiografías del
73 73 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama torso. Al estar este tipo de redes preentrenadas con imágenes clínicas podrían mostrar un mejor rendimiento en la evaluación de las mamografías.
Conclusiones 74 74
75 REFERENCIAS 1. Fernández, R. Diseño de una red neuronal para la automatización del proceso de taladrado mediante la estimación de diámetros de avellanado. 2023. 2. DataScientist. Kaggle: todo lo que hay que saber sobre esta plataforma. DataScientist. [En línea] [Citado el: 30 de Junio de 2024.] https://datascientest.com/es/kaggle-todo-lo-que-hay-que-saber-sobre-esta- plataforma#:~:text=¿Qué%20es%20Kaggle%3F,al%20máximo%20en%20data%20science.. 3. Kaggle. Kaggle: Your Home for Data Science. Kaggle. [En línea] [Citado el: 30 de June de 2024.] kaggle.com. 4. RSNA. Radiological Society of North America | RSNA. RSNA. [En línea] [Citado el: 30 de June de 2024.] https://www.rsna.org. 5. World Health Organization. Breast cancer. World Health Organization. [En línea] 13 de March de 2024. [Citado el: 30 de June de 2024.] https://www.who.int/news-room/fact-sheets/detail/breast-cancer. 6. DenseBreast-info. ¿Qué es la densidad mamaria y por qué es importante? DenseBreast-info. [En línea] 28 de Enero de 2021. [Citado el: 30 de Junio de 2024.] https://densebreast-info.org/spanish_faq_category/que-es- la-densidad-mamaria-y-por-que-es-importante/. 7. IBM. Transforming healthcare for better outcomes. IBM Healthcare. [En línea] [Citado el: 30 de Junio de 2024.] https://www.ibm.com/industries/healthcare. 8. Alali, Y., Harrou, F. y Sun, Y. A proficient approach to forecast COVID-19 spread via optimized dynamic machine learning models. 2022. 9. AtomWise. AtomWise - Better medicines faster. AtomWise. [En línea] [Citado el: 30 de Junio de 2024.] https://www.atomwise.com. 10. Esteva, A., y otros. Dermatologist-level classification of skin cancer with deep neural networks. 2017. 11. McKinney, S. M., y otros. International evaluation of an AI system for breast cancer screening. 2020. 12. Wakefild, K. A guide to the types of machine learning algorithms and their applications. SAS UK. [En línea] [Citado el: 1 de Julio de 2024.] https://www.sas.com/en_gb/insights/articles/analytics/machine-learning- algorithms.html#:~:text=There%20are%20four%20types%20of,- supervised%2C%20unsupervised%20and%20reinforcement. 13. Jain, A. Types of Machine Learning. GeeksforGeeks. [En línea] 29 de Noviembre de 2023. [Citado el: 1 de Julio de 2024.] https://www.geeksforgeeks.org/types-of-machine-learning/. 14. Shukla, L. Designing Your Neural Networks. Towards Data Science. [En línea] 23 de Septiembre de 2019. [Citado el: 1 de Julio de 2024.] https://towardsdatascience.com/designing-your-neural-networks- a5e4617027ed. 15. Baheti, P. Activation Functions in Neural Networks [12 Types & Use Cases]. V7 Labs. [En línea] 27 de Mayo de 2023. [Citado el: 2 de Julio de 2024.] https://www.v7labs.com/blog/neural-networks-activation- functions. 16. Bhande, A. What is underfitting and overfitting in machine learning and how to deal with it. Medimum. [En línea] 11 de Marzo de 2018. [Citado el: 2 de Julio de 2024.] https://medium.com/greyatom/what-is- underfitting-and-overfitting-in-machine-learning-and-how-to-deal-with-it-6803a989c76. 17. Wikipedia. Precision and recall. Wikipedia. [En línea] [Citado el: 3 de Julio de 2024.] https://en.wikipedia.org/wiki/Precision_and_recall. 18. Glen, S. ROC Curve Explained in One Picture. Data science Central. [En línea] 3 de Septiembre de 2019. [Citado el: 3 de Julio de 2024.] https://en.wikipedia.org/wiki/Precision_and_recall. 19. Dharwal, A. What is Precision and Recall Curves? Analytics India Magazine. [En línea] 28 de Junio de
Anexo: Códigos 82 82 Código 2. Distribución de subconjuntos # set_creator.py # This code redistributes patients id folders into training, validation and test sets import os import random import shutil DATASET = 'normal' # Change between 'roi' or 'normal' to choose between two posible datasets DISTRIBUTION = 0.1 # Percentage of images destinated to the validation and test folders # Function that divides the original dataset into training, validation and test datasets def split_images(folder, training_folder, validation_folder, test_folder): # Read all subfolders from original folder patients = [file for file in os.listdir(folder) if os.path.isdir(os.path.join(folder, file))] n_patients = len(patients) print(f'There are {n_patients} folders in {folder}') # Distribution of subfolders for validation and test sets n_validation_patients = int(n_patients * DISTRIBUTION) n_test_patitents = int(n_patients * DISTRIBUTION) # Random selection of subfolders from original folder validation_patients = random.sample(patients, n_validation_patients) remaining_patients = [patient for patient in patients if patient not in validation_patients] # Keeps folders that are not in the validation folder test_patients = random.sample(remaining_patients, n_test_patitents) remaining_patients = [patient for patient in remaining_patients if patient not in test_patients] # Keeps folders that are not in both the validation and test folders # Move subfolders to the test/validation/train folders moved_patients = 0 for patient in validation_patients: # Create origin and destination paths origin = os.path.join(folder, patient) destination = os.path.join(validation_folder, patient)
83 83 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama shutil.move(origin, destination) moved_patients += 1 print(f'Moved {moved_patients}/{len(validation_patients)} patients...', end ='\r') print(f'{len(validation_patients)} subfolders have been moved from {folder} to {validation_folder}') moved_patients = 0 for patient in test_patients: # Create origin and destination paths origin = os.path.join(folder, patient) destination = os.path.join(test_folder, patient) shutil.move(origin, destination) moved_patients += 1 print(f'Moved {moved_patients}/{len(test_patients)} patients...', end ='\r') print(f'{len(test_patients)} subfolders have been moved from {folder} to {test_folder}') moved_patients = 0 for patient in remaining_patients: # Create origin and destination paths origin = os.path.join(folder, patient) destination = os.path.join(training_folder, patient) shutil.move(origin, destination) moved_patients += 1 print(f'Moved {moved_patients}/{len(remaining_patients)} patients...', end ='\r') print(f'{len(remaining_patients)} subfolders have been moved from {folder} to {training_folder}') if __name__ == '__main__': dataset = DATASET training_folder = 'training_set' validation_folder = 'validation_set' test_folder = 'test_set'
Anexo: Códigos 84 84 # Check if folders exist for set in [training_folder, validation_folder, test_folder]: if not os.path.exists(set): os.makedirs(set) os.makedirs(set + '/0') os.makedirs(set + '/1') if dataset == 'normal': origin_folder = 'train_images_processed_512' elif dataset == 'roi': origin_folder = 'rescaled_dataset' split_images(origin_folder, training_folder, validation_folder, test_folder)
85 85 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Código 3. Separación de muestras positivas y negativas # set_organizer.py # This code divides the images of each set in positive and negative folders using the csv provided by Kaggle import pandas as pd import os import shutil # Function to get the path of all images in a folder def get_image_list(folder): image_paths = [] for root, _, files in os.walk(folder): for file in files: if file.endswith('.png'): image_paths.append(os.path.join(root, file)) return image_paths # Function to get the paths of the images with/without cancer using the csv def split_images(image_paths, csv_path, cancer): # Read full CSV df = pd.read_csv(csv_path) # Create own dataframe with the image_paths array df_images = pd.DataFrame({'path': image_paths}) # Remove root path and extension from the image path and save it df_images['filename'] = df_images['path'].apply(lambda x: int(os.path.splitext(os.path.basename(x))[0])) # Merge the dfs to match the images ids with their path df_merged = df_images.merge(df, left_on = 'filename', right_on = 'image_id') # Filter df by cancer presence df_filtered = df_merged[(df_merged['cancer'] == cancer)] # Return the paths of the filtered images as a list
Anexo: Códigos 86 86 return df_filtered['path'].tolist() # Function to delete the empty folders that are left behind after moving the images def delete_empty_folders(folder): for root, dirs, _ in os.walk(folder, topdown = False): for folder in dirs: folder_path = os.path.join(root, folder) if not os.listdir(folder_path): os.rmdir(folder_path) if __name__ == '__main__': sets = ['test_set', 'validation_set', 'training_set'] cancer_presence = [0, 1] csv_path = 'train.csv' for set in sets: image_paths = get_image_list(set) for cancer in cancer_presence: # Creates path to output folder depending on the presence of cancer output_folder = f'{set}/{str(cancer)}' filtered_images = split_images(image_paths, csv_path, cancer) count_imgs = 0 for path in filtered_images: # Moves image to the output folder shutil.move(path, output_folder) count_imgs += 1 print(f'Moved {count_imgs}/{len(filtered_images)} images...', end ='\r') print(f'{len(filtered_images)} files have been moved to {output_folder}')
87 87 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama print('') delete_empty_folders(set)
Anexo: Códigos 88 88 Código 4. Aumento de datos # data_augmentation.py # This code does data augmentation to a specific folder given the maximum number of samples it should have at the end import os, shutil, random import numpy as np from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img, save_img IMG_SIZE = 512 ASPECT_RATIO = 2 # 1 for 512x512 images | 2 for 512x1024 images AUGMENTATION_FOLDER = '1' # '0' or '1' TOTAL_IMAGES = 4096 # How many images should folder 0/1 have after augmentating # Random image cropping (custom augmentation function) def random_cropping(image, crop_size = 30, num_patches_range = (6, 8)): # Apply function only to 1 out of 10 images if np.random.random() < 0.1: # Get image shape h, w, _ = image.shape # Random number of patches to crop num_patches = np.random.randint(num_patches_range[0], num_patches_range[1] + 1) for _ in range(num_patches): # Randomly get patch upper left corner coordinates top = np.random.randint(0, h - crop_size + 1) left = np.random.randint(0, w - crop_size + 1) # Crop the image part affected by the patch image[top:top + crop_size, left:left + crop_size, :] = 0 return image # Definition of the augmentation generator data_generator = ImageDataGenerator( rescale = 1./255, rotation_range = 9, width_shift_range = 0.05,
89 89 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama height_shift_range = 0.05, shear_range = 6, zoom_range = [0.9, 1.1], horizontal_flip = True, vertical_flip = True, preprocessing_function = random_cropping, fill_mode = 'constant', cval = 0 ) # Data augmenation process def augmentation(folder, output_folder, target_size, target_images): # Read number of files and get total number of augmentations image_files = [f for f in os.listdir(folder) if f.endswith('.png')] n_augmentations = target_images - len(image_files) augmented_images = 0 if n_augmentations > 0: while augmented_images < n_augmentations: # Load random image as an array image_name = random.choice(image_files) image_path = os.path.join(folder, image_name) image = load_img(image_path, target_size = target_size) image_array = img_to_array(image) / 255.0 image_array = np.expand_dims(image_array, axis = 0) # Perform random augmentations batch = next(data_generator.flow(image_array, batch_size = 1)) aug_image = batch[0] # Save image aug_image_path = os.path.join(output_folder, f'aug{augmented_images}_{image_name}') save_img(aug_image_path, aug_image) augmented_images += 1 print(f'Processing image {augmented_images}/{n_augmentations}...', end ='\r') else: print(f'Error: There are more than {target_images} images in {folder} already')
Anexo: Códigos 90 90 if __name__ == '__main__': set_path = os.path.join('training_set', AUGMENTATION_FOLDER) augmentation_output = set_path augmentation(set_path, augmentation_output, target_size = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE), target_images = TOTAL_IMAGES) print(f'{set_path} augmented to {TOTAL_IMAGES} images')
91 91 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Código 5. Submuestreo # set_downsampler.py # This code downsamples the subsets given a maximum number of images by removing only negative samples import os import random # How many images do you want to have in your train, val and test sets; respectively TARGET_IMAGES = [8192, 4096, 4096] # Counts the number of images inside the specified folder def count_images(folder): n_images = len ([f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f))]) return n_images # Downsampling function def downsample_set(folder, target_samples): # Count images in the folders 0 and 1 separatedly counter_0 = 0 counter_1 = 0 for cancer in ['0', '1']: samples_path = os.path.join(folder, cancer) if os.path.isdir(samples_path): if cancer == '1': counter_1 = count_images(samples_path) elif cancer == '0': counter_0 = count_images(samples_path) total_images = counter_0 + counter_1 print(f'Total images in {folder}: {total_images} ({counter_1} images are labeled as 1)') print(f'Downsampling {folder} folder to {target_samples} images...')
Anexo: Códigos 98 98 train_generator = data_generator.flow_from_directory( training_path, target_size = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE), batch_size = BATCH_SIZE, shuffle = True, class_mode = 'binary', color_mode = 'rgb' # rgb or grayscale ) validation_generator = data_generator.flow_from_directory( validation_path, target_size = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE), batch_size = BATCH_SIZE, shuffle = False, class_mode = 'binary', color_mode = 'rgb' # rgb or grayscale ) # CNN architecture # EfficientNetB4 model without the top layers base_model = EfficientNetB4( include_top = False, input_shape = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE, 3), # Images must have 3 channels drop_connect_rate = 0.3, # Dropout % for fine tuning weights = 'imagenet' ) # Freeze all layers from the pretrained model base_model.trainable = False # Unfreezes layers from the last blocks of the model (for fine tuning) for layer in base_model.layers[-238:]: if not isinstance(layer, BatchNormalization): # Keras recommendation layer.trainable = True # Model definition model = Sequential() # To avoid model.build() model.add(Input(shape = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE, 3)))
99 99 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama # Pretrained model model.add(base_model) # Better than Flatten + Dense to avoid overfitting model.add(GlobalAveragePooling2D()) # Added trainable parameters model.add(BatchNormalization()) # Regularization to avoid overfitting model.add(Dropout(0.5)) # Output layer model.add(Dense(1, activation = 'sigmoid')) # Model compilation model.compile( optimizer = AdamW(weight_decay = 1e-3), loss = 'binary_crossentropy', metrics = ['accuracy', 'recall', pRecall(), 'precision', pPrecision(), F1Score(), pF1Score(), tf.keras.metrics.AUC(name = 'roc_auc', curve = 'ROC'), tf.keras.metrics.AUC(name = 'pr_auc', curve = 'PR'), get_lr_metric(AdamW(weight_decay = 1e-3))] ) # Summary of layers and parameters model.summary() # Callbacks # Checkpoints ch_val_p_recall = ModelCheckpoint( filepath = 'best_val_p_recall_model.weights.h5', monitor = 'val_p_recall', mode = 'max', save_best_only = True, save_weights_only = True ) ch_val_pf1_score = ModelCheckpoint( filepath = 'best_val_pf1_score_model.weights.h5', monitor = 'val_pf1_score', mode = 'max',
Anexo: Códigos 100 100 save_best_only = True, save_weights_only = True ) # Early Stopping (not needed in last tests) #early_stopping = EarlyStopping(monitor = 'val_pf1_score', mode = 'max', patience = PATIENCE, restore_best_weights = True) # LR Scheduler total_samples = len(train_generator.classes) iterations = (total_samples / BATCH_SIZE) * EPOCHS scheduler = OneCycleScheduler(iterations, MAX_LR) # Callbacks vector callbacks = [ch_val_p_recall, ch_val_pf1_score, scheduler] # Class weights balance (only for 512x512 dataset) unique_classes = np.unique(train_generator.classes) class_counts = np.bincount(train_generator.classes) class_weights = {} total_samples = total_samples # Calculated earlier for label in unique_classes: class_weight = total_samples / (len(unique_classes) * class_counts[label]) class_weights[label] = class_weight print("Class Weights:", class_weights) # Launch training history = model.fit( train_generator, validation_data = validation_generator, batch_size = BATCH_SIZE, epochs = EPOCHS, callbacks = callbacks, class_weight = class_weights ) print('Training completed') model.save_weights('last.weights.h5')
101 101 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama # Metrics from the training with open('model_history.pkl', 'wb') as f: pickle.dump(history.history['accuracy'], f) pickle.dump(history.history['loss'], f) pickle.dump(history.history['recall'], f) pickle.dump(history.history['p_recall'], f) pickle.dump(history.history['precision'], f) pickle.dump(history.history['p_precision'], f) pickle.dump(history.history['f1_score'], f) pickle.dump(history.history['pf1_score'], f) pickle.dump(history.history['roc_auc'], f) pickle.dump(history.history['pr_auc'], f) pickle.dump(history.history['lr'], f) with open('model_history_val.pkl', 'wb') as f: pickle.dump(history.history['val_accuracy'], f) pickle.dump(history.history['val_loss'], f) pickle.dump(history.history['val_recall'], f) pickle.dump(history.history['val_p_recall'], f) pickle.dump(history.history['val_precision'], f) pickle.dump(history.history['val_p_precision'], f) pickle.dump(history.history['val_f1_score'], f) pickle.dump(history.history['val_pf1_score'], f) pickle.dump(history.history['val_roc_auc'], f) pickle.dump(history.history['val_pr_auc'], f) pickle.dump(history.history['lr'], f) # Plot training evolution # Accuracy accuracy = history.history['accuracy'] val_accuracy = history.history['val_accuracy'] epochs_range = range(len(accuracy)) plt.figure(1) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, accuracy, label = 'Training Accuracy') plt.plot(epochs_range, val_accuracy, label = 'Validation Accuracy') plt.legend(loc = 'lower left') plt.title('Training and Validation Accuracy') plt.xlabel('Epoch') plt.ylabel('Accuracy Value') plt.show() # Loss loss = history.history['loss'] val_loss = history.history['val_loss'] plt.figure(2) plt.ylim([0.0, 1.0])
Anexo: Códigos 102 102 plt.plot(epochs_range, loss, label = 'Training Loss') plt.plot(epochs_range, val_loss, label = 'Validation Loss') plt.legend(loc = 'upper right') plt.title('Training and Validation Loss') plt.xlabel('Epoch') plt.ylabel('Loss Value') plt.show() # Recall recall = history.history['recall'] val_recall = history.history['val_recall'] plt.figure(3) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, recall, label = 'Training Recall') plt.plot(epochs_range, val_recall, label = 'Validation Recall') plt.legend(loc = 'upper left') plt.title('Training and Validation Recall') plt.xlabel('Epoch') plt.ylabel('Recall Value') plt.show() # Probabilistic Recall p_recall = history.history['p_recall'] val_p_recall = history.history['val_p_recall'] plt.figure(4) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, p_recall, label = 'Training Probabilistic Recall') plt.plot(epochs_range, val_p_recall, label = 'Validation Probabilistic Recall') plt.legend(loc = 'upper left') plt.title('Training and Validation Probabilistic Recall') plt.xlabel('Epoch') plt.ylabel('Probabilistic Recall Value') plt.show() # Precision precision = history.history['precision'] val_precision = history.history['val_precision'] plt.figure(5) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, precision, label = 'Training Precision') plt.plot(epochs_range, val_precision, label = 'Validation Precision') plt.legend(loc = 'upper left') plt.title('Training and Validation Precision') plt.xlabel('Epoch') plt.ylabel('Precision Value') plt.show() # Probabilistic Precision p_precision = history.history['p_precision']
103 103 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama val_p_precision = history.history['val_p_precision'] plt.figure(6) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, p_precision, label = 'Training Probabilistic Precision') plt.plot(epochs_range, val_p_precision, label = 'Validation Probabilistic Precision') plt.legend(loc = 'upper left') plt.title('Training and Validation Probabilistic Precision') plt.xlabel('Epoch') plt.ylabel('Probabilistic Precision Value') plt.show() # F1 Score f1_score = history.history['f1_score'] val_f1_score = history.history['val_f1_score'] plt.figure(7) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, f1_score, label = 'Training F1 Score') plt.plot(epochs_range, val_f1_score, label = 'Validation F1 Score') plt.legend(loc = 'upper left') plt.title('Training and Validation F1 Score') plt.xlabel('Epoch') plt.ylabel('F1 Score Value') plt.show() # Probabilistic F1 Score pf1_score = history.history['pf1_score'] val_pf1_score = history.history['val_pf1_score'] plt.figure(8) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, pf1_score, label = 'Training pF1 Score') plt.plot(epochs_range, val_pf1_score, label = 'Validation pF1 Score') plt.legend(loc = 'upper left') plt.title('Training and Validation pF1 Score') plt.xlabel('Epoch') plt.ylabel('pF1 Score Value') plt.show() # ROC AUC roc_auc = history.history['roc_auc'] val_roc_auc = history.history['val_roc_auc'] plt.figure(9) plt.ylim([0.0, 1.0]) plt.plot(epochs_range, roc_auc, label = 'Training ROC Curve') plt.plot(epochs_range, val_roc_auc, label = 'Validation ROC Curve') plt.legend(loc = 'upper left') plt.title('Training and Validation ROC Curve') plt.xlabel('Epoch') plt.ylabel('ROC Curve Value')
Anexo: Códigos 104 104 plt.show() # Learning Rate lr = history.history['lr'] plt.figure(11) plt.plot(epochs_range, lr, label = 'Learning Rate') plt.title('Learning Rate') plt.xlabel('Epoch') plt.ylabel('Learning Rate Value') plt.show()
105 105 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Código 7. Evaluación del rendimiento de la red # test_cnn.py import os, pickle import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization, Activation, Input, GlobalAveragePooling2D from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping from tensorflow.keras.optimizers import AdamW from tensorflow.keras import regularizers, metrics import numpy as np from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, roc_curve, precision_recall_curve, auc # Import the pretrained model # from tensorflow.keras.applications.inception_v3 import InceptionV3, preprocess_input from tensorflow.keras.applications.efficientnet import EfficientNetB4, preprocess_input IMG_SIZE = 512 ASPECT_RATIO = 2 # 1 for 512x512 | 2 for 512x1024 BATCH_SIZE = 32 PREDICTION_THRESHOLD = 0.9 # Change after hard tresholding (normal value: 0.5) MODEL_VERSION = 'v17.8.0' # Remember to match the sequential model to the version METRIC = 'val_pf1_score' # Metric from wich to load the weights file (Check if there are more weights files in output_train/MODEL_VERSION folder) ONLY_GRAPHS = True # Visualizes plots from results.pkl file without doing model.predict # Custom metrics class F1Score(tf.keras.metrics.Metric): def __init__(self, name = 'f1_score', **kwargs): super().__init__(name = name, **kwargs) self.precision = tf.keras.metrics.Precision() self.recall = tf.keras.metrics.Recall() def update_state(self, y_true, y_pred, sample_weight = None):
Anexo: Códigos 106 106 y_true = tf.expand_dims(y_true, axis = -1) # Ensure y_true has shape (batch_size, 1) self.precision.update_state(y_true, y_pred, sample_weight) self.recall.update_state(y_true, y_pred, sample_weight) def result(self): precision = self.precision.result() recall = self.recall.result() return 2 * (precision * recall) / (precision + recall + tf.keras.backend.epsilon()) class pPrecision(tf.keras.metrics.Metric): def __init__(self, name = 'p_precision', **kwargs): super().__init__(name = name, **kwargs) self.AP = self.add_weight(name = 'AP', initializer = 'zeros') self.pTP = self.add_weight(name = 'pTP', initializer = 'zeros') self.pFP = self.add_weight(name = 'pFP', initializer = 'zeros') def update_state(self, y_true, y_pred, sample_weight = None): # Transform in float and fix prediction range in [0, 1] y_true = tf.cast(y_true, tf.float32) y_pred = tf.clip_by_value(y_pred, 0, 1) # Mask of model positive predictions: If prediction is >= threshold, get prediction value; if not, get 0 filtered_y_pred = tf.where(y_pred >= PREDICTION_THRESHOLD, y_pred, tf.zeros_like(y_pred)) # Actual positives AP = tf.reduce_sum(y_true) # True positives probabilities pTP = tf.reduce_sum(filtered_y_pred[y_true == 1]) # False positives probabilities pFP = tf.reduce_sum(filtered_y_pred[y_true == 0]) self.AP.assign_add(AP) self.pTP.assign_add(pTP) self.pFP.assign_add(pFP) def result(self): # Calculate the metric value result = self.pTP / (self.pTP + self.pFP + tf.keras.backend.epsilon())
107 107 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama return result class pRecall(tf.keras.metrics.Metric): def __init__(self, name = 'p_recall', **kwargs): super().__init__(name = name, **kwargs) self.AP = self.add_weight(name = 'AP', initializer = 'zeros') self.pTP = self.add_weight(name = 'pTP', initializer = 'zeros') def update_state(self, y_true, y_pred, sample_weight = None): # Transform in float and fix prediction range in [0, 1] y_true = tf.cast(y_true, tf.float32) y_pred = tf.clip_by_value(y_pred, 0, 1) # Mask of model positive predictions: If prediction is >= threshold, get prediction value; if not, get 0 filtered_y_pred = tf.where(y_pred >= PREDICTION_THRESHOLD, y_pred, tf.zeros_like(y_pred)) # Actual positives AP = tf.reduce_sum(y_true) # True positives probabilities pTP = tf.reduce_sum(filtered_y_pred[y_true == 1]) self.AP.assign_add(AP) self.pTP.assign_add(pTP) def result(self): # Calculate the metric value result = self.pTP / (self.AP + tf.keras.backend.epsilon()) return result class pF1Score(tf.keras.metrics.Metric): def __init__(self, name = 'pf1_score', **kwargs): super().__init__(name = name, **kwargs) self.AP = self.add_weight(name = 'AP', initializer = 'zeros') self.pTP = self.add_weight(name = 'pTP', initializer = 'zeros') self.pFP = self.add_weight(name = 'pFP', initializer = 'zeros') def update_state(self, y_true, y_pred, sample_weight = None): # Transform in float and fix prediction range in [0, 1] y_true = tf.cast(y_true, tf.float32)
Anexo: Códigos 114 114 # Plot ROC curve fpr, tpr, _ = roc_curve(real_values, predictions) roc_auc = auc(fpr, tpr) plt.figure(4) plt.plot(fpr, tpr, color = 'blue', label = 'ROC curve (area = %0.2f)' % roc_auc) plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title('Receiver Operating Characteristic Curve') plt.legend(loc = "lower right") # Plot Precision-Recall curve precision_vals, recall_vals, _ = precision_recall_curve(real_values, predictions) pr_auc = auc(recall_vals, precision_vals) plt.figure(5) plt.plot(recall_vals, precision_vals, color = 'blue', label = 'PR curve (area = %0.2f)' % pr_auc) plt.xlabel('Recall') plt.ylabel('Precision') plt.title('Precision-Recall Curve') plt.legend(loc = "upper right") # Plot confusion matrix cm = confusion_matrix(real_values, binary_predictions) disp = ConfusionMatrixDisplay(confusion_matrix = cm, display_labels = ['No Cancer', 'Cancer']) disp.plot(cmap = plt.cm.Blues) plt.title('Confusion Matrix') plt.show() # F1 Score function proposed by Kaggle (gives same results as custom F1Score metric) def pfbeta(labels, predictions, beta): y_true_count = 0 ctp = 0 cfp = 0 for idx in range(len(labels)): prediction = min(max(predictions[idx], 0), 1) if (labels[idx]): y_true_count += 1
115 115 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama ctp += prediction else: cfp += prediction beta_squared = beta * beta c_precision = ctp / (ctp + cfp) c_recall = ctp / y_true_count if (c_precision > 0 and c_recall > 0): result = (1 + beta_squared) * (c_precision * c_recall) / (beta_squared * c_precision + c_recall) return result else: return 0 # pF1 score hard thresholding def pf1_hard_thresholding(real_values, predictions): # Calculate pF1 Score with multiple thresholds pf1_metric = pF1Score_HT() pf1_metric.update_state(real_values, predictions) best_threshold, best_pf1_score, thresholds, pf1_scores = pf1_metric.result() # Plot pF1 Score values for each threshold plt.scatter(best_threshold, best_pf1_score, color='red', label=f'Best Threshold: {best_threshold:.1f}\nBest pF1 Score: {best_pf1_score:.3f}') plt.plot(thresholds, pf1_scores) plt.xlabel('Threshold') plt.ylabel('pF1 Score') plt.title('Hard Thresholding') plt.legend() plt.show() return best_threshold, best_pf1_score if __name__ == '__main__': test_path = 'test_set' if ONLY_GRAPHS: # Open the pkl file created after testing and loads predictions and real values with open(f'output_test/{MODEL_VERSION}/test_results.pkl', 'rb') as f: data = pickle.load(f)
Anexo: Códigos 116 116 predictions = data.get('Prediction', []) real_values = data.get('Real', []) binary_predictions = [1 if prediction >= PREDICTION_THRESHOLD else 0 for prediction in predictions] else: real_values, predictions, binary_predictions = get_predictions(test_path) plot_predictions(real_values, predictions, binary_predictions) model_score = pfbeta(real_values, binary_predictions, beta = 1) print(f'Kaggle F1 score = {model_score:.3f} (model {MODEL_VERSION}, TH = {PREDICTION_THRESHOLD})') best_th, best_pf1 = pf1_hard_thresholding(real_values, predictions) print(f'Best threshold found = {best_th:.1f} (pF1 Score = {best_pf1:.3f})')
117 117 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama Código 8. Visualización de los mapas de características # feature_maps_visualizer.py # This code shows the feature maps extracted by a chosen Conv2D layer from the EfficientNetB4 model import os import matplotlib.pyplot as plt import matplotlib as mpl import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential, Model from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization, Activation, Input, GlobalAveragePooling2D from tensorflow.keras.utils import load_img, img_to_array import numpy as np from IPython.display import Image, display from tensorflow.keras.applications.efficientnet import EfficientNetB4, preprocess_input, decode_predictions IMG_SIZE = 512 ASPECT_RATIO = 2 # 1 for 512x512 | 2 for 512x1024 MODEL_VERSION = 'v17.8.0' # Remember to match the sequential model to the version METRIC = 'val_pf1_score' # Metric from wich to load the weights file (Check if there are more weights files in output_train/MODEL_VERSION folder) LAST_LAYER = 'block5c_expand_conv' # Layer name extracted from model.summary() IMAGE_ID = '953022973' # Extracted from the positive folder from the test set # Gets CNN architecture def get_model(last_conv): # EfficientNetB4 model without the top layers base_model = EfficientNetB4( include_top = False, input_shape = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE, 3), # Images must have 3 channels weights = None # Very important ) # We don't freeze layers if we are loading weights later # Model definition model = Sequential()
Anexo: Códigos 118 118 # To avoid model.build() model.add(Input(shape = (ASPECT_RATIO*IMG_SIZE, IMG_SIZE, 3))) # Pretrained model model.add(base_model) # Load previously trained weights model.load_weights(f'output_train/{MODEL_VERSION}/best_{METRIC}_model.weight s.h5', skip_mismatch = True) chopped_model = Model( inputs = base_model.inputs, outputs = base_model.get_layer(last_conv).output ) return chopped_model # Image to array conversion def get_image_array(image_path, image_size): image = load_img(image_path, target_size = (ASPECT_RATIO*image_size, image_size)) array = img_to_array(image) # We need to "transform" the array into a batch array = np.expand_dims(array, axis = 0) return array if __name__ == '__main__': image_array = preprocess_input(get_image_array(f'test_set/1/{IMAGE_ID}.png', IMG_SIZE)) feature_map_model = get_model(LAST_LAYER) # Extract feature maps from the selected image feature_maps = feature_map_model.predict(image_array) # Plot feature maps plt.figure(1) plt.title(f'Feature maps from {LAST_LAYER}') plt.text(1, 0.5, 'Weights', va = 'center', rotation = 270) plt.axis('off')
119 119 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama for i in range(0, 6): fm = feature_maps[0, :, :, i] #print(fm.max()) #print(fm.min()) plt.subplot(2, 3, i + 1) plt.imshow(fm, cmap = 'viridis') plt.yticks([], []) plt.xticks([], []) plt.colorbar() plt.show()
Anexo: Códigos 120 120
121 ANEXO: MODELOS DE RED Modelo 1. Primera arquitectura de red propuesta Model: "sequential" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 510, 510, 16) │ 448 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 255, 255, 16) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout (Dropout) │ (None, 255, 255, 16) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 253, 253, 32) │ 4,640 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 126, 126, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_1 (Dropout) │ (None, 126, 126, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_2 (Conv2D) │ (None, 124, 124, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 62, 62, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_2 (Dropout) │ (None, 62, 62, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 246016) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 128) │ 31,490,176 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_3 (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 1) │ 129 │ └─────────────────────────────────┴────────────────────────┴───────────────┘ Total params: 31,513,889 (120.22 MB) Trainable params: 31,513,889 (120.22 MB) Non-trainable params: 0 (0.00 B)
Anexo: Modelos de red 122 122 Modelo 2. Arquitectura propuesta por Keras para la clasificación binaria de neumonía Model: "functional_1" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None, 512, 512, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ rescaling (Rescaling) │ (None, 512, 512, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d (Conv2D) │ (None, 512, 512, 16) │ 448 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 512, 512, 16) │ 2,320 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 256, 256, 16) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d │ (None, 256, 256, 32) │ 688 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_1 │ (None, 256, 256, 32) │ 1,344 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization │ (None, 256, 256, 32) │ 128 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 128, 128, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_2 │ (None, 128, 128, 64) │ 2,400 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_3 │ (None, 128, 128, 64) │ 4,736 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_1 │ (None, 128, 128, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 64, 64, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_4 │ (None, 64, 64, 128) │ 8,896 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_5 │ (None, 64, 64, 128) │ 17,664 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_2 │ (None, 64, 64, 128) │ 512 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 32, 32, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout (Dropout) │ (None, 32, 32, 128) │ 0 │
123 123 Diseño y desarrollo de una red neuronal convolucional para la detección de cáncer de mama ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_6 │ (None, 32, 32, 256) │ 34,176 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ separable_conv2d_7 │ (None, 32, 32, 256) │ 68,096 │ │ (SeparableConv2D) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_3 │ (None, 32, 32, 256) │ 1,024 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_4 (MaxPooling2D) │ (None, 16, 16, 256) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_1 (Dropout) │ (None, 16, 16, 256) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 65536) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 512) │ 33,554,944 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_4 │ (None, 512) │ 2,048 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_2 (Dropout) │ (None, 512) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 128) │ 65,664 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_5 │ (None, 128) │ 512 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_3 (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_2 (Dense) │ (None, 64) │ 8,256 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ batch_normalization_6 │ (None, 64) │ 256 │ │ (BatchNormalization) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_4 (Dropout) │ (None, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_3 (Dense) │ (None, 1) │ 65 │ └─────────────────────────────────┴────────────────────────┴───────────────┘ Total params: 33,774,433 (128.84 MB) Trainable params: 33,772,065 (128.83 MB) Non-trainable params: 2,368 (9.25 KB)