febrero 2, 2019 6:00 pm

Jesús

?? Read in English

El desarrollo de redes neuronales eficientes (y a la vez poderosas) es tanto ciencia como arte. Por una parte, tenemos un sinfín de propiedades matemáticas complejas que hacen posible que estos algoritmos lleven a cabo tareas hasta hace muy poco imposibles para una máquina, como el reconocimiento de objetos en una imagen, tan sólo a partir de operaciones aritméticas sencillas, mientras que, por el otro lado, contamos con un catálogo cada día más grande de pequeños trucos, técnicas, heurísticas y tips que facilitan la labor de engendrar un agente inteligente.

Hace un par de semanas discutimos sobre cómo, a pesar de lo que la teoría establece, las redes excesivamente profundas tienden a olvidar lo aprendido, por lo que es necesario proveerla de “atajos” para contrarrestar esta situación.

También estamos conscientes de la tendencia de algunas redes de memorizar, en vez de aprender, por lo que solemos corregir esta conducta indeseada con técnicas como dropout y regularización.

Es como si nuestras redes neuronales fueran estudiantes con un enorme potencial, pero increíblemente distraidas y olvidadizas, por lo que es nuestro deber, como los buenos maestros que somos, encauzarlas por el camino adecuado para que logren su máximo desempeño.

Así, pues, el día de hoy hablaremos sobre una técnica muy útil que podemos aplicar para acelerar el aprendizaje de nuestros indisciplinados estudiantes: Normalización por lotes.

>>> Descarga el código de este post aquí <<<

¿Por qué la normalización funciona?

En machine learning, típicamente queremos relacionar o, más bien, correlacionar un conjunto de variables de entrada, con un un conjunto de variables de salida. De manera más sencilla, nuestra meta es descubrir una posible conexión entres estos conjuntos que nos permita hacer predicciones. Así, por ejemplo, si sabemos que una persona mide 1,65 metros y pesa 80 Kgs, podemos con bastante seguridad afirmar que sufre de sobrepreso, puesto que sabemos que existe una conexión entre dicha condición y la estatura y la cantidad de masa corporal de un individuo.

Ahora, si nos fijamos detenidamente en los valores que pueden tomar estas dos variables (peso y estatura), observamos que su rango varía muchísimo en magnitud. Por un lado, el peso de un individuo puede expresarse en decenas o cientos de kilos, mientras que su estatura (en metros), suele estar entre 1,3 y 2,5. El problema en este caso es que el peso siempre irá asociado a un número mucho más grande que la estatura, por lo que nuestro modelo puede erróneamente llegar a la conclusión de que el primero es mucho más importante que el segundo, cuando en realidad no es así, ya que la información que ambos factores proveen es crucial para determinar si alguien está pasado de peso.

Para contrarrestar esta situación, recurrimos a una técnica conocida como normalización, la cual consiste en llevar cada variable a un rango familiar, similar al del resto, de manera que la red o modelo le preste igual atención a cada una de ellas.

Normalización por Lotes

En el mundo de las redes neuronales, solemos normalizar los datos que le pasamos a las mismas. En particular, en el caso de las imágenes, llevamos el valor de cada píxel a un rango fácil de manejar (usualmente [0, 1]) para evitar que los números se salgan de control y caigamos en problemas de overflow que son capaces de evitar que nuestra red siga aprendiendo.

El problema con este método es que el efecto o beneficio del mismo se va perdiendo a medida que nos adentramos en la red. Es decir, cada capa subsiguiente tiene que lidiar con entradas más y más inestables, producto de las operaciones de la capa anterior, disipando el efecto inicial de la normalización.

¿Qué podemos hacer para resolver este problema?

¡Ah! ¡Muy buena pregunta! Lo primero que debemos hacer es cambiar de perspectiva.

Fíjate en la imagen anterior. Para todos los efectos, es una red neuronal común y corriente, ¿verdad?. Ahora, con tu mano, tapa la primera mitad de la red. ¿Qué ves? Una red más pequeña, ¿no? De hecho, podemos pensar que cada capa alimenta a una red más chiquita. De ser así, ¿por qué no normalizar los datos que entran en cada una de estas subredes?

Esto, en pocas palabras, es lo que hace la normalización por lotes: Cada nuevo lote de datos que entra a una capa de la red es normalizado. Como veremos a continuación, trae consigo una serie de beneficios importantes.

Beneficios de la Normalización por Lotes

  • Las redes entrenan más rápido: A pesar de que cada iteración del entrenamiento tomará más tiempo por las operaciones extra relacionadas con la normalización de cada lote de información, la red debería converger mucho más rápido, por lo que la totalidad del proceso será más veloz.
  • Permite el uso de learning rates más altos: Learning rate es el hiperparámetro que utilizamos para controlar la tasa a la que aprende una red neuronal. Típicamente, altos valores de este parámetro hacen que la red tome más tiempo en converger y, en algunos casos, nunca lo logra, debido al comportamiento errático del gradiente, por lo que es estándar utilizar valores pequeños (como 0.001). Sin embargo, el problema de esta alternativa es que la red se tarda mucho más en entrenar. Con normalización podemos ser un poco más atrevidos con este hiperparámetro.
  • Facilita la inicialización de los pesos: El estado inicial de una red viene dado por los pesos (parámetros) de cada capa. Como podrás imaginar, inicializar dichos pesos es un arte en sí mismo, por lo que es de agradecer que la normalización por lotes nos permita ser un poco más “descuidados” o relajados en este aspecto, ya que el proceso de normalizar cada grupo de data contrarrestará los posibles errores que hayamos cometido durante la inicialización.
  • Hace viable el uso de más funciones de activación: En el 99% de los casos verás que las capas de una red neuronal son activadas con ReLU o tanh. Esto se debe a que son las funciones que mejor se adaptan a la mayoría de las situaciones. Debido a que la normalización por lotes regula los valores que entran a cada función de activación, los problemas de estabilidad presentes en la mayoría ya no son una preocupación.
  • Simplifica la creación de redes más profundas: Básicamente, las cuatro razones anteriores facilitan la creación de redes más profundas y complejas, lo cual es excelente ya que es un hecho que éstas producen mejores resultados que redes más superficiales.
  • Proveen un poco de regularización: La normalización de cada lote añade un poco de ruido a los datos, lo que previene que la red se acomode demasiado a los mismos. Es necesario mencionar que éste es un efecto colateral. No es buena idea utilizar normalización por lotes para regularizar una red, sino para acelerar su entrenamiento.
  • Puede producir mejores resultados, en general: De nuevo, éste es un efecto colateral que se ha visto en diversos experimentos que utilizan esta técnica, pero como ya mencioné en el punto anterior, la normalización por lotes es un mecanismo para acelerar el entrenamiento de una red, no para mejorar los resultados. Obviamente, si podemos entrenar una red más velozmente, entonces, podemos experimentar con más alternativas, lo que, en teoría, debería llevarnos a una mejor solución.

Para finalizar esta sección, te recomiendo que mires los próximos dos videos, donde el padrino de deep learning, Andrew Ng, explica con más tecnicismos el funcionamiento de la normalización por lotes.

En el notebook a continuación experimentaremos un poco con la normalización por lotes usando TensorFlow:

Finalmente, en este segundo notebook mostraremos el uso de la normalización por lotes entrenando una red neuronal:

>>> Descarga el código de este post aquí <<<

Hasta luego.

Sobre el Autor

Jesús Martínez es el creador de DataSmarts, un lugar para los apasionados por computer vision y machine learning. Cuando no se encuentra bloggeando, jugando con algún algoritmo o trabajando en un proyecto (muy) cool, disfruta escuchar a The Beatles, leer o viajar por carretera.