Procesamiento de imágenes satelitales: el desafío de la selva del Amazonas

En 7Puentes somos amantes de los desafíos y si tienen que ver con aquello que nos gusta y sabemos hacer, mejor aún. Es por ello que nos anotamos en una competencia de Kaggle, la comunidad de cientistas de datos más grande del mundo, llamada “Planet: Understanding the Amazon from Space”.

El propósito de la competencia es, a través de imágenes satelitales, detectar la deforestación en el bosque tropical más extenso del mundo: la selva del Amazonas. Para ello, el reto consiste en desarrollar un algoritmo que pueda “taggear” (o clasificar) en 17 categorías posibles imágenes satelitales con diversas condiciones atmosféricas y clases de superficies.

Al evidenciarse la transformación de la superficie en las imágenes satelitales, se busca hacer un aporte para poder prevenir y tratar las áreas deforestadas a futuro.

La competencia provee de un dataset de imágenes satelitales del tipo GeoTiff. Cada una de estas imágenes (en la competencia se llaman chips) posee cuatro bandas de datos: roja, verde, azul e infrarroja. Este tipo de imágenes se suelen llamar multiespectrales y son tomadas por cámaras especiales que no solo toman el reflejo del espectro visible, sino también las bandas infrarrojas y ultravioletas. El tamaño de cada imagen es de 256px por 256px, donde cada pixel representa alrededor de 3,15 metros cuadrados.

Cada chip es clasificada en una o más categorías, indicando que puede observarse de una forma parecida a como se ve en la imagen de abajo:

Para hacer frente a este desafío elegimos utilizar redes convolutivas (CNN por sus siglas en inglés), un tipo de red neuronal especializada en visión por computadora (Computer Vision). Este tipo de redes, si bien están presentes en la literatura especializada hace ya mucho tiempo, son parte del auge actual del deep learning o aprendizaje profundo.

Pero quizás muchos no estén al tanto de este auge actual, así que empecemos por el principio: ¿Qué es una red neuronal?

Una red neuronal es un modelo que imita el funcionamiento de las redes neuronales de los organismos vivos y, al igual que estos sistemas, está formado por un conjunto de neuronas conectadas entre sí. Una neurona puede ser vista como una función que “mapea” un dato de entrada a una salida y una red neuronal, es un aglomerado de dichas funciones para poder hacer “mapeos” más complejos. Ninguna de estas neuronas tiene una tarea concreta: a la red se la provee de un input y de un output. Posteriormente, las neuronas van creando y reforzando ciertas conexiones para “aprender” y, en un futuro, recibir sólo el input y, por ejemplo, poder definir el output por sí solas.

¿Cómo se crean y refuerzan las conexiones entre neuronas? Existen diferentes algoritmos de aprendizaje para darles comportamiento (peso), lo que definirá cuáles de ellas son más o menos significativas y cuándo propagarán información a las neuronas a las cuales están conectadas.

Para poder construir nuestro algoritmo primeramente elegimos usar Python, como lenguaje, y TensorFlow, que es una librería de software de código abierto desarrollada y utilizada por el equipo de Cerebro de Google. Para simplificar aún más la tarea, elegimos utilizar una librería llamada Keras, que permite focalizarnos solamente en la forma de la red neuronal y en el aprendizaje de la misma, y no tanto en detalles de bajo nivel. Además, recurrimos a la investigación de redes neuronales profundas, que permite la manipulación de imágenes mediante cálculos matemáticos y aumenta considerablemente la velocidad de aprendizaje de una red neuronal.

Para llevar a cabo el desarrollo elegimos utilizar Jupyter Notebook. Esta aplicación web de código abierto nos permite crear y compartir documentos en tiempo real. Cuenta con soporte para alrededor de 40 diferentes lenguajes de programación incluyendo a Python, nuestro lenguaje elegido, por lo que el desarrollo y la prueba del código fueron hechas con esta herramienta.

El código es ejecutado en una instancia p2.xlarge de Amazon que se caracterizan por ser  instancias potentes y escalables diseñadas para la paralelización y el cálculo mediante el uso de GPUS, ideales, entre otras cosas, para machine learning.

Lo primero hicimos fue transformar nuestro set de training, 40.479 imágenes .tif y sus respectivos, en un input para la red neuronal:

https://gist.github.com/tatianamolinari/cba169311e1ee502aa5dff4f240280f9#file-cargatiff-py

Para ello cargamos las imágenes como una matriz de 256x256x4 (256×256 es su tamaño original y 4 son las bandas). A esa matriz la redujimos a una de dimensión 128x128x4 por problemas con la memoria ya que, luego de intentarlo con el tamaño original, descubrimos que las imágenes eran tan grandes que no llegaban a completar el training.

Para los labels elegimos mapear cada uno de ellos a un número i ∈ [0,16] ya que decidimos representarlos como un array de 17 ceros, donde la posición i sólo tendría valor uno si el label mapeado al número i pertenece a esa imagen. De esta manera, los labels de cada imagen son representados por una matriz de 1×17. Cada una de las representaciones de los labels son agregados al array y_train.

Entonces ya tenemos por un lado todas las imágenes en x_train y sus respectivos labels en y_train.

Con los datos cargados luego dividimos el total de imágenes en un conjunto de test y en otro de validación que serán utilizados por la red neuronal para entrenarse y, según los resultados que obtenga más los esperados, auto ajustar los parámetros con el fin de aprender a etiquetar correctamente las imágenes.

https://gist.github.com/tatianamolinari/460b07a8c3c2c99d2c7bb7785d16edf5

Elegimos que el set de training sea considerablemente más grande que el de validación para nutrir la red neuronal con más ejemplos de input, por eso tomamos para train 35.000 imágenes para que en validación sólo queden las restantes 5.497.

Luego armamos el modelo, es decir, el esqueleto de la red neuronal:

https://gist.github.com/tatianamolinari/117cd73e9c069f78347da14a864f9dd4

Elegimos una red neuronal secuencial, lo que supone que la información pasa entre las capas en el orden que les demos, una sóla vez.

Cada una de las operaciones “add” que le aplicamos al modelo es una nueva capa de neuronas que agregamos para formar la red, entre las cuales podemos destacar tres:

  • Las capas de neuronas convolucionales: aquellas que aplican una convolución a la entrada (en nuestro caso las capas creadas con Conv2D). Esta operación produce una transformación en los datos para que ciertas características que son determinadas por la forma del núcleo de la neurona se vean resaltadas en la imagen de salida mediante un valor numérico más alto en los pixeles que la representan. Esto se usa por ejemplo para la detección de formas y bordes.
  • Las capas de neuronas de reducción de muestreo, que se encargan de encontrar los valores máximos en una muestra y pasar esos valores como las características más importantes sobre el área de input (por ejemplo, las capas creadas con MaxPooling2D). De esta manera, el tamaño de los datos se va reduciendo a las características más representativas de las imágenes.
  • Las capas de prevención de overfitting (prevenir que el aprendizaje de la red sólo sirva para este grupo de imágenes por estar demasiado ligado con el input), que apaga o pone en cero un porcentaje dado de unidades del input al azar en cada vuelta de aprendizaje de la red neuronal (en nuestro caso la capa dropout que apaga el 0,15 % del input aleatoriamente)

Después del armado del esqueleto de la red neuronal, se compiló la misma y se eligió una función de pérdida, de optimización y de qué manera se sacarán las métricas para que la red pueda realizar el ajuste de parámetros.

https://gist.github.com/tatianamolinari/c81a418cd22a5f22e010c5a2207e6c38

Una vez hecho esto, sólo resta alimentar la red neuronal con el conjunto de test y el de validation anteriormente creados eligiendo cuantas épocas tendrá (que tantas veces se va a alimentar de todo el input para ajustar los parámetros):

https://gist.github.com/tatianamolinari/40ec1492a73a8e6b7da4f587031f2e90

Una vez hecho el train, ya podemos procesar el input de la competencia de Kaggle que no tiene label para generar el output de la competencia.

https://gist.github.com/tatianamolinari/46b42f521c4622b8a375cae6bde15c93

Para esto tenemos que repetir los pasos de transformación de las imágenes tiff para training: transformarlos en una matriz 256x256x4, luego reducirla a 128x128x128 y dividir por el máximo para que los valores sean entre cero y uno.

Ahora, simplemente le pedimos al modelo que prediga el output del nuevo set de imágenes del cual no tenemos sus correspondientes labels:

https://gist.github.com/tatianamolinari/0c60719dd4bf6e66ca2f5f08755ccedd

El output de la red neuronal son las representaciones de labels de cada uno de los archivos que recibimos como input, es decir, cada una de las representaciones es un arrayde 17 posiciones con un valor de cero a uno.

Cómo nosotros tenemos que guardar en un archivo .csv el nombre de la imagen junto a sus labels, elegimos un umbral por el cual si el valor de la posición i de la representación es mayor a él, entonces la imagen tendrá ese label.

https://gist.github.com/tatianamolinari/76f1f9a3c06282c6b12b9d6c647215fc

Luego de 8 intentos subidos a la competencia de Kaggle en los cuales variamos el tamaño de las imágenes, la cantidad de épocas y el umbral, actualmente estamos en la posición 302 de la competencia, habiendo subido 15 puestos desde nuestro primer intento.

https://www.kaggle.com/c/planet-understanding-the-amazon-from-space/leaderboard

En próximos posts podrán conocer nuestros avances y, también, una nueva y absolutamente diferente aplicación concreta de esta metodología de deep learning: se trata del recuento de individuos presentes en una manifestación masiva en la Ciudad de Buenos Aires.

Stay tunned!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *