Deep Lerning Wint Python
Deep Lerning Wint Python
François Chollet
MANTENIMIENTO
Machine Translated by Google
Aprendizaje
profundo con Pytho
FRANÇOIS CHOLLET
MANTENIMIENTO
ISLA REFUGIO
Para obtener información en línea y realizar pedidos de este y otros libros de Manning, visite
www.manning.com. La editorial ofrece descuentos en este libro cuando se pide en cantidad.
Para obtener más información, póngase en contacto
Ninguna parte de esta publicación puede reproducirse, almacenarse en un sistema de recuperación o transmitirse,
de ninguna forma o por medios electrónicos, mecánicos, fotocopias o de otro tipo, sin el permiso previo por escrito del
editor.
Muchas de las designaciones utilizadas por fabricantes y vendedores para distinguir sus productos se consideran
marcas comerciales. Cuando esas designaciones aparecen en el libro y Manning Publications tenía
conocimiento de un reclamo de marca registrada, las designaciones se imprimieron en mayúsculas iniciales o todas en
mayúsculas.
Reconociendo la importancia de preservar lo que se ha escrito, la política de Manning es imprimir los libros que
publicamos en papel libre de ácido, y hacemos nuestros mejores esfuerzos para lograr ese fin.
Reconociendo también nuestra responsabilidad de conservar los recursos de nuestro planeta, Manning publica
están impresos en papel que es al menos un 15 por ciento reciclado y procesado sin el uso de cloro elemental.
ISBN 9781617294433
Impreso en los Estados Unidos de América.
1 2 3 4 5 6 7 8 9 10 – MBE – 22 21 20 19 18 17
contenidos breves
PARTE 1 FUNDAMENTOS DEL APRENDIZAJE PROFUNDO ................................ 1
1 ■ ¿ Qué es el aprendizaje profundo?
3 2 ■ Antes de comenzar: los componentes matemáticos de las redes
neuronales 25
3 ■ Introducción a las redes neuronales 56 4 ■
Fundamentos del aprendizaje automático 93
contenido
prefacio xiii
agradecimientos xv
acerca de este libro xvi
sobre el autor xx
sobre la portada xxi
viii
viii CONTENIDO
CONTENIDO ix
X CONTENIDO
CONTENIDO xi
Usar devoluciones de llamada para actuar sobre un modelo durante el entrenamiento 249
Introducción a TensorBoard: el marco de visualización de TensorFlow
252 ■ Conclusión 259
xiii CONTENIDO
9 Conclusiones 314
9.1 Conceptos clave en revisión 315
Varios enfoques de la IA 315 ■ Qué hace que el aprendizaje profundo sea
especial dentro del campo del aprendizaje automático 315 ■ Cómo pensar
sobre el aprendizaje profundo 316 ■ Tecnologías habilitadoras clave 317
El flujo de trabajo universal de aprendizaje automático 318 ■ Arquitecturas de
red clave 319 ■ El espacio de posibilidades 322
índice 353
prefacio
Si ha leído este libro, probablemente sea consciente del extraordinario progreso que ha representado el
aprendizaje profundo para el campo de la inteligencia artificial en el pasado reciente. En apenas cinco años,
hemos pasado del reconocimiento de imágenes y la transcripción de voz casi inutilizables a un desempeño
sobrehumano en estas tareas.
Las consecuencias de este repentino progreso se extienden a casi todas las industrias. Pero para comenzar
a implementar tecnología de aprendizaje profundo en todos los problemas que pueda resolver, debemos hacerla
accesible a la mayor cantidad de personas posible, incluidos los no expertos (personas que no son investigadores
ni estudiantes de posgrado). Para que el aprendizaje profundo alcance su máximo potencial, necesitamos
democratizarlo radicalmente.
Cuando lancé la primera versión del marco de aprendizaje profundo de Keras en marzo de 2015, la
democratización de la IA no era lo que tenía en mente. Había estado investigando sobre aprendizaje automático
durante varios años y había creado Keras para ayudarme con mis propios experimentos. Pero a lo largo de 2015
y 2016, decenas de miles de nuevas personas ingresaron al campo del aprendizaje profundo; muchos de ellos
eligieron Keras porque era, y sigue siendo, el marco más sencillo para empezar. Mientras observaba a decenas
de recién llegados utilizar Keras de formas inesperadas y poderosas, llegué a preocuparme profundamente por la
accesibilidad y la democratización de la IA. Me di cuenta de que cuanto más difundimos estas tecnologías, más
útiles y valiosas se vuelven. La accesibilidad rápidamente se convirtió en un objetivo explícito en el desarrollo de
Keras y, en unos pocos años, la comunidad de desarrolladores de Keras ha logrado logros fantásticos en este
frente. Hemos puesto el aprendizaje profundo en manos de decenas de miles de personas, quienes a su vez lo
están utilizando para resolver problemas importantes que ni siquiera sabíamos que existían hasta hace poco.
El libro que tienes en las manos es otro paso en el camino para hacer que el aprendizaje profundo esté
disponible para la mayor cantidad de personas posible. Keras siempre había necesitado un curso complementario para
xiii
xiv PREFACIO
cubre simultáneamente los fundamentos del aprendizaje profundo, los patrones de uso de Keras y las
mejores prácticas de aprendizaje profundo. Este libro es mi mejor esfuerzo para producir tal curso. yo lo escribi
con un enfoque en hacer que los conceptos detrás del aprendizaje profundo y su implementación sean lo
más accesibles posible. Hacerlo no requirió que simplificara nada; creo firmemente que no hay ideas
difíciles en el aprendizaje profundo. Espero
Este libro le resultará valioso y le permitirá comenzar a construir sistemas inteligentes.
aplicaciones y resolver los problemas que le importan.
expresiones de gratitud
Me gustaría agradecer a la comunidad de Keras por hacer posible este libro. Keras ha crecido
tener cientos de contribuyentes de código abierto y más de 200.000 usuarios. Sus contribuciones y comentarios
han convertido a Keras en lo que es hoy.
También me gustaría agradecer a Google por respaldar el proyecto Keras. Ha sido fantástico
Vea Keras adoptado como API de alto nivel de TensorFlow . Una integración fluida entre Keras
y TensorFlow beneficia enormemente tanto a los usuarios de TensorFlow como a los usuarios de Keras y hace
Aprendizaje profundo accesible para la mayoría.
Quiero agradecer a la gente de Manning que hizo posible este libro: editor
Marjan Bace y todos los miembros de los equipos editorial y de producción, incluida Christina
Taylor, Janet Vail, Tiffany Taylor, Katie Tennant, Dottie Marsico y muchas otras que
trabajó detrás de escena.
xvi
xvi
Este libro está escrito para personas con experiencia en programación Python que desean comenzar
con el aprendizaje automático y el aprendizaje profundo. Pero este libro también puede resultar valioso
para muchos tipos diferentes de lectores:
Incluso las personas con mentalidad técnica que no codifican con regularidad encontrarán este libro
útil como introducción a conceptos básicos y avanzados de aprendizaje profundo.
Para utilizar Keras, necesitará un dominio razonable de Python. Además, será útil estar
familiarizado con la biblioteca Numpy, aunque no es obligatorio. No necesitas experiencia previa con
aprendizaje automático o aprendizaje profundo: este libro cubre desde cero todos los conceptos
básicos necesarios. Tampoco necesitas una formación avanzada en matemáticas: las matemáticas de
nivel de escuela secundaria deberían ser suficientes para seguir adelante.
Mapa vial
Este libro está estructurado en dos partes. Si no tiene experiencia previa con el aprendizaje automático,
le recomiendo encarecidamente que complete la parte 1 antes de pasar a la parte 2.
Comenzaremos con ejemplos simples y, a medida que avance el libro, nos acercaremos cada vez más
a las técnicas más modernas.
La Parte 1 es una introducción de alto nivel al aprendizaje profundo, que proporciona contexto y
definiciones, y explica todas las nociones necesarias para comenzar con el aprendizaje automático y
las redes neuronales:
El Capítulo 3 incluye todo lo que necesita para comenzar con las redes neuronales: una
introducción a Keras, nuestro marco de aprendizaje profundo preferido; una guía para
configurar su estación de trabajo; y tres ejemplos de código fundamental con explicaciones
detalladas. Al final de este capítulo, podrás entrenar neuronas simples.
redes para manejar tareas de clasificación y regresión, y tendrá una idea sólida de lo que sucede
en segundo plano mientras las entrena.
El Capítulo 4 explora el flujo de trabajo canónico de aprendizaje automático. También aprenderás
sobre problemas comunes y sus soluciones.
La Parte 2 profundiza en las aplicaciones prácticas del aprendizaje profundo en visión por computadora
y procesamiento del lenguaje natural. Muchos de los ejemplos presentados en esta parte se pueden
utilizar como plantillas para resolver problemas que encontrará en la práctica del aprendizaje profundo
en el mundo real:
El Capítulo 5 examina una variedad de ejemplos prácticos de visión por computadora, con un
enfoque en la clasificación
de imágenes. El Capítulo 6 le brinda práctica con técnicas para procesar datos de secuencia, como
como texto y serie temporal.
Requisitos de software/hardware
Todos los ejemplos de código de este libro utilizan el marco de aprendizaje profundo de Keras (https://
keras.io ), que es de código abierto y de descarga gratuita. Necesitará acceso a una máquina UNIX ;
También es posible utilizar Windows, pero no lo recomiendo. El Apéndice A lo guía a través de la
configuración completa.
También le recomiendo que tenga una GPU NVIDIA reciente en su máquina, como una TITAN X.
Esto no es necesario, pero mejorará su experiencia al permitirle ejecutar los ejemplos de código varias
veces más rápido. Consulte la sección 3.3 para obtener más información sobre cómo configurar una
estación de trabajo de aprendizaje profundo.
Si no tiene acceso a una estación de trabajo local con una GPU NVIDIA reciente, puede utilizar
un entorno de nube. En particular, puede utilizar instancias de Google Cloud (como una instancia
n1standard8 con un complemento NVIDIA Tesla K80) o instancias de GPU de Amazon Web
Services (AWS) (como una instancia p2.xlarge). El Apéndice B presenta en detalle un posible flujo
de trabajo en la nube que ejecuta una instancia de AWS a través de cuadernos Jupyter, accesibles
desde su navegador.
Código fuente
Todos los ejemplos de código de este libro están disponibles para descargar como cuadernos Jupyter
desde el sitio web del libro, www.manning.com/books/deeplearningwithpython. y en GitHub en https://
github.com/fchollet/deeplearningwithpythonnotebooks.
Foro de libros
La compra de Deep Learning con Python incluye acceso gratuito a un foro web privado
administrado por Manning Publications donde puede hacer comentarios sobre el libro,
hacer preguntas técnicas y recibir ayuda del autor y de otros usuarios. Para acceder al
foro, vaya a https://forums.manning.com/forums/deeplearningwithpython. También
puede obtener más información sobre los foros de Manning y las reglas de conducta en
https://forums.manning.com/forums/about .
El compromiso de Manning con nuestros lectores es proporcionar un lugar donde
pueda tener lugar un diálogo significativo entre lectores individuales y entre lectores y el
autor. No es un compromiso de participación específica por parte del autor, cuya
contribución al foro sigue siendo voluntaria (y no remunerada). ¡Te sugerimos que
intentes hacerle algunas preguntas desafiantes para que no se desvíe su interés! Se
podrá acceder al foro y a los archivos de debates anteriores desde el sitio web del editor
mientras el libro esté impreso.
Sobre el Autor
François Chollet trabaja en aprendizaje profundo en Google en
Mountain View, CA. Es el creador del aprendizaje profundo de Keras.
biblioteca, así como colaborador del marco de aprendizaje automático
TensorFlow. También realiza investigaciones de aprendizaje profundo,
con un enfoque en la visión por computadora y la aplicación de
aprendizaje automático al razonamiento formal. Sus papeles han sido
publicado en importantes congresos en el campo, incluido el
Jornada sobre visión por ordenador y reconocimiento de patrones
(CVPR), la Conferencia y Taller sobre Sistemas de Procesamiento de
Información Neural (NIPS), la Conferencia Internacional sobre Representaciones de Aprendizaje
(ICLR), y otros.
xx
sobre la portada
La figura de la portada de Deep Learning with Python se titula "Hábito de una dama persa en 1568". La
ilustración está tomada de A Collection of the Dresses of Different Nations, Ancient and Modern (cuatro
volúmenes), Londres, de Thomas Jefferys, publicada entre 1757 y 1772. La portada indica que se trata
de grabados en cobre coloreados a mano, realzados con goma arábiga.
Thomas Jefferys (17191771) fue llamado "geógrafo del rey Jorge III". Fue un cartógrafo inglés que
fue el principal proveedor de mapas de su época. Grabó e imprimió mapas para el gobierno y otros
organismos oficiales y produjo una amplia gama de mapas y atlas comerciales, especialmente de
América del Norte. Su trabajo como cartógrafo despertó un interés en las costumbres de vestimenta
locales de las tierras que examinó y cartografió, que se muestran brillantemente en esta colección. La
fascinación por las tierras lejanas y los viajes por placer eran fenómenos relativamente nuevos a finales
del siglo XVIII, y colecciones como ésta eran populares, presentando tanto al turista como al viajero de
sillón a los habitantes de otros países.
xxi
Parte 1
Fundamentos del
aprendizaje profundo
En los últimos años, la inteligencia artificial (IA) ha sido objeto de intensa publicidad en los medios. El
aprendizaje automático, el aprendizaje profundo y la inteligencia artificial aparecen en innumerables
artículos, a menudo fuera de las publicaciones orientadas a la tecnología. Se nos promete un futuro de
chatbots inteligentes, vehículos autónomos y asistentes virtuales: un futuro a veces pintado de forma
sombría y otras veces utópico, donde los empleos humanos serán escasos y la mayor parte de la actividad
económica será manejada por robots o inteligencia artificial . agentes. Para un practicante actual o futuro
del aprendizaje automático, es importante poder reconocer la señal en el ruido para poder distinguir los
desarrollos que cambian el mundo a partir de comunicados de prensa sobrevalorados. Nuestro futuro está
en juego, y es un futuro en el que tú tienes un papel activo que desempeñar: después de leer este libro,
serás uno de los que desarrollen los agentes de IA . Entonces, abordemos estas preguntas: ¿Qué se ha
logrado hasta ahora con el aprendizaje profundo?
¿Qué importancia tiene? ¿Hacia dónde nos dirigimos ahora? ¿Deberías creer en las exageraciones?
Este capítulo proporciona un contexto esencial en torno a la inteligencia artificial, las máquinas
aprendizaje y aprendizaje profundo.
Artificial
inteligencia
Aprendizaje
automático
Aprendizaje profundo
Engine no estaba pensado como una computadora de propósito general cuando se diseñó en las décadas
de 1830 y 1840, porque el concepto de computación de propósito general aún no se había inventado.
Simplemente pretendía ser una forma de utilizar operaciones mecánicas para automatizar ciertos
cálculos del campo del análisis matemático, de ahí el nombre de motor analítico. En 1843, Ada Lovelace
comentó sobre la invención: “La máquina analítica no tiene ninguna pretensión de originar nada. Puede
hacer cualquier cosa que sepamos ordenarle que realice... Su función es ayudarnos a poner a nuestra
disposición lo que ya conocemos”.
Esta observación fue citada más tarde por el pionero de la IA Alan Turing como “la objeción de Lady
Lovelace” en su histórico artículo de 1950 “Computing Machinery and Intelligence”,1 que presentaba la
prueba de Turing así como conceptos clave que darían forma a la IA. Turing citaba a Ada Lovelace
mientras reflexionaba sobre si los ordenadores de uso general podrían ser capaces de aprender y ser
originales, y llegó a la conclusión de que sí.
El aprendizaje automático surge de esta pregunta: ¿podría una computadora ir más allá de “lo que
sabemos cómo ordenarle que realice” y aprender por sí sola cómo realizar una tarea específica?
¿Podría sorprendernos un ordenador? En lugar de que los programadores elaboren reglas de
procesamiento de datos a mano, ¿podría una computadora aprenderlas automáticamente observando los datos?
Esta pregunta abre la puerta a un nuevo paradigma de programación. En la programación clásica,
el paradigma de la IA simbólica, los humanos ingresan reglas (un programa) y datos que se procesarán
de acuerdo con estas reglas, y obtienen respuestas (ver figura 1.2). Con el aprendizaje automático, los
humanos ingresan datos, así como las respuestas que se esperan de los datos, y surgen las reglas.
Estas reglas pueden luego aplicarse a nuevos datos para producir respuestas originales.
Normas
Programación
Respuestas
Datos clásica
Datos
Aprendizaje
Normas Figura 1.2 Aprendizaje automático:
Respuestas automático un nuevo paradigma de programación
1
AM Turing, “Maquinaria informática e inteligencia”, Mind 59, no. 236 (1950): 433460.
Puntos de datos de entrada: por ejemplo, si la tarea es el reconocimiento de voz, estos datos
Los puntos podrían ser archivos de sonido de personas hablando. Si la tarea es etiquetar imágenes,
podrían ser fotografías.
Ejemplos del resultado esperado: en una tarea de reconocimiento de voz, estos podrían ser
transcripciones de archivos de sonido generadas por humanos. En una tarea de imagen, resultados esperados
podrían ser etiquetas como "perro", "gato", etc.
Una forma de medir si el algoritmo está haciendo un buen trabajo. Esto es necesario en
para determinar la distancia entre la salida actual del algoritmo y
su producción esperada. La medición se utiliza como señal de retroalimentación para ajustar
la forma en que funciona el algoritmo. Este paso de ajuste es lo que llamamos aprendizaje.
Un modelo de aprendizaje automático transforma sus datos de entrada en resultados significativos, un proceso que se
“aprende” a partir de la exposición a ejemplos conocidos de entradas y salidas. Por lo tanto, el problema central en el
aprendizaje automático y el aprendizaje profundo es lograr de manera significativa
transformar datos: en otras palabras, aprender representaciones útiles de los datos de entrada en
mano: representaciones que nos acercan al resultado esperado. Antes de irnos
además: ¿qué es una representación? En esencia, es una forma diferente de ver los datos: representarlos o
codificarlos . Por ejemplo, una imagen en color se puede codificar en formato RGB .
(rojoverdeazul) o en formato HSV (tonosaturaciónvalor): son dos diferentes
representaciones de los mismos datos. Algunas tareas que pueden resultar difíciles con una representación pueden
resultar fáciles con otra. Por ejemplo, la tarea "seleccionar todos los píxeles rojos en el
imagen” es más simple en el formato RG , mientras que “hacer la imagen menos saturada” es más simple
en formato HSV . Los modelos de aprendizaje automático tratan de encontrar representaciones apropiadas para sus
datos de entrada: transformaciones de los datos que los hacen más adaptables a la tarea en cuestión, como una tarea
de clasificación.
Lo que necesitamos aquí es una nueva representación de nuestros datos que separe claramente los
puntos blancos de los puntos negros. Una transformación que podríamos utilizar, entre muchas otras
posibilidades, sería un cambio de coordenadas, ilustrado en la figura 1.4.
X X
En este nuevo sistema de coordenadas, se puede decir que las coordenadas de nuestros puntos son
una nueva representación de nuestros datos. ¡Y es bueno! Con esta representación, el problema de
clasificación blanco/negro se puede expresar como una regla simple: “Los puntos negros son tales
que x > 0” o “Los puntos blancos son tales que x < 0”. Esta nueva representación básicamente resuelve
el problema de clasificación.
En este caso, definimos el cambio de coordenadas a mano. Pero si en lugar de eso intentáramos
buscar sistemáticamente diferentes posibles cambios de coordenadas y usáramos como
retroalimentación el porcentaje de puntos clasificados correctamente, entonces estaríamos haciendo
aprendizaje automático. El aprendizaje, en el contexto del aprendizaje automático, describe un proceso
de búsqueda automática de mejores representaciones.
Todos los algoritmos de aprendizaje automático consisten en encontrar automáticamente
transformaciones que conviertan los datos en representaciones más útiles para una tarea determinada.
Estas operaciones pueden ser cambios de coordenadas, como acaba de ver, o proyecciones lineales
(que pueden destruir información), traslaciones, operaciones no lineales (como “seleccionar todos los
puntos tales que x > 0”), etc. Los algoritmos de aprendizaje automático no suelen ser creativos en
El aprendizaje profundo es un subcampo específico del aprendizaje automático: una nueva visión de las representaciones
de aprendizaje a partir de datos que pone énfasis en el aprendizaje de capas sucesivas de datos cada vez más complejos.
representaciones significativas. Lo profundo en el aprendizaje profundo no es una referencia a ningún tipo de
comprensión más profunda lograda por el enfoque; más bien, representa esta idea de capas sucesivas de representaciones.
¿Cuántas capas contribuyen a un modelo de datos?
llamada profundidad del modelo. Otros nombres apropiados para el campo podrían haber sido
aprendizaje de representaciones en capas y aprendizaje de representaciones jerárquicas. moderno profundo
El aprendizaje a menudo implica decenas o incluso cientos de capas sucesivas de representaciones.
y todos se aprenden automáticamente a partir de la exposición a los datos de entrenamiento. Mientras tanto,
otros enfoques del aprendizaje automático tienden a centrarse en aprender sólo una o dos capas de representaciones de
los datos; de ahí que a veces se les llame aprendizaje superficial.
En el aprendizaje profundo, estas representaciones en capas se aprenden (casi siempre) a través de
Modelos llamados redes neuronales, estructurados en capas literales apiladas una encima de otra.
El término red neuronal es una referencia a la neurobiología, pero aunque algunos de los conceptos centrales del
aprendizaje profundo se desarrollaron en parte inspirándose en nuestra
comprensión del cerebro, los modelos de aprendizaje profundo no son modelos del cerebro.
No hay evidencia de que el cerebro implemente algo parecido a los mecanismos de aprendizaje utilizados en los modelos
modernos de aprendizaje profundo. Es posible que se encuentre con artículos de ciencia popular que proclamen que el
aprendizaje profundo funciona como el cerebro o que fue modelado según el
cerebro, pero ese no es el caso. Sería confuso y contraproducente para los recién llegados al campo pensar que el
aprendizaje profundo está relacionado de alguna manera con la neurobiología; no necesitas ese manto de mística y
misterio "igual que nuestras mentes", y
También puedes olvidar cualquier cosa que hayas leído sobre vínculos hipotéticos entre
aprendizaje profundo y biología. Para nuestros propósitos, el aprendizaje profundo es un marco matemático para aprender
representaciones a partir de datos.
¿Cómo son las representaciones aprendidas por un algoritmo de aprendizaje profundo? Examinemos
cómo una red de varias capas de profundidad (ver figura 1.5) transforma una imagen de un dígito para
reconocer de qué dígito se trata.
Entrada 0
original
1
3 Salida
4 final
5
Figura 1.5 Una red neuronal
6
Como se puede ver en la figura 1.6, la red transforma la imagen de los dígitos en representaciones
cada vez más diferentes de la imagen original y cada vez más informativas sobre el resultado final. Se
puede pensar en una red profunda como una operación de destilación de información de varias etapas,
donde la información pasa por filtros sucesivos y sale cada vez más purificada (es decir, útil con respecto
a alguna tarea).
Representaciones
de capa 4 (salida final)
0
Entrada
original
1
89
Así que, técnicamente, eso es el aprendizaje profundo: una forma de aprender representaciones de
datos en varias etapas. Es una idea simple, pero resulta que mecanismos muy simples, suficientemente
escalados, pueden terminar pareciendo mágicos.
mapeo a través de una secuencia profunda de transformaciones de datos simples (capas) y que estas
transformaciones de datos se aprenden mediante la exposición a ejemplos. Ahora veamos cómo
ocurre este aprendizaje, concretamente.
La especificación de lo que hace una capa con sus datos de entrada se almacena en los pesos
de la capa, que en esencia son un montón de números. En términos técnicos, diríamos que la
transformación implementada por una capa está parametrizada por sus pesos (ver figura 1.7).
(Los pesos a veces también se denominan parámetros de una capa). En este contexto, aprender
significa encontrar un conjunto de valores para los pesos de todas las capas en una red, de modo que
la red asigne correctamente las entradas de ejemplo a sus objetivos asociados. Pero aquí está la
cuestión: una red neuronal profunda puede contener decenas de millones de parámetros. Encontrar el
valor correcto para todos ellos puede parecer una tarea desalentadora, especialmente teniendo en
cuenta que modificar el valor de un parámetro afectará el comportamiento de todos los demás.
Entrada X
Capa
Pesos
Objetivo: encontrar (transformación de datos)
los valores correctos
para estos pesos
Capa
Pesos
(transformación de datos)
Para controlar algo, primero hay que poder observarlo. Para controlar la salida de una red neuronal,
debe poder medir qué tan lejos está esta salida de lo esperado. Este es el trabajo de la función de
pérdida de la red, también llamada función objetivo. La función de pérdida toma las predicciones de la
red y el objetivo verdadero (lo que quería que la red generara) y calcula una puntuación de distancia,
capturando qué tan bien le ha ido a la red en este ejemplo específico (consulte la figura 1.8).
Entrada X
Capa
Pesos
(transformación de datos)
Capa
Pesos
(transformación de datos)
Función de pérdida
El truco fundamental en el aprendizaje profundo es utilizar esta puntuación como señal de retroalimentación para
ajustar un poco el valor de los pesos, en una dirección que reducirá la puntuación de pérdida para el ejemplo
actual (consulte la figura 1.9). Este ajuste es tarea del optimizador, que implementa lo que se llama algoritmo de
retropropagación : el algoritmo central del aprendizaje profundo. El siguiente capítulo explica con más detalle
cómo funciona la retropropagación.
Entrada X
Capa
Pesos
(transformación de datos)
Capa
Pesos
(transformación de datos)
de peso Y' Y
Inicialmente, a los pesos de la red se les asignan valores aleatorios, por lo que la red simplemente implementa
una serie de transformaciones aleatorias. Naturalmente, su rendimiento está lejos de lo que idealmente debería
ser y, en consecuencia, la puntuación de pérdidas es muy alta. Pero con cada ejemplo que procesa la red, los
pesos se ajustan un poco en la dirección correcta y la puntuación de pérdida disminuye. Este es el ciclo de
entrenamiento que, repetido un número suficiente de veces (normalmente decenas de iteraciones en miles de
ejemplos), produce valores de peso que minimizan la función de pérdida. Una red con una pérdida mínima es
aquella cuyas salidas están lo más cerca posible de los objetivos: una red entrenada. Una vez más, se trata de un
mecanismo sencillo que, una vez ampliado, acaba pareciendo mágico.
Todavía estamos explorando todo el alcance de lo que puede hacer el aprendizaje profundo. Hemos
empezado a aplicarlo a una amplia variedad de problemas fuera de la percepción de las máquinas y la
comprensión del lenguaje natural, como el razonamiento formal. Si tiene éxito, esto puede presagiar una
era en la que el aprendizaje profundo ayude a los humanos en la ciencia, el desarrollo de software y más.
Aunque el aprendizaje profundo ha dado lugar a logros notables en los últimos años, las expectativas sobre
lo que este campo podrá lograr en la próxima década tienden a variar mucho.
más alto de lo que probablemente será posible. Aunque algunas aplicaciones que cambiarán el mundo,
como los automóviles autónomos, ya están a nuestro alcance, es probable que muchas más sigan siendo
difíciles de alcanzar durante mucho tiempo, como los sistemas de diálogo creíbles, la traducción automática
a nivel humano en idiomas arbitrarios y la comprensión del lenguaje natural a nivel humano. En particular,
hablar de inteligencia general a nivel humano no debería tomarse demasiado en serio. El riesgo de las altas
expectativas para el corto plazo es que, a medida que la tecnología no dé resultados, la inversión en
investigación se agotará, lo que ralentizará el progreso durante mucho tiempo.
Esto ha sucedido antes. Dos veces en el pasado, la IA pasó por un ciclo de intenso optimismo seguido
de decepción y escepticismo, con el resultado de una escasez de financiación. Comenzó con la IA simbólica
en los años 1960. En aquellos primeros días, las proyecciones sobre la IA eran muy altas. Uno de los
pioneros y defensores más conocidos del enfoque de la IA simbólica fue Marvin Minsky, quien afirmó en
1967: "Dentro de una generación... el problema de la creación de 'inteligencia artificial' se resolverá
sustancialmente". Tres años más tarde, en 1970, hizo una predicción cuantificada con mayor precisión:
“Dentro de tres a ocho años tendremos una máquina con la inteligencia general de un ser humano
promedio”. En 2016, tal logro todavía parece estar lejano en el futuro (tan lejos que no tenemos manera de
predecir cuánto tiempo tomará), pero en los años 1960 y principios de los años 1970, varios expertos creían
que estaba a la vuelta de la esquina (como lo hacen muchas personas hoy en día). Unos años más tarde,
cuando estas altas expectativas no se materializaron, los investigadores y los fondos gubernamentales se
retiraron del campo, lo que marcó el inicio del primer invierno de IA (una referencia a un invierno nuclear,
porque esto ocurrió poco después del apogeo de la Guerra Fría).
No sería el último. En la década de 1980, una nueva visión de la IA simbólica, los sistemas expertos,
comenzó a cobrar fuerza entre las grandes empresas. Algunas historias de éxito iniciales desencadenaron
una ola de inversiones, y corporaciones de todo el mundo iniciaron sus propios departamentos internos de
IA para desarrollar sistemas expertos. Alrededor de 1985, las empresas gastaban más de mil millones de
dólares cada año en esta tecnología; pero a principios de la década de 1990, estos sistemas habían
demostrado ser costosos de mantener, difíciles de escalar y de alcance limitado, y el interés disminuyó. Así
comenzó el segundo invierno de la IA .
Es posible que actualmente estemos siendo testigos del tercer ciclo de exageración y decepción de
la IA , y todavía estamos en la fase de intenso optimismo. Es mejor moderar nuestras expectativas a
corto plazo y asegurarnos de que las personas menos familiarizadas con el aspecto técnico del campo
tengan una idea clara de lo que el aprendizaje profundo puede y no puede ofrecer.
1.1.8 La promesa de la IA
Aunque es posible que tengamos expectativas poco realistas a corto plazo para la IA, el panorama a
largo plazo parece prometedor. Apenas estamos comenzando a aplicar el aprendizaje profundo a
muchos problemas importantes para los cuales podría resultar transformador, desde diagnósticos
médicos hasta asistentes digitales. La investigación en IA ha avanzado sorprendentemente rápido en
los últimos cinco años, en gran parte debido a un nivel de financiación nunca antes visto en la corta
historia de la IA, pero hasta ahora relativamente poco de este progreso ha llegado a los productos y
procesos. que forman nuestro mundo. La mayoría de los hallazgos de la investigación sobre el
aprendizaje profundo aún no se aplican, o al menos no se aplican a toda la gama de problemas que
pueden resolver en todas las industrias. Su médico aún no utiliza IA, y su contador tampoco.
Probablemente no utilices tecnologías de IA en tu vida diaria. Por supuesto, puede hacer preguntas
sencillas a su teléfono inteligente y obtener respuestas razonables, puede obtener recomendaciones
de productos bastante útiles en Amazon.com y puede buscar "cumpleaños" en Google Fotos y encontrar
instantáneamente las fotos de la última fiesta de cumpleaños de su hija. mes. Eso está muy lejos de
donde solían estar estas tecnologías. Pero estas herramientas siguen siendo sólo accesorios de nuestra
vida diaria. La IA aún tiene que pasar a ser central en la forma en que trabajamos, pensamos y vivimos.
En este momento, puede parecer difícil de creer que la IA pueda tener un gran impacto en nuestro
mundo, porque aún no está ampliamente implementada, de la misma manera que, allá por 1995, habría
sido difícil creer en el impacto futuro de Internet. . En aquel entonces, la mayoría de la gente no veía la
importancia que tenía Internet para ellos y cómo iba a cambiar sus vidas. Lo mismo ocurre hoy con el
aprendizaje profundo y la IA . Pero no nos equivoquemos: la IA está por llegar. En un futuro no muy
lejano, la IA será tu asistente, incluso tu amiga; Responderá a tus preguntas, ayudará a educar a tus
hijos y velará por tu salud. Le entregará sus compras en su puerta y lo llevará del punto A al punto B.
Será su interfaz con un mundo cada vez más complejo y con uso intensivo de información. Y, lo que es
aún más importante, la IA ayudará a la humanidad en su conjunto a avanzar, ayudando a los científicos
humanos en nuevos descubrimientos revolucionarios en todos los campos científicos, desde la
genómica hasta las matemáticas.
En el camino, es posible que enfrentemos algunos reveses y tal vez un nuevo invierno de la IA , de
la misma manera que la industria de Internet fue sobrevalorada en 19981999 y sufrió una crisis que
acabó con la inversión a principios de la década de 2000. Pero eventualmente llegaremos allí. La IA
terminará aplicándose a casi todos los procesos que conforman nuestra sociedad y nuestra vida diaria,
de manera muy similar a como lo es Internet hoy.
No crea en las exageraciones a corto plazo, pero sí crea en la visión a largo plazo. Puede que pase
un tiempo antes de que la IA alcance su verdadero potencial (un potencial cuyo alcance nadie se ha
atrevido aún a soñar), pero la IA está llegando y transformará nuestro mundo de una manera fantástica.
Antes del aprendizaje profundo: una breve historia del aprendizaje automático 15
Los datos se asignan a una nueva representación de alta dimensión donde el límite de decisión se
puede expresar como un hiperplano (si los datos fueran bidimensionales, como en la figura 1.10,
un hiperplano sería Una línea recta).
2 Un buen límite de decisión (un hiperplano de separación) se calcula intentando maximizar la distancia
entre el hiperplano y los puntos de datos más cercanos de cada clase, un paso llamado maximizar
el margen. Esto permite que el límite se generalice bien en nuevas muestras fuera del conjunto
de datos de entrenamiento.
La técnica de mapear datos en una representación de alta dimensión donde un problema de clasificación
se vuelve más simple puede parecer buena en el papel, pero en la práctica a menudo es computacionalmente
intratable. Ahí es donde entra en juego el truco del kernel (la idea clave que da nombre a los métodos del
kernel). Aquí está la esencia del asunto: encontrar una buena decisión
2
Vladimir Vapnik y Corinna Cortes, “SupportVector Networks”, Machine Learning 20, no. 3 (1995): 273–297.
3
Vladimir Vapnik y Alexey Chervonenkis, “Una nota sobre una clase de perceptrones”, Automatización y control
remoto 25 (1964).
dieciséis
CAPÍTULO 1 ¿Qué es el aprendizaje profundo?
Pero las SVM resultaron difíciles de escalar a grandes conjuntos de datos y no proporcionaron buenos resultados para
Problemas de percepción como la clasificación de imágenes. Debido a que una SVM es poco profunda
Con este método, aplicar una SVM a problemas de percepción requiere primero extraer manualmente
representaciones útiles (un paso llamado ingeniería de características), lo cual es difícil y frágil.
Los árboles de decisión son estructuras similares a diagramas de flujo que le permiten clasificar puntos de datos de
entrada o predecir valores de salida dadas las entradas (consulte la figura 1.11). Son fáciles de visualizar e
interpretar. Los árboles de decisiones aprendidos a partir de datos comenzaron a recibir un importante interés de investigación
en la década de 2000 y en 2010 a menudo se preferían a los métodos del núcleo.
Datos de entrada
Pregunta
Antes del aprendizaje profundo: una breve historia del aprendizaje automático 17
utiliza el aumento de gradiente, una forma de mejorar cualquier modelo de aprendizaje automático mediante el
entrenamiento iterativo de nuevos modelos que se especializan en abordar los puntos débiles de los modelos anteriores.
Aplicado a árboles de decisión, el uso de la técnica de aumento de gradiente da como resultado modelos
que superan estrictamente a los bosques aleatorios la mayor parte del tiempo, aunque tienen propiedades similares.
Puede ser uno de los mejores, si no el mejor, algoritmo para tratar con problemas no perceptivos.
datos hoy. Junto con el aprendizaje profundo, es una de las técnicas más utilizadas en
Competiciones de Kaggle.
Alrededor de 2010, aunque las redes neuronales fueron rechazadas casi por completo por la comunidad científica en
general, varias personas todavía trabajaban en redes neuronales.
comenzaron a lograr avances importantes: los grupos de Geoffrey Hinton en la Universidad de Toronto, Yoshua
Bengio en la Universidad de Montreal, Yann LeCun en New
Universidad de York e IDSIA en Suiza.
En 2011, Dan Ciresan de IDSIA comenzó a ganar concursos académicos de clasificación de imágenes con redes
neuronales profundas entrenadas por GPU: el primer éxito práctico del aprendizaje profundo moderno. Pero el
momento decisivo llegó en 2012, con la entrada de
El grupo de Hinton en el desafío anual de clasificación de imágenes a gran escala ImageNet. El
El desafío ImageNet era notoriamente difícil en ese momento y consistía en clasificar imágenes en color de alta
resolución en 1.000 categorías diferentes después de entrenar en 1,4 millones.
imágenes. En 2011, los cinco primeros en precisión del modelo ganador, basado en el clásico
enfoques de visión por computadora, fue solo del 74,3%. Luego, en 2012, un equipo liderado por Alex
Krizhevsky y asesorado por Geoffrey Hinton pudo lograr una precisión entre los cinco primeros de
83,6%: un avance significativo. La competencia ha estado dominada por profundos
redes neuronales convolucionales cada año desde entonces. En 2015, el ganador alcanzó una precisión del 96,4% y
la tarea de clasificación en ImageNet se consideró un problema completamente resuelto.
Desde 2012, las redes neuronales convolucionales profundas (convnets) se han convertido en la opción preferida.
algoritmo para todas las tareas de visión por computadora; En términos más generales, actúan sobre todos los aspectos perceptivos.
tareas. En las principales conferencias de visión por computadora celebradas en 2015 y 2016, fue casi imposible
encontrar presentaciones que no involucraran convnets de alguna forma. Al mismo tiempo,
El aprendizaje profundo también ha encontrado aplicaciones en muchos otros tipos de problemas, como
procesamiento natural del lenguaje. Ha reemplazado completamente a las SVM y los árboles de decisión en un
amplia gama de aplicaciones. Por ejemplo, desde hace varios años, la Organización Europea
para la Investigación Nuclear, CERN, utilizó métodos basados en árboles de decisión para el análisis de partículas.
datos del detector ATLAS del Gran Colisionador de Hadrones (LHC); pero el CERN finalmente cambió a redes
neuronales profundas basadas en Keras debido a su mayor rendimiento.
y facilidad de entrenamiento en grandes conjuntos de datos.
La razón principal por la que el aprendizaje profundo despegó tan rápidamente es que ofrecía un mejor rendimiento
en muchos problemas. Pero esa no es la única razón. El aprendizaje profundo también hace
La resolución de problemas es mucho más fácil, porque automatiza completamente lo que solía ser el
El paso más crucial en un flujo de trabajo de aprendizaje automático: la ingeniería de funciones.
Las técnicas anteriores de aprendizaje automático (aprendizaje superficial) solo implicaban transformar los datos
de entrada en uno o dos espacios de representación sucesivos, generalmente a través de
transformaciones simples como proyecciones no lineales de alta dimensión (SVM) o
árboles de decisión. Pero las representaciones refinadas que requieren los problemas complejos generalmente no pueden
lograrse mediante tales técnicas. Como tal, los humanos tuvieron que hacer grandes esfuerzos
para hacer que los datos de entrada iniciales sean más susceptibles de procesamiento mediante estos métodos:
Tuvo que diseñar manualmente buenas capas de representaciones para sus datos. Se llama
Ingeniería de características. El aprendizaje profundo, por otro lado, automatiza completamente este paso:
Con el aprendizaje profundo, aprende todas las funciones de una sola vez en lugar de tener que diseñar
ellos mismos. Esto ha simplificado enormemente los flujos de trabajo de aprendizaje automático, reemplazando a menudo
sofisticados procesos de múltiples etapas con un sistema de aprendizaje profundo único, simple y de extremo a extremo.
modelo.
Quizás se pregunte, si el quid de la cuestión es tener múltiples capas sucesivas de representaciones, ¿podrían
aplicarse repetidamente métodos superficiales para emular los efectos de
¿aprendizaje profundo? En la práctica, hay rendimientos decrecientes rápidamente en aplicaciones sucesivas de métodos
de aprendizaje superficial, porque la primera capa de representación óptima en un modelo de tres capas no es la primera
capa óptima en un modelo de una o dos capas. Lo transformador del aprendizaje profundo es que permite que un modelo
aprenda todas las capas de representación.
conjuntamente, al mismo tiempo, y no en sucesión (con avidez, como se dice). Con articulación
aprendizaje de características, cada vez que el modelo ajusta una de sus características internas, todas las demás
características que dependen de él se adaptan automáticamente al cambio, sin necesidad de intervención humana.
intervención. Todo está supervisado por una única señal de retroalimentación: cada cambio en el
El modelo sirve al objetivo final. Esto es mucho más poderoso que apilar con avidez
modelos, porque permite que las representaciones complejas y abstractas sean aprendidas por
dividirlos en largas series de espacios intermedios (capas); cada espacio es
sólo una simple transformación del anterior.
Estas son las dos características esenciales de cómo el aprendizaje profundo aprende de los datos:
la forma incremental, capa por capa, en la que se desarrollan representaciones cada vez más complejas,
y el hecho de que estas representaciones incrementales intermedias se aprenden de forma conjunta, cada capa
siendo actualizado para seguir tanto las necesidades de representación de la capa superior como las
necesidades de la capa inferior. Juntas, estas dos propiedades han hecho que el aprendizaje profundo
mucho más exitoso que los enfoques anteriores del aprendizaje automático.
Antes del aprendizaje profundo: una breve historia del aprendizaje automático 19
En 2016 y 2017, Kaggle estuvo dominado por dos enfoques: aumento de gradiente
máquinas y aprendizaje profundo. Específicamente, el aumento de gradiente se utiliza para problemas.
donde hay datos estructurados disponibles, mientras que el aprendizaje profundo se utiliza para problemas de
percepción como la clasificación de imágenes. Quienes practican el primero casi siempre utilizan el
Excelente biblioteca XGBoost, que ofrece soporte para los dos lenguajes más populares de
ciencia de datos: Python y R. Mientras tanto, la mayoría de los participantes de Kaggle que utilizan el aprendizaje
profundo utilizan la biblioteca Keras, debido a su facilidad de uso, flexibilidad y compatibilidad con Python.
Estas son las dos técnicas con las que deberías estar más familiarizado para poder
éxito en el aprendizaje automático aplicado en la actualidad: máquinas que aumentan el gradiente, para
problemas de aprendizaje superficial; y aprendizaje profundo, para problemas de percepción. En términos técnicos,
Esto significa que necesitarás estar familiarizado con XGBoost y Keras, las dos bibliotecas que
Actualmente domina las competiciones de Kaggle. Con este libro en mano, ya eres uno
gran paso más cerca.
Avances algorítmicos
Debido a que el campo se guía por hallazgos experimentales más que por la teoría, los avances algorítmicos
sólo son posibles cuando se dispone de datos y hardware apropiados para probar nuevas ideas (o ampliar
ideas antiguas, como suele ser el caso). El aprendizaje automático no es matemática o física, donde se
pueden lograr avances importantes con un lápiz y una hoja de papel. Es una ciencia de la ingeniería.
Los verdaderos obstáculos a lo largo de las décadas de 1990 y 2000 fueron los datos y el hardware.
Pero esto es lo que sucedió durante ese tiempo: Internet despegó y se desarrollaron chips gráficos de alto
rendimiento para las necesidades del mercado de los juegos.
1.3.1 Hardware
Entre 1990 y 2010, las CPU disponibles en el mercado se volvieron más rápidas en un factor de
aproximadamente 5.000. Como resultado, hoy en día es posible ejecutar pequeños modelos de aprendizaje
profundo en su computadora portátil, mientras que esto habría sido imposible hace 25 años.
Pero los modelos típicos de aprendizaje profundo utilizados en visión por computadora o reconocimiento
de voz requieren órdenes de magnitud más de potencia computacional que la que puede ofrecer una
computadora portátil. A lo largo de la década de 2000, empresas como NVIDIA y AMD han estado invirtiendo
miles de millones de dólares en el desarrollo de chips rápidos y masivamente paralelos (unidades de
procesamiento gráfico [GPU]) para potenciar los gráficos de videojuegos cada vez más fotorrealistas:
supercomputadoras baratas y de un solo propósito diseñadas para representar imágenes complejas. Escenas
3D en tu pantalla en tiempo real. Esta inversión llegó a beneficiar a la comunidad científica cuando, en 2007,
NVIDIA lanzó CUDA (https://developer.nvidia.com/aboutcuda), una interfaz de programación para su línea
de GPU. Una pequeña cantidad de GPU comenzó a reemplazar grupos masivos de CPU en varias
aplicaciones altamente paralelizables, comenzando con el modelado físico. Las redes neuronales profundas,
que consisten principalmente en muchas multiplicaciones de matrices pequeñas, también son altamente
paralelizables; y alrededor de 2011, algunos investigadores comenzaron a escribir implementaciones CUDA
de redes neuronales; Dan Ciresan4 y Alex Krizhevsky5 estuvieron entre los primeros.
4
Consulte “Redes neuronales convolucionales flexibles y de alto rendimiento para la clasificación de imágenes”, Actas de la 22ª
Conferencia Internacional Conjunta sobre Inteligencia Artificial (2011), www.ijcai.org/Proceedings/11/Papers/210.pdf .
5
Consulte “Clasificación de ImageNet con redes neuronales convolucionales profundas”, Avances en sistemas de procesamiento
de información neuronal 25 (2012), http://mng.bz/2286.
Lo que pasó es que el mercado de los juegos subvencionó la supercomputación durante el próximo
Generación de aplicaciones de inteligencia artificial. A veces, las grandes cosas comienzan como
juegos. Hoy en día, la NVIDIA TITAN X, una GPU para juegos que costaba 1.000 dólares a finales de 2015,
puede ofrecer un pico de 6,6 TFLOPS en precisión simple: 6,6 billones de operaciones float32
por segundo. Eso es aproximadamente 350 veces más de lo que se puede obtener de una computadora portátil
moderna. En una TITAN X, sólo se necesitan un par de días para entrenar un modelo ImageNet del
tipo que habría ganado la competencia ILSVRC hace unos años. Mientras tanto, grandes
Las empresas entrenan modelos de aprendizaje profundo en grupos de cientos de GPU de un tipo.
desarrollado específicamente para las necesidades de aprendizaje profundo, como el NVIDIA Tesla K80.
El puro poder computacional de tales clusters es algo que nunca habría existido.
sido posible sin las GPU modernas.
Es más, la industria del aprendizaje profundo está empezando a ir más allá de las GPU y está
invertir en chips cada vez más especializados y eficientes para el aprendizaje profundo. En 2016, en su
En la convención anual de E/S , Google reveló su proyecto de unidad de procesamiento tensorial (TPU) : una
Nuevo diseño de chip desarrollado desde cero para ejecutar redes neuronales profundas, que es
Según se informa, es 10 veces más rápido y mucho más eficiente energéticamente que las GPU de primera línea .
1.3.2 Datos
A veces se anuncia la IA como la nueva revolución industrial. Si el aprendizaje profundo es el vapor
Como motor de esta revolución, los datos son su carbón: la materia prima que impulsa nuestras máquinas
inteligentes, sin la cual nada sería posible. Cuando se trata de datos, en
Además del progreso exponencial en hardware de almacenamiento en los últimos 20 años (siguiendo la ley de
Moore), el factor decisivo ha sido el auge de Internet, que hace posible recopilar y distribuir conjuntos de datos muy
grandes para el aprendizaje automático. Hoy, grande
Las empresas trabajan con conjuntos de datos de imágenes, conjuntos de datos de vídeo y conjuntos de datos de lenguaje natural que
no podría haberse recopilado sin Internet. Etiquetas de imagen generadas por el usuario en
Flickr, por ejemplo, ha sido un tesoro de datos para la visión por computadora. También lo son los vídeos de
YouTube. Y Wikipedia es un conjunto de datos clave para el procesamiento del lenguaje natural.
Si hay un conjunto de datos que ha sido un catalizador para el auge del aprendizaje profundo, es el
Conjunto de datos ImageNet, que consta de 1,4 millones de imágenes anotadas manualmente
con 1.000 categorías de imágenes (1 categoría por imagen). Pero, ¿qué hace que ImageNet sea especial?
no es sólo su gran tamaño, sino también la competencia anual asociada a él.6
Como Kaggle ha estado demostrando desde 2010, los concursos públicos son una forma excelente de motivar
a investigadores e ingenieros a ir más allá. tener en común
Los puntos de referencia por los que los investigadores compiten para superar han ayudado en gran medida al reciente aumento de
aprendizaje profundo.
1.3.3 Algoritmos
Además del hardware y los datos, hasta finales de la década de 2000 nos faltaba una forma fiable de
entrenar redes neuronales muy profundas. Como resultado, las redes neuronales todavía eran bastante superficiales,
6
El desafío de reconocimiento visual a gran escala de ImageNet (ILSVRC), www.imagenet.org/challenges/LSVRC.
utilizando sólo una o dos capas de representaciones; por lo tanto, no pudieron brillar frente a métodos
superficiales más refinados, como SVM y bosques aleatorios. La cuestión clave era la de la propagación del
gradiente a través de pilas profundas de capas. La señal de retroalimentación utilizada para entrenar redes
neuronales se desvanecería a medida que aumentara el número de capas.
Esto cambió alrededor de 20092010 con la llegada de varios sencillos pero importantes
mejoras algorítmicas que permitieron una mejor propagación del gradiente:
Sólo cuando estas mejoras comenzaron a permitir modelos de entrenamiento con 10 o más capas comenzó a
brillar el aprendizaje profundo.
Finalmente, en 2014, 2015 y 2016, se descubrieron formas aún más avanzadas de ayudar a la propagación
de gradientes, como la normalización por lotes, conexiones residuales y convoluciones separables en
profundidad. Hoy podemos entrenar desde cero modelos que tienen miles de capas de profundidad.
eventualmente, para todas las tareas de percepción, los líderes de la industria tomaron nota. Lo que siguió fue una ola gradual de inversión
En 2011, justo antes de que el aprendizaje profundo apareciera en el centro de atención, la inversión total
de capital de riesgo en IA fue de alrededor de 19 millones de dólares, que se destinaron casi en su totalidad a
aplicaciones prácticas de enfoques de aprendizaje automático superficial. En 2014, había aumentado a la
asombrosa cifra de 394 millones de dólares. Se lanzaron docenas de nuevas empresas en estos tres años,
tratando de capitalizar el revuelo por el aprendizaje profundo. Mientras tanto, las grandes empresas tecnológicas
como Google, Facebook, Baidu y Microsoft han invertido en departamentos de investigación internos en
cantidades que muy probablemente eclipsarían el flujo de dinero de capital de riesgo. Solo han surgido unas
pocas cifras: en 2013, Google adquirió la startup de aprendizaje profundo DeepMind por 500 millones de dólares,
la mayor adquisición de una empresa de inteligencia artificial en la historia. En 2014, Baidu puso en marcha un
centro de investigación de aprendizaje profundo en Silicon Valley, invirtiendo 300 millones de dólares en el
proyecto. Intel adquirió la startup de hardware de aprendizaje profundo Nervana Systems en 2016 por más de
400 millones de dólares.
El aprendizaje automático (en particular, el aprendizaje profundo) se ha convertido en un elemento central
de la estrategia de productos de estos gigantes tecnológicos. A finales de 2015, el director ejecutivo de Google ,
Sundar Pichai, afirmó: “El aprendizaje automático es una forma fundamental y transformadora mediante la cual
repensamos cómo hacemos todo. Lo estamos aplicando cuidadosamente en todos nuestros productos, ya sea
búsqueda, anuncios, YouTube o Play. Y estamos en los primeros días, pero nos verán, de manera sistemática,
aplicar el aprendizaje automático en todas estas áreas”.7
7
Sundar Pichai, llamada sobre resultados de Alphabet, 22 de octubre de 2015.
Como resultado de esta ola de inversión, el número de personas que trabajan en aprendizaje profundo
pasó en sólo cinco años de unos pocos cientos a decenas de miles, y el progreso de la investigación ha
alcanzado un ritmo frenético. Actualmente no hay señales de que esta tendencia vaya a desacelerarse en el
corto plazo.
Uno de los factores clave que impulsa esta afluencia de caras nuevas en el aprendizaje profundo ha sido la
democratización de los conjuntos de herramientas utilizados en el campo. Al principio, realizar aprendizaje
profundo requería una gran experiencia en C++ y CUDA , que pocas personas poseían. Hoy en día, las
habilidades básicas de programación de Python son suficientes para realizar investigaciones avanzadas de
aprendizaje profundo. Esto ha sido impulsado principalmente por el desarrollo de Theano y luego TensorFlow
(dos marcos de manipulación de tensores simbólicos para Python que admiten la autodiferenciación,
simplificando enormemente la implementación de nuevos modelos) y por el surgimiento de bibliotecas fáciles
de usar como Keras. lo que hace que el aprendizaje profundo sea tan fácil como manipular ladrillos LEGO .
Después de su lanzamiento a principios de 2015, Keras se convirtió rápidamente en la solución de aprendizaje
profundo de referencia para un gran número de nuevas empresas, estudiantes graduados e investigadores que se lanzan al camp
1.3.6 ¿Durará?
¿Hay algo especial en las redes neuronales profundas que las convierta en el enfoque “correcto” para que las
empresas inviertan y los investigadores acudan en masa? ¿O es el aprendizaje profundo sólo una moda
pasajera que tal vez no dure? ¿Seguiremos usando redes neuronales profundas dentro de 20 años?
El aprendizaje profundo tiene varias propiedades que justifican su condición de revolución de la IA, y llegó
para quedarse. Puede que dentro de dos décadas no utilicemos redes neuronales, pero todo lo que usemos
heredará directamente del aprendizaje profundo moderno y sus conceptos centrales.
Estas importantes propiedades se pueden clasificar en términos generales en tres categorías:
Modelos complejos y poderosos. Esto también hace que el aprendizaje profundo sea aplicable a
conjuntos de datos bastante pequeños.
El aprendizaje profundo sólo ha estado en el centro de atención durante unos pocos años y aún no hemos establecido
el alcance total de lo que puede hacer. Cada mes que pasa, aprendemos sobre nuevas
casos de uso y mejoras de ingeniería que eliminan las limitaciones anteriores. Después de una revolución científica,
el progreso generalmente sigue una curva sigmoidea: comienza con un período de
progreso rápido, que se estabiliza gradualmente a medida que los investigadores topan con duras limitaciones, y luego
Las mejoras adicionales se vuelven incrementales. El aprendizaje profundo en 2017 parece estar en el
primera mitad de ese sigmoide, con mucho más progreso por venir en los próximos años.
Antes de comenzar:
los componentes
matemáticos de las redes neurona
25
eso se ha introducido, punto por punto. Ten en cuenta que estos conceptos te serán fundamentales
para comprender los ejemplos prácticos que vendrán a continuación.
capítulos!
Después de leer este capítulo, tendrá una comprensión intuitiva de cómo funcionan las neuronas.
Las redes funcionan y podrá pasar a aplicaciones prácticas, que le ayudarán
Comience con el capítulo 3.
El problema que intentamos resolver aquí es clasificar imágenes en escala de grises de diez dígitos escritos a mano
(28 × 28 píxeles) en sus 10 categorías (0 a 9). Usaremos el conjunto de datos MNIST , un clásico en la comunidad de
aprendizaje automático, que ha existido casi tanto tiempo como el campo mismo y ha sido estudiado intensamente. Es
un conjunto de 60.000 imágenes de entrenamiento, más 10.000 imágenes de prueba, reunidas por el Instituto Nacional
de Estándares y Tecnología (el NIST en MNIST) en la década de 1980. Puede pensar en "resolver" MNIST como el "Hola
mundo" del aprendizaje profundo: es lo que hace para verificar que sus algoritmos estén funcionando como se esperaba.
A medida que se convierta en un practicante del aprendizaje automático, verá que MNIST aparece una y otra vez en
artículos científicos, publicaciones de blogs, etc. Puede ver algunos ejemplos de MNIST en la figura 2.1.
No es necesario que intente reproducir este ejemplo en su máquina ahora mismo. Si lo desea, primero deberá configurar
Keras, lo cual se trata en la sección 3.3.
El conjunto de datos MNIST viene precargado en Keras, en forma de un conjunto de cuatro Numpy
matrices.
train_images y train_labels forman el conjunto de entrenamiento, los datos de los que aprenderá el modelo. Luego, el
modelo se probará en el conjunto de prueba, test_images y test_labels.
Las imágenes están codificadas como matrices Numpy y las etiquetas son una matriz de dígitos, que
van del 0 al 9. Las imágenes y las etiquetas tienen una correspondencia uno a uno.
Veamos los datos de entrenamiento:
train_labels array([5, 0,
4, ..., 5, 6, 8], dtype=uint8)
El flujo de trabajo será el siguiente: Primero, alimentaremos a la red neuronal con los datos de
entrenamiento, train_images y train_labels. La red aprenderá entonces a asociar imágenes y etiquetas.
Finalmente, le pediremos a la red que produzca predicciones para test_images y verificaremos si estas
predicciones coinciden con las etiquetas de test_labels.
Construyamos la red; nuevamente, recuerde que no se espera que usted entienda todo acerca de
este ejemplo todavía.
red = modelos.Sequential()
red.add(capas.Dense(512, activación='relu', input_shape=(28 * 28,))) red.add(capas.Dense(10, activación='softmax'))
El componente central de las redes neuronales es la capa, un módulo de procesamiento de datos que se
puede considerar como un filtro de datos. Algunos datos entran y salen en una forma más útil.
Específicamente, las capas extraen representaciones de los datos que se les introducen; con suerte,
representaciones que sean más significativas para el problema en cuestión. La mayor parte del
aprendizaje profundo consiste en encadenar capas simples que implementarán una forma de destilación
progresiva de datos. Un modelo de aprendizaje profundo es como un tamiz para el procesamiento de
datos, hecho de una sucesión de filtros de datos cada vez más refinados: las capas.
Aquí, nuestra red consta de una secuencia de dos capas densas , que son capas neuronales
densamente conectadas (también llamadas completamente conectadas) . La segunda (y última) capa es
una capa softmax de 10 vías , lo que significa que devolverá una matriz de 10 puntuaciones de
probabilidad (que suman 1). Cada puntuación será la probabilidad de que la imagen del dígito actual
pertenezca a una de nuestras clases de 10 dígitos.
Para que la red esté lista para la capacitación, debemos elegir tres cosas más, como parte
del paso de compilación :
Una función de pérdida: cómo podrá la red medir su desempeño con los datos de entrenamiento
y, por lo tanto, cómo podrá orientarse en la dirección correcta.
Métricas para monitorear durante el entrenamiento y las pruebas: aquí solo nos preocuparemos por la precisión.
picante (la fracción de las imágenes que se clasificaron correctamente).
El propósito exacto de la función de pérdida y el optimizador se aclarará a lo largo de los dos capítulos
siguientes.
network.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy', métricas=['precisión'])
Antes del entrenamiento, procesaremos previamente los datos dándoles la forma que espera la red y
escalemos para que todos los valores estén en el intervalo [0, 1] . Anteriormente, nuestras imágenes de
entrenamiento, por ejemplo, se almacenaban en una matriz de forma (60000, 28, 28) de tipo uint8 con
valores en el intervalo [0, 255] . Lo transformamos en una matriz float32 de forma (60000, 28 * 28) con
valores entre 0 y 1.
También necesitamos codificar categóricamente las etiquetas, un paso que se explica en el capítulo 3.
Ahora estamos listos para entrenar la red, lo que en Keras se realiza mediante una llamada al método
de ajuste de la red : ajustamos el modelo a sus datos de entrenamiento:
Durante el entrenamiento se muestran dos cantidades: la pérdida de la red sobre los datos de entrenamiento y
la precisión de la red sobre los datos de entrenamiento.
Rápidamente alcanzamos una precisión de 0,989 (98,9%) en los datos de entrenamiento. Ahora vamos a
Verifique también que el modelo funcione bien en el conjunto de prueba:
La precisión del conjunto de prueba resulta ser del 97,8%, lo que es bastante inferior a la precisión del conjunto
de entrenamiento. Esta brecha entre la precisión del entrenamiento y la precisión de las pruebas es un ejemplo
de sobreajuste: el hecho de que los modelos de aprendizaje automático tienden a funcionar peor con datos
nuevos que con sus datos de entrenamiento. El sobreajuste es un tema central en el capítulo 3.
Con esto concluye nuestro primer ejemplo: acaba de ver cómo se puede construir y entrenar una red
neuronal para clasificar dígitos escritos a mano en menos de 20 líneas de código Python. En el próximo capítulo,
entraré en detalles sobre cada pieza en movimiento que acabamos de ver y aclararé lo que sucede detrás de
escena. Aprenderá sobre los tensores, los objetos que almacenan datos que ingresan a la red; operaciones
tensoriales, de qué están hechas las capas; y descenso de gradiente, que permite que su red aprenda de sus
ejemplos de entrenamiento.
de datos almacenados en matrices Numpy multidimensionales, también llamadas tensores. En general, todos los sistemas
actuales de aprendizaje automático utilizan tensores como estructura de datos básica. Los tensores son fundamentales
para el campo, tan fundamentales
Este vector tiene cinco entradas y por eso se llama vector de 5 dimensiones. No confundas un 5D
vector con un tensor 5D ! Un vector 5D tiene solo un eje y cinco dimensiones a lo largo de su eje, mientras que un tensor
5D tiene cinco ejes (y puede tener cualquier número de dimensiones a lo largo de cada eje). La dimensionalidad puede
denotar el número de entradas a lo largo de un eje específico (como en el caso de nuestro vector 5D ) o el número de
ejes en un tensor (como un tensor 5D), lo que a veces puede resultar confuso. En el último caso, es técnicamente más
correcto hablar de un tensor de rango 5 (el rango de un tensor es el número de ejes), pero la notación ambigua tensor 5D
es común de todos modos.
>>> x = np.array([[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4,
36, 2]])
>>> x.ndim
2
Las entradas del primer eje se denominan filas y las entradas del segundo eje se denominan columnas.
En el ejemplo anterior, [5, 78, 2, 34, 0] es la primera fila de x y [5, 6, 7] es la primera columna.
>>> x = np.array([[[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36,
2]],
>>> x.ndim
3
Al empaquetar tensores 3D en una matriz, puede crear un tensor 4D , etc. En el aprendizaje profundo,
generalmente manipularás tensores que van de 0D a 4D, aunque puedes llegar hasta 5D si procesas
datos de video.
Número de ejes (rango): por ejemplo, un tensor 3D tiene tres ejes y una matriz tiene dos ejes.
Esto también se llama ndim del tensor en bibliotecas de Python como Numpy. Forma: es
una tupla de números enteros que describe cuántas dimensiones tiene la decena a lo largo de
cada eje. Por ejemplo, el ejemplo de matriz anterior tiene forma (3, 5) y el ejemplo de tensor
3D tiene forma (3, 3, 5). Un vector tiene una forma con un solo elemento, como (5,), mientras
que un escalar tiene una forma vacía, (). Tipo de datos (generalmente llamado dtype en
las bibliotecas de Python): este es el tipo de datos contenidos en el tensor; por ejemplo, el tipo de
tensor podría ser float32, uint8, float64, etc. En raras ocasiones, es posible que vea un tensor
de caracteres . Tenga en cuenta que los tensores de cadenas no existen en Numpy (ni en la
mayoría de las otras bibliotecas), porque los tensores viven en segmentos de memoria
contiguos y preasignados: y las cadenas, al ser de longitud variable, impedirían el uso de esta
implementación.
Para hacer esto más concreto, volvamos a mirar los datos que procesamos en el ejemplo MNIST . Primero,
cargamos el conjunto de datos MNIST :
>>> imprimir(tren_imagenes.ndim) 3
Entonces, lo que tenemos aquí es un tensor 3D de enteros de 8 bits. Más precisamente, es una matriz de
60.000 matrices de 28 × 8 números enteros. Cada una de estas matrices es una imagen en escala de
grises, con coeficientes entre 0 y 255.
Mostremos el cuarto dígito en este tensor 3D , usando la biblioteca Matplotlib (parte del conjunto
científico estándar de Python); ver figura 2.2.
dígito = imágenes_tren[4]
Es equivalente a esta notación más detallada, que especifica un índice inicial y un índice final para el corte
a lo largo de cada eje tensorial. Tenga en cuenta que : equivale a seleccionar todo el eje:
Equivalente al
>>> my_slice = train_images[10:100, :, :] >>> my_slice.shape ejemplo anterior
(90, 28, 28) >>> my_slice =
train_images[10:100, También equivalente al
0:28, 0:28] >>> mi_rebanada.forma (90, 28, 28) ejemplo anterior.
En general, puede seleccionar entre dos índices cualesquiera a lo largo de cada eje tensorial. Por ejemplo,
para seleccionar 14 × 14 píxeles en la esquina inferior derecha de todas las imágenes, haga esto:
También es posible utilizar índices negativos. Al igual que los índices negativos en las listas de Python,
indican una posición relativa al final del eje actual. Para recortar las imágenes en parches de 14 × 14
píxeles centrados en el medio, haga esto:
lote = tren_imagenes[:128]
lote = tren_imagenes[128:256]
Y el enésimo lote:
Al considerar un tensor de lote de este tipo, el primer eje (eje 0) se denomina eje de lote o dimensión de lote.
Este es un término que encontrará con frecuencia cuando utilice Keras y otras bibliotecas de aprendizaje
profundo.
Hagamos que los tensores de datos sean más concretos con algunos ejemplos similares a los que encontrará
más adelante. Los datos que manipulará casi siempre caerán en una de las siguientes categorías: Datos
vectoriales: tensores
Este es el caso más común. En dicho conjunto de datos, cada punto de datos se puede codificar como un
vector y, por lo tanto, un lote de datos se codificará como un tensor 2D (es decir, una matriz de vectores),
donde el primer eje es el eje de las muestras y el segundo. El eje es el eje de características.
Echemos un vistazo a dos ejemplos:
Un conjunto de datos actuariales de personas, donde consideramos la edad, el código postal y los
ingresos de cada persona. Cada persona se puede caracterizar como un vector de 3 valores y, por
lo tanto, se puede almacenar un conjunto de datos completo de 100.000 personas en un tensor de
forma 2D (100000, 3).
Un conjunto de datos de documentos de texto, donde representamos cada documento por el número
de veces que aparece cada palabra en él (de un diccionario de 20.000 palabras comunes). Cada
documento se puede codificar como un vector de 20.000 valores (un conteo por palabra en el
diccionario) y, por lo tanto, se puede almacenar un conjunto de datos completo de 500 documentos
en un tensor de forma (500, 20000).
Siempre que el tiempo importe en sus datos (o la noción de orden de secuencia), tiene sentido almacenarlos
en un tensor 3D con un eje de tiempo explícito. Cada muestra se puede codificar como una secuencia de
vectores (un tensor 2D ) y, por lo tanto, un lote de datos se codificará como un tensor 3D (consulte la figura
2.3).
Características
Muestras
El eje del tiempo es siempre el segundo eje (eje del índice 1), por convención. Veamos algunos
ejemplos: Un
conjunto de datos de precios de acciones. Cada minuto, almacenamos el precio actual de las
acciones, el precio más alto del último minuto y el precio más bajo del último minuto.
Así, cada minuto se codifica como un vector 3D , un día completo de negociación se
codifica como un tensor de forma 2D (390, 3) (hay 390 minutos en un día de negociación)
y se pueden almacenar 250 días de datos en un tensor de forma 3D (250, 390, 3). En este
caso, cada muestra equivaldría a los datos de un día.
Un conjunto de datos de tweets, donde codificamos cada tweet como una secuencia de
280 caracteres de un alfabeto de 128 caracteres únicos. En esta configuración, cada
carácter se puede codificar como un vector binario de tamaño 128 (un vector todo ceros
excepto una entrada 1 en el índice correspondiente al carácter). Luego, cada tweet se
puede codificar como un tensor de forma 2D (280, 128) y se puede almacenar un conjunto
de datos de 1 millón de tweets en un tensor de forma (1000000, 280, 128).
Las imágenes suelen tener tres dimensiones: alto, ancho y profundidad de color. Aunque las
imágenes en escala de grises (como nuestros dígitos MNIST ) tienen un solo canal de color y, por
lo tanto, podrían almacenarse en tensores 2D , por convención los tensores de imágenes son
siempre 3D, con un canal de color unidimensional para imágenes en escala de grises. De este
modo, se podría almacenar un lote de 128 imágenes en escala de grises de tamaño 256 × 256 en
un tensor de forma (128, 256, 256, 1), y un lote de 128 imágenes en color en un tensor de forma
(128, 256, 256, 3) (ver figura 2.4).
Canales de color
Altura
Muestras
Hay dos convenciones para las formas de los tensores de imágenes: la convención del último
canal (utilizada por TensorFlow) y la convención del primer canal (utilizada por Theano). El marco
de aprendizaje automático TensorFlow, de Google, coloca el eje de profundidad de color al final:
(muestras, altura, ancho, profundidad_color). Mientras tanto, Theano coloca el eje de profundidad
de color justo después del eje del lote: (muestras, profundidad_color, altura, ancho). Con
la convención Theano, los ejemplos anteriores se convertirían en (128, 1, 256, 256) y (128, 3, 256, 256). El
marco Keras proporciona soporte para ambos formatos.
keras.layers.Dense(512, activación='relu')
Esta capa se puede interpretar como una función que toma como entrada un tensor 2D y devuelve
otro tensor 2D , una nueva representación del tensor de entrada. Específicamente, la función es la
siguiente (donde W es un tensor 2D y b es un vector, ambos atributos de la capa):
Analicemos esto. Aquí tenemos tres operaciones tensoriales: un producto escalar (punto) entre el
tensor de entrada y un tensor llamado W; una suma (+) entre el decena 2D resultante y un vector b;
y, finalmente, una operación relu . relu(x) es máximo(x, 0).
NOTA Aunque esta sección trata enteramente de expresiones de álgebra lineal, aquí no
encontrará ninguna notación matemática. He descubierto que los programadores sin
experiencia matemática pueden dominar más fácilmente los conceptos matemáticos si se
expresan como fragmentos cortos de Python en lugar de ecuaciones matemáticas.
Entonces usaremos el código Numpy en todo momento.
x = x.copy() para i
Evite sobrescribir
en el rango(x.shape[0]):
el tensor de entrada.
para j en rango(x.shape[1]): x[i, j] += y[i, j]
devuelve x
Siguiendo el mismo principio, puedes realizar multiplicaciones, restas, etc. por elementos.
En la práctica, cuando se trata de matrices Numpy, estas operaciones están disponibles como funciones
integradas de Numpy bien optimizadas, que a su vez delegan el trabajo pesado a una implementación de
Subprogramas de Álgebra Lineal Básica (BLAS) si tiene uno instalado (lo cual debería). BLAS son rutinas de
manipulación de tensores eficientes, altamente paralelas y de bajo nivel que normalmente se implementan en
Fortran o C.
Entonces, en Numpy, puedes realizar la siguiente operación de elementos, y será increíblemente rápido:
2.3.2 Radiodifusión
Nuestra implementación ingenua anterior de naive_add solo admite la adición de decenas 2D con formas
idénticas. Pero en la capa Densa presentada anteriormente, agregamos un tensor 2D con un vector. ¿Qué
sucede con la suma cuando las formas de los dos tensores que se suman difieren?
1 Se agregan ejes (llamados ejes de transmisión) al tensor más pequeño para que coincida con el ndim del
tensor más grande.
2 El tensor más pequeño se repite a lo largo de estos nuevos ejes para que coincida con la forma completa.
del tensor mayor.
Veamos un ejemplo concreto. Considere X con forma (32, 10) e y con forma (10,). Primero, agregamos un
primer eje vacío a y, cuya forma se convierte en (1, 10). Luego, repetimos y 32 veces a lo largo de este nuevo
eje, de modo que terminemos con un tensor Y con forma (32, 10), donde Y[i, :] == y para i en el rango (0, 32).
En este punto, podemos proceder a sumar X e Y, porque tienen la misma forma.
En términos de implementación, no se crea ningún tensor 2D nuevo , porque eso sería terriblemente
ineficiente. La operación de repetición es enteramente virtual: ocurre a nivel algorítmico y no a nivel de memoria.
Pero pensando en el vector siendo
repetido 10 veces a lo largo de un nuevo eje es un modelo mental útil. Así es como se vería una
implementación ingenua:
x = x.copy() para i
Evite sobrescribir
en el rango(x.shape[0]):
el tensor de entrada.
para j en rango (x.shape[1]):
x[i, j] += y[j] devolver x
z = np.punto(x, y)
z=xy
z = 0.
para i en el rango (x.shape[0]): z += x[i] * y[i]
regresar z
Habrás notado que el producto escalar entre dos vectores es un escalar y que solo los vectores
con el mismo número de elementos son compatibles para un producto escalar.
También puedes tomar el producto escalar entre una matriz x y un vector y, que devuelve un
vector donde los coeficientes son los productos escalares entre y y las filas de x. Lo implementas
de la siguiente manera:
importar numpy como np
x es una matriz Numpy.
def naive_matrix_vector_dot(x, y):
afirmar len(x.shape) == 2 afirmar
y es un vector Numpy.
len(y.shape) == 1 afirmar x.shape[1] ==
y.shape[0]
¡La primera dimensión de x debe ser la
z = np.zeros(x.shape[0]) para i en el
misma que la dimensión 0 de y!
rango(x.shape[0]):
para j en rango(x.shape[1]): z[i] += x[i, j] * y[j] Esta operación devuelve un vector de
0 con la misma forma que y.
regresar z
También puedes reutilizar el código que escribimos anteriormente, que resalta la relación entre
un producto matrizvector y un producto vectorial:
Tenga en cuenta que tan pronto como uno de los dos tensores tiene un ndim mayor que 1, el
punto ya no es simétrico, es decir, el punto (x, y) no es lo mismo que el punto (y, x).
Por supuesto, un producto escalar se generaliza a tensores con un número arbitrario de ejes.
Las aplicaciones más comunes pueden ser el producto escalar entre dos matrices. Puede tomar
el producto escalar de dos matrices x e y (punto (x, y)) si y solo si x.shape[1] == y.shape[0]. El
resultado es una matriz con forma (x.shape[0], y.shape[1]), donde los coeficientes son los
productos vectoriales entre las filas de x y las columnas de y. Aquí está la implementación
ingenua:
def naive_matrix_dot(x, y):
afirmar len(x.shape) == 2 afirmar
La primera dimensión de x debe ser la
XyY
son len(y.shape) == 2 afirmar x.shape[1] == igual que la dimensión 0 de y!
y.shape[0]
Numerosas matrices. Esta operación devuelve una matriz
z = np.zeros((x.shape[0], y.shape[1])) para i en el rango(x.shape[0]): de 0 con una forma específica.
Itera sobre las filas de x …
para j en rango (y.shape[1]): … y sobre las columnas de y.
fila_x = x[i, :] columna_y
= y[:, j] z[i, j] =
naive_vector_dot(fila_x, columna_y)
regresar z
Para comprender la compatibilidad de formas del producto escalar, es útil visualizar los tensores de
entrada y salida alineándolos como se muestra en la figura 2.5.
forma y:
(b, c)
X.y=z
b columna de y
forma x: forma z:
(a, b) (a, c)
a
x, y y z se representan como rectángulos (cuadros literales de coeficientes). Debido a que las filas y x y
las columnas de y deben tener el mismo tamaño, se deduce que el ancho de x debe coincidir con la
altura de y. Si continúa desarrollando nuevos algoritmos de aprendizaje automático, probablemente
dibujará dichos diagramas con frecuencia.
De manera más general, puede tomar el producto escalar entre tensores de dimensiones superiores,
siguiendo las mismas reglas de compatibilidad de formas descritas anteriormente para el caso 2D:
(a B C D) . (d,) > (a, b, c)
Etcétera.
Un tercer tipo de operación tensorial que es esencial comprender es la remodelación del tensor.
Aunque no se usó en las capas densas en nuestro primer ejemplo de red neuronal, lo usamos cuando
preprocesamos los datos de los dígitos antes de introducirlos en nuestra red:
imágenes_tren = imágenes_tren.reshape((60000, 28 * 28))
Reformar un tensor significa reorganizar sus filas y columnas para que coincidan con la forma objetivo.
Naturalmente, el tensor reformado tiene el mismo número total de coeficientes que el tensor inicial. La
remodelación se comprende mejor con ejemplos sencillos:
>>> x = np.matriz([[0., 1.],
[2., 3.],
[4., 5.]])
>>> imprimir(x.forma)
(3, 2)
Una = [0,5, 1]
Es un punto en un espacio 2D (ver figura 2.6). Es común imaginar un vector como una flecha que une
el origen al punto, como se muestra en la figura 2.7.
1 A [0.5, 1] 1 A [0.5, 1]
1 1
A+B
1 A
B
1 Figura 2.8 Interpretación geométrica de la
suma de dos vectores
En esta expresión, W y b son tensores que son atributos de la capa. Se denominan pesos o parámetros
entrenables de la capa (los atributos del núcleo y del sesgo , respectivamente). Estos pesos contienen la
información aprendida por la red a partir de la exposición a los datos de entrenamiento.
Inicialmente, estas matrices de peso se llenan con pequeños valores aleatorios (un paso llamado
inicialización aleatoria). Por supuesto, no hay razón para esperar que relu(punto(W, entrada) + b), cuando W
y b son aleatorios, produzca representaciones útiles. Las representaciones resultantes no tienen sentido,
pero son un punto de partida. Lo que viene a continuación es ajustar gradualmente estos pesos, en función
de una señal de retroalimentación. Este ajuste gradual, también llamado entrenamiento, es básicamente el
aprendizaje del que se trata el aprendizaje automático.
Esto sucede dentro de lo que se llama un ciclo de entrenamiento, que funciona de la siguiente manera. Repetir
estos pasos en un bucle, siempre que sea necesario:
4 Actualice todos los pesos de la red de manera que reduzca ligeramente la pérdida en esta
lote.
Eventualmente terminará con una red que tiene una pérdida muy baja en sus datos de entrenamiento: una
falta de coincidencia entre las predicciones y_pred y los objetivos esperados y. La red ha “aprendido” a
asignar sus entradas a objetivos correctos. Desde lejos puede parecer mágico, pero cuando lo reduces a
pasos elementales, resulta sencillo.
El paso 1 parece bastante fácil: sólo código de E/S . Los pasos 2 y 3 son simplemente la aplicación de
un puñado de operaciones tensoriales, por lo que podrías implementar estos pasos únicamente a partir de lo
que aprendiste en la sección anterior. La parte difícil es el paso 4: actualizar los pesos de la red. Dado un
coeficiente de peso individual en la red, ¿cómo se puede calcular si el coeficiente debe aumentarse o
disminuirse, y en qué medida?
Una solución ingenua sería congelar todos los pesos de la red excepto el coeficiente escalar que se está
considerando, y probar diferentes valores para este coeficiente. Digamos que el valor inicial del coeficiente es
0,3. Después del reenvío de un lote de datos, la pérdida de red en el lote es 0,5. Si cambia el valor del
coeficiente a 0,35 y vuelve a ejecutar el pase hacia adelante, la pérdida aumenta a 0,6. Pero si bajas el
coeficiente a 0,25, la pérdida cae a 0,4. En este caso, parece que actualizar el coeficiente en 0,05
contribuiría a minimizar la pérdida. Esto tendría que repetirse para todos los coeficientes de la red.
Pero tal enfoque sería terriblemente ineficaz, porque necesitaríamos calcular dos pases hacia delante
(que son caros) para cada coeficiente individual (de
que son muchos, generalmente miles y a veces hasta millones). Un enfoque mucho mejor es aprovechar
el hecho de que todas las operaciones utilizadas en la red
son diferenciables y calculan el gradiente de la pérdida con respecto a la red.
coeficientes. Luego puede mover los coeficientes en la dirección opuesta a la
gradiente, disminuyendo así la pérdida.
Si ya sabes qué significa diferenciable y qué es un gradiente , puedes pasar a
sección 2.4.3. De lo contrario, las dos secciones siguientes le ayudarán a comprender estos
conceptos.
Considere una función continua y suave f(x) = y, asignando un número real x a un nuevo
número real y. Como la función es continua, un pequeño cambio en x sólo puede resultar
en un pequeño cambio en y: esa es la intuición detrás de la continuidad. Digamos que aumentas x
por un pequeño factor epsilon_x: esto da como resultado un pequeño cambio de epsilon_y a y:
Además, debido a que la función es suave (su curva no tiene ángulos abruptos),
cuando epsilon_x es lo suficientemente pequeño, alrededor de cierto punto p, es posible aproximar f como
una función lineal de la pendiente a, de modo que epsilon_y se convierta en un * epsilon_x:
Obviamente, esta aproximación lineal es válida sólo cuando x está lo suficientemente cerca de p.
La pendiente a se llama derivada de f en p. Si a es negativo, significa un pequeño cambio.
de x alrededor de p resultará en una disminución de f(x) (como se muestra en la figura 2.10); y si a es
positivo, un pequeño cambio en x dará como resultado un aumento de f(x). Además, el valor absoluto
de a (la magnitud de la derivada) le indica qué tan rápido este aumento o disminución
pasará.
lineal local
aproximación de f,
con pendiente a
F
Figura 2.10 Derivada de f en p
Para cada función diferenciable f(x) (diferenciable significa “puede derivarse”: por ejemplo, se pueden
derivar funciones suaves y continuas), existe una función derivada
f'(x) que asigna valores de x a la pendiente de la aproximación lineal local de f en aquellos
puntos. Por ejemplo, la derivada de cos(x) es sen(x), la derivada de f(x) = a es f'(x) = a, y así * X
sucesivamente.
Si está intentando actualizar x mediante un factor épsilon_x para minimizar f(x), y
conoces la derivada de f, entonces tu trabajo está hecho: la derivada completamente
describe cómo f(x) evoluciona a medida que cambia x. Si desea reducir el valor de f(x),
sólo necesitas mover x un poco en la dirección opuesta a la derivada.
y_pred = punto(W, x)
valor_pérdida = pérdida(y_pred, y)
Si las entradas de datos x e y están congeladas, entonces esto puede interpretarse como una función que
asigna valores de W a valores de pérdida:
valor_pérdida = f(W)
En su lugar, puede utilizar el algoritmo de cuatro pasos descrito al principio de esta sección: modifique
los parámetros poco a poco según el valor de pérdida actual en un lote aleatorio de datos. Debido a que
estás tratando con una función diferenciable, puedes
calcular su gradiente, lo que le brinda una manera eficiente de implementar el paso 4. Si
actualice los pesos en la dirección opuesta al gradiente, la pérdida será un poco
cada vez menos:
¡Suficientemente fácil! Lo que acabo de describir se llama descenso de gradiente estocástico de mini lotes
(SGD de mini lotes ). El término estocástico se refiere al hecho de que cada lote de datos se extrae en
aleatorio (el estocástico es sinónimo científico de aleatorio). La Figura 2.11 ilustra lo que sucede en 1D,
cuando la red tiene solo un parámetro y usted tiene solo un entrenamiento.
muestra.
valor
A partir de
punto (t=0)
t=1
t=2
t=3
Como puede ver, intuitivamente es importante elegir un valor razonable para el factor de paso .
Si es demasiado pequeño, el descenso por la curva requerirá muchas iteraciones y podría quedarse
estancado en un mínimo local. Si el paso es demasiado grande, sus actualizaciones pueden terminar
llevándolo a ubicaciones completamente aleatorias en la curva.
Tenga en cuenta que una variante del algoritmo SGD de mini lotes sería extraer una única muestra y
objetivo en cada iteración, en lugar de extraer un lote de datos. Esto sería un verdadero SGD (a diferencia
del SGD de mini lotes ). Alternativamente, yendo al extremo opuesto, podría ejecutar cada paso con todos
los datos disponibles, lo que se denomina SGD por lotes . Entonces, cada actualización sería más precisa,
pero mucho más cara. El compromiso eficiente entre estos dos extremos es utilizar minilotes de tamaño
razonable.
Aunque la figura 2.11 ilustra el descenso de gradiente en un espacio de parámetros 1D , en la práctica
utilizará el descenso de gradiente en espacios altamente dimensionales: cada coeficiente de peso en una
red neuronal es una dimensión libre en el espacio, y puede haber decenas de miles. arenas o incluso
millones de ellas. Para ayudarle a generar intuición sobre las superficies de pérdida, también puede
visualizar el descenso del gradiente a lo largo de una superficie de pérdida 2D , como se muestra en la
figura 2.12. Pero no es posible visualizar cómo es el proceso real de entrenamiento de una red neuronal;
no se puede representar un espacio de 1.000.000 de dimensiones de una manera que tenga sentido para
los humanos. Como tal, es bueno tener en cuenta que es posible que las intuiciones que desarrolles a
través de estas representaciones de baja dimensión no siempre sean precisas en la práctica.
Históricamente, esto ha sido una fuente de problemas en el mundo de la investigación del aprendizaje profundo.
Punto de partida
45
40
35
30
25
20
15
10
5
Figura 2.12 Descenso de gradiente por
una superficie de pérdida 2D (dos
Punto final parámetros que se pueden aprender)
Además, existen múltiples variantes de SGD que se diferencian al tener en cuenta las actualizaciones de
peso anteriores al calcular la siguiente actualización de peso, en lugar de simplemente observar el valor
actual de los gradientes. Están, por ejemplo, SGD con impulso, así como Adagrad, RMSProp y varios
otros. Estas variantes se conocen como métodos de optimización u optimizadores. En particular, merece
su atención el concepto de impulso, que se utiliza en muchas de estas variantes. Momentum aborda dos
cuestiones con SGD: la velocidad de convergencia y los mínimos locales. Considere la figura 2.13, que
muestra la curva de una pérdida en función de un parámetro de la red.
Valor
de pérdida
Mínimo
local
Mínimo
global
Como puede ver, alrededor de un determinado valor de parámetro, hay un mínimo local: alrededor de ese punto,
moverse hacia la izquierda provocaría un aumento de la pérdida, pero también lo haría moverse hacia la derecha.
Si el parámetro bajo consideración se optimizara a través de SGD con una tasa de aprendizaje pequeña, entonces
el proceso de optimización se quedaría estancado en el mínimo local en lugar de llegar al mínimo global.
Puedes evitar estos problemas utilizando el impulso, que se inspira en la física. Una imagen mental útil aquí
es pensar en el proceso de optimización como una pequeña bola que rueda por la curva de pérdidas. Si tiene
suficiente impulso, la bola no se quedará atrapada en un barranco y acabará en el mínimo global. El impulso se
implementa moviendo la bola en cada paso basándose no solo en el valor de pendiente actual (aceleración
actual) sino también en la velocidad actual (resultante de la aceleración pasada). En la práctica, esto significa
actualizar el parámetro w basándose no solo en el valor de gradiente actual sino también en la actualización del
parámetro anterior, como en esta implementación ingenua:
El cálculo nos dice que dicha cadena de funciones se puede derivar usando la siguiente identidad, llamada regla
de la cadena: f(g(x)) = f'(g(x)) * g'(x). La aplicación de la regla de la cadena al cálculo de los valores de gradiente
de una red neuronal da lugar a un algoritmo
llamada retropropagación (a veces también llamada diferenciación en modo inverso). La retropropagación comienza
con el valor de pérdida final y trabaja hacia atrás desde las capas superiores a las inferiores, aplicando la regla de
la cadena para calcular la contribución de cada parámetro.
tenido en el valor de la pérdida.
Hoy en día, y en los años venideros, la gente implementará redes en los sistemas modernos.
marcos que son capaces de diferenciación simbólica, como TensorFlow. Esto significa
que, dada una cadena de operaciones con una derivada conocida, pueden calcular un gradiente
función para la cadena (aplicando la regla de la cadena) que asigna valores de parámetros de red
a los valores de gradiente. Cuando tenga acceso a dicha función, el pase hacia atrás es
reducido a una llamada a esta función de gradiente. Gracias a la diferenciación simbólica, podrás
Nunca tendrá que implementar el algoritmo de retropropagación a mano. Por esta razón, nosotros
No perderá su tiempo ni su concentración en derivar la formulación exacta del algoritmo de retropropagación en
estas páginas. Todo lo que necesitas es una buena comprensión de cómo
La optimización basada en gradientes funciona.
Ahora comprende que las imágenes de entrada se almacenan en tensores Numpy, que aquí están
formateados como tensores float32 de forma (60000, 784) (datos de entrenamiento) y (10000, 784)
(datos de prueba), respectivamente.
Esta era nuestra red:
red = modelos.Sequential()
red.add(capas.Dense(512, activación='relu', input_shape=(28 * 28,))) red.add(capas.Dense(10, activación='softmax'))
Ahora comprende que esta red consta de una cadena de dos capas densas , que cada capa aplica
algunas operaciones tensoriales simples a los datos de entrada y que estas operaciones involucran
tensores de peso. Los tensores de peso, que son atributos de las capas, son donde persiste el
conocimiento de la red.
Este fue el paso de compilación de la red:
network.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy',
métricas=['precisión'])
Ahora comprende que categorical_crossentropy es la función de pérdida que se utiliza como señal
de retroalimentación para aprender los tensores de peso y que la fase de entrenamiento intentará
minimizar. También sabe que esta reducción de la pérdida se produce mediante un descenso de
gradiente estocástico de mini lotes. Las reglas exactas que rigen un uso específico del descenso de
gradiente las define el optimizador rmsprop pasado como primer argumento.
Finalmente, este fue el circuito de entrenamiento:
Ahora comprende lo que sucede cuando dice que es apto: la red comenzará a iterar sobre los datos
de entrenamiento en mini lotes de 128 muestras, 5 veces (cada iteración sobre todos los datos de
entrenamiento se llama época ). En cada iteración, la red calculará los gradientes de los pesos con
respecto a la pérdida en el lote y actualizará los pesos.
Dos conceptos clave que verá con frecuencia en capítulos futuros son pérdida y
optimizadores. Estas son las dos cosas que necesita definir antes de comenzar a introducir
datos en una red.
Empezando
con las redes neuronales
Este capítulo está diseñado para iniciarle en el uso de redes neuronales para resolver
problemas reales. Consolidará el conocimiento que obtuvo de nuestro primer ejemplo práctico
en el capítulo 2 y aplicará lo que ha aprendido a tres nuevos problemas que cubren los tres
casos de uso más comunes de las redes neuronales: clasificación binaria, clasificación
multiclase, y regresión escalar.
En este capítulo, veremos más de cerca los componentes centrales de las redes
neuronales que presentamos en el capítulo 2: capas, redes, funciones objetivo y
optimizadores. Le daremos una introducción rápida a Keras, la biblioteca de aprendizaje
profundo de Python que usaremos a lo largo del libro. Configurará una estación de trabajo de aprendizaje profun
56
57
Compatibilidad con TensorFlow, Keras y GPU . Nos sumergiremos en tres ejemplos introductorios de
cómo utilizar redes neuronales para abordar problemas reales:
Al final de este capítulo, podrá utilizar redes neuronales para resolver problemas simples.
Problemas de máquina como clasificación y regresión sobre datos vectoriales. entonces tu
Esté preparado para comenzar a construir una comprensión más basada en principios y basada en la teoría de las máquinas.
aprendizaje en el capítulo 4.
Puede visualizar su interacción como se ilustra en la figura 3.1: la red, compuesta por capas encadenadas, asigna los datos de
entrada a predicciones. Luego, la función de pérdida compara estas predicciones con los objetivos, generando un valor de
pérdida: una medida de qué tan bien las predicciones de la red coinciden con lo esperado. El optimizador utiliza este valor de
pérdida para actualizar los pesos de la red.
Entrada X
Capa
Pesos
(transformación de datos)
Capa
Pesos
(transformación de datos)
de peso Y' Y
Echemos un vistazo más de cerca a las capas, redes, funciones de pérdida y optimizadores.
La estructura de datos fundamental en las redes neuronales es la capa, que conocimos en el capítulo 2. Una capa es un módulo
de procesamiento de datos que toma como entrada uno o más tensores y genera uno o más tensores. Algunas capas no tienen
estado, pero lo más frecuente es que las capas tengan un estado: los pesos de la capa, uno o varios tensores aprendidos con
descenso de gradiente estocástico, que en conjunto contienen el conocimiento de la red.
Diferentes capas son apropiadas para diferentes formatos de tensor y diferentes tipos de procesamiento de datos. Por
ejemplo, los datos vectoriales simples, almacenados en tensores de forma 2D (muestras, características), a menudo se procesan
mediante capas densamente conectadas , también llamadas capas completamente conectadas o densas (la clase Densa en
Keras). Los datos de secuencia, almacenados en tensores de forma 3D (muestras, pasos de tiempo, características),
generalmente se procesan mediante capas recurrentes , como una capa LSTM .
Los datos de imagen, almacenados en tensores 4D , generalmente se procesan mediante capas convolucionales 2D (Conv2D).
Puedes pensar en las capas como los ladrillos LEGO del aprendizaje profundo, una metáfora que es
hecho explícito por marcos como Keras. Construir modelos de aprendizaje profundo en Keras es
Esto se hace recortando capas compatibles para formar líneas útiles de transformación de datos. La
noción de compatibilidad de capas aquí se refiere específicamente al hecho de que cada capa
solo aceptará tensores de entrada de una determinada forma y devolverá tensores de salida de una
determinada forma. Considere el siguiente ejemplo:
Estamos creando una capa que solo aceptará como entrada tensores 2D donde la primera dimensión
es 784 (el eje 0, la dimensión del lote, no está especificado y, por lo tanto, cualquier valor sería
aceptado). Esta capa devolverá un tensor donde la primera dimensión se transformó en 32.
Por lo tanto, esta capa sólo puede conectarse a una capa aguas abajo que espera 32
vectores dimensionales como su entrada. Al usar Keras, no tienes que preocuparte por
compatibilidad, porque las capas que agrega a sus modelos se construyen dinámicamente para
coincidir con la forma de la capa entrante. Por ejemplo, supongamos que escribe lo siguiente:
modelo = modelos.Secuencial()
model.add(capas.Dense(32, input_shape=(784,)))
modelo.add(capas.Densa(32))
Bloques iniciales
La topología de una red define un espacio de hipótesis. Quizás recuerde que en el capítulo 1 definimos
el aprendizaje automático como “la búsqueda de representaciones útiles de algunas
datos de entrada, dentro de un espacio predefinido de posibilidades, utilizando la guía de un sistema de retroalimentación
señal." Al elegir una topología de red, usted limita su espacio de posibilidades
(espacio de hipótesis) a una serie específica de operaciones tensoriales, asignando datos de entrada a
datos de salida. Lo que entonces buscará es un buen conjunto de valores para los diez grados de peso
involucrados en estas operaciones tensoriales.
Elegir la arquitectura de red adecuada es más un arte que una ciencia; y aunque existen algunas mejores
prácticas y principios en los que puede confiar, sólo la práctica puede ayudarle a convertirse en un verdadero
arquitecto de redes neuronales. Los próximos capítulos le enseñarán principios explícitos para construir
redes neuronales y le ayudarán a desarrollar la intuición sobre lo que funciona o no para problemas
específicos.
Optimizador: determina cómo se actualizará la red en función de la función de pérdida. Implementa una
variante específica de descenso de gradiente estocástico (SGD).
Una red neuronal que tiene múltiples salidas puede tener múltiples funciones de pérdida (una por salida).
Pero el proceso de descenso de gradiente debe basarse en un único valor de pérdida escalar; por lo tanto,
para las redes de pérdidas múltiples, todas las pérdidas se combinan (mediante un promedio) en una única
cantidad escalar.
Elegir la función objetivo correcta para el problema correcto es extremadamente importante: su red
tomará cualquier atajo que pueda para minimizar la pérdida; Por lo tanto, si el objetivo no se correlaciona
completamente con el éxito de la tarea en cuestión, su red terminará haciendo cosas que quizás no hubiera
deseado. Imaginemos una IA estúpida y omnipotente entrenada mediante SGD, con esta función objetivo
mal elegida: “maximizar el bienestar promedio de todos los humanos vivos”. Para facilitar su trabajo, esta IA
podría optar por matar a todos los humanos excepto a unos pocos y centrarse en el bienestar de los
restantes, porque el bienestar promedio no se ve afectado por el número de humanos que quedan. ¡Puede
que eso no sea lo que pretendías! Sólo recuerde que todas las redes neuronales que construya serán
igualmente despiadadas a la hora de reducir su función de pérdida, así que elija sabiamente el objetivo o
tendrá que afrontar efectos secundarios no deseados.
Introducción a Keras 61
Tiene soporte integrado para redes convolucionales (para visión por computadora), redes recurrentes
(para procesamiento de secuencias) y cualquier combinación de ambas.
Admite arquitecturas de red arbitrarias: modelos de múltiples entradas o múltiples salidas,
compartir capas, compartir modelos, etc. Esto significa que Keras es apropiado para
construir esencialmente cualquier modelo de aprendizaje profundo, desde una red generativa adversaria
hasta una máquina neuronal de Turing.
Keras se distribuye bajo la licencia permisiva MIT , lo que significa que puede ser libremente
utilizado en proyectos comerciales. Es compatible con cualquier versión de Python desde 2.7 a 3.6.
(a mediados de 2017).
Keras tiene más de 200.000 usuarios, desde investigadores académicos e ingenieros tanto de nuevas
empresas como de grandes empresas hasta estudiantes graduados y aficionados. Keras
se utiliza en Google, Netflix, Uber, CERN, Yelp, Square y cientos de nuevas empresas que trabajan en una amplia
gama de problemas. Keras también es un marco popular en Kaggle, el
Sitio web de competencia de aprendizaje automático, donde casi todas las competencias recientes de aprendizaje
profundo se han ganado utilizando modelos Keras.
Figura 3.2 Interés de búsqueda web en Google para diferentes marcos de aprendizaje profundo a lo largo del tiempo
Keras es una biblioteca a nivel de modelo que proporciona componentes básicos de alto nivel para
desarrollar modelos de aprendizaje profundo. No maneja operaciones de bajo nivel como la manipulación
y diferenciación de tensores. En cambio, se basa en una biblioteca de tensores especializada y bien
optimizada para hacerlo, que actúa como motor de backend de Keras. En lugar de elegir una única biblioteca
tensorial y vincular la implementación de Keras a esa biblioteca, Keras maneja el problema de forma
modular (ver figura 3.3); por lo tanto, se pueden conectar sin problemas varios motores backend diferentes
a Keras. Actualmente, las tres implementaciones de backend existentes son el backend de TensorFlow, el
backend de Theano y el backend de Microsoft Cognitive Toolkit (CNTK) . En el futuro, es probable que
Keras se extienda para funcionar con aún más motores de ejecución de aprendizaje profundo.
TensorFlow, CNTK y Theano son algunas de las principales plataformas para el aprendizaje profundo en la
actualidad. Theano (http://deeplearning.net/software/theano) es desarrollado por el laboratorio MILA de la
Universidad de Montreal, TensorFlow (www.tensorflow.org) es desarrollado por Google y CNTK (https://
github.com/Microsoft/CNTK) es desarrollado por Microsoft. Cualquier fragmento de código que escriba con
Keras se puede ejecutar con cualquiera de estos backends sin tener que cambiar nada en el código: puede
cambiar sin problemas entre los dos durante el desarrollo, lo que a menudo resulta útil; por ejemplo, si uno
de estos backends resulta ser más rápido para una tarea específica. Recomendamos utilizar el backend de
TensorFlow como predeterminado para la mayoría de sus necesidades de aprendizaje profundo, porque es
el más ampliamente adoptado, escalable y listo para producción.
A través de TensorFlow (o Theano o CNTK), Keras puede ejecutarse sin problemas tanto en CPU como
en GPU. Cuando se ejecuta en la CPU, TensorFlow envuelve una biblioteca de bajo nivel para operaciones
tensoriales llamada Eigen (http://eigen.tuxfamily.org). En GPU, TensorFlow incluye una biblioteca de
operaciones de aprendizaje profundo bien optimizadas llamada biblioteca NVIDIA CUDA Deep Neural
Network (cuDNN).
Ya has visto un ejemplo de un modelo de Keras: el ejemplo MNIST . El flujo de trabajo típico de Keras se
parece a ese ejemplo:
Introducción a Keras 63
Hay dos formas de definir un modelo: usando la clase Sequential (solo para pilas lineales de capas,
que es, con diferencia, la arquitectura de red más común) o la API funcional (para gráficos de capas
acíclicos dirigidos, que le permite construir completamente arquitecturas arbitrarias).
Como repaso, aquí hay un modelo de dos capas definido usando la clase Sequential (nota
que estamos pasando la forma esperada de los datos de entrada a la primera capa):
modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(784,))) model.add(layers.Dense(10,
activación='softmax'))
Con la API funcional, estás manipulando los tensores de datos que procesa el modelo y aplicando
capas a este tensor como si fueran funciones.
NOTA Puede encontrar una guía detallada sobre lo que puede hacer con la API funcional en
el capítulo 7. Hasta el capítulo 7, solo usaremos la clase Sequential en nuestros ejemplos de
código.
Una vez definida la arquitectura de su modelo, no importa si utilizó un modelo secuencial o la API
funcional. Todos los pasos siguientes son iguales.
El proceso de aprendizaje se configura en el paso de compilación, donde usted especifica las
funciones de optimización y pérdida que debe usar el modelo, así como las métricas que desea
monitorear durante el entrenamiento. A continuación se muestra un ejemplo con una función de pérdida
única, que es, con diferencia, el caso más común:
model.compile(optimizador=optimizadores.RMSprop(lr=0.001),
pérdida='mse',
métricas=['precisión'])
Finalmente, el proceso de aprendizaje consiste en pasar matrices Numpy de datos de entrada (y los
datos de destino correspondientes) al modelo mediante el método fit() , similar a lo que haría en Scikit
Learn y varias otras bibliotecas de aprendizaje automático:
En los próximos capítulos, desarrollará una sólida intuición sobre qué tipo de red
las arquitecturas funcionan para diferentes tipos de problemas, cómo elegir la configuración de
aprendizaje correcta y cómo modificar un modelo hasta que proporcione los resultados que desea ver. Bien
Mire tres ejemplos básicos en las secciones 3.4, 3.5 y 3.6: una clasificación de dos clases
ejemplo, un ejemplo de clasificación de muchas clases y un ejemplo de regresión.
3.3 Configuración de una estación de trabajo de aprendizaje profundo Antes de comenzar a desarrollar
aplicaciones de aprendizaje profundo, debe configurar su estación de trabajo. Es muy recomendable, aunque no estrictamente necesario, que ejecute código de
aprendizaje profundo en una GPU NVIDIA moderna. Algunas aplicaciones (en particular, el procesamiento de imágenes con redes convolucionales y el procesamiento
de secuencias con redes neuronales recurrentes) serán terriblemente lentas en la CPU, incluso una CPU multinúcleo rápida .
E incluso para las aplicaciones que, de manera realista, se pueden ejecutar en la CPU, generalmente
verá un aumento de velocidad en un factor de 5 o 10 al usar una GPU moderna. Si no desea instalar
una GPU en su máquina, también puede considerar ejecutar sus experimentos en una instancia de
GPU AWS EC2 o en Google Cloud Platform. Pero tenga en cuenta que las instancias de GPU en la
nube pueden volverse costosas con el tiempo.
Ya sea que esté ejecutando localmente o en la nube, es mejor usar una estación de trabajo
Unix. Aunque es técnicamente posible utilizar Keras en Windows (los tres backends de Keras son
compatibles con Windows), no lo recomendamos. En las instrucciones de instalación del apéndice
A, consideraremos una máquina Ubuntu. Si es usuario de Windows, la solución más sencilla para
que todo funcione es configurar un arranque dual de Ubuntu en su máquina. Puede parecer una
molestia, pero usar Ubuntu le ahorrará mucho tiempo y problemas a largo plazo.
Tenga en cuenta que para usar Keras, necesita instalar TensorFlow o CNTK o Theano (o todos
ellos, si desea poder alternar entre los tres backends). En este libro, nos centraremos en
TensorFlow, con algunas instrucciones sencillas relacionadas con Theano. No cubriremos a CNTK.
Los cuadernos de Jupyter son una excelente manera de ejecutar experimentos de aprendizaje
profundo, en particular, los numerosos ejemplos de código de este libro. Se utilizan ampliamente en
las comunidades de ciencia de datos y aprendizaje automático. Un cuaderno es un archivo generado
por la aplicación Jupyter Notebook (https://jupyter.org), que puedes editar en tu navegador. Combina
la capacidad de ejecutar código Python con capacidades enriquecidas de edición de texto para
anotar lo que estás haciendo. Un cuaderno también le permite dividir experimentos largos en partes
más pequeñas que se pueden ejecutar de forma independiente, lo que hace que el desarrollo sea
interactivo y significa que no tiene que volver a ejecutar todo el código anterior si algo sale mal al
final de un experimento.
Recomendamos usar cuadernos de Jupyter para comenzar con Keras, aunque eso no es un
requisito: también puede ejecutar scripts de Python independientes o ejecutar código desde un IDE
como PyCharm. Todos los ejemplos de código de este libro están disponibles como cuadernos de
código abierto; Puede descargarlos del sitio web del libro en www.manning.com/books/deeplearning
withpython .
Echemos un vistazo más de cerca a algunos de los compromisos que implica elegir una opción
sobre la otra.
Si vas a comprar una GPU, ¿cuál deberías elegir? Lo primero que hay que tener en cuenta es que debe ser una
GPU NVIDIA. NVIDIA es la única empresa de computación gráfica que ha invertido mucho en aprendizaje
profundo hasta ahora, y los marcos modernos de aprendizaje profundo solo pueden ejecutarse en tarjetas
NVIDIA .
A mediados de 2017, recomendamos NVIDIA TITAN Xp como la mejor tarjeta del mercado
para aprendizaje profundo. Para presupuestos más bajos, es posible que desees considerar la
GTX 1060. Si estás leyendo estas páginas en 2018 o después, tómate el tiempo para buscar
recomendaciones más recientes en línea, porque cada año salen nuevos modelos.
A partir de esta sección, asumiremos que tiene acceso a una máquina con
Keras y sus dependencias instaladas, preferiblemente con soporte para GPU . Asegúrese
Termine este paso antes de continuar. Lea las guías paso a paso en los apéndices y busque en
línea si necesita más ayuda. No faltan tutoriales sobre
cómo instalar Keras y dependencias comunes de aprendizaje profundo.
Ahora podemos sumergirnos en ejemplos prácticos de Keras.
Trabajará con el conjunto de datos IMDB : un conjunto de 50.000 reseñas altamente polarizadas de Internet
Movie Database. Están divididos en 25.000 revisiones para capacitación y 25.000 revisiones para pruebas,
cada conjunto consta de un 50% de críticas negativas y un 50% de positivas.
¿Por qué utilizar conjuntos de prueba y entrenamiento separados? ¡Porque nunca debes probar un
modelo de aprendizaje automático con los mismos datos que usaste para entrenarlo! El hecho de que un
modelo funcione bien con sus datos de entrenamiento no significa que funcionará bien con datos que
nunca ha visto; y lo que le importa es el rendimiento de su modelo con datos nuevos (porque ya conoce
las etiquetas de sus datos de entrenamiento; obviamente, no necesita su modelo para predecirlas). Por
ejemplo, es posible que su modelo termine simplemente memorizando un mapeo entre sus muestras de
entrenamiento y sus objetivos, lo que sería inútil para la tarea de predecir objetivos para datos que el
modelo nunca ha visto antes.
Repasaremos este punto con mucho más detalle en el próximo capítulo.
Al igual que el conjunto de datos MNIST , el conjunto de datos IMDB viene empaquetado con Keras.
Ya ha sido preprocesado: las reseñas (secuencias de palabras) se han convertido en secuencias de
números enteros, donde cada número entero representa una palabra específica en un diccionario.
El siguiente código cargará el conjunto de datos (cuando lo ejecute por primera vez, aproximadamente
Se descargarán 80 MB de datos en su máquina).
El argumento num_words=10000 significa que solo conservará las 10 000 palabras que aparecen con
más frecuencia en los datos de entrenamiento. Se descartarán las palabras raras. Esto le permite trabajar
con datos vectoriales de tamaño manejable.
Las variables train_data y test_data son listas de reseñas; cada revisión es una lista de índices de
palabras (que codifican una secuencia de palabras). train_labels y test_labels son listas de 0 y 1, donde 0
representa negativo y 1 representa positivo:
>>> etiquetas_tren[0] 1
Debido a que se está restringiendo a las 10 000 palabras más frecuentes, ningún índice de palabras
excederá las 10 000:
Por diversión, así es como puedes decodificar rápidamente una de estas reseñas en palabras en
inglés:
word_index es un diccionario que asigna palabras
índice_palabra = imdb.get_word_index() a un índice entero.
índice_palabra inversa = dict(
[(valor, clave) para (clave, valor) en word_index.items()]) decoded_review = '
'.join( [reverse_word_index.get(i 3, '?')
para i en train_data[0]])
Rellene sus listas para que todas tengan la misma longitud, conviértalas en un tensor de
forma entero (muestras, índices_palabras) y luego use como primera capa en su red una
capa capaz de manejar dichos tensores enteros (la capa de incrustación , que cubriremos en
detalle más adelante en el libro). Codifique sus listas en
caliente para convertirlas en vectores de 0 y 1. Esto significaría, por ejemplo, convertir la
secuencia [3, 5] en un vector de 10.000 dimensiones que sería todo 0 excepto los índices 3
y 5, que serían 1. Luego, podría usar como primera capa en su red una capa Densa , capaz
de manejar datos vectoriales de punto flotante.
Vayamos con la última solución para vectorizar los datos, lo que hará manualmente para obtener la
máxima claridad.
Establece índices
devolver resultados
específicos de resultados [i] en 1
x_train = vectorize_sequences(train_data) x_test = Datos de entrenamiento vectorizados
vectorize_sequences(test_data) Datos de prueba vectorizados
>>> x_train[0]
matriz([ 0., 1., 1., ..., 0., 0., 0.])
Ahora los datos están listos para ser introducidos en una red neuronal.
El argumento que se pasa a cada capa Densa (16) es el número de unidades ocultas de la capa. Una
unidad oculta es una dimensión en el espacio de representación de la capa.
Quizás recuerdes del capítulo 2 que cada capa densa con activación relu implementa la siguiente cadena de
operaciones tensoriales:
Tener 16 unidades ocultas significa que la matriz de peso W tendrá forma (input_dimension, 16): el producto
escalar con W proyectará los datos de entrada en un espacio de representación de 16 dimensiones (y luego
agregará el vector de sesgo b y aplicará la operación relu ). Puedes entender intuitivamente la dimensionalidad
de tu espacio de representación como "cuánta libertad estás permitiendo que tenga la red cuando aprende
representaciones internas". Tener más unidades ocultas (un espacio de representación de mayor dimensión) le
permite a su red aprender representaciones más complejas, pero hace que la red sea más costosa desde el
punto de vista computacional y puede llevar a aprender patrones no deseados (patrones que mejorarán el
rendimiento de los datos de entrenamiento pero que mejorarán el rendimiento de los datos de entrenamiento).
no en los datos de la prueba).
Hay dos decisiones de arquitectura clave que se deben tomar acerca de dicha pila de capas densas :
En el capítulo 4, aprenderá principios formales que le guiarán a la hora de tomar estas decisiones. Por el
momento, tendrás que confiarme la siguiente elección de arquitectura:
Las capas intermedias usarán relu como función de activación, y la capa final usará una activación sigmoidea
para generar una probabilidad (una puntuación entre 0 y 1,
indicando la probabilidad de que la muestra tenga el objetivo “1”: probabilidad de que la revisión
sea positiva). Un relu (unidad lineal rectificada) es una función destinada a poner a cero valores
negativos (ver figura 3.4), mientras que un sigmoide "aplasta" valores arbitrarios en el intervalo
[0, 1] (ver figura 3.5), generando algo que puede interpretarse. como una probabilidad.
Salida
(probabilidad)
Denso (unidades=1)
Denso (unidades=16)
Denso (unidades=16)
Secuencial
La Figura 3.6 muestra cómo se ve la red. Y aquí está la implementación de Keras, similar
al ejemplo de MNIST que vio anteriormente.
modelo = modelos.Sequential()
model.add(layers.Dense(16, activación='relu', input_shape=(10000,))) model.add(layers.Dense(16,
activación='relu')) modelo. agregar(capas.Dense(1, activación='sigmoide'))
Para obtener acceso a un espacio de hipótesis mucho más rico que se beneficiaría de
representaciones profundas, necesita una función de activación o no linealidad. relu es la función
de activación más popular en el aprendizaje profundo, pero hay muchos otros candidatos, todos
ellos con nombres igualmente extraños: prelu, elu, etc.
Pérdida de entropía cruzada binaria . No es la única opción viable: podrías usar, por ejemplo,
mean_squared_error. Pero la entropía cruzada suele ser la mejor opción cuando se trata de modelos
que generan probabilidades. La entropía cruzada es una cantidad del campo de la Teoría de la
Información que mide la distancia entre distribuciones de probabilidad o, en este caso, entre la
distribución de verdad fundamental y sus predicciones.
Este es el paso en el que configura el modelo con el optimizador rmsprop y la función de pérdida
binario_crossentropy . Tenga en cuenta que también controlará la precisión durante el entrenamiento.
model.compile(optimizador='rmsprop',
pérdida='binary_crossentropy',
métricas=['exactitud'])
Estás pasando el optimizador, la función de pérdida y las métricas como cadenas, lo cual es
posible porque rmsprop, binario_crossentropy y precision están empaquetados como parte de Keras.
A veces es posible que desee configurar los parámetros de su optimizador o pasar una función de
pérdida o función métrica personalizada. Lo primero se puede hacer pasando una instancia de clase
de optimizador como argumento del optimizador , como se muestra en el listado 3.5; esto último se
puede hacer pasando objetos de función como argumentos de pérdida y/o métricas , como se muestra
en el listado 3.6.
model.compile(optimizador=optimizadores.RMSprop(lr=0.001),
pérdida='binary_crossentropy',
métricas=['precisión'])
model.compile(optimizador=optimizadores.RMSprop(lr=0.001),
pérdida=pérdida.binary_crossentropy,
métricas=[metrics.binary_accuracy])
Ahora entrenará el modelo durante 20 épocas (20 iteraciones sobre todas las muestras en los
tensores x_train e y_train ), en minilotes de 512 muestras. Al mismo tiempo, controlará la pérdida y
la precisión de las 10 000 muestras que separó. Para ello, pase los datos de validación como el
argumento validation_data .
model.compile(optimizador='rmsprop',
pérdida='binary_crossentropy', métricas=['acc'])
datos_validación=(x_val,
y_val))
En la CPU, esto tomará menos de 2 segundos por época; el entrenamiento finaliza en 20 segundos.
Al final de cada época, hay una pequeña pausa mientras el modelo calcula su pérdida y precisión en
las 10.000 muestras de los datos de validación.
Tenga en cuenta que la llamada a model.fit() devuelve un objeto Historial . Este objeto tiene un
historial de miembros , que es un diccionario que contiene datos sobre todo lo que sucedió durante
el entrenamiento. Veámoslo:
El diccionario contiene cuatro entradas: una por métrica que se estaba monitoreando durante el
entrenamiento y durante la validación. En los dos listados siguientes, usemos Matplotlib para trazar
la pérdida de entrenamiento y validación en paralelo (ver figura 3.7), así como la precisión del
entrenamiento y la validación (ver figura 3.8). Tenga en cuenta que sus propios resultados pueden
variar ligeramente debido a una inicialización aleatoria diferente de su red.
plt.mostrar()
plt.mostrar()
Como puede ver, la pérdida de entrenamiento disminuye con cada época y la precisión del
entrenamiento aumenta con cada época. Eso es lo que se esperaría al ejecutar la optimización de
descenso de gradiente: la cantidad que intenta minimizar debería ser menor con cada iteración. Pero
ese no es el caso de la pérdida de validación y la precisión: parecen alcanzar su punto máximo en la
cuarta época. Este es un ejemplo de lo que advertimos anteriormente: un modelo que funciona mejor
con los datos de entrenamiento no es necesariamente un modelo que funcionará mejor con datos que
nunca antes se ha visto. En términos precisos, lo que estás viendo es un sobreajuste: después de la
segunda época, estás optimizando demasiado los datos de entrenamiento y terminas aprendiendo
representaciones que son específicas de los datos de entrenamiento y no se generalizan a datos
fuera de ellos. el conjunto de entrenamiento.
En este caso, para evitar el sobreajuste, puedes dejar de entrenar después de tres épocas. En
general, puede utilizar una variedad de técnicas para mitigar el sobreajuste, que cubriremos en el
capítulo 4.
Entrenemos una nueva red desde cero durante cuatro épocas y luego evalúémosla con los datos
de prueba.
modelo = modelos.Sequential()
model.add(layers.Dense(16, activación='relu', input_shape=(10000,))) model.add(layers.Dense(16, activación='relu'))
modelo. agregar(capas.Dense(1, activación='sigmoide'))
model.compile(optimizador='rmsprop',
pérdida='binary_crossentropy',
métricas=['exactitud'])
>>> resultados
[0,2929924130630493, 0,88327999999999995]
Este enfoque bastante ingenuo logra una precisión del 88%. Con enfoques de última generación,
debería poder acercarse al 95%.
3.4.5 Uso de una red entrenada para generar predicciones sobre nuevos datos
Después de haber entrenado una red, querrás utilizarla en un entorno práctico. Puede generar la
probabilidad de que las reseñas sean positivas utilizando el método de predicción :
>>> model.predict(x_test)
matriz([[ 0.98006207] [ 0.99758697]
[ 0.99975556]
...,
[0,82167041]
[0,02885115]
[0.65371346]], dtype=float32)
Como puede ver, la red tiene confianza para algunas muestras (0,99 o más, o 0,01 o menos), pero menos
confianza para otras (0,6, 0,4).
Usaste dos capas ocultas. Intente usar una o tres capas ocultas y vea cómo
hacerlo afecta la validación y la precisión de la prueba.
Intente usar capas con más unidades ocultas o menos unidades ocultas: 32 unidades, 64 unidades,
etcétera.
3.4.7 Conclusión
Esto es lo que deberías aprender de este ejemplo:
Por lo general, es necesario realizar bastante preprocesamiento de los datos sin procesar para
poder introducirlos (como tensores) en una red neuronal. Las secuencias de palabras se pueden
codificar como vectores binarios, pero también existen otras opciones de codificación.
Las pilas de capas densas con activaciones relu pueden resolver una amplia gama de problemas
(incluida la clasificación de sentimientos) y probablemente las usará con frecuencia. En
un problema de clasificación binaria (dos clases de salida), su red debe terminar con una capa Densa
con una unidad y una activación sigmoidea : la salida de su red debe ser un escalar entre 0 y 1,
codificando una probabilidad. Con una salida sigmoidea escalar de este tipo en un
problema de clasificación binaria, la función de pérdida que debe utilizar es binario_crossentropy.
El optimizador rmsprop generalmente es una buena opción,
sea cual sea su problema. Eso es una cosa menos de la que debes preocuparte. A medida que
mejoran sus datos de entrenamiento, las redes neuronales
eventualmente comienzan a sobreajustarse y terminan obteniendo resultados cada vez peores en
datos que nunca antes habían visto. Asegúrese de monitorear siempre el rendimiento con datos
que estén fuera del conjunto de entrenamiento.
Trabajará con el conjunto de datos de Reuters, un conjunto de noticias breves y sus temas, publicado por Reuters en 1986. Es
un conjunto de datos de juguete simple y ampliamente utilizado para la clasificación de texto. Hay 46 temas diferentes; Algunos
temas están más representados que otros, pero cada tema tiene al menos 10 ejemplos en el conjunto de capacitación.
Al igual que IMDB y MNIST, el conjunto de datos de Reuters viene empaquetado como parte de Keras. Vamos a ver.
Al igual que con el conjunto de datos IMDB , el argumento num_words=10000 restringe los datos a las 10.000 palabras que
aparecen con más frecuencia en los datos.
Tienes 8982 ejemplos de entrenamiento y 2246 ejemplos de prueba:
Al igual que con las revisiones de IMDB , cada ejemplo es una lista de números enteros (índices de palabras):
Tenga en cuenta que los índices están compensados por 3 porque 0, 1 y 2 son
índices reservados para "relleno", "inicio de secuencia" y "desconocido".
La etiqueta asociada con un ejemplo es un número entero entre 0 y 45: un índice de tema:
>>> etiquetas_tren[10] 3
devolver resultados
Datos de entrenamiento vectorizados
x_train = vectorize_sequences(train_data) x_test =
vectorize_sequences(test_data) Datos de prueba vectorizados
Para vectorizar las etiquetas, hay dos posibilidades: puede convertir la lista de etiquetas como un
tensor entero o puede usar codificación onehot. La codificación onehot es un formato ampliamente
utilizado para datos categóricos, también llamado codificación categórica. Para obtener una
explicación más detallada de la codificación onehot, consulte la sección 6.1. En este caso, la
codificación onehot de las etiquetas consiste en incrustar cada etiqueta como un vector todo cero
con un 1 en el lugar del índice de la etiqueta. He aquí un ejemplo:
devolver resultados
Etiquetas de entrenamiento vectorizadas
one_hot_train_labels = to_one_hot(entren_etiquetas) one_hot_test_labels =
to_one_hot(prueba_etiquetas) Etiquetas de prueba vectorizadas
Tenga en cuenta que existe una forma integrada de hacer esto en Keras, que ya ha visto en acción
en el ejemplo de MNIST :
Como es relevante para el problema de clasificación, esta información nunca podrá ser recuperada por capas
posteriores: cada capa puede convertirse potencialmente en un cuello de botella de información. En el ejemplo
anterior, utilizó capas intermedias de 16 dimensiones, pero un espacio de 16 dimensiones puede ser demasiado
limitado para aprender a separar 46 clases diferentes: capas tan pequeñas pueden actuar como cuellos de botella
de información, eliminando permanentemente información relevante.
Por esta razón usarás capas más grandes. Vamos con 64 unidades.
modelo = modelos.Sequential()
model.add(layers.Dense(64, activación='relu', input_shape=(10000,))) model.add(layers.Dense(64,
activación='relu')) modelo. agregar(capas.Dense(46, activación='softmax'))
Hay otras dos cosas que debes tener en cuenta sobre esta arquitectura:
Termina la red con una capa Densa de tamaño 46. Esto significa que para cada muestra de entrada, la red
generará un vector de 46 dimensiones. Cada entrada en este vector (cada dimensión) codificará una clase
de salida diferente. La última capa utiliza una activación softmax . Viste este
patrón en el ejemplo MNIST . Significa que la red generará una distribución de probabilidad entre las 46 clases
de salida diferentes; para cada muestra de entrada, la red producirá un vector de salida de 46 dimensiones,
donde salida[i] es la probabilidad de que la muestra pertenezca a la clase i. Las 46 puntuaciones sumarán 1.
La mejor función de pérdida para usar en este caso es categorical_crossentropy. Mide la distancia entre dos
distribuciones de probabilidad: aquí, entre la distribución de probabilidad generada por la red y la verdadera
distribución de las etiquetas. Al minimizar la distancia entre estas dos distribuciones, entrena a la red para generar
algo lo más cercano posible a las etiquetas verdaderas.
model.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy',
métricas=['precisión'])
datos_validación=(x_val,
y_val))
Y finalmente, visualicemos sus curvas de pérdida y precisión (ver figuras 3.9 y 3.10).
plt.mostrar()
plt.mostrar()
La red comienza a sobreadaptarse después de nueve épocas. Entrenemos una nueva red desde cero durante nueve épocas
y luego evaluémosla en el conjunto de prueba.
modelo = modelos.Sequential()
model.add(layers.Dense(64, activación='relu', input_shape=(10000,))) model.add(layers.Dense(64, activación='relu'))
modelo. agregar(capas.Dense(46, activación='softmax'))
model.compile(optimizador='rmsprop',
pérdida = 'categorical_crossentropy', métricas =
['exactitud']) model.fit (partial_x_train,
parcial_y_train, épocas = 9, tamaño de lote
= 512, datos de validación
= (val_x, val_y))
>>> resultados
[0,9565213431445807, 0,79697239536954589]
Este enfoque alcanza una precisión de ~80%. Con un problema de clasificación binaria balanceada, la precisión alcanzada
por un clasificador puramente aleatorio sería del 50%. Pero en este caso está más cerca del 19%, por lo que los resultados
parecen bastante buenos, al menos en comparación con
una línea de base aleatoria:
predicciones = modelo.predict(x_test)
>>> np.argmax(predicciones[0]) 4
Lo único que cambiaría este enfoque es la elección de la función de pérdida. La función de pérdida
utilizada en el listado 3.21, categorical_crossentropy, espera que las etiquetas sigan una codificación
categórica. Con etiquetas de números enteros, debes usar sparse_categorical_
entropía cruzada:
model.compile(optimizer='rmsprop',
loss='sparse_categorical_crossentropy', metrics=['acc'])
Esta nueva función de pérdida sigue siendo matemáticamente la misma que categorical_crossentropy;
simplemente tiene una interfaz diferente.
model.compile(optimizador='rmsprop',
pérdida = 'categorical_crossentropy', métricas =
['exactitud']) model.fit
(partial_x_train, parcial_y_train, épocas
= 20, tamaño de lote =
128, datos de
validación = (val_x,
val_y))
La red ahora alcanza un máximo de ~71% de precisión de validación, una caída absoluta del 8%. Esta
caída se debe principalmente al hecho de que estás intentando comprimir una gran cantidad de información
(suficiente información para recuperar los hiperplanos de separación de 46 clases) en un espacio intermedio
que tiene dimensiones demasiado bajas. La red es capaz de meter la mayor parte de la información
necesaria en estas representaciones de ocho dimensiones, pero no toda.
3.5.9 Conclusión
Esto es lo que deberías aprender de este ejemplo:
Si está intentando clasificar puntos de datos entre N clases, su red debería terminar
con una capa densa de tamaño N.
En un problema de clasificación multiclase y de etiqueta única, su red debe terminar con una
activación softmax para que genere una distribución de probabilidad sobre las N clases de salida.
La entropía
cruzada categórica es casi siempre la función de pérdida que debe utilizar para este tipo de problemas.
Minimiza la distancia entre las distribuciones de probabilidad generadas por la red y la distribución
real de los objetivos. Hay dos formas de manejar etiquetas en la
clasificación multiclase:
– Codificar las etiquetas mediante codificación categórica (también conocida como codificación one
hot) y utilizar categorical_crossentropy como función de pérdida
– Codificar las etiquetas como números enteros y usar sparse_categorical_crossentropy
función de pérdida
Si necesita clasificar datos en una gran cantidad de categorías, debe evitar crear cuellos de botella
de información en su red debido a capas intermedias que son demasiado pequeñas.
Intentará predecir el precio medio de las viviendas en un determinado suburbio de Boston a mediados
de la década de 1970, teniendo en cuenta datos sobre el suburbio en ese momento, como la tasa de
criminalidad, la tasa del impuesto local a la propiedad, etc. El conjunto de datos que utilizará tiene una
diferencia interesante con respecto a los dos ejemplos anteriores. Tiene relativamente pocos puntos de
datos: sólo 506, divididos entre 404 muestras de entrenamiento y 102 muestras de prueba. Y cada
característica de los datos de entrada (por ejemplo, la tasa de criminalidad) tiene una escala diferente.
Por ejemplo, algunos valores son proporciones, que toman valores entre 0 y 1; otros toman valores
entre 1 y 12, otros entre 0 y 100, y así sucesivamente.
Como puede ver, tiene 404 muestras de capacitación y 102 muestras de prueba, cada una con 13
características numéricas, como tasa de criminalidad per cápita, número promedio de habitaciones por
vivienda, accesibilidad a carreteras, etc.
Los objetivos son los valores medios de las viviendas ocupadas por sus propietarios, en miles de
dólares:
>>> entrenar_objetivos
[ 15.2, 42.3, 50. ... 19.4, 19.4, 29.1]
Los precios suelen oscilar entre 10.000 y 50.000 dólares. Si esto suena barato, recuerde que esto fue
a mediados de la década de 1970 y que estos precios no están ajustados a la inflación.
Tenga en cuenta que las cantidades utilizadas para normalizar los datos de prueba se calculan utilizando los
datos de entrenamiento. Nunca debe utilizar en su flujo de trabajo ninguna cantidad calculada a partir de los
datos de prueba, ni siquiera para algo tan simple como la normalización de datos.
La red termina con una sola unidad y sin activación (será una capa lineal). Esta es una configuración típica
para la regresión escalar (una regresión en la que se intenta predecir un único valor continuo). La aplicación
de una función de activación limitaría el rango que puede tomar la salida; por ejemplo, si aplicara una función
de activación sigmoidea a la última capa, la red solo podría aprender a predecir valores entre 0 y 1. Aquí,
debido a que la última capa es puramente lineal, la red es libre de aprender a predecir valores en cualquier
rango. .
Tenga en cuenta que compila la red con la función de pérdida mse : error cuadrático medio,
el cuadrado de la diferencia entre las predicciones y los objetivos. Este es un proceso ampliamente
Se utilizó la función de pérdida para problemas de regresión.
También estás monitoreando una nueva métrica durante el entrenamiento: el error absoluto medio (MAE). Es
el valor absoluto de la diferencia entre las predicciones y los objetivos. Para
Por ejemplo, un MAE de 0,5 en este problema significaría que sus predicciones están equivocadas en $500
de media.
Validación
Doblar 1 Validación Capacitación Capacitación
puntuación #1
Validación
Doblar 3 Validación Capacitación Validación
puntuación #3
k=4
num_val_samples = len(train_data) // k
núm_épocas = 100
todas las puntuaciones = []
para i en rango(k):
print('procesando pliegue #', i) val_data =
train_data[i * num_val_samples: (i + 1) * num_val_samples] val_targets = train_targets[i * num_val_samples:
(i + 1) * num_val_samples]
parcial_train_data = np.concatenate(
[train_data[:i * num_val_samples], train_data[(i + 1) *
num_val_samples:]], eje=0) Construye el modelo Keras (ya
compilado)
objetivos_tren_parciales = np.concatenate(
[train_targets[:i * num_val_samples], train_targets[(i + 1) Entrena el modelo (en
* num_val_samples:]], eje=0) modo silencioso,
detallado = 0)
modelo = build_model()
model.fit(partial_train_data, parcial_train_targets,
épocas=num_épocas, tamaño_lote=1, detallado=0)
val_mse, val_mae = model.evaluate(val_data, val_targets, detallado=0) all_scores.append(val_mae)
De hecho, las diferentes ejecuciones muestran puntuaciones de validación bastante diferentes, de 2,6 a 3,2.
El promedio (3,0) es una métrica mucho más confiable que cualquier puntuación única; ese es el objetivo
de la validación cruzada Kfold. En este caso, estás perdiendo $3,000 en promedio, lo cual es significativo
considerando que los precios oscilan entre $10,000 y $50,000.
Intentemos entrenar la red un poco más: 500 épocas. Para mantener un registro de qué tan bien
funciona el modelo en cada época, modificará el ciclo de entrenamiento para guardar el registro de
puntuación de validación por época.
num_epochs = 500
Prepara los datos de validación: datos
all_mae_histories = [] para i en
de la partición #k
rango(k): print('procesando
pliegue #', i) val_data = train_data[i *
num_val_samples: (i + 1) * num_val_samples] val_targets = train_targets[i * num_val_samples: ( i + 1) *
num_val_muestras]
parcial_train_data = np.concatenate(
Prepara los datos de
[train_data[:i * num_val_samples], train_data[(i + 1) *
entrenamiento: datos de
num_val_samples:]], eje=0)
todas las demás particiones.
objetivos_tren_parciales = np.concatenate(
[train_targets[:i * num_val_samples], train_targets[(i + 1) * Construye el modelo Keras
num_val_samples:]], eje=0) (ya compilado)
Luego puede calcular el promedio de las puntuaciones MAE por época para todos los pliegues.
promedio_mae_historia = [
np.mean([x[i] para x en all_mae_histories]) para i en el rango(num_epochs)]
Puede resultar un poco difícil ver la trama debido a problemas de escala y a una variación relativamente
alta. Hagamos lo siguiente:
Omita los primeros 10 puntos de datos, que están en una escala diferente al resto de la curva.
Reemplace cada punto con una media móvil exponencial de los puntos anteriores,
para obtener una curva suave.
Listado 3.31 Trazado de puntuaciones de validación, excluyendo los primeros 10 puntos de datos
smooth_mae_history = smooth_curve(promedio_mae_history[10:])
Según este gráfico, la validación MAE deja de mejorar significativamente después de 80 épocas.
Pasado ese punto, empiezas a sobreadaptarte.
Una vez que haya terminado de ajustar otros parámetros del modelo (además del número de épocas,
también puede ajustar el tamaño de las capas ocultas), puede entrenar un modelo de producción final con todos
los datos de entrenamiento, con los mejores parámetros. y luego observe su rendimiento en los datos de prueba.
>>> prueba_mae_score
2.5532484335057877
3.6.5 Conclusión
Esto es lo que deberías aprender de este ejemplo:
La regresión se realiza utilizando funciones de pérdida diferentes a las que usamos para la clasificación. El
error cuadrático medio (MSE) es una función de pérdida comúnmente utilizada para la regresión.
De manera similar, las métricas de evaluación que se utilizarán para la regresión difieren de las utilizadas para
clasificación; Naturalmente, el concepto de precisión no se aplica a la regresión. A
puede manejar los tipos más comunes de tareas de aprendizaje automático en datos vectoriales:
clasificación binaria, clasificación multiclase y regresión escalar. Las secciones de “Resumen”
anteriores en este capítulo resumen los puntos importantes que ha aprendido con respecto a este
tipo de tareas. Por lo general, necesitará preprocesar los datos sin procesar antes de
Si no tiene muchos datos de entrenamiento, use una red pequeña con solo uno o
Dos capas ocultas, para evitar un sobreajuste severo.
Si sus datos se dividen en muchas categorías, puede causar que la información
cuellos de botella si hace las capas intermedias demasiado pequeñas.
La regresión utiliza diferentes funciones de pérdida y diferentes métricas de evaluación.
que la clasificación.
Cuando trabaja con pocos datos, la validación Kfold puede ayudar a evaluar de manera confiable su
modelo.
Fundamentos de
aprendizaje automático
Ingeniería de características
Abordar el sobreajuste
Después de los tres ejemplos prácticos del capítulo 3, debería empezar a sentirse familiarizado con
cómo abordar los problemas de clasificación y regresión utilizando redes neuronales, y habrá sido
testigo del problema central del aprendizaje automático: el sobreajuste.
Este capítulo formalizará parte de su nueva intuición en una base conceptual sólida.
marco para atacar y resolver problemas de aprendizaje profundo. Consolidaremos todo
de estos conceptos: evaluación de modelos, preprocesamiento de datos e ingeniería de características,
y abordar el sobreajuste, en un flujo de trabajo detallado de siete pasos para abordar cualquier
tarea de aprendizaje automático.
93
anteriores, se ha familiarizado con tres tipos específicos de problemas de aprendizaje automático: clasificación
binaria, clasificación multiclase y regresión escalar. Los tres son casos de aprendizaje supervisado, donde el objetivo
es aprender la relación entre los insumos y los objetivos de la formación.
El aprendizaje supervisado es sólo la punta del iceberg: el aprendizaje automático es un campo vasto con una
taxonomía de subcampos compleja. Los algoritmos de aprendizaje automático generalmente se dividen en cuatro
categorías amplias, que se describen en las siguientes secciones.
Generación de secuencia: dada una imagen, predice un título que la describe. La generación de secuencias
a veces se puede reformular como una serie de problemas de clasificación (como predecir repetidamente
una palabra o token en una secuencia).
Predicción del árbol de sintaxis: dada una oración, predice su descomposición en una sintaxis.
árbol.
Detección de objetos: dada una imagen, dibuje un cuadro delimitador alrededor de ciertos objetos dentro de
la imagen. Esto también se puede expresar como un problema de clasificación (dados muchos cuadros
delimitadores candidatos, clasifique el contenido de cada uno) o como un problema conjunto de clasificación
y regresión, donde las coordenadas del cuadro delimitador se predicen mediante regresión vectorial.
Segmentación de imágenes: dada una imagen, dibuje una máscara a nivel de píxel en un objeto específico.
etiquetas anotadas por humanos: puede considerarlo como un aprendizaje supervisado sin ningún ser
humano involucrado. Todavía hay etiquetas involucradas (porque el aprendizaje tiene que ser supervisado
por algo), pero se generan a partir de los datos de entrada, normalmente utilizando un algoritmo heurístico.
Por ejemplo, los codificadores automáticos son un ejemplo bien conocido de aprendizaje autosupervisado,
donde los objetivos generados son la entrada, sin modificaciones. De la misma manera, intentar predecir el
siguiente fotograma de un vídeo, teniendo en cuenta fotogramas pasados, o la siguiente palabra en un texto,
teniendo en cuenta las palabras anteriores, son ejemplos de aprendizaje autosupervisado (aprendizaje
supervisado temporalmente, en este caso). : la supervisión proviene de datos de entrada futuros). Tenga en
cuenta que la distinción entre aprendizaje supervisado, autosupervisado y no supervisado a veces puede
resultar borrosa; estas categorías son más bien un continuo sin fronteras sólidas. El aprendizaje
autosupervisado puede reinterpretarse como aprendizaje supervisado o no supervisado, dependiendo de si
se presta atención al mecanismo de aprendizaje o al contexto de su aplicación.
NOTA En este libro, nos centraremos específicamente en el aprendizaje supervisado, porque es,
con diferencia, la forma dominante de aprendizaje profundo en la actualidad, con una amplia gama
de aplicaciones industriales. También veremos más brevemente el aprendizaje autosupervisado
en capítulos posteriores.
(continuado)
Error de predicción o valor de pérdida: una medida de la distancia entre la predicción del
modelo y el objetivo. Clases: un
conjunto de posibles etiquetas para elegir en un problema de clasificación.
Por ejemplo, al clasificar imágenes de perros y gatos, "perro" y "gato" son las dos clases.
Evaluar un modelo siempre se reduce a dividir los datos disponibles en tres conjuntos: entrenamiento,
validación y prueba. Usted entrena con los datos de entrenamiento y evalúa su modelo con los datos de
validación. Una vez que su modelo esté listo para el horario de máxima audiencia, lo prueba una última vez
con los datos de prueba.
Quizás se pregunte, ¿por qué no tener dos conjuntos: un conjunto de entrenamiento y un conjunto de prueba? Entrenarías
con los datos de entrenamiento y evaluarías con los datos de prueba. ¡Mucho más sencillo!
La razón es que desarrollar un modelo siempre implica ajustar su configuración: por ejemplo, elegir el
número de capas o el tamaño de las capas (llamados hiperparámetros del modelo, para distinguirlos de los
parámetros, que son los parámetros de la red). pesos). Este ajuste se realiza utilizando como señal de
retroalimentación el rendimiento del modelo en los datos de validación. En esencia, este ajuste es una forma
de aprendizaje: una búsqueda de una buena configuración en algún espacio de parámetros. Como resultado,
ajustar la configuración del modelo en función de su rendimiento en el conjunto de validación puede resultar
rápidamente en un sobreajuste del conjunto de validación, aunque su modelo nunca se entrene directamente
en él.
Un elemento central de este fenómeno es la noción de fuga de información. Cada vez que ajusta un
hiperparámetro de su modelo en función del rendimiento del modelo en el conjunto de validación, cierta
información sobre los datos de validación se filtra en el modelo. Si hace esto sólo una vez, para un
parámetro, se filtrarán muy pocos bits de información y su conjunto de validación seguirá siendo confiable
para evaluar el modelo. Pero si repite esto muchas veces (ejecutando un experimento, evaluando el conjunto
de validación y modificando su modelo como resultado), filtrará una cantidad cada vez más significativa de
información sobre el conjunto de validación en el modelo.
Al final del día, terminará con un modelo que funciona artificialmente bien con los datos de validación,
porque para eso lo optimizó. A usted le importa el rendimiento de datos completamente nuevos, no los
datos de validación, por lo que necesita utilizar un conjunto de datos completamente diferente y nunca
antes visto para evaluar el modelo: el conjunto de datos de prueba. Su modelo no debería haber tenido
acceso a ninguna información sobre el conjunto de prueba, ni siquiera indirectamente.
Si se ha ajustado algo del modelo en función del rendimiento del equipo de prueba, entonces su
medida de generalización será defectuosa.
Dividir sus datos en conjuntos de entrenamiento, validación y prueba puede parecer sencillo,
pero hay algunas formas avanzadas de hacerlo que pueden resultar útiles cuando se dispone de pocos datos.
disponible. Repasemos tres recetas de evaluación clásicas: validación de reserva simple, validación Kfold y
validación Kfold iterada con barajado.
Separe una fracción de sus datos como conjunto de prueba. Entrene con los datos restantes y
evaluar en el conjunto de prueba. Como vio en las secciones anteriores, para evitar fugas de información, no
debe ajustar su modelo basándose en el conjunto de prueba y, por lo tanto,
También debe reservar un conjunto de validación.
Tendió
núm_validación_muestras = 10000
Generalmente es
np.aleatorio.shuffle(datos) apropiado mezclar los datos.
Define el
conjunto de validación.
datos_validación = datos[:num_validation_samples]
datos = datos[num_validation_samples:]
Define el conjunto de entrenamiento.
datos_entrenamiento = datos[:]
Este es el protocolo de evaluación más simple y adolece de un defecto: si se aportan pocos datos
disponible, entonces sus conjuntos de validación y prueba pueden contener muy pocas muestras para ser
estadísticamente representativos de los datos disponibles. Esto es fácil de reconocer: si son diferentes al azar
mezclar rondas de datos antes de dividirlos termina arrojando medidas muy diferentes
del rendimiento del modelo, entonces estás teniendo este problema. Validación Kfold e iterada.
La validación Kfold son dos formas de abordar esto, como se analiza a continuación.
VALIDACIÓN KFOLD
Con este enfoque, divide sus datos en K particiones de igual tamaño. Para cada partición i, entrene un modelo en
las K – 1 particiones restantes y evalúelo en la partición i.
Su puntuación final son entonces los promedios de las puntuaciones K obtenidas. Este método es útil
cuando el rendimiento de su modelo muestra una variación significativa según su división de prueba de tren. Al
igual que la validación de reserva, este método no le exime de utilizar un conjunto de validación distinto para la
calibración del modelo.
Esquemáticamente, la validación cruzada Kfold se parece a la figura 4.2. El listado 4.2 muestra un sencillo
implementación.
Validación
Doblar 1 Validación Capacitación Capacitación
puntuación #1
Validación
Doblar 3 Validación Capacitación Validación
puntuación #3
k=4
num_validation_samples = len(datos) // k
np.aleatorio.shuffle(datos)
Selecciona la partición de
puntuaciones_validación = [] datos de validación.
para doblar en rango (k):
datos_validación = datos[num_validation_samples * fold:
num_validation_samples * (pliegue + 1)]
datos_entrenamiento = datos[:num_validation_samples * fold] +
datos[num_validation_samples * (fold + 1):]
Utiliza el resto de los datos como datos
modelo = get_model() de entrenamiento. Tenga en cuenta que
modelo.train(datos_entrenamiento) el operador + es una concatenación de
puntuación_validación = modelo.evaluar(datos_validación) listas, no una suma.
validation_scores.append(validation_scores)
Crea una instancia nueva
del modelo (no entrenado)
Representatividad de los datos: desea que tanto su conjunto de entrenamiento como su conjunto de
prueba sean representativos de los datos disponibles. Por ejemplo, si intentas clasificar imágenes de
dígitos, y usted está comenzando a partir de una serie de muestras donde las muestras están
ordenados por su clase, tomando el primer 80% de la matriz como su conjunto de entrenamiento y
el 20% restante como su conjunto de prueba dará como resultado que su conjunto de entrenamiento contenga
solo las clases 0 a 7, mientras que su conjunto de prueba contiene solo las clases 8 a 9. Esto parece
Un error ridículo, pero sorprendentemente común. Por esta razón, normalmente
debería mezclar aleatoriamente sus datos antes de dividirlos en conjuntos de entrenamiento y prueba.
La flecha del tiempo: si intenta predecir el futuro teniendo en cuenta el pasado (por ejemplo, el clima de
mañana, movimientos bursátiles, etc.), no debe mezclar aleatoriamente sus datos antes de dividirlos,
porque al hacerlo así creará un
Fuga temporal: su modelo se entrenará efectivamente con datos del futuro. En
En tales situaciones, siempre debe asegurarse de que todos los datos en su conjunto de prueba sean posteriores.
a los datos del conjunto de entrenamiento.
Redundancia en sus datos: si algunos puntos de datos aparecen dos veces (bastante
común con los datos del mundo real), luego barajar los datos y dividirlos en un
conjunto de entrenamiento y un conjunto de validación dará como resultado una redundancia entre el conjunto de entrenamiento
VECTORIZACIÓN
Todas las entradas y objetivos de una red neuronal deben ser tensores de datos de punto flotante (o, en casos
específicos, tensores de números enteros). Independientemente de los datos que necesite procesar (sonido,
imágenes, texto), primero debe convertirlos en tensores, un paso llamado vectorización de datos. Por ejemplo, en
los dos ejemplos anteriores de clasificación de texto, comenzamos a partir de texto representado como listas de
números enteros (que representan secuencias de palabras) y usamos codificación onehot para convertirlos en un
tensor de datos float32 . En los ejemplos de clasificación de dígitos y predicción de precios de viviendas, los datos
ya venían en forma vectorizada, por lo que pudo omitir este paso.
NORMALIZACIÓN DE VALORES
En el ejemplo de clasificación de dígitos, comenzó a partir de datos de imagen codificados como números enteros
en el rango de 0 a 255, codificando valores en escala de grises. Antes de introducir estos datos en su red, tenía
que convertirlos en float32 y dividirlos por 255 para terminar con valores de punto flotante en el rango de 0 a 1. De
manera similar, al predecir los precios de la vivienda, se partía de características que tomaban una variedad de
rangos: algunas características tenían pequeños valores de punto flotante, otras tenían valores enteros bastante
grandes. Antes de introducir estos datos en su red, tenía que normalizar cada característica de forma independiente
para que tuviera una desviación estándar de 1 y una media de 0.
En general, no es seguro introducir en una red neuronal datos que toman valores relativamente grandes (por
ejemplo, enteros de varios dígitos, que son mucho mayores que los valores iniciales tomados por los pesos de una
red) o datos que son heterogéneos. (por ejemplo, datos en los que una característica está en el rango de 0 a 1 y
otra en el rango de 100 a 200). Hacerlo puede desencadenar grandes actualizaciones de gradiente que impedirán
que la red converja. Para facilitar el aprendizaje de su red, sus datos deben tener las siguientes características:
Tome valores pequeños: normalmente, la mayoría de los valores deben estar en el rango de
0 a 1. Ser homogéneo: es decir, todas las características deben tomar valores aproximadamente del mismo
rango.
Además, la siguiente práctica de normalización más estricta es común y puede ayudar, aunque no siempre
es necesaria (por ejemplo, no hizo esto en el ejemplo de clasificación de dígitos):
Normalice cada característica de forma independiente para que tenga una media de
0. Normalice cada característica de forma independiente para que tenga una desviación estándar de 1.
x = x.media(eje=0) x /=
Suponiendo que x es una matriz de datos
x.std(eje=0)
2D de forma (muestras, características)
Es posible que a veces le falten valores en sus datos. Por ejemplo, en el ejemplo del precio de la vivienda,
la primera característica (la columna del índice 0 en los datos) fue la tasa de criminalidad per cápita. ¿Qué
pasaría si esta función no estuviera disponible para todas las muestras? Entonces le faltarían valores en
los datos de entrenamiento o prueba.
En general, con las redes neuronales, es seguro ingresar valores faltantes como 0, con la condición
de que 0 no sea ya un valor significativo. La red aprenderá de la exposición a los datos que el valor 0
significa datos faltantes y comenzará a ignorar el valor.
Tenga en cuenta que si espera valores faltantes en los datos de prueba, pero la red se entrenó con
datos sin valores faltantes, ¡la red no habrá aprendido a ignorar los valores faltantes! En esta situación,
debe generar artificialmente muestras de entrenamiento con entradas faltantes: copie algunas muestras
de entrenamiento varias veces y elimine algunas de las características que espera que falten en los datos
de prueba.
Supongamos que está intentando desarrollar aún mejores: theta2: 0 theta2: 140
ángulos de las
un modelo que pueda tomar como entrada manecillas del reloj
Si elige utilizar los píxeles sin procesar de la imagen como datos de entrada, entonces tendrá una difícil tarea.
problema de aprendizaje automático en tus manos. Necesitará una red neuronal convolucional para resolverlo y tendrá
que gastar bastantes recursos computacionales para
entrenar la red.
Las buenas características aún le permiten resolver problemas de manera más elegante usando menos
recursos. Por ejemplo, sería ridículo resolver el problema de leer un
esfera del reloj utilizando una red neuronal convolucional.
Las buenas funciones le permiten resolver un problema con muchos menos datos. La capacidad de los
modelos de aprendizaje profundo para aprender funciones por sí solos depende de tener mucha capacitación.
datos disponibles; Si solo tiene unas pocas muestras, entonces el valor de la información en
sus características se vuelven críticas.
necesitaríamos sólo 10 parámetros binarios para cada uno de los 50.000 dígitos. Pero un modelo así
sería inútil para clasificar muestras de dígitos nuevos. Tenga siempre esto en cuenta: los modelos de
aprendizaje profundo tienden a adaptarse bien a los datos de entrenamiento, pero el verdadero desafío
es la generalización, no el ajuste.
Por otro lado, si la red tiene recursos de memorización limitados, no podrá aprender este mapeo
tan fácilmente; por lo tanto, para minimizar su pérdida, tendrá que recurrir a aprender representaciones
comprimidas que tengan poder predictivo con respecto a los objetivos, precisamente el tipo de
representaciones que nos interesan. Al mismo tiempo, tenga en cuenta que debe utilizar modelos que
tengan suficientes parámetros para no ser inadecuados: su modelo no debería carecer de recursos de
memorización. Hay que encontrar un equilibrio entre demasiada capacidad y capacidad insuficiente.
Desafortunadamente, no existe una fórmula mágica para determinar el número correcto de capas
o el tamaño correcto para cada capa. Debe evaluar una serie de arquitecturas diferentes (en su
conjunto de validación, no en su conjunto de prueba, por supuesto) para encontrar el tamaño de
modelo correcto para sus datos. El flujo de trabajo general para encontrar un tamaño de modelo
apropiado es comenzar con relativamente pocas capas y parámetros, y aumentar el tamaño de las
capas o agregar nuevas capas hasta que vea rendimientos decrecientes con respecto a la pérdida de validación.
Probemos esto en la red de clasificación de reseñas de películas. La red original se muestra a
continuación.
modelo = modelos.Sequential()
model.add(layers.Dense(16, activación='relu', input_shape=(10000,))) model.add(layers.Dense(16,
activación='relu')) modelo. agregar(capas.Dense(1, activación='sigmoide'))
modelo = modelos.Sequential()
model.add(layers.Dense(4, activación='relu', input_shape=(10000,))) model.add(layers.Dense(4,
activación='relu')) modelo. agregar(capas.Dense(1, activación='sigmoide'))
La Figura 4.4 muestra una comparación de las pérdidas de validación de la red original y la red más
pequeña. Los puntos son los valores de pérdida de validación de la red más pequeña y las cruces son
la red inicial (recuerde, una pérdida de validación más baja indica un mejor modelo).
Como puede ver, la red más pequeña comienza a sobreadaptarse más tarde que la red de referencia.
(después de seis épocas en lugar de cuatro), y su rendimiento se degrada más lentamente una vez que
comienza a sobreajustarse.
Ahora, por diversión, agreguemos a este punto de referencia una red que tiene mucha más capacidad.
idad, mucho más de lo que justifica el problema.
modelo = modelos.Secuencial()
model.add(layers.Dense(512, activación='relu', input_shape=(10000,)))
model.add(layers.Dense(512, activación='relu'))
model.add(layers.Dense(1, activación='sigmoide'))
La Figura 4.5 muestra las tarifas de la red más grande en comparación con la red de referencia.
Los puntos son los valores de pérdida de validación de la red más grande y las cruces son los
red inicial.
La red más grande comienza a sobreadaptarse casi inmediatamente, después de sólo una época, y
se sobreadapta mucho más severamente. Su pérdida de validación también es más ruidosa.
Mientras tanto, la figura 4.6 muestra las pérdidas de capacitación para las dos redes. Como puedas
Verá, la red más grande obtiene una pérdida de entrenamiento cercana a cero muy rápidamente. Cuanta más capacidad
tiene la red, más rápidamente podrá modelar los datos de entrenamiento (lo que resulta en un bajo
pérdida de entrenamiento), pero más susceptible es al sobreajuste (lo que resulta en una gran diferencia entre la
pérdida de entrenamiento y de validación).
En Keras, la regularización de peso se agrega pasando instancias de regularizador de peso a capas como
argumentos de palabras clave. Agreguemos la regularización del peso L2 a la red de clasificación de
reseñas de películas.
modelo = modelos.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activación='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activación = 'relu'))
model.add(layers.Dense(1, activación='sigmoide'))
l2(0,001) significa que cada coeficiente en la matriz de peso de la capa agregará 0,001 *
valor_coeficiente_peso a la pérdida total de la red. Tenga en cuenta que debido a que esta penalización
solo se agrega en el momento del entrenamiento, la pérdida para esta red será mucho mayor en el momento
del entrenamiento que en el momento de la prueba.
La Figura 4.7 muestra el impacto de la penalización por regularización L2 . Como puede ver, el modelo
con regularización L2 (puntos) se ha vuelto mucho más resistente al sobreajuste que el modelo de referencia
(cruces), aunque ambos modelos tienen el mismo número de parámetros.
Como alternativa a la regularización L2 , puede utilizar uno de los siguientes regularizadores de peso de
Keras.
Considere una matriz Numpy que contiene la salida de una capa, Layer_Output, de forma (batch_size,
características). En el momento del entrenamiento, ponemos a cero aleatoriamente una fracción de los valores
de la matriz:
En el momento de la prueba, reducimos la producción según la tasa de deserción. Aquí escalamos a 0,5
(porque anteriormente eliminamos la mitad de las unidades):
Tenga en cuenta que este proceso se puede implementar realizando ambas operaciones en el momento del
entrenamiento y dejando la salida sin cambios en el momento de la prueba, que suele ser la forma en que se
implementa en la práctica (consulte la figura 4.8):
Esta técnica puede parecer extraña y arbitraria. ¿Por qué ayudaría esto a reducir el sobreajuste? Hinton dice
que se inspiró, entre otras cosas, en un mecanismo de prevención de fraude utilizado por los bancos. En sus
propias palabras, “fui a mi banco. Los cajeros seguían cambiando y le pregunté a uno de ellos por qué. Dijo
que no lo sabía pero que los movían mucho.
Supuse que debía ser porque se requeriría la cooperación entre los empleados para defraudar con éxito al banco.
Esto me hizo darme cuenta de que eliminar aleatoriamente un archivo diferente
Un subconjunto de neuronas en cada ejemplo evitaría conspiraciones y, por lo tanto, reduciría el sobreajuste”. 1 La
idea central es que introducir ruido en los valores de salida de una capa puede
romper patrones fortuitos que no son significativos (lo que Hinton llama conspiraciones), que la red comenzará a
memorizar si no hay ruido presente.
En Keras, puede introducir el abandono en una red a través de la capa Abandono , que es
aplicado a la salida de la capa justo antes:
modelo.add(capas.Abandono(0.5))
Agreguemos dos capas de abandono en la red IMDB para ver qué tan bien reducen
sobreajuste.
modelo = modelos.Secuencial()
model.add(layers.Dense(16, activación='relu', input_shape=(10000,)))
modelo.add(capas.Abandono(0.5))
model.add(layers.Dense(16, activación='relu'))
modelo.add(capas.Abandono(0.5))
model.add(layers.Dense(1, activación='sigmoide'))
La Figura 4.9 muestra un gráfico de los resultados. Una vez más, se trata de una clara mejora con respecto a la red
de referencia.
En resumen, estas son las formas más comunes de evitar el sobreajuste en redes neuronales:
1
Consulte el hilo de Reddit “AMA: Somos el equipo de Google Brain. Nos encantaría responder a tus preguntas sobre
aprendizaje automático”, http://mng.bz/XrsS.
¿Cuáles serán sus datos de entrada? ¿Qué estás tratando de predecir? Solo puede aprender a
predecir algo si tiene datos de entrenamiento disponibles: por ejemplo, solo puede aprender a
clasificar el sentimiento de las reseñas de películas si tiene disponibles reseñas de películas y
anotaciones de sentimientos. Como tal, la disponibilidad de datos suele ser el factor limitante en esta
etapa (a menos que tenga los medios para pagarle a personas para que recopilen datos por usted).
¿ A qué tipo de
problema se enfrenta? ¿Es clasificación binaria? ¿Clasificación multiclase? ¿Regresión escalar?
¿Regresión vectorial? ¿Clasificación multiclase y multietiqueta? ¿Algo más, como agrupación,
generación o aprendizaje por refuerzo?
La identificación del tipo de problema guiará la elección de la arquitectura del modelo, la función de
pérdida, etc.
No puede pasar a la siguiente etapa hasta que sepa cuáles son sus entradas y salidas, y qué datos utilizará.
Sea consciente de las hipótesis que plantea en esta etapa:
Plantea la hipótesis de que sus resultados pueden predecirse dadas sus entradas.
Usted plantea la hipótesis de que los datos disponibles son suficientemente informativos para conocer el
relación entre entradas y salidas.
Hasta que tenga un modelo funcional, estas son meras hipótesis que esperan ser validadas o invalidadas. No
todos los problemas se pueden resolver; Sólo porque haya reunido ejemplos de entradas X y objetivos Y no
significa que X contenga suficiente información para predecir Y. Por ejemplo, si está tratando de predecir los
movimientos de una acción en el mercado de valores dada su historial de precios reciente, es poco probable
que tenga éxito, porque el historial de precios no contiene mucha información predictiva.
Una clase de problemas irresolubles que usted debe tener en cuenta son los problemas no estacionarios.
Supongamos que está intentando crear un motor de recomendaciones para ropa, lo está entrenando con un
mes de datos (agosto) y desea comenzar a generar recomendaciones en el invierno. Un gran problema es
que el tipo de ropa que la gente compra cambia de una temporada a otra: la compra de ropa es un fenómeno
no estacionario que dura unos pocos meses. Lo que intentas modelar cambia con el tiempo. En este caso, lo
correcto es volver a entrenar constantemente su modelo con datos del pasado reciente o recopilar datos en
una escala de tiempo en la que el problema es estacionario. Para un problema cíclico como la compra de
ropa, los datos de algunos años serán suficientes para capturar la variación estacional, ¡pero recuerde incluir
la época del año en su modelo!
Tenga en cuenta que el aprendizaje automático solo se puede utilizar para memorizar patrones que están
presentes en sus datos de entrenamiento. Sólo puedes reconocer lo que has visto antes.
Utilizar el aprendizaje automático entrenado con datos del pasado para predecir el futuro es asumir que el futuro
se comportará como el pasado. Muchas veces ese no es el caso.
Para problemas de clasificación equilibrada, donde todas las clases tienen la misma probabilidad, la
precisión y el área bajo la curva característica operativa del receptor (ROC AUC) son métricas comunes. Para
problemas de desequilibrio de clases, puede utilizar precisión y recuperación. Para problemas de clasificación
o clasificación de etiquetas múltiples, puede utilizar la precisión promedio media. Y no es raro tener que definir
su propia métrica personalizada para medir el éxito. Para tener una idea de la diversidad de métricas de éxito
del aprendizaje automático y cómo se relacionan con diferentes dominios de problemas, es útil explorar las
competencias de ciencia de datos en Kaggle (https://kaggle.com); Muestran una amplia gama de problemas y
métricas de evaluación.
Realizar una validación cruzada Kfold: la elección correcta cuando se tienen muy pocas muestras para
que la validación de reserva sea confiable
Realizar una validación iterada de Kfold: para realizar evaluaciones de modelos de alta precisión.
ción cuando hay pocos datos disponibles
Sólo elige uno de estos. En la mayoría de los casos, el primero funcionará bastante bien.
Como vio anteriormente, sus datos deben tener el formato de tensores. Los
valores tomados por estos tensores generalmente deben escalarse a valores pequeños: por
por ejemplo, en el rango [1, 1] o [0, 1].
Es posible que desee realizar algunas funciones de ingeniería, especialmente para problemas de datos pequeños.
Una vez que sus tensores de datos de entrada y datos de destino estén listos, puede comenzar a entrenar modelos.
4.5.5 Desarrollar un modelo que funcione mejor que una línea de base
Su objetivo en esta etapa es lograr poder estadístico: es decir, desarrollar un modelo pequeño que sea capaz
de superar una línea de base tonta. En el ejemplo de clasificación de dígitos del MNIST , se puede decir que
cualquier cosa que alcance una precisión superior a 0,1 tiene poder estadístico; en el ejemplo de IMDB , es
cualquier cosa con una precisión superior a 0,5.
Tenga en cuenta que no siempre es posible lograr poder estadístico. Si no puede superar una línea de
base aleatoria después de probar varias arquitecturas razonables, es posible que la respuesta a la pregunta
que hace no esté presente en los datos de entrada. Recuerde que formula dos hipótesis: Plantea la
hipótesis de que
sus resultados pueden predecirse dadas sus entradas. Usted plantea la hipótesis de que
los datos disponibles son suficientemente informativos para conocer el
relación entre entradas y salidas.
Es muy posible que estas hipótesis sean falsas, en cuyo caso hay que volver a la mesa de dibujo.
Suponiendo que todo vaya bien, debe tomar tres decisiones clave para construir su primer modelo
funcional:
Activación de última capa: establece restricciones útiles en la salida de la red. Por ejemplo, el ejemplo
de clasificación de IMDB utilizó sigmoide en la última capa; el ejemplo de regresión no utilizó ninguna
activación de última capa; etcétera. Función de pérdida: debe coincidir con el tipo de
problema que intenta resolver. Por ejemplo, el ejemplo de IMDB usó binario_crossentropy, el ejemplo de
regresión usó mse, y así sucesivamente. Configuración de optimización: ¿qué optimizador
utilizará? ¿Cuál será su tasa
de aprendizaje? En la mayoría de los casos, es seguro utilizar rmsprop y su tasa de aprendizaje
predeterminada.
Con respecto a la elección de una función de pérdida, tenga en cuenta que no siempre es posible optimizar
directamente la métrica que mide el éxito de un problema. A veces no existe una manera sencilla de convertir
una métrica en una función de pérdida; Después de todo, las funciones de pérdida deben ser computables
dado solo un mini lote de datos (idealmente, una función de pérdida debería ser computable para tan solo un
único punto de datos) y deben ser diferenciables (de lo contrario, no se puede usar retropropagación para
entrenar su red). Por ejemplo, la métrica de clasificación ROC AUC, ampliamente utilizada , no se puede
optimizar directamente. Por lo tanto, en las tareas de clasificación, es común optimizar para una métrica
proxy de ROC AUC, como la entropía cruzada. En general, se puede esperar que cuanto menor sea la
entropía cruzada, mayor será el AUC de la República de China .
La Tabla 4.1 puede ayudarle a elegir una activación de última capa y una función de pérdida para algunos
tipos de problemas comunes.
Tabla 4.1 Elección de la función de activación y pérdida de última capa adecuada para su modelo
Una vez que haya obtenido un modelo que tenga poder estadístico, la pregunta será: ¿su
modelo lo suficientemente potente? ¿Tiene suficientes capas y parámetros para correctamente?
modelar el problema en cuestión? Por ejemplo, una red con una única capa oculta con
dos unidades tendrían poder estadístico en MNIST pero no serían suficientes para resolver el problema.
problema bien. Recuerde que la tensión universal en el aprendizaje automático es entre
optimización y generalización; el modelo ideal es aquel que se sitúa justo en la frontera
entre desajuste y sobreajuste; entre capacidad insuficiente y exceso de capacidad. Para saber dónde se encuentra
esta frontera, primero debes cruzarla.
Para determinar el tamaño del modelo que necesitará, debe desarrollar un modelo que se ajuste demasiado.
Esto es bastante fácil:
1 Añade capas.
2 Haz las capas más grandes.
3 Entrena para más épocas.
Supervise siempre la pérdida de entrenamiento y la pérdida de validación, así como los valores de entrenamiento y
validación para cualquier métrica que le interese. Cuando vea que el rendimiento del modelo en los datos de
validación comienza a degradarse, habrá logrado un sobreajuste.
La siguiente etapa es comenzar a regularizar y ajustar el modelo, para acercarse lo más posible al modelo ideal
que no se ajuste ni se ajuste demasiado.
Este paso será el que llevará más tiempo: modificará repetidamente su modelo, lo entrenará, evaluará sus datos de
validación (no los datos de prueba, en este punto), lo modificará nuevamente y
repita, hasta que el modelo sea lo mejor posible. Estas son algunas cosas que deberías probar:
Agregar abandono.
Pruebe diferentes arquitecturas: agregue o elimine capas.
Agregar regularización L1 y/o L2 .
Tenga en cuenta lo siguiente: cada vez que utilice comentarios de su proceso de validación
Para ajustar su modelo, filtra información sobre el proceso de validación en el modelo.
Repetido sólo unas pocas veces, esto es inofensivo; pero si se hace sistemáticamente durante muchas
iteraciones, eventualmente hará que su modelo se sobreajuste al proceso de validación (incluso
aunque ningún modelo está entrenado directamente en ninguno de los datos de validación). Esto hace que el
proceso de evaluación menos confiable.
Una vez que haya desarrollado una configuración de modelo satisfactoria, podrá entrenar su versión final.
modelo de producción sobre todos los datos disponibles (entrenamiento y validación) y evaluarlo
una última vez en el set de prueba. Si resulta que el rendimiento en el conjunto de prueba es significativamente
peor que el rendimiento medido en los datos de validación, esto puede significar
o que su procedimiento de validación no era confiable después de todo, o que comenzó a sobreajustar los
datos de validación mientras ajustaba los parámetros del modelo. En este caso,
es posible que desee cambiar a un protocolo de evaluación más confiable (como Kfold iterado
validación).
Elija cómo medirá el éxito de su problema. ¿Qué métricas monitoreará en sus datos de
validación?
Desarrollar un primer modelo que funcione mejor que una línea de base básica: un modelo
con poder estadístico.
Parte 2
Los capítulos 5 a 9 le ayudarán a adquirir una intuición práctica sobre cómo resolver problemas reales.
problemas mundiales utilizando el aprendizaje profundo y lo familiarizará con las mejores prácticas
esenciales de aprendizaje profundo. La mayoría de los ejemplos de código del libro se concentran
en esta segunda mitad.
Aprendizaje profundo
para visión por computadora
Este capítulo presenta las redes neuronales convolucionales, también conocidas como convnets, una
tipo de modelo de aprendizaje profundo utilizado casi universalmente en aplicaciones de visión por computadora. Aprenderá a
aquellos que involucran pequeños conjuntos de datos de entrenamiento, que son el caso de uso más común si
119
modelo = modelos.Secuencial()
model.add(layers.Conv2D(32, (3, 3), activación='relu', input_shape=(28, 28, 1)))
modelo.add(capas.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activación='relu'))
modelo.add(capas.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activación='relu'))
Es importante destacar que una convnet toma como entrada tensores de forma (image_height, image_width,
image_channels) (sin incluir la dimensión del lote). En este caso configuraremos
el convnet para procesar entradas de tamaño (28, 28, 1), que es el formato de MNIST
imágenes. Haremos esto pasando el argumento input_shape=(28, 28, 1) al primer
capa.
Mostremos la arquitectura de convnet hasta ahora:
>>> modelo.resumen()
________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============
a medida que profundizas en la red. El número de canales está controlado por el primero.
argumento pasado a las capas Conv2D (32 o 64).
El siguiente paso es introducir el último tensor de salida (de forma (3, 3, 64)) en un
red clasificadora conectada como aquellas con las que ya está familiarizado: una pila de Denso
capas. Estos clasificadores procesan vectores, que son 1D, mientras que la salida actual es una
Tensores 3D . Primero tenemos que aplanar las salidas 3D a 1D y luego agregar algunas capas
densas encima.
modelo.add(capas.Aplanar())
model.add(layers.Dense(64, activación='relu'))
model.add(layers.Dense(10, activación='softmax'))
Haremos una clasificación de 10 vías, usando una capa final con 10 salidas y una activación
softmax. Así es como se ve la red ahora:
>>> modelo.resumen()
Como puede ver, las salidas (3, 3, 64) se aplanan en vectores de forma (576,)
antes de pasar por dos capas densas .
Ahora, entrenemos el convnet con los dígitos MNIST . Reutilizaremos gran parte del código de
el ejemplo MNIST en el capítulo 2.
model.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy',
métricas=['precisión'])
model.fit(train_images, train_labels, épocas=5, tamaño_lote=64)
Mientras que la red densamente conectada del capítulo 2 tuvo una precisión de prueba del 97,8%, la convnet
básica tiene una precisión de prueba del 99,3%: reducimos la tasa de error en un 68% (relativo). ¡Nada mal!
Pero, ¿por qué esta simple convnet funciona tan bien, en comparación con una red densamente conectada?
¿modelo? Para responder a esto, profundicemos en lo que hacen las capas Conv2D y MaxPooling2D .
Los patrones que aprenden son invariantes de traducción. Después de aprender un determinado
patrón en la esquina inferior derecha de una imagen, un convnet puede reconocerlo en cualquier
lugar: por ejemplo, en la esquina superior izquierda. Una red densamente conectada tendría que
aprender el patrón nuevamente si apareciera en una nueva ubicación. Esto hace que los datos de
convnets sean eficientes al procesar imágenes (porque el mundo visual es fundamentalmente
invariante en la traducción): necesitan menos muestras de entrenamiento para aprender
representaciones que tengan poder de generalización.
Pueden aprender jerarquías espaciales de patrones (ver figura 5.2). Una primera capa de convolución
aprenderá pequeños patrones locales, como bordes, una segunda capa de convolución aprenderá
patrones más grandes formados por las características de las primeras capas, y así sucesivamente.
Esto permite a los convnets aprender eficientemente conceptos visuales cada vez más complejos y
abstractos (porque el mundo visual es fundamentalmente espacialmente jerárquico).
"gato"
Las convoluciones operan sobre tensores 3D , llamados mapas de características, con dos ejes espaciales
(alto y ancho), así como un eje de profundidad (también llamado eje de canales ). Para una imagen RGB , la
dimensión del eje de profundidad es 3, porque la imagen tiene tres canales de color: rojo, verde y azul. Para
una imagen en blanco y negro, como los dígitos MNIST , la profundidad es 1 (niveles de gris). La operación
de convolución extrae parches de su mapa de características de entrada y aplica la misma transformación a
todos estos parches, produciendo un mapa de características de salida. Este mapa de características de
salida sigue siendo un tensor 3D: tiene un ancho y un alto. Su profundidad puede ser arbitraria, porque la
profundidad de salida es un parámetro de la capa, y la
diferentes canales en ese eje de profundidad ya no representan colores específicos como en la entrada
RGB ; más bien, representan filtros. Los filtros codifican aspectos específicos de los datos de entrada: en
un nivel alto, un solo filtro podría codificar el concepto "presencia de una cara en la entrada", por ejemplo.
Mapa de
respuesta, que cuantifica la
presencia del patrón del
Entrada original filtro en diferentes ubicaciones.
Filtro único
En las capas de Keras Conv2D , estos parámetros son los primeros argumentos que se pasan a la capa:
Conv2D(profundidad_salida, (altura_ventana, ancho_ventana)).
Una convolución funciona deslizando estas ventanas de tamaño 3 × 3 o 5 × 5 sobre el mapa de
características de entrada 3D , deteniéndose en cada ubicación posible y extrayendo el parche 3D de las
características circundantes (forma (alto_ventana, ancho_ventana, profundidad_entrada)). Luego, cada
parche 3D se transforma (a través de un producto tensor con la misma matriz de peso aprendida, llamada
núcleo de convolución) en un vector de forma 1D (profundidad_salida). Luego, todos estos vectores se
vuelven a ensamblar espacialmente en un mapa de forma de salida 3D (alto, ancho, profundidad_salida).
Cada ubicación espacial en el mapa de características de salida corresponde a la misma ubicación en el
mapa de características de entrada (por ejemplo, la esquina inferior derecha de la salida contiene
información sobre la esquina inferior derecha de la entrada). Por ejemplo, con ventanas de 3 × 3, la salida
del vector [i, j, :] proviene de la entrada del parche 3D [i1:i+1, j1:j+1, :]. El proceso completo se detalla en
la figura 5.4.
Ancho Altura
3 × 3 parches de entrada
Producto escalar
con núcleo
Profundidad
Parches transformados
de salida
de salida
Tenga en cuenta que el ancho y el alto de salida pueden diferir del ancho y alto de entrada.
Pueden diferir por dos razones:
Considere un mapa de características de 5 × 5 (25 mosaicos en total). Sólo hay 9 mosaicos alrededor de los
cuales puedes centrar una ventana de 3 × 3, formando una cuadrícula de 3 × 3 (ver figura 5.5). Por lo tanto,
el mapa de características de salida será 3 × 3. Se encoge un poco: en este caso, exactamente dos mosaicos
a lo largo de cada dimensión. Puedes ver este efecto de borde en acción en el ejemplo anterior: comienzas
con entradas de 28 × 28, que se convierten en 26 × 26 después de la primera capa de convolución.
Si desea obtener un mapa de características de salida con las mismas dimensiones espaciales que el
entrada, puedes usar relleno. El relleno consiste en agregar un número apropiado de filas
y columnas a cada lado del mapa de características de entrada para hacer posible ajustar ventanas de
convolución centrales alrededor de cada mosaico de entrada. Para una ventana de 3 × 3, agrega una
columna a la derecha, una columna a la izquierda, una fila en la parte superior y una fila en la parte superior.
abajo. Para una ventana de 5 × 5, agrega dos filas (ver figura 5.6).
etc.
En las capas Conv2D , el relleno se puede configurar mediante el argumento de relleno , que requiere dos
valores: "válido", lo que significa que no hay relleno (solo se utilizarán ubicaciones de ventana válidas);
y "mismo", que significa "rellenar de tal manera que tenga una salida con el mismo ancho
y altura como entrada”. El argumento de relleno por defecto es "válido".
El otro factor que puede influir en el tamaño de la producción es la noción de avances. La descripción
de convolución hasta ahora ha asumido que los mosaicos centrales de las ventanas de convolución son
todos contiguos. Pero la distancia entre dos ventanas sucesivas es un parámetro del
convolución, llamada zancada, que por defecto es 1. Es posible tener convoluciones con zancadas: convoluciones con
una zancada superior a 1. En la figura 5.7, puedes ver los parches
extraído mediante una convolución de 3 × 3 con paso 2 sobre una entrada de 5 × 5 (sin relleno).
1 2
1 2
3 4
3 4
Usar la zancada 2 significa que el ancho y el alto del mapa de características se reducen mediante un
factor de 2 (además de cualquier cambio inducido por efectos de frontera). Las convoluciones zancadas rara vez se
utilizan en la práctica, aunque pueden resultar útiles para algunos tipos de
modelos; Es bueno estar familiarizado con el concepto.
Para reducir la muestra de mapas de características, en lugar de avances, tendemos a utilizar la agrupación máxima.
operación, que vio en acción en el primer ejemplo de convnet. Veámoslo en
más profundidad.
La agrupación máxima consiste en extraer ventanas de los mapas de características de entrada y generar el valor
máximo de cada canal. Es conceptualmente similar a la convolución, excepto
que en lugar de transformar parches locales mediante una transformación lineal aprendida (el núcleo de convolución),
se transforman mediante una operación de tensor máximo codificada. Una gran diferencia con la convolución es que la
agrupación máxima generalmente se realiza con ventanas de 2 × 2 y
paso 2, para reducir la muestra de los mapas de características en un factor de 2. Por otro lado,
La convolución generalmente se realiza con ventanas de 3 × 3 y sin zancada (zancada 1).
¿Por qué reducir la resolución de los mapas de características de esta manera? ¿Por qué no eliminar las capas de agrupación máxima?
¿Y mantener mapas de características bastante grandes hasta el final? Veamos esta opción. La base convolucional
del modelo quedaría así:
model_no_max_pool = modelos.Sequential()
model_no_max_pool.add(layers.Conv2D(32, (3, 3), activación='relu',
forma_entrada=(28, 28, 1)))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activación='relu'))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activación='relu'))
>>> modelo_no_max_pool.summary()
No es propicio para aprender una jerarquía espacial de características. Las ventanas 3×3
en la tercera capa solo contendrá información proveniente de ventanas 7 × 7 en
la entrada inicial. Los patrones de alto nivel aprendidos por convnet seguirán siendo muy
pequeño con respecto a la entrada inicial, que puede no ser suficiente para aprender a clasificar dígitos
(intente reconocer un dígito mirándolo solo a través de ventanas que están abiertas).
¡7 × 7 píxeles!). Necesitamos que las características de la última capa de convolución contengan
información sobre la totalidad de la entrada.
El mapa de características final tiene 22 × 22 × 64 = 30,976 coeficientes totales por muestra.
Esto es enorme. Si lo aplanaras para pegar una capa densa de tamaño 512 encima,
esa capa tendría 15,8 millones de parámetros. Esto es demasiado grande para tal
modelo pequeño y daría lugar a un intenso sobreajuste.
En resumen, la razón para utilizar la reducción de resolución es reducir la cantidad de mapas de características.
coeficientes a procesar, así como para inducir jerarquías de filtros espaciales al hacer que las capas de convolución
sucesivas miren ventanas cada vez más grandes (en términos de la fracción de
los insumos originales que cubren).
Tenga en cuenta que la agrupación máxima no es la única forma de lograr dicha reducción de resolución. Como
Ya lo sabes, también puedes usar pasos en la capa de convolución anterior. Y tu puedes
use agrupación promedio en lugar de agrupación máxima, donde cada parche de entrada local se transforma
tomando el valor promedio de cada canal sobre el parche, en lugar del
máx. Pero la agrupación máxima tiende a funcionar mejor que estas soluciones alternativas. En pocas palabras,
la razón es que las características tienden a codificar la presencia espacial de algún patrón.
o concepto sobre los diferentes mosaicos del mapa de características (de ahí el término mapa de características),
y es más informativo observar la presencia máxima de diferentes características que en
su presencia media. Entonces, la estrategia de submuestreo más razonable es producir primero
mapas densos de características (a través de convoluciones sin zancadas) y luego mirar el máximo
activación de las funciones en parches pequeños, en lugar de mirar ventanas más dispersas de
las entradas (a través de convoluciones zancadas) o promediando parches de entrada, lo que podría causar
que se pierda o se diluya la información sobre la presencia de características.
En este punto, debes comprender los conceptos básicos de las convnets (mapas de funciones, convolución
y agrupación máxima) y saber cómo construir una pequeña convnet para resolver un juguete.
problema como la clasificación de dígitos MNIST . Ahora pasemos a aplicaciones más útiles y prácticas.
A veces escucharás que el aprendizaje profundo solo funciona cuando hay muchos datos disponibles.
Esto es válido en parte: una característica fundamental del aprendizaje profundo es que puede encontrar
características interesantes en los datos de entrenamiento por sí solas, sin necesidad de ingeniería manual de
características, y esto sólo se puede lograr cuando se utilizan muchos ejemplos de entrenamiento.
disponible. Esto es especialmente cierto para problemas en los que las muestras de entrada son de muy altas dimensiones,
como imágenes.
Pero lo que constituye muchas muestras es relativo: relativo al tamaño y la profundidad de la
red que estás intentando entrenar, para empezar. No es posible entrenar un convnet para resolver un
problema complejo con sólo unas pocas decenas de muestras, pero unos cientos pueden potencialmente
Es suficiente si el modelo es pequeño y está bien regularizado y la tarea es sencilla. Debido a que las convnets aprenden
características locales invariantes en la traducción, son muy eficientes en materia de datos en problemas de percepción.
Entrenar un convnet desde cero en un conjunto de datos de imágenes muy pequeño aún
producir resultados razonables a pesar de una relativa falta de datos, sin la necesidad de ninguna costumbre
Ingeniería de características. Verás esto en acción en esta sección.
Es más, los modelos de aprendizaje profundo son, por naturaleza, altamente reutilizables: puedes
Tomemos, por ejemplo, un modelo de clasificación de imágenes o de conversión de voz a texto entrenado en un conjunto de datos a gran escala.
En el caso de la visión por computadora, muchos modelos previamente entrenados (generalmente entrenados en el
conjunto de datos ImageNet) ahora están disponibles públicamente para su descarga y pueden usarse para iniciar
potentes modelos de visión a partir de muy pocos datos. Eso es lo que harás en la siguiente sección. vamos
Empiece por tener en sus manos los datos.
Figura 5.8 Muestras del conjunto de datos Perros vs. Gatos. Los tamaños no fueron modificados: las
muestras son heterogéneas en tamaño, apariencia, etc.
Como era de esperar, los participantes ganaron la competencia Kaggle de perros contra gatos en 2013.
quien usó convnets. Las mejores entradas lograron hasta un 95% de precisión. En este ejemplo,
Te acercarás bastante a esta precisión (en la siguiente sección), aunque entrenes
sus modelos con menos del 10% de los datos que estaban disponibles para los competidores.
Este conjunto de datos contiene 25.000 imágenes de perros y gatos (12.500 de cada clase) y
es 543 MB (comprimido). Después de descargarlo y descomprimirlo, creará un nuevo
conjunto de datos que contiene tres subconjuntos: un conjunto de entrenamiento con 1000 muestras de cada clase,
un conjunto de validación con 500 muestras de cada clase y un conjunto de prueba con 500 muestras de cada clase.
original_dataset_dir = '/Usuarios/fchollet/Descargas/kaggle_original_data'
base_dir = '/Usuarios/fchollet/Descargas/gatos_y_perros_pequeños'
os.mkdir(base_dir)
shutil.copyfile(src,dst)
Como control de cordura, contemos cuántas imágenes hay en cada división de entrenamiento (entrenamiento/
validación/prueba):
Por lo tanto, efectivamente tiene 2000 imágenes de entrenamiento, 1000 imágenes de validación y 1000
imágenes de prueba. Cada división contiene la misma cantidad de muestras de cada clase: este es un problema
de clasificación binaria equilibrada, lo que significa que la precisión de la clasificación será una medida adecuada
del éxito.
Listado 5.5 Creación de instancias de una pequeña convnet para la clasificación de perros versus gatos
modelo = modelos.Secuencial()
model.add(layers.Conv2D(32, (3, 3), activación='relu',
forma_entrada=(150, 150, 3)))
modelo.add(capas.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activación='relu'))
modelo.add(capas.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activación='relu'))
modelo.add(capas.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activación='relu'))
modelo.add(capas.MaxPooling2D((2, 2)))
modelo.add(capas.Aplanar())
model.add(layers.Dense(512, activación='relu'))
model.add(layers.Dense(1, activación='sigmoide'))
Veamos cómo cambian las dimensiones de los mapas de características con cada paso sucesivo.
capa:
>>> modelo.resumen()
Para el paso de compilación, utilizará el optimizador RMSprop , como de costumbre. Debido a que finalizó la red
con una sola unidad sigmoidea, usará la entropía cruzada binaria como pérdida (como recordatorio, consulte la
tabla 4.1 para obtener una hoja de referencia sobre qué función de pérdida usar en diversas situaciones).
model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=1e4), métricas=['acc'])
Puede parecer un poco desalentador, pero afortunadamente Keras tiene utilidades para realizar estos pasos
automáticamente. Keras tiene un módulo con herramientas auxiliares de procesamiento de imágenes, ubicado en
keras.preprocessing.image. En particular, contiene la clase ImageDataGenerator, que le permite configurar
rápidamente generadores de Python que pueden convertir automáticamente archivos de imágenes en el disco en
lotes de tensores preprocesados. Esto es lo que usarás aquí.
Imprime esto:
1
2
3
4
5
Veamos la salida de uno de estos generadores: produce lotes de imágenes RGB de 150 ×
150 (forma (20, 150, 150, 3)) y etiquetas binarias (forma (20,)). Hay 20 muestras en cada
lote (el tamaño del lote). Tenga en cuenta que el generador genera estos lotes de forma
indefinida: recorre sin cesar las imágenes en la carpeta de destino. Por este motivo, es
necesario romper el ciclo de iteración en algún momento:
Ajustemos el modelo a los datos usando el generador. Para ello, utilice el método
fit_generator , el equivalente a fit para generadores de datos como este. Espera como primer
argumento un generador de Python que produzca lotes de entradas y objetivos de forma
indefinida, como lo hace este. Debido a que los datos se generan sin cesar, el modelo de
Keras necesita saber cuántas muestras extraer del generador antes de declarar el fin de una
época. Esta es la función del argumento pasos_per_epoch : después de haber extraído lotes
de pasos_per_epoch del generador, es decir, después de haberlos ejecutado durante
historial = model.fit_generator(train_generator,
pasos_por_epoch=100,
épocas=30,
validation_data=validation_generator, validation_steps=50)
Es una buena práctica guardar siempre los modelos después del entrenamiento.
model.save('gatos_y_perros_pequeños_1.h5')
Tracemos la pérdida y precisión del modelo sobre los datos de entrenamiento y validación durante el entrenamiento
(ver figuras 5.9 y 5.10).
plt.figura()
plt.mostrar()
Estas parcelas son características del sobreajuste. La precisión del entrenamiento aumenta linealmente.
con el tiempo, hasta alcanzar casi el 100%, mientras que la precisión de la validación se estanca entre el 70% y el 72%.
La pérdida de validación alcanza su mínimo después de sólo cinco épocas y luego se detiene, mientras que
la pérdida de entrenamiento sigue disminuyendo linealmente hasta llegar casi a 0.
Debido a que tiene relativamente pocas muestras de entrenamiento (2000), el sobreajuste será su opción.
preocupación número uno. Ya conoces una serie de técnicas que pueden ayudar
mitigar el sobreajuste, como el abandono escolar y la caída de peso (regularización L2). estamos ahora
Voy a trabajar con uno nuevo, específico para la visión por computadora y utilizado casi universalmente.
al procesar imágenes con modelos de aprendizaje profundo: aumento de datos.
Estaría expuesto a todos los aspectos posibles de la distribución de datos en cuestión: nunca se sobreajustaría. El
aumento de datos adopta el enfoque de generar más datos de entrenamiento a partir de muestras de entrenamiento
existentes, aumentando las muestras mediante una serie de transformaciones aleatorias que producen imágenes de
apariencia creíble. El objetivo es que durante el entrenamiento, su modelo nunca vea exactamente la misma imagen dos
veces. Esto ayuda a exponer el modelo a más aspectos de los datos y a generalizar mejor.
En Keras, esto se puede hacer configurando una serie de transformaciones aleatorias que se realizarán en las
imágenes leídas por la instancia ImageDataGenerator . Comencemos con un ejemplo.
Estas son sólo algunas de las opciones disponibles (para obtener más información, consulte la documentación de Keras).
Repasemos rápidamente este código:
width_shift y height_shift son rangos (como fracción del ancho o alto total) dentro de los cuales se traducen
aleatoriamente imágenes vertical u horizontalmente.
horizontal_flip sirve para voltear aleatoriamente la mitad de las imágenes horizontalmente; es relevante cuando
no hay suposiciones de asimetría horizontal (por ejemplo, imágenes del mundo real).
fill_mode es la estrategia utilizada para rellenar los píxeles recién creados, que puede
aparecen después de una rotación o un cambio de ancho/alto.
yo=0
Genera lotes de
para lote en datagen.flow(x, tamaño_lote=1):
Imágenes transformadas
plt.figura(i)
aleatoriamente. Se repite
imgplot = plt.imshow(image.array_to_img(lote[0]))
indefinidamente, por lo que
yo += 1
debes romper el ciclo en algún momento.
si yo % 4 == 0:
romper
plt.mostrar()
Si entrena una nueva red usando esta configuración de aumento de datos, la red
nunca verá la misma entrada dos veces. Pero las entradas que ve todavía están fuertemente relacionadas con
la intercorrección, porque provienen de un pequeño número de imágenes originales: no se puede producir nueva
información, sólo se puede remezclar la información existente. Como tal, esto puede no
ser suficiente para deshacerse por completo del sobreajuste. Para luchar aún más contra el sobreajuste, también
agregue una capa de abandono a su modelo, justo antes del clasificador densamente conectado.
modelo = modelos.Sequential()
model.add(layers.Conv2D(32, (3, 3), activación='relu',
input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activación='relu')) model .add(layers.MaxPooling2D((2,
2))) model.add(layers.Conv2D(128, (3, 3), activación='relu'))
model.add(layers.MaxPooling2D((2, 2) )) model.add(layers.Conv2D(128, (3, 3),
activación='relu')) model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten()) model.add(layers.Dropout(0.5)) model.add(layers.Dense(512,
activación='relu')) model.add(layers.Dense(1,
activación='sigmoide'))
model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=1e4), métricas=['acc'])
historial = model.fit_generator(train_generator,
pasos_por_epoch=100,
épocas=100,
validation_data=validation_generator, validation_steps=50)
model.save('gatos_y_perros_pequeños_2.h5')
Y volvamos a trazar los resultados: véanse las figuras 5.12 y 5.13. Gracias al aumento y al
abandono de datos, ya no estás sobreentrenado: las curvas de entrenamiento siguen de cerca
las curvas de validación. Ahora alcanza una precisión del 82%, una mejora relativa del 15%.
sobre el modelo no regularizado.
Utilizando aún más técnicas de regularización y ajustando los parámetros de la red (como el
número de filtros por capa de convolución o el número de capas en
la red), es posible que pueda obtener una precisión aún mejor, probablemente hasta 86% u 87%.
Pero resultaría difícil llegar más alto simplemente entrenando a tu propio convnet desde
cero, porque tienes muy pocos datos con los que trabajar. Como siguiente paso para mejorar su
precisión en este problema, tendrá que utilizar un modelo previamente entrenado, que es el foco de
las siguientes dos secciones.
Hay dos formas de utilizar una red previamente entrenada: extracción de características y ajuste.
Los cubriremos a ambos. Comencemos con la extracción de funciones.
La extracción de características consiste en utilizar las representaciones aprendidas por una red anterior
para extraer características interesantes de nuevas muestras. Luego, estas características se ejecutan a
través de un nuevo clasificador, que se entrena desde cero.
Como vio anteriormente, las convnets utilizadas para la clasificación de imágenes constan de dos
partes: comienzan con una serie de capas de agrupación y convolución, y terminan con un clasificador
densamente conectado. La primera parte se llama base convolucional del modelo. En el caso de convnets,
la extracción de características consiste en tomar la base convolucional de un
1
Karen Simonyan y Andrew Zisserman, “Redes convolucionales muy profundas para el reconocimiento de imágenes
a gran escala”, arXiv (2014), https://arxiv.org/abs/1409.1556.
red previamente entrenada, ejecutando los nuevos datos a través de ella y entrenando un nuevo
clasificador además de la salida (ver figura 5.14).
entrenada (congelada)
¿Por qué reutilizar únicamente la base convolucional? ¿Podrías reutilizar también el clasificador
densamente conectado? En general, se debe evitar hacerlo. La razón es que las representaciones
aprendidas por la base convolucional probablemente sean más genéricas y, por lo tanto, más
reutilizables: los mapas de características de una convnet son mapas de presencia de conceptos
genéricos sobre una imagen, lo que probablemente será útil independientemente del ordenador.
Problema de visión en cuestión. Pero las representaciones aprendidas por el clasificador serán
necesariamente específicas del conjunto de clases en las que se entrenó el modelo; solo contendrán
información sobre la probabilidad de presencia de tal o cual clase en la imagen completa. Además, las
representaciones encontradas en capas densamente conectadas ya no contienen ninguna información
sobre dónde se encuentran los objetos en la imagen de entrada: estas capas eliminan la noción de
espacio, mientras que la ubicación del objeto todavía se describe mediante mapas de características
convolucionales. Para problemas en los que la ubicación de los objetos importa, las características densamente conectada
Tenga en cuenta que el nivel de generalidad (y por lo tanto de reutilización) de las representaciones
extraídas por capas convolucionales específicas depende de la profundidad de la capa en el modelo.
Las capas que aparecen antes en el modelo extraen mapas de características locales y altamente
genéricos (como bordes visuales, colores y texturas), mientras que las capas que están más arriba
extraen conceptos más abstractos (como “oreja de gato” u “ojo de perro”). . Entonces, si su nuevo
conjunto de datos difiere mucho del conjunto de datos en el que se entrenó el modelo original, es mejor
que use solo las primeras capas del modelo para realizar la extracción de características, en lugar de
usar toda la base convolucional.
En este caso, debido a que el conjunto de clases ImageNet contiene múltiples clases de perros
y gatos, probablemente sea beneficioso reutilizar la información contenida en las capas densamente
conectadas del modelo original. Pero elegiremos no hacerlo, para cubrir el caso más general en
el que el conjunto de clases del nuevo problema no se superpone al conjunto de clases del modelo
original. Pongamos esto en práctica utilizando la base convolucional de la red VGG16 , entrenada
en ImageNet, para extraer características interesantes de imágenes de perros y gatos, y luego
entrenemos un clasificador de perros versus gatos sobre estas características.
El modelo VGG16 , entre otros, viene preempaquetado con Keras. Puedes importarlo desde el
módulo keras.applications . Aquí está la lista de modelos de clasificación de imágenes (todos
previamente entrenados en el conjunto de datos ImageNet) que están disponibles como parte de
keras .applications:
Xception
Inicio V3
ResNet50
VGG16
VGG19
MobileNet
Instanciamos el modelo VGG16 .
>>> conv_base.summary()
________________________________________________________________
block1_conv1 (Convolución2D) (Ninguno, 150, 150, 64) 1792
________________________________________________________________
block1_conv2 (Convolución2D) (Ninguno, 150, 150, 64) 36928
________________________________________________________________
block1_pool (MaxPooling2D) (Ninguno, 75, 75, 64) 0
________________________________________________________________
block2_conv1 (Convolución2D) (Ninguno, 75, 75, 128) 73856
________________________________________________________________
block2_conv2 (Convolución2D) (Ninguno, 75, 75, 128) 147584
________________________________________________________________
block2_pool (MaxPooling2D) (Ninguno, 37, 37, 128) 0
________________________________________________________________
block3_conv1 (Convolución2D) (Ninguno, 37, 37, 256) 295168
________________________________________________________________
block3_conv2 (Convolución2D) (Ninguno, 37, 37, 256) 590080
________________________________________________________________
block3_conv3 (Convolución2D) (Ninguno, 37, 37, 256) 590080
________________________________________________________________
block3_pool (MaxPooling2D) (Ninguno, 18, 18, 256) 0
________________________________________________________________
block4_conv1 (Convolución2D) (Ninguno, 18, 18, 512) 1180160
________________________________________________________________
block4_conv2 (Convolución2D) (Ninguno, 18, 18, 512) 2359808
________________________________________________________________
block4_conv3 (Convolución2D) (Ninguno, 18, 18, 512) 2359808
________________________________________________________________
block4_pool (MaxPooling2D) (Ninguno, 9, 9, 512) 0
________________________________________________________________
block5_conv1 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_conv2 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_conv3 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_pool (MaxPooling2D) (Ninguno, 4, 4, 512) 0
==================================================== ===============
El mapa de características final tiene la forma (4, 4, 512). Esa es la característica además de la cual
pegue un clasificador densamente conectado.
En este punto, hay dos maneras de proceder:
Ampliar el modelo que tiene (conv_base) agregando capas densas en la parte superior y
ejecutando todo de un extremo a otro en los datos de entrada. Esto le permitirá utilizar el
aumento de datos, porque cada imagen de entrada pasa por la base convolucional cada
vez que el modelo la ve. Pero por la misma razón, esta técnica es mucho más cara que
la primera.
Cubriremos ambas técnicas. Repasemos el código necesario para configurar el primero: registrar
la salida de conv_base en sus datos y usar estas salidas como entradas para un nuevo modelo.
importar sistema
operativo importar numpy como
np desde keras.preprocessing.image importar ImageDataGenerator
Las características extraídas actualmente tienen forma (muestras, 4, 4, 512). Los alimentará a
un clasificador densamente conectado, por lo que primero debe aplanarlos en (muestras, 8192):
En este punto, puede definir su clasificador densamente conectado (tenga en cuenta el uso de
abandono para la regularización) y entrenarlo con los datos y las etiquetas que acaba de registrar.
modelo = modelos.Sequential()
model.add(layers.Dense(256, activación='relu', input_dim=4 * 4 * 512)) model.add(layers.Dropout(0.5))
model.add(layers.Dense (1, activación =
'sigmoide'))
modelo.compile(optimizador=optimizadores.RMSprop(lr=2e5),
pérdida='binary_crossentropy',
métricas=['acc'])
validation_data=(validation_features, validation_labels))
El entrenamiento es muy rápido porque solo tienes que lidiar con dos capas densas : una época
tarda menos de un segundo incluso en la CPU.
Veamos las curvas de pérdida y precisión durante el entrenamiento (ver figuras 5.15 y 5.16).
plt.figura()
plt.mostrar()
Alcanza una precisión de validación de aproximadamente el 90%, mucho mejor que la que logró en el
sección anterior con el modelo pequeño entrenado desde cero. Pero las tramas también indican
que estás sobreadaptado casi desde el principio, a pesar de usar la deserción con un número bastante grande
tasa. Esto se debe a que esta técnica no utiliza el aumento de datos, lo cual es esencial.
para evitar el sobreajuste con conjuntos de datos de imágenes pequeños.
Debido a que los modelos se comportan como capas, puedes agregar un modelo (como conv_base) a un
Modelo secuencial tal como agregarías una capa.
modelo = modelos.Secuencial()
modelo.add(conv_base)
modelo.add(capas.Aplanar())
model.add(layers.Dense(256, activación='relu'))
model.add(layers.Dense(1, activación='sigmoide'))
>>> modelo.resumen()
Como puede ver, la base convolucional de VGG16 tiene 14.714.688 parámetros, que es
muy grande. El clasificador que estás agregando encima tiene 2 millones de parámetros.
Antes de compilar y entrenar el modelo, es muy importante congelar la base convolucional. Congelar
una capa o conjunto de capas significa evitar que sus pesos sean
actualizado durante el entrenamiento. Si no hace esto, las representaciones que la base convolucional
aprendió previamente se modificarán durante el entrenamiento. Porque
Las capas densas en la parte superior se inicializan aleatoriamente, se necesitarían actualizaciones de peso muy grandes.
propagado a través de la red, destruyendo efectivamente las representaciones previamente
aprendió.
Con esta configuración, solo se entrenarán los pesos de las dos capas densas que agregó. Eso es
un total de cuatro tensores de peso: dos por capa (la matriz de peso principal y el vector de sesgo).
Tenga en cuenta que para que estos cambios surtan efecto, primero debe compilar el modelo. Si
alguna vez modifica la capacidad de entrenamiento con peso después de la compilación, debe
volver a compilar el modelo o estos cambios se ignorarán.
Ahora puede comenzar a entrenar su modelo, con la misma configuración de aumento de datos
que utilizó en el ejemplo anterior.
Listado 5.21 Entrenamiento del modelo extremo a extremo con una base convolucional congelada
model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=2e5), métricas=['acc'])
historial = model.fit_generator(train_generator,
pasos_por_epoch=100,
épocas=30,
validation_data=validation_generator, validation_steps=50)
Tracemos los resultados nuevamente (ver figuras 5.17 y 5.18). Como puede ver, se alcanza una
precisión de validación de aproximadamente el 96%. Esto es mucho mejor de lo que logró con el
pequeño convnet entrenado desde cero.
tanto la parte recién agregada del modelo (en este caso, el clasificador completamente conectado)
y estas capas superiores. Esto se llama ajuste fino porque ajusta ligeramente cuanto más
representaciones abstractas del modelo que se está reutilizando, con el fin de hacerlas más relevantes
para el problema en cuestión.
Convolución2D
Bloque de conversión
Convolución2D
1: congelado
MaxPooling2D
Convolución2D
Bloque de conversión
Convolución2D
2: congelado
MaxPooling2D
Convolución2D
Convolución2D
Bloque de conversión
3: congelado
Convolución2D
MaxPooling2D
Convolución2D
Convolución2D
Bloque de conversión
4: congelado
Convolución2D
MaxPooling2D
Convolución2D
Convolución2D
afinamos
Bloque de conversión 5.
Convolución2D
MaxPooling2D
Aplanar
afinamos
nuestro propio
Denso
clasificador
totalmente conectado.
Denso Figura 5.19 Ajuste del último bloque
convolucional de la red VGG16
Mencioné anteriormente que es necesario congelar la base de convolución de VGG16 para poder
Ser capaz de entrenar un clasificador inicializado aleatoriamente en la parte superior. Por la misma razón, es sólo
Es posible ajustar las capas superiores de la base convolucional una vez que el clasificador en la parte superior
ya ha sido entrenado. Si el clasificador aún no está entrenado, entonces la señal de error
La propagación a través de la red durante el entrenamiento será demasiado grande y las representaciones
aprendidas previamente por las capas que se están ajustando serán destruidas. Por lo tanto, la
Los pasos para ajustar una red son los siguientes:
Ya completó los primeros tres pasos al realizar la extracción de funciones. Procedamos con el paso 4:
descongelarás tu conv_base y luego congelarás capas individuales.
dentro de eso.
>>> conv_base.summary()
________________________________________________________________
block5_conv1 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_conv2 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_conv3 (Convolución2D) (Ninguno, 9, 9, 512) 2359808
________________________________________________________________
block5_pool (MaxPooling2D) (Ninguno, 4, 4, 512) 0
==================================================== ===============
Ajustará las últimas tres capas convolucionales, lo que significa que todas las capas hasta
block4_pool debe estar congelado y las capas block5_conv1, block5_conv2 y
block5_conv3 debería poder entrenarse.
¿Por qué no ajustar más capas? ¿Por qué no ajustar toda la base convolucional?
Tú podrías. Pero debes considerar lo siguiente:
Las capas anteriores en la base convolucional codifican características más genéricas y reutilizables,
mientras que las capas superiores codifican características más especializadas. Es más útil
afinar las características más especializadas, porque estas son las que necesitan
ser reutilizado en su nuevo problema. Habría rendimientos rápidamente decrecientes en
afinar las capas inferiores.
Cuantos más parámetros entrenes, mayor será el riesgo de sobreajuste.
La base convolucional tiene 15 millones de parámetros, por lo que sería arriesgado
Intente entrenarlo en su pequeño conjunto de datos.
Por lo tanto, en esta situación, es una buena estrategia ajustar solo las dos o tres capas superiores.
en la base convolucional. Configuremos esto, comenzando desde donde lo dejó en el ejemplo anterior.
conv_base.trainable = Verdadero
set_trainable = Falso
para capa en conv_base.layers:
si capa.nombre == 'block5_conv1':
set_trainable = Verdadero
si set_trainable:
capa.entrenable = Verdadero
demás:
capa.entrenable = Falso
Ahora puede comenzar a ajustar la red. Hará esto con el optimizador RMSProp , utilizando una tasa de
aprendizaje muy baja. La razón para utilizar una tasa de aprendizaje baja es que
desea limitar la magnitud de las modificaciones que realiza en las representaciones
de las tres capas que estás afinando. Las actualizaciones demasiado grandes pueden dañar estas
representaciones.
historia = modelo.fit_generator(
generador_de_trenes,
pasos_por_época=100,
épocas = 100,
validation_data=validation_generator,
pasos_de_validación=50)
Tracemos los resultados usando el mismo código de trazado que antes (ver figuras 5.20 y 5.21).
Estas curvas parecen ruidosas. Para hacerlos más legibles, puede suavizarlos
reemplazando cada pérdida y precisión con promedios móviles exponenciales de estas
cantidades. He aquí una función de utilidad trivial para hacer esto (ver figuras 5.22 y 5.23).
plt.plot(épocas,
smooth_curve(acc), 'bo', label='acc de entrenamiento suavizado')
plt.plot(épocas,
smooth_curve(val_acc), 'b', label='Validación suavizada acc')
plt.title('Precisión del entrenamiento y validación') plt.legend()
plt.figura()
plt.plot(épocas,
smooth_curve(loss), 'bo', label='Pérdida de entrenamiento suavizada')
plt.plot(épocas,
smooth_curve(val_loss), 'b', label='Pérdida de validación suavizada') plt.title('Pérdida de entrenamiento
y validación') plt.legend()
plt.mostrar()
Figura 5.23 Curvas suavizadas para entrenamiento y pérdida de validación para ajuste fino
La curva de precisión de la validación parece mucho más limpia. Estás viendo una agradable mejora
absoluta del 1% en la precisión, de aproximadamente el 96% a más del 97%.
Tenga en cuenta que la curva de pérdidas no muestra ninguna mejora real (de hecho, se está
deteriorando). Quizás se pregunte: ¿cómo podría la precisión mantenerse estable o mejorar si la pérdida
no disminuye? La respuesta es simple: lo que se muestra es un promedio de valores de pérdida puntuales;
pero lo que importa para la precisión es la distribución de los valores de pérdida, no su promedio, porque
la precisión es el resultado de un umbral binario de la probabilidad de clase predicha por el modelo. Es
posible que el modelo aún esté mejorando incluso si esto no se refleja en la pérdida promedio.
Ahora finalmente puede evaluar este modelo con los datos de prueba:
Aquí obtiene una precisión de prueba del 97%. En la competencia original de Kaggle en torno a este
conjunto de datos, este habría sido uno de los mejores resultados. Pero utilizando técnicas modernas de
aprendizaje profundo, logró alcanzar este resultado utilizando solo una pequeña fracción de los datos de
entrenamiento disponibles (alrededor del 10%). ¡Existe una gran diferencia entre poder entrenar con
20.000 muestras y con 2.000 muestras!
5.3.3 Conclusión
Esto es lo que debes aprender de los ejercicios de las dos últimas secciones:
Las convnets son el mejor tipo de modelos de aprendizaje automático para tareas de visión por computadora.
Es posible entrenar uno desde cero incluso con un conjunto de datos muy pequeño, con resultados decentes.
En un conjunto de datos pequeño, el problema principal será el sobreajuste. El aumento de datos es una
forma poderosa de combatir el sobreajuste cuando se trabaja con datos de imágenes. Es
fácil reutilizar una convnet existente en un nuevo conjunto de datos mediante la extracción de funciones.
Esta es una técnica valiosa para trabajar con conjuntos de datos de imágenes pequeños.
Como complemento a la extracción de características, se puede utilizar el ajuste fino, que adapta a un nuevo
problema algunas de las representaciones aprendidas previamente por un modelo existente. Esto lleva el
rendimiento un poco más allá.
Ahora dispone de un sólido conjunto de herramientas para abordar problemas de clasificación de imágenes, en
particular con conjuntos de datos pequeños.
________________________________________________________________
conv2d_7 (Conv2D) (Ninguno, 34, 34, 128) 73856
________________________________________________________________
maxpooling2d_7 (MaxPooling2D) (Ninguno, 17, 17, 128) 0
________________________________________________________________
conv2d_8 (Conv2D) (Ninguno, 15, 15, 128) 147584
________________________________________________________________
maxpooling2d_8 (MaxPooling2D) (Ninguno, 7, 7, 128) 0
________________________________________________________________
flatten_2 (Aplanar) (Ninguno, 6272) 0
________________________________________________________________
dropout_1 (abandono) (Ninguno, 6272) 0
________________________________________________________________
denso_3 (denso) (Ninguno, 512) 3211776
________________________________________________________________
denso_4 (denso) (Ninguno, 1) 513
==================================================== ===============
A continuación, obtendrá una imagen de entrada: una imagen de un gato, que no forma parte de las imágenes que la red
fue entrenado.
img_path = '/Users/fchollet/Downloads/cats_and_dogs_small/test/cats/cat.1700.jpg'
plt.imshow(img_tensor[0])
plt.mostrar()
Para extraer los mapas de características que desea ver, creará un modelo de Keras que toma lotes
de imágenes como entrada y genera las activaciones de todas las capas de convolución y agrupación.
Para hacer esto, utilizará el modelo de clase Keras. Se crea una instancia de un modelo utilizando
dos argumentos: un tensor de entrada (o lista de tensores de entrada) y un tensor de salida (o lista de
tensores de salida). La clase resultante es un modelo Keras, al igual que los modelos secuenciales
con los que está familiarizado, que asigna las entradas especificadas a las salidas especificadas.
Lo que distingue a la clase Model es que permite modelos con múltiples salidas, a diferencia de
Sequential. Para obtener más información sobre la clase Modelo , consulte la sección 7.1.
Listado 5.27 Creación de instancias de un modelo a partir de un tensor de entrada y una lista de tensores de salida
Extrae las salidas de las Crea un modelo que devolverá estas salidas,
ocho capas superiores. dada la entrada del modelo.
Cuando se alimenta una entrada de imagen, este modelo devuelve los valores de las activaciones de
capa en el modelo original. Esta es la primera vez que te encuentras con un modelo de múltiples
salidas en este libro: hasta ahora, los modelos que has visto tenían exactamente una entrada y una salida.
En el caso general, un modelo puede tener cualquier número de entradas y salidas. Este tiene una
entrada y ocho salidas: una salida por activación de capa.
Por ejemplo, esta es la activación de la primera capa de convolución para la entrada de la imagen del gato:
Es un mapa de funciones de 148 × 148 con 32 canales. Intentemos trazar el cuarto canal de
la activación de la primera capa del modelo original (ver figura 5.25).
Este canal parece codificar un detector de borde diagonal. Probemos con el séptimo canal (consulte la figura
5.26), pero tenga en cuenta que sus propios canales pueden variar, porque el canal específico
Los filtros aprendidos por las capas convolucionales no son deterministas.
Éste parece un detector de “punto verde brillante”, útil para codificar ojos de gato. En este
punto, tracemos una visualización completa de todas las activaciones en la red (ver figura
5.27). Extraerá y trazará cada canal en cada uno de los ocho mapas de activación y apilará
los resultados en un tensor de imagen grande, con los canales apilados uno al lado del otro.
nombres_capas = [] para la
Nombres de las capas, para que puedas
capa en model.layers[:8]:
tenerlas como parte de tu trama.
nombres_capas.append(nombre.capa)
mapa de características
n_cols = n_features // imágenes_por_fila display_grid =
np.zeros((tamaño * n_cols, imágenes_por_fila * tamaño))
Coloca en
mosaico los para columna en rango (n_cols): para fila en
Coloca cada filtro en una gran
canales de rango (images_per_row):
cuadrícula horizontal.
activación en esta matriz. imagen_canal = activación_capa[0,
:, :,
col * imágenes_por_fila + fila]
imagen_canal = imagen_canal.mean() imagen_canal /=
Postprocesa la
imagen_canal.std() imagen_canal *= 64 imagen_canal += 128
característica para
imagen_canal =
hacerla visualmente
np.clip(imagen_canal, 0,
agradable
255).astype('uint8') display_grid[col * tamaño: ( columna + 1) * tamaño,
escala = 1. / tamaño
Muestra la cuadrícula
plt.figure(figsize=(escala * display_grid.shape[1],
escala * display_grid.shape[0])) plt.title(layer_name)
plt.grid(False) plt.imshow(display_grid,
aspecto='auto',
cmap='viridis')
Figura 5.27 Activación de cada canal de cada capa en la imagen del gato de prueba
La primera capa actúa como una colección de varios detectores de bordes. En esa etapa, el
Las activaciones retienen casi toda la información presente en la imagen inicial.
A medida que se asciende, las activaciones se vuelven cada vez más abstractas y menos visuales.
interpretables. Comienzan a codificar conceptos de nivel superior como "oreja de gato" y
"ojo de gato." Las presentaciones más altas contienen cada vez menos información sobre el
contenidos visuales de la imagen, y cada vez más información relacionada con la
clase de la imagen.
La escasez de activaciones aumenta con la profundidad de la capa: en la primera
capa, todos los filtros se activan mediante la imagen de entrada; pero en las siguientes capas,
Cada vez hay más filtros en blanco. Esto significa que el patrón codificado por el filtro.
no se encuentra en la imagen de entrada.
modelo = VGG16(pesos='imagenet',
incluir_top=Falso)
Para implementar el descenso de gradiente, necesitará el gradiente de esta pérdida con respecto a la
entrada del modelo. Para hacer esto, utilizará la función de gradientes incluida con el módulo backend
de Keras.
Un truco no obvio que se puede utilizar para ayudar a que el proceso de descenso del gradiente se
desarrolle sin problemas es normalizar el tensor de gradiente dividiéndolo por su norma L2 (la raíz
cuadrada del promedio del cuadrado de los valores en el tensor). Esto garantiza que la magnitud de las
actualizaciones realizadas en la imagen de entrada esté siempre dentro del mismo rango.
Ahora necesita una forma de calcular el valor del tensor de pérdida y el tensor de gradiente, dada una
imagen de entrada. Puede definir una función backend de Keras para hacer esto: iterar es
una función que toma un tensor de Numpy (como una lista de tensores de tamaño 1) y devuelve una lista de
dos tensores de Numpy: el valor de pérdida y el valor de gradiente.
Listado 5.35 Obteniendo valores de salida de Numpy dados los valores de entrada de Numpy
En este punto, puede definir un bucle de Python para realizar un descenso de gradiente estocástico.
El tensor de imagen resultante es un tensor de coma flotante de forma (1, 150, 150, 3), con valores que pueden
no ser números enteros dentro de [0, 255]. Por lo tanto, es necesario posprocesar este tensor para convertirlo
en una imagen visualizable. Lo hace con la siguiente función de utilidad sencilla.
Listado 5.37 Función de utilidad para convertir un tensor en una imagen válida
def deprocess_image(x):
x = x.media() x /= Normaliza el tensor: se centra
(x.std() + 1e5) x *= 0.1 en 0, asegura que std sea 0,1
x += 0,5 x =
Se recorta a [0, 1]
np.clip(x, 0, 1)
x *= 255 x =
np.clip(x, 0, 255).astype('uint8') devuelve x Se convierte a una matriz RGB
Ahora tienes todas las piezas. Juntémoslos en una función de Python que toma como entrada un nombre de
capa y un índice de filtro, y devuelve un tensor de imagen válido que representa el patrón que maximiza la
activación del filtro especificado.
Devuelve la pérdida
graduados /= (K.sqrt(K.media(K.cuadrado(graduados))) + 1e5)
y los graduados dada
iterar = K.function([model.input], [pérdida, graduados]) la imagen de entrada.
Carreras
paso = 1. Comienza desde un
degradado para i en el rango (40): imagen gris con
ascenso para
valor_pérdida, valor_graduación = iterar([input_img_data]) algo de ruido
40 pasos input_img_data += valor_graduación * paso
img = entrada_img_data[0]
devolver deprocess_image(img)
Listado 5.39 Generando una cuadrícula de todos los patrones de respuesta de filtro en una capa
Estas visualizaciones de filtro le dicen mucho sobre cómo ven el mundo las capas de convnet: cada
La capa en un convnet aprende una colección de filtros de modo que sus entradas se puedan expresar.
como una combinación de filtros. Esto es similar a cómo se descompone la transformada de Fourier.
plantea señales en un banco de funciones cosenos. Los filtros en estos bancos de filtros convnet
Vuélvete cada vez más complejo y refinado a medida que avanzas en el modelo:
Los filtros en las capas superiores comienzan a parecerse a las texturas que se encuentran en las imágenes naturales:
plumas, ojos, hojas, etc.
Introduciré una técnica de visualización más: una que sea útil para comprender
qué partes de una imagen determinada llevaron a un convnet a su decisión de clasificación final. Esto es
útil para depurar el proceso de decisión de un convnet, particularmente en el caso de un
error de clasificación. También le permite localizar objetos específicos en una imagen.
Esta categoría general de técnicas se denomina visualización del mapa de activación de clases (CAM) .
y consiste en producir mapas de calor de activación de clases sobre imágenes de entrada. Un mapa de calor de
activación de clase es una cuadrícula 2D de puntuaciones asociadas con una clase de salida específica, calculada
para cada ubicación en cualquier imagen de entrada, indicando qué tan importante es cada ubicación con
respecto de la clase considerada. Por ejemplo, dada una imagen ingresada en una red virtual de perros
contra gatos, la visualización CAM le permite generar un mapa de calor para la clase "gato", que indica qué
tan parecidas a los gatos son las diferentes partes de la imagen, y también un mapa de calor para la clase.
“perro”, que indica cuán parecidas son las partes de la imagen a un perro.
La implementación específica que utilizará es la que se describe en “GradCAM: explicaciones visuales
de redes profundas mediante localización basada en gradientes”. 2 Es muy simple: consiste en tomar el
mapa de características de salida de una capa convolucional, dada una entrada. imagen y ponderar cada
canal en ese mapa de características por el gradiente de la clase con respecto al canal. Intuitivamente, una
forma de entender este truco es ponderar un mapa espacial de “con qué intensidad la imagen de entrada
activa diferentes canales” por “qué tan importante es cada canal con respecto a la clase”, lo que da como
resultado un mapa espacial de "Con qué intensidad la imagen de entrada activa la clase".
Consideremos la imagen de dos elefantes africanos que se muestra en la figura 5.34 (bajo una licencia
Creative Commons), posiblemente una madre y su cría, paseando por la sabana. Convirtamos esta
imagen en algo que el modelo VGG16 pueda leer: el modelo se entrenó en imágenes de tamaño 224 ×
244, preprocesadas de acuerdo con algunas reglas empaquetadas en la función de utilidad
keras.applications.vgg16.preprocess_input. Por lo tanto, debe cargar la imagen, cambiar su tamaño a 224
× 224, convertirla en un tensor Numpy float32 y aplicar estas reglas de preprocesamiento.
2
Ramprasaath R. Selvaraju et al., arXiv (2017), https://arxiv.org/abs/ 1610.02391.
img_path = '/Users/fchollet/Downloads/creative_commons_elephant.jpg'
Ahora puede ejecutar la red previamente entrenada en la imagen y decodificar su vector de predicción
a un formato legible por humanos:
Las tres clases principales previstas para esta imagen son las siguientes:
>>> np.argmax(preds[0])
386
Para visualizar qué partes de la imagen se parecen más a un elefante africano, configuremos
el proceso GradCAM .
iterar = K.función([modelo.entrada],
[graduados_agrupados, last_conv_layer.output[0]])
0 24 6 8 10 12
0
10
Finalmente, usarás OpenCV para generar una imagen que superponga la imagen original.
en el mapa de calor que acaba de obtener (consulte la figura 5.36).
cv2.imwrite('/Users/fchollet/Downloads/elephant_cam.jpg', superimposed_img)
Figura 5.36 Superposición del mapa de calor de activación de clases en la imagen original
¿ Por qué la cadena pensó que esta imagen contenía un elefante africano?
¿ Dónde está ubicado el elefante africano en la imagen?
En particular, es interesante observar que las orejas de la cría de elefante están fuertemente activadas:
probablemente así es como la red puede diferenciar entre África y África.
Elefantes indios.
Las convnets son la mejor herramienta para atacar los problemas de clasificación visual.
Las representaciones que aprenden son fáciles de inspeccionar: ¡las convnets son lo
opuesto a las cajas negras!
Ahora eres capaz de entrenar tu propio convnet desde cero para resolver un
Problema de clasificación de imágenes.
Sabe cómo utilizar un convnet previamente entrenado para realizar extracción de características y
sintonia FINA.
Puedes generar visualizaciones de los filtros aprendidos por tus convnets, así como mapas
de calor de la actividad de clase.
Aprendizaje profundo
para texto y secuencias.
el procesamiento de secuencias
Este capítulo explora modelos de aprendizaje profundo que pueden procesar texto (entendido como
secuencias de palabras o secuencias de caracteres), series temporales y secuencias de datos en
general. Los dos algoritmos fundamentales de aprendizaje profundo para el procesamiento de secuencias
son las redes neuronales recurrentes y las convnets 1D , la versión unidimensional de las convnets 2D
que cubrimos en los capítulos anteriores. Discutiremos ambos enfoques en este capítulo.
178
179
Los ejemplos de este capítulo se centran en dos tareas específicas: el análisis de sentimientos en el conjunto de datos IMDB ,
una tarea que abordamos anteriormente en el libro, y el pronóstico de temperatura. Pero las técnicas demostradas para estas
dos tareas son relevantes para todas las aplicaciones que acabamos de enumerar y muchas más.
una de las formas más extendidas de datos de secuencia. Puede entenderse como una secuencia de
caracteres o una secuencia de palabras, pero lo más común es trabajar a nivel de palabras. Los modelos
de procesamiento de secuencias de aprendizaje profundo que se presentan en las siguientes secciones
pueden usar texto para producir una forma básica de comprensión del lenguaje natural, suficiente para
aplicaciones que incluyen clasificación de documentos, análisis de sentimientos, identificación de autores
e incluso respuesta a preguntas (QA). (en un contexto restringido). Por supuesto, tenga en cuenta a lo
largo de este capítulo que ninguno de estos modelos de aprendizaje profundo comprende realmente el
texto en un sentido humano; más bien, estos modelos pueden mapear la estructura estadística del
lenguaje escrito, lo cual es suficiente para resolver muchas tareas textuales simples. El aprendizaje
profundo para el procesamiento del lenguaje natural es el reconocimiento de patrones aplicado a palabras,
oraciones y párrafos, de la misma manera que la visión por computadora es el reconocimiento de patrones
aplicado a píxeles.
Como todas las demás redes neuronales, los modelos de aprendizaje profundo no toman como
entrada texto sin formato: solo funcionan con tensores numéricos. Vectorizar texto es el proceso de
transformar texto en tensores numéricos. Esto se puede hacer de varias maneras:
En conjunto, las diferentes unidades en las que se puede dividir el texto (palabras, caracteres o ngramas)
se denominan tokens, y dividir el texto en dichos tokens se denomina tokenización. Todos los procesos
de vectorización de texto consisten en aplicar algún esquema de tokenización y luego asociar vectores
numéricos con los tokens generados. Estos vectores, empaquetados en tensores de secuencia, se
introducen en redes neuronales profundas. Hay varias formas de asociar un vector con un token. En esta
sección, presentaré dos de los principales: codificación onehot de tokens e incrustación de tokens
(normalmente utilizada exclusivamente para palabras y denominada incrustación de palabras). El resto de
esta sección explica estas técnicas y muestra cómo usarlas para pasar de texto sin formato a un tensor
Numpy que puede enviar a una red Keras.
Envía
Fichas
“el”, “gato”, “sat”, “on”, “el”, “mat”, “.”
He aquí un ejemplo sencillo. Considere la frase "El gato se sentó en la alfombra". Puede
descomponerse en el siguiente conjunto de 2 gramos:
Debido a que la bolsa de palabras no es un método de tokenización que preserva el orden (los
tokens generados se entienden como un conjunto, no como una secuencia, y la estructura general
de las oraciones se pierde), tiende a usarse en modelos de procesamiento del lenguaje superficial
en lugar de modelos de aprendizaje profundo. Extraer ngramas es una forma de ingeniería de
características, y el aprendizaje profundo elimina este tipo de enfoque rígido y frágil, reemplazándolo
con el aprendizaje de características jerárquico. Las redes neuronales unidimensionales y las redes
neuronales recurrentes, que se presentan más adelante en este capítulo, son capaces de aprender
representaciones de grupos de palabras y caracteres sin que se les informe explícitamente sobre la
existencia de dichos grupos, al observar secuencias continuas de palabras o caracteres. Por esta
razón, no abordaremos más los ngramas en este libro. Pero tenga en cuenta que son una
herramienta de ingeniería de funciones potente e inevitable cuando se utilizan modelos de
procesamiento de texto ligeros y superficiales, como la regresión logística y los bosques aleatorios.
token_index = {} para
muestra en muestras: para palabra
en muestra.split():
si la palabra no está en token_index:
token_index[palabra] = len(token_index) + 1
longitud_max = 10
cadena de importación
muestras = ['El gato se sentó en la alfombra.', 'El perro se comió mi tarea.'] caracteres = string.printable
token_index = dict(zip(range(1, len(caracteres)
+ 1), caracteres))
max_length = 50
resultados = np.zeros((len(samples), max_length, max(token_index.keys()) + 1)) para i, muestra en enumerate(samples):
Tenga en cuenta que Keras tiene utilidades integradas para realizar codificación onehot de texto a
nivel de palabra o de carácter, a partir de datos de texto sin formato. Debería utilizar estas utilidades,
porque se encargan de una serie de características importantes, como eliminar caracteres especiales
de cadenas y solo tener en cuenta las N palabras más comunes en su conjunto de datos (una
restricción común, para evitar tratar con espacios vectoriales de entrada muy grandes). ).
tokenizador = Tokenizador(num_words=1000)
construye
tokenizer.fit_on_texts(muestras)
Convierte cadenas en
el listas de índices enteros
secuencias = tokenizer.texts_to_sequences(muestras)
palabra
índice one_hot_results = tokenizer.texts_to_matrix(muestras, modo='binario')
índice_palabra = tokenizer.índice_palabra
print('Se encontraron %s tokens únicos.' % len(word_index))
¿Cómo se puede
recuperar el índice de
También puede obtener directamente las
palabras que se calculó?
representaciones binarias únicas. Este
tokenizador admite modos de vectorización
distintos de la codificación onehot.
Una variante de la codificación onehot es el llamado truco de hash onehot, que puede utilizar
cuando la cantidad de tokens únicos en su vocabulario es demasiado grande para manejarla explícitamente.
En lugar de asignar explícitamente un índice a cada palabra y mantener una referencia de estas
índices en un diccionario, puede dividir palabras en vectores de tamaño fijo. Esto es típicamente
hecho con una función hash muy ligera. La principal ventaja de este método es
que elimina el mantenimiento de un índice de palabras explícito, lo que ahorra memoria y
permite la codificación en línea de los datos (puede generar vectores simbólicos de inmediato, antes
has visto todos los datos disponibles). El único inconveniente de este enfoque es que es
susceptible a colisiones de hash: dos palabras diferentes pueden terminar con el mismo hash, y
Posteriormente, cualquier modelo de aprendizaje automático que analice estos hashes no podrá decirlo.
la diferencia entre estas palabras. La probabilidad de colisiones de hash disminuye cuando
Listado 6.4 Codificación onehot a nivel de palabra con truco de hash (ejemplo de juguete)
dimensionalidad = 1000
longitud_max = 10
Otra forma popular y poderosa de asociar un vector con una palabra es el uso de términos densos.
vectores de palabras, también llamados incrustaciones de palabras. Mientras que los vectores obtenidos mediante onehot
La codificación es binaria, escasa (en su mayoría compuesta de ceros) y de muy alta dimensión (lo mismo).
dimensionalidad como el número de palabras en el vocabulario), las incrustaciones de palabras son vectores de punto
flotante de baja dimensión (es decir, vectores densos, a diferencia de vectores dispersos); ver figura 6.2. A diferencia de
los vectores de palabras obtenidos mediante codificación onehot, word
Las incorporaciones se aprenden de los datos. Es común ver incrustaciones de palabras que son
256 dimensiones, 512 dimensiones o 1024 dimensiones cuando se trata de objetos muy grandes.
vocabularios. Por otro lado, las palabras de codificación onehot generalmente conducen a vectores.
que tienen 20.000 dimensiones o más (capturando un vocabulario de 20.000 fichas, en
este caso). Por lo tanto, las incrustaciones de palabras contienen más información en muchas menos dimensiones.
Aprenda incrustaciones de palabras junto con la tarea principal que le interesa (como la clasificación de
documentos o la predicción de sentimientos). En esta configuración, comienza con vectores de palabras
aleatorios y luego aprende vectores de palabras de la misma manera que aprende los vectores de palabras.
Pesos de una red neuronal.
Cargue en su modelo incrustaciones de palabras que se calcularon previamente utilizando una tarea de
aprendizaje automático diferente a la que está intentando resolver. estos se llaman
incrustaciones de palabras previamente entrenadas.
Veamos ambos.
La forma más sencilla de asociar un vector denso con una palabra es elegir el vector en
aleatorio. El problema con este enfoque es que el espacio de incrustación resultante tiene
sin estructura: por ejemplo, las palabras preciso y exacto pueden terminar completamente
incrustaciones diferentes, aunque son intercambiables en la mayoría de las oraciones. Es
Es difícil para una red neuronal profunda entender algo tan ruidoso y desestructurado.
espacio de incrustación.
Para ser un poco más abstracto, las relaciones geométricas entre vectores de palabras
debe reflejar las relaciones semánticas entre estas palabras. Las incrustaciones de palabras son
destinado a mapear el lenguaje humano en un espacio geométrico. Por ejemplo, de forma razonable
incrustando espacio, se esperaría que los sinónimos estuvieran incrustados en vectores de palabras similares; y en
general, se esperaría que la distancia geométrica (como la distancia L2)
entre dos vectores de palabras cualesquiera para relacionarse con la distancia semántica entre las palabras
asociadas (las palabras que significan cosas diferentes están incrustadas en puntos muy alejados de
entre sí, mientras que las palabras relacionadas están más cercanas). Además de la distancia, es posible que desees
direcciones específicas en el espacio de incrustación para que sean significativas. Para que esto quede más claro, vamos
Mire un ejemplo concreto.
En la figura 6.3, hay cuatro palabras incrustadas en un plano 2D :
gato, perro, lobo y tigre. Con las representaciones vectoriales tenemos
1 Lobo
elegido aquí, algunas relaciones semánticas entre estos Tigre
Las palabras se pueden codificar como transformaciones geométricas. Para
Por ejemplo, el mismo vector nos permite pasar del gato al tigre. Perro
Gato
y del perro al lobo: este vector podría interpretarse como el
Vector “de mascota a animal salvaje”. De manera similar, otro vector 0 X
pasemos del perro al gato y del lobo al tigre, lo que podría 0 1
interpretarse como un vector “de canino a felino”. Figura 6.3 Un ejemplo de juguete
En espacios de incrustación de palabras del mundo real, ejemplos comunes de un espacio para incrustar palabras
¿Existe algún espacio ideal para incrustar palabras que mapee perfectamente el lenguaje humano y pueda
usarse para cualquier tarea de procesamiento del lenguaje natural? Posiblemente, pero nosotros
Todavía tengo que calcular algo por el estilo. Además, no existe el lenguaje humano : hay muchos lenguajes
diferentes y no son isomórficos, porque un lenguaje es el reflejo de una cultura y un contexto específicos. Pero más
Pragmáticamente, lo que constituye un buen espacio para incrustar palabras depende en gran medida de su tarea:
El espacio perfecto para incrustar palabras para un modelo de análisis de sentimientos de reseñas de películas en
inglés puede parecer diferente del espacio de incrustación perfecto para un modelo de clasificación de documentos
legales en inglés, debido a la importancia de ciertos
Las relaciones semánticas varían de una tarea a otra.
Por tanto, es razonable aprender un nuevo espacio de incrustación con cada nueva tarea. Afortunadamente, la
retropropagación hace que esto sea fácil y Keras lo hace aún más fácil. Se trata de
aprender los pesos de una capa: la capa de incrustación .
La capa de incrustación se entiende mejor como un diccionario que asigna índices enteros.
(que representan palabras específicas) hasta vectores densos. Toma números enteros como entrada, busca
estos números enteros en un diccionario interno y devuelve los vectores asociados. Es efectivamente una búsqueda
en un diccionario (ver figura 6.4).
La capa de incrustación toma como entrada un tensor 2D de números enteros, de forma (muestras,
secuencia_longitud), donde cada entrada es una secuencia de números enteros. puede incrustar
secuencias de longitudes variables: por ejemplo, podría introducir la capa de incrustación en
el ejemplo anterior lotes con formas (32, 10) (lote de 32 secuencias de longitud
10) o (64, 15) (lote de 64 secuencias de longitud 15). Todas las secuencias de un lote deben
Sin embargo, tienen la misma longitud (porque es necesario empaquetarlos en un solo tensor),
por lo que las secuencias que son más cortas que otras deben rellenarse con ceros y las secuencias
que sean más largos deben truncarse.
Esta capa devuelve un tensor de forma de punto flotante 3D (muestras, secuencia_
longitud, incrustación_dimensionalidad). Un tensor 3D de este tipo puede procesarse mediante
una capa RNN o una capa de convolución 1D (ambas se presentarán a continuación)
secciones).
Cuando crea una instancia de una capa de incrustación , sus pesos (su diccionario interno de
vectores simbólicos) son inicialmente aleatorios, al igual que cualquier otra capa. Durante el entrenamiento, estos
Los vectores de palabras se ajustan gradualmente mediante retropropagación, estructurando el espacio en
algo que el modelo downstream puede explotar. Una vez completamente entrenado, la incrustación
El espacio mostrará mucha estructura, un tipo de estructura especializada para el problema específico para el cual
estás entrenando tu modelo.
Apliquemos esta idea a la tarea de predicción de sentimientos de reseñas de películas de IMDB que
ya estás familiarizado. Primero, preparará rápidamente los datos. Restringirás el
reseñas de películas hasta las 10.000 palabras más comunes (como lo hizo la primera vez que
trabajó con este conjunto de datos) y cortó las reseñas después de solo 20 palabras. La red
aprenda incrustaciones de 8 dimensiones para cada una de las 10,000 palabras, convierta el número entero de entrada
secuencias (tensor entero 2D) en secuencias incrustadas ( tensor flotante 3D), aplanar el
tensor a 2D y entrenar una sola capa Densa en la parte superior para su clasificación.
Listado 6.6 Cargando los datos IMDB para usarlos con una capa de incrustación
Listado 6.7 Usando una capa de incrustación y un clasificador en los datos IMDB
modelo = Secuencial()
model.add(Incrustación(10000, 8, input_length=maxlen))
model.add(Aplanar()) Agrega el
clasificador en la parte superior
model.add(Denso(1, activación='sigmoide'))
model.compile(optimizador='rmsprop', pérdida='binary_crossentropy', métricas=['acc'])
Resumen Modelo()
Obtienes una precisión de validación de ~76%, lo cual es bastante bueno considerando que estás
Solo mirando las primeras 20 palabras en cada reseña. Pero tenga en cuenta que simplemente aplanar el
secuencias incrustadas y entrenar una sola capa Densa en la parte superior conduce a un modelo que
trata cada palabra en la secuencia de entrada por separado, sin considerar las palabras entre palabras.
relaciones y estructura de la oración (por ejemplo, este modelo probablemente trataría tanto
“esta película es una bomba” y “esta película es la bomba” como críticas negativas). Es
Es mucho mejor agregar capas recurrentes o capas convolucionales 1D encima de las secuencias
incrustadas para aprender características que tengan en cuenta cada secuencia como un todo.
En eso nos centraremos en las próximas secciones.
A veces, tienes tan pocos datos de entrenamiento disponibles que no puedes utilizarlos.
solo para aprender una incorporación apropiada de su vocabulario a una tarea específica. Qué hacer
haces entonces?
En lugar de aprender a integrar palabras junto con el problema que deseas resolver,
puede cargar vectores de incrustación desde un espacio de incrustación precalculado que
lo que sabemos está altamente estructurado y exhibe propiedades útiles, que capturan lo genérico.
aspectos de la estructura del lenguaje. La razón detrás del uso de incrustaciones de palabras previamente entrenadas
en el procesamiento del lenguaje natural es muy similar a la del uso de convnets previamente entrenadas en la
clasificación de imágenes: no tiene suficientes datos disponibles para aprender verdaderamente.
funciones potentes por su cuenta, pero espera que las funciones que necesita sean bastante
genérico, es decir, características visuales comunes o características semánticas. En este caso, hace
Tiene sentido reutilizar características aprendidas en un problema diferente.
Estas incrustaciones de palabras generalmente se calculan utilizando estadísticas de aparición de palabras.
(observaciones sobre qué palabras coexisten en oraciones o documentos), utilizando una variedad de
técnicas, algunas que involucran redes neuronales, otras no. La idea de un espacio denso y de baja dimensión para
incrustar palabras, calculado de forma no supervisada, fue explorada inicialmente por Bengio et al. a principios de la
década de 2000,1 pero sólo empezó a despegar en
aplicaciones de investigación e industria después del lanzamiento de uno de los esquemas de incrustación de
palabras más famosos y exitosos: el algoritmo Word2vec (https://code.google.com/
archivo/p/word2vec), desarrollado por Tomas Mikolov en Google en 2013. Word2vec
Las dimensiones capturan propiedades semánticas específicas, como el género.
Existen varias bases de datos precalculadas de incrustaciones de palabras que puede descargar y usar en una
capa de Keras Embedding . Word2vec es uno de ellos. Otro popular
uno se llama Vectores globales para representación de palabras (GloVe, https://nlp.stanford
.edu/projects/glove), que fue desarrollado por investigadores de Stanford en 2014. Este
La técnica de incrustación se basa en factorizar una matriz de estadísticas de coocurrencia de palabras. Sus
desarrolladores han puesto a disposición incorporaciones precalculadas para millones de
Tokens en inglés, obtenidos de datos de Wikipedia y datos de Common Crawl.
Veamos cómo puedes empezar a utilizar las incrustaciones de GloVe en un modelo de Keras.
El mismo método es válido para incrustaciones de Word2vec o cualquier otra incrustación de palabras.
base de datos. También utilizará este ejemplo para actualizar las técnicas de tokenización de texto.
presentado hace unos párrafos: comenzará desde el texto sin formato y avanzará hacia arriba.
6.1.3 Poniéndolo todo junto: desde texto sin formato hasta incrustaciones de palabras
1
Yoshua Bengio et al., Modelos de lenguaje probabilístico neuronal (Springer, 2003).
Primero, dirígete a http://mng.bz/0tIo y descargue el conjunto de datos IMDB sin procesar . Descomprimirlo.
Ahora, recopilemos las revisiones de capacitación individuales en una lista de cadenas, una cadena por
revisar. También recopilará las etiquetas de reseñas (positivas/negativas) en una lista de etiquetas .
Listado 6.8 Procesando las etiquetas de los datos IMDB sin procesar
etiquetas = []
textos = []
if fname[4:] == '.txt': f =
open(os.path.join(dir_name, fname)) texts.append(f.read())
f.close() if label_type == 'neg ':
etiquetas.append(0)
demás:
etiquetas.append(1)
Vectoricemos el texto y preparemos una división de entrenamiento y validación, utilizando los conceptos
introducidos anteriormente en esta sección. Debido a que las incrustaciones de palabras previamente
entrenadas están destinadas a ser particularmente útiles en problemas donde hay pocos datos de
entrenamiento disponibles (de lo contrario, es probable que las incrustaciones de tareas específicas los
superen), agregaremos el siguiente giro: restringir los datos de entrenamiento a las primeras 200 muestras.
Así aprenderás a clasificar reseñas de películas después de ver sólo 200 ejemplos.
Listado 6.9 Tokenización del texto de los datos IMDB sin procesar
índices = np.arange(datos.forma[0])
Divide los datos en un conjunto de entrenamiento y
np.random.shuffle(índices) datos = un conjunto de validación, pero primero mezcla los
datos[índices] datos, porque estás comenzando con datos en los que las
etiquetas = etiquetas[índices] muestras están ordenadas (primero todas las negativas,
luego todas las positivas)
x_train = datos[:muestras_de_formación] y_train =
etiquetas[:muestras_de_formación]
x_val = datos[muestras_entrenamiento: muestras_entrenamiento + muestras_validación] y_val =
etiquetas[muestras_entrenamiento: muestras_entrenamiento + muestras_validación]
Analicemos el archivo descomprimido (un archivo .txt) para crear un índice que asigne palabras (como
cadenas) a su representación vectorial (como vectores numéricos).
guante_dir = '/Usuarios/fchollet/Descargas/glove.6B'
embeddings_index = {} f =
open(os.path.join(glove_dir, 'glove.6B.100d.txt')) para la línea en f:
A continuación, creará una matriz de incrustación que podrá cargar en una capa de incrustación . Debe ser
una matriz de forma (max_words, embedding_dim), donde cada entrada i contiene el vector dimensional
embedding_dim para la palabra del índice i en el índice de palabras de referencia (creado durante la
tokenización). Tenga en cuenta que se supone que el índice 0 no representa ninguna palabra o token: es
un marcador de posición.
incrustación_dim = 100
DEFINIR UN MODELO
modelo = Sequential()
model.add(Embedding(max_words, embedding_dim, input_length=maxlen)) model.add(Flatten())
model.add(Dense(32,
activación='relu')) model.add(Dense(1 , activación='sigmoide'))
model.summary()
La capa de incrustación tiene una única matriz de peso: una matriz flotante 2D donde cada entrada i es el vector
de palabras que debe asociarse con el índice i. Suficientemente simple. Cargue la matriz GloVe que preparó en
la capa Incrustar , la primera capa del modelo.
model.layers[0].set_weights([embedding_matrix]) model.layers[0].trainable
= False
Además, congelará la capa de Incrustación (establecerá su atributo entrenable en Falso), siguiendo el mismo
razonamiento con el que ya está familiarizado en el contexto de las características de convnet previamente
entrenadas: cuando las partes de un modelo están preentrenadas (como su capa de Incrustación ) y las partes
se inicializan aleatoriamente (como su clasificador), las partes previamente entrenadas no deben actualizarse
durante el entrenamiento, para evitar olvidar lo que ya saben. Las grandes actualizaciones de gradiente
provocadas por las capas inicializadas aleatoriamente perturbarían las funciones ya aprendidas.
model.compile(optimizer='rmsprop',
pérdida='binary_crossentropy',
metrics=['acc']) historial =
model.fit(x_train, y_train, epochs=10, batch_size=32,
validation_data=(x_val,
y_val)) model.save_weights('pre_trained_glove_model.h5')
Ahora, trace el desempeño del modelo a lo largo del tiempo (ver figuras 6.5 y 6.6).
plt.figura()
plt.mostrar()
También puede entrenar el mismo modelo sin cargar las incrustaciones de palabras previamente
entrenadas y sin congelar la capa de incrustación. En ese caso, aprenderá a incrustar los tokens de
entrada para tareas específicas, que generalmente es más potente que las incrustaciones de palabras
previamente entrenadas cuando hay muchos datos disponibles. Pero en este caso, sólo tienes 200
muestras de entrenamiento. Probémoslo (ver figuras 6.7 y 6.8).
Listado 6.16 Entrenando el mismo modelo sin incrustaciones de palabras previamente entrenadas
modelo = Sequential()
model.add(Embedding(max_words, embedding_dim, input_length=maxlen)) model.add(Flatten())
model.add(Dense(32,
activación='relu')) model.add(Dense(1 , activación='sigmoide'))
model.summary()
model.compile(optimizer='rmsprop',
pérdida='binary_crossentropy',
metrics=['acc']) historial
= model.fit(x_train, y_train, epochs=10, batch_size=32,
validation_data=(x_val,
y_val))
La precisión de la validación se estanca en los 50 grados. En este caso, las incrustaciones de palabras previamente entrenadas
superan las incorporaciones aprendidas conjuntamente. Si aumentas el número de muestras de entrenamiento, rápidamente
dejará de ser así; pruébalo como ejercicio.
Finalmente, evaluemos el modelo con los datos de prueba. Primero, necesitas tokenizar la prueba.
datos.
etiquetas = []
textos = []
f.close() si
label_type == 'neg':
etiquetas.append(0)
demás:
etiquetas.append(1)
model.load_weights('pre_trained_glove_model.h5') model.evaluate(x_test,
y_test)
Obtiene una precisión de prueba espantosa del 56%. ¡Trabajar con sólo un puñado de muestras de entrenamiento es
difícil!
6.1.4 Conclusión
Ahora puedes hacer lo siguiente:
Convierta el texto sin formato en algo que una red neuronal pueda procesar .
Utilice la capa de incrustación en un modelo de Keras para aprender a incrustar tokens de tareas específicas.
golpes
Utilice incrustaciones de palabras previamente entrenadas para obtener un impulso adicional en pequeñas palabras naturales.
que cambia es que este punto de datos ya no se procesa en un Figura 6.9 Una red recurrente:
solo paso; más bien, la red recorre internamente elementos de una red con un bucle
secuencia.
Para aclarar estas nociones de bucle y estado , implementemos el paso directo de un RNN
de juguete en Numpy. Este RNN toma como entrada una secuencia de vectores, que codificará
como un tensor de tamaño 2D (timesteps, input_features). Recorre pasos de tiempo y, en cada
paso de tiempo, considera su estado actual en t y la entrada en t (de forma (input_features,) y los
combina para obtener la salida en t. Luego, establecerá el estado para el El siguiente paso será
esta salida anterior. Para el primer paso de tiempo, la salida anterior no está definida; por lo
tanto, no hay un estado actual. Por lo tanto, inicializarás el estado como un vector todo cero
llamado estado inicial de la red. .
estado_t = 0 El estado en t
para input_t en input_sequence: Itera sobre elementos de secuencia.
salida_t = f(entrada_t, estado_t)
estado_t = salida_t La salida anterior se convierte en el estado de la siguiente iteración.
estado_t = 0
para input_t en input_sequence:
salida_t = activación(punto(W, entrada_t) + punto(U, estado_t) + b)
estado_t = salida_t
Para que estas nociones sean absolutamente inequívocas, escribamos una implementación ingenua de Numpy del paso directo del RNN
simple.
estado_t = np.zeros((características_salida,))
W = np.random.random((características_salida, características_entrada))
Crea matrices de
U = np.random.random((características_salida, características_salida))
b = np.random.random((características_salida,)) peso aleatorias.
input_t es un vector de
salidas_sucesivas = []
forma (input_features,).
para input_t en entradas:
salida_t = np.tanh(np.dot(W, entrada_t) + np.punto(U, estado_t) + b)
salidas_sucesivas.append(salida_t)
estado_t = salida_t
Bastante fácil: en resumen, un RNN es un bucle for que reutiliza cantidades calculadas
durante la iteración anterior del ciclo, nada más. Por supuesto, hay muchos
diferentes RNN que se ajusten a esta definición que podría construir; este ejemplo es uno de los
formulaciones RNN más simples . Los RNN se caracterizan por su función escalonada, como la
siguiente función en este caso (ver figura 6.10):
salida_t = np.tanh(np.dot(W, entrada_t) + np.punto(U, estado_t) + b)
salida_t =
activación(
... W•entrada_t + ...
Estado t U•estado_t + Estado t+1
bo)
Hay una pequeña diferencia: SimpleRNN procesa lotes de secuencias, como todos los demás.
Capas de Keras, no una sola secuencia como en el ejemplo de Numpy. Esto significa que requiere entradas
de forma (batch_size, timesteps, input_features), en lugar de (timesteps,
características_entrada).
Como todas las capas recurrentes en Keras, SimpleRNN se puede ejecutar en dos modos diferentes:
puede devolver las secuencias completas de salidas sucesivas para cada paso de tiempo (una decena de
forma 3D (tamaño_batch, pasos de tiempo, características_de_salida)) o solo la última salida para
cada secuencia de entrada (un tensor de forma 2D (tamaño_batch, características_salida)). Estos
Dos modos están controlados por el argumento del constructor return_sequences . Miremos
Vea un ejemplo que usa SimpleRNN y devuelve solo la salida en el último paso:
________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============
A veces resulta útil apilar varias capas recurrentes una tras otra para
aumentar el poder de representación de una red. En tal configuración, debes obtener todos
de las capas intermedias para devolver la secuencia completa de salidas:
Entrenemos una red recurrente simple usando una capa de incrustación y una capa SimpleRNN .
modelo = Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activación='sigmoide'))
Ahora, mostremos la pérdida y precisión del entrenamiento y la validación (ver figuras 6.11 y 6.12).
plt.figura()
plt.mostrar()
Como recordatorio, en el capítulo 3, el primer enfoque ingenuo de este conjunto de datos le permitió
obtener una precisión de prueba del 88 %. Desafortunadamente, esta pequeña red recurrente no
funciona bien en comparación con esta línea de base (solo 85% de precisión de validación). Parte del
problema es que sus entradas solo consideran las primeras 500 palabras, en lugar de secuencias
completas; por lo tanto, el RNN tiene acceso a menos información que el modelo de referencia anterior. El resto de
el problema es que SimpleRNN no es bueno procesando secuencias largas, como texto.
Otros tipos de capas recurrentes funcionan mucho mejor. Veamos algunas capas más avanzadas.
salida_t =
activación(
... Wo•entrada_t + ...
Estado t Uo•estado_t + Estado t+1
bo)
2
Véase, por ejemplo, Yoshua Bengio, Patrice Simard y Paolo Frasconi, “Learning LongTerm Dependencies
con gradiente descenso es difícil”, IEEE Transactions on Neural Networks 5, no. 2 (1994).
3
Sepp Hochreiter y Jürgen Schmidhuber, “Memoria a largo plazo”, Neural Computation 9, no. 8 (1997).
Agreguemos a esta imagen un flujo de datos adicional que transporta información a través de pasos de
tiempo. Llame a sus valores en diferentes pasos de tiempo Ct, donde C significa acarreo. Esta información
tendrá el siguiente impacto en la celda: se combinará con la entrada
conexión y la conexión recurrente (a través de una transformación densa: un producto escalar
con una matriz de peso seguida de una adición de sesgo y la aplicación de una función de activación), y
afectará el estado que se envía al siguiente paso de tiempo (a través de una activación
función y una operación de multiplicación). Conceptualmente, el flujo de datos de acarreo es una forma de
modular la siguiente salida y el siguiente estado (ver figura 6.14). Sencillo hasta ahora.
Connecticut
salida_t = Connecticut
activación(
Wo•entrada_t +
... ...
Uo•estado_t +
Estado t Estado t+1
Vo•c_t + bo)
Ahora la sutileza: la forma en que se calcula el siguiente valor del flujo de datos de acarreo. Implica
tres transformaciones distintas. Los tres tienen la forma de una celda SimpleRNN :
y = activación(punto(estado_t, U) + punto(entrada_t, W) + b)
Pero las tres transformaciones tienen sus propias matrices de peso, con las que indexarás
las letras i, f y k. Esto es lo que tienes hasta ahora (puede parecer un poco arbitrario, pero ten cuidado).
conmigo).
Obtienes el nuevo estado de acarreo (el siguiente c_t) combinando i_t, f_t y k_t.
Agregue esto como se muestra en la figura 6.15. Y eso es. No es tan complicado, solo un poco
complejo.
llevar llevar
Connecticut
salida_t = Connecticut
activación(
Wo•entrada_t +
... ...
Estado t Uo•estado_t + Estado t+1
Vo•c_t + bo)
Si quieres ponerte filosófico, puedes interpretar cuál es cada una de estas operaciones.
destinado a hacer. Por ejemplo, se puede decir que multiplicar c_t y f_t es una forma de olvidar deliberadamente
información irrelevante en el flujo de datos de transporte. Mientras tanto, i_t y k_t proporcionan información
sobre el presente, actualizando el seguimiento de acarreo con nueva información.
Pero al final del día, estas interpretaciones no significan mucho, porque lo que estas
operaciones realmente lo hacen está determinado por el contenido de los pesos que parametrizan
a ellos; y los pesos se aprenden de un extremo a otro, comenzando de nuevo con cada
ronda de entrenamiento, lo que hace imposible atribuir a tal o cual operación una determinada
objetivo. La especificación de una celda RNN (como se acaba de describir) determina su espacio de hipótesis:
el espacio en el que buscará una buena configuración del modelo durante
entrenamiento, pero no determina lo que hace la célula; eso depende del peso de las celdas.
La misma celda con diferentes pesos puede estar haciendo cosas muy diferentes. Por lo tanto, la combinación
de operaciones que componen una celda RNN se interpreta mejor como un conjunto de restricciones.
en su búsqueda, no como un diseño en el sentido de ingeniería.
Para un investigador, parece que la elección de tales restricciones –la cuestión de cómo
implementar celdas RNN ; es mejor dejarlo en manos de algoritmos de optimización (como algoritmos genéticos
o procesos de aprendizaje por refuerzo) que a los ingenieros humanos. Y en el futuro,
así es como construiremos redes. En resumen: no necesitas entender nada.
sobre la arquitectura específica de una celda LSTM ; Como ser humano, no debería ser tu trabajo
entiendelo. Solo tenga en cuenta lo que debe hacer la celda LSTM : permitir que la información pasada se
reinyecte más adelante, combatiendo así el problema del gradiente de fuga.
valores predeterminados. Keras tiene buenos valores predeterminados y las cosas casi siempre “simplemente funcionarán” sin
que usted tenga que perder tiempo ajustando los parámetros manualmente.
modelo = Sequential()
model.add(Embedding(max_features, 32))
model.add(LSTM(32))
model.add(Dense(1, activación='sigmoide'))
model.compile(optimizador='rmsprop',
pérdida='binary_crossentropy',
métricas=['acc'])
historial = model.fit(input_train, y_train, épocas=10,
tamaño_de_lote=128,
división_de_validación=0.2)
Esta vez, logra hasta un 89% de precisión en la validación. No está mal: ciertamente mucho mejor que la red
SimpleRNN (esto se debe en gran medida a que LSTM sufre mucho menos el problema del gradiente de fuga)
y ligeramente mejor que el enfoque totalmente conectado del capítulo 3, aunque esté viendo menos datos que
en Capítulo 3.
Estás truncando secuencias después de 500 pasos de tiempo, mientras que en el capítulo 3 estabas
considerando secuencias completas.
Pero este resultado no es innovador para un enfoque computacional tan intensivo. ¿Por qué LSTM no
funciona mejor? Una razón es que no hizo ningún esfuerzo por ajustar los hiperparámetros, como la
dimensionalidad de las incrustaciones o la dimensionalidad de salida de LSTM . Otra puede ser la falta de
regularización. Pero, sinceramente, la razón principal es que analizar la estructura global a largo plazo de las
revisiones (en qué es bueno LSTM ) no es útil para un problema de análisis de sentimientos. Un problema tan
básico se resuelve bien observando qué palabras aparecen en cada revisión y con qué frecuencia. Eso es lo
que examinó el primer enfoque totalmente conectado. Pero existen problemas de procesamiento del lenguaje
natural mucho más difíciles, donde la fortaleza de LSTM se hará evidente: en particular, la respuesta a
preguntas y la traducción automática.
6.2.4 Conclusión
Ahora entiendes lo siguiente:
A continuación, revisaremos una serie de características más avanzadas de los RNN, que pueden ayudarlo a
aprovechar al máximo sus modelos de secuencia de aprendizaje profundo.
Abandono recurrente: esta es una forma específica e integrada de utilizar el abandono para combatir el
sobreajuste en capas recurrentes.
Apilar capas recurrentes: esto aumenta el poder de representación de la red (a costa de mayores cargas
computacionales).
Capas recurrentes bidireccionales : presentan la misma información a una capa recurrente.
red de diferentes maneras, aumentando la precisión y mitigando los problemas de olvido.
Hasta ahora, los únicos datos de secuencia que hemos cubierto han sido datos de texto, como IMDB .
conjunto de datos y el conjunto de datos de Reuters. Pero los datos de secuencia se encuentran en muchos más problemas.
que simplemente el procesamiento del lenguaje. En todos los ejemplos de esta sección, jugarás con un
Conjunto de datos de series temporales meteorológicas registrados en la estación meteorológica del Instituto Max Planck.
cd ~/Descargas
mkdir jena_climate
cd jena_climate
wget https://s3.amazonaws.com/kerasdatasets/jena_climate_2009_2016.csv.zip
descomprimir jena_climate_2009_2016.csv.zip
4
Olaf Kolle, www.bgcjena.mpg.de/wetter.
Listado 6.28 Inspeccionando los datos del conjunto de datos meteorológicos de Jena
f = abrir(fname) datos =
f.read()
f.cerrar()
imprimir(encabezado)
imprimir(len(líneas))
Esto genera un recuento de 420.551 líneas de datos (cada línea es un paso de tiempo: un registro de
una fecha y 14 valores relacionados con el clima), así como el siguiente encabezado:
Por ejemplo, aquí está el gráfico de la temperatura (en grados Celsius) a lo largo del tiempo (ver figura
6.18). En este gráfico se puede ver claramente la periodicidad anual de la temperatura.
A continuación se muestra un gráfico más estrecho de los primeros 10 días de datos de temperatura (ver figura 6.19).
Como los datos se registran cada 10 minutos, se obtienen 144 puntos de datos por día.
plt.plot(rango(1440), temperatura[:1440])
En este gráfico se puede ver la periodicidad diaria, especialmente evidente durante los últimos 4 días. Tenga
en cuenta también que este período de 10 días debe provenir de un mes de invierno bastante frío.
Si estuviera tratando de predecir la temperatura promedio para el mes siguiente teniendo en cuenta
algunos meses de datos anteriores, el problema sería fácil, debido a la periodicidad confiable a escala anual
de los datos. Pero al observar los datos en una escala de días, la temperatura parece mucho más caótica.
¿Es esta serie temporal predecible a escala diaria? Vamos a averiguar.
Preprocesar los datos en un formato que una red neuronal pueda ingerir. Esto es fácil: los datos ya
son numéricos, por lo que no es necesario realizar ninguna vectorización. Pero cada serie temporal
de los datos está en una escala diferente (por ejemplo, la temperatura suele estar entre 20 y +30,
pero la presión atmosférica, medida en mbar, es de alrededor de 1.000). Normalizará cada serie
temporal de forma independiente para que todas tomen valores pequeños en una escala similar.
Escriba un generador de Python que tome la matriz actual de datos flotantes y genere lotes de datos
del pasado reciente, junto con una temperatura objetivo en el futuro. Debido a que las muestras en el
conjunto de datos son altamente redundantes (la muestra N y la muestra N + 1 tendrán la mayoría de
sus pasos de tiempo en común), sería un desperdicio asignar explícitamente cada muestra. En su
lugar, generará las muestras sobre la marcha utilizando los datos originales.
Preprocesará los datos restando la media de cada serie temporal y dividiéndolos por la desviación estándar.
Vas a utilizar los primeros 200.000 pasos de tiempo como datos de entrenamiento, así que calcula la media
y la desviación estándar solo en esta fracción de los datos.
El Listado 6.33 muestra el generador de datos que utilizará. Produce una tupla (muestras, objetivos), donde
las muestras son un lote de datos de entrada y los objetivos son la matriz correspondiente de temperaturas
objetivo. Se necesitan los siguientes argumentos:
datos: la matriz original de datos de punto flotante, que normalizó en el listado 6.32.
mirar hacia atrás: cuántos pasos atrás deben retroceder los datos de
entrada. retraso: cuántos pasos de tiempo en el futuro debe tener el
objetivo. min_index y max_index: índices en la matriz de datos que delimitan de qué
pasos de tiempo extraer. Esto es útil para mantener un segmento de datos para validación
y otro para prueba.
barajar: si se mezclan las muestras o se dibujan en orden cronológico.
tamaño_lote: el número de muestras por lote. paso:
el período, en pasos de tiempo, en el que se muestrean los datos. Lo configurarás en 6 pulgadas
para dibujar un punto de datos cada hora.
Listado 6.33 Generador que produce muestras de series temporales y sus objetivos
muestras = np.zeros((len(filas),
mirar hacia atrás // paso,
datos.forma[1]))
objetivos = np.zeros((len(filas),)) para j, fila en
enumerar(filas): índices = rango(filas[j]
retrospectiva, filas[j], paso) muestras[j] = datos[índices ] objetivos[j] = datos[filas[j]
+ retraso][1] muestras de rendimiento,
objetivos
Ahora, usemos la función de generador abstracto para crear instancias de tres generadores: uno
para entrenamiento, otro para validación y otro para prueba. Cada uno analizará diferentes
segmentos temporales de los datos originales: el generador de entrenamiento analiza los
primeros 200.000 pasos de tiempo, el generador de validación analiza los siguientes 100.000 y
el generador de prueba analiza el resto.
train_gen = generador(float_data,
lookback=lookback,
retraso=retraso,
min_index=0,
max_index=200000,
shuffle=True,
paso=paso,
tamaño_lote=tamaño_lote)
val_gen = generador(float_data,
lookback=lookback,
retraso=retraso,
min_index=200001,
max_index=300000,
paso=paso,
tamaño_lote=tamaño_lote)
test_gen = generador(float_data,
lookback=lookback,
retraso=retraso,
¿Cuántos pasos extraer de val_gen
min_index=300001,
para ver el conjunto de validación
max_index=Ninguno, completo?
paso=paso,
tamaño_lote=tamaño_lote)
Antes de empezar a utilizar modelos de aprendizaje profundo de caja negra para resolver el problema
de predicción de la temperatura, intentemos un enfoque sencillo y de sentido común. Servirá como
control de cordura y establecerá una base que tendrás que superar para demostrar la utilidad de
modelos de aprendizaje automático más avanzados. Estas líneas de base de sentido común pueden
resultar útiles cuando se aborda un nuevo problema para el que no existe una solución conocida
(todavía). Un ejemplo clásico es el de las tareas de clasificación desequilibradas, donde algunas
clases son mucho más comunes que otras. Si su conjunto de datos contiene un 90% de instancias de
clase A y un 10% de instancias de clase B, entonces un enfoque de sentido común para la tarea de
clasificación es predecir siempre "A" cuando se le presente una nueva muestra. Un clasificador de
este tipo tiene una precisión general del 90% y, por lo tanto, cualquier enfoque basado en el
aprendizaje debería superar esta puntuación del 90% para demostrar su utilidad. A veces, estos
puntos de referencia elementales pueden resultar sorprendentemente difíciles de superar.
En este caso, se puede suponer con seguridad que la serie temporal de temperatura es continua
(es probable que las temperaturas de mañana sean cercanas a las de hoy), así como periódica con
un período diario. Por lo tanto, un enfoque de sentido común es predecir siempre que la temperatura
dentro de 24 horas será igual a la temperatura actual.
Evaluemos este enfoque utilizando la métrica del error absoluto medio (MAE) :
np.mean(np.abs(preds objetivos))
def evaluar_naive_method():
lote_maes = [] para el
paso en el rango (val_steps):
muestras, objetivos = siguiente(val_gen) preds =
muestras[:, 1, 1] mae =
np.mean(np.abs(preds objetivos)) lote_maes.append(mae)
print(np.mean(batch_maes))
evaluar_naive_method()
Esto produce un MAE de 0,29. Debido a que los datos de temperatura se han normalizado para estar centrados
en 0 y tener una desviación estándar de 1, este número no se puede interpretar de inmediato. Se traduce en un
error absoluto promedio de 0,29 × temperatura_grados Celsius estándar: 2,57˚C.
Ese es un error absoluto promedio bastante grande. Ahora el juego consiste en utilizar tus conocimientos de
aprendizaje profundo para hacerlo mejor.
modelo = Sequential()
model.add(layers.Flatten(input_shape=(lookback // paso, float_data.shape[1]))) model.add(layers.Dense(32, activación='relu'))
modelo. agregar(capas.Densa(1))
validation_data=val_gen,
validation_steps=val_steps)
Mostremos las curvas de pérdida para validación y entrenamiento (ver figura 6.20).
plt.figura()
plt.mostrar()
Algunas de las pérdidas de validación están cercanas a la línea de base de no aprendizaje, pero no de manera confiable.
Esto demuestra el mérito de contar con esta base de referencia: resulta que no es fácil superarlo.
Su sentido común contiene mucha información valiosa a la que un modelo de aprendizaje
automático no tiene acceso.
Quizás se pregunte, si existe un modelo simple y de buen rendimiento para pasar de los datos
a los objetivos (la línea de base de sentido común), ¿por qué el modelo que está entrenando no lo
encuentra y lo mejora? Porque esta sencilla solución no es lo que busca tu configuración de
entrenamiento. El espacio de modelos en el que busca una solución (es decir, su espacio de
hipótesis) es el espacio de todas las redes de dos capas posibles con la configuración que definió.
Estas redes ya son bastante complicadas. Cuando buscas un
solución con un espacio de modelos complicados, la línea de base simple y de buen rendimiento puede
ser imposible de aprender, incluso si técnicamente es parte del espacio de hipótesis. Ésta es una
limitación bastante significativa del aprendizaje automático en general: a menos que el algoritmo de
aprendizaje esté codificado para buscar un tipo específico de modelo simple, el aprendizaje de
parámetros a veces puede no encontrar una solución simple a un problema simple.
El primer enfoque totalmente conectado no funcionó bien, pero eso no significa que el aprendizaje
automático no sea aplicable a este problema. El enfoque anterior primero aplanó las series temporales,
lo que eliminó la noción de tiempo de los datos de entrada. En lugar de eso, miremos los datos como lo
que son: una secuencia, donde la causalidad y el orden importan. Probará un modelo de procesamiento
de secuencia recurrente; debería ser perfecto para dichos datos de secuencia, precisamente porque
explota el ordenamiento temporal de los puntos de datos, a diferencia del primer enfoque.
En lugar de la capa LSTM introducida en la sección anterior, utilizará la capa GRU , desarrollada
por Chung et al. en 2014.5 Las capas de unidades recurrentes cerradas (GRU) funcionan utilizando el
mismo principio que LSTM, pero son algo optimizadas y, por lo tanto, más baratas de ejecutar (aunque
es posible que no tengan tanto poder de representación como LSTM). Esta compensación entre el
costo computacional y el poder de representación se ve en todas partes en el aprendizaje automático.
modelo = Sequential()
model.add(layers.GRU(32, input_shape=(Ninguno, float_data.shape[1]))) model.add(layers.Dense(1))
validation_data=val_gen,
validation_steps=val_steps)
La figura 6.21 muestra los resultados. ¡Mucho mejor! Se puede superar significativamente la línea de
base de sentido común, lo que demuestra el valor del aprendizaje automático, así como la superioridad
de las redes recurrentes en comparación con las redes densas que aplanan secuencias en este tipo de
tarea.
5
Junyoung Chung et al., "Evaluación empírica de redes neuronales recurrentes cerradas en modelado de secuencias",
Conferencia sobre sistemas de procesamiento de información neuronal (2014), https://arxiv.org/abs/1412.3555.
Es evidente a partir de las curvas de entrenamiento y validación que el modelo está sobreajustado: el
Las pérdidas de entrenamiento y validación comienzan a divergir considerablemente después de algunas épocas. Estás
ya estamos familiarizados con una técnica clásica para luchar contra este fenómeno: el abandono escolar,
que pone a cero aleatoriamente las unidades de entrada de una capa para romper las correlaciones fortuitas en los
datos de entrenamiento a los que está expuesta la capa. Pero como aplicar correctamente.
El abandono de las redes recurrentes no es una cuestión baladí. Desde hace tiempo se sabe que
aplicar el abandono antes de una capa recurrente dificulta el aprendizaje en lugar de ayudar
regularización. En 2015, Yarin Gal, como parte de su tesis doctoral sobre aprendizaje profundo bayesiano,6 determinó
la forma correcta de utilizar el abandono con una red recurrente: lo mismo
Se debe aplicar una máscara de abandono (el mismo patrón de unidades eliminadas) en cada paso de tiempo, en lugar
de una máscara de abandono que varía aleatoriamente de un paso de tiempo a otro.
Es más, para regularizar las representaciones formadas por las puertas recurrentes
de capas como GRU y LSTM, se debe aplicar una máscara de eliminación temporalmente constante
a las activaciones recurrentes internas de la capa (una máscara de abandono recurrente ). Utilizando el
La misma máscara de abandono en cada paso permite que la red propague adecuadamente su
error de aprendizaje a través del tiempo; una máscara de abandono temporal aleatoria interrumpiría esto
señal de error y ser perjudicial para el proceso de aprendizaje.
Yarin Gal hizo su investigación utilizando Keras y ayudó a construir este mecanismo directamente
en capas recurrentes de Keras. Cada capa recurrente en Keras tiene dos problemas relacionados con el abandono.
argumentos: abandono, un flotante que especifica la tasa de abandono para las unidades de entrada de la capa,
6
Véase Yarin Gal, “Uncertainty in Deep Learning (PhD Thesis)”, 13 de octubre de 2016, http://mlg.eng.cam.ac.uk/
yarin/blog_2248.html.
Listado 6.40 Entrenamiento y evaluación de un modelo basado en GRU regularizado para la deserción escolar
modelo = Sequential()
model.add(layers.GRU(32, dropout=0.2,
recurrent_dropout=0.2,
input_shape=(Ninguno,
float_data.shape[1])))
modelo.add(capas.Densa(1))
validation_data=val_gen,
validation_steps=val_steps)
La figura 6.22 muestra los resultados. ¡Éxito! Ya no estás sobreajustado durante las primeras 30
épocas. Pero aunque tiene puntuaciones de evaluación más estables, sus mejores puntuaciones
no son mucho más bajas que antes.
ya está tomando medidas básicas para mitigar el sobreajuste, como utilizar el abandono). Siempre y cuando
no esté demasiado adaptado, es probable que esté por debajo de su capacidad.
El aumento de la capacidad de la red generalmente se logra aumentando la cantidad de unidades en las
capas o agregando más capas. El apilamiento de capas recurrentes es una forma clásica de construir redes
recurrentes más poderosas: por ejemplo, lo que actualmente impulsa el algoritmo del Traductor de Google es
una pila de siete capas LSTM grandes , eso es enorme.
Para apilar capas recurrentes una encima de otra en Keras, todas las capas intermedias deben devolver
su secuencia completa de salidas (un tensor 3D) en lugar de su salida en el último paso de tiempo. Esto se
hace especificando return_sequences=True.
modelo = Sequential()
model.add(layers.GRU(32, dropout=0.1,
recurrent_dropout=0.5,
return_sequences=True,
input_shape=(Ninguno,
float_data.shape[1]))) model.add(layers.GRU( 64, activación='relu',
abandono=0.1, abandono_recurrente=0.5))
modelo.add(capas.Densa(1))
validation_data=val_gen,
validation_steps=val_steps)
La figura 6.23 muestra los resultados. Puedes ver que la capa agregada mejora un poco los resultados,
aunque no significativamente. Puedes sacar dos conclusiones:
Debido a que todavía no se está sobreajustando demasiado, podría aumentar con seguridad el
tamaño de sus capas en una búsqueda para mejorar la pérdida de validación. Sin embargo, esto
tiene un coste computacional no despreciable.
Agregar una capa no ayudó por un factor significativo, por lo que es posible que esté viendo
rendimientos decrecientes al aumentar la capacidad de la red en este momento.
Los RNN dependen notablemente del orden o del tiempo: procesan los pasos de tiempo
de sus secuencias de entrada en orden, y mezclar o invertir los pasos de tiempo puede cambiar completamente las
representaciones que el RNN extrae de la secuencia. Esta es precisamente la razón por la que se desempeñan bien en
problemas donde el orden es significativo, como
El problema de la predicción de la temperatura. Un RNN bidireccional explota la sensibilidad al orden de los RNN: consiste
en utilizar dos RNN regulares, como las capas GRU y LSTM .
con los que ya está familiarizado, cada uno de los cuales procesa la secuencia de entrada en una dirección (cronológica y
anticronológicamente) y luego fusiona sus representaciones. Al procesar una secuencia en ambos sentidos, un RNN
bidireccional puede capturar patrones que
El GRU de orden inverso tiene un rendimiento muy inferior incluso al de referencia de sentido común,
indicando que en este caso, el procesamiento cronológico es importante para el éxito de su
acercarse. Esto tiene mucho sentido: la capa GRU subyacente normalmente será mejor en
recordar el pasado reciente que el pasado lejano y, naturalmente, el pasado más reciente.
Los puntos de datos meteorológicos son más predictivos que los puntos de datos más antiguos para el problema (es decir,
lo que hace que la línea de base de sentido común sea bastante sólida). Así la versión cronológica
de la capa seguramente superará a la versión de orden inverso. Es importante destacar que esto no es
Esto es cierto para muchos otros problemas, incluido el lenguaje natural: intuitivamente, la importancia
La capacidad de una palabra para comprender una oración no suele depender de su posición en la oración. Probemos el
mismo truco en el ejemplo de LSTM IMDB de la sección 6.2.
modelo = Secuencial()
modelo.add(capas.Embedding(max_features, 128))
modelo.add(capas.LSTM(32))
model.add(layers.Dense(1, activación='sigmoide'))
model.compile(optimizador='rmsprop',
pérdida = 'binary_crossentropy',
métricas=['acc'])
tamaño_de_lote=128,
división_de_validación=0.2)
Datos de entrada
Fusionar
(añadir, concatenar)
RNN RNN
aBCDe e, d, c, b, a
Para crear una instancia de un RNN bidireccional en Keras, se utiliza la capa bidireccional , que toma
como primer argumento una instancia de capa recurrente. Bidireccional crea una segunda instancia
separada de esta capa recurrente y utiliza una instancia para procesar las secuencias de entrada en
orden cronológico y la otra instancia para procesar las secuencias de entrada en orden inverso.
Probémoslo en la tarea de análisis de sentimientos de IMDB .
modelo = Sequential()
model.add(layers.Embedding(max_features, 32))
model.add(layers.Bidireccional(layers.LSTM(32))) model.add(layers.Dense(1,
activación='sigmoide') )
Funciona ligeramente mejor que el LSTM normal que probó en la sección anterior y logra una precisión
de validación superior al 89 %. También parece sobreajustarse más rápidamente, lo cual no es
sorprendente porque una capa bidireccional tiene el doble de parámetros que un LSTM cronológico . Con
cierta regularización, el enfoque bidireccional probablemente tendría un buen desempeño en esta tarea.
modelo = Secuencial()
modelo.add(capas.Bidireccional(
capas.GRU(32), input_shape=(Ninguno, float_data.shape[1])))
modelo.add(capas.Densa(1))
validation_data=val_gen,
validation_steps=val_steps)
Esto funciona tan bien como la capa GRU normal . Es fácil entender por qué: toda la capacidad predictiva
debe provenir de la mitad cronológica de la red, porque se sabe que la mitad anticronológica tiene un
rendimiento muy deficiente en esta tarea (nuevamente, porque el pasado reciente importa mucho más
que el pasado distante en este caso). ).
Ajuste el número de unidades en cada capa recurrente en la configuración apilada. Las opciones
actuales son en gran medida arbitrarias y, por tanto, probablemente subóptimas.
Ajuste la tasa de aprendizaje utilizada por el optimizador RMSprop .
Intente utilizar capas LSTM en lugar de capas GRU .
Intente utilizar un regresor densamente conectado más grande encima de las capas recurrentes:
es decir, una capa Densa más grande o incluso una pila de capas
Densa . ¡ No olvide ejecutar eventualmente los modelos de mejor rendimiento (en términos de
validación MAE) en el conjunto de prueba! De lo contrario, desarrollará arquitecturas que se
ajustarán demasiado al conjunto de validación.
Como siempre, el aprendizaje profundo es más un arte que una ciencia. Podemos proporcionar pautas que
sugieran qué es probable que funcione o no en un problema determinado, pero, en última instancia, cada
problema es único; Tendrás que evaluar diferentes estrategias empíricamente. Actualmente no existe
ninguna teoría que le diga de antemano exactamente qué debe hacer para resolver un problema de
manera óptima. Debes iterar.
6.3.10 Conclusión
Esto es lo que deberías aprender de esta sección:
Como aprendió por primera vez en el capítulo 4, al abordar un problema nuevo, es bueno establecer
primero líneas de base de sentido común para la métrica elegida. Si no tiene una línea de base que
superar, no podrá saber si está logrando un progreso real. Pruebe modelos simples antes
que modelos costosos, para justificar el gasto adicional.
A veces un modelo sencillo resultará ser tu mejor opción.
Cuando se tienen datos en los que el orden temporal es importante, las redes recurrentes son una
excelente opción y superan fácilmente a los modelos que primero aplanan los datos
temporales. Para utilizar la deserción con redes recurrentes, debe usar una máscara de desconexión
de tiempo constante y una máscara de deserción recurrente. Estos están integrados en las capas
recurrentes de Keras, por lo que todo lo que tiene que hacer es usar los argumentos dropout y
recurrent_dropout de
las capas recurrentes. Los RNN apilados proporcionan más poder de representación que una sola capa de RNN .
También son mucho más caros y, por tanto, no siempre merecen la pena. Aunque ofrecen
beneficios claros en problemas complejos (como la traducción automática), es posible que no
siempre sean relevantes para problemas más pequeños y
simples. Los RNN bidireccionales , que analizan una secuencia en ambos sentidos, son útiles en
problemas de procesamiento del lenguaje natural. Pero no tienen un buen desempeño en datos de
secuencia donde el pasado reciente es mucho más informativo que el comienzo del
secuencia.
NOTA Hay dos conceptos importantes que no cubriremos en detalle aquí: atención recurrente y
enmascaramiento de secuencia. Ambos tienden a ser especialmente relevantes para el
procesamiento del lenguaje natural y no son particularmente aplicables al problema de la
predicción de temperatura. Los dejaremos para futuros estudios fuera de este libro.
ventana de
talla 5
Aporte
Aporte
características
Tiempo
Extraído
parche
Producto escalar
con pesas
Estas capas de convolución 1D pueden reconocer patrones locales en una secuencia. Porque el
Se realiza la misma transformación de entrada en cada parche, un patrón aprendido en un cierto
La posición en una oración se puede reconocer posteriormente en una posición diferente, lo que hace que la traducción
de convnets 1D sea invariante (para traducciones temporales). Por ejemplo, una convnet 1D que procesa secuencias
de caracteres utilizando ventanas de convolución de tamaño 5 debería poder
aprender palabras o fragmentos de palabras de longitud 5 o menos, y debería poder reconocer
estas palabras en cualquier contexto en una secuencia de entrada. Por lo tanto , una convnet 1D a nivel de personaje
puede aprender sobre la morfología de las palabras.
Ya está familiarizado con las operaciones de agrupación 2D , como la agrupación promedio 2D y la agrupación
máxima, que se utilizan en convnets para reducir espacialmente la resolución de tensores de imágenes. La operación
de agrupación 2D tiene un equivalente 1D : extraer parches 1D (subsecuencias) de una entrada y generar el valor
máximo (agrupación máxima) o el valor promedio (agrupación promedio).
Al igual que con las convnets 2D , esto se utiliza para reducir la longitud de las entradas 1D (submuestreo).
En Keras, utiliza una convnet 1D a través de la capa Conv1D , que tiene una interfaz similar a Conv2D. Toma como
entrada tensores 3D con forma (muestras, tiempo, características) y devuelve tensores 3D de formas similares. La
ventana de convolución es una ventana 1D en el eje temporal: eje 1 en el tensor de entrada.
Construyamos una convnet 1D simple de dos capas y la aplicaremos a la tarea de clasificación de sentimientos
de IMDB con la que ya está familiarizado. Como recordatorio, este es el código para obtener y preprocesar los datos.
Las convnets 1D están estructuradas de la misma manera que sus contrapartes 2D , que usaste en el capítulo 5:
constan de una pila de capas Conv1D y MaxPooling1D , que terminan en una capa de agrupación global o una capa
Flatten , que convierte las salidas 3D en 2D. salidas, lo que le permite agregar una o más capas densas al modelo
para clasificación o regresión.
Sin embargo, una diferencia es el hecho de que puede permitirse el lujo de utilizar ventanas de convolución más
grandes con convnets 1D . Con una capa de convolución 2D , una ventana de convolución de 3 × 3 contiene 3 × 3 =
9 vectores de características; pero con una capa de convolución 1D , una ventana de convolución de tamaño 3
contiene solo 3 vectores de características. De este modo, puede permitirse fácilmente ventanas de convolución 1D
de tamaño 7 o 9.
modelo = Sequential()
model.add(layers.Embedding(max_features, 128, input_length=max_len)) model.add(layers.Conv1D(32, 7,
activación='relu')) model.add(layers.MaxPooling1D(5 )) model.add(layers.Conv1D(32,
7, activación='relu'))
model.add(layers.GlobalMaxPooling1D()) model.add(layers.Dense(1))
Resumen Modelo()
model.compile(optimizer=RMSprop(lr=1e4),
loss='binary_crossentropy', metrics=['acc'])
historial = model.fit(x_train,
y_train, epochs=10, lote_size=128, validation_split=0.2 )
Las Figuras 6.27 y 6.28 muestran los resultados del entrenamiento y la validación. La precisión de la
validación es algo menor que la del LSTM, pero el tiempo de ejecución es más rápido tanto en la CPU como
en la GPU (el aumento exacto de la velocidad variará mucho según su configuración exacta). En este punto,
puede volver a entrenar este modelo durante la cantidad correcta de épocas (ocho) y ejecutarlo en el
conjunto de prueba. Esta es una demostración convincente de que una convnet 1D puede ofrecer una
alternativa rápida y económica a una red recurrente en una tarea de clasificación de sentimientos a nivel de palabras.
Listado 6.47 Entrenamiento y evaluación de un convnet 1D simple sobre los datos de Jena
modelo = Sequential()
model.add(layers.Conv1D(32, 5, activación='relu', input_shape=(Ninguno,
float_data.shape[1]))) model.add(layers.MaxPooling1D(3))
model .add(layers.Conv1D(32, 5, activación='relu'))
model.add(layers.MaxPooling1D(3)) model.add(layers.Conv1D(32, 5,
activación='relu')) modelo.
agregar(capas.GlobalMaxPooling1D()) modelo.add(capas.Dense(1))
validation_data=val_gen,
validation_steps=val_steps)
El MAE de validación se mantiene en los 0,40: ni siquiera se puede superar la línea base de sentido común
usando el pequeño convnet. Nuevamente, esto se debe a que el convnet busca patrones en cualquier parte de la
serie temporal de entrada y no tiene conocimiento de la posición temporal de un patrón que ve (hacia el principio,
hacia el final, etc.). Porque más reciente
Los puntos de datos deben interpretarse de manera diferente a los puntos de datos más antiguos en el caso de este
problema de pronóstico específico, el convnet no logra producir resultados significativos. Este
La limitación de convnets no es un problema con los datos de IMDB , porque los patrones de palabras clave
asociados con un sentimiento positivo o negativo son informativos independientemente de
dónde se encuentran en las oraciones de entrada.
Una estrategia para combinar la velocidad y ligereza de los convnets con la sensibilidad al orden
de RNN es utilizar una convnet 1D como paso de preprocesamiento antes de un RNN (ver figura 6.30).
Esto es especialmente beneficioso cuando se trata de
secuencias que son tan largas que no pueden
ser procesado de manera realista con RNN, como
Secuencias con miles de pasos. El convnet convertirá la RNN
larga secuencia de entrada en
secuencias mucho más cortas (reducidas) de
características de nivel superior. Esta secuencia de Corta
Funciones de CNN
Las características extraídas se convierten en la entrada para secuencia
mire datos de hace más tiempo (aumentando el parámetro de retrospectiva del generador de datos) o mire
series temporales de alta resolución (disminuyendo el parámetro de paso del generador). Aquí, de manera un
tanto arbitraria, utilizará un paso que es la mitad de grande, lo que dará como resultado una serie de tiempo
dos veces más larga, donde los datos de temperatura se muestrean a una velocidad de 1 punto cada 30
minutos. El ejemplo reutiliza la función generadora definida anteriormente.
Listado 6.48 Preparando generadores de datos de mayor resolución para el conjunto de datos de Jena
paso = 3
retrospectiva = 720 Anteriormente fijado en 6 (1 punto por hora);
Sin alterar ahora 3 (1 punto cada 30 min)
retraso = 144
train_gen = generador(float_data,
mirar atrás=mirar atrás,
retardo=retraso,
min_index=0,
max_index=200000,
shuffle=True,
paso=paso)
val_gen = generador(float_data,
mirar atrás=mirar atrás,
retraso=retraso,
min_index=200001,
max_index=300000,
paso=paso)
test_gen = generador(float_data,
lookback=lookback,
retardo=retraso,
min_index=300001,
max_index=Ninguno,
paso=paso)
val_steps = (300000 200001 lookback) // 128 test_steps = (len(float_data)
300001 lookback) // 128
Este es el modelo, comenzando con dos capas Conv1D y siguiendo con una capa GRU .
La figura 6.31 muestra los resultados.
Listado 6.49 Modelo que combina una base convolucional 1D y una capa GRU
modelo = Sequential()
model.add(layers.Conv1D(32, 5, activación='relu', input_shape=(Ninguno,
float_data.shape[1]))) model.add(layers.MaxPooling1D(3))
model .add(layers.Conv1D(32, 5, activación='relu'))
model.add(layers.GRU(32, dropout=0.1, recurrent_dropout=0.5))
model.add(layers.Dense(1))
Resumen Modelo()
model.compile(optimizador=RMSprop(), pérdida='mae')
historia = model.fit_generator(train_gen,
pasos_por_epoch=500,
épocas = 20,
datos_validación=val_gen,
pasos_validación=val_pasos)
A juzgar por la pérdida de validación, esta configuración no es tan buena como la GRU regularizada por sí sola.
pero es significativamente más rápido. Examina el doble de datos, lo que en este caso no
Parecen ser de gran ayuda, pero pueden ser importantes para otros conjuntos de datos.
6.4.5 Conclusión
Esto es lo que deberías aprender de esta sección:
De la misma manera que las convnets 2D funcionan bien para procesar patrones visuales en
Espacio 2D , las convnets 1D funcionan bien para procesar patrones temporales. Ellos
Ofrecer una alternativa más rápida a los RNN en algunos problemas, en particular en tareas de
procesamiento de lenguaje natural.
Normalmente, las convnets 1D están estructuradas de forma muy parecida a sus equivalentes 2D del
mundo de la visión por computadora: constan de pilas de capas Conv1D y capas MaxPooling1D ,
que terminan en una operación de agrupación global o operación de aplanamiento.
Porque los RNN son extremadamente costosos para procesar secuencias muy largas, pero
Las convnets 1D son económicas, puede ser una buena idea usar una convnet 1D como paso
de preprocesamiento antes de un RNN, acortando la secuencia y extrayendo representaciones
útiles para que las procese el RNN .
Si el orden global es importante en los datos de secuencia, entonces es preferible utilizar una
red recurrente para procesarlos. Este suele ser el caso de las series temporales, donde es
probable que el pasado reciente sea más informativo que el pasado lejano.
a Véase https://arxiv.org/abs/1706.03059.
Este capítulo explora una serie de herramientas poderosas que lo acercarán a la posibilidad de
desarrollar modelos de última generación sobre problemas difíciles. Con la API funcional de Keras,
puede crear modelos similares a gráficos, compartir una capa entre diferentes entradas y utilizar
modelos de Keras como las funciones de Python. Las devoluciones de llamadas de Keras y la
herramienta de visualización basada en navegador TensorBoard le permiten monitorear los
modelos durante el entrenamiento. También discutiremos otras mejores prácticas, incluida la
normalización por lotes, conexiones residuales, optimización de hiperparámetros y ensamblaje de modelos.
233
Predicción de precios
Fusionando
módulo
Metadatos Descripción del texto Imagen Figura 7.2 Un modelo de múltiples entradas
De manera similar, algunas tareas necesitan predecir múltiples atributos objetivo de los datos de
entrada. Dado el texto de una novela o cuento, es posible que desees clasificarlo automáticamente
por género (como romance o suspenso), pero también predecir la fecha aproximada en que se
escribió. Por supuesto, puedes entrenar dos modelos separados: uno para el género y otro para
la fecha. Pero como estos atributos no son estadísticamente independientes, se podría construir
un modelo mejor aprendiendo a predecir conjuntamente el género y la fecha al mismo tiempo.
Un modelo conjunto de este tipo tendría entonces dos salidas o cabezas (ver figura 7.3). Debido
a las correlaciones entre género y fecha, conocer la fecha de una novela ayudaría al modelo a
aprender representaciones ricas y precisas del espacio de los géneros novedosos, y viceversa.
Género Fecha
Fecha
Clasificador de género regresor
Módulo de procesamiento
de textos
Texto novedoso
1
Christian Szegedy et al., “Going Deeper with Convolutions”, Conferencia sobre visión por computadora y reconocimiento de
patrones (2014), https://arxiv.org/abs/1409.4842.
2
Kaiming He et al., “Aprendizaje residual profundo para el reconocimiento de imágenes”, Conferencia sobre visión por
computadora y reconocimiento de patrones (2015), https://arxiv.org/abs/1512.03385.
Producción
Concatenar
Conv2D
3 × 3, zancadas = 2
Aporte
Capa
Capa Residual
conexión
Capa
Estos tres casos de uso importantes (modelos de múltiples entradas, modelos de múltiples
salidas y modelos similares a gráficos) no son posibles cuando se usa solo la clase de
modelo Sequential en Keras. Pero hay otra forma mucho más general y flexible de utilizar
Keras: la API funcional . Esta sección explica en detalle qué es, qué puede hacer y cómo utilizarlo.
tensor_salida = denso(tensor_entrada)
Se puede llamar a una capa
en un tensor y devuelve un tensor.
Comencemos con un ejemplo mínimo que muestra lado a lado un modelo secuencial simple.
y su equivalente en la API funcional:
input_tensor = Entrada(forma=(64,))
x = capas.Dense(32, activación='relu')(input_tensor) Es funcional
x = capas.Densa(32, activación='relu')(x) equivalente
tensor_salida = capas.Denso(10, activación='softmax')(x)
_________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ================
La única parte que puede parecer un poco mágica en este momento es crear una instancia de un objeto Modelo .
usando solo un tensor de entrada y un tensor de salida. Detrás de escena, Keras recupera
cada capa involucrada en pasar de input_tensor a output_tensor, llevándolas
juntos en una estructura de datos similar a un gráfico: un modelo. Por supuesto, la razón por la que funciona es
ese tensor_salida se obtuvo transformando repetidamente tensor_entrada. Si usted
Intenté construir un modelo a partir de entradas y salidas que no estaban relacionadas, obtendría un Run
error de tiempo:
Este error le indica, en esencia, que Keras no pudo acceder a input_1 desde el archivo proporcionado.
tensor de salida.
Cuando se trata de compilar, entrenar o evaluar dicha instancia de Modelo, el
La API es la misma que la de Sequential:
model.compile(optimizador='rmsprop', pérdida='categorical_crossentropy')
Compila el
importar numpy como np modelo
Genera datos ficticios de Numpy
x_train = np.aleatorio.aleatorio((1000, 64))
para entrenar
y_train = np.aleatorio.aleatorio((1000, 10))
La API funcional se puede utilizar para crear modelos que tengan múltiples entradas. Típicamente,
Dichos modelos en algún momento fusionan sus diferentes ramas de entrada usando una capa que puede
combinar varios tensores: sumándolos, concatenándolos, etc. Esto generalmente se
hace mediante una operación de fusión de Keras como keras.layers.add, keras.layers
.concatenar, etc. Veamos un ejemplo muy simple de un modelo de entradas múltiples:
un modelo de preguntas y respuestas.
Un modelo típico de preguntas y respuestas tiene dos entradas: una pregunta en lenguaje natural
y un fragmento de texto (como un artículo de noticias) que proporciona información que se utilizará para
respondiendo la pregunta. Luego, el modelo debe producir una respuesta: en la configuración más simple posible,
esta es una respuesta de una palabra obtenida a través de un softmax sobre algunos predefinidos.
vocabulario (ver figura 7.6).
Respuesta
Denso
Concatenar
LSTM LSTM
incrustar incrustar
A continuación se muestra un ejemplo de cómo se puede crear un modelo de este tipo con la API funcional.
Configura dos ramas independientes, codificando la entrada de texto y la entrada de la pregunta como
vectores de representación; luego, concatena estos vectores; y finalmente, agregue un clasificador
softmax encima de las representaciones concatenadas.
Listado 7.1 Implementación de API funcional de un modelo de preguntas y respuestas de dos entradas
texto_incrustado = capas.Incrustación(
64, tamaño_vocabulario_texto)(entrada_texto)
Incrusta las entradas en
una secuencia de vectores
texto_codificado = capas.LSTM(32)(texto_incrustado)
de tamaño 64
model.compile(optimizer='rmsprop',
Al crear instancias del modelo, usted especifica
pérdida='categorical_crossentropy', métricas=['acc'])
las dos entradas y la salida.
Ahora bien, ¿cómo se entrena este modelo de dos entradas? Hay dos API posibles: puede alimentar al
modelo con una lista de matrices Numpy como entradas, o puede alimentarlo con un diccionario que
asigne nombres de entrada a matrices Numpy. Naturalmente, la última opción sólo está disponible si le
da nombres a sus entradas.
De la misma manera, puede utilizar la API funcional para crear modelos con múltiples
salidas (o múltiples cabezas). Un ejemplo simple es una red que intenta predecir
simultáneamente diferentes propiedades de los datos, como una red que toma como
entrada una serie de publicaciones en redes sociales de una sola persona anónima e
intenta predecir atributos de esa persona, como edad, género. y nivel de ingresos (ver figura 7.7).
tamaño_vocabulario = 50000
núm_grupos_ingresos = 10
conversión 1D
Es importante destacar que entrenar un modelo de este tipo requiere la capacidad de especificar
diferentes funciones de pérdida para diferentes cabezas de la red: por ejemplo, la predicción de edad
es una tarea de regresión escalar, pero la predicción de género es una tarea de clasificación binaria,
que requiere un procedimiento de entrenamiento diferente. . Pero debido a que el descenso de gradiente
requiere que usted minimice un escalar, debe combinar estas pérdidas en un valor único para poder
entrenar el modelo. La forma más sencilla de combinar diferentes pérdidas es sumarlas todas. En Keras,
puede utilizar una lista o un diccionario de pérdidas en la compilación para especificar diferentes objetos
para diferentes salidas; Los valores de pérdida resultantes se suman en una pérdida global, que se
minimiza durante el entrenamiento.
model.compile(optimizador='rmsprop',
pérdida=['mse', 'categorical_crossentropy', 'binary_crossentropy'])
model.compile(optimizador='rmsprop',
Equivalente (solo es
pérdida={'edad': 'mse', posible si le da nombres
'ingresos': 'categorical_crossentropy', 'género': a las capas de salida)
'binary_crossentropy'})
Tenga en cuenta que las contribuciones de pérdida muy desequilibradas harán que las representaciones
del modelo se optimicen preferentemente para la tarea con la mayor pérdida individual, a expensas de
las otras tareas. Para remediar esto, puede asignar diferentes niveles de importancia a los valores de
pérdida en su contribución a la pérdida final. Esto es útil en particular si los valores de las pérdidas
utilizan escalas diferentes. Por ejemplo, la pérdida de error cuadrático medio (MSE) utilizada para la
tarea de regresión de edad normalmente toma un valor de alrededor de 3 a 5, mientras que la pérdida
de entropía cruzada utilizada para la tarea de clasificación de género puede ser tan baja como 0,1. En
tal situación, para equilibrar la contribución de las diferentes pérdidas, se puede asignar un peso de 10
a la pérdida de entropía cruzada y un peso de 0,25 a la pérdida de MSE .
model.compile(optimizador='rmsprop',
pérdida=['mse', 'categorical_crossentropy', 'binary_crossentropy'], loss_weights=[0.25, 1., 10.])
model.compile(optimizador='rmsprop',
pérdida={'edad': 'mse',
'ingresos': 'categorical_crossentropy', 'género': Equivalente (solo es
'binary_crossentropy'}, loss_weights={'edad': 0,25, posible si le da nombres
a las capas de salida)
'ingresos': 1.,
'género': 10.})
Al igual que en el caso de los modelos de entradas múltiples, puede pasar datos de Numpy al modelo para
entrenarlos mediante una lista de matrices o mediante un diccionario de matrices.
MÓDULOS DE INICIO
Inception3 es un tipo popular de arquitectura de red para redes neuronales convolucionales; fue desarrollado
por Christian Szegedy y sus colegas de Google en 20132014, inspirado en la anterior arquitectura de red en
red.4 Consiste en una pila de módulos que parecen pequeñas redes independientes, divididas en varias ramas
paralelas. La forma más básica de un módulo Inception tiene de tres a cuatro ramas que comienzan con una
convolución de 1 × 1, seguida de una convolución de 3 × 3 y terminan con la concatenación de las
características resultantes. Esta configuración ayuda a la red a aprender por separado
3
https://arxiv.org/abs/1409.4842.
4
Min Lin, Qiang Chen y Shuicheng Yan, “Network in Network”, Conferencia internacional sobre representaciones del aprendizaje (2013),
https://arxiv.org/abs/1312.4400.
Producción
Concatenar
Conv2D 3
× 3, zancadas = 2
Aporte
Así es como implementaría el módulo que se muestra en la figura 7.8 usando la API funcional.
Este ejemplo asume la existencia de un tensor de entrada 4D x:
rama_a = capas.Conv2D(128, 1,
activación='relu', zancadas=2)(x)
rama_b = capas.Conv2D(128, 1, activación='relu')(x)
rama_b = capas.Conv2D(128, 3, activación='relu', zancadas=2)(branch_b)
salida = capas.concatenar(
[rama_a, rama_b, rama_c, rama_d], eje = 1) Concatena las salidas
de la rama para obtener
En esta rama, la zancada se produce en la la salida del módulo.
capa media de la piscina.
Tenga en cuenta que la arquitectura completa de Inception V3 está disponible en Keras como keras.applications
.inception_v3.InceptionV3, incluidos pesos previamente entrenados en el conjunto de datos de ImageNet.
Otro modelo estrechamente relacionado disponible como parte del módulo de aplicaciones de Keras es
5
Xcepción. Xception, que significa inicio extremo, es una arquitectura convnet vagamente
inspirado en Origen. Toma la idea de separar el aprendizaje de canales y
características espaciales hasta su extremo lógico, y reemplaza los módulos Inception con convoluciones
separables en profundidad que consisten en una convolución en profundidad (una convolución espacial donde
cada canal de entrada se maneja por separado) seguida de una convolución puntual
convolución (una convolución 1 × 1): efectivamente, una forma extrema de un módulo Inception, donde las
características espaciales y las características de canal están completamente separadas. Xception tiene
aproximadamente la misma cantidad de parámetros que Inception V3, pero muestra un mejor tiempo de ejecución
rendimiento y mayor precisión en ImageNet, así como en otros conjuntos de datos a gran escala,
debido a un uso más eficiente de los parámetros del modelo.
CONEXIONES RESIDUALES
Las conexiones residuales son un componente de red similar a un gráfico común que se encuentra en muchas
arquitecturas de red posteriores a 2015, incluida Xception. Fueron introducidos por He et al.
de Microsoft en su propuesta ganadora en el desafío ILSVRC ImageNet a finales de 2015.6
Abordan dos problemas comunes que afectan a cualquier modelo de aprendizaje profundo a gran escala:
gradientes que se desvanecen y cuellos de botella representacionales. En general, es probable que sea
beneficioso agregar conexiones residuales a cualquier modelo que tenga más de 10 capas.
5
François Chollet, “Xception: Deep Learning with Depthwise Separable Convolutions”, Conferencia sobre visión
por computadora y reconocimiento de patrones (2017), https://arxiv.org/abs/1610.02357.
6
He et al., "Aprendizaje residual profundo para el reconocimiento de imágenes", https://arxiv.org/abs/1512.03385.
Una conexión residual consiste en hacer que la salida de una capa anterior esté disponible como
entrada a una capa posterior, creando efectivamente un acceso directo en una red secuencial. Bastante
En lugar de concatenarse a la activación posterior, la salida anterior se suma con la
activación posterior, que supone que ambas activaciones son del mismo tamaño. Si son de diferentes
tamaños, puedes usar una transformación lineal para remodelar la activación anterior en la
forma objetivo (por ejemplo, una capa densa sin activación o, para convolucional
mapas de características, una convolución 1 × 1 sin activación).
Aquí se explica cómo implementar una conexión residual en Keras cuando el mapa de características
Los tamaños son los mismos, utilizando conexiones residuales de identidad. Este ejemplo asume la
existencia de un tensor de entrada 4D x:
x= ...
y = capas.Conv2D(128, 3, activación='relu', padding='same')(x)
y = capas.Conv2D(128, 3, activación='relu', padding='same')(y)
y = capas.Conv2D(128, 3, activación='relu', padding='same')(y)
y = capas.add([y, x])
Agrega la x original nuevamente a
las funciones de salida.
Y lo siguiente implementa una conexión residual cuando los tamaños del mapa de características difieren,
usando una conexión residual lineal (nuevamente, asumiendo la existencia de una entrada 4D ).
tensorx ):
Utiliza una convolución 1 × 1 para
desde keras importan capas reducir linealmente la resolución del original
tensor x con la misma forma que y
x= ...
y = capas.Conv2D(128, 3, activación='relu', padding='same')(x)
y = capas.Conv2D(128, 3, activación='relu', padding='same')(y)
y = capas.MaxPooling2D(2, zancadas=2)(y)
(continuado)
Puede comprender este concepto con una analogía con el procesamiento de señales: si tiene un proceso de
procesamiento de audio que consta de una serie de operaciones, cada una de las cuales toma como entrada la
salida de la operación anterior, entonces, si una operación recorta su señal en un rango de baja frecuencia (por
ejemplo, 0–15 kHz), las operaciones posteriores nunca podrán recuperar las frecuencias caídas. Cualquier pérdida
de información es permanente.
Las conexiones residuales, al reinyectar información anterior en sentido descendente, resuelven parcialmente este
problema en los modelos de aprendizaje profundo.
Este problema ocurre tanto con redes profundas como con redes recurrentes en secuencias muy largas; en ambos
casos, una señal de retroalimentación debe propagarse a través de una larga serie de operaciones. Ya está
familiarizado con la solución que utiliza la capa LSTM para abordar este problema en redes recurrentes: introduce
una pista de acarreo que propaga información paralela a la pista de procesamiento principal. Las conexiones
residuales funcionan de manera similar en redes profundas de retroalimentación, pero son aún más simples:
introducen una pista de transporte de información puramente lineal paralela a la pila de capas principal, lo que ayuda
a propagar gradientes a través de pilas de capas arbitrariamente profundas.
Una característica más importante de la API funcional es la capacidad de reutilizar una instancia de
capa varias veces. Cuando llamas a una instancia de capa dos veces, en lugar de crear una instancia
de una nueva capa para cada llamada, reutilizas los mismos pesos con cada llamada. Esto le permite
construir modelos que tengan ramas compartidas (varias ramas que comparten lo mismo)
conocimiento y realizar las mismas operaciones. Es decir, comparten las mismas representaciones
y aprenden estas representaciones simultáneamente para diferentes conjuntos de entradas.
Por ejemplo, consideremos un modelo que intenta evaluar la similitud semántica entre dos
oraciones. El modelo tiene dos entradas (las dos oraciones a comparar) y genera una puntuación
entre 0 y 1, donde 0 significa oraciones no relacionadas y 1 significa
oraciones que son idénticas o reformulaciones entre sí. tal modelo
podría ser útil en muchas aplicaciones, incluida la deduplicación de consultas en lenguaje natural en
un sistema de diálogo.
En esta configuración, las dos oraciones de entrada son intercambiables, porque la similitud
semántica es una relación simétrica: la similitud de A con B es idéntica a la similitud de B con A. Por
esta razón, no tendría sentido aprender dos oraciones de entrada. modelos independientes para
Procesando cada oración de entrada. Más bien, desea procesar ambos con una sola capa LSTM . Las
representaciones de esta capa LSTM (sus pesos) se aprenden en función de ambas entradas simultáneamente.
Esto es lo que llamamos modelo LSTM siamés o LSTM compartido .
A continuación se explica cómo implementar dicho modelo utilizando el uso compartido de capas (reutilización de capas) en el
API funcional de Keras :
Naturalmente, una instancia de capa se puede usar más de una vez; se puede llamar arbitrariamente muchas
veces, reutilizando el mismo conjunto de pesos cada vez.
y = modelo(x)
Si el modelo tiene múltiples tensores de entrada y múltiples tensores de salida, se debe llamar con una lista
de tensores:
Cuando llamas a una instancia de modelo, estás reutilizando los pesos del modelo, exactamente como sucede
cuando llamas a una instancia de capa. Llamar a una instancia, ya sea una instancia de capa o una instancia
de modelo, siempre reutilizará las representaciones aprendidas existentes de la instancia, lo cual es intuitivo.
Un ejemplo práctico simple de lo que se puede construir reutilizando una instancia de modelo es un
modelo de visión que utiliza una cámara dual como entrada: dos cámaras paralelas, separadas por unos
pocos centímetros (una pulgada). Un modelo de este tipo puede percibir profundidad, lo que puede resultar
útil en muchas aplicaciones. No deberías necesitar dos modelos independientes para extraer imágenes
funciones de la cámara izquierda y de la cámara derecha antes de fusionar las dos fuentes.
Este procesamiento de bajo nivel se puede compartir entre las dos entradas: es decir, se realiza a través de capas.
que utilizan los mismos pesos y por lo tanto comparten las mismas representaciones. Así es como lo harías
implementar un modelo de visión siamés (base convolucional compartida) en Keras:
xception_base = aplicaciones.Xception(pesos=Ninguno,
incluir_top=Falso)
fusionadas_features = capas.concatenar (
[características_izquierdas, entrada_derecha], eje=1)
Las funciones fusionadas contienen
información de la fuente visual derecha y de
la fuente visual izquierda.
7.1.7 Conclusión
Con esto concluye nuestra introducción a la API funcional de Keras, una herramienta esencial para
Para salir de la API secuencial siempre que necesite algo más que una pila lineal de capas
Cómo construir modelos Keras con varias entradas, varias salidas y complejos
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 249
7.2.1 Usar devoluciones de llamada para actuar sobre un modelo durante el entrenamiento
Cuando entrenas un modelo, hay muchas cosas que no puedes predecir desde el principio.
En particular, no se puede saber cuántas épocas se necesitarán para llegar a una pérdida de validación óptima. Los
ejemplos hasta ahora han adoptado la estrategia de entrenar durante suficientes épocas para comenzar a sobreajustar,
usar la primera ejecución para determinar la cantidad adecuada de épocas para entrenar y luego, finalmente, lanzar
una nueva ejecución de entrenamiento desde cero usando este número óptimo. Por supuesto, este enfoque es un
desperdicio.
Una forma mucho mejor de manejar esto es dejar de entrenar cuando midas que la pérdida de validación ya no
mejora. Esto se puede lograr mediante una devolución de llamada de Keras. Una devolución de llamada es un objeto
(una instancia de clase que implementa métodos específicos) que se pasa al modelo en la llamada para ajustarse y
que el modelo llama en varios puntos durante el entrenamiento. Tiene acceso a todos los datos disponibles sobre el
estado del modelo y su rendimiento, y puede tomar medidas: interrumpir el entrenamiento, guardar un modelo, cargar
un conjunto de pesos diferente o alterar el estado del modelo.
A continuación se muestran algunos ejemplos de formas en las que puede utilizar las devoluciones de llamada:
Puntos de control del modelo: guardar los pesos actuales del modelo en diferentes puntos
durante el entrenamiento.
Detención anticipada : interrumpir el entrenamiento cuando la pérdida de validación ya no mejora (y, por
supuesto, guardar el mejor modelo obtenido durante el entrenamiento). Ajustar dinámicamente el
valor de ciertos parámetros durante el entrenamiento, como el
Tasa de aprendizaje del optimizador.
Registrar métricas de entrenamiento y validación durante el entrenamiento, o visualizar las representaciones
aprendidas por el modelo a medida que se actualizan. ¡La barra de progreso de Keras con la que está
familiarizado es una devolución de llamada!
El módulo keras.callbacks incluye una serie de devoluciones de llamada integradas (esta no es una lista exhaustiva):
keras.callbacks.ModelCheckpoint
keras.callbacks.EarlyStopping
keras.callbacks.LearningRateScheduler
keras.callbacks.ReduceLROnPlateau
keras.callbacks.CSVLogger
Repasemos algunos de ellos para darle una idea de cómo usarlos: ModelCheckpoint,
Parada temprana y ReducciónLROnPlateau.
EL PUNTO DE VERIFICACIÓN DEL MODELO Y LAS DEVOLUCIONES DE LLAMADA ANTICIPADA
Puede utilizar la devolución de llamada EarlyStopping para interrumpir el entrenamiento una vez que se alcanza la métrica objetivo.
siendo monitoreado ha dejado de mejorar durante un número fijo de épocas. Por ejemplo,
Esta devolución de llamada le permite interrumpir el entrenamiento tan pronto como comience a sobreajustarse, por lo tanto
evitando tener que volver a entrenar su modelo para un número menor de épocas. Esta devolución de llamada es
normalmente se utiliza en combinación con ModelCheckpoint, que le permite guardar continuamente
el modelo durante el entrenamiento (y, opcionalmente, guardar solo el mejor modelo actual hasta el momento:
la versión del modelo que logró el mejor rendimiento al final de una época):
),
keras.callbacks.ModelCheckpoint( Guarda los pesos actuales después de cada época.
ruta de archivo = 'mi_modelo.h5', Ruta al archivo del modelo de destino
monitor='val_loss',
save_best_only=Verdadero, Estos dos argumentos significan que no sobrescribirá el archivo del modelo
) a menos que val_loss haya mejorado, lo que le permite mantener el mejor
] modelo visto durante el entrenamiento.
model.compile(optimizador='rmsprop',
pérdida = 'binary_crossentropy', Usted monitorea la precisión, por lo que debería
modelo.fit(x, y,
Tenga en cuenta que debido a que la devolución
épocas = 10,
de llamada monitoreará la pérdida de
tamaño_lote = 32,
validación y la precisión de la validación, debe pasar
devoluciones de llamada=lista_devoluciones de llamada,
validation_data a la llamada para que se ajuste.
datos_validación=(x_val, y_val))
Puede utilizar esta devolución de llamada para reducir la tasa de aprendizaje cuando la pérdida de validación ha
dejó de mejorar. Reducir o aumentar la tasa de aprendizaje en caso de una meseta de pérdidas es
Es una estrategia eficaz para salir de los mínimos locales durante el entrenamiento. El siguiente ejemplo utiliza la
devolución de llamada ReduceLROnPlateau :
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 251
factor=0.1, paciencia=10,
Divide la tasa de aprendizaje por 10 cuando se activa
model.fit(x, y, épocas=10,
Debido a que la devolución de
tamaño_lote=32,
llamada monitoreará la pérdida de validación,
devoluciones de
debe pasar validation_data a la llamada para
llamada=lista_devoluciones de llamada,
que se ajuste.
datos_validación=(x_val, y_val))
Si necesita realizar una acción específica durante el entrenamiento que no esté cubierta por una de las
devoluciones de llamada integradas, puede escribir su propia devolución de llamada. Las devoluciones
de llamada se implementan subclasificando la clase keras.callbacks.Callback. Luego puede implementar
cualquier cantidad de los siguientes métodos con nombres transparentes, que se llaman en varios puntos
durante el entrenamiento:
Todos estos métodos se llaman con un argumento de registros , que es un diccionario que contiene
información sobre el lote, época o ejecución de entrenamiento anterior: métricas de entrenamiento y
validación, etc. Además, la devolución de llamada tiene acceso a los siguientes atributos:
Aquí hay un ejemplo simple de una devolución de llamada personalizada que guarda en el disco (como
matrices Numpy) las activaciones de cada capa del modelo al final de cada época, calculadas en la
primera muestra del conjunto de validación:
importar keras
importar numpy como np
Llamado por el modelo principal antes
Esto es todo lo que necesita saber sobre las devoluciones de llamada; el resto son detalles técnicos,
que puede consultar fácilmente. Ahora está equipado para realizar cualquier tipo de registro o
intervención preprogramada en un modelo de Keras durante el entrenamiento.
Idea
Marco de Marco de
visualización: aprendizaje profundo:
Tablero Tensor Keras
Resultados Experimento
Esta sección presenta TensorBoard, una herramienta de visualización basada en navegador que viene
incluida con TensorFlow. Tenga en cuenta que solo está disponible para los modelos de Keras cuando
usa Keras con el backend de TensorFlow.
El propósito clave de TensorBoard es ayudarte a monitorear visualmente todo lo que sucede
dentro de tu modelo durante el entrenamiento. Si está monitoreando más información que solo la
pérdida final del modelo, puede desarrollar una visión más clara de lo que hace y no hace el modelo, y
puede avanzar más rápidamente. TensorBoard le brinda acceso a varias funciones interesantes, todas
en su navegador:
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 253
importar keras
desde keras importar capas desde Número de palabras a
modelo = keras.models.Sequential()
model.add(layers.Embedding(max_features, 128,
input_length=max_len,
nombre='embed'))
model.add(layers.Conv1D(32, 7, activación='relu')) model.add(layers.MaxPooling1D(5))
model.add(layers.Conv1D(32 , 7, activación='relu'))
model.add(layers.GlobalMaxPooling1D()) model.add(layers.Dense(1))
model.summary() model.compile(optimizer='rmsprop',
loss='binary_crossentropy ', métricas=['acc'])
Antes de comenzar a usar TensorBoard, debe crear un directorio donde almacenará los archivos de registro
que genera.
$ mkdir mi_log_dir
Iniciemos la capacitación con una instancia de devolución de llamada de TensorBoard . Esta devolución de llamada escribirá
eventos de registro en el disco en la ubicación especificada.
devoluciones de llamada = [
Los archivos de registro se
keras.callbacks.TensorBoard( escribirán en esta ubicación.
log_dir='my_log_dir',
histogram_freq=1, Registra histogramas de activación cada 1 época
embeddings_freq=1,
)
Registros que incorporan
datos cada 1 época
] historial = model.fit (x_train, y_train, épocas = 20, tamaño de
lote = 128,
validación_split = 0.2,
devoluciones de llamada =
devoluciones de llamada)
En este punto, puede iniciar el servidor TensorBoard desde la línea de comando, indicándole que lea
los registros que la devolución de llamada está escribiendo actualmente. La utilidad tensorboard
debería haberse instalado automáticamente en su máquina en el momento en que instaló TensorFlow
(por ejemplo, mediante pip):
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 255
La pestaña Incrustaciones le ofrece una manera de inspeccionar las ubicaciones de incrustación y las ubicaciones espaciales.
relaciones de las 10.000 palabras del vocabulario de entrada, tal como las aprendió el alumno inicial
Capa de incrustación . Debido a que el espacio de incrustación es de 128 dimensiones, TensorBoard lo reduce
automáticamente a 2D o 3D usando un algoritmo de reducción de dimensionalidad de su
elección: análisis de componentes principales (PCA) o vecino estocástico distribuido en t
incrustación (tSNE). En la figura 7.12, en la nube de puntos, se pueden ver claramente dos grupos:
palabras con connotación positiva y palabras con connotación negativa. La visualización hace inmediatamente obvio que las
incrustaciones entrenadas conjuntamente con un específico
resultado objetivo en modelos que son completamente específicos de la tarea subyacente, eso es
La razón por la que utilizar incrustaciones de palabras genéricas previamente entrenadas rara vez es una buena idea.
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 257
Tenga en cuenta que Keras también proporciona otra forma más limpia de trazar modelos como
gráficos de capas en lugar de gráficos de operaciones de TensorFlow: la utilidad keras.utils.plot_model.
Para usarlo, es necesario haber instalado las bibliotecas Python pydot y pydotng , así como la
biblioteca Graphviz . Echemos un vistazo rápido:
plot_model(modelo, to_file='model.png')
Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 259
7.2.3 Conclusión
Las devoluciones de llamada de Keras proporcionan una forma sencilla de monitorear modelos durante el entrenamiento y
NORMALIZACIÓN DE LOTES
La normalización es una categoría amplia de métodos que buscan hacer que las diferentes muestras
vistas por un modelo de aprendizaje automático sean más similares entre sí, lo que ayuda al modelo
a aprender y generalizar bien a nuevos datos. La forma más común de normalización de datos es una
que ya ha visto varias veces en este libro: centrar los datos en 0 restando la media de los datos y
darles a los datos una desviación estándar unitaria dividiendo los datos por su desviación estándar.
En efecto, esto supone que los datos siguen una distribución normal (o gaussiana) y garantiza que
esta distribución esté centrada y escalada a la varianza unitaria:
Los ejemplos anteriores normalizaron los datos antes de introducirlos en los modelos. Pero la
normalización de los datos debería ser una preocupación después de cada transformación operada
por la red: incluso si los datos que ingresan a una red Densa o Conv2D tienen una media de 0 y una
varianza unitaria, no hay razón para esperar a priori que este sea el caso para la red. datos que salen.
La normalización por lotes es un tipo de capa (BatchNormalization en Keras) introducida en 2015
por Ioffe y Szegedy;7 puede normalizar datos de forma adaptativa incluso cuando la media y la
varianza cambian con el tiempo durante el entrenamiento. Funciona manteniendo internamente un
promedio móvil exponencial de la media por lotes y la varianza de los datos observados durante el
entrenamiento. El efecto principal de la normalización por lotes es que ayuda con la propagación del
gradiente (al igual que las conexiones residuales) y, por lo tanto, permite redes más profundas.
Algunas redes muy profundas solo se pueden entrenar si incluyen múltiples capas de
BatchNormalization . Por ejemplo, BatchNormalization se usa generosamente en muchas de las
arquitecturas convnet avanzadas que vienen incluidas con Keras, como ResNet50, Inception V3 y
Xception.
7
Sergey Ioffe y Christian Szegedy, “Batch Normalization: Accelerating Deep Network Training mediante la reducción
del cambio de covariables internas”, Actas de la 32ª Conferencia Internacional sobre Aprendizaje Automático
(2015), https://arxiv.org/abs/1502.03167.
La capa BatchNormalization toma un argumento de eje , que especifica el eje de característica que
debe normalizarse. Este argumento tiene por defecto 1, el último eje del tensor de entrada. Este es
el valor correcto cuando se utilizan capas densas , capas Conv1D , capas RNN y capas Conv2D
con formato_datos establecido en "channels_last". Pero en el caso de uso de nicho de capas
Conv2D con formato_datos establecido en "canales_primero", el eje de características es el eje 1;
En consecuencia, el argumento del eje en BatchNormalization debe establecerse en 1.
a
Sergey Ioffe, “Renormalización de lotes: hacia la reducción de la dependencia de minibatch en modelos
normalizados por lotes” (2017), https://arxiv.org/abs/1702.03275.
b
Günter Klambauer et al., “SelfNormalizing Neural Networks”, Conferencia sobre sistemas de procesamiento
de información neuronal (2017), https://arxiv.org/abs/1706.02515.
¿Qué pasaría si te dijera que hay una capa que puedes usar como reemplazo directo de Conv2D
que hará que tu modelo sea más liviano (menos parámetros de peso entrenables) y más rápido
(menos operaciones de punto flotante) y hará que realice algunos puntos porcentuales? mejor en
su tarea? Eso es precisamente lo que hace la capa de convolución separable en profundidad
(SeparableConv2D). Esta capa realiza una convolución espacial en cada canal de su entrada, de
forma independiente, antes de mezclar los canales de salida mediante una convolución puntual (una
convolución 1 × 1), como se muestra en la figura 7.16. Esto equivale a separar el aprendizaje de
características espaciales y el aprendizaje de características de canal, lo que tiene mucho sentido si
se supone que las ubicaciones espaciales en la entrada están altamente correlacionadas, pero los
diferentes canales son bastante independientes. Requiere muchos menos parámetros e implica
menos cálculos, lo que da como resultado modelos más pequeños y más rápidos. Y debido a que
es una manera más eficiente desde el punto de vista representacional de realizar convolución,
tiende a aprender mejores representaciones usando menos datos, lo que resulta en modelos con mejor rendimiento
Conv. 1 ×
1 (conv. puntual)
Concatenar
Convolución en profundidad:
convs espaciales
independientes por canal
Conv 3 × 3 Conv 3 × 3 Conv 3 × 3 Conv 3 × 3
Estas ventajas se vuelven especialmente importantes cuando entrenas modelos pequeños desde
cero con datos limitados. Por ejemplo, así es como puede crear una convnet liviana y separable
en profundidad para una tarea de clasificación de imágenes (clasificación categórica softmax)
en un conjunto de datos pequeño:
alto = 64 ancho =
64
canales = 3
núm_clases = 10
modelo = Sequential()
model.add(layers.SeparableConv2D(32, 3,
activación='relu',
input_shape=(alto, ancho, canales,)))
model.add(layers.SeparableConv2D(64, 3, activación='relu')) model.add(layers.MaxPooling2D(2))
model.add(layers.Dense(32, activación='relu'))
model.add(layers.Dense(num_classes, activación='softmax'))
model.compile(optimizador='rmsprop', pérdida='categorical_crossentropy')
Cuando se trata de modelos a mayor escala, las convoluciones separables en profundidad son
la base de la arquitectura Xception, una convnet de alto rendimiento que viene incluida con
Keras. Puede leer más sobre la base teórica para separables en profundidad.
Por lo tanto, es necesario explorar el espacio de las posibles decisiones de forma automática, sistemática
y basada en principios. Es necesario buscar en el espacio de la arquitectura y encontrar empíricamente las de
mejor rendimiento. De eso se trata el campo de la optimización automática de hiperparámetros: es todo un
campo de investigación, y uno importante.
El proceso de optimización de hiperparámetros suele tener este aspecto:
La clave de este proceso es el algoritmo que utiliza este historial de rendimiento de validación, dados varios
conjuntos de hiperparámetros, para elegir el siguiente conjunto de hiperparámetros a evaluar. Son posibles
muchas técnicas diferentes: optimización bayesiana,
algoritmos genéticos, búsqueda aleatoria simple, etc.
Entrenar los pesos de un modelo es relativamente fácil: se calcula una función de pérdida en un
mini lote de datos y luego usar el algoritmo de retropropagación para mover los pesos
en la dirección correcta. Por otro lado, actualizar los hiperparámetros es un gran desafío. Considera lo
siguiente:
Debido a que estos desafíos son difíciles y el campo aún es joven, actualmente solo tenemos acceso a
herramientas muy limitadas para optimizar modelos. A menudo resulta que la búsqueda aleatoria (elegir
hiperparámetros para evaluarlos al azar, repetidamente) es la mejor solución, a pesar de ser la más
ingenua. Pero una herramienta que he encontrado confiablemente mejor que la búsqueda aleatoria es
Hyperopt (https://github.com/hyperopt/hyperopt), una biblioteca de Python para la optimización de
hiperparámetros que utiliza internamente árboles de estimadores de Parzen para predecir conjuntos de
hiperparámetros que probablemente funcionen bien. Otra biblioteca llamada Hyperas (https://github.com/
maxpumperla/hyperas) integra Hyperopt para su uso con modelos Keras. Compruébalo.
NOTA Una cuestión importante a tener en cuenta al realizar una optimización automática de
hiperparámetros a escala es el sobreajuste del conjunto de validación. Debido a que está
actualizando hiperparámetros en función de una señal que se calcula utilizando sus datos de
validación, los está entrenando efectivamente en los datos de validación y, por lo tanto,
rápidamente se sobreajustarán a los datos de validación. Tenga siempre esto en cuenta.
En general, la optimización de hiperparámetros es una técnica poderosa que es un requisito absoluto para
llegar a modelos de última generación en cualquier tarea o ganar competencias de aprendizaje automático.
Piénselo: hace un tiempo, la gente creaba a mano las funciones que se incluían en modelos superficiales
de aprendizaje automático. Eso fue muy subóptimo. Ahora, el aprendizaje profundo automatiza la tarea de
ingeniería de funciones jerárquicas: las funciones se aprenden mediante una señal de retroalimentación, no
se ajustan manualmente, y así debe ser. De la misma manera, no deberías crear a mano tus arquitecturas
modelo; debes optimizarlos según principios. En el momento de escribir este artículo, el campo de la
optimización automática de hiperparámetros es muy joven e inmaduro, como lo era el aprendizaje profundo
hace algunos años, pero espero que crezca en los próximos años.
Los ciegos son esencialmente modelos de aprendizaje automático que intentan comprender la variedad de
datos de entrenamiento, cada uno desde su propia perspectiva, utilizando sus propias suposiciones
(proporcionadas por la arquitectura única del modelo y la inicialización de peso aleatorio única). Cada uno de
ellos obtiene parte de la verdad de los datos, pero no toda la verdad. Al combinar sus perspectivas, se puede
obtener una descripción mucho más precisa de los datos. El elefante es una combinación de partes: ningún
ciego lo entiende del todo bien, pero, entrevistados juntos, pueden contar una historia bastante precisa.
Usemos la clasificación como ejemplo. La forma más sencilla de agrupar las predicciones de un conjunto
de clasificadores (para agrupar los clasificadores) es promediar sus predicciones en el momento de la inferencia:
Esto funcionará sólo si los clasificadores son más o menos igualmente buenos. Si uno de ellos es
significativamente peor que los demás, las predicciones finales pueden no ser tan buenas como las del mejor
clasificador del grupo.
Una forma más inteligente de agrupar clasificadores es hacer un promedio ponderado, donde las
ponderaciones se aprenden a partir de los datos de validación; por lo general, a los mejores clasificadores se
les asigna una ponderación más alta y a los peores clasificadores se les asigna una ponderación menor. Para
buscar un buen conjunto de pesos de conjunto, puede utilizar la búsqueda aleatoria o un algoritmo de
optimización simple como NelderMead:
Hay muchas variantes posibles: puedes hacer un promedio exponencial de las predicciones, por ejemplo. En
general, un promedio ponderado simple con ponderaciones optimizadas en los datos de validación proporciona
una base de referencia muy sólida.
La clave para que el conjunto funcione es la diversidad del conjunto de clasificadores. La diversidad es
fuerza. Si todos los ciegos sólo tocaran la trompa del elefante, estarían de acuerdo
que los elefantes son como serpientes, y que siempre permanecerían ignorantes de la verdad del
elefante. La diversidad es lo que hace que el montaje funcione. En términos de aprendizaje automático, si todos
de tus modelos están sesgados de la misma manera, entonces tu conjunto conservará este mismo
inclinación. Si sus modelos están sesgados de diferentes maneras, los sesgos se cancelarán entre sí,
y el conjunto será más robusto y más preciso.
Por esta razón, debes combinar modelos que sean lo mejor posible y al mismo tiempo
lo más diferente posible. Esto normalmente significa usar arquitecturas muy diferentes o incluso
diferentes tipos de enfoques de aprendizaje automático. Una cosa que en gran medida no vale la pena.
Lo que se hace es ensamblar la misma red entrenada varias veces de forma independiente, a partir de
diferentes inicializaciones aleatorias. Si la única diferencia entre sus modelos es su inicialización aleatoria y el
orden en el que fueron expuestos a los datos de entrenamiento, entonces
su conjunto será de baja diversidad y proporcionará sólo una pequeña mejora con respecto a cualquier
modelo único.
Una cosa que he descubierto que funciona bien en la práctica, pero que no se puede generalizar a
cada dominio del problema—es el uso de un conjunto de métodos basados en árboles (como bosques
aleatorios o árboles impulsados por gradiente) y redes neuronales profundas. En 2014, socio
Andrei Kolev y yo obtuvimos el cuarto lugar en el desafío de detección de la desintegración del bosón de Higgs
en Kaggle (www.kaggle.com/c/higgsboson) utilizando un conjunto de varios modelos de árbol y redes
neuronales profundas. Sorprendentemente, uno de los modelos del conjunto se originó a partir de un método
diferente al de los demás (era un bosque codicioso regularizado) y
obtuvo una puntuación significativamente peor que los demás. Como era de esperar, se le asignó una pequeña
peso en el conjunto. Pero para nuestra sorpresa, resultó mejorar la situación general.
conjunto por un factor importante, porque era muy diferente de cualquier otro modelo: proporcionaba información
a la que los otros modelos no tenían acceso. Esa es precisamente la
punto de montaje. No se trata tanto de qué tan bueno sea tu mejor modelo; se trata de
la diversidad de su conjunto de modelos candidatos.
En los últimos tiempos, un estilo de conjunto básico que ha tenido mucho éxito en la práctica es la
categoría amplia y profunda de modelos, que combina el aprendizaje profundo con el aprendizaje superficial.
Dichos modelos consisten en entrenar conjuntamente una red neuronal profunda con una gran red lineal.
modelo. La formación conjunta de una familia de modelos diversos es una opción más para
lograr el ensamblaje del modelo.
7.3.4 Conclusión
Al crear convnets profundas de alto rendimiento, necesitará utilizar conexiones residuales, normalización
por lotes y convoluciones separables en profundidad. En el
En el futuro, es probable que las convoluciones separables en profundidad reemplacen por completo
convoluciones regulares, ya sea para aplicaciones 1D, 2D o 3D , debido a su
mayor eficiencia representacional.
La construcción de redes profundas requiere crear muchos hiperparámetros pequeños y
opciones de arquitectura, que en conjunto definen qué tan bueno será su modelo.
En lugar de basar estas elecciones en la intuición o el azar, es mejor
Busque sistemáticamente el espacio de hiperparámetros para encontrar opciones óptimas. En esto
tiempo, el proceso es costoso y las herramientas para hacerlo no son muy buenas. Pero el
Las bibliotecas Hyperopt y Hyperas pueden ayudarle. Al realizar la optimización de hiperparámetros,
tenga en cuenta el sobreajuste del conjunto de validación.
Ganar competencias de aprendizaje automático u obtener los mejores resultados posibles en una
tarea solo se puede lograr con grandes conjuntos de modelos. El conjunto a través de un promedio
ponderado bien optimizado suele ser suficiente. Recordar:
La diversidad es fuerza. En gran medida es inútil ensamblar modelos muy similares; el
Los mejores conjuntos son conjuntos de modelos que son lo más diferentes posible (sin dejar de tener
tanto poder predictivo como sea posible, naturalmente).
– Qué son la normalización por lotes, la convolución separable en profundidad y las conexiones residuales.
– Por qué debería utilizar la optimización de hiperparámetros y el ensamblaje de modelos. Con estas
nuevas herramientas, estará mejor equipado para utilizar el aprendizaje profundo en el mundo real y comenzar
a crear modelos de aprendizaje profundo altamente competitivos.
Implementación de DeepDream
269
Por supuesto, las producciones artísticas que hemos visto de la IA hasta ahora han sido bastante bajas.
calidad. La IA no está ni cerca de rivalizar con guionistas, pintores y compositores humanos. Pero reemplazar a los
humanos siempre estuvo fuera de lugar: la inteligencia artificial no es
Se trata de sustituir nuestra propia inteligencia por otra cosa, se trata de incorporar a nuestra
Viven y trabajan con más inteligencia: inteligencia de un tipo diferente. En muchos campos, pero
Especialmente en los creativos, los humanos utilizarán la IA como una herramienta para aumentar sus propios
Capacidades: más inteligencia aumentada que inteligencia artificial .
Gran parte de la creación artística consiste en el reconocimiento de patrones simples y técnicas.
habilidad. Y esa es precisamente la parte del proceso que muchos encuentran menos atractiva o incluso
dispensable. Ahí es donde entra en juego la IA . Nuestras modalidades de percepción, nuestro lenguaje y
Todas nuestras obras de arte tienen estructura estadística. Aprender esta estructura es lo que el aprendizaje profundo
los algoritmos sobresalen. Los modelos de aprendizaje automático pueden aprender el espacio latente estadístico de
imágenes, música e historias, y luego pueden tomar muestras de este espacio, creando nuevas obras de arte con
características similares a las que el modelo ha visto en sus datos de entrenamiento. Naturalmente, tal muestreo no
es en sí mismo un acto de creación artística. es un mero
operación matemática: el algoritmo no tiene base en la vida humana, las emociones humanas o nuestra experiencia
del mundo; en cambio, aprende de una experiencia que tiene poco en común con la nuestra. Es sólo nuestra
interpretación, como espectadores humanos, la que
dar significado a lo que genera el modelo. Pero en manos de un artista experto, la generación algorítmica puede
convertirse en algo significativo y hermoso. Latente
El muestreo espacial puede convertirse en un pincel que empodera al artista, aumenta nuestra creatividad.
posibilidades y amplía el espacio de lo que podemos imaginar. Es más, puede hacer
la creación artística sea más accesible al eliminar la necesidad de habilidad y práctica técnica, estableciendo un
nuevo medio de expresión pura, separando el arte de la artesanía.
Iannis Xenakis, un visionario pionero de la música electrónica y algorítmica, expresó maravillosamente esta
misma idea en la década de 1960, en el contexto de la aplicación de la tecnología de automatización a la composición
musical:1
En este capítulo, exploraremos desde varios ángulos el potencial del aprendizaje profundo para
potenciar la creación artística. Revisaremos la generación de datos de secuencia (que se puede utilizar
para generar texto o música), DeepDream y generación de imágenes utilizando ambos variacionales.
codificadores automáticos y redes generativas adversarias. Haremos que tu ordenador sueñe
subir contenido nunca antes visto; y tal vez consigamos que usted también sueñe con las fantásticas posibilidades
que se encuentran en la intersección de la tecnología y el arte. Empecemos.
1
Iannis Xenakis, “Musiques formelles: nouveaux principes formels de listening musicale”, número especial de La
Revista musical, núms. 253254 (1963).
2
Sepp Hochreiter y Jürgen Schmidhuber, “Memoria a largo plazo”, Neural Computation 9, no. 8 (1997).
3
Alex Graves, "Generación de secuencias con redes neuronales recurrentes", arXiv (2013), https://arxiv.org/
abs/1308.0850.
Distribución
de probabilidad para el Siguiente personaje
Texto inicial Texto inicial siguiente personaje. muestreado
modelo de Estrategia
El gato se sentó en la m a
lenguaje de muestreo
modelo de Estrategia
El gato se sentó en la ma. t
lenguaje de muestreo
...
Figura 8.1 El proceso de generación de texto carácter por carácter utilizando un modelo de lenguaje
30% del tiempo. Tenga en cuenta que el muestreo codicioso también se puede interpretar como muestreo a partir de
una distribución de probabilidad: aquella en la que un determinado personaje tiene probabilidad 1 y todos los demás tienen
probabilidad 0.
El muestreo probabilístico de la salida softmax del modelo es claro: permite
incluso personajes poco probables se muestrearon algunas veces, generando oraciones de
apariencia más interesante y, a veces, mostrando creatividad al encontrar palabras nuevas que
suenan realistas que no aparecían en los datos de entrenamiento. Pero hay un problema con esto.
Estrategia: no ofrece una forma de controlar la cantidad de aleatoriedad en el proceso de muestreo.
¿Por qué querrías más o menos aleatoriedad? Consideremos un caso extremo: puro
Muestreo aleatorio, en el que se extrae el siguiente carácter de una distribución de probabilidad
uniforme y todos los caracteres tienen la misma probabilidad. Este esquema tiene máxima
aleatoriedad; en otras palabras, esta distribución de probabilidad tiene máxima entropía. Naturalmente,
No producirá nada interesante. En el otro extremo, el muestreo codicioso no
tampoco produce nada interesante y no tiene aleatoriedad: el correspondiente
La distribución de probabilidad tiene una entropía mínima. Muestreo a partir de la probabilidad "real"
La distribución (la distribución que genera la función softmax del modelo) constituye un punto
intermedio entre estos dos extremos. Pero hay muchos otros
puntos intermedios de mayor o menor entropía que quizás quieras explorar. Menos
la entropía dará a las secuencias generadas una estructura más predecible (y por lo tanto
potencialmente tendrá un aspecto más realista), mientras que más entropía dará como resultado más
Secuencias sorprendentes y creativas. Al tomar muestras de modelos generativos, siempre es
Es bueno explorar diferentes cantidades de aleatoriedad en el proceso de generación. Porque
Nosotros, los humanos, somos los jueces finales de cuán interesantes son los datos generados, el
interés es altamente subjetivo y no se puede saber de antemano cuál es el punto de interés.
la entropía óptima se encuentra.
Las temperaturas más altas dan como resultado distribuciones de muestreo de mayor entropía que generarán más
datos generados sorprendentes y no estructurados, mientras que una temperatura más baja dará como resultado
datos generados menos aleatorios y mucho más predecibles (ver figura 8.2).
Pongamos estas ideas en práctica en una implementación de Keras. Lo primero que necesitas
Hay una gran cantidad de datos de texto que puedes usar para aprender un modelo de lenguaje. Puede utilizar
cualquier archivo de texto o conjunto de archivos de texto suficientemente grande: Wikipedia, El Señor de los Anillos, etc. En
En este ejemplo, utilizará algunos de los escritos de Nietzsche, de finales del siglo XIX.
Filósofo alemán (traducido al inglés). El modelo de lenguaje que aprenderás
ser, por lo tanto, específicamente un modelo del estilo de escritura y los temas de elección de Nietzsche, en lugar de
que un modelo más genérico de la lengua inglesa.
PREPARANDO LOS DATOS
importar keras
importar numpy como np
ruta = keras.utils.get_file(
'nietzsche.txt',
origen ='https://s3.amazonaws.com/textdatasets/nietzsche.txt')
texto = abrir(ruta).leer().inferior()
print('Longitud del cuerpo:', len(texto))
Extraerás secuencias de 60
caracteres.
maxlen = 60
Probarás una nueva secuencia cada
paso = 3 tres caracteres.
caracteres_siguientes = []
Sostiene los objetivos (los
para i en rango(0, len(texto) maxlen, paso): oraciones.append(texto[i: i + personajes de seguimiento)
print('Vectorización...')
x = np.zeros((len(oraciones), maxlen, len(caracteres)), dtype=np.bool)
y = np.zeros((len(oraciones), len(caracteres)), dtype=np.bool)
para i, oración en enumerar(sentencias): para t, char en Onehot codifica los
caracteres en
enumerar(oración): x[i, t, char_indices[char]] = 1 y[i,
matrices binarias
char_indices[next_chars[i]]] = 1
CONSTRUYENDO LA RED
Esta red es una única capa LSTM seguida de un clasificador denso y softmax sobre todos los caracteres
posibles. Pero tenga en cuenta que las redes neuronales recurrentes no son la única forma de generar
datos de secuencia; Las convnets 1D también han demostrado ser extremadamente exitosas en esta
tarea en los últimos tiempos.
Listado 8.4 Modelo LSTM de una sola capa para predicción del siguiente carácter
modelo = keras.models.Sequential()
model.add(layers.LSTM(128, input_shape=(maxlen, len(chars)))) model.add(layers.Dense(len(chars),
activación='softmax') )
Debido a que sus objetivos están codificados en caliente, utilizará categorical_crossentropy como
pérdida para entrenar el modelo.
optimizador = keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizador=optimizador)
Dado un modelo entrenado y un fragmento de texto inicial, puede generar texto nuevo haciendo lo
siguiente repetidamente:
1 Dibuje del modelo una distribución de probabilidad para el siguiente personaje, dada la
texto generado disponible hasta el momento.
Este es el código que se utiliza para volver a ponderar la distribución de probabilidad original que sale
del modelo y extraer un índice de caracteres de ella (la función de muestreo).
Listado 8.6 Función para muestrear el siguiente personaje dadas las predicciones del modelo
Finalmente, el siguiente bucle entrena y genera texto repetidamente. Comienzas a generar texto
usando un rango de temperaturas diferentes después de cada época. Esto le permite ver cómo
evoluciona el texto generado a medida que el modelo comienza a converger, así como el impacto de
la temperatura en la estrategia de muestreo.
importar sistema de
importación aleatoria Entrena el modelo durante 60 épocas.
para época en rango (1, 60): print('época', Se ajusta al modelo para una iteración
época) model.fit(x, y, de los datos.
tamaño_de_lote=128, épocas=1) start_index = random.randint(0,
len(texto) maxlen 1 ) texto_generado = texto[indice_inicio: índice_inicial + maxlen] Selecciona una
texto_generado += siguiente_carbón
texto_generado = texto_generado[1:]
sys.stdout.write(siguiente_char)
Aquí utilizamos el texto semilla aleatorio “nueva facultad, y el júbilo alcanzó su clímax cuando kant”.
Esto es lo que se obtiene en la época 20, mucho antes de que el modelo haya convergido por
completo, con temperatura = 0,2:
nueva facultad, y el júbilo alcanzó su clímax cuando kant y tal hombre al mismo tiempo el espíritu de lo seguro y lo tal
como un hombre es la luz del sol y somete el presente a la superioridad del dolor especial el más
hombre y extraño el sometimiento de la conciencia especial el especial y la naturaleza y tales hombres el sometimiento
de los hombres especiales, lo más seguro es el sometimiento del intelecto especial del
sometimiento de las mismas cosas y
nueva facultad, y el júbilo alcanzó su clímax cuando Kant en el hombre eterno y tal como es también se convirtió en la
condición del
experiencia de fuera de la base la superioridad y la morty especial de la fuerza, en el langus, como que al mismo
tiempo la vida e "incluso quien desprecia a la humanidad, con un tema y un hecho todo lo que tienes que
ser el stand and lave no viene a troveración del hombre y seguramente la conciencia la superioridad, y cuando uno debe
ser w
nueva facultad, y el júbilo alcanzó su clímax cuando Kant, como un peligro de manera a todo lo definido y
transspectivo, lo hizo tan hicable y sobre él resultado artiar
demasiado como si alguna vez el apoyo a la cneciencia. para ser juden, todo el mundo podía enfriar toda la
pasividad, las capas como las que podrían haber tenido en cuenta, el germen indiferente, que todo es
una destrucción segura, el intelecto en el origen deteriorable del moral, y una menor parte de
la alegría, la amistad y la bondad de un corazón son el sentido del espíritu es un hombre con el sentido del sentido
del mundo del fin del yo y del yo con respecto al sometimiento de las fortalezas el
la alegría, la simpatía y la bondad de un corazón son las partes del alma que han sido el arte de los filósofos, y que
uno no quiere decir, cuál es la más alta y con la religión de los franceses. la vida del espíritu
entre los más continúa del fortalecedor del sentido la conciencia de los hombres de precisamente antes de
suficiente presunción, y puede la humanidad, y algo las concepciones, la sujeción del
sentido y el sufrimiento y el
la alegría, la amabilidad y la bondad de un corazón son espirituales por la cultura para los enredados,
asombrados, o errores
para nuestro usted, y es necesario, pensar con palos para completar las vidas de los recién llegados,
perfectamente raales. fue
nombre, por ejemplo, pero voludd atuespecity"o rango uno, o incluso todos los "solett increessic del mundo
y
experiencia trágica implussional, transf, o insiderar, debe tener si los deseos de la estructura son
fuertes
Como puede ver, un valor de temperatura bajo da como resultado un texto extremadamente repetitivo y
predecible, pero la estructura local es muy realista: en particular, todas las palabras (una palabra es un
patrón local de caracteres) son palabras reales en inglés. Con temperaturas más altas, el texto generado
se vuelve más interesante, sorprendente e incluso creativo; a veces inventa palabras completamente
nuevas que suenan algo plausibles (como eterned y troveration).
Con una temperatura alta, la estructura local comienza a descomponerse y la mayoría de las palabras
parecen cadenas de caracteres semialeatorias. Sin duda, 0,5 es la temperatura más interesante para la
generación de texto en esta configuración específica. ¡Experimente siempre con múltiples estrategias
de muestreo! Un equilibrio inteligente entre la estructura aprendida y la aleatoriedad es lo que hace que
la generación sea interesante.
Tenga en cuenta que al entrenar un modelo más grande, por más tiempo y con más datos, puede
lograr muestras generadas que parecen mucho más coherentes y realistas que ésta. Pero, por supuesto,
no espere generar ningún texto significativo, excepto por casualidad: todo lo que está haciendo es
muestrear datos de un modelo estadístico de qué caracteres vienen después de qué caracteres. El
lenguaje es un canal de comunicación, y hay una distinción entre de qué se tratan las comunicaciones y
la estructura estadística de
los mensajes en los que se codifican las comunicaciones. Para evidenciar esta distinción, he aquí un
experimento mental: ¿qué pasaría si el lenguaje humano comprimiera mejor las comunicaciones, de
forma muy similar a como lo hacen las computadoras con la mayoría de las comunicaciones digitales?
El lenguaje no sería menos significativo, pero carecería de estructura estadística intrínseca, lo que haría
imposible aprender un modelo de lenguaje como acaba de hacerlo.
8.1.5 Conclusión
Puede generar datos de secuencia discreta entrenando un modelo para predecir el siguiente
tokens, dados los tokens anteriores.
En el caso del texto, dicho modelo se denomina modelo de lenguaje. Puede basarse en
ya sean palabras o caracteres.
El muestreo del siguiente token requiere un equilibrio entre adherirse a lo que el modelo
jueces probables e introduciendo la aleatoriedad.
Una forma de manejar esto es la noción de temperatura softmax. Siempre experimenta
ment con diferentes temperaturas para encontrar la adecuada.
El algoritmo DeepDream es casi idéntico a la técnica de visualización de filtro de convnet introducida en el capítulo
5, y consiste en ejecutar un convnet a la inversa: hacer un ascenso gradual en la entrada al convnet para maximizar
la activación de un convnet.
filtro específico en una capa superior del convnet. DeepDream utiliza esta misma idea, con un
algunas diferencias simples:
4
Alexander Mordvintsev, Christopher Olah y Mike Tyka, “DeepDream: un ejemplo de código para visualizar
Neural Networks”, blog de investigación de Google, 1 de julio de 2015, http://mng.bz/xXlM.
No se parte de una entrada en blanco y ligeramente ruidosa, sino de una entrada existente.
imagen: por lo tanto, los efectos resultantes se aferran a patrones visuales preexistentes,
distorsionando elementos de la imagen de una manera un tanto artística.
Las imágenes de entrada se procesan en diferentes escalas (llamadas octavas), lo que
mejora la calidad de las visualizaciones.
elección afectará naturalmente sus visualizaciones, porque diferentes arquitecturas de convnet dan como resultado diferentes
características aprendidas. El
El convnet utilizado en la versión original de DeepDream era un modelo Inception y, en la práctica, se sabe
que Inception produce DeepDreams atractivos, por lo que usará el modelo Inception V3 que viene con Keras.
A continuación, calculará la pérdida: la cantidad que buscará maximizar durante el ascenso del gradiente.
proceso. En el capítulo 5, para la visualización de filtros, intentó maximizar el valor de un filtro específico.
en una capa específica. Aquí, maximizará simultáneamente la activación de todos los filtros en un número
de capas. Específicamente, maximizarás una suma ponderada de la norma L2 de las activaciones de un conjunto.
de capas de alto nivel. El conjunto exacto de capas que elijas (así como su contribución al resultado final).
pérdida) tiene una gran influencia en las imágenes que podrás producir, por lo que querrás hacer estas
Parámetros fácilmente configurables. Las capas inferiores dan como resultado patrones geométricos, mientras que las capas superiores
dan como resultado imágenes en las que se pueden reconocer algunas clases de ImageNet (por ejemplo, pájaros o
perros). Comenzarás con una configuración un tanto arbitraria que involucra cuatro capas, pero
contribuciones_capa = {
Diccionario que asigna nombres de capas a un coeficiente que cuantifica
'mixto2': 0,2,
cuánto contribuye la activación de la capa a la pérdida que buscará
'mixto3': 3.,
maximizar. Tenga en cuenta que los nombres de las capas están
'mixto4': 2.,
codificados en la aplicación integrada Inception V3. Puede enumerar todos
'mixto5': 1,5, los nombres de las capas usando model.summary().
}
pérdida = K.variable(0.)
Definirá la pérdida agregando
para nombre_capa en contribuciones_capa:
contribuciones de capa a esta
coeficiente = contribuciones_capa[nombre_capa] variable escalar.
activación = dictamen_capa[nombre_capa].salida
Finalmente: el algoritmo DeepDream real. Primero, define una lista de escalas (también llamada
octavas) en las que procesar las imágenes. Cada escala sucesiva es mayor que la anterior en
un factor de 1,4 (es un 40% mayor): se empieza procesando una imagen pequeña y
luego ampliarlo cada vez más (ver figura 8.4).
Detalle
Detalle reinyección
reinyección
Octava 1
Octava 2
Octava 3
Figura 8.4 El proceso DeepDream: escalas sucesivas de procesamiento espacial (octavas) y reinyección de detalles al aumentar la escala
Para cada escala sucesiva, desde la más pequeña hasta la más grande, ejecuta un ascenso de gradiente hasta
maximizar la pérdida que definiste previamente, a esa escala. Después de cada recorrido de ascenso en gradiente,
mejora la imagen resultante en un 40%.
Para evitar perder muchos detalles de la imagen después de cada ampliación sucesiva (lo que resulta en
imágenes cada vez más borrosas o pixeladas), puedes usar un truco simple: después de cada ampliación,
reinyectarás los detalles perdidos nuevamente en la imagen, lo cual es posible porque
sepa cómo debería verse la imagen original a mayor escala. Dada una pequeña imagen
tamaño S y una imagen más grande tamaño L, puede calcular la diferencia entre el original
imagen redimensionada al tamaño L y el original redimensionado al tamaño S: esta diferencia cuantifica la
Detalles perdidos al pasar de S a L.
Jugar con estos hiperparámetros te permitirá Tamaño del paso de ascenso del gradiente
ruta_imagen_base = '...' Complete esto con la ruta a la imagen que desea usar.
img = imagen_preproceso(ruta_imagen_base)
Carga la imagen base en una matriz Numpy (la
función se define en el listado 8.13)
img += detalles_perdidos
shrunk_original_img = resize_img(original_img, forma) save_img(img,
fname='dream_at_scale_' + str(forma) + '.png')
save_img(img, fname='final_dream.png')
Reinyecta detalles perdidos en el sueño.
Calcula la versión de alta calidad de la imagen
original en este tamaño. La diferencia entre los dos es el detalle que se
perdió al ampliar.
Tenga en cuenta que este código utiliza las siguientes funciones auxiliares sencillas de
Numpy, que hacen lo que sugieren sus nombres. Requieren que tengas SciPy instalado.
importar scipy
desde keras.preprocesamiento importar imagen
def deprocess_image(x):
Función útil para convertir un
si K.image_data_format() == 'canales_primero':
tensor en una imagen válida.
x = x.reshape((3, x.shape[2], x.shape[3])) x = x.transpose((1, 2, 0))
demás:
x = x.reformar((x.forma[1], x.forma[2], 3))
Deshace el preprocesamiento
x/= 2.
realizado por
x += 0,5
inception_v3.preprocess_ input
x*= 255.
x = np.clip(x, 0, 255).astype('uint8') devuelve x
NOTA Debido a que la red Inception V3 original fue entrenada para reconocer conceptos
en imágenes de tamaño 299 × 299, y dado que el proceso implica reducir las imágenes
en un factor razonable, la implementación de DeepDream produce resultados mucho
mejores en imágenes que están en algún punto entre 300 × 300 y 400 × 400.
Independientemente, puede ejecutar el mismo código en imágenes de cualquier tamaño
y proporción.
A partir de una fotografía tomada en las pequeñas colinas entre la Bahía de San Francisco y el
campus de Google, obtuvimos el DeepDream que se muestra en la figura 8.5.
Le sugerimos encarecidamente que explore lo que puede hacer ajustando las capas que utiliza en
su pérdida. Las capas que se encuentran más abajo en la red contienen representaciones más
locales y menos abstractas y conducen a patrones oníricos que parecen más geométricos. Las
capas que están más arriba conducen a patrones visuales más reconocibles basados en los
objetos más comunes que se encuentran en ImageNet, como ojos de perro, plumas de pájaro, etc. Puedes usar
Figura 8.6 Probar una variedad de configuraciones de DeepDream en una imagen de ejemplo
8.2.2 Conclusión
DeepDream consiste en ejecutar un convnet a la inversa para generar entradas basadas
sobre las representaciones aprendidas por la red.
Los resultados producidos son divertidos y algo similares a los artefactos visuales.
inducido en humanos por la alteración de la corteza visual a través de psicodélicos.
Tenga en cuenta que el proceso no es específico de los modelos de imágenes ni siquiera de las convnets. Puede
hacerse para el habla, la música y más.
En este contexto, estilo significa esencialmente texturas, colores y patrones visuales en la imagen, al menos.
varias escalas espaciales; y el contenido es la macroestructura de nivel superior de la imagen.
Por ejemplo, las pinceladas circulares azules y amarillas se consideran el estilo de la figura 8.7 (usando Noche
estrellada de Vincent Van Gogh), y los edificios de Tubinga
La fotografía se considera contenido.
La idea de transferencia de estilo, que está estrechamente relacionada con la de generación de texturas, ha
tenía una larga historia en la comunidad de procesamiento de imágenes antes del desarrollo de
transferencia de estilo neuronal en 2015. Pero resulta que las implementaciones de transferencia de estilo
basadas en el aprendizaje profundo ofrecen resultados incomparables con lo que se había logrado anteriormente.
con técnicas clásicas de visión por computadora, y desencadenaron un sorprendente renacimiento
en aplicaciones creativas de visión por computadora.
La noción clave detrás de la implementación de la transferencia de estilo es la misma idea central
a todos los algoritmos de aprendizaje profundo: usted define una función de pérdida para especificar lo que desea
lograr y minimizar esta pérdida. Ya sabes lo que quieres conseguir: conservar
el contenido de la imagen original adoptando el estilo de la imagen de referencia. Si
Si pudiéramos definir matemáticamente el contenido y el estilo, entonces una función de pérdida adecuada para
minimizar sería la siguiente:
5
Leon A. Gatys, Alexander S. Ecker y Matthias Bethge, "Un algoritmo neuronal de estilo artístico", arXiv (2015),
https://arxiv.org/abs/1508.06576.
Aquí, la distancia es una función normal como la norma L2, el contenido es una función que
toma una imagen y calcula una representación de su contenido, y el estilo es una función
que toma una imagen y calcula una representación de su estilo. Minimizando esto
la pérdida hace que el estilo (imagen_generada) esté cerca del estilo (imagen_referencia), y
El contenido (imagen_generada) está cerca del contenido (imagen_generada), logrando así
Transferencia de estilo tal como lo definimos.
Una observación fundamental hecha por Gatys et al. fue que las redes neuronales convolucionales profundas
ofrecen una manera de definir matemáticamente las funciones de estilo y contenido .
Veamos cómo.
Como ya sabe, las activaciones de capas anteriores en una red contienen información local sobre la imagen,
mientras que las activaciones de capas superiores contienen información cada vez más global.
información abstracta . Formulado de otra manera, las activaciones de las diferentes capas de una red proporcionan
una descomposición del contenido de una imagen en diferentes escalas espaciales. Por lo tanto, es de esperar
que el contenido de una imagen, que es más global y
abstracto, para ser capturado por las representaciones de las capas superiores en un convnet.
Un buen candidato para la pérdida de contenido es, por tanto, la norma L2 entre las activaciones de
una capa superior en una red convnet previamente entrenada, calculada sobre la imagen de destino, y las
activaciones de la misma capa calculadas sobre la imagen generada. Esto garantiza que, como
Visto desde la capa superior, la imagen generada se verá similar al objetivo original.
imagen. Suponiendo que lo que ven las capas superiores de una convnet es realmente el contenido de
sus imágenes de entrada, entonces esto funciona como una forma de preservar el contenido de la imagen.
La pérdida de contenido solo usa una única capa superior, pero la pérdida de estilo definida por Gatys
et al. utiliza múltiples capas de un convnet: intenta capturar la apariencia de la imagen de referencia de estilo en
todas las escalas espaciales extraídas por el convnet, no solo en una sola escala.
Para la pérdida de estilo, Gatys et al. utilizar la matriz de Gram de las activaciones de una capa: la interna
Producto de los mapas de características de una capa determinada. Este producto interno puede entenderse como
que representa un mapa de las correlaciones entre las características de la capa. Estas correlaciones de
características capturan las estadísticas de los patrones de una escala espacial particular, que corresponden
empíricamente a la apariencia de las texturas encontradas en esta escala.
Por lo tanto, la pérdida de estilo tiene como objetivo preservar correlaciones internas similares dentro de las
activaciones de diferentes capas, a través de la imagen de referencia de estilo y la imagen generada. En
A su vez, esto garantiza que las texturas encontradas en diferentes escalas espaciales se vean similares.
a través de la imagen de referencia de estilo y la imagen generada.
En resumen, puede utilizar una convnet previamente entrenada para definir una pérdida que hará lo siguiente:
Preservar el estilo manteniendo correlaciones similares dentro de las activaciones tanto para las capas de
bajo nivel como para las de alto nivel. Las correlaciones de características capturan texturas: la imagen
generada y la imagen de referencia de estilo deben compartir las mismas texturas en diferentes escalas
espaciales.
Ahora, veamos una implementación de Keras del algoritmo de transferencia de estilo neuronal original de 2015.
Como verá, comparte muchas similitudes con la implementación de DeepDream desarrollada en la sección anterior.
La transferencia de estilo neuronal se puede implementar utilizando cualquier convnet previamente entrenado.
Aquí, utilizará la red VGG19 utilizada por Gatys et al. VGG19 es una variante simple de la red VGG16 presentada
en el capítulo 5, con tres capas convolucionales más.
Este es el proceso general:
1 Configure una red que calcule las activaciones de capa VGG19 para la imagen de referencia de estilo, la
imagen de destino y la imagen generada al mismo tiempo.
2 Utilice las activaciones de capa calculadas sobre estas tres imágenes para definir la función de pérdida
descrita anteriormente, que minimizará para lograr la transferencia de estilo.
Comencemos definiendo las rutas a la imagen de referencia de estilo y a la imagen de destino. Para asegurarse
de que las imágenes procesadas tengan un tamaño similar (los tamaños muy diferentes dificultan la transferencia
de estilo), luego cambiará su tamaño a una altura compartida de 400 px.
Necesita algunas funciones auxiliares para cargar, preprocesar y posprocesar las imágenes que entran y salen del
convnet VGG19 .
def imagen_preproceso(ruta_imagen):
img = load_img(image_path, target_size=(img_height, img_width)) img = img_to_array(img) img =
np.expand_dims(img, axis=0) img
= vgg19.preprocess_input(img) return img
def deprocess_image(x):
x[:, :, 0] += 103.939 Centrado en cero eliminando el valor medio de píxeles de
x[:, :, 1] += 116.779 ImageNet. Esto invierte una transformación realizada
x[:, :, 2] += 123,68 por vgg19.preprocess_input.
x = x[:, :, ::1] x = np.clip(x,
0, 255).astype('uint8') Convierte imágenes de 'BGR' a 'RGB'.
volver x Esto también es parte de la reversión
de vgg19.preprocess_input.
Configuremos la red VGG19 . Toma como entrada un lote de tres imágenes: la imagen de referencia de
estilo, la imagen de destino y un marcador de posición que contendrá la imagen generada. Un marcador
de posición es un tensor simbólico, cuyos valores se proporcionan externamente a través de matrices
Numpy. La referencia de estilo y la imagen de destino son estáticas y, por lo tanto, se definen utilizando
K.constant, mientras que los valores contenidos en el marcador de posición de la imagen generada
cambiarán con el tiempo.
Listado 8.16 Cargando la red VGG19 previamente entrenada y aplicándola a las tres imágenes
input_tensor = K.concatenar([imagen_objetivo,
Combina las tres
imagen_referencia_estilo,
imágenes en un solo lote.
imagen_combinación], eje=0)
Definamos la pérdida de contenido, lo que garantizará que la capa superior de la red convnet VGG19 tenga
una vista similar de la imagen de destino y la imagen generada.
Lo siguiente es la pérdida de estilo. Utiliza una función auxiliar para calcular la matriz de Gram de una
matriz de entrada: un mapa de las correlaciones encontradas en la matriz de características original.
C = gram_matrix(combinación) canales = 3
tamaño = img_height
* img_width return K.sum(K.square(S C)) / (4.
* (canales ** 2) * (tamaño ** 2))
A estos dos componentes de pérdida, se agrega un tercero: la pérdida de variación total, que
opera en los píxeles de la imagen combinada generada. Fomenta la continuidad espacial en la
imagen generada, evitando así resultados demasiado pixelados. Puedes interpretarlo como una
pérdida de regularización.
b = K.cuadrado(
x[:, :img_height 1, :img_width 1, :] x[:, :img_height 1, 1:, :])
La pérdida que minimiza es un promedio ponderado de estas tres pérdidas. Para calcular la
pérdida de contenido, usa solo una capa superior (la capa block5_conv2 ), mientras que para la
pérdida de estilo, usa una lista de capas que abarca tanto las capas de bajo como las de alto
nivel. Agrega la pérdida total de variación al final.
Dependiendo de la imagen de referencia de estilo y la imagen de contenido que esté
utilizando, es probable que desee ajustar el coeficiente content_weight (la contribución de la
pérdida de contenido a la pérdida total). Un content_weight más alto significa que el contenido
de destino será más reconocible en la imagen generada.
pérdida = K.variable(0.)
Agrega Definirá la pérdida agregando todos
características_capa = dictamen_salidas[capa_contenido]
la los componentes a esta variable
características_imagen_objetivo = características_capa[0, :, :, :] escalar.
pérdida de
características_combinación = características_capa[2, :, :, :] pérdida +=
contenido.
peso_contenido * pérdida_contenido(características_imagen_objetivo,
combinación_características)
para nombre_capa en capas_estilo:
Agrega un componente
características_capa = dictamen_salidas[nombre_capa]
de pérdida de estilo
características_referencia_estilo = características_capa[1, :, :, :]
para cada capa de destino
características_combinación = características_capa[2, :, :, :] sl =
Agrega la
pérdida_estilo(características_referencia_estilo, características_combinación) pérdida += (peso_estilo /
pérdida
total de
len(capas_estilo)) * SL
variación.
pérdida += peso_variación_total * pérdida_variación_total(imagen_combinación)
Sería ineficaz calcular el valor de la función de pérdida y el valor de los gradientes de forma independiente,
porque hacerlo conduciría a muchos cálculos redundantes entre los dos; el proceso sería casi el doble
de lento que computarlos conjuntamente. Para evitar esto, configurará una clase de Python llamada
Evaluator que calcula tanto el valor de pérdida como el valor de gradientes a la vez, devuelve el valor de
pérdida cuando se llama por primera vez y almacena en caché los gradientes para la siguiente llamada.
valor_pérdida = salidas[0]
grad_values = salidas[1].flatten().astype('float64')
self.loss_value = valor_pérdida
self.grad_values = valores_graduación
devolver self.loss_value
evaluador = Evaluador()
Finalmente, puede ejecutar el proceso de ascenso de gradiente utilizando el algoritmo LBFGS de SciPy , ahorrando
la imagen generada actualmente en cada iteración del algoritmo (aquí, una sola iteración representa 20 pasos de
ascenso de gradiente).
x = imagen_preproceso(ruta_imagen_objetivo)
x = x.aplanar() Ejecuta la optimización L
para i en rango (iteraciones): BFGS sobre los píxeles
print('Inicio de iteración', i) de la imagen generada
hora_inicio = hora.hora() para minimizar la pérdida de
x, min_val, info = fmin_l_bfgs_b(evaluador.loss, estilo neuronal. Tenga en
X,
cuenta que debe pasar la
función que calcula la pérdida
fprime=evaluador.grads,
y la función que calcula los
máximafun=20)
gradientes como dos
print('Valor de pérdida actual:', min_val)
argumentos separados.
img = x.copia().reshape((img_height, img_width, 3))
img = deprocess_image(img)
nombref = prefijo_resultado + '_at_iteration_%d.png' % i Guarda el actual
imsave(nombref, img) imagen generada.
print('Imagen guardada como', fname)
tiempo_final = tiempo.tiempo()
print('Iteración %d completada en %ds' % (i, hora_final hora_inicio))
La figura 8.8 muestra lo que obtienes. Ten en cuenta que lo que consigue esta técnica es
simplemente una forma de retexturización de imágenes o transferencia de textura. Funciona mejor con imágenes
de referencia de estilo que tienen una textura fuerte y son muy similares, y con contenido
objetivos que no requieren altos niveles de detalle para ser reconocibles. normalmente
No puedo lograr hazañas bastante abstractas como transferir el estilo de un retrato a
otro. El algoritmo está más cerca del procesamiento de señales clásico que de la IA, así que no
¡Espera que funcione como por arte de magia!
Además, tenga en cuenta que la ejecución de este algoritmo de transferencia de estilo es lenta. Pero la transformación
operada por la configuración es lo suficientemente simple como para que también pueda aprenderse mediante una
pequeña y rápida red de alimentación anticipada, siempre que tenga disponibles los datos de entrenamiento adecuados.
Por lo tanto, se puede lograr una transferencia de estilo rápida gastando primero una gran cantidad de ciclos de
cómputo para generar ejemplos de entrenamiento de entradasalida para una imagen de referencia de estilo fija,
utilizando el método descrito aquí, y luego entrenando un convnet simple para aprender esta transformación específica
de estilo. Una vez hecho esto, estilizar una imagen determinada es instantáneo: es sólo un paso adelante de este
pequeño convnet.
8.3.4 Conclusión
La transferencia de estilo consiste en crear una nueva imagen que conserva el contenido de una
imagen de destino y al mismo tiempo captura el estilo de una imagen de referencia.
El contenido puede capturarse mediante activaciones de alto nivel de una convnet. El estilo
puede ser capturado por las correlaciones internas de las activaciones de diferentes capas de una convnet.
Por lo tanto, el aprendizaje
profundo permite formular la transferencia de estilo como una optimización.
proceso utilizando una pérdida definida con un convnet previamente
entrenado. A partir de esta idea básica, son posibles muchas variantes y perfeccionamientos.
Datos de entrenamiento
Proceso de
?
aprendizaje
Generador / Decodificador
latente artificial
Espacio latente
de imágenes
(un espacio vectorial)
Figura 8.9 Aprender un espacio vectorial latente de imágenes y usarlo para muestrear nuevas imágenes
Las GAN y los VAE son dos estrategias diferentes para aprender espacios latentes de representaciones de imágenes, cada
una con sus propias características. Los VAE son excelentes para aprender espacios latentes que están bien estructurados,
donde direcciones específicas codifican un eje de variación significativo en los datos. Las GAN generan imágenes que pueden
ser potencialmente muy realistas, pero el espacio latente del que provienen puede no tener tanta estructura y continuidad.
Figura 8.10 Un espacio continuo de caras generado por Tom White usando VAE
6
Diederik P. Kingma y Max Welling, “AutoEncoding Variational Bayes, arXiv (2013), https://arxiv.org/abs/
1312.6114 .
7
Danilo Jiménez Rezende, Shakir Mohamed y Daan Wierstra, “Retropropagación estocástica e inferencia
aproximada en modelos generativos profundos”, arXiv (2014), https://arxiv.org/abs/1401.4082.
Codificador Descifrador
Figura 8.12 Un codificador automático: asignar una entrada x a una representación comprimida y
luego decodificarla nuevamente como x'
En la práctica, estos codificadores automáticos clásicos no conducen a resultados particularmente útiles o agradables.
espacios latentes estructurados. Tampoco son muy buenos en compresión. Por estas razones, en gran medida han
pasado de moda. Los VAE, sin embargo, aumentan los codificadores automáticos
con un poco de magia estadística que les obliga a aprender espacios latentes continuos y muy estructurados. Han
resultado ser una poderosa herramienta para la generación de imágenes.
Un VAE, en lugar de comprimir su imagen de entrada en un código fijo en el espacio latente,
convierte la imagen en los parámetros de una distribución estadística: una media y una varianza. Básicamente, esto
significa que estás asumiendo que la imagen de entrada ha sido generada por un
proceso estadístico, y que la aleatoriedad de este proceso debe tenerse en cuenta
contabilidad durante la codificación y decodificación. Luego, el VAE utiliza la media y la varianza.
parámetros para muestrear aleatoriamente un elemento de la distribución y decodifica ese elemento a la entrada
original (ver figura 8.13). La estocasticidad de este proceso.
mejora la robustez y obliga al espacio latente a codificar representaciones significativas en todas partes: cada punto
muestreado en el espacio latente se decodifica en una salida válida.
Codificador
Reconstruido
imagen
Descifrador
Punto al azar
muestreado de
la distribución
Figura 8.13 Un VAE asigna una imagen a dos vectores, z_mean y z_log_sigma, que definen una distribución
de probabilidad sobre el espacio latente, que se utiliza para muestrear un punto latente para decodificar.
Debido a que épsilon es aleatorio, el proceso garantiza que cada punto que esté cerca de la ubicación
latente donde codificó input_img (zmean) pueda decodificarse en algo similar a
input_img, lo que obliga al espacio latente a ser continuamente significativo. Cualquier dos puntos cercanos
en el espacio latente se decodificará en imágenes muy similares. La continuidad, combinada con la baja
La dimensionalidad del espacio latente obliga a cada dirección en el espacio latente a codificar un eje significativo de
variación de los datos, haciendo que el espacio latente sea muy estructurado y, por lo tanto, altamente adecuado para
la manipulación a través de vectores conceptuales.
Los parámetros de un VAE se entrenan mediante dos funciones de pérdida: una pérdida de reconstrucción que
obliga a las muestras decodificadas a coincidir con las entradas iniciales, y una pérdida de regularización que
ayuda a aprender espacios latentes bien formados y reduce el sobreajuste de los datos de entrenamiento. vamos
repasar rápidamente una implementación de Keras de un VAE. Esquemáticamente, se ve así:
importar keras
desde keras importan capas
desde keras importa el backend como K
de keras.models importar modelo
importar numpy como np
input_img = keras.Input(forma=img_shape)
x = capas.Conv2D(32, 3,
padding='mismo', activación='relu')(input_img) x = capas.Conv2D(64, 3,
padding='mismo', activación='relu',
zancadas=( 2, 2))(x) x = capas.Conv2D(64, 3,
padding='mismo',
activación='relu')(x) x =
capas.Conv2D(64, 3, padding='mismo', activación ='relu')(x)
forma_antes_aplanamiento = K.int_forma(x)
x = capas.Flatten()(x) x =
capas.Dense(32, activación='relu')(x)
La imagen de entrada termina
z_mean = capas.Dense(latent_dim)(x) z_log_var =
codificada en estos dos parámetros.
capas.Dense(latent_dim)(x)
El siguiente es el código para usar z_mean y z_log_var, los parámetros de la distribución estadística
que se supone han producido input_img, para generar un punto espacial latente z.
Aquí, envuelve un código arbitrario (construido sobre las primitivas del backend de Keras) en una
capa Lambda . En Keras, todo debe ser una capa, por lo que el código que no forma parte de una
capa incorporada debe incluirse en una Lambda (o en una capa personalizada).
z = capas.Lambda(muestreo)([z_mean, z_log_var])
La siguiente lista muestra la implementación del decodificador. Cambia la forma del vector z a las
dimensiones de una imagen y luego usa algunas capas de convolución para obtener una salida de
imagen final que tiene las mismas dimensiones que el input_img original.
Listado 8.25 Red de decodificadores VAE, mapeando puntos de espacio latente a imágenes
x = capas.Dense(np.prod(shape_before_flattening[1:]),
Muestra mejorada de la entrada
activación='relu')(decoder_input)
x = capas.Reformar(forma_antes_aplanar[1:])(x)
x = capas.Conv2DTranspose(32, 3,
relleno='mismo', Utiliza una capa Conv2DTranspose
activación='relu', zancadas=(2, y una capa Conv2D para decodificar
2))(x) z en un mapa de características del
x = capas.Conv2D(1, 3, mismo tamaño que la
relleno='igual', entrada de la imagen original.
activación='sigmoide')(x)
Finalmente, está listo para crear instancias y entrenar el modelo. Debido a que la pérdida se soluciona
en la capa personalizada, no especifica una pérdida externa en el momento de la compilación
(pérdida=Ninguna), lo que a su vez significa que no pasará datos de destino durante el entrenamiento
(como puede ver, solo pasar x_train al modelo en forma).
vae = Modelo(input_img, y)
vae.compile(optimizer='rmsprop', pérdida=Ninguna) vae.summary()
vae.fit(x=x_train, y=Ninguno,
barajar = Verdadero,
épocas = 10,
tamaño_lote = tamaño_lote,
datos_validación = (x_test, Ninguno))
Una vez que se entrena dicho modelo (en MNIST, en este caso), se puede utilizar la red decodificadora
para convertir vectores espaciales latentes arbitrarios en imágenes.
Listado 8.28 Muestreo de una cuadrícula de puntos del espacio latente 2D y decodificarlos en imágenes
Decodifica el lote
Imágenes de digitos
8.4.4 Conclusión
La generación de imágenes con aprendizaje profundo se realiza mediante el aprendizaje de espacios
latentes que capturan información estadística sobre un conjunto de datos de imágenes. Al muestrear y
decodificar puntos del espacio latente, se pueden generar imágenes nunca antes vistas.
Hay dos herramientas principales para hacer esto: VAE y GAN.
Los VAE dan como resultado representaciones latentes continuas y altamente estructuradas. Por esta razón, funcionan
bien para realizar todo tipo de edición de imágenes en el espacio latente: cara
intercambiar, convertir una cara con el ceño fruncido en una cara sonriente, etc. ellos tambien trabajan
muy bien para hacer animaciones basadas en el espacio latente, como animar un paseo por un
sección transversal del espacio latente, que muestra una imagen inicial que se transforma lentamente en
diferentes imágenes de forma continua.
Las GAN permiten la generación de imágenes realistas de un solo cuadro, pero pueden no inducir
Espacios latentes con estructura sólida y alta continuidad.
Las aplicaciones prácticas más exitosas que he visto con imágenes se basan en VAE, pero las GAN
son extremadamente populares en el mundo de la investigación académica, al menos alrededor de 20162017.
Descubrirá cómo funcionan y cómo implementar uno en la siguiente sección.
SUGERENCIA Para seguir jugando con la generación de imágenes, sugiero trabajar con el conjunto de
datos de Atributos de rostros de celebridades a gran escala (CelebA). Es una imagen de descarga gratuita.
conjunto de datos que contiene más de 200.000 retratos de celebridades. Es excelente para
experimentar con vectores conceptuales en particular; definitivamente supera a MNIST.
8.5 Introducción a las redes generativas adversarias Las redes generativas adversarias
(GAN), introducidas en 2014 por Goodfellow et al.8, son una alternativa a las VAE para aprender espacios
latentes de imágenes. Permiten la generación de imágenes sintéticas bastante realistas al obligar a las
imágenes generadas a ser estadísticamente casi indistinguibles de las reales.
Una forma intuitiva de entender las GAN es imaginar a un falsificador intentando crear una pintura falsa
de Picasso. Al principio, el falsificador es bastante malo en la tarea. Mezcla algunas de sus falsificaciones
con Picassos auténticos y se los muestra todos a un marchante de arte. El marchante de arte hace
una evaluación de autenticidad para cada pintura y le da al falsificador información sobre lo que hace que un
Picasso parezca un Picasso. El falsificador vuelve a su estudio para preparar nuevas falsificaciones. A
medida que pasa el tiempo, el falsificador se vuelve cada vez más competente a la hora de imitar el estilo de
Picasso, y el marchante de arte se vuelve cada vez más experto en detectar falsificaciones.
Al final, tienen entre manos unos excelentes Picassos falsos.
Eso es lo que es una GAN : una red falsificadora y una red experta, cada una de ellas entrenada
para superar al otro. Como tal, una GAN se compone de dos partes:
Red generadora: toma como entrada un vector aleatorio (un punto aleatorio en el espacio latente) y
lo decodifica en una imagen sintética. Red discriminadora (o
adversaria): toma como entrada una imagen (real o sintética) y predice si la La imagen proviene del
conjunto de entrenamiento o fue creada por la red del generador.
La red generadora está entrenada para poder engañar a la red discriminadora y, por lo tanto, evoluciona
hacia la generación de imágenes cada vez más realistas a medida que avanza el entrenamiento: imágenes
artificiales que parecen indistinguibles de las reales, hasta el punto de que es imposible para el discriminador.
red para diferenciarlos (ver figura 8.15). Mientras tanto, el discriminador se adapta constantemente a las
capacidades del generador que mejoran gradualmente, estableciendo un alto nivel de realismo para las
imágenes generadas. Una vez finalizado el entrenamiento, el generador es capaz de convertir cualquier
punto de su espacio de entrada en una imagen creíble. A diferencia de los VAE, este espacio latente tiene
menos garantías explícitas de estructura significativa; en particular, no es continuo.
8
Ian Goodfellow et al., “Generative Adversarial Networks”, arXiv (2014), https://arxiv.org/abs/1406.2661.
vector aleatorio
Generado
desde el
(descifrado)
espacio latente
imagen
Generador (decodificador)
Capacitación
comentario
Mezcla de
imágenes reales y falsas.
Figura 8.15 Un generador transforma vectores latentes aleatorios en imágenes y un discriminador busca
distinguir las imágenes reales de las generadas. El generador está entrenado para engañar al discriminador.
Figura 8.16 Habitantes del espacio latente. Imágenes generadas por Mike Tyka utilizando una
GAN de varias etapas entrenada en un conjunto de datos de rostros (www.miketyka.com).
Estos son algunos de los trucos utilizados en la implementación del generador y discriminador GAN
en esta sección. No es una lista exhaustiva de consejos relacionados con GAN; Encontrará muchos
más en la literatura de GAN :
La estocasticidad es buena para inducir robustez. Debido a que el entrenamiento GAN da como resultado un
En equilibrio dinámico, es probable que las GAN se atasquen de muchas maneras. Introducir la aleatoriedad
durante el entrenamiento ayuda a prevenir esto. Introducimos la aleatoriedad
de dos maneras: usando abandono en el discriminador y agregando ruido aleatorio
a las etiquetas del discriminador.
Los gradientes escasos pueden dificultar el entrenamiento de GAN . En el aprendizaje profundo, la escasez es a menudo un
propiedad deseable, pero no en GAN. Dos cosas pueden inducir la escasez de gradiente:
operaciones de agrupación máxima y activaciones ReLU . En lugar de agrupación máxima, recomendamos
utilizar convoluciones escalonadas para reducir la resolución y recomendamos
usando una capa LeakyReLU en lugar de una activación ReLU . Es similar a ReLU, pero
relaja las restricciones de escasez al permitir pequeños valores de activación negativos.
En las imágenes generadas, es común ver artefactos de tablero de ajedrez causados por
cobertura desigual del espacio de píxeles en el generador (ver figura 8.17). Arreglar
Para esto, usamos un tamaño de núcleo que es divisible por el tamaño de la zancada siempre que usamos un
Avanzó Conv2DTranpose o Conv2D tanto en el generador como en el discriminador.
Figura 8.17 Artefactos de tablero de ajedrez causados por zancadas y tamaños de kernel no
coincidentes, lo que resulta en una cobertura desigual del espacio de píxeles: uno de los muchos errores de las GAN
8.5.3 El generador
Primero, desarrollemos un modelo generador que convierta un vector (del espacio latente—
durante el entrenamiento se muestreará al azar) en una imagen candidata. Uno de los
Muchos de los problemas que surgen comúnmente con las GAN es que el generador se atasca con imágenes
generadas que parecen ruido. Una posible solución es utilizar la desconexión tanto en el discriminador como en el
generador.
importar keras
desde keras importan capas
importar numpy como np
tenue_latente = 32
altura = 32
ancho = 32
canales = 3
generador_entrada = keras.Input(forma=(latent_dim,))
8.5.4 El discriminador
A continuación, desarrollará un modelo discriminador que toma como entrada una imagen
candidata (real o sintética) y la clasifica en una de dos clases: "imagen generada" o "imagen
real que proviene del conjunto de entrenamiento".
Capa de clasificación
x = capas.Abandono(0.4)(x)
Crea una instancia del modelo
x = capas.Denso(1, activación='sigmoide')(x) discriminador, que convierte una
entrada (32, 32, 3) en una
discriminador = keras.models.Model(discriminator_input, x) discriminator.summary()
decisión de clasificación
binaria (falso/real)
discriminator_optimizer = keras.optimizers.RMSprop(
lr=0,0008, Utiliza recorte de gradiente (por
valor de clip=1,0, valor) en el optimizador
decaimiento=1e8)
Para estabilizar el entrenamiento,
discriminator.compile(optimizador=discriminator_optimizer, pérdida='binary_crossentropy')
utiliza la caída de la tasa de aprendizaje.
discriminador.entrenable = Falso
Establece los pesos del
gan_input = keras.Input(forma=(latent_dim,)) gan_output = discriminador como no entrenables
discriminador(generador(gan_input)) gan = keras.models.Model(gan_input, (esto solo se aplicará al modelo gan)
gan_output)
Implementémoslo.
importar
sistema operativo desde keras.preprocesamiento importar imagen Carga datos CIFAR10
x_tren = x_tren[y_tren.aplanar() == 6]
Selecciona imágenes de ranas (clase 6)
x_train = x_train.reshape( (x_train.shape[0],)
+ (alto, ancho,
Normaliza los datos
canales)).astype('float32') / 255.
iteraciones = 10000
tamaño_lote = 20
save_dir = 'tu_dir'
Especifica dónde desea guardar las
imágenes generadas.
inicio = 0 para
el paso en el rango (iteraciones): Muestras de puntos
vectores_latentes_aleatorios = np.random.normal(tamaño=(tamaño_lote, latent_dim)) aleatorios en el
espacio latente.
Muestras de puntos
aleatorios en el
Reúne objetivos_engañosos = np.zeros((tamaño_batch, 1)) espacio latente.
etiquetas que
si paso % 100 == 0:
gan.save_weights('gan.h5') Guarda los pesos del modelo.
Guarda una
print('pérdida discriminadora:', d_loss) print('pérdida
Imprime métricas imagen generada
adversaria:', a_loss)
Durante el entrenamiento, es posible que vea que la pérdida adversaria comienza a aumentar considerablemente, mientras que
la pérdida discriminativa tiende a cero: el discriminador puede terminar dominando el
generador. Si ese es el caso, intente reducir la tasa de aprendizaje del discriminador y aumente
la tasa de abandono del discriminador.
Figura 8.18 Juega al discriminador: en cada fila, la GAN ideó dos imágenes y una imagen proviene
del conjunto de entrenamiento. ¿Puedes diferenciarlos? (Respuestas: las imágenes reales en
cada columna son media, superior, inferior, media).
8.5.7 Conclusión
Una GAN consta de una red generadora acoplada a una red discriminadora.
El discriminador está entrenado para diferenciar entre la salida del generador
e imágenes reales de un conjunto de datos de entrenamiento, y el generador está entrenado para engañar al
discriminado. Sorprendentemente, el generador nunca ve imágenes del entrenamiento.
establecer directamente; la información que tiene sobre los datos proviene del discriminador.
Las GAN son difíciles de entrenar, porque entrenar una GAN es un proceso dinámico en lugar de
que un simple proceso de descenso de gradiente con un paisaje de pérdidas fijas. Conseguir un
GAN para entrenar correctamente requiere el uso de una serie de trucos heurísticos, así como
extensa sintonía.
Las GAN pueden producir potencialmente imágenes muy realistas. Pero a diferencia de los VAE, el
El espacio latente que aprenden no tiene una estructura continua y ordenada y, por lo tanto, puede
no ser adecuado para ciertas aplicaciones prácticas, como la edición de imágenes mediante vectores
conceptuales de espacio latente.
Estas pocas técnicas cubren sólo los conceptos básicos de este campo en rápida expansión.
Hay mucho más por descubrir: el aprendizaje profundo generativo merece un libro completo.
Conclusiones
Casi has llegado al final de este libro. Este último capítulo resumirá y revisará los conceptos básicos y, al
mismo tiempo, ampliará sus horizontes más allá de las nociones relativamente básicas que ha aprendido
hasta ahora. Comprender el aprendizaje profundo y la IA es un viaje, y terminar este libro es simplemente el
primer paso. Quiero asegurarme de que se dé cuenta de esto y esté debidamente equipado para dar los
siguientes pasos de este viaje por su cuenta.
Comenzaremos con una vista panorámica de lo que deberías aprender de este libro.
Esto debería refrescar tu memoria con respecto a algunos de los conceptos que has aprendido.
A continuación, presentaremos una descripción general de algunas limitaciones clave del aprendizaje
profundo. Para utilizar una herramienta de forma adecuada, no sólo se debe comprender lo que puede hacer,
sino también ser consciente de lo que no puede hacer. Finalmente, ofreceré algunas ideas especulativas
sobre la evolución futura de los campos del aprendizaje profundo, el aprendizaje automático y la IA. Esto
debería resultarle especialmente interesante si desea dedicarse a la investigación fundamental. El capítulo
termina con una breve lista de recursos y estrategias para aprender más sobre la IA y mantenerse actualizado
con nuevos avances.
314
Aunque el aprendizaje profundo es sólo uno entre muchos enfoques del aprendizaje automático, no
está en pie de igualdad con los demás. El aprendizaje profundo es un gran éxito.
Este es el por qué.
En el lapso de sólo unos pocos años, el aprendizaje profundo ha logrado enormes avances en una amplia
gama de tareas que históricamente se han percibido como extremadamente difíciles para las computadoras,
especialmente en el área de la percepción de las máquinas: extraer información útil de imágenes, videos,
sonido y más. Con suficientes datos de entrenamiento (en particular, datos de entrenamiento adecuadamente
etiquetados por humanos), es posible extraer de los datos de percepción casi cualquier cosa que un humano
pueda extraer.
Por lo tanto, a veces se dice que el aprendizaje profundo ha resuelto la percepción, aunque eso es cierto
sólo para una definición bastante estrecha de percepción.
Debido a sus éxitos técnicos sin precedentes, el aprendizaje profundo ha provocado por sí solo el tercer
y, con diferencia, el mayor verano de IA : un período de intenso interés, inversión y exageración en el campo
de la IA. Mientras se escribe este libro, estamos en medio de él. Si este período terminará en un futuro
próximo y qué sucederá después de que finalice son temas de debate. Una cosa es segura: en marcado
contraste con veranos anteriores de IA , el aprendizaje profundo ha proporcionado un enorme valor comercial
a varias grandes empresas de tecnología, permitiendo el reconocimiento de voz a nivel humano, asistentes
inteligentes,
1
Richard Feynman, entrevista, El mundo desde otro punto de vista, Yorkshire Television, 1972.
Esa es la magia del aprendizaje profundo: convertir el significado en vectores, en espacios geométricos, y
luego aprender de forma incremental transformaciones geométricas complejas que asignan un espacio a otro.
Todo lo que necesita son espacios de dimensionalidad suficientemente alta para capturar el alcance completo de
las relaciones encontradas en los datos originales.
Todo gira en torno a una única idea central: que el significado se deriva de la relación de pares entre cosas
(entre palabras en un idioma, entre píxeles en una imagen, etc.) y que estas relaciones pueden capturarse
mediante una función de distancia. Pero tenga en cuenta que si el cerebro implementa significado a través de
espacios geométricos es una cuestión completamente separada. Es eficiente trabajar con espacios vectoriales
desde un punto de vista computacional, pero se pueden imaginar fácilmente diferentes estructuras de datos para
la inteligencia, en particular, gráficos. Las redes neuronales surgieron inicialmente de la idea de utilizar gráficos
como una forma de codificar significado, por lo que se denominan redes neuronales; el campo de investigación
circundante solía llamarse conexionismo. Hoy en día, el nombre de red neuronal existe puramente por razones
históricas; es un nombre extremadamente engañoso porque no son ni neuronales ni redes. En particular, las
redes neuronales apenas tienen nada que ver con el cerebro. Un nombre más apropiado habría sido aprendizaje
de representaciones en capas o aprendizaje de representaciones jerárquicas, o tal vez incluso modelos
diferenciables profundos o transformaciones geométricas encadenadas, para enfatizar el hecho de que la
manipulación continua del espacio geométrico es su núcleo.
Una compleja pila de capas de software que pone este poder computacional a disposición de los humanos:
el lenguaje CUDA , marcos como TensorFlow que realizan diferenciación automática y Keras, que hace
que el aprendizaje profundo sea accesible para la mayoría de las personas.
En el futuro, el aprendizaje profundo no sólo será utilizado por especialistas (investigadores, estudiantes de posgrado
e ingenieros con perfil académico), sino que también será una herramienta en la caja de herramientas de todo
desarrollador, muy similar a la tecnología web actual. Todo el mundo necesita crear aplicaciones inteligentes: así
como hoy en día todas las empresas necesitan un sitio web, cada producto deberá dar sentido de forma inteligente
a los datos generados por los usuarios. Para lograr este futuro será necesario crear herramientas que hagan que el
aprendizaje profundo sea radicalmente fácil de usar y accesible para cualquier persona con habilidades básicas de
codificación. Keras es el primer gran paso en esa dirección.
Tener acceso a una herramienta extremadamente poderosa para crear modelos que asignan cualquier espacio de
entrada a cualquier espacio objetivo es excelente, pero la parte difícil del flujo de trabajo del aprendizaje automático
es a menudo todo lo que viene antes de diseñar y entrenar dichos modelos (y, para la producción). modelos, lo que
viene después también). Comprender el dominio del problema para poder determinar qué intentar predecir, con qué
datos y cómo medir el éxito es un requisito previo para cualquier aplicación exitosa del aprendizaje automático, y no
es algo que herramientas avanzadas como Keras y TensorFlow puede ayudarte. Como recordatorio, aquí hay un
resumen rápido del flujo de trabajo típico de aprendizaje automático como se describe en el capítulo 4:
1 Defina el problema: ¿Qué datos están disponibles y qué está tratando de predecir?
¿Necesitará recopilar más datos o contratar personas para etiquetar manualmente un conjunto de datos?
2 Identifique una forma de medir de manera confiable el éxito en su objetivo. Para tareas simples, esto puede
ser precisión de predicción, pero en muchos casos requerirá métricas sofisticadas específicas del dominio.
3 Prepare el proceso de validación que utilizará para evaluar sus modelos. En particular, debe definir un
conjunto de entrenamiento, un conjunto de validación y un conjunto de prueba. Las etiquetas de validación
y conjunto de pruebas no deberían filtrarse en los datos de entrenamiento: por ejemplo, con la predicción
temporal, los datos de validación y prueba deben ser posteriores a los datos de entrenamiento.
4 Vectorice los datos convirtiéndolos en vectores y preprocesándolos de manera que sean más fácilmente
accesibles para una red neuronal (normalización, etc.).
5 Desarrolle un primer modelo que supere una base trivial de sentido común, demostrando así que el
aprendizaje automático puede funcionar en su problema. ¡Puede que este no sea siempre el caso!
Recuerde: para realizar una clasificación binaria, finalice su pila de capas con una capa Densa con
una sola unidad y una activación sigmoidea , y use binario_crossentropy como pérdida. Tus objetivos
deben ser 0 o 1:
modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(num_input_features,))) model.add(layers.Dense(32,
activación='relu')) modelo. agregar(capas.Dense(1,
activación='sigmoide'))
model.compile(optimizador='rmsprop', pérdida='binary_crossentropy')
Para realizar una clasificación categórica de etiqueta única (donde cada muestra tiene exactamente una
clase, no más), finalice su pila de capas con una capa Densa con una cantidad de unidades igual a la
cantidad de clases y una activación softmax . Si sus objetivos están codificados en caliente, utilice
categorical_crossentropy como pérdida; si son números enteros, use sparse_categorical_crossentropy:
modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(num_input_features,))) model.add(layers.Dense(32,
activación='relu')) modelo. agregar(capas.Dense(núm_clases,
activación='softmax'))
model.compile(optimizador='rmsprop', pérdida='categorical_crossentropy')
Para realizar una clasificación categórica de etiquetas múltiples (donde cada muestra puede tener varias
clases), finalice su pila de capas con una capa Densa con una cantidad de unidades igual a la cantidad
de clases y una activación sigmoidea , y use binario_crossentropy como pérdida.
Tus objetivos deben estar codificados en khot:
modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(num_input_features,))) model.add(layers.Dense(32,
activación='relu')) modelo. agregar(capas.Dense(núm_clases,
activación='sigmoide'))
model.compile(optimizador='rmsprop', pérdida='binary_crossentropy')
Para realizar una regresión hacia un vector de valores continuos, finalice su pila de capas con una capa
Densa con una cantidad de unidades igual a la cantidad de valores que está tratando de predecir (a
menudo uno solo, como el precio de una casa). y sin activación. Se pueden utilizar varias pérdidas para
la regresión, más comúnmente error_cuadrado medio (MSE) y error_absoluto_medio (MAE):
modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(num_input_features,))) model.add(layers.Dense(32,
activación='relu')) modelo. agregar (capas.Dense (núm_valores))
model.compile(optimizador='rmsprop', pérdida='mse')
CONVNETS
Las capas de convolución observan patrones espaciales locales aplicando la misma transformación
geométrica a diferentes ubicaciones espaciales (parches) en un tensor de entrada. Esto da como resultado
representaciones que son invariantes en la traducción, lo que hace que las capas convolucionales sean
altamente modulares y eficientes en términos de datos. Esta idea es aplicable a espacios de cualquier
dimensionalidad: 1D (secuencias), 2D (imágenes), 3D (volúmenes), etc. Puede usar la capa Conv1D para
procesar secuencias (especialmente texto; no funciona tan bien en series temporales, que a menudo no
siguen el supuesto de invariancia de traducción), la capa Conv2D para procesar imágenes y las capas
Conv3D para procesar volúmenes. .
Las convnets, o redes convolucionales, constan de pilas de capas convolucionales y de agrupación
máxima. Las capas de agrupación le permiten reducir espacialmente los datos, lo cual es necesario para
mantener los mapas de características en un tamaño razonable a medida que crece la cantidad de
características y para permitir que las capas de convolución posteriores "vean" una mayor extensión espacial
de las entradas. Las convnets a menudo finalizan con una operación de aplanamiento o una capa de
agrupación global, convirtiendo mapas de características espaciales en vectores, seguidos de capas densas
para lograr clasificación o regresión.
Tenga en cuenta que es muy probable que las convoluciones regulares pronto sean reemplazadas en
su mayor parte (o completamente) por una alternativa equivalente pero más rápida y representacionalmente
eficiente: la convolución separable en profundidad (capa SeparableConv2D ). Esto es válido para entradas
3D, 2D y 1D. Cuando estás construyendo una nueva red desde cero, usar convoluciones separables en
profundidad es definitivamente el camino a seguir. La capa SeparableConv2D se puede utilizar como
reemplazo directo de Conv2D, lo que da como resultado una red más pequeña y más rápida que también
funciona mejor en su tarea.
A continuación se muestra una red típica de clasificación de imágenes (clasificación categórica, en este
caso):
modelo = modelos.Sequential()
model.add(layers.SeparableConv2D(32, 3, activación='relu', input_shape=(alto, ancho,
canales))) model.add(layers.SeparableConv2D(64, 3,
activación= 'relu')) modelo.add(capas.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activación='relu'))
model.add(layers.SeparableConv2D(128, 3, activación='relu')) model.add(layers.MaxPooling2D(2))
model.add(layers.SeparableConv2D(64, 3, activación='relu'))
model.add(layers.SeparableConv2D(128, 3, activación='relu'))
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dense(32, activación='relu'))
model.add(layers.Dense(num_classes, activación='softmax'))
model.compile(optimizador='rmsprop', pérdida='categorical_crossentropy')
RNNS
Las redes neuronales recurrentes (RNN) funcionan procesando secuencias de entradas paso a paso y
manteniendo un estado en todo momento (un estado suele ser un vector o un conjunto de vectores:
un punto en un espacio geométrico de estados). Deben usarse preferentemente sobre redes de conversión
1D en el caso de secuencias donde los patrones de interés no son invariantes por tiempo.
traducción (por ejemplo, datos de series temporales donde el pasado reciente es más importante que
el pasado lejano).
Hay tres capas RNN disponibles en Keras: SimpleRNN, GRU y LSTM. Para la mayoría de los propósitos
prácticos, debe utilizar GRU o LSTM. LSTM es el más poderoso de los
dos pero también es más caro; Puedes pensar en GRU como una alternativa más sencilla y económica.
lo.
Para apilar varias capas RNN una encima de la otra, cada capa anterior a la
la última capa de la pila debe devolver la secuencia completa de sus salidas (cada paso de tiempo de entrada
corresponderá a un paso de tiempo de salida); Si no está apilando más capas RNN , entonces es común
devolver solo la última salida, que contiene información.
sobre toda la secuencia.
A continuación se muestra una única capa RNN para la clasificación binaria de secuencias de vectores:
modelo = modelos.Secuencial()
model.add(layers.LSTM(32, input_shape=(num_timesteps, num_features)))
model.add(layers.Dense(num_classes, activación='sigmoide'))
50model.compile(optimizador='rmsprop', pérdida='binary_crossentropy')
Y esta es una capa RNN apilada para la clasificación binaria de secuencias vectoriales:
modelo = modelos.Secuencial()
model.add(layers.LSTM(32, return_sequences=True,
input_shape=(num_timesteps, num_features)))
model.add(layers.LSTM(32, return_sequences=True))
modelo.add(capas.LSTM(32))
model.add(layers.Dense(num_classes, activación='sigmoide'))
model.compile(optimizador='rmsprop', pérdida='binary_crossentropy')
– Orientación por comportamiento: mapear un conjunto de atributos del sitio web con datos sobre cuánto tiempo
pasará un usuario en el sitio web.
– Control de calidad del producto : mapeo de un conjunto de atributos relativos a una instancia de un producto
fabricado con la probabilidad de que el producto falle en el próximo
año
– Asistente médico: mapeo de diapositivas de imágenes médicas con una predicción sobre la presencia de un
tumor
– Vehículo autónomo : asignación de cuadros de video de la cámara del tablero del automóvil a comandos de
ángulo del volante
– Predicción del tiempo: mapeo de series temporales de datos meteorológicos en una cuadrícula de ubicaciones
de datos meteorológicos de la semana siguiente en una ubicación específica
– Orientación por comportamiento: mapeo de series temporales de interacciones de usuarios en un sitio web para
la probabilidad de que un usuario compre algo Mapeo de
texto a texto
– Respuesta inteligente : asignación de correos electrónicos a posibles respuestas de una sola línea
– Generación de imágenes condicionadas: asignación de una breve descripción de texto a imágenes que
coinciden con la descripción.
– Súper resolución: asignación de imágenes reducidas a versiones de mayor resolución de las mismas imágenes
Casi todo es posible, pero no absolutamente nada. Veamos en la siguiente sección qué
No podemos hacerlo con el aprendizaje profundo.
Esto se debe a que un modelo de aprendizaje profundo es solo una cadena de transformaciones
geométricas continuas y simples que mapean un espacio vectorial en otro. Todo lo que puede hacer es mapear
una variedad de datos X en otra variedad Y, asumiendo la existencia de una transformación continua que se
puede aprender de X a Y. Un modelo de aprendizaje profundo puede interpretarse como una especie de
programa; pero, a la inversa, la mayoría de los programas no pueden expresarse como modelos de aprendizaje
profundo: para la mayoría de las tareas, o no existe una red neuronal profunda correspondiente que resuelva
la tarea o, incluso si existe, es posible que no se pueda aprender: la correspondiente red neuronal profunda La
transformación puede ser demasiado compleja o puede que no haya datos adecuados disponibles para aprenderla.
Ampliar las técnicas actuales de aprendizaje profundo apilando más capas y utilizando más datos de
entrenamiento sólo puede paliar superficialmente algunos de estos problemas. No resolverá los problemas
más fundamentales de que los modelos de aprendizaje profundo están limitados en lo que pueden representar
y que la mayoría de los programas que desee aprender no pueden expresarse como una transformación
geométrica continua de una variedad de datos.
En particular, esto se pone de relieve en los ejemplos contradictorios, que son muestras enviadas a una red
de aprendizaje profundo que están diseñadas para engañar al modelo para que las clasifique erróneamente.
Ya sabes que, por ejemplo, es posible realizar un ascenso de gradiente en el espacio de entrada para
generar entradas que maximicen la activación de algún filtro convnet; esta es la base de la técnica de
visualización de filtro introducida en el capítulo 5, así como la Algoritmo DeepDream en el capítulo 8. De
manera similar, mediante el ascenso de gradiente, puede modificar ligeramente una imagen para maximizar
la predicción de clase para una clase determinada. Al tomar una fotografía de un panda y agregarle un
gradiente de gibón, podemos obtener una red neuronal para clasificar al panda como un gibón (ver figura
9.2). Esto evidencia tanto la fragilidad de estos modelos como la profunda diferencia entre su mapeo de
entrada a salida y nuestra percepción humana.
f(x) f(x)
Panda ¡Gibón!
gradiente
de clase gibón
Figura 9.2 Un ejemplo contradictorio: cambios imperceptibles en una imagen pueden alterar
la clasificación de la imagen por parte de un modelo.
Datos etiquetados
encarnado Conceptos abstractos ejemplificando Aprendizaje automático
Mundo real experiencia humana en la mente humana. estos conceptos modelo
f(x)
Figura 9.3 Modelos actuales de aprendizaje automático: como una imagen tenue en un espejo
Como profesional del aprendizaje automático, tenga siempre esto en cuenta y nunca caiga en el error.
trampa de creer que las redes neuronales comprenden la tarea que realizan; no es así,
al menos no de una manera que tenga sentido para nosotros. Fueron entrenados en un mundo diferente y lejano.
tarea más estrecha que la que queríamos enseñarles: la de mapear los insumos de capacitación
a los objetivos de entrenamiento, punto por punto. Muéstreles cualquier cosa que se desvíe de sus datos de entrenamiento
y se romperán de forma absurda.
antes, como imaginarnos un caballo con jeans, por ejemplo, o imaginar lo que haríamos si ganáramos la
lotería. Esta capacidad de manejar hipótesis, de expandir el espacio de nuestro modelo mental mucho
más allá de lo que podemos experimentar directamente (para realizar abstracciones y razonamientos )
es posiblemente la característica definitoria de la cognición humana. Yo lo llamo generalización extrema:
la capacidad de adaptarse a situaciones novedosas y nunca antes experimentadas utilizando pocos datos
o incluso ningún dato nuevo.
Esto contrasta fuertemente con lo que hacen las redes profundas, que yo llamo generalización local
(ver figura 9.4). El mapeo de entradas a salidas realizado por una red profunda rápidamente deja de tener
sentido si las nuevas entradas difieren aunque sea ligeramente de lo que la red vio en el momento del
entrenamiento. Consideremos, por ejemplo, el problema de aprender los parámetros de lanzamiento
apropiados para lograr que un cohete aterrice en la Luna. Si usara una red profunda para esta tarea y la
entrenara usando aprendizaje supervisado o aprendizaje reforzado, tendría que alimentarla con miles o
incluso millones de pruebas de lanzamiento: necesitaría exponerla a una muestra densa del espacio de
entrada, para que aprenda un mapeo confiable desde el espacio de entrada al espacio de salida. Por el
contrario, como seres humanos podemos utilizar nuestro poder de abstracción para crear modelos físicos
(ciencia espacial) y derivar una solución exacta que permita alunizar el cohete.
en uno o varios ensayos. De manera similar, si desarrollaras una red profunda que controle un cuerpo
humano y quisieras que aprendiera a navegar con seguridad por una ciudad sin ser atropellada por
automóviles, la red tendría que morir miles de veces en diversas situaciones hasta que pudiera inferir que
los automóviles son peligroso y desarrollar conductas de evitación apropiadas. Al caer en una nueva
ciudad, la red tendría que reaprender la mayor parte de lo que sabe. Por otro lado, los humanos podemos
aprender comportamientos seguros sin tener que morir ni una sola vez, gracias a nuestro poder de
modelado abstracto de situaciones hipotéticas.
El mismo conjunto de
puntos de
datos o experiencia
En resumen, a pesar de nuestros avances en la percepción de las máquinas, todavía estamos lejos de
una IA a nivel humano. Nuestros modelos sólo pueden realizar generalizaciones locales, adaptándose a
nuevas situaciones que deben ser similares a datos pasados, mientras que la cognición humana es capaz de
9.2.3 Conclusión
Esto es lo que debes recordar: el único éxito real del aprendizaje profundo hasta ahora ha sido
sido la capacidad de mapear el espacio X al espacio Y usando una transformación geométrica continua,
dadas grandes cantidades de datos anotados por humanos. Hacer esto bien es un punto de inflexión para
Básicamente, todas las industrias, pero todavía está muy lejos de la IA a nivel humano.
Eliminar algunas de las limitaciones que hemos discutido y crear una IA que pueda competir.
Con los cerebros humanos, debemos alejarnos de los mapeos directos de entrada a salida y pasar al razonamiento
y la abstracción. Un sustrato probablemente apropiado para abstractos.
El modelado de diversas situaciones y conceptos es el de los programas de ordenador. Dijimos
anteriormente que los modelos de aprendizaje automático pueden definirse como programas que se pueden
aprender; Actualmente sólo podemos aprender programas que pertenecen a un subconjunto estrecho y específico de todos.
posibles programas. Pero, ¿y si pudiéramos aprender cualquier programa, de forma modular y reutilizable?
¿forma? Veamos en la siguiente sección cómo será el camino por delante.
Modelos más cercanos a los programas informáticos de propósito general, construidos sobre primitivos
mucho más ricos que las capas diferenciables actuales. Así es como llegaremos al razonamiento y
abstracción, cuya falta es la debilidad fundamental de los modelos actuales.
Nuevas formas de aprendizaje que hacen posible el punto anterior, permitiendo que los modelos se muevan
lejos de transformaciones diferenciables.
Modelos que requieren menos participación de los ingenieros humanos. No debería ser tu trabajo
perillas de sintonización sin cesar.
Una mayor reutilización sistemática de características y arquitecturas previamente aprendidas, como los
sistemas de metaaprendizaje que utilizan subrutinas de programas modulares y reutilizables.
Además, tenga en cuenta que estas consideraciones no son específicas del tipo de supervisión
aprendizaje que ha sido el pan y la mantequilla del aprendizaje profundo hasta ahora; más bien, son
aplicable a cualquier forma de aprendizaje automático, incluido el no supervisado, el autosupervisado,
y aprendizaje por refuerzo. No es fundamentalmente importante de dónde vienen tus etiquetas.
desde o cómo se ve su ciclo de entrenamiento; Estas diferentes ramas del aprendizaje automático son diferentes
facetas del mismo constructo. Vamos a sumergirnos.
¿Qué camino podría hacer que esto suceda? Considere un tipo de red bien conocido: las RNN.
Es importante tener en cuenta que las RNN tienen ligeramente menos limitaciones que las redes feedforward. Esto
se debe a que los RNN son un poco más que meras transformaciones geométricas: