0% encontró este documento útil (0 votos)
40 vistas353 páginas

Deep Lerning Wint Python

El documento es un libro sobre aprendizaje profundo con Python escrito por François Chollet, que cubre tanto los fundamentos teóricos como las aplicaciones prácticas del aprendizaje profundo. Se organiza en dos partes: la primera aborda los conceptos básicos y matemáticos, mientras que la segunda se centra en la implementación en áreas como visión por computadora y procesamiento de texto. Además, incluye ejemplos prácticos y mejores prácticas para el desarrollo de modelos de aprendizaje profundo.

Cargado por

Angel Zapata
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
40 vistas353 páginas

Deep Lerning Wint Python

El documento es un libro sobre aprendizaje profundo con Python escrito por François Chollet, que cubre tanto los fundamentos teóricos como las aplicaciones prácticas del aprendizaje profundo. Se organiza en dos partes: la primera aborda los conceptos básicos y matemáticos, mientras que la segunda se centra en la implementación en áreas como visión por computadora y procesamiento de texto. Además, incluye ejemplos prácticos y mejores prácticas para el desarrollo de modelos de aprendizaje profundo.

Cargado por

Angel Zapata
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 353

Machine Translated by Google

François Chollet

MANTENIMIENTO
Machine Translated by Google

Aprendizaje profundo con Python

Con licencia para <nulo>


Machine Translated by Google

Con licencia para <nulo>


Machine Translated by Google

Aprendizaje
profundo con Pytho

FRANÇOIS CHOLLET

MANTENIMIENTO
ISLA REFUGIO

Con licencia para <nulo>


Machine Translated by Google

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

Departamento de Ventas Especiales


Publicaciones Manning Co.
20 Baldwin Road
Apartado postal 761

Isla Shelter, Nueva York 11964


Correo electrónico: [email protected]

©2018 por Manning Publications Co. Todos los derechos reservados.

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.

Publicaciones Manning Co. Editor de desarrollo: Toni Arritola


20 Baldwin Road PO Editor de desarrollo técnico: Jerry Gaines
Box 761 Editor de reseñas: Aleksandar Dragosavljevic´
Shelter Island, Nueva York 11964 Editora del proyecto: Tiffany Taylor
Editora: Tiffany Taylor
Correctora: Katie Tennant
Correctores técnicos: Alex Ott y Richard Tobias
Tipografista: Dottie Marsico
Diseño de portada: Marija Tudor

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

Con licencia para <nulo>


Machine Translated by Google

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

PARTE 2 APRENDIZAJE PROFUNDO EN LA PRÁCTICA ........................................ 117


5 ■ Aprendizaje profundo para visión por computadora
119 6 ■ Aprendizaje profundo para texto y secuencias 178
7 ■ Mejores prácticas avanzadas de aprendizaje profundo
233 8 ■ Aprendizaje profundo generativo 269
9 ■ Conclusiones 314

Con licencia para <nulo>


Machine Translated by Google

Con licencia para <nulo>


Machine Translated by Google

contenido
prefacio xiii
agradecimientos xv
acerca de este libro xvi
sobre el autor xx
sobre la portada xxi

PARTE 1 FUNDAMENTOS DEL APRENDIZAJE PROFUNDO ...................1

1 ¿Qué es el aprendizaje profundo? 3


1.1 Inteligencia artificial, aprendizaje automático y
aprendizaje profundo 4
Inteligencia artificial 4 ■ Aprendizaje automático 4 ■ Aprendizaje de
representaciones a partir de datos 6 ■ Lo “profundo” en el aprendizaje profundo 8
Comprender cómo funciona el aprendizaje profundo, en tres cifras 9
Lo que el aprendizaje profundo ha logrado hasta ahora 11 ■ No crea en
las exageraciones a corto plazo 12 ■ La promesa de la IA 13

1.2 Antes del aprendizaje profundo: una breve historia del


aprendizaje automático 14
Modelado probabilístico 14 ■ Primeras redes neuronales 14
Métodos del kernel 15 ■ Árboles de decisión, bosques aleatorios
y máquinas de aumento de gradiente 16 ■ Regreso a las redes
neuronales 17 ■ Qué hace que el aprendizaje profundo sea diferente 17
El panorama moderno del aprendizaje automático 18

viii

Con licencia para <nulo>


Machine Translated by Google

viii CONTENIDO

1.3 ¿Por qué el aprendizaje profundo? ¿Porqué ahora? 20


Hardware 20 ■ Datos 21 ■ Algoritmos 21 ■ Una nueva ola de
inversión 22 ■ La democratización del aprendizaje profundo 23
■ ¿Durará? 23

2 Antes de comenzar: los componentes matemáticos de


redes neuronales 25
2.1 Un primer vistazo a una red neuronal 27

2.2 Representaciones de datos para redes neuronales 31


Escalares (tensores 0D) 31 ■ Vectores (tensores 1D) 31
Matrices (tensores 2D) 31 ■ Tensores 3D y tensores de
dimensiones superiores 32 ■ Atributos clave 32
Manipulación de tensores en Numpy 34 ■ La noción de lotes
de datos 34 ■ Ejemplos del mundo real de tensores de
datos 35 ■ Datos vectoriales 35 ■ Datos de series temporales
o datos de secuencia 35 ■ Datos de imágenes 36 ■ Datos de vídeo 37

2.3 Los engranajes de las redes neuronales: operaciones tensoriales 38


Operaciones de elementos 38 ■ Difusión 39 ■ Punto tensorial 40 ■
Remodelación de tensores 42 ■ Interpretación geométrica de
operaciones tensoriales 43 ■ Una interpretación geométrica del
aprendizaje profundo 44

2.4 El motor de las redes neuronales: optimización basada en


gradientes 46
¿Qué es un derivado? 47 ■ Derivada de una operación tensorial: el
gradiente 48 ■ Descenso del gradiente estocástico 48
Encadenamiento de derivadas: el algoritmo de retropropagación 51

2.5 Mirando hacia atrás a nuestro primer ejemplo 53


2.6 Resumen del capítulo 55

3 Comenzando con las redes neuronales 56


3.1 Anatomía de una red neuronal 58
Capas: los componentes básicos del aprendizaje profundo 58 ■
Modelos: redes de capas 59 ■ Funciones de pérdida y optimizadores:
claves para configurar el proceso de aprendizaje 60

3.2 Introducción a Keras 61


Keras, TensorFlow, Theano y CNTK 62 ■ Desarrollar con Keras: una
descripción general rápida 62

3.3 Configuración de una estación de trabajo de aprendizaje profundo 65


Cuadernos Jupyter: la forma preferida de ejecutar experimentos
de aprendizaje profundo 65 ■ Ejecutar Keras: dos opciones 66

Con licencia para <nulo>


Machine Translated by Google

CONTENIDO ix

Ejecutar trabajos de aprendizaje profundo en la nube: pros y contras 66


¿Cuál es la mejor GPU para el aprendizaje profundo? 66

3.4 Clasificación de reseñas de películas: un ejemplo de clasificación


binaria 68
El conjunto de datos IMDB 68 ■ Preparación de los datos 69
Construyendo su red 70 ■ Validando su enfoque 73
Uso de una red entrenada para generar predicciones sobre nuevos
datos 76 ■ Experimentos adicionales 77 ■ Conclusión 77

3.5 Clasificación de noticias: un ejemplo de clasificación multiclase


78
El conjunto de datos de Reuters 78 ■ Preparación de los datos 79
Construyendo su red 79 ■ Validando su enfoque 80
Generar predicciones sobre nuevos datos 83 ■ Una forma diferente de
manejar las etiquetas y la pérdida 83 ■ La importancia de tener capas
intermedias suficientemente grandes 83 ■ Experimentos adicionales 84 ■
Conclusión 84

3.6 Predicción de los precios de la vivienda: un ejemplo de regresión 85


El conjunto de datos del precio de la vivienda en Boston 85 ■ Preparar
los datos 86 ■ Construir su red 86 ■ Validar su enfoque usando la
validación K­fold 87 ■ Conclusión 91

3.7 Resumen del capítulo 92

4 Fundamentos del aprendizaje automático 93


4.1 Cuatro ramas del aprendizaje automático 94
Aprendizaje supervisado 94 ■ Aprendizaje no supervisado 94
Aprendizaje autosupervisado 94 ■ Aprendizaje por refuerzo 95

4.2 Evaluación de modelos de aprendizaje automático 97


Conjuntos de entrenamiento, validación y pruebas 97 ■ Cosas a
tener en cuenta 100

4.3 Preprocesamiento de datos, ingeniería de funciones y


aprendizaje de funciones 101
Preprocesamiento de datos para redes neuronales 101 ■ Ingeniería
de funciones 102

4.4 Sobreajuste y desajuste 104


Reducir el tamaño de la red 104 ■ Agregar regularización de
peso 107 ■ Agregar abandono 109

4.5 El flujo de trabajo universal del aprendizaje automático 111


Definir el problema y armar un conjunto de datos 111
Elegir una medida de éxito 112 ■ Decidir sobre una

Con licencia para <nulo>


Machine Translated by Google

X CONTENIDO

protocolo de evaluación 112 ■ Preparación de sus datos 112


Desarrollar un modelo que funcione mejor que una línea base 113
Ampliación: desarrollo de un modelo que se ajuste a 114
Regularizando su modelo y ajustando sus hiperparámetros 114

4.6 Resumen del capítulo 116

PARTE 2 APRENDIZAJE PROFUNDO EN LA PRÁCTICA .........117


5 Aprendizaje profundo para la visión por computadora 119
5.1 Introducción a las convnets 120

La operación de convolución 122 ■ La operación de agrupación


máxima 127

5.2 Entrenando un convnet desde cero en un pequeño conjunto de datos 130


La relevancia del aprendizaje profundo para problemas de datos pequeños 130
Descargar los datos 131 ■ Construir su red 133
Preprocesamiento de datos 135 ■ Uso del aumento de datos 138

5.3 Usando un convnet previamente entrenado 143


Extracción de características 143 ■ Ajuste fino 152 ■ Conclusión 159

5.4 Visualizando lo que aprenden los convnets 160


Visualización de activaciones intermedias 160 ■ Visualización de filtros
convnet 167 ■ Visualización de mapas de calor de activación de
clases 172

5.5 Resumen del capítulo 177

6 Aprendizaje profundo para texto y secuencias 178


6.1 Trabajar con datos de texto 180
Codificación one­hot de palabras y caracteres 181 ■ Uso de incrustaciones
de palabras 184 ■ Juntándolo todo: desde texto sin formato hasta
incrustaciones de palabras 188 ■ Conclusión 195

6.2 Comprensión de las redes neuronales recurrentes 196


Una capa recurrente en Keras 198 ■ Comprensión de las capas LSTM
y GRU 202 ■ Un ejemplo concreto de LSTM en Keras 204 ■ Conclusión 206

6.3 Uso avanzado de redes neuronales recurrentes 207

Un problema de pronóstico de temperatura 207 ■ Preparación de los datos


210 ■ Una línea base de sentido común, sin aprendizaje automático
212 ■ Un enfoque básico de aprendizaje automático 213
Una primera línea de base recurrente 215 ■ Uso del abandono recurrente

Con licencia para <nulo>


Machine Translated by Google

CONTENIDO xi

para luchar contra el sobreajuste 216 ■ Apilar capas recurrentes 217


Uso de RNN bidireccionales 219 ■ Yendo aún más lejos 222
Concluyendo 223

6.4 Procesamiento de secuencias con convnets 225


Comprender la convolución 1D para datos de secuencia 225
Agrupación 1D para datos de secuencia 226 ■ Implementación de
una convnet 1D 226 ■ Combinación de CNN y RNN para procesar
secuencias largas 228 ■ Conclusión 231

6.5 Resumen del capítulo 232

7 mejores prácticas avanzadas de aprendizaje profundo 233


7.1 Más allá del modelo Secuencial: las Keras
API funcional 234
Introducción a la API funcional 236 ■ Modelos de múltiples
entradas 238 ■ Modelos de múltiples salidas 240 ■ Gráficos acíclicos
dirigidos de capas 242 ■ Peso compartido de capas 246 ■ Modelos como
capas 247 ■ Conclusión 248

7.2 Inspeccionar y monitorear modelos de aprendizaje profundo utilizando


Devoluciones de llamada de Keras y TensorBoard 249

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

7.3 Aprovechando al máximo tus modelos 260


Patrones de arquitectura avanzada 260 ■ Optimización de
hiperparámetros 263 ■ Ensamblaje de modelos 264 ■ Conclusión 266

7.4 Resumen del capítulo 268

8 Aprendizaje profundo generativo 269


8.1 Generación de texto con LSTM 271
Una breve historia de las redes generativas recurrentes 271 ■ ¿Cómo
se generan datos de secuencia? 272 ■ La importancia de la
estrategia de muestreo 272 ■ Implementación de la generación de
texto LSTM a nivel de caracteres 274 ■ Conclusión 279

8.2 Sueño profundo 280


Implementación de DeepDream en Keras 281 ■ Conclusión 286

8.3 Transferencia de estilo neuronal 287


La pérdida de contenido 288 ■ La pérdida de estilo 288 ■ Transferencia
de estilo neuronal en Keras 289 ■ Conclusión 295

Con licencia para <nulo>


Machine Translated by Google

xiii CONTENIDO

8.4 Generación de imágenes con codificadores automáticos variacionales 296


Muestreo de espacios latentes de imágenes 296 ■ Vectores conceptuales para
edición de imágenes 297 ■ Codificadores automáticos variacionales 298
Concluyendo 304

8.5 Introducción a las redes generativas adversativas 305


Una implementación esquemática de GAN 307 ■ Una bolsa de trucos 307
El generador 308 ■ El discriminador 309 ■ La red adversaria 310 ■ Cómo entrenar
su DCGAN 310 ■ Conclusión 312

8.6 Resumen del capítulo 313

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

9.2 Las limitaciones del aprendizaje profundo 325


El riesgo de antropomorfizar los modelos de aprendizaje automático 325
Generalización local versus generalización extrema 327
Concluyendo 329

9.3 El futuro del aprendizaje profundo 330


Modelos como programas 330 ■ Más allá de la retropropagación y las
capas diferenciables 332 ■ Aprendizaje automático automatizado 332
Aprendizaje permanente y reutilización de subrutinas modulares 333
La visión a largo plazo 335

9.4 Mantenerse actualizado en un campo en rápido movimiento 337


Practique problemas del mundo real usando Kaggle 337
Lea sobre los últimos desarrollos en arXiv 337
Explora el ecosistema de Keras 338

9.5 Palabras finales 339

Apéndice A Instalación de Keras y sus dependencias en Ubuntu 340


Apéndice B Ejecución de portátiles Jupyter en una instancia de GPU EC2 345

índice 353

Con licencia para <nulo>


Machine Translated by Google

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

Con licencia para <nulo>


Machine Translated by Google

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.

Con licencia para <nulo>


Machine Translated by Google

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.

Muchas gracias a los revisores técnicos dirigidos por Aleksandar Dragosavljevic´ .


Diego Acuña Rozas, Geoff Barto, David Blumenthal­Barby, Abel Brown, Clark Dor­man, Clark Gaylord, Thomas
Heiman, Wilson Mar, Sumit Pal, Vladimir Pasman, Gus­tavo Patino, Peter Rabinovitch, Alvin Raj, Claudio
Rodriguez, Srdjan Santic, Ricardo
Tobias, Martin Verzilli, William E. Wheeler y Daniel Williams, y los contribuyentes del foro. Sus contribuciones
incluyeron detectar errores técnicos, errores de terminología y errores tipográficos, y hacer sugerencias de
temas. Cada paso por la revisión.
proceso y cada pieza de retroalimentación implementada a través de los temas del foro conformados
y moldeó el manuscrito.
En el aspecto técnico, un agradecimiento especial a Jerry Gaines, quien trabajó como autor del libro.
editor técnico; y Alex Ott y Richard Tobias, quienes sirvieron como técnicos del libro.
correctores. Son los mejores editores técnicos que podría haber esperado.
Finalmente, me gustaría expresar mi agradecimiento a mi esposa María por ser extremadamente
apoyo durante todo el desarrollo de Keras y la escritura de este libro.

xvi

Con licencia para <nulo>


Machine Translated by Google

sobre este libro


Este libro fue escrito para cualquiera que desee explorar el aprendizaje profundo desde cero o
ampliar su comprensión del aprendizaje profundo. Ya sea que sea un ingeniero en aprendizaje automático, un desarrollador
de software o un estudiante universitario, encontrará valor en estas páginas.
Este libro ofrece una exploración práctica y práctica del aprendizaje profundo. Evita la notación matemática y prefiere
explicar conceptos cuantitativos mediante fragmentos de código y desarrollar una intuición práctica sobre las ideas
centrales del aprendizaje automático y
aprendizaje profundo.

Aprenderá de más de 30 ejemplos de código que incluyen comentarios detallados,


recomendaciones prácticas y explicaciones sencillas de alto nivel de todo lo que
Necesito saber para comenzar a utilizar el aprendizaje profundo para resolver problemas concretos.
Los ejemplos de código utilizan el marco de aprendizaje profundo de Python, Keras, con Tensor­Flow como motor
backend. Keras, uno de los marcos de aprendizaje profundo más populares y de más rápido crecimiento, se recomienda
ampliamente como la mejor herramienta para comenzar con el aprendizaje profundo.
aprendiendo.
Después de leer este libro, comprenderá sólidamente qué es el aprendizaje profundo.
cuándo es aplicable y cuáles son sus limitaciones. Estarás familiarizado con el estándar.
flujo de trabajo para abordar y resolver problemas de aprendizaje automático, y sabrá
cómo abordar los problemas que se encuentran comúnmente. Podrás utilizar Keras para abordar
Problemas del mundo real que van desde la visión por computadora hasta el procesamiento del lenguaje natural:
clasificación de imágenes, pronóstico de series temporales, análisis de sentimientos, generación de imágenes y texto, y
más.

xvi

Con licencia para <nulo>


Machine Translated by Google

SOBRE ESTE LIBRO xvii

Quién debería leer este libro

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:

Si es un científico de datos familiarizado con el aprendizaje automático, este libro le


proporcionará una introducción sólida y práctica al aprendizaje profundo, el subcampo más
importante y de más rápido crecimiento del aprendizaje
automático. Si es un experto en aprendizaje profundo y busca comenzar con el marco de
trabajo de Keras, encontrará que este libro es el mejor curso intensivo de Keras
disponible. Si es un estudiante de posgrado que estudia aprendizaje profundo en un entorno
formal, encontrará que este libro es un complemento práctico para su educación, ya que lo
ayudará a desarrollar su intuición en torno al comportamiento de las redes neuronales profundas
y lo familiarizará con las mejores prácticas clave.

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 1 presenta el contexto esencial y los conocimientos previos sobre la IA, el


aprendizaje automático y el aprendizaje
profundo. El Capítulo 2 presenta conceptos fundamentales necesarios para abordar el
aprendizaje profundo: tensores, operaciones tensoriales, descenso de gradiente y
retropropagación. Este capítulo también presenta el primer ejemplo del libro de una red
neuronal en funcionamiento.

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.

Con licencia para <nulo>


Machine Translated by Google

xviii SOBRE ESTE LIBRO

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.

El Capítulo 7 presenta técnicas avanzadas para construir modelos de aprendizaje profundo de


última generación.
El Capítulo 8 explica los modelos generativos: modelos de aprendizaje profundo capaces de crear
imágenes y textos, con resultados a veces sorprendentemente artísticos.
El capítulo 9 está dedicado a consolidar lo aprendido a lo largo del libro, así como a abrir
perspectivas sobre las limitaciones del aprendizaje profundo y explorar su futuro probable.

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
n1­standard­8 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/deep­learning­with­python. y en GitHub en https://
github.com/fchollet/deep­learning­with­python­notebooks.

Con licencia para <nulo>


Machine Translated by Google

SOBRE ESTE LIBRO xix

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/deep­learning­with­python. 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.

Con licencia para <nulo>


Machine Translated by Google

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

Con licencia para <nulo>


Machine Translated by Google

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 (1719­1771) 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.

La diversidad de los dibujos de los volúmenes de Jefferys habla vívidamente de la singularidad y


la individualidad de las naciones del mundo hace unos 200 años. Los códigos de vestimenta han
cambiado desde entonces, y la diversidad por regiones y países, tan rica en ese momento, se ha desvanecido.
Actualmente resulta difícil distinguir a los habitantes de un continente de los de otro. Quizás, al tratar
de verlo con optimismo, hemos cambiado una diversidad cultural y visual por una vida personal más
variada, o una vida intelectual y técnica más variada e interesante.
En una época en la que es difícil distinguir un libro de informática de otro, Manning celebra la
inventiva y la iniciativa del negocio de la informática con portadas de libros basadas en la rica diversidad
de la vida regional de hace dos siglos, devuelta a la vida por las fotografías de Jefferys. .

xxi

Con licencia para <nulo>


Machine Translated by Google

Con licencia para <nulo>


Machine Translated by Google

Parte 1

Fundamentos del
aprendizaje profundo

Los capítulos 1 a 4 de este libro le brindarán una comprensión fundamental de


qué es el aprendizaje profundo, qué puede lograr y cómo funciona. También lo familiarizará
con el flujo de trabajo canónico para resolver problemas de datos mediante el aprendizaje
profundo. Si aún no tiene un gran conocimiento sobre el aprendizaje profundo, definitivamente
debería comenzar leyendo la parte 1 en su totalidad antes de pasar a las aplicaciones
prácticas de la parte 2.

Con licencia para <nulo>


Machine Translated by Google

Con licencia para <nulo>


Machine Translated by Google

¿Qué es el aprendizaje profundo?

Este capítulo cubre


Definiciones de alto nivel de conceptos fundamentales
Cronología del desarrollo del aprendizaje automático
Factores clave detrás de la creciente popularidad y
el potencial futuro 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.

Con licencia para <nulo>


Machine Translated by Google

4 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

1.1 Inteligencia artificial, aprendizaje automático y aprendizaje


profundo Primero, debemos
definir claramente de qué hablamos cuando mencionamos IA. ¿Qué son la inteligencia artificial, el
aprendizaje automático y el aprendizaje profundo (ver figura 1.1)? ¿Cómo se relacionan entre sí?

Artificial

inteligencia

Aprendizaje

automático

Aprendizaje profundo

Figura 1.1 Inteligencia artificial,


aprendizaje automático y aprendizaje profundo

1.1.1 Inteligencia artificial


La inteligencia artificial nació en la década de 1950, cuando un puñado de pioneros del naciente
campo de la informática comenzaron a preguntarse si se podía hacer que las computadoras
"pensaran", una cuestión cuyas ramificaciones todavía estamos explorando hoy. Una definición
concisa del campo sería la siguiente: el esfuerzo por automatizar tareas intelectuales normalmente
realizadas por humanos. Como tal, la IA es un campo general que abarca el aprendizaje automático
y el aprendizaje profundo, pero que también incluye muchos más enfoques que no implican ningún
aprendizaje. Los primeros programas de ajedrez, por ejemplo, solo involucraban reglas codificadas
elaboradas por programadores y no calificaban como aprendizaje automático. Durante bastante
tiempo, muchos expertos creyeron que se podía lograr inteligencia artificial a nivel humano haciendo
que los programadores elaboraran un conjunto suficientemente grande de reglas explícitas para
manipular el conocimiento. Este enfoque se conoce como IA simbólica y fue el paradigma dominante
en la IA desde la década de 1950 hasta finales de la de 1980. Alcanzó su máxima popularidad
durante el auge de los sistemas expertos de la década de 1980.
Aunque la IA simbólica demostró ser adecuada para resolver problemas lógicos bien definidos,
como jugar al ajedrez, resultó difícil encontrar reglas explícitas para resolver problemas más
complejos y confusos, como la clasificación de imágenes, el reconocimiento de voz y el lenguaje.
traducción. Surgió un nuevo enfoque para ocupar el lugar de la IA simbólica: el aprendizaje automático.

1.1.2 Aprendizaje automático


En la Inglaterra victoriana, Lady Ada Lovelace era amiga y colaboradora de Charles Babbage, el
inventor de la máquina analítica: la primera computadora mecánica de propósito general conocida.
Aunque visionario y adelantado a su tiempo, el Analytical

Con licencia para <nulo>


Machine Translated by Google

Inteligencia artificial, aprendizaje automático y aprendizaje profundo 5

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

Un sistema de aprendizaje automático se entrena en lugar de programarse explícitamente. Se presenta


con muchos ejemplos relevantes para una tarea y encuentra una estructura estadística en estos ejemplos
que eventualmente permite al sistema generar reglas para automatizar la tarea.
Por ejemplo, si desea automatizar la tarea de etiquetar las fotografías de sus vacaciones, podría presentar
un sistema de aprendizaje automático con muchos ejemplos de fotografías ya etiquetadas por humanos,
y el sistema aprendería reglas estadísticas para asociar fotografías específicas a etiquetas específicas.

1
AM Turing, “Maquinaria informática e inteligencia”, Mind 59, no. 236 (1950): 433­460.

Con licencia para <nulo>


Machine Translated by Google

6 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

Aunque el aprendizaje automático empezó a florecer recién en la década de 1990, rápidamente


convertirse en el subcampo más popular y exitoso de la IA, una tendencia impulsada por
Disponibilidad de hardware más rápido y conjuntos de datos más grandes. El aprendizaje automático está estrechamente relacionado

a la estadística matemática, pero se diferencia de la estadística en varios aspectos importantes.


A diferencia de las estadísticas, el aprendizaje automático tiende a trabajar con conjuntos de datos grandes y complejos (como
un conjunto de datos de millones de imágenes, cada una de las cuales consta de decenas de miles de píxeles) para
cual el análisis estadístico clásico, como el análisis bayesiano, no sería práctico. Como un
Como resultado, el aprendizaje automático, y especialmente el aprendizaje profundo, muestra comparativamente poco
teoría matemática (tal vez muy poca) y está orientada a la ingeniería. es una practica
Disciplina en la que las ideas se prueban empíricamente con más frecuencia que teóricamente.

1.1.3 Aprender representaciones a partir de datos


Definir el aprendizaje profundo y comprender la diferencia entre el aprendizaje profundo.
y otros enfoques de aprendizaje automático, primero necesitamos una idea de lo que hacen los algoritmos de
aprendizaje automático. Acabo de decir que el aprendizaje automático descubre reglas para ejecutar.
una tarea de procesamiento de datos, dados ejemplos de lo que se espera. Entonces, para hacer aprendizaje
automático, necesitamos tres cosas:

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 .
(rojo­verde­azul) o en formato HSV (tono­saturación­valor): 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.

Con licencia para <nulo>


Machine Translated by Google

Inteligencia artificial, aprendizaje automático y aprendizaje profundo 7

Hagamos esto concreto. Considere un eje x, un eje y y algunos puntos y

representados por sus coordenadas en el sistema (x, y), como se muestra


en la figura 1.3.
Como puede ver, tenemos algunos puntos blancos y algunos puntos
negros. Digamos que queremos desarrollar un algoritmo que pueda tomar
las coordenadas (x, y) de un punto y determinar si es probable que ese
punto sea negro o blanco. En este caso, Las entradas son
X
las coordenadas de nuestros puntos. Los resultados
esperados son los colores de nuestros puntos. Una forma Figura 1.3
de medir si nuestro algoritmo está haciendo un buen trabajo podría Algunos datos de muestra

ser, por ejemplo, el porcentaje de puntos que se están clasificando


correctamente.

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.

1: datos sin procesar 2: cambio de coordenadas 3: Mejor representación


y
y y

X X

Figura 1.4 Cambio de coordenadas

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

Con licencia para <nulo>


Machine Translated by Google

8 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

encontrar estas transformaciones; simplemente están buscando a través de un conjunto predefinido de


operaciones, llamado espacio de hipótesis.
Entonces, eso es técnicamente el aprendizaje automático: buscar representaciones útiles de algunos datos de
entrada, dentro de un espacio predefinido de posibilidades, utilizando guías.
a partir de una señal de retroalimentación. Esta sencilla idea permite resolver una gama notablemente amplia
de tareas intelectuales, desde el reconocimiento de voz hasta la conducción autónoma de automóviles.
Ahora que comprende lo que queremos decir con aprendizaje, echemos un vistazo a lo que hace
Especial aprendizaje profundo .

1.1.4 Lo “profundo” en el aprendizaje profundo

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.

Con licencia para <nulo>


Machine Translated by Google

Inteligencia artificial, aprendizaje automático y aprendizaje profundo 9

¿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.

Capa 1 Capa 2 Capa 3 Capa 4

Entrada 0

original
1

3 Salida
4 final
5
Figura 1.5 Una red neuronal
6

7 profunda para clasificación de dígitos


89

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 1 Representaciones de capa 2 Representaciones de capa 3

Representaciones
de capa 4 (salida final)
0
Entrada
original
1

89

Capa 1 Capa 2 Capa 3 Capa 4

Figura 1.6 Representaciones profundas aprendidas mediante un modelo de clasificación de dígitos

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.

1.1.5 Comprender cómo funciona el aprendizaje profundo, en tres cifras


En este punto, ya sabes que el aprendizaje automático consiste en asignar entradas (como imágenes)
a objetivos (como la etiqueta "gato"), lo que se realiza observando muchos ejemplos de entradas y
objetivos. También sabes que las redes neuronales profundas hacen esta entrada al objetivo.

Con licencia para <nulo>


Machine Translated by Google

10 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

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)

Predicciones Figura 1.7 Una red neuronal está


Y'
parametrizada por sus pesos.

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)

Predicciones Objetivos verdaderos


Y' Y

Función de pérdida

Figura 1.8 Una función de pérdida mide


Puntuación de pérdida
la calidad de la salida de la red.

Con licencia para <nulo>


Machine Translated by Google

Inteligencia artificial, aprendizaje automático y aprendizaje profundo 11

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)

Actualización Predicciones Objetivos verdaderos

de peso Y' Y

Optimizador Función de pérdida

Figura 1.9 La puntuación de pérdida se utiliza como


Puntuación de pérdida
señal de retroalimentación para ajustar los pesos.

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.

1.1.6 Qué ha logrado el aprendizaje profundo hasta ahora


Aunque el aprendizaje profundo es un subcampo bastante antiguo del aprendizaje automático, sólo saltó a la
fama a principios de la década de 2010. En los pocos años transcurridos desde entonces, ha logrado nada menos
que una revolución en este campo, con resultados notables en problemas de percepción como la vista y el oído,
problemas que involucran habilidades que parecen naturales e intuitivas para los humanos pero que durante
mucho tiempo han sido difíciles de alcanzar para las máquinas.
En particular, el aprendizaje profundo ha logrado los siguientes avances, todos en áreas históricamente
difíciles del aprendizaje automático: Clasificación de

imágenes a nivel casi humano Reconocimiento de


voz a nivel casi humano Transcripción de escritura
a mano a nivel casi humano Traducción automática
mejorada

Con licencia para <nulo>


Machine Translated by Google

12 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

Conversión de texto a voz mejorada


Asistentes digitales como Google Now y Amazon Alexa
Conducción autónoma casi a nivel humano
Orientación de anuncios mejorada, como la que utilizan Google, Baidu
y Bing Resultados de búsqueda
mejorados en la web Capacidad de responder
preguntas en lenguaje natural Superhumano Ir a jugar

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.

1.1.7 No creas en las exageraciones a corto plazo

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 .

Con licencia para <nulo>


Machine Translated by Google

Inteligencia artificial, aprendizaje automático y aprendizaje profundo 13

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 1998­1999 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.

Con licencia para <nulo>


Machine Translated by Google

14 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

1.2 Antes del aprendizaje profundo:


una breve historia del aprendizaje automático
El aprendizaje profundo ha alcanzado un nivel de atención pública y la inversión de la industria nunca
visto antes en la historia de la IA, pero no es la primera forma exitosa de aprendizaje automático. Es seguro decir que la
mayoría de los algoritmos de aprendizaje automático utilizados en la industria
hoy no son algoritmos de aprendizaje profundo. El aprendizaje profundo no siempre es la herramienta adecuada para el
trabajo: a veces no hay suficientes datos para que el aprendizaje profundo sea aplicable y, a veces, el problema se
resuelve mejor con un algoritmo diferente. Si el aprendizaje profundo es tu
primer contacto con el aprendizaje automático, entonces puede encontrarse en una situación en la que todos
lo que tienes es el martillo de aprendizaje profundo, y cada problema de aprendizaje automático comienza a resolverse.
parece un clavo. La única manera de no caer en esta trampa es conocer otros
enfoques y practicarlos cuando sea apropiado.
Una discusión detallada de los enfoques clásicos de aprendizaje automático está fuera del
alcance de este libro, pero los repasaremos brevemente y describiremos el contexto histórico
en el que se desarrollaron. Esto nos permitirá situar el aprendizaje profundo en un ámbito más amplio.
contexto del aprendizaje automático y comprender mejor de dónde proviene el aprendizaje profundo
y por qué es importante.

1.2.1 Modelado probabilístico


El modelado probabilístico es la aplicación de los principios de la estadística al análisis de datos. Él
fue una de las primeras formas de aprendizaje automático y todavía se utiliza ampliamente hasta el día de hoy.
Uno de los algoritmos más conocidos en esta categoría es el algoritmo Naive Bayes.
Naive Bayes es un tipo de clasificador de aprendizaje automático que se basa en la aplicación del teorema de Bayes
y supone que las características de los datos de entrada son todas independientes (un fuerte,
o suposición “ingenua”, de donde viene el nombre). Esta forma de análisis de datos es anterior a las computadoras y se
aplicó manualmente décadas antes de que se construyera la primera computadora.
implementación (que probablemente se remonta a la década de 1950). El teorema de Bayes y los fundamentos de la
estadística se remontan al siglo XVIII, y esto es todo lo que necesitas para entender.
comience a utilizar clasificadores Naive Bayes.
Un modelo estrechamente relacionado es la regresión logística (logreg para abreviar), que a veces se considera el
"hola mundo" del aprendizaje automático moderno. No se deje engañar por su nombre: logreg es un algoritmo de
clasificación en lugar de una regresión.
algoritmo. Al igual que Naive Bayes, logreg es mucho anterior a la informática, pero es
Sigue siendo útil hasta el día de hoy, gracias a su naturaleza simple y versátil. Muchas veces es lo primero
un científico de datos probará un conjunto de datos para tener una idea de la tarea de clasificación en cuestión.

1.2.2 Redes neuronales tempranas


Las primeras iteraciones de redes neuronales han sido completamente suplantadas por las modernas.
Hay variantes que se tratan en estas páginas, pero es útil tener en cuenta cómo se originó el aprendizaje profundo.
Aunque las ideas centrales de las redes neuronales ya se investigaron en forma de juguetes
Como en la década de 1950, el enfoque tardó décadas en ponerse en marcha. Durante mucho tiempo, la pieza que faltaba
Era una forma eficiente de entrenar grandes redes neuronales. Esto cambió a mediados de los años 1980,

Con licencia para <nulo>


Machine Translated by Google

Antes del aprendizaje profundo: una breve historia del aprendizaje automático 15

cuando varias personas redescubrieron de forma independiente el algoritmo de retropropagación (una


forma de entrenar cadenas de operaciones paramétricas utilizando la optimización de descenso de
gradiente (más adelante en el libro definiremos con precisión estos conceptos)) y comenzaron a aplicarlo
a las redes neuronales.
La primera aplicación práctica exitosa de las redes neuronales se produjo en 1989 en los Laboratorios
Bell, cuando Yann LeCun combinó las ideas anteriores de redes neuronales convolucionales y
retropropagación, y las aplicó al problema de clasificar dígitos escritos a mano. La red resultante,
denominada LeNet, fue utilizada por el Servicio Postal de los Estados Unidos en la década de 1990 para
automatizar la lectura de códigos postales en los sobres de correo.

1.2.3 Métodos del núcleo


A medida que las redes neuronales comenzaron a ganar cierto respeto entre los investigadores en la
década de 1990, gracias a este primer éxito, un nuevo enfoque de aprendizaje automático saltó a la fama
y rápidamente devolvió las redes neuronales al olvido: los métodos del núcleo. Los métodos kernel son un
grupo de algoritmos de clasificación, el más conocido de los cuales es la máquina de vectores de soporte (SVM).
La formulación moderna de una SVM fue desarrollada por Vladimir Vapnik y
Corinna Cortes a principios de la década de 1990 en Bell Labs y publicada en
1995,2 aunque Vapnik y Alexey Chervonenkis publicaron una formulación
lineal más antigua ya en 1963.3 Las SVM tienen como objetivo resolver
problemas de clasificación mediante encontrar buenos límites de decisión
(ver figura 1.10) entre dos conjuntos de puntos que pertenecen a dos categorías
diferentes. Se puede considerar un límite de decisión como una línea o
superficie que separa los datos de entrenamiento en dos espacios
correspondientes a dos categorías. Para clasificar nuevos puntos de datos,
sólo necesita comprobar en qué lado del límite de decisión se encuentran. Figura 1.10
Un límite de decisión

Las SVM proceden a encontrar estos límites en dos pasos: 1.

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, “Support­Vector 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).

Con licencia para <nulo>


Machine Translated by Google

dieciséis
CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

hiperplanos en el nuevo espacio de representación, no es necesario calcular explícitamente


las coordenadas de tus puntos en el nuevo espacio; sólo necesita calcular la distancia entre pares de puntos en ese
espacio, lo cual se puede hacer de manera eficiente usando una función central. Una función del núcleo es una
operación computacional manejable que mapea cualquier
dos puntos en su espacio inicial a la distancia entre estos puntos en su objetivo
espacio de representación, evitando por completo el cálculo explícito de la nueva representación. Las funciones del
kernel generalmente se elaboran a mano en lugar de aprenderse de ellas.
datos: en el caso de un SVM, solo se aprende el hiperplano de separación.
En el momento en que se desarrollaron, las SVM exhibían un rendimiento de última generación en
problemas de clasificación simples y fueron uno de los pocos métodos de aprendizaje automático
respaldado por una teoría extensa y susceptible de un análisis matemático serio, haciendo
sean bien comprendidos y fácilmente interpretables. Debido a estas propiedades útiles,
Las SVM se volvieron extremadamente populares en este campo durante mucho tiempo.

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.

1.2.4 Árboles de decisión, bosques aleatorios y máquinas de aumento de gradiente

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

Pregunta Pregunta Figura 1.11 Un árbol de decisión: los parámetros


que se aprenden son las preguntas sobre los
datos. Una pregunta podría ser, por ejemplo:
Categoría Categoría Categoría Categoría "¿El coeficiente 2 de los datos es mayor que 3,5?"

En particular, el algoritmo Random Forest introdujo una visión sólida y práctica de


Aprendizaje de árbol de decisiones que implica la construcción de una gran cantidad de decisiones especializadas.
árboles y luego ensamblar sus resultados. Los bosques aleatorios son aplicables a una amplia
gama de problemas: se podría decir que casi siempre son el segundo mejor algoritmo
para cualquier tarea superficial de aprendizaje automático. Cuando el popular sitio web de competición de aprendizaje
automático Kaggle (http://kaggle.com) comenzó en 2010, bosques aleatorios rápidamente
se convirtió en un favorito en la plataforma, hasta 2014, cuando las máquinas de impulso de gradiente tomaron
encima. Una máquina de aumento de gradiente, muy parecida a un bosque aleatorio, es una máquina de aprendizaje automático.

Técnica basada en el ensamblaje de modelos de predicción débiles, generalmente árboles de decisión. Él

Con licencia para <nulo>


Machine Translated by Google

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.

1.2.5 Volver a las redes neuronales

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.

1.2.6 ¿Qué hace que el aprendizaje profundo sea diferente?

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

Con licencia para <nulo>


Machine Translated by Google

18 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

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.

1.2.7 El panorama moderno del aprendizaje automático


Una excelente manera de tener una idea del panorama actual de los algoritmos de aprendizaje automático
y herramientas es mirar competencias de aprendizaje automático en Kaggle. Debido a su alta
entorno competitivo (algunos concursos tienen miles de participantes y premios de millones de dólares) y a la amplia
variedad de problemas de aprendizaje automático cubiertos, Kaggle
Ofrece una forma realista de evaluar qué funciona y qué no. Entonces, ¿qué tipo de algoritmo?
¿Se ganan competiciones de forma fiable? ¿Qué herramientas utilizan los mejores participantes?

Con licencia para <nulo>


Machine Translated by Google

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.

Con licencia para <nulo>


Machine Translated by Google

20 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

1.3 ¿Por qué el aprendizaje profundo? ¿Porqué ahora?


Las dos ideas clave del aprendizaje profundo para la visión por computadora (redes neuronales
convolucionales y retropropagación) ya se entendían bien en 1989. El algoritmo de memoria a corto plazo
(LSTM) , que es fundamental para el aprendizaje profundo de series temporales, se desarrolló en 1997. y
apenas ha cambiado desde entonces. Entonces, ¿por qué el aprendizaje profundo despegó recién después
de 2012? ¿Qué cambió en estas dos décadas?
En general, tres fuerzas técnicas están impulsando los avances en el aprendizaje automático:
Hardware
Conjuntos de datos y puntos de referencia

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/about­cuda), 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.

Con licencia para <nulo>


Machine Translated by Google

¿Por qué el aprendizaje profundo? ¿Porqué ahora? 21

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.image­net.org/challenges/LSVRC.

Con licencia para <nulo>


Machine Translated by Google

22 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

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 2009­2010 con la llegada de varios sencillos pero importantes
mejoras algorítmicas que permitieron una mejor propagación del gradiente:

Mejores funciones de activación para capas neuronales


Mejores esquemas de inicialización de peso, comenzando con el preentrenamiento por capas, que fue
rápidamente abandonado
Mejores esquemas de optimización, como RMSProp y Adam

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.

1.3.4 Una nueva ola de inversión


A medida que el aprendizaje profundo se convirtió en el nuevo estado del arte para la visión por computadora en 2012­2013 y,

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

industrial mucho más allá de todo lo visto anteriormente en la historia de la IA.

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.

Con licencia para <nulo>


Machine Translated by Google

¿Por qué el aprendizaje profundo? ¿Porqué ahora? 23

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.

1.3.5 La democratización del aprendizaje profundo

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:

Simplicidad: el aprendizaje profundo elimina la necesidad de ingeniería de características, reemplazando


tuberías complejas, frágiles y con mucha ingeniería por modelos simples y entrenables de extremo a
extremo que generalmente se construyen utilizando solo cinco o seis operaciones tensoriales
diferentes. Escalabilidad: el aprendizaje profundo es muy susceptible de paralelización en GPU o TPU,
por lo que puede aprovechar al máximo la ley de Moore. Además, los modelos de aprendizaje profundo
se entrenan iterando sobre pequeños lotes de datos, lo que les permite entrenarse en conjuntos de
datos de tamaño arbitrario. (El único cuello de botella es la cantidad de potencia computacional paralela
disponible, que, gracias a la ley de Moore, es una barrera que se mueve rápidamente). Versatilidad
y reutilización: a
diferencia de muchos enfoques anteriores de aprendizaje automático, los modelos de aprendizaje profundo
se pueden entrenar con funciones adicionales. datos sin reiniciar desde cero, lo que los hace viables
para el aprendizaje continuo en línea, una propiedad importante para modelos de producción muy
grandes. Además, los modelos de aprendizaje profundo entrenados son reutilizables y, por lo tanto,
reutilizables: por ejemplo, es posible tomar un modelo de aprendizaje profundo entrenado para la
clasificación de imágenes y colocarlo en un proceso de procesamiento de video. Esto nos permite
reinvertir el trabajo anterior en cada vez más

Con licencia para <nulo>


Machine Translated by Google

24 CAPÍTULO 1 ¿Qué es el aprendizaje profundo?

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.

Con licencia para <nulo>


Machine Translated by Google

Antes de comenzar:
los componentes
matemáticos de las redes neurona

Este capítulo cubre


Un primer ejemplo de una red neuronal
Tensores y operaciones tensoriales
Cómo aprenden las redes neuronales mediante
retropropagación y descenso de gradiente

Comprender el aprendizaje profundo requiere estar familiarizado con muchos conceptos


matemáticos simples: tensores, operaciones tensoriales, diferenciación, descenso de gradientes, etc.
Nuestro objetivo en este capítulo será desarrollar su intuición sobre estas nociones sin volverse
demasiado técnico. En particular, nos alejaremos de la notación matemática, que puede resultar
desagradable para quienes no tienen conocimientos matemáticos y no es estrictamente necesaria
para explicar bien las cosas.
Para agregar algo de contexto para los tensores y el descenso de gradientes, comenzaremos
el capítulo con un ejemplo práctico de una red neuronal. Luego repasaremos cada nuevo concepto.

25

Con licencia para <nulo>


Machine Translated by Google

26 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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.

Con licencia para <nulo>


Machine Translated by Google

Un primer vistazo a una red neuronal 27

2.1 Una primera mirada a una red neuronal


Veamos un ejemplo concreto de una red neuronal que utiliza la biblioteca Keras de Python para aprender a clasificar
dígitos escritos a mano. A menos que ya tenga experiencia con Keras o bibliotecas similares, no comprenderá todo
acerca de este primer ejemplo de inmediato. Probablemente ni siquiera hayas instalado Keras todavía; está bien. En el
próximo capítulo, revisaremos cada elemento del ejemplo y los explicaremos en detalle. ¡Así que no te preocupes si
algunos pasos te parecen arbitrarios o mágicos! Tenemos que empezar por alguna parte.

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.

Nota sobre clases y etiquetas En


el aprendizaje automático, una categoría en un problema de clasificación se denomina clase.
Los puntos de datos se llaman muestras. La clase asociada con una muestra específica se
llama etiqueta.

Figura 2.1 Dígitos de muestra de MNIST

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.

Listado 2.1 Cargando el conjunto de datos MNIST en Keras

desde keras.datasets importar mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

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.

Con licencia para <nulo>


Machine Translated by Google

28 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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_images.shape (60000,


28, 28) >>>
len(train_labels) 60000 >>>

train_labels array([5, 0,
4, ..., 5, 6, 8], dtype=uint8)

Y aquí están los datos de la prueba:

>>> test_images.shape (10000,


28, 28) >>>
len(test_labels) 10000

>>> matriz test_labels([7,


2, 1, ..., 4, 5, 6], 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.

Listado 2.2 La arquitectura de la red

desde keras importar modelos desde


keras importar capas

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.

Con licencia para <nulo>


Machine Translated by Google

Un primer vistazo a una red neuronal 29

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.

Un optimizador: el mecanismo a través del cual la red se actualizará.


en función de los datos que ve y su función de pérdida.

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.

Listado 2.3 El paso de compilación

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.

Listado 2.4 Preparando los datos de la imagen

imágenes_tren = imágenes_tren.reshape((60000, 28 * 28)) imágenes_tren =


imágenes_tren.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28)) test_images = test_images.astype('float32') /


255

También necesitamos codificar categóricamente las etiquetas, un paso que se explica en el capítulo 3.

Listado 2.5 Preparando las etiquetas

desde keras.utils importar a_categorical

etiquetas_tren = to_categorical(etiquetas_tren) etiquetas_prueba =


to_categorical(etiquetas_prueba)

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:

>>> red.fit(train_images, train_labels, épocas=5, lote_size=128)


Época 1/5
60000/60000 [==============================] ­ 9s ­ pérdida: 0,2524 ­ acc: 0,9273 Época 2/5 51328/60000

[========================>.....] ­ ETA: 1s ­ pérdida: 0,1035 ­ acc: 0,9692

Con licencia para <nulo>


Machine Translated by Google

30 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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:

>>> test_loss, test_acc = network.evaluate(test_images, test_labels) >>> print('test_acc:', test_acc)


test_acc: 0.9785

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.

Con licencia para <nulo>


Machine Translated by Google

Representaciones de datos para redes neuronales. 31

2.2 Representaciones de datos para redes neuronales En el ejemplo anterior, partimos

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

que TensorFlow de Google lleva su nombre. Entonces, ¿qué es un tensor?


En esencia, un tensor es un contenedor de datos, casi siempre datos numéricos. Entonces, es un contenedor de
números. Es posible que ya esté familiarizado con las matrices, que son diez­sores 2D: los tensores son una
generalización de matrices a un número arbitrario de dimensiones (tenga en cuenta que en el contexto de los tensores,
una dimensión a menudo se denomina eje).

2.2.1 Escalares (tensores 0D)


Un tensor que contiene solo un número se llama escalar (o tensor escalar, o tensor de dimensión 0, o tensor de 0D). En
Numpy, un número float32 o float64 es un tensor escalar (o matriz escalar). Puede mostrar el número de ejes de un tensor
Numpy mediante el atributo ndim ; un tensor escalar tiene 0 ejes (ndim == 0). El número de ejes de un tensor también se
llama rango.
Aquí hay un escalar de Numpy:

>>> importar numpy como np >>> x


= np.array(12)
>>> x
matriz(12) >>>
x.ndim
0

2.2.2 Vectores (tensores 1D)


Una matriz de números se llama vector o tensor 1D . Se dice que un tensor 1D tiene exactamente un eje. A continuación
se muestra un vector Numpy:

>>> x = np.matriz([12, 3, 6, 14])


>>> x
matriz([12, 3, 6, 14]) >>> x.ndim

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.

2.2.3 Matrices (tensores 2D)


Una matriz de vectores es una matriz o tensor 2D . Una matriz tiene dos ejes (a menudo denominados filas y columnas).
Puede interpretar visualmente una matriz como una cuadrícula rectangular de números.
Esta es una matriz Numpy:

Con licencia para <nulo>


Machine Translated by Google

32 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

>>> 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.

2.2.4 Tensores 3D y tensores de dimensiones superiores


Si empaqueta dichas matrices en una nueva matriz, obtendrá un tensor 3D , que puede interpretar
visualmente como un cubo de números. El siguiente es un tensor 3D de Numpy:

>>> x = np.array([[[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36,
2]],

[[5, 78, 2, 34, 0], [6, 79, 3, 35,


1], [7, 80, 4, 36, 2]],

[[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.

2.2.5 Atributos clave


Un tensor se define por tres atributos clave:

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.

Con licencia para <nulo>


Machine Translated by Google

Representaciones de datos para redes neuronales. 33

Para hacer esto más concreto, volvamos a mirar los datos que procesamos en el ejemplo MNIST . Primero,
cargamos el conjunto de datos MNIST :

desde keras.datasets importar mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

A continuación, mostramos el número de ejes del tensor train_images, el atributo ndim :

>>> imprimir(tren_imagenes.ndim) 3

Aquí está su forma:

>>> imprimir(tren_imagenes.forma) (60000,


28, 28)

Y este es su tipo de datos, el atributo dtype :

>>> imprimir(train_images.dtype) uint8

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.

Listado 2.6 Mostrando el cuarto dígito

dígito = imágenes_tren[4]

importar matplotlib.pyplot como plt


plt.imshow(dígito, cmap=plt.cm.binary) plt.show()

Figura 2.2 La cuarta muestra de nuestro conjunto de datos

Con licencia para <nulo>


Machine Translated by Google

34 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

2.2.6 Manipulación de tensores en Numpy


En el ejemplo anterior, seleccionamos un dígito específico junto al primer eje usando la sintaxis
train_images[i]. La selección de elementos específicos en un tensor se llama corte tensor.
Veamos las operaciones de corte de tensores que puede realizar en matrices Numpy.
El siguiente ejemplo selecciona los dígitos del 10 al 100 (el 100 no está incluido) y coloca
ellos en una variedad de formas (90, 28, 28):

>>> mi_rebanada = tren_imagenes[10:100] >>>


imprimir(mi_rebanada.forma) (90, 28,
28)

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:

my_slice = tren_imagenes[:, 14:, 14:]

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:

my_slice = train_images[:, 7:­7, 7:­7]

2.2.7 La noción de lotes de datos


En general, el primer eje (eje 0, porque la indexación comienza en 0) en todos los tensores de datos que
encontrará en el aprendizaje profundo será el eje de muestras (a veces llamado dimensión de muestras).
En el ejemplo de MNIST , las muestras son imágenes de dígitos.
Además, los modelos de aprendizaje profundo no procesan un conjunto de datos completo a la vez;
más bien, dividen los datos en pequeños lotes. Concretamente, aquí hay un lote de nuestros dígitos
MNIST , con un tamaño de lote de 128:

lote = tren_imagenes[:128]

Y aquí está el siguiente lote:

lote = tren_imagenes[128:256]

Y el enésimo lote:

lote = tren_imagenes[128 * n:128 * (n + 1)]

Con licencia para <nulo>


Machine Translated by Google

Representaciones de datos para redes neuronales. 35

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.

2.2.8 Ejemplos del mundo real de tensores de datos

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

de forma 2D (muestras, características) Datos de series temporales


o datos de secuencia: tensores de forma 3D (muestras, pasos de tiempo,
características)

Imágenes: tensores de forma 4D (muestras, alto, ancho, canales) o (muestras,


canales, alto, ancho)
Vídeo: tensores de forma 5D (muestras, fotogramas, alto, ancho, canales) o
(muestras, marcos, canales, alto, ancho)

2.2.9 Datos vectoriales

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).

2.2.10 Datos de series temporales o datos de secuencia

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

Pasos de tiempo Figura 2.3 Un tensor de datos de series temporales 3D

Con licencia para <nulo>


Machine Translated by Google

36 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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).

2.2.11 Datos de imagen

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

Figura 2.4 Un tensor de datos de


Ancho imagen 4D (convención de canales primero)

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 Tensor­Flow, 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

Con licencia para <nulo>


Machine Translated by Google

Representaciones de datos para redes neuronales. 37

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.

2.2.12 Datos de vídeo


Los datos de vídeo son uno de los pocos tipos de datos del mundo real para los que necesitará tensores 5D .
Un vídeo puede entenderse como una secuencia de fotogramas, siendo cada fotograma una imagen en color.
Debido a que cada cuadro se puede almacenar en un tensor 3D (alto, ancho, profundidad_color), se puede
almacenar una secuencia de cuadros en un tensor 4D (cuadros, alto, ancho, profundidad_color) y, por lo
tanto, se puede almacenar un lote de videos diferentes. en un tensor de forma 5D (muestras, marcos, alto,
ancho, profundidad de color).
Por ejemplo, un videoclip de YouTube de 60 segundos y 144 × 256 muestreado a 4 fotogramas por
segundo tendría 240 fotogramas. Un lote de cuatro de estos videoclips se almacenaría en una forma tensor
(4, 240, 144, 256, 3). ¡Eso es un total de 106.168.320 valores! Si el tipo d del tensor fuera float32, entonces
cada valor se almacenaría en 32 bits, por lo que el tensor representaría 405 MB. ¡Pesado! Los vídeos que
encuentras en la vida real son mucho más ligeros porque no están almacenados en float32 y, por lo general,
están comprimidos en un factor grande (como en el formato MPEG ).

Con licencia para <nulo>


Machine Translated by Google

38 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

2.3 Los engranajes de las redes neuronales: operaciones tensoriales De la misma


manera que cualquier programa de computadora puede reducirse en última instancia a un pequeño
conjunto de operaciones binarias sobre entradas binarias (Y, O, NOR, etc.), todas las transformaciones
aprendidas por las redes neuronales profundas se pueden reducir a un puñado de operaciones
tensoriales aplicadas a tensores de datos numéricos. Por ejemplo, es posible sumar tensores,
multiplicar tensores, etc.
En nuestro ejemplo inicial, estábamos construyendo nuestra red apilando capas densas en
uno encima del otro. Una instancia de capa Keras se ve así:

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):

salida = relu(punto(W, entrada) + b)

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.

2.3.1 Operaciones por elementos


La operación relu y la suma son operaciones de elementos : operaciones que se aplican de forma
independiente a cada entrada en los tensores que se consideran. Esto significa que estas operaciones
son altamente susceptibles a implementaciones masivamente paralelas ( implementaciones
vectorizadas, un término que proviene de la arquitectura de supercomputadoras con procesadores
vectoriales del período 1970­1990). Si desea escribir una implementación ingenua en Python de una
operación por elementos, utilice un bucle for , como en esta implementación ingenua de una operación
relu por elementos:

def naive_relu(x): afirmar


len(x.shape) == 2 x es un tensor Numpy 2D.

x = x.copy() para i Evite sobrescribir el tensor de entrada.


en el rango(x.shape[0]):
para j en rango (x.shape[1]):
x[i, j] = máx(x[i, j], 0) devuelve x

Con licencia para <nulo>


Machine Translated by Google

Los engranajes de las redes neuronales: operaciones tensoriales 39

Haz lo mismo para la suma:

def naive_add(x, y): afirmar xey son tensores


len(x.shape) == 2 afirmar x.shape == Numpy 2D.
y.shape

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:

importar numpy como np

z=x+y Suma de elementos

z = np.máximo(z, 0.) Relu por elementos

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?

Cuando sea posible, y si no hay ambigüedad, el tensor más pequeño se transmitirá a


coincide con la forma del tensor más grande. La radiodifusión consta de dos pasos:

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

Con licencia para <nulo>


Machine Translated by Google

40 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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:

def naive_add_matrix_and_vector(x, y): afirmar len(x.shape) == x es un tensor Numpy 2D.


2 afirmar len(y.shape) == 1 afirmar
x.shape[1] == y.shape[0] y es un vector Numpy.

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

Con la radiodifusión, generalmente se pueden aplicar operaciones de elementos de dos tensores


si un tensor tiene forma (a, b,… n, n + 1,… m) y el otro tiene forma (n, n + 1,… m). La transmisión
se realizará automáticamente para los ejes del a al n ­ 1.
El siguiente ejemplo aplica la operación máxima por elementos a dos tensores
de diferentes formas vía radiodifusión:

importar numpy como np x es un tensor aleatorio con


x = np.aleatorio.aleatorio((64, 3, 32, 10)) y = forma (64, 3, 32, 10).
np.aleatorio.aleatorio((32, 10))
y es un tensor aleatorio
z = np.máximo(x, y) con forma (32, 10).

La salida z tiene la forma


(64, 3, 32, 10) como x.
2.3.3 Punto tensorial
La operación de punto, también llamada producto tensorial (que no debe confundirse con un
producto de elementos) es la operación tensorial más común y útil. A diferencia de las operaciones
por elementos, combina entradas en los tensores de entrada.
Un producto por elementos se realiza con el operador * en Numpy, Keras, Theano y
TensorFlow. dot usa una sintaxis diferente en TensorFlow, pero tanto en Numpy como en Keras
se realiza usando el operador de punto estándar:

importar numpy como np

z = np.punto(x, y)

En notación matemática, la operación se anotaría con un punto (.):

z=xy

Matemáticamente, ¿qué hace la operación de puntos? Comencemos con el producto escalar de


dos vectores xey . Se calcula de la siguiente manera:

def naive_vector_dot(x, y):


afirmar len(x.shape) == 1 afirmar
xey son vectores Numpy.
len(y.shape) == 1 afirmar x.shape[0] ==
y.shape[0]

Con licencia para <nulo>


Machine Translated by Google

Los engranajes de las redes neuronales: operaciones tensoriales 41

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 matriz­vector y un producto vectorial:

def naive_matrix_vector_dot(x, y):


z = np.zeros(x.shape[0]) para i en el
rango(x.shape[0]):
z[i] = ingenuo_vector_punto(x[i, :], y)
regresar z

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

Con licencia para <nulo>


Machine Translated by Google

42 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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

z [yo,j] Figura 2.5 Diagrama de caja de producto


Fila de x
escalar de matriz

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)

(a B C D) . (d,e) ­> (a,b,c,e)

Etcétera.

2.3.4 Remodelación del tensor

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)

Con licencia para <nulo>


Machine Translated by Google

Los engranajes de las redes neuronales: operaciones tensoriales 43

>>> x = x.reformar((6, 1))


>>> x
matriz ([[ 0.], [ 1.],
[ 2.], [ 3.],
[ 4.],
[ 5.]])

>>> x = x.reformar((2, 3))


>>> x
matriz([[ 0., 1., 2.], [ 3., 4., 5.]])

Un caso especial de remodelación que se encuentra comúnmente es la transposición. Transponer


una matriz significa intercambiar sus filas y sus columnas, de modo que x[i, :] se convierta en x[:, i]:

>>> x = np.zeros((300, 20)) >>> x =


Crea una matriz de todos ceros.
np.transpose(x) >>> imprimir(x.forma)
de forma (300, 20)
(20, 300)

2.3.5 Interpretación geométrica de operaciones tensoriales


Debido a que el contenido de los tensores manipulados mediante operaciones tensoriales se puede
interpretar como coordenadas de puntos en algún espacio geométrico, todas las operaciones
tensoriales tienen una interpretación geométrica. Por ejemplo, consideremos la suma. Comenzaremos
con el siguiente vector:

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

Figura 2.6 Un punto en un espacio 2D Figura 2.7 Un punto en un espacio 2D


representado como una flecha

Con licencia para <nulo>


Machine Translated by Google

44 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

Consideremos un nuevo punto, B = [1, 0,25], que sumaremos al anterior. Esto es


hecho geométricamente encadenando las flechas vectoriales, con la ubicación resultante
siendo el vector el que representa la suma de los dos vectores anteriores (ver figura 2.8).

A+B

1 A

B
1 Figura 2.8 Interpretación geométrica de la
suma de dos vectores

En general, las operaciones geométricas elementales como transformaciones afines, rotaciones,


el escalado, etc., se pueden expresar como operaciones tensoriales. Por ejemplo, una rotación de un
El vector 2D por un ángulo theta se puede lograr mediante un producto escalar con una matriz de 2 × 2
R = [u, v], donde u y v son ambos vectores del plano: u = [cos(theta),
pecado(theta)] y v = [­sin(theta), cos(theta)].

2.3.6 Una interpretación geométrica del aprendizaje profundo


Acabas de aprender que las redes neuronales consisten enteramente en cadenas de operaciones tensoriales y
que todas estas operaciones tensoriales son solo transformaciones geométricas de los datos de entrada.
De ello se deduce que se puede interpretar una red neuronal como una transformación geométrica muy
compleja en un espacio de alta dimensión, implementada mediante una larga serie de pasos simples.
En 3D, la siguiente imagen mental puede resultar útil. Imagina dos hojas de colores.
papel: uno rojo y otro azul. Pon uno encima del otro. Ahora arrúgalos
juntos formando una pequeña bola. Esa bola de papel arrugada son tus datos de entrada, y cada hoja
de papel es una clase de datos en un problema de clasificación. ¿Qué red neuronal (o cualquier
otro modelo de aprendizaje automático) se supone que debe hacer es descubrir una transformación del
bola de papel que la desarrugaría, para que las dos clases fueran claramente separables
de nuevo. Con el aprendizaje profundo, esto se implementaría como una serie de transformaciones simples
del espacio 3D , como las que se podrían aplicar a la bola de papel con los dedos, un movimiento a la vez.

Figura 2.9 Desenrollando


una complicada variedad de datos

Con licencia para <nulo>


Machine Translated by Google

Los engranajes de las redes neuronales: operaciones tensoriales 45

Desenrollar bolas de papel es de lo que se trata el aprendizaje automático: de encontrar


representaciones claras para conjuntos de datos complejos y muy plegados. En este punto, deberías tener un
Una intuición bastante buena de por qué el aprendizaje profundo sobresale en esto: toma el enfoque de
descomponer incrementalmente una complicada transformación geométrica en una larga
cadena de elementales, que es más o menos la estrategia que un humano seguiría para
desarrugar una bola de papel. Cada capa en una red profunda aplica una transformación que
desenreda un poco los datos, y una pila profunda de capas hace que sea manejable un
complicado proceso de desenredado.

Con licencia para <nulo>


Machine Translated by Google

46 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

2.4 El motor de las redes neuronales: optimización


basada en gradientes Como vio en la
sección anterior, cada capa neuronal de nuestro primer ejemplo de red transforma sus
datos de entrada de la siguiente manera:
salida = relu(punto(W, entrada) + b)

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:

1 Dibuje un lote de muestras de entrenamiento x y los objetivos correspondientes y.


2 Ejecute la red en x (un paso llamado paso directo) para obtener predicciones y_pred.
3 Calcule la pérdida de la red en el lote, una medida de la falta de coincidencia entre y_pred e y.

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

Con licencia para <nulo>


Machine Translated by Google

El motor de las redes neuronales: optimización basada en gradientes 47

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.

2.4.1 ¿Qué es una derivada?

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:

f(x + épsilon_x) = y + épsilon_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:

f(x + épsilon_x) = y + a * épsilon_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

Con licencia para <nulo>


Machine Translated by Google

48 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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.

2.4.2 Derivada de una operación tensorial: el gradiente


Un gradiente es la derivada de una operación tensorial. Es la generalización del concepto.
de derivadas a funciones de entradas multidimensionales: es decir, a funciones que toman
tensores como entradas.
Considere un vector de entrada x, una matriz W, un objetivo y y una función de pérdida . Puede
use W para calcular un candidato objetivo y_pred y calcule la pérdida o falta de coincidencia,
entre el candidato objetivo y_pred y el objetivo y:

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)

Digamos que el valor actual de W es W0. Entonces la derivada de f en el punto W0 es un tensor


gradiente(f)(W0) con la misma forma que W, donde cada coeficiente gradiente(f)
(W0)[i, j] indica la dirección y magnitud del cambio en el valor de pérdida que usted
observar al modificar W0[i, j]. Ese gradiente tensorial (f)(W0) es el gradiente de
la función f(W) = valor_pérdida en W0.
Viste antes que la derivada de una función f(x) de un solo coeficiente puede ser
interpretado como la pendiente de la curva de f. Asimismo, el gradiente(f)(W0) puede interpretarse
como el tensor que describe la curvatura de f(W) alrededor de W0.
Por esta razón, de la misma manera que, para una función f(x), puedes reducir
el valor de f(x) moviendo x un poco en la dirección opuesta a la derivada,
con una función f(W) de un tensor, puedes reducir f(W) moviendo W en sentido opuesto
dirección desde el gradiente: por ejemplo, W1 = W0 ­ paso * gradiente(f)(W0) (donde
el paso es un pequeño factor de escala). Eso significa ir en contra de la curvatura, lo que intuitivamente
debería situarte más abajo en la curva. Tenga en cuenta que el paso del factor de escala es necesario
porque gradient(f)(W0) solo se aproxima a la curvatura cuando estás cerca de W0,
por lo que no querrás alejarte demasiado de W0.

2.4.3 Descenso de gradiente estocástico


Dada una función diferenciable, es teóricamente posible encontrar su mínimo analíticamente: se sabe
que el mínimo de una función es un punto donde la derivada es 0, por lo que todos
lo que tienes que hacer es encontrar todos los puntos donde la derivada llega a 0 y verificar cuáles
de estos puntos la función tiene el valor más bajo.

Con licencia para <nulo>


Machine Translated by Google

El motor de las redes neuronales: optimización basada en gradientes 49

Aplicado a una red neuronal, eso significa encontrar analíticamente la combinación de


valores de peso que produzcan la función de pérdida más pequeña posible. Esto se puede hacer
resolviendo la ecuación gradiente(f)(W) = 0 para W. Esta es una ecuación polinómica de N variables,
donde N es el número de coeficientes en la red. Aunque sería
posible resolver tal ecuación para N = 2 o N = 3, hacerlo es intratable en la realidad
Redes neuronales, donde el número de parámetros nunca es inferior a unos pocos miles.
y a menudo pueden ser varias decenas de millones.

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:

1 Dibuje un lote de muestras de entrenamiento x y los objetivos correspondientes y.


2 Ejecute la red en x para obtener predicciones y_pred.
3 Calcule la pérdida de la red en el lote, una medida del desajuste
entre y_pred y y.
4 Calcular el gradiente de pérdida con respecto a los parámetros de la red (un
pase hacia atrás).
5 Mueva los parámetros un poco en la dirección opuesta al gradiente;
ejemplo W ­= paso * gradiente, reduciendo así un poco la pérdida en el lote.

¡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.

Pérdida Paso, también llamado tasa de aprendizaje

valor
A partir de
punto (t=0)

t=1

t=2
t=3

Parámetro Figura 2.11 SGD bajando una curva de pérdida


valor 1D (un parámetro que se puede aprender)

Con licencia para <nulo>


Machine Translated by Google

50 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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.

Con licencia para <nulo>


Machine Translated by Google

El motor de las redes neuronales: optimización basada en gradientes 51

Valor

de pérdida

Mínimo
local

Mínimo
global

Valor del Figura 2.13 Un mínimo local y un mínimo


parámetro 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:

velocidad_pasada = 0. factor de impulso constante


impulso = 0,1
mientras que la pérdida > 0,01: Bucle de optimización

w, pérdida, gradiente = get_current_parameters() velocidad = velocidad_pasada *


impulso + tasa_de_aprendizaje * gradiente w=w+ impulso * velocidad ­ tasa_de_aprendizaje * gradiente velocidad_pasada
= velocidad parámetro_actualización(w)

2.4.4 Encadenamiento de derivadas: el algoritmo de retropropagación


En el algoritmo anterior, asumimos casualmente que debido a que una función es diferenciable, podemos calcular
explícitamente su derivada. En la práctica, una función de red neuronal consta de muchas operaciones tensoriales
encadenadas, cada una de las cuales tiene una derivada simple y conocida. Por ejemplo, ésta es una red f
compuesta de tres operaciones tensoriales, a, byc , con matrices de pesos W1, W2 y W3:

f(W1, W2, W3) = a(W1, b(W2, c(W3)))

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

Con licencia para <nulo>


Machine Translated by Google

52 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

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.

Con licencia para <nulo>


Machine Translated by Google

Mirando hacia atrás a nuestro primer ejemplo 53

2.5 Volviendo a nuestro primer ejemplo Ha


llegado al final de este capítulo y ahora debería tener una comprensión
general de lo que sucede detrás de escena en una red neuronal. Volvamos
al primer ejemplo y revisemos cada parte a la luz de lo que has aprendido en
las tres secciones anteriores.
Estos fueron los datos de entrada:

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

imágenes_tren = imágenes_tren.reshape((60000, 28 * 28)) imágenes_tren =


imágenes_tren.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28)) test_images =


test_images.astype('float32') / 255

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:

network.fit(train_images, train_labels, épocas=5, tamaño_lote=128)

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.

Con licencia para <nulo>


Machine Translated by Google

54 CAPÍTULO 2 Antes de comenzar: los componentes matemáticos de las redes neuronales

respectivamente. Después de estas 5 épocas, la red habrá realizado 2345 actualizaciones de


gradiente (469 por época) y la pérdida de la red será lo suficientemente baja como para que la
red sea capaz de clasificar dígitos escritos a mano con alta precisión.
En este punto, ya sabes la mayor parte de lo que hay que saber sobre las redes neuronales.

Con licencia para <nulo>


Machine Translated by Google

Mirando hacia atrás a nuestro primer ejemplo 55

Resumen del capítulo


Aprender significa encontrar una combinación de parámetros del modelo que minimice una
función de pérdida para un conjunto dado de muestras de datos de entrenamiento y sus
objetivos correspondientes.

El aprendizaje ocurre al extraer lotes aleatorios de muestras de datos y sus


objetivos y calcular el gradiente de los parámetros de la red con
con respecto a la pérdida del lote. Luego los parámetros de la red se mueven
un poco (la magnitud del movimiento está definida por la tasa de aprendizaje) en el
dirección opuesta al gradiente.

Todo el proceso de aprendizaje es posible gracias al hecho de que las redes


neuronales son cadenas de operaciones tensoriales diferenciables y, por lo tanto, es posible
aplicar la regla de la cadena de derivación para encontrar la función de gradiente que
asigna los parámetros actuales y el lote actual de datos a un valor de gradiente.

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.

La pérdida es la cantidad que intentarás minimizar durante el entrenamiento, por lo que


debe representar una medida del éxito de la tarea que estás intentando resolver.

El optimizador especifica la forma exacta en la que el gradiente de pérdida


usarse para actualizar parámetros: por ejemplo, podría ser el optimizador RMSProp , SGD
con impulso, etc.

Con licencia para <nulo>


Machine Translated by Google

Empezando
con las redes neuronales

Este capítulo cubre


Componentes centrales de las redes neuronales.

Una introducción a Keras

Configurar una estación de trabajo de aprendizaje profundo

Usar redes neuronales para resolver problemas básicos


problemas de clasificación y regresión

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

Con licencia para <nulo>


Machine Translated by Google

57

Compatibilidad con TensorFlow, Keras y GPU . Nos sumergiremos en tres ejemplos introductorios de
cómo utilizar redes neuronales para abordar problemas reales:

Clasificar reseñas de películas como positivas o negativas (clasificación binaria) Clasificar


noticias por tema (clasificación multiclase) Estimar el precio de una casa,
dados los datos inmobiliarios (regresión)

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.

Con licencia para <nulo>


Machine Translated by Google

58 CAPÍTULO 3 Comenzando con las redes neuronales

3.1 Anatomía de una red neuronal


Como vio en los capítulos anteriores, el entrenamiento de una red neuronal gira en torno a los siguientes objetos: Capas,
que se combinan en

una red (o modelo) Los datos de entrada y los objetivos correspondientes

La función de pérdida, que define la señal de retroalimentación

utilizado para el aprendizaje El optimizador, que determina cómo procede el aprendizaje.

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)

Actualización Predicciones Objetivos verdaderos

de peso Y' Y

Optimizador Función de pérdida

Figura 3.1 Relación entre la red, capas,


Puntuación de pérdida
función de pérdida y optimizador

Echemos un vistazo más de cerca a las capas, redes, funciones de pérdida y optimizadores.

3.1.1 Capas: los componentes básicos del aprendizaje profundo

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).

Con licencia para <nulo>


Machine Translated by Google

Anatomía de una red neuronal. 59

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:

desde keras importan capas


Una capa densa con 32
capa = capas.Dense(32, input_shape=(784,)) unidades de salida.

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:

de modelos importados de keras


desde keras importan capas

modelo = modelos.Secuencial()
model.add(capas.Dense(32, input_shape=(784,)))
modelo.add(capas.Densa(32))

La segunda capa no recibió un argumento de forma de entrada; en cambio, automáticamente


dedujo que su forma de entrada era la forma de salida de la capa anterior.

3.1.2 Modelos: redes de capas


Un modelo de aprendizaje profundo es un gráfico de capas dirigido y acíclico. Los más comunes
La instancia es una pila lineal de capas, que asigna una única entrada a una única salida.
Pero a medida que avance, estará expuesto a una variedad mucho más amplia de redes.
topologías. Algunos comunes incluyen los siguientes:
Redes de dos ramas
Redes multicabezal

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.

Con licencia para <nulo>


Machine Translated by Google

60 CAPÍTULO 3 Comenzando con las redes neuronales

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.

3.1.3 Funciones de pérdida y optimizadores:


claves para configurar el proceso de aprendizaje
Una vez definida la arquitectura de red, todavía tienes que elegir dos cosas más:

Función de pérdida (función objetivo): la cantidad que se minimizará durante


capacitación. Representa una medida de éxito de la tarea en cuestión.

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.

Afortunadamente, cuando se trata de problemas comunes como clasificación, regresión y predicción de


secuencias, existen pautas simples que puede seguir para elegir la pérdida correcta. Por ejemplo, utilizará la
entropía cruzada binaria para un problema de clasificación de dos clases, la entropía cruzada categórica
para un problema de clasificación de muchas clases, el error cuadrático medio para un problema de
regresión, la clasificación temporal conexionista (CTC) para un problema de aprendizaje de secuencias y
pronto. Sólo cuando esté trabajando en problemas de investigación verdaderamente nuevos tendrá que
desarrollar sus propias funciones objetivas. En los próximos capítulos, detallaremos explícitamente qué
funciones de pérdida elegir para una amplia gama de tareas comunes.

Con licencia para <nulo>


Machine Translated by Google

Introducción a Keras 61

3.2 Introducción a Keras


A lo largo de este libro, los ejemplos de código utilizan Keras (https://keras.io). Keras es un
marco de aprendizaje profundo para Python que proporciona una manera conveniente de definir y
entrenar casi cualquier tipo de modelo de aprendizaje profundo. Keras fue desarrollado inicialmente para
investigadores, con el objetivo de permitir una experimentación rápida.
Keras tiene las siguientes características clave:

Permite que el mismo código se ejecute sin problemas en CPU o GPU.


Tiene una API fácil de usar que facilita la creación rápida de prototipos de aprendizaje profundo.
modelos.

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

Con licencia para <nulo>


Machine Translated by Google

62 CAPÍTULO 3 Comenzando con las redes neuronales

3.2.1 Keras, TensorFlow, Theano y CNTK

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.

Figura 3.3 La pila de software y


hardware 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).

3.2.2 Desarrollar con Keras: una descripción general rápida

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:

1 Defina sus datos de entrenamiento: tensores de entrada y tensores objetivo.


2 Defina una red de capas (o modelo ) que asigne sus entradas a sus objetivos.

Con licencia para <nulo>


Machine Translated by Google

Introducción a Keras 63

3 Configure el proceso de aprendizaje eligiendo una función de pérdida, un optimizador y


algunas métricas para monitorear.
4 Itere sus datos de entrenamiento llamando al método fit() de su modelo.

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):

desde keras importar modelos desde


keras importar capas

modelo = modelos.Sequential()
model.add(layers.Dense(32, activación='relu', input_shape=(784,))) model.add(layers.Dense(10,
activación='softmax'))

Y aquí está el mismo modelo definido usando la API funcional:


input_tensor = capas.Input(forma=(784,)) x = capas.Dense(32,
activación='relu')(input_tensor) output_tensor = capas.Dense(10, activación='softmax')
(x)

modelo = modelos.Modelo(entradas=tensor_entrada, salidas=tensor_salida)

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:

de optimizadores de importación de keras

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:

model.fit(input_tensor, target_tensor, lote_size=128, épocas=10)

Con licencia para <nulo>


Machine Translated by Google

64 CAPÍTULO 3 Comenzando con las redes neuronales

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.

Con licencia para <nulo>


Machine Translated by Google

Configurar una estación de trabajo de aprendizaje profundo sesenta y cinco

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 back­ends). En este libro, nos centraremos en
TensorFlow, con algunas instrucciones sencillas relacionadas con Theano. No cubriremos a CNTK.

3.3.1 Cuadernos Jupyter: la forma preferida de ejecutar


experimentos de aprendizaje profundo

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/deep­learning­
with­python .

Con licencia para <nulo>


Machine Translated by Google

66 CAPÍTULO 3 Comenzando con las redes neuronales

3.3.2 Ejecutar Keras: dos opciones


Para comenzar en la práctica, recomendamos una de las dos opciones siguientes:

Utilice la AMI oficial de aprendizaje profundo de EC2 (https://aws.amazon.com/amazon­ai/


amis ), y ejecute experimentos de Keras como cuadernos Jupyter en EC2. Haga esto si aún
no tiene una GPU en su máquina local. El Apéndice B proporciona una guía paso a paso.
Instale todo desde
cero en una estación de trabajo Unix local. Luego puede ejecutar cuadernos Jupyter locales o
una base de código Python normal. Haga esto si ya tiene una GPU NVIDIA de alta gama .
El Apéndice A proporciona una guía paso a paso específica de Ubuntu.

Echemos un vistazo más de cerca a algunos de los compromisos que implica elegir una opción
sobre la otra.

3.3.3 Ejecutar trabajos de aprendizaje profundo en la nube: pros y contras


Si aún no tiene una GPU que pueda usar para el aprendizaje profundo (una GPU NVIDIA reciente
y de alta gama), ejecutar experimentos de aprendizaje profundo en la nube es una forma sencilla
y económica de comenzar sin tener que comprar hardware adicional. Si utiliza notebooks Jupyter,
la experiencia de ejecutar en la nube no es diferente de ejecutar localmente. A mediados de 2017,
la oferta de nube que facilita el inicio del aprendizaje profundo es definitivamente AWS EC2. El
Apéndice B proporciona una guía paso a paso para ejecutar notebooks Jupyter en una instancia
de GPU EC2 .
Pero si eres un gran usuario de aprendizaje profundo, esta configuración no es sostenible a
largo plazo, ni siquiera durante más de unas pocas semanas. Las instancias EC2 son caras: el tipo
de instancia recomendado en el apéndice B (la instancia p2.xlarge , que no le proporcionará mucha
energía) cuesta $0,90 por hora a mediados de 2017. Mientras tanto, una GPU sólida para el
consumidor le costará entre $ 1000 y $ 1500, un precio que se ha mantenido bastante estable a lo
largo del tiempo, incluso cuando las especificaciones de estas GPU siguen mejorando. Si se toma
en serio el aprendizaje profundo, debe configurar una estación de trabajo local con una o más GPU.
En resumen, EC2 es una excelente manera de comenzar. Puede seguir los ejemplos de código de este
libro completamente en una instancia de GPU EC2 . Pero si vas a ser un usuario avanzado del aprendizaje
profundo, consigue tus propias GPU.

3.3.4 ¿Cuál es la mejor GPU para aprendizaje profundo?

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.

Con licencia para <nulo>


Machine Translated by Google

Configurar una estación de trabajo de aprendizaje profundo 67

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.

Con licencia para <nulo>


Machine Translated by Google

68 CAPÍTULO 3 Comenzando con las redes neuronales

3.4 Clasificación de reseñas de películas:


un ejemplo de clasificación binaria La
clasificación de dos clases, o clasificación binaria, puede ser el tipo de problema de
aprendizaje automático más aplicado. En este ejemplo, aprenderá a clasificar las reseñas de
películas como positivas o negativas, según el contenido del texto de las reseñas.

3.4.1 El conjunto de datos IMDB

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).

Listado 3.1 Cargando el conjunto de datos IMDB

desde keras.datasets importar imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(


número_palabras=10000)

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:

>>> datos_tren[0] [1, 14,


22, 16, ... 178, 32]

>>> etiquetas_tren[0] 1

Con licencia para <nulo>


Machine Translated by Google

Clasificación de reseñas de películas: un ejemplo de clasificación binaria 69

Debido a que se está restringiendo a las 10 000 palabras más frecuentes, ningún índice de palabras
excederá las 10 000:

>>> max([max(secuencia) para secuencia en train_data]) 9999

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]])

Lo invierte, asignando Decodifica la reseña. Tenga en cuenta que los índices


índices enteros a palabras.
están compensados por 3 porque 0, 1 y 2 son
índices reservados para "relleno", "inicio de secuencia"
y "desconocido".

3.4.2 Preparación de los datos


No se pueden introducir listas de números enteros en una red neuronal. Tienes que convertir tus
listas en tensores. Hay dos maneras de hacerlo:

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.

Listado 3.2 Codificación de secuencias enteras en una matriz binaria

importar numpy como np


Crea una matriz de forma todo cero
def vectorize_sequences(secuencias, dimensión=10000): resultados = (len (secuencias), dimensión)
np.zeros((len(secuencias), dimensión)) para i, secuencia en
enumerar(secuencias): resultados[i, secuencia] = 1.

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

Con licencia para <nulo>


Machine Translated by Google

70 CAPÍTULO 3 Comenzando con las redes neuronales

Así es como se ven las muestras ahora:

>>> x_train[0]
matriz([ 0., 1., 1., ..., 0., 0., 0.])

También debes vectorizar tus etiquetas, lo cual es sencillo:

y_train = np.asarray(train_labels).astype('float32') y_test =


np.asarray(test_labels).astype('float32')

Ahora los datos están listos para ser introducidos en una red neuronal.

3.4.3 Construyendo su red


Los datos de entrada son vectores y las etiquetas son escalares (1 y 0): esta es la configuración más sencilla
que jamás haya encontrado. Un tipo de red que funciona bien en este tipo de problemas es una pila simple de
capas completamente conectadas (Densas) con activaciones relu : Dense(16, activación='relu').

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:

salida = relu(punto(W, entrada) + b)

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 :

Cuántas capas usar Cuántas


unidades ocultas elegir para cada capa

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:

Dos capas intermedias con 16 unidades ocultas cada una Una


tercera capa que generará la predicción escalar con respecto al sentimiento de la revisión actual

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,

Con licencia para <nulo>


Machine Translated by Google

Clasificación de reseñas de películas: un ejemplo de clasificación binaria 71

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.

Figura 3.4 La función unitaria lineal rectificada

Figura 3.5 La función sigmoidea

Con licencia para <nulo>


Machine Translated by Google

72 CAPÍTULO 3 Comenzando con las redes neuronales

Salida
(probabilidad)

Denso (unidades=1)

Denso (unidades=16)

Denso (unidades=16)

Secuencial

Entrada (texto vectorizado) Figura 3.6 La red de tres capas

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.

Listado 3.3 La definición del modelo

desde keras importar modelos desde


keras importar capas

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'))

¿Qué son las funciones de activación y por qué son necesarias?


Sin una función de activación como relu (también llamada no linealidad), la capa Densa
consistiría en dos operaciones lineales: un producto escalar y una suma:

salida = punto (W, entrada) + b

Entonces, la capa solo podría aprender transformaciones lineales (transformaciones afines) de


los datos de entrada: el espacio de hipótesis de la capa sería el conjunto de todas las
transformaciones lineales posibles de los datos de entrada en un espacio de 16 dimensiones.
Un espacio de hipótesis de este tipo es demasiado restringido y no se beneficiaría de múltiples
capas de representaciones, porque una pila profunda de capas lineales aún implementaría una
operación lineal: agregar más capas no ampliaría el espacio de hipótesis.

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.

Finalmente, debes elegir una función de pérdida y un optimizador. Debido a que se


enfrenta a un problema de clasificación binaria y la salida de su red es una probabilidad
(finaliza su red con una capa de una sola unidad con una activación sigmoidea), es mejor usar el

Con licencia para <nulo>


Machine Translated by Google

Clasificación de reseñas de películas: un ejemplo de clasificación binaria 73

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.

Listado 3.4 Compilando el modelo

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.

Listado 3.5 Configurando el optimizador

de optimizadores de importación de keras

model.compile(optimizador=optimizadores.RMSprop(lr=0.001),
pérdida='binary_crossentropy',
métricas=['precisión'])

Listado 3.6 Uso de pérdidas y métricas personalizadas

de las pérdidas de importación de keras


de las métricas de importación de keras

model.compile(optimizador=optimizadores.RMSprop(lr=0.001),
pérdida=pérdida.binary_crossentropy,
métricas=[metrics.binary_accuracy])

3.4.4 Validar su enfoque


Para monitorear durante el entrenamiento la precisión del modelo en datos que nunca antes se habían
visto, creará un conjunto de validación separando 10 000 muestras de los datos de entrenamiento
originales.

Listado 3.7 Reservar un conjunto de validación

valor_x = tren_x[:10000] tren_x


parcial = tren_x[10000:]

Con licencia para <nulo>


Machine Translated by Google

74 CAPÍTULO 3 Comenzando con las redes neuronales

y_val = y_train[:10000] parcial_y_train =


y_train[10000:]

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 .

Listado 3.8 Entrenando tu modelo

model.compile(optimizador='rmsprop',
pérdida='binary_crossentropy', métricas=['acc'])

historial = model.fit(partial_x_train, parcial_y_train, épocas=20,


tamaño_lote=512,

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:

>>> dict_historia = historia.historia >>> dict_historia.keys()


[u'acc', u'loss', u'val_acc', u'val_loss']

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.

Listado 3.9 Trazado de la pérdida de entrenamiento y validación

importar matplotlib.pyplot como plt

dict_historia = historia.valores_pérdida historia =


dict_historia['pérdida'] val_loss_values = dict_historia['val_loss']

épocas = rango(1, len(acc) + 1) "bo" significa


"punto azul".
plt.plot(epochs, loss_values, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs, val_loss_values, 'b',
label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación') plt .xlabel('Épocas') plt.ylabel('Pérdida')
plt.legend()
"b" es para "línea
azul continua".

plt.mostrar()

Con licencia para <nulo>


Machine Translated by Google

Clasificación de reseñas de películas: un ejemplo de clasificación binaria 75

Figura 3.7 Pérdida de capacitación y validación

Listado 3.10 Trazado de la precisión del entrenamiento y la validación

plt.clf() Limpia la figura


acc_values = historial_dict['acc'] val_acc_values =
historial_dict['val_acc']

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation


acc') plt.title('Precisión de entrenamiento y validación') plt .xlabel('Épocas') plt.ylabel('Pérdida')
plt.legend()

plt.mostrar()

Figura 3.8 Precisión de capacitación y validación

Con licencia para <nulo>


Machine Translated by Google

76 CAPÍTULO 3 Comenzando con las redes neuronales

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.

Listado 3.11 Reentrenamiento de un modelo desde cero

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'])

model.fit(x_train, y_train, épocas=4, lote_size=512) resultados = model.evaluate(x_test,


y_test)

Los resultados finales son los siguientes:

>>> 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)

Con licencia para <nulo>


Machine Translated by Google

Clasificación de reseñas de películas: un ejemplo de clasificación binaria 77

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).

3.4.6 Experimentos adicionales


Los siguientes experimentos le ayudarán a convencerse de que las elecciones de arquitectura que ha
realizado son bastante razonables, aunque todavía hay margen de mejora:

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.

Intente utilizar la función de pérdida mse en lugar de binario_crossentropy.


Intente utilizar la activación tanh (una activación que era popular en los primeros días de las redes
neuronales) en lugar de relu.

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.

Con licencia para <nulo>


Machine Translated by Google

78 CAPÍTULO 3 Comenzando con las redes neuronales

3.5 Clasificación de noticias: un


ejemplo de clasificación multiclase En la
sección anterior, vio cómo clasificar entradas vectoriales en dos clases mutuamente
excluyentes utilizando una red neuronal densamente conectada. ¿Pero qué pasa
cuando tienes más de dos clases?
En esta sección, creará una red para clasificar los canales de noticias de Reuters en 46 temas mutuamente excluyentes.
Como tiene muchas clases, este problema es un ejemplo de clasificación de clases múltiples; y debido a que cada punto de
datos debe clasificarse en una sola categoría, el problema es más específicamente un caso de clasificación multiclase con
etiqueta única.
Si cada punto de datos pudiera pertenecer a varias categorías (en este caso, temas), se enfrentaría a un problema de
clasificación de varias etiquetas y clases .

3.5.1 El conjunto de datos de Reuters

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.

Listado 3.12 Cargando el conjunto de datos de Reuters

de keras.datasets importar Reuters

(train_data, train_labels), (test_data, test_labels) = reuters.load_data(


número_palabras=10000)

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:

>>> len(datos_tren) 8982

>>> len(datos_prueba) 2246

Al igual que con las revisiones de IMDB , cada ejemplo es una lista de números enteros (índices de palabras):

>>> datos_tren[10] [1, 245,


273, 207, 156, 53, 74, 160, 26, 14, 46, 296, 26, 39, 74, 2979, 3554, 14, 46, 4689, 4329, 86, 61, 3499, 4795, 14, 61, 451,
4329, 17, 12]

Así es como puedes decodificarlo en palabras, en caso de que tengas curiosidad.

Listado 3.13 Decodificación de cables de noticias en texto

word_index = reuters.get_word_index() reverse_word_index


= dict([(valor, clave) para (clave, valor) en word_index.items()]) decoded_newswire = ' '.join([reverse_word_index.get(i ­ 3, '?' ) para
i en train_data[0]])

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".

Con licencia para <nulo>


Machine Translated by Google

Clasificación de noticias: un ejemplo de clasificación multiclase 79

La etiqueta asociada con un ejemplo es un número entero entre 0 y 45: un índice de tema:

>>> etiquetas_tren[10] 3

3.5.2 Preparación de los datos


Puede vectorizar los datos con exactamente el mismo código que en el ejemplo anterior.

Listado 3.14 Codificando los datos

importar numpy como np

def vectorize_sequences(secuencias, dimensión=10000): resultados =


np.zeros((len(secuencias), dimensión)) para i, secuencia en
enumerar(secuencias): resultados[i, secuencia] = 1.

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 one­hot. La codificación one­hot 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 one­hot, consulte la sección 6.1. En este caso, la
codificación one­hot 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:

def to_one_hot(etiquetas, dimensión=46): resultados =


np.zeros((len(etiquetas), dimensión)) para i, etiqueta en
enumerar(etiquetas): resultados[i, etiqueta] = 1.

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 :

de keras.utils.np_utils importar a_categorical

one_hot_train_labels = to_categorical(train_labels) one_hot_test_labels =


to_categorical(test_labels)

3.5.3 Construyendo su red


Este problema de clasificación de temas es similar al problema anterior de clasificación de reseñas
de películas: en ambos casos, estás intentando clasificar fragmentos cortos de texto. Pero aquí hay
una nueva restricción: el número de clases de salida ha pasado de 2 a 46. La dimensionalidad del
espacio de salida es mucho mayor.
En una pila de capas densas como la que ha estado usando, cada capa solo puede acceder a la
información presente en la salida de la capa anterior. Si una capa deja caer alguna información

Con licencia para <nulo>


Machine Translated by Google

80 CAPÍTULO 3 Comenzando con las redes neuronales

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.

Listado 3.15 Definición del modelo

desde keras importar modelos desde


keras importar capas

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.

Listado 3.16 Compilando el modelo

model.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy',
métricas=['precisión'])

3.5.4 Validar su enfoque


Separemos 1000 muestras en los datos de entrenamiento para usarlas como conjunto de validación.

Listado 3.17 Reservar un conjunto de validación

valor_x = tren_x[:1000] tren_x


parcial = tren_x[1000:]

y_val = one_hot_train_labels[:1000] parcial_y_train =


one_hot_train_labels[1000:]

Con licencia para <nulo>


Machine Translated by Google

Clasificación de noticias: un ejemplo de clasificación multiclase 81

Ahora, entrenemos la red durante 20 épocas.

Listado 3.18 Entrenando el modelo

historial = model.fit(partial_x_train, parcial_y_train, épocas=20,


tamaño_lote=512,

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).

Listado 3.19 Trazado de la pérdida de entrenamiento y validación

importar matplotlib.pyplot como plt

pérdida = historia.historia['pérdida'] val_loss =


historia.historia['val_loss']

épocas = rango (1, len (pérdida) + 1)

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs, val_loss, 'b',


label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación') plt .xlabel('Épocas')
plt.ylabel('Pérdida') plt.legend()

plt.mostrar()

Listado 3.20 Trazado de la precisión del entrenamiento y la validación

plt.clf() Limpia la figura


acc = historia.historia['acc'] val_acc =
historia.historia['val_acc']

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation


acc') plt.title('Precisión de entrenamiento y validación') plt .xlabel('Épocas') plt.ylabel('Pérdida')
plt.legend()

plt.mostrar()

Figura 3.9 Pérdida de capacitación y validación

Con licencia para <nulo>


Machine Translated by Google

82 CAPÍTULO 3 Comenzando con las redes neuronales

Figura 3.10 Precisión de


capacitación y validación

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.

Listado 3.21 Reentrenamiento de un modelo desde cero

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 = model.evaluate(x_test, one_hot_test_labels)

Aquí están los resultados finales:

>>> 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:

>>> importar copia >>>


test_labels_copy = copy.copy(test_labels) >>>
np.random.shuffle(test_labels_copy) >>> hits_array =
np.array(test_labels) == np.array(test_labels_copy) >>> flotante (np.sum(hits_array)) / len(test_labels)
0.18655387355298308

Con licencia para <nulo>


Machine Translated by Google

Clasificación de noticias: un ejemplo de clasificación multiclase 83

3.5.5 Generar predicciones sobre nuevos datos


Puede verificar que el método de predicción de la instancia del modelo devuelve una distribución de
probabilidad sobre los 46 temas. Generemos predicciones de temas para todos los datos de prueba.

Listado 3.22 Generando predicciones para nuevos datos

predicciones = modelo.predict(x_test)

Cada entrada en las predicciones es un vector de longitud 46:

>>> predicciones[0].forma (46,)

Los coeficientes de este vector suman 1:

>>> np.sum(predicciones[0]) 1.0

La entrada más grande es la clase predicha: la clase con la mayor probabilidad:

>>> np.argmax(predicciones[0]) 4

3.5.6 Una forma diferente de manejar las etiquetas y la pérdida


Mencionamos anteriormente que otra forma de codificar las etiquetas sería convertirlas como un tensor
entero, así:

y_train = np.array(train_labels) y_test =


np.array(test_labels)

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.

3.5.7 La importancia de tener capas intermedias suficientemente grandes


Mencionamos anteriormente que debido a que las salidas finales son de 46 dimensiones, debes evitar
capas intermedias con menos de 46 unidades ocultas. Ahora veamos qué sucede cuando se introduce un
cuello de botella de información al tener capas intermedias que tienen significativamente menos de 46
dimensiones: por ejemplo, 4 dimensiones.

Con licencia para <nulo>


Machine Translated by Google

84 CAPÍTULO 3 Comenzando con las redes neuronales

Listado 3.23 Un modelo con un cuello de botella de información


modelo = modelos.Sequential()
model.add(layers.Dense(64, activación='relu', input_shape=(10000,))) model.add(layers.Dense(4,
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
= 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.8 Experimentos adicionales


Intente utilizar capas más grandes o más pequeñas: 32 unidades, 128 unidades,
etc. Usaste dos capas ocultas. Ahora intenta usar una sola capa oculta, o tres capas ocultas.
capas de guarida.

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.

Con licencia para <nulo>


Machine Translated by Google

Predicción de los precios de la vivienda: un ejemplo de regresión 85

3.6 Predicción de los precios de la vivienda: un ejemplo de regresión Los dos


ejemplos anteriores se consideraron problemas de clasificación, donde el objetivo era predecir una
única etiqueta discreta de un punto de datos de entrada. Otro tipo común de problema de aprendizaje
automático es la regresión, que consiste en predecir un valor continuo en lugar de una etiqueta discreta:
por ejemplo, predecir la temperatura de mañana, dados los datos meteorológicos; o predecir el tiempo
que tardará en completarse un proyecto de software, dadas sus especificaciones.

NOTA No confunda la regresión y el algoritmo de regresión logística. Resulta confuso que la


regresión logística no sea un algoritmo de regresión, sino un algoritmo de clasificación.

3.6.1 Conjunto de datos sobre precios de la vivienda en Boston

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.

Listado 3.24 Cargando el conjunto de datos de vivienda de Boston

de keras.datasets importar boston_housing

(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

Veamos los datos:

>>> datos_entren.forma (404,


13) >>>
datos_prueba.forma (102, 13)

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.

Con licencia para <nulo>


Machine Translated by Google

86 CAPÍTULO 3 Comenzando con las redes neuronales

3.6.2 Preparación de los datos


Sería problemático introducir valores en una red neuronal que tomen rangos tremendamente diferentes. La
red podría adaptarse automáticamente a datos tan heterogéneos, pero definitivamente dificultaría el
aprendizaje. Una mejor práctica generalizada para tratar estos datos es realizar una normalización por
características: para cada característica en los datos de entrada (una columna en la matriz de datos de
entrada), se resta la media de la característica y se divide por la desviación estándar, de modo que la
característica está centrada alrededor de 0 y tiene una desviación estándar unitaria. Esto se hace fácilmente
en Numpy.

Listado 3.25 Normalizando los datos

media = train_data.mean(axis=0) train_data ­=


media std =
train_data.std(axis=0) train_data /= std

test_data ­= media test_data /


= estándar

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.

3.6.3 Construyendo su red


Como hay muy pocas muestras disponibles, utilizará una red muy pequeña con dos capas ocultas, cada una
con 64 unidades. En general, cuantos menos datos de entrenamiento tenga, peor será el sobreajuste, y
utilizar una red pequeña es una forma de mitigar el sobreajuste.

Listado 3.26 Definición del modelo

desde keras importar modelos desde


Como necesitarás crear instancias
keras importar capas
del mismo modelo varias veces,
def build_model(): modelo = utilizas una función para construirlo.
modelos.Sequential()
model.add(layers.Dense(64, activación='relu',
input_shape=(train_data.shape[1],)))
model.add(layers.Dense(64, activación='relu')) model.add(layers.Dense(1))
model.compile(optimizer='rmsprop' ,
pérdida='mse', metrics=['mae']) modelo de retorno

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. .

Con licencia para <nulo>


Machine Translated by Google

Predicción de los precios de la vivienda: un ejemplo de regresión 87

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.

3.6.4 Validar su enfoque utilizando la validación K­fold


Para evaluar tu red mientras sigues ajustando sus parámetros (como el número
de épocas utilizadas para el entrenamiento), podría dividir los datos en un conjunto de entrenamiento y un
conjunto de validación, como lo hizo en los ejemplos anteriores. Pero como tienes tan pocos puntos de datos,
el conjunto de validación terminaría siendo muy pequeño (por ejemplo, alrededor de 100 ejemplos).
Como consecuencia, las puntuaciones de validación pueden cambiar mucho dependiendo de qué datos
puntos que elegiste usar para la validación y que elegiste para la capacitación: la validación
las puntuaciones pueden tener una gran variación con respecto a la división de validación. Esto le impediría
evaluar de forma fiable su modelo.
La mejor práctica en tales situaciones es utilizar la validación cruzada K­fold (ver figura 3.11).
Consiste en dividir los datos disponibles en K particiones (normalmente K = 4 o 5), crear instancias de K modelos
idénticos y entrenar cada uno en K – 1 particiones mientras se evalúa en
la partición restante. La puntuación de validación del modelo utilizado es entonces el promedio de
las puntuaciones de validación K obtenidas. En términos de código, esto es sencillo.

Datos divididos en 3 particiones

Validación
Doblar 1 Validación Capacitación Capacitación
puntuación #1

Validación Puntuación final:


Doblar 2 Validación Validación Capacitación
puntuación #2 promedio

Validación
Doblar 3 Validación Capacitación Validación
puntuación #3

Figura 3.11 Validación cruzada triple

Listado 3.27 Validación K veces

importar numpy como np

k=4
num_val_samples = len(train_data) // k
núm_épocas = 100
todas las puntuaciones = []

Con licencia para <nulo>


Machine Translated by Google

88 CAPÍTULO 3 Comenzando con las redes neuronales

Prepara los datos de validación: datos Prepara los datos de entrenamiento:


de la partición #k datos de todas las demás particiones.

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)

Evalúa el modelo sobre los


datos de validación.

Ejecutar esto con num_epochs = 100 produce los siguientes resultados:

>>> todas las


puntuaciones [2.588258957792037, 3.1289568449719116, 3.1856116051248984, 3.0763342615401386] >>> np.mean(todas
las puntuaciones) 2.9947904173572462

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 K­fold. 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.

Listado 3.28 Guardando los registros de validación en cada pliegue

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.

Con licencia para <nulo>


Machine Translated by Google

Predicción de los precios de la vivienda: un ejemplo de regresión 89

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)

modelo = build_model() historial =


model.fit(partial_train_data, parcial_train_targets,
validation_data=(val_data, val_targets), epochs=num_epochs,
lote_size=1, detallado=0) mae_history = historial.history['val_mean_absolute_error']
all_mae_histories.append(mae_history)
Entrena el modelo
(en modo silencioso, detallado = 0)

Luego puede calcular el promedio de las puntuaciones MAE por época para todos los pliegues.

Listado 3.29 Construyendo el historial de puntuaciones de validación medias sucesivas de K veces

promedio_mae_historia = [
np.mean([x[i] para x en all_mae_histories]) para i en el rango(num_epochs)]

Tracemos esto; ver figura 3.12.

Listado 3.30 Trazado de puntuaciones de validación

importar matplotlib.pyplot como plt

plt.plot(range(1, len(average_mae_history) + 1), Average_mae_history) plt.xlabel('Épocas') plt.ylabel('Validación MAE') plt.show()

Figura 3.12 Validación MAE por


época

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.

Con licencia para <nulo>


Machine Translated by Google

90 CAPÍTULO 3 Comenzando con las redes neuronales

El resultado se muestra en la figura 3.13.

Listado 3.31 Trazado de puntuaciones de validación, excluyendo los primeros 10 puntos de datos

def smooth_curve(puntos, factor=0.9):


smoothed_points = [] para punto en
puntos:
si puntos_suavizados: anterior =
puntos_suavizados[­1] puntos_suavizados.append(anterior
* factor + punto * (1 ­ factor))
demás:
smoothed_points.append(punto) devuelve
smoothed_points

smooth_mae_history = smooth_curve(promedio_mae_history[10:])

plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history) plt.xlabel('Épocas') plt.ylabel('Validación MAE')


plt.show()

Figura 3.13 Validación MAE por


época, excluyendo los primeros
10 puntos de datos

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.

Listado 3.32 Entrenando el modelo final

Obtiene un modelo nuevo y compilado


modelo = build_model()
Lo entrena en la totalidad de los datos.
model.fit(train_data, train_targets,
épocas=80, tamaño_lote=16, detallado=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)

Con licencia para <nulo>


Machine Translated by Google

Predicción de los precios de la vivienda: un ejemplo de regresión 91

Aquí está el resultado final:

>>> prueba_mae_score
2.5532484335057877

Aún te faltan unos $2,550.

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

La métrica de regresión común es el error absoluto medio (MAE).


Cuando las características en los datos de entrada tienen valores en diferentes rangos, cada característica
debe escalarse de forma independiente como paso de preprocesamiento.
Cuando hay pocos datos disponibles, utilizar la validación K­fold es una excelente manera de evaluar un
modelo de manera confiable.
Cuando hay pocos datos de entrenamiento disponibles, es preferible utilizar una red pequeña con
pocas capas ocultas (normalmente sólo una o dos), para evitar un sobreajuste grave.

Con licencia para <nulo>


Machine Translated by Google

92 CAPÍTULO 3 Comenzando con las redes neuronales

Resumen del capítulo Ahora

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

introducirlos en un sistema neuronal.


red.
Cuando sus datos tengan características con diferentes rangos, escale cada característica
de forma independiente como parte del preprocesamiento.

A medida que avanza el entrenamiento, las redes neuronales eventualmente comienzan a


sobreadaptarse y a obtener peores resultados con datos nunca antes vistos.

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 K­fold puede ayudar a evaluar de manera confiable su
modelo.

Con licencia para <nulo>


Machine Translated by Google

Fundamentos de
aprendizaje automático

Este capítulo cubre


Formas de aprendizaje automático más allá de la clasificación y la regresión

Procedimientos formales de evaluación de modelos de aprendizaje


automático.

Preparación de datos para el aprendizaje profundo

Ingeniería de características

Abordar el sobreajuste

El flujo de trabajo universal para abordar problemas de aprendizaje


automático

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

Con licencia para <nulo>


Machine Translated by Google

94 CAPÍTULO 4 Fundamentos del aprendizaje automático

4.1 Cuatro ramas del aprendizaje automático En nuestros ejemplos

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.

4.1.1 Aprendizaje supervisado


Este es, con diferencia, el caso más común. Consiste en aprender a asignar datos de entrada a objetivos conocidos
(también llamados anotaciones), dado un conjunto de ejemplos (a menudo anotados por humanos). Los cuatro
ejemplos que has encontrado hasta ahora en este libro eran ejemplos canónicos de aprendizaje supervisado. En
general, casi todas las aplicaciones de aprendizaje profundo que están en el centro de atención hoy en día
pertenecen a esta categoría, como el reconocimiento óptico de caracteres, el reconocimiento de voz, la clasificación
de imágenes y la traducción de idiomas.
Aunque el aprendizaje supervisado consiste principalmente en clasificación y regresión, existen
También hay variantes más exóticas, incluidas las siguientes (con ejemplos):

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.

4.1.2 Aprendizaje no supervisado


Esta rama del aprendizaje automático consiste en encontrar transformaciones interesantes de los datos de entrada
sin la ayuda de ningún objetivo, con fines de visualización de datos, compresión de datos o eliminación de ruido de
datos, o para comprender mejor las correlaciones presentes en los datos en cuestión. El aprendizaje no supervisado
es el pan de cada día del análisis de datos y, a menudo, es un paso necesario para comprender mejor un conjunto
de datos antes de intentar resolver un problema de aprendizaje supervisado. La reducción de dimensionalidad y la
agrupación son categorías bien conocidas de aprendizaje no supervisado.

4.1.3 Aprendizaje autosupervisado


Este es un caso específico de aprendizaje supervisado, pero es lo suficientemente diferente como para merecer su
propia categoría. El aprendizaje autosupervisado es un aprendizaje supervisado sin

Con licencia para <nulo>


Machine Translated by Google

Cuatro ramas del aprendizaje automático 95

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.

4.1.4 Aprendizaje por refuerzo


Esta rama del aprendizaje automático, ignorada durante mucho tiempo, recientemente comenzó a recibir
mucha atención después de que Google DeepMind la aplicara con éxito para aprender a jugar juegos de
Atari (y, más tarde, aprender a jugar Go al más alto nivel). En el aprendizaje por refuerzo, un agente recibe
información sobre su entorno y aprende a elegir acciones que maximizarán alguna recompensa. Por ejemplo,
una red neuronal que "mira" la pantalla de un videojuego y genera acciones del juego para maximizar su
puntuación se puede entrenar mediante el aprendizaje por refuerzo.

Actualmente, el aprendizaje por refuerzo es principalmente un área de investigación y aún no ha tenido


éxitos prácticos significativos más allá de los juegos. Sin embargo, con el tiempo esperamos ver que el
aprendizaje reforzado se apodere de una gama cada vez mayor de aplicaciones del mundo real: vehículos
autónomos, robótica, gestión de recursos, educación, etc. Es una idea cuyo momento ha llegado, o llegará
pronto.

Glosario de clasificación y regresión La


clasificación y la regresión implican muchos términos especializados. Se ha
encontrado con algunos de ellos en ejemplos anteriores y verá más en capítulos futuros.
Tienen definiciones precisas y específicas del aprendizaje automático, y usted debería estar
familiarizado con ellas:

Muestra o entrada: un punto de datos que se incluye en su


modelo. Predicción o resultado: lo que surge de su
modelo. Objetivo—La verdad. Lo que su modelo debería haber predicho idealmente, según
a una fuente externa de datos.

Con licencia para <nulo>


Machine Translated by Google

96 CAPÍTULO 4 Fundamentos del aprendizaje automático

(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.

Etiqueta: una instancia específica de una anotación de clase en un problema de clasificación.


Por ejemplo, si se anota que la imagen n.° 1234 contiene la clase “perro”, entonces
“perro” es una etiqueta de la imagen n.° 1234.
Verdad sobre el terreno o anotaciones: todos los objetivos de un conjunto de datos, generalmente recopilados por
humanos.
Clasificación binaria: una tarea de clasificación en la que cada muestra de entrada debe
clasificarse en dos categorías exclusivas.
Clasificación multiclase: una tarea de clasificación en la que cada muestra de entrada debe
clasificarse en más de dos categorías: por ejemplo, clasificar dígitos escritos a mano.

Clasificación de etiquetas múltiples: una tarea de clasificación en la que a cada muestra de


entrada se le pueden asignar múltiples etiquetas. Por ejemplo, una imagen determinada
puede contener tanto un gato como un perro y debe anotarse tanto con la etiqueta "gato"
como con la etiqueta "perro". El número de etiquetas por imagen suele ser variable.
Regresión escalar: una tarea donde el objetivo es un valor escalar continuo. Predecir los
precios de la vivienda es un buen ejemplo: los diferentes precios objetivo forman un espacio
continuo.
Regresión vectorial: tarea en la que el objetivo es un conjunto de valores continuos: por
ejemplo, un vector continuo. Si está haciendo una regresión contra múltiples valores (como
las coordenadas de un cuadro delimitador en una imagen), entonces está haciendo una
regresión vectorial. Mini
lote o lote: un pequeño conjunto de muestras (normalmente entre 8 y 128) que el modelo
procesa simultáneamente. El número de muestras suele ser una potencia de 2, para facilitar
la asignación de memoria en la GPU. Durante el entrenamiento, se utiliza un mini lote para
calcular una única actualización de descenso de gradiente aplicada a los pesos del modelo.

Con licencia para <nulo>


Machine Translated by Google

Evaluación de modelos de aprendizaje automático 97

4.2 Evaluación de modelos de aprendizaje automático


En los tres ejemplos presentados en el capítulo 3, dividimos los datos en un conjunto de entrenamiento, un
conjunto de validación y un conjunto de prueba. La razón para no evaluar los modelos con los mismos datos
con los que fueron entrenados rápidamente se hizo evidente: después de unas pocas épocas, los tres
modelos comenzaron a sobreajustarse. Es decir, su rendimiento con datos nunca antes vistos comenzó a
estancarse (o empeorar) en comparación con su rendimiento con los datos de entrenamiento, que siempre
mejora a medida que avanza el entrenamiento.
En el aprendizaje automático, el objetivo es lograr modelos que se generalicen (que funcionen bien con
datos nunca antes vistos) y el sobreajuste es el obstáculo central. Sólo puedes controlar lo que puedes
observar, por lo que es crucial poder medir de manera confiable el poder de generalización de tu modelo.
Las siguientes secciones analizan estrategias para mitigar el sobreajuste y maximizar la generalización. En
esta sección, nos centraremos en cómo medir la generalización: cómo evaluar modelos de aprendizaje
automático.

4.2.1 Conjuntos de entrenamiento, validación y prueba

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.

Con licencia para <nulo>


Machine Translated by Google

98 CAPÍTULO 4 Fundamentos del aprendizaje automático

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 K­fold y
validación K­fold iterada con barajado.

VALIDACIÓN DE RETENCIÓN SIMPLE

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.

Esquemáticamente, la validación de reserva se parece a la figura 4.1. El siguiente listado muestra


una implementación sencilla.

Total de datos etiquetados disponibles

Tendió

Conjunto de entrenamiento validación Figura 4.1 División de


colocar
validación de reserva simple

Entrena en esto Evaluar


en este

Listado 4.1 Validación de reserva

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[:]

modelo = get_model() Entrena un modelo con los datos de


modelo.train(datos_entrenamiento) entrenamiento y lo evalúa con los
datos de validación.
puntuación_validación = modelo.evaluar(datos_validación)

# En este punto puedes ajustar tu modelo,


# volver a entrenarlo, evaluarlo, sintonizarlo nuevamente...

modelo = get_model() Una vez que haya ajustado


model.train(np.concatenate([datos_entrenamiento, sus hiperparámetros, es común entrenar
datos_validación])) su modelo final desde cero con todos los
datos disponibles que no sean de prueba.
puntuación_prueba = modelo.evaluar (datos_prueba)

Con licencia para <nulo>


Machine Translated by Google

Evaluación de modelos de aprendizaje automático 99

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 K­fold e iterada.
La validación K­fold son dos formas de abordar esto, como se analiza a continuación.

VALIDACIÓN K­FOLD
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 K­fold se parece a la figura 4.2. El listado 4.2 muestra un sencillo
implementación.

Datos divididos en 3 particiones

Validación
Doblar 1 Validación Capacitación Capacitación
puntuación #1

Validación Puntuación final:


Doblar 2 Validación Validación Capacitación
puntuación #2 promedio

Validación
Doblar 3 Validación Capacitación Validación
puntuación #3

Figura 4.2 Validación triple

Listado 4.2 Validación cruzada K­fold

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)

Con licencia para <nulo>


Machine Translated by Google

100 CAPÍTULO 4 Fundamentos del aprendizaje automático

puntuación_validación = np.average(puntuaciones_validación) Puntuación de validación:

modelo = get_model() promedio de las


Entrena el modelo final puntuaciones de validación
modelo.tren(datos)
con todos los datos de los k pliegues
puntuación_prueba = modelo.evaluar (datos_prueba) disponibles que no son de prueba.

VALIDACIÓN ITERADA DE K­FOLD CON BARATO


Éste es para situaciones en las que tiene relativamente pocos datos disponibles y necesita
para evaluar su modelo con la mayor precisión posible. He encontrado que es extremadamente útil en
Competiciones de Kaggle. Consiste en aplicar la validación K­fold varias veces, barajar
los datos cada vez antes de dividirlos de K formas. La puntuación final es la media de los
puntuaciones obtenidas en cada ejecución de la validación K­fold. Tenga en cuenta que terminará entrenando y
evaluar modelos P × K (donde P es el número de iteraciones que utiliza), lo que puede
caro.

4.2.2 Cosas a tener en cuenta


Esté atento a lo siguiente cuando elija un protocolo de evaluación:

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

y conjuntos de validación. De hecho, estarás probando parte de tus datos de entrenamiento,


¡Que es lo peor que puedes hacer! Asegúrese de que su conjunto de entrenamiento y validación
conjunto son disjuntos.

Con licencia para <nulo>


Machine Translated by Google

Preprocesamiento de datos, ingeniería de funciones y aprendizaje de funciones 101

4.3 Preprocesamiento de datos, ingeniería de características y


aprendizaje de características
Además de la evaluación del modelo, una pregunta importante que debemos abordar antes de
profundizar en el desarrollo del modelo es la siguiente: ¿cómo se preparan los datos de entrada y
los objetivos antes de introducirlos en una red neuronal? Muchas técnicas de preprocesamiento de
datos e ingeniería de características son específicas de un dominio (por ejemplo, específicas de
datos de texto o de imágenes); Los cubriremos en los siguientes capítulos a medida que los
encontremos en ejemplos prácticos. Por ahora, revisaremos los conceptos básicos que son
comunes a todos los dominios de datos.

4.3.1 Preprocesamiento de datos para redes neuronales


El preprocesamiento de datos tiene como objetivo hacer que los datos sin procesar disponibles sean más
susceptibles a las redes neuronales. Esto incluye vectorización, normalización, manejo de valores faltantes y
extracción de características.

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 one­hot 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.

Con licencia para <nulo>


Machine Translated by Google

102 CAPÍTULO 4 Fundamentos del aprendizaje automático

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.

Esto es fácil de hacer con matrices Numpy:

x ­= x.media(eje=0) x /=
Suponiendo que x es una matriz de datos
x.std(eje=0)
2D de forma (muestras, características)

MANEJO DE VALORES FALTANTES

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.

4.3.2 Ingeniería de funciones


La ingeniería de características es el proceso de utilizar su propio conocimiento sobre los datos y sobre el
algoritmo de aprendizaje automático disponible (en este caso, una red neuronal) para hacer que el
algoritmo funcione mejor aplicando
transformaciones codificadas (no aprendidas)
a los datos antes. entra en el modelo. En Datos sin procesar:

muchos casos, no es razonable esperar que cuadrícula de píxeles

un modelo de aprendizaje automático pueda


aprender de datos completamente arbitrarios.
Los datos deben presentarse al modelo de Mejores {x1: 0,7, y1: {x1: 0,0, y2:
características:
una manera que facilite el trabajo del modelo. coordenadas de las
0,7} {x2: 1,0} {x2:
0,5, y2: 0,0} ­0,38, 2: 0,32}
manecillas del reloj

Veamos un ejemplo intuitivo. theta1: 45 theta1: 90


Características

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

una imagen de un reloj y generar la hora del


día (consulte la figura 4.3). Figura 4.3 Ingeniería de funciones para leer la hora en un reloj

Con licencia para <nulo>


Machine Translated by Google

Preprocesamiento de datos, ingeniería de funciones y aprendizaje de funciones 103

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.

Pero si ya entiendes el problema a un alto nivel (entiendes cómo


los humanos leen la hora en la esfera de un reloj), entonces se pueden crear características de entrada mucho mejores
para un algoritmo de aprendizaje automático: por ejemplo, es fácil escribir un reloj de cinco líneas
Secuencia de comandos de Python para seguir los píxeles negros de las manecillas del reloj y generar las coordenadas
(x, y) de la punta de cada manecilla. Entonces un simple algoritmo de aprendizaje automático puede aprender
para asociar estas coordenadas con la hora apropiada del día.
Puedes ir aún más lejos: hacer un cambio de coordenadas y expresar las coordenadas (x, y) como coordenadas
polares con respecto al centro de la imagen. Su aporte será
convertirse en el ángulo theta de cada manecilla del reloj. En este punto, tus rasgos están haciendo
el problema es tan sencillo que no se requiere aprendizaje automático; una simple operación de redondeo y una
búsqueda en el diccionario son suficientes para recuperar la hora aproximada del día.
Esa es la esencia de la ingeniería de características: facilitar un problema expresando
hacerlo de una manera más sencilla. Generalmente requiere comprender el problema en profundidad.
Antes del aprendizaje profundo, la ingeniería de características solía ser fundamental, porque la ingeniería clásica
Los algoritmos superficiales no tenían espacios de hipótesis lo suficientemente ricos como para aprender características útiles.
por ellos mismos. La forma en que presentó los datos al algoritmo fue esencial para su éxito. Por ejemplo, antes de
que las redes neuronales convolucionales tuvieran éxito en el
Al problema de clasificación de dígitos del MNIST , las soluciones generalmente se basaban en características codificadas como el

número de bucles en una imagen de dígitos, la altura de cada dígito en una

imagen, un histograma de valores de píxeles, etc.


Afortunadamente, el aprendizaje profundo moderno elimina la necesidad de la mayor parte de la ingeniería de
funciones, porque las redes neuronales son capaces de extraer automáticamente funciones útiles.
a partir de datos sin procesar. ¿Significa esto que no tiene que preocuparse por la ingeniería de funciones como
¿Siempre y cuando uses redes neuronales profundas? No, por dos razones:

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.

Con licencia para <nulo>


Machine Translated by Google

104 CAPÍTULO 4 Fundamentos del aprendizaje automático

4.4 Sobreajuste y desajuste


En los tres ejemplos del capítulo anterior (predicción de reseñas de películas, clasificación de temas y
regresión del precio de la vivienda), el rendimiento del modelo en los datos de validación retenidos siempre
alcanzó su punto máximo después de algunas épocas y luego comenzó a degradarse: el modelo
Rápidamente comenzó a sobreajustarse a los datos de entrenamiento. El sobreajuste ocurre en todos los
problemas de aprendizaje automático. Aprender a lidiar con el sobreajuste es esencial para dominar el
aprendizaje automático.
La cuestión fundamental en el aprendizaje automático es la tensión entre optimización y generalización.
La optimización se refiere al proceso de ajustar un modelo para obtener el mejor rendimiento posible con
los datos de entrenamiento (el aprendizaje en el aprendizaje automático), mientras que la generalización
se refiere a qué tan bien se desempeña el modelo entrenado con datos que nunca antes había visto. El
objetivo del juego es conseguir una buena generalización, por supuesto, pero no controlas la generalización;
solo puede ajustar el modelo en función de sus datos de entrenamiento.

Al comienzo del entrenamiento, la optimización y la generalización están correlacionadas: cuanto


menor es la pérdida de datos de entrenamiento, menor es la pérdida de datos de prueba. Mientras esto
sucede, se dice que su modelo no es adecuado: aún queda progreso por hacer; la red aún no ha modelado
todos los patrones relevantes en los datos de entrenamiento. Pero después de un cierto número de
iteraciones sobre los datos de entrenamiento, la generalización deja de mejorar y las métricas de validación
se estancan y luego comienzan a degradarse: el modelo está empezando a sobreajustarse. Es decir, está
empezando a aprender patrones que son específicos de los datos de entrenamiento pero que son
engañosos o irrelevantes cuando se trata de datos nuevos.
Para evitar que un modelo aprenda patrones engañosos o irrelevantes que se encuentran en los datos
de entrenamiento, la mejor solución es obtener más datos de entrenamiento. Un modelo entrenado con
más datos naturalmente se generalizará mejor. Cuando eso no es posible, la siguiente mejor solución es
modular la cantidad de información que su modelo puede almacenar o agregar restricciones sobre la
información que puede almacenar. Si una red sólo puede permitirse el lujo de memorizar una pequeña
cantidad de patrones, el proceso de optimización la obligará a centrarse en los patrones más destacados,
que tienen más posibilidades de generalizarse bien.
El proceso de lucha contra el sobreajuste de esta manera se llama regularización. Repasemos algunas
de las técnicas de regularización más comunes y apliquemoslas en la práctica para mejorar el modelo de
clasificación de películas de la sección 3.4.

4.4.1 Reducir el tamaño de la red


La forma más sencilla de evitar el sobreajuste es reducir el tamaño del modelo: la cantidad de parámetros
que se pueden aprender en el modelo (que está determinada por la cantidad de capas y la cantidad de
unidades por capa). En el aprendizaje profundo, la cantidad de parámetros que se pueden aprender en
un modelo a menudo se denomina capacidad del modelo. Intuitivamente, un modelo con más parámetros
tiene más capacidad de memorización y, por lo tanto, puede aprender fácilmente un mapeo perfecto
similar a un diccionario entre las muestras de entrenamiento y sus objetivos, un mapeo sin ningún poder
de generalización. Por ejemplo, se podría crear fácilmente un modelo con 500.000 parámetros binarios
para aprender la clase de cada dígito en el conjunto de entrenamiento MNIST :

Con licencia para <nulo>


Machine Translated by Google

Sobreajuste y desajuste 105

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.

Listado 4.3 Modelo original

desde keras importar modelos desde


keras importar capas

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'))

Ahora intentemos reemplazarlo con esta red más pequeña.

Listado 4.4 Versión del modelo con menor capacidad

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).

Con licencia para <nulo>


Machine Translated by Google

106 CAPÍTULO 4 Fundamentos del aprendizaje automático

Figura 4.4 Efecto de la capacidad del


modelo sobre la pérdida de validación: probar
un modelo más pequeño

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.

Listado 4.5 Versión del modelo con mayor capacidad

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.

Figura 4.5 Efecto de la capacidad del


modelo sobre la pérdida de validación:
probar un modelo más grande

Con licencia para <nulo>


Machine Translated by Google

Sobreajuste y desajuste 107

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).

Figura 4.6 Efecto de la capacidad del


modelo sobre la pérdida de
entrenamiento: probar un modelo más grande

4.4.2 Agregar regularización de peso


Quizás esté familiarizado con el principio de la navaja de Occam: dadas dos explicaciones para
algo, la explicación que tiene más probabilidades de ser correcta es la más simple: la que
hace menos suposiciones. Esta idea también se aplica a los modelos aprendidos por redes neuronales: dados algunos
datos de entrenamiento y una arquitectura de red, múltiples conjuntos de pesos
Los valores (múltiples modelos) podrían explicar los datos. Es menos probable que los modelos más simples se ajusten
demasiado que los complejos.
Un modelo simple en este contexto es un modelo donde la distribución de los valores de los parámetros
tiene menos entropía (o un modelo con menos parámetros, como vio en la sección anterior). Por lo tanto, una forma
común de mitigar el sobreajuste es imponer restricciones a la complejidad de una red obligando a sus pesos a tomar
sólo valores pequeños, lo que hace que
distribución de los valores de peso más regular. Esto se llama regularización de peso y es
Esto se hace agregando a la función de pérdida de la red un costo asociado con tener grandes
pesos. Este costo viene en dos sabores:

Regularización L1 : el costo agregado es proporcional al valor absoluto del


coeficientes de peso (la norma L1 de los pesos).

Regularización L2 : el costo agregado es proporcional al cuadrado del valor del


coeficientes de peso (la norma L2 de los pesos). La regularización L2 también se llama
Decaimiento de peso en el contexto de redes neuronales. No dejes que el nombre diferente te confunda: la
caída de peso es matemáticamente lo mismo que la regularización L2.

Con licencia para <nulo>


Machine Translated by Google

108 CAPÍTULO 4 Fundamentos del aprendizaje automático

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.

Listado 4.6 Agregar regularización de peso L2 al modelo

de regularizadores de importación de keras

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.

Figura 4.7 Efecto de la regularización


del peso L2 sobre la pérdida de validación

Como alternativa a la regularización L2 , puede utilizar uno de los siguientes regularizadores de peso de
Keras.

Listado 4.7 Diferentes regularizadores de peso disponibles en Keras

de regularizadores de importación de keras

regularizadores.l1(0.001) Regularización L1 L1 y L1 simultáneos


regularizadores.l1_l2(l1=0.001, l2=0.001) regularización L2

Con licencia para <nulo>


Machine Translated by Google

Sobreajuste y desajuste 109

4.4.3 Agregar abandono


El abandono es una de las técnicas de regularización de redes neuronales más efectivas y más utilizadas,
desarrollada por Geoff Hinton y sus estudiantes en la Universidad de Toronto. La eliminación, aplicada a una
capa, consiste en eliminar aleatoriamente (establecer en cero) una serie de características de salida de la capa
durante el entrenamiento. Digamos que una capa determinada normalmente devolvería un vector [0.2, 0.5, 1.3,
0.8, 1.1] para una muestra de entrada determinada durante el entrenamiento. Después de aplicar la eliminación,
este vector tendrá algunas entradas cero distribuidas aleatoriamente: por ejemplo, [0, 0,5, 1,3, 0, 1,1]. La tasa
de abandono es la fracción de las funciones que se eliminan a cero; normalmente se establece entre 0,2 y 0,5.
En el momento de la prueba, no se abandona ninguna unidad; en cambio, los valores de salida de la capa se
reducen mediante un factor igual a la tasa de abandono, para equilibrar el hecho de que hay más unidades
activas que en el momento del entrenamiento.

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:

salida_capa *= np.random.randint(0, alto=2, tamaño=salida_capa.forma)

En el momento del entrenamiento, abandona


el 50% de las unidades en la salida.

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):

salida_capa *= 0,5 en el momento de la prueba

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):

salida_capa *= np.random.randint(0, alto=2, tamaño=salida_capa.forma) salida_capa /= 0,5

Tenga en cuenta que en este caso estamos


en el momento del entrenamiento
ampliando y no reduciendo.

0.3 0,2 1.5 0.0 0.0 0,2 1.5 0.0


50%
0,6 0.1 0.0 0.3 de abandono
0,6 0.1 0.0 0.3 Figura 4.8 Abandono aplicado a una matriz de
*
2 activación en el momento del entrenamiento,
0,2 1.9 0.3 1.2 0.0 1.9 0.3 0.0
con un reescalamiento que se produce
durante el entrenamiento. En el momento de la
0,7 0,5 1.0 0.0 0,7 0.0 0.0 0.0
prueba, la matriz de activación no cambia.

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.

Con licencia para <nulo>


Machine Translated by Google

110 CAPÍTULO 4 Fundamentos del aprendizaje automático

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.

Listado 4.8 Agregar abandono a la red IMDB

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.

Figura 4.9 Efecto del abandono sobre


la pérdida de validación

En resumen, estas son las formas más comunes de evitar el sobreajuste en redes neuronales:

Obtenga más datos de entrenamiento.

Reducir la capacidad de la red.


Añadir regularización de peso.
Agregar abandono.

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.

Con licencia para <nulo>


Machine Translated by Google

El flujo de trabajo universal del aprendizaje automático 111

4.5 El flujo de trabajo universal del aprendizaje automático


En esta sección, presentaremos un modelo universal que puede utilizar para atacar y resolver cualquier
problema de aprendizaje automático. El plano une los conceptos que ha aprendido en este capítulo: definición
de problemas, evaluación, ingeniería de características y lucha contra el sobreajuste.

4.5.1 Definición del problema y armado de un conjunto de datos


Primero, debes definir el problema en cuestión:

¿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!

Con licencia para <nulo>


Machine Translated by Google

112 CAPÍTULO 4 Fundamentos del aprendizaje automático

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.

4.5.2 Elegir una medida de éxito


Para controlar algo, es necesario poder observarlo. Para lograr el éxito, debe definir lo que quiere decir con
éxito: ¿precisión? ¿Precisión y recuperación? ¿Tasa de retención de clientes? Su métrica de éxito guiará la
elección de una función de pérdida: lo que optimizará su modelo. Debe alinearse directamente con sus objetivos
de nivel superior, como el éxito de su negocio.

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.

4.5.3 Decidir sobre un protocolo de evaluación


Una vez que sepa cuál es su objetivo, debe establecer cómo medirá su progreso actual. Anteriormente hemos
revisado tres protocolos de evaluación comunes:

Mantener un conjunto de validación de reservas : el camino a seguir cuando tienes suficientes


datos

Realizar una validación cruzada K­fold: 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 K­fold: 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.

4.5.4 Preparando sus datos


Una vez que sepa en qué está entrenando, para qué está optimizando y cómo evaluar su enfoque, estará casi
listo para comenzar a entrenar modelos. Pero primero, debe formatear sus datos de una manera que pueda
introducirse en un modelo de aprendizaje automático; aquí, asumiremos una red neuronal profunda:

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].

Con licencia para <nulo>


Machine Translated by Google

El flujo de trabajo universal del aprendizaje automático 113

Si diferentes características toman valores en diferentes rangos (datos heterogéneos), entonces


los datos deben normalizarse.

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.

Con licencia para <nulo>


Machine Translated by Google

114 CAPÍTULO 4 Fundamentos del aprendizaje automático

Tabla 4.1 Elección de la función de activación y pérdida de última capa adecuada para su modelo

tipo de problema Activación de última capa Función de pérdida

Clasificación binaria sigmoideo entropía_cruzada_binaria

Clasificación multiclase y etiqueta única softmax categorical_crossentropy

Clasificación multiclase y multietiqueta sigmoideo entropía_cruzada_binaria

Regresión a valores arbitrarios Ninguno mse

Regresión a valores entre 0 y 1 sigmoide mse o binario_crossentropy

4.5.6 Ampliación: desarrollo de un modelo que se ajuste excesivamente

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.

4.5.7 Regularizando su modelo y ajustando sus hiperparámetros

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 .

Con licencia para <nulo>


Machine Translated by Google

El flujo de trabajo universal del aprendizaje automático 115

Pruebe diferentes hiperparámetros (como el número de unidades por capa o el


tasa de aprendizaje del optimizador) para encontrar la configuración óptima.
Opcionalmente, itere la ingeniería de funciones: agregue nuevas funciones o elimine funciones.
imágenes que no parecen ser informativas.

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 K­fold iterado
validación).

Con licencia para <nulo>


Machine Translated by Google

116 CAPÍTULO 4 Fundamentos del aprendizaje automático

Resumen del capítulo


Defina el problema en cuestión y los datos con los que entrenará. Recolectar
estos datos, o anotarlos con etiquetas si es necesario.

Elija cómo medirá el éxito de su problema. ¿Qué métricas monitoreará en sus datos de
validación?

Determine su protocolo de evaluación: ¿validación de reserva? ¿Validación K­fold? ¿Qué


parte de los datos debería utilizar para la validación?

Desarrollar un primer modelo que funcione mejor que una línea de base básica: un modelo
con poder estadístico.

Desarrollar un modelo que se

sobreajuste. Regularice su modelo y ajuste sus hiperparámetros, según el rendimiento de


los datos de validación. Gran parte de la investigación sobre aprendizaje automático tiende
a centrarse únicamente en este paso, pero tenga en cuenta el panorama general.

Con licencia para <nulo>


Machine Translated by Google

Parte 2

Aprendizaje profundo en la práctica

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.

Con licencia para <nulo>


Machine Translated by Google

Con licencia para <nulo>


Machine Translated by Google

Aprendizaje profundo
para visión por computadora

Este capítulo cubre


Comprensión de las redes neuronales convolucionales (convnets)

Uso del aumento de datos para mitigar el sobreajuste

Usar un convnet previamente entrenado para realizar la extracción


de características

Ajuste de una red convnet previamente entrenada

Visualizar lo que aprenden los convnets y cómo toman decisiones de


clasificación.

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

aplicar convnets a problemas de clasificación de imágenes, en particular

aquellos que involucran pequeños conjuntos de datos de entrenamiento, que son el caso de uso más común si

No somos una gran empresa de tecnología.

119

Con licencia para <nulo>


Machine Translated by Google

120 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

5.1 Introducción a las convnets


Estamos a punto de sumergirnos en la teoría de qué son las convnets y por qué han sido tan
exitoso en tareas de visión por computadora. Pero primero, echemos un vistazo práctico a un ejemplo
sencillo de conv­net. Utiliza un convnet para clasificar dígitos MNIST , una tarea que realizamos en el capítulo
2 utilizando una red densamente conectada (la precisión de nuestra prueba en ese momento fue del 97,8%). A pesar de
el convnet será básico, su precisión superará a la de los densamente
modelo conectado del capítulo 2.
Las siguientes líneas de código le muestran cómo se ve una convnet básica. es una pila de
Capas Conv2D y MaxPooling2D . Verás en un minuto exactamente lo que hacen.

Listado 5.1 Creación de instancias de una pequeña convnet

desde keras importan capas


de modelos importados de keras

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 #
==================================================== ===============

conv2d_1 (Conv2D) (Ninguno, 26, 26, 32) 320


________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (Ninguno, 13, 13, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (Ninguno, 11, 11, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (Ninguno, 5, 5, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (Ninguno, 3, 3, 64) 36928
==================================================== ===============

Parámetros totales: 55.744


Parámetros entrenables: 55,744
Parámetros no entrenables: 0

Puede ver que la salida de cada capa Conv2D y MaxPooling2D es un tensor 3D de


forma (alto, ancho, canales). Las dimensiones de ancho y alto tienden a reducirse.

Con licencia para <nulo>


Machine Translated by Google

Introducción a las convnets 121

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.

Listado 5.2 Agregar un clasificador encima de convnet

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()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

conv2d_1 (Conv2D) (Ninguno, 26, 26, 32) 320


________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (Ninguno, 13, 13, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (Ninguno, 11, 11, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (Ninguno, 5, 5, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (Ninguno, 3, 3, 64) 36928
________________________________________________________________
flatten_1 (Aplanar) (Ninguno, 576) 0
________________________________________________________________
denso_1 (denso) (Ninguno, 64) 36928
________________________________________________________________
denso_2 (denso) (Ninguno, 10) 650
==================================================== ===============

Parámetros totales: 93,322


Parámetros entrenables: 93,322
Parámetros no entrenables: 0

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.

Listado 5.3 Entrenamiento del convnet en imágenes MNIST

desde keras.datasets importar mnist


desde keras.utils importar a_categorical

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

Con licencia para <nulo>


Machine Translated by Google

122 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

imágenes_tren = imágenes_tren.reshape((60000, 28, 28, 1)) imágenes_tren =


imágenes_tren.astype('float32') / 255

test_images = test_images.reshape((10000, 28, 28, 1)) test_images =


test_images.astype('float32') / 255

etiquetas_tren = to_categorical(etiquetas_tren) etiquetas_prueba =


to_categorical(etiquetas_prueba)

model.compile(optimizador='rmsprop',
pérdida='categorical_crossentropy',
métricas=['precisión'])
model.fit(train_images, train_labels, épocas=5, tamaño_lote=64)

Evaluamos el modelo con los datos de prueba:

>>> test_loss, test_acc = model.evaluate(test_images, test_labels) >>> test_acc 0.99080000000000001

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 .

5.1.1 La operación de convolución


La diferencia fundamental entre una capa densamente conectada y una capa convolucional es la siguiente: las
capas densas aprenden patrones globales en su espacio de características de entrada (por ejemplo, para un
dígito MNIST , patrones que involucran todos los píxeles), mientras que las capas convolucionales aprenden
patrones locales (ver figura 5.1): en el caso de imágenes, patrones que se encuentran en pequeñas ventanas
2D de las entradas. En el ejemplo anterior, estas ventanas eran todas de 3 × 3.

Figura 5.1 Las imágenes se pueden


dividir en patrones locales como bordes,
texturas, etc.

Con licencia para <nulo>


Machine Translated by Google

Introducción a las convnets 123

Esta característica clave le da a los convnets dos propiedades interesantes:

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"

Figura 5.2 El mundo visual forma una jerarquía espacial de módulos


visuales: los bordes hiperlocales se combinan en objetos locales como
ojos u oídos, que se combinan en conceptos de alto nivel como "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

Con licencia para <nulo>


Machine Translated by Google

124 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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.

En el ejemplo de MNIST , la primera capa de convolución toma un mapa de características de tamaño


(28, 28, 1) y genera un mapa de características de tamaño (26, 26, 32): calcula 32 filtros sobre su entrada.
Cada uno de estos 32 canales de salida contiene una cuadrícula de valores de 26 × 26, que es un mapa
de respuesta del filtro sobre la entrada, que indica la respuesta de ese patrón de filtro en diferentes
ubicaciones de la entrada (ver figura 5.3). Eso es lo que significa el término mapa de características : cada
dimensión en el eje de profundidad es una característica (o filtro), y la salida del tensor 2D [:, :, n] es el
mapa espacial 2D de la respuesta de este filtro sobre la entrada.

Mapa de
respuesta, que cuantifica la
presencia del patrón del
Entrada original filtro en diferentes ubicaciones.

Filtro único

Figura 5.3 El concepto de mapa


de respuesta: un mapa 2D de la
presencia de un patrón en diferentes
ubicaciones de una entrada

Las convoluciones están definidas por dos parámetros clave:

Tamaño de los parches extraídos de las entradas: normalmente son 3 × 3 o 5 × 5. En el ejemplo,


eran 3 × 3, que es una opción común.
Profundidad del mapa de características de salida: el número de filtros calculados por la
convolución. El ejemplo comenzó con una profundidad de 32 y terminó con una profundidad de 64.

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 [i­1:i+1, j­1:j+1, :]. El proceso completo se detalla en
la figura 5.4.

Con licencia para <nulo>


Machine Translated by Google

Introducción a las convnets 125

Ancho Altura

Profundidad Mapa de características de entrada


de entrada

3 × 3 parches de entrada

Producto escalar
con núcleo

Profundidad
Parches transformados
de salida

Mapa de características de salida


Profundidad

de salida

Figura 5.4 Cómo funciona la convolución

Tenga en cuenta que el ancho y el alto de salida pueden diferir del ancho y alto de entrada.
Pueden diferir por dos razones:

Efectos de borde, que se pueden contrarrestar rellenando el mapa de características de


entrada . El uso de zancadas, que definiré en un segundo.

Echemos un vistazo más profundo a estas nociones.

ENTENDIENDO LOS EFECTOS DE FRONTERA Y EL RELLENO

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.

Con licencia para <nulo>


Machine Translated by Google

126 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.5 Ubicaciones válidas de parches de 3 × 3 en un mapa de características de entrada de 5 × 5

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.

Figura 5.6 Rellenar una entrada de 5 × 5 para poder extraer 25 parches de 3 × 3

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".

Con licencia para <nulo>


Machine Translated by Google

Introducción a las convnets 127

ENTENDIENDO LOS AVANCES DE CONVOLUCIÓN

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

Figura 5.7 Parches de convolución de 3 × 3 con zancadas de 2 × 2

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.

5.1.2 La operación de agrupación máxima

En el ejemplo de convnet, habrás notado que el tamaño de los mapas de características es


reducido a la mitad después de cada capa MaxPooling2D . Por ejemplo, antes de las primeras capas MaxPooling2D , el
mapa de características es 26 × 26, pero la operación de agrupación máxima lo reduce a la mitad a 13 × 13.
Ésa es la función de la agrupación máxima: reducir agresivamente la resolución de los mapas de características, como
circunvoluciones zancadas.

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

Con licencia para <nulo>


Machine Translated by Google

128 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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'))

Aquí hay un resumen del modelo:

>>> modelo_no_max_pool.summary()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

conv2d_4 (Conv2D) (Ninguno, 26, 26, 32) 320


________________________________________________________________
conv2d_5 (Conv2D) (Ninguno, 24, 24, 64) 18496
________________________________________________________________
conv2d_6 (Conv2D) (Ninguno, 22, 22, 64) 36928
==================================================== ===============

Parámetros totales: 55.744


Parámetros entrenables: 55,744
Parámetros no entrenables: 0

¿Qué hay de malo en esta configuración? Dos cosas:

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

Con licencia para <nulo>


Machine Translated by Google

Introducción a las convnets 129

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.

Con licencia para <nulo>


Machine Translated by Google

130 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

5.2 Entrenar un convnet desde cero en un pequeño conjunto de datos


Tener que entrenar un modelo de clasificación de imágenes usando muy pocos datos es una situación común, que
probablemente encontrarás en la práctica si alguna vez practicas visión por computadora en un
contexto profesional. Unas "pocas" muestras pueden significar desde unos pocos cientos hasta un
unas pocas decenas de miles de imágenes. Como ejemplo práctico, nos centraremos en clasificar
imágenes como perros o gatos, en un conjunto de datos que contiene 4.000 imágenes de perros y gatos (2.000
gatos, 2.000 perros). Usaremos 2000 imágenes para el entrenamiento, 1000 para la validación y
1.000 para las pruebas.
En esta sección, revisaremos una estrategia básica para abordar este problema: capacitar a un nuevo
modele desde cero utilizando los pocos datos que tenga. Comenzarás entrenando ingenuamente a un
pequeña convnet en las 2000 muestras de entrenamiento, sin ninguna regularización, para establecer una línea de base
de lo que se puede lograr. Esto le llevará a una precisión de clasificación del 71%. En
En ese momento, el problema principal será el sobreajuste. Luego presentaremos el aumento de datos, una
poderosa técnica para mitigar el sobreajuste en la visión por computadora. Al utilizar el aumento de datos, mejorará la red
hasta alcanzar una precisión del 82 %.
En la siguiente sección, revisaremos dos técnicas más esenciales para aplicar técnicas profundas.
aprender a conjuntos de datos pequeños: extracción de características con una red previamente entrenada (que le permitirá
con una precisión del 90% al 96%) y ajustar una red previamente entrenada (esto lo llevará a un
precisión final del 97%). Juntas, estas tres estrategias (entrenar un modelo pequeño a partir de
scratch, extraer características utilizando un modelo previamente entrenado y ajustar un modelo previamente entrenado
constituirán su futura caja de herramientas para abordar el problema de realizar la clasificación de imágenes con pequeños
conjuntos de datos.

5.2.1 La relevancia del aprendizaje profundo para problemas de datos pequeños

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 conv­nets 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.

y reutilizarlo en un problema significativamente diferente con solo cambios menores. Específicamente,

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 131

En el caso de la visión por computadora, muchos modelos previamente entrenados (generalmente entrenados en el
conjunto de datos Image­Net) 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.

5.2.2 Descarga de los datos


El conjunto de datos de Dogs vs. Cats que utilizará no está incluido en Keras. Kaggle lo puso a disposición como
parte de una competencia de visión por computadora a fines de 2013, cuando
Los convnets no eran la corriente principal. Puede descargar el conjunto de datos original desde www.kaggle
.com/c/perros­vs­gatos/data (deberá crear una cuenta de Kaggle si aún no lo ha hecho
tener uno, no se preocupe, el proceso es indoloro).
Las imágenes son archivos JPEG en color de resolución media . La figura 5.8 muestra algunos ejemplos.

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.

Con licencia para <nulo>


Machine Translated by Google

132 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

A continuación se muestra el código para hacer esto.

Listado 5.4 Copiar imágenes a directorios de entrenamiento, validación y prueba

Ruta al directorio donde se descomprimió el Directorio donde almacenarás


conjunto de datos original tu conjunto de datos más pequeño

importar sistema operativo,shutil

original_dataset_dir = '/Usuarios/fchollet/Descargas/kaggle_original_data'

base_dir = '/Usuarios/fchollet/Descargas/gatos_y_perros_pequeños'
os.mkdir(base_dir)

train_dir = os.path.join(base_dir, 'tren')


Directorios para las
os.mkdir(tren_dir)
validation_dir = os.path.join(base_dir, 'validación') divisiones de
formación, validación
os.mkdir(validación_dir)
y pruebas.
test_dir = os.path.join(base_dir, 'prueba')
os.mkdir(test_dir)

train_cats_dir = os.path.join(train_dir, 'gatos') Directorio con fotos


os.mkdir(tren_gatos_dir) de gatos adiestrando

train_dogs_dir = os.path.join(train_dir, 'perros')


Directorio con fotos
os.mkdir(entren_perros_dir) de perros de adiestramiento.

validation_cats_dir = os.path.join(validation_dir, 'gatos')


Directorio con
os.mkdir(validación_cats_dir) imágenes de gatos de validación.

validation_dogs_dir = os.path.join(validation_dir, 'perros')


Directorio con fotos
os.mkdir(validación_perros_dir) de perros de validación.

test_cats_dir = os.path.join(test_dir, 'gatos')


Directorio con imágenes de gatos de prueba.
os.mkdir(test_cats_dir)

test_dogs_dir = os.path.join(test_dir, 'perros')


Directorio con fotos de perros de prueba.
os.mkdir(test_dogs_dir)

fnames = ['cat.{}.jpg'.format(i) para i en el rango(1000)]


para fname en fnames: Copia las primeras

src = os.path.join(original_dataset_dir, fname) 1000 imágenes de gatos

dst = os.path.join(train_cats_dir, fname) en train_cats_dir

shutil.copyfile(src,dst)

fnames = ['cat.{}.jpg'.format(i) para i en el rango(1000, 1500)]


para fname en fnames: Copia las siguientes 500
src = os.path.join(original_dataset_dir, fname) imágenes de gatos
dst = os.path.join(validation_cats_dir, fname) en validation_cats_dir
shutil.copyfile(src,dst)

fnames = ['cat.{}.jpg'.format(i) para i en el rango(1500, 2000)]


para fname en fnames: Copia las siguientes 500
src = os.path.join(original_dataset_dir, fname) imágenes de gatos
dst = os.path.join(test_cats_dir, fname) en test_cats_dir
shutil.copyfile(src,dst)

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 133

fnames = ['dog.{}.jpg'.format(i) para i en el rango(1000)] para fname en fnames:


Copia las primeras
src = os.path.join(original_dataset_dir, fname) dst = 1000 imágenes de
os.path.join(train_dogs_dir, fname)shutil.copyfile(src, dst) perros en train_dogs_dir

fnames = ['dog.{}.jpg'.format(i) para i en el rango(1000, 1500)] para fname en fnames:


Copia las siguientes 500
src = os.path.join(original_dataset_dir, fname) dst = imágenes de
os.path.join(validation_dogs_dir, fname)shutil.copyfile(src, dst) perros en validation_dogs_dir

fnames = ['dog.{}.jpg'.format(i) para i en rango(1500, 2000)] para fname en fnames:


Copia las siguientes 500
src = os.path.join(original_dataset_dir, fname) dst = imágenes de
os.path.join(test_dogs_dir, fname)shutil.copyfile(src, dst) perros en test_dogs_dir

Como control de cordura, contemos cuántas imágenes hay en cada división de entrenamiento (entrenamiento/
validación/prueba):

>>> print('imágenes totales de gatos de entrenamiento:', len(os.listdir(train_cats_dir))) imágenes totales de


gatos de entrenamiento: 1000 >>>
print('imágenes totales de perros de entrenamiento:', len(os.listdir(train_dogs_dir) )) imágenes totales de perros
de entrenamiento: 1000 >>> print('imágenes
totales de gatos de validación:', len(os.listdir(validation_cats_dir))) imágenes totales de gatos de validación: 500 >>>
print('imágenes totales de perros de validación:',
len(os.listdir(validation_dogs_dir))) imágenes totales de perros de validación: 500 >>> print('imágenes totales de gatos
de prueba:', len(os.listdir(test_cats_dir)))
imágenes totales de gatos de prueba: 500 >>> print( 'total de imágenes de perros de prueba:',
len(os.listdir(test_dogs_dir))) total de
imágenes de perros de prueba: 500

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.

5.2.3 Construyendo su red


Construiste una pequeña convnet para MNIST en el ejemplo anterior, por lo que deberías estar familiarizado con
dichas convnets. Reutilizarás la misma estructura general: convnet será una pila de capas Conv2D (con activación
relu ) y MaxPooling2D alternadas .
Pero debido a que se trata de imágenes más grandes y un problema más complejo, hará que su red sea
más grande en consecuencia: tendrá una etapa Conv2D + MaxPooling2D más . Esto sirve tanto para aumentar
la capacidad de la red como para reducir aún más el tamaño de los mapas de características para que no sean
demasiado grandes cuando llegue a la capa Aplanar . Aquí, debido a que comienza con entradas de tamaño 150
× 150 (una elección un tanto arbitraria), termina con mapas de características de tamaño 7 × 7 justo antes de la
capa Aplanar .

Con licencia para <nulo>


Machine Translated by Google

134 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

NOTA La profundidad de los mapas de características aumenta progresivamente en la red.


(de 32 a 128), mientras que el tamaño de los mapas de características disminuye (de 148 ×
148 a 7×7). Este es un patrón que verás en casi todas las convnets.

Como estás atacando un problema de clasificación binaria, terminarás la red con un


unidad única (una capa densa de tamaño 1) y una activación sigmoidea . Esta unidad codificará el
probabilidad de que la red esté mirando una clase u otra.

Listado 5.5 Creación de instancias de una pequeña convnet para la clasificación de perros versus gatos

desde keras importan capas


de modelos importados de keras

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()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

conv2d_1 (Conv2D) (Ninguno, 148, 148, 32) 896


________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (Ninguno, 74, 74, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (Ninguno, 72, 72, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (Ninguno, 36, 36, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (Ninguno, 34, 34, 128) 73856
________________________________________________________________
maxpooling2d_3 (MaxPooling2D) (Ninguno, 17, 17, 128) 0
________________________________________________________________
conv2d_4 (Conv2D) (Ninguno, 15, 15, 128) 147584
________________________________________________________________
maxpooling2d_4 (MaxPooling2D) (Ninguno, 7, 7, 128) 0
________________________________________________________________
flatten_1 (Aplanar) (Ninguno, 6272) 0
________________________________________________________________
denso_1 (denso) (Ninguno, 512) 3211776
________________________________________________________________

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 135

denso_2 (denso) (Ninguno, 1) 513


==================================================== ===============

Parámetros totales: 3.453.121


Parámetros entrenables: 3,453,121
Parámetros no entrenables: 0

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).

Listado 5.6 Configurando el modelo para entrenamiento

de optimizadores de importación de keras

model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=1e­4), métricas=['acc'])

5.2.4 Preprocesamiento de datos


Como ya sabe, los datos deben formatearse en tensores de punto flotante preprocesados adecuadamente antes
de introducirlos en la red. Actualmente, los datos se encuentran en una unidad como archivos JPEG , por lo que
los pasos para ingresarlos a la red son aproximadamente los siguientes:

1 Lea los archivos de imágenes.


2 Decodifica el contenido JPEG en cuadrículas de píxeles RGB .
3 Conviértalos en tensores de punto flotante.
4 Cambie la escala de los valores de píxeles (entre 0 y 255) al intervalo [0, 1] (como sabe,
las redes neuronales prefieren trabajar con valores de entrada pequeños).

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í.

Listado 5.7 Usando ImageDataGenerator para leer imágenes de directorios

desde keras.preprocessing.image importar ImageDataGenerator

train_datagen = ImageDataGenerator(reescalar=1./255) test_datagen = Cambia la escala de todas las imágenes en 1/255


ImageDataGenerator(reescalar=1./255)

train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150, 150)


lote_size=20,
class_mode='binario') Cambia el tamaño de todas las imágenes a 150 × 150
Directorio
de destino
Debido a que utiliza

validation_generator = test_datagen.flow_from_directory ( la pérdida de


binario_crossentropy,
dir_validación,
necesita etiquetas binarias.

Con licencia para <nulo>


Machine Translated by Google

136 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

target_size=(150, 150), lote_size=20,


class_mode='binario')

Comprender los generadores de Python Un


generador de Python es un objeto que actúa como un iterador: es un objeto que puedes usar con el
operador for... in . Los generadores se construyen utilizando el operador de rendimiento .

A continuación se muestra un ejemplo de un generador que produce números enteros:

generador def(): i=0


mientras
que Verdadero: i
+= 1
produce i

para elemento en generador():


imprimir(elemento)
si elemento > 4:
romper

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:

>>> para data_batch, etiquetas_batch en train_generator: print('forma de lote de


>>> datos:', data_batch.shape) print('forma de lote de etiquetas:',
>>> etiquetas_batch.shape) break
>>>
forma del lote de datos: (20, 150, 150, 3) forma del lote
de etiquetas: (20,)

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

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 137

pasos_per_epoch pasos de descenso de gradiente: el proceso de ajuste pasará a la siguiente


época. En este caso, los lotes son 20 muestras, por lo que se necesitarán 100 lotes hasta que
alcance su objetivo de 2000 muestras.
Cuando usas fit_generator, puedes pasar un argumento validation_data , al igual que con el método fit . Es
importante tener en cuenta que se permite que este argumento sea un generador de datos, pero también podría ser
una tupla de matrices Numpy. Si pasa un generador como validation_data, entonces se espera que este generador
produzca lotes de datos de validación sin fin; por lo tanto, también debe especificar el argumento validation_steps ,
que le indica al proceso cuántos lotes extraer del generador de validación para su evaluación.

Listado 5.8 Ajuste del modelo usando un generador por lotes

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.

Listado 5.9 Guardando el modelo

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).

Listado 5.10 Visualización de curvas de pérdida y precisión durante el entrenamiento

importar matplotlib.pyplot como plt

acc = historia.historia['acc'] val_acc =


historia.historia['val_acc'] pérdida = historia.historia['pérdida']
val_loss = historia.historia['val_loss']

épocas = rango(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b',


label='Validation acc') plt.title('Precisión de entrenamiento y validación') plt .leyenda()

plt.figura()

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs,


val_loss, 'b', label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación')
plt .leyenda()

plt.mostrar()

Con licencia para <nulo>


Machine Translated by Google

138 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.9 Precisión de


capacitación y validación

Figura 5.10 Pérdida de


entrenamiento y validación

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.

5.2.5 Uso del aumento de datos


El sobreajuste se debe a tener muy pocas muestras de las que aprender, lo que le impide
entrenar un modelo que pueda generalizarse a nuevos datos. Dados infinitos datos, su modelo

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 139

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.

Listado 5.11 Configurar una configuración de aumento de datos a través de ImageDataGenerator

generador de datos = ImageDataGenerator(


rango_rotación=40,
rango_cambio_ancho=0.2,
rango_cambio_alto=0.2,
rango_cortante=0.2,
rango_zoom=0.2,
giro_horizontal=Verdadero,
modo_relleno='más cercano')

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:

rango_rotación es un valor en grados (0–180), un rango dentro del cual


rotar imágenes con frecuencia.

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.

shear_range es para aplicar aleatoriamente transformaciones de corte. zoom_range sirve

para hacer zoom dentro de las imágenes de forma aleatoria.

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.

Miremos las imágenes aumentadas (ver figura 5.11).

Listado 5.12 Mostrando algunas imágenes de entrenamiento aumentadas aleatoriamente

desde keras.preprocesamiento importar imagen


Módulo con utilidades de
preprocesamiento de imágenes.
nombresf = [os.path.join(train_cats_dir, nombref) para
fname en os.listdir(train_cats_dir)]

img_path = nombresf[3] Elige una imagen para aumentar

img = imagen.load_img(img_path, target_size=(150, 150))


Lee la imagen y la
cambia de tamaño.

Con licencia para <nulo>


Machine Translated by Google

140 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

x = imagen.img_to_array(img) Lo convierte en una matriz Numpy con forma (150, 150, 3)

x = x.reformar((1,) + x.forma) Le da nueva forma a (1, 150, 150, 3)

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()

Figura 5.11 Generación de imágenes de gatos mediante aumento de datos aleatorios

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.

Con licencia para <nulo>


Machine Translated by Google

Entrenando un convnet desde cero en un pequeño conjunto de datos 141

Listado 5.13 Definiendo una nueva convnet que incluye abandono

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=1e­4), métricas=['acc'])

Entrenemos la red utilizando el aumento y la deserción de datos.

Listado 5.14 Entrenando el convnet usando generadores de aumento de datos

train_datagen = Generador de datos de imagen (


reescalar = 1./255,
rango de rotación = 40,
rango de cambio de ancho = 0,2,
rango de cambio de altura = 0,2,
rango de corte = 0,2,
¡Tenga en cuenta
rango de zoom = 0,2,
que los datos de
giro_horizontal = Verdadero,)
validación no
test_datagen = ImageDataGenerator (reescala = 1./255) deben aumentarse!

train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150,


150),
Directorio
lote_size=32, class_mode='binario') Cambia el tamaño de todas las imágenes a 150 × 150
de destino

Debido a que utiliza


validation_generator = test_datagen.flow_from_directory ( la pérdida de
validation_dir, binario_crossentropy,
necesita etiquetas binarias.
target_size=(150, 150), lote_size=32,
class_mode='binario')

historial = model.fit_generator(train_generator,
pasos_por_epoch=100,
épocas=100,

validation_data=validation_generator, validation_steps=50)

Con licencia para <nulo>


Machine Translated by Google

142 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Guardemos el modelo; lo usará en la sección 5.4.

Listado 5.15 Guardando el modelo

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.

Figura 5.12 Precisión de entrenamiento y


validación con aumento de datos

Figura 5.13 Pérdida de entrenamiento y


validación con aumento de datos

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.

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 143

5.3 Usando un convnet previamente entrenado


Un enfoque común y muy eficaz para el aprendizaje profundo en conjuntos de datos de imágenes
pequeños es utilizar una red previamente entrenada. Una red previamente entrenada es una red guardada
que se entrenó previamente en un gran conjunto de datos, generalmente en una tarea de clasificación de
imágenes a gran escala. Si este conjunto de datos original es lo suficientemente grande y general,
entonces la jerarquía espacial de las características aprendidas por la red previamente entrenada puede
actuar efectivamente como un modelo genérico del mundo visual y, por lo tanto, sus características
pueden resultar útiles para muchos problemas diferentes de visión por computadora. , aunque estos
nuevos problemas pueden involucrar clases completamente diferentes a las de la tarea original. Por
ejemplo, podría entrenar una red en ImageNet (donde las clases son principalmente animales y objetos
cotidianos) y luego reutilizar esta red entrenada para algo tan remoto como identificar muebles en
imágenes. Esta portabilidad de las características aprendidas entre diferentes problemas es una ventaja
clave del aprendizaje profundo en comparación con muchos enfoques más antiguos de aprendizaje
superficial, y hace que el aprendizaje profundo sea muy eficaz para problemas de datos pequeños.
En este caso, consideremos una gran convnet entrenada en el conjunto de datos de ImageNet (1,4
millones de imágenes etiquetadas y 1000 clases diferentes). ImageNet contiene muchas clases de
animales, incluidas diferentes especies de gatos y perros, por lo que se puede esperar un buen desempeño
en el problema de clasificación de perros versus gatos.
Utilizarás la arquitectura VGG16 , desarrollada por Karen Simonyan y Andrew Zisserman en 2014; es
una arquitectura convnet simple y ampliamente utilizada para ImageNet.1 Aunque es un modelo más
antiguo, alejado del estado actual del arte y algo más pesado que muchos otros modelos recientes, lo elegí
porque su arquitectura es similar a la que ya conoces. con y es fácil de entender sin introducir ningún
concepto nuevo. Este puede ser su primer encuentro con uno de estos nombres de modelos cursis: VGG,
ResNet, Inception, Inception­ResNet, Xception, etc.; Te acostumbrarás a ellos, porque aparecerán con
frecuencia si continúas haciendo aprendizaje profundo para la visión por computadora.

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.

5.3.1 Extracción de características

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.

Con licencia para <nulo>


Machine Translated by Google

144 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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).

Predicción Predicción Predicción

Entrenado Entrenado Nuevo clasificador


clasificador clasificador (inicializado aleatoriamente)

Base Base Base


convolucional convolucional convolucional
entrenada entrenada

entrenada (congelada)

Aporte Aporte Aporte

Figura 5.14 Intercambio de clasificadores manteniendo la misma base convolucional

¿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.

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 145

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 .

Listado 5.16 Creación de instancias de la base convolucional VGG16

desde keras.applications importe VGG16

conv_base = VGG16(pesos='imagenet', include_top=False,


input_shape=(150, 150, 3))

Pasas tres argumentos al constructor:

pesos especifica el punto de control de peso desde el cual inicializar el modelo.


include_top se refiere a incluir (o no) el clasificador densamente conectado en la parte superior
de la red. De forma predeterminada, este clasificador densamente conectado corresponde
a las 1000 clases de ImageNet. Debido a que pretende utilizar su propio clasificador
densamente conectado (con sólo dos clases: gato y perro), no es necesario incluirlo.

input_shape es la forma de los tensores de imagen que alimentará a la red.


Este argumento es puramente opcional: si no lo pasa, la red podrá procesar entradas de
cualquier tamaño.
Aquí está el detalle de la arquitectura de la base convolucional VGG16 . Es similar a

los convnets simples con los que ya estás familiarizado:

>>> conv_base.summary()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

entrada_1 (capa de entrada) (Ninguno, 150, 150, 3) 0

Con licencia para <nulo>


Machine Translated by Google

146 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

________________________________________________________________
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
==================================================== ===============

Parámetros totales: 14.714.688


Parámetros entrenables: 14,714,688
Parámetros no entrenables: 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:

Ejecutar la base convolucional sobre su conjunto de datos, registrando su salida en un


Numpy array en disco, y luego usar estos datos como entrada para un sistema independiente y densamente
clasificador conectado similar a los que vio en la parte 1 de este libro. Esta solución
Es rápido y económico de ejecutar, porque solo requiere ejecutar el convolucional.
base una vez para cada imagen de entrada, y la base convolucional es, con diferencia, la más
parte costosa del oleoducto. Pero por la misma razón, esta técnica no
le permite utilizar el aumento de datos.

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 147

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.

EXTRACCIÓN RÁPIDA DE FUNCIONES SIN AUMENTO DE DATOS


Comenzará ejecutando instancias del ImageDataGenerator introducido anteriormente para
extraer imágenes como matrices Numpy, así como sus etiquetas. Extraerá características de
estas imágenes llamando al método de predicción del modelo conv_base .

Listado 5.17 Extracción de características usando la base convolucional previamente entrenada

importar sistema
operativo importar numpy como
np desde keras.preprocessing.image importar ImageDataGenerator

base_dir = '/Users/fchollet/Downloads/cats_and_dogs_small' train_dir = os.path.join(base_dir, 'train')


validation_dir = os.path.join(base_dir, 'validation') test_dir = os.path.join(base_dir,
'prueba')

datagen = ImageDataGenerator(rescale=1./255) tamaño_lote = 20

def extract_features(directorio, sample_count):


características = np.zeros(forma=(sample_count, 4, 4, 512)) etiquetas =
np.zeros(forma=(sample_count)) generador = datagen.flow_from_directory(

Tenga en cuenta que debido a que los


directorio, generadores generan datos de forma indefinida
tamaño_objetivo=(150, 150), en un bucle, debe interrumpir después
tamaño_lote=tamaño_lote, de que cada imagen se haya visto una vez.
modo_clase='binario')
yo=0
para inputs_batch, etiquetas_batch en el generador:
características_batch = conv_base.predict(inputs_batch) características[i * tamaño_lote:
(i + 1) * tamaño_lote] = características_lote_etiquetas[i * tamaño_lote: (i + 1) * tamaño_lote] = etiquetas_lote i += 1

si i * tamaño_lote >= recuento_muestra:


romper
características de devolución, etiquetas

train_features, train_labels = extraer_features(train_dir, 2000) validation_features, validation_labels =


extraer_features(validation_dir, 1000) test_features, test_labels = extraer_features(test_dir, 1000)

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):

Con licencia para <nulo>


Machine Translated by Google

148 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

train_features = np.reshape(train_features, (2000, 4*4* 512)) validation_features =


np.reshape(validation_features, (1000, 4*4* 512)) test_features = np.reshape(test_features, (1000, 4*4 * 512))

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.

Listado 5.18 Definición y entrenamiento del clasificador densamente conectado

desde keras importar modelos desde


keras importar capas desde keras
importar optimizadores

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=2e­5),
pérdida='binary_crossentropy',
métricas=['acc'])

historial = model.fit(train_features, train_labels, épocas=30, lote_size=20,

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).

Listado 5.19 Trazado de los resultados

importar matplotlib.pyplot como plt

acc = historia.historia['acc'] val_acc =


historia.historia['val_acc'] pérdida = historia.historia['pérdida']
val_loss = historia.historia['val_loss']

épocas = rango(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b',


label='Validation acc') plt.title('Precisión de entrenamiento y validación') plt .leyenda()

plt.figura()

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs,


val_loss, 'b', label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación')
plt .leyenda()

plt.mostrar()

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 149

Figura 5.15 Precisión de entrenamiento y


validación para extracción de características simples

Figura 5.16 Pérdida de entrenamiento y


validación para extracción de características simples

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.

EXTRACCIÓN DE FUNCIONES CON AUMENTO DE DATOS


Ahora, revisemos la segunda técnica que mencioné para realizar la extracción de características.
que es mucho más lento y más costoso, pero que le permite usar el aumento de datos durante el
entrenamiento: extender el modelo conv_base y ejecutarlo de un extremo a otro
las entradas.

NOTA Esta técnica es tan costosa que sólo debes intentarla si


tener acceso a una GPU: es absolutamente intratable en la CPU. Si no puedes ejecutar tu
código en GPU, entonces la técnica anterior es el camino a seguir.

Con licencia para <nulo>


Machine Translated by Google

150 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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.

Listado 5.20 Agregar un clasificador densamente conectado encima de la base convolucional

de modelos importados de keras


desde keras importan capas

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'))

Así es como se ve el modelo ahora:

>>> modelo.resumen()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

vgg16 (modelo) (Ninguno, 4, 4, 512) 14714688


________________________________________________________________
flatten_1 (Aplanar) (Ninguno, 8192) 0
________________________________________________________________
denso_1 (denso) (Ninguno, 256) 2097408
________________________________________________________________
denso_2 (denso) (Ninguno, 1) 257
==================================================== ===============

Parámetros totales: 16.812.353


Parámetros entrenables: 16,812,353
Parámetros no entrenables: 0

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ó.

En Keras, congelas una red estableciendo su atributo entrenable en False:

>>> print('Este es el número de pesos entrenables '


'antes de congelar la base de conversiones:', len(model.trainable_weights))
Este es el número de pesos entrenables antes de congelar la base de conv: 30
>>> conv_base.trainable = Falso
>>> print('Este es el número de pesos entrenables '
'después de congelar la base de conversiones:', len(model.trainable_weights))
Este es el número de pesos entrenables después de congelar la base de conv: 4

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 151

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

desde keras.preprocessing.image importar ImageDataGenerator desde keras importar


optimizadores

train_datagen = Generador de datos de imagen (


reescalar=1./255,
rango_rotación=40,
rango_cambio_ancho=0.2,
¡Tenga en cuenta
rango_cambio_alto=0.2,
que los datos de
rango_cortante=0.2,
validación no deben
rango_zoom=0.2,
aumentarse!
giro_horizontal=Verdadero,
modo_relleno='más cercano')

test_datagen = ImageDataGenerator (reescala = 1./255)

train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150,


150),
Directorio
lote_size=20, class_mode='binario') Cambia el tamaño de todas las imágenes a 150 × 150
de destino

Debido a que utiliza la

validation_generator = test_datagen.flow_from_directory ( pérdida de binario_crossentropy,

validation_dir, necesita etiquetas binarias.

target_size=(150, 150), lote_size=20,


class_mode='binario')

model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=2e­5), 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.

Con licencia para <nulo>


Machine Translated by Google

152 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.17 Precisión de entrenamiento y


validación para la extracción de características
con aumento de datos

Figura 5.18 Pérdida de entrenamiento y


validación para la extracción de características
con aumento de datos

5.3.2 Ajuste fino


Otra técnica ampliamente utilizada para la reutilización de modelos, complementaria a la característica.
extracción, es el ajuste fino (ver figura 5.19). El ajuste consiste en descongelar algunos de
las capas superiores de una base de modelo congelada utilizada para la extracción de características y el entrenamiento conjunto

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.

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 153

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

Con licencia para <nulo>


Machine Translated by Google

154 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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:

1 Agregue su red personalizada además de una red base ya capacitada.


2 Congele la red base.

3 Entrena la parte que agregaste.


4 Descongela algunas capas en la red base.
5 Entrena conjuntamente ambas capas y la parte que agregaste.

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.

Como recordatorio, así es como se ve su base convolucional:

>>> conv_base.summary()

Capa (tipo) Forma de salida Parámetro #


==================================================== ===============

entrada_1 (capa de entrada) (Ninguno, 150, 150, 3) 0


________________________________________________________________
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

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 155

________________________________________________________________
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
==================================================== ===============

Parámetros totales: 14714688

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.

Listado 5.22 Congelar todas las capas hasta una específica

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.

Con licencia para <nulo>


Machine Translated by Google

156 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Listado 5.23 Afinando el modelo


model.compile(loss='binary_crossentropy',
optimizador=optimizadores.RMSprop(lr=1e­5),
métricas=['acc'])

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).

Figura 5.20 Precisión de


entrenamiento y validación para ajuste fino

Figura 5.21 Pérdida de


entrenamiento y validación para ajuste fino

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).

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 157

Listado 5.24 Suavizando las gráficas

def smooth_curve(puntos, factor=0.8):


smoothed_points = [] para punto
en puntos:
si puntos_suavizados: anterior
= puntos_suavizados[­1]
puntos_suavizados.append(anterior * factor + punto * (1 ­ factor))
demás:
smoothed_points.append(punto) devuelve
smoothed_points

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.22 Curvas suavizadas para precisión de entrenamiento y validación


para ajuste fino

Con licencia para <nulo>


Machine Translated by Google

158 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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:

test_generator = test_datagen.flow_from_directory( test_dir, target_size=(150,


150),
lote_size=20, class_mode='binario')

test_loss, test_acc = model.evaluate_generator(test_generator, pasos=50) print('test acc:', test_acc)

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!

Con licencia para <nulo>


Machine Translated by Google

Usando un convnet previamente entrenado 159

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.

Con licencia para <nulo>


Machine Translated by Google

160 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

5.4 Visualizando lo que aprenden los convnets


A menudo se dice que los modelos de aprendizaje profundo son "cajas negras": representaciones de aprendizaje
que son difíciles de extraer y presentar en un formato legible por humanos. Aunque esto es
parcialmente cierto para ciertos tipos de modelos de aprendizaje profundo, definitivamente no es cierto para
convnets. Las representaciones aprendidas por los convnets son muy susceptibles de visualización, en gran
parte porque son representaciones de conceptos visuales. Desde 2013, una amplia
Se ha desarrollado una serie de técnicas para visualizar e interpretar estas representaciones. No los
examinaremos todos, pero cubriremos tres de los más accesibles.
y útiles:

Visualización de salidas de conversión intermedias (activaciones intermedias): útil para


comprender cómo las sucesivas capas de convnet transforman su entrada y tener una primera idea
del significado de los filtros de convnet individuales.
Visualización de filtros de convnets: útil para comprender con precisión qué patrones visuales
término o concepto al que cada filtro en una convnet es receptivo.
Visualizar mapas de calor de activación de clase en una imagen: útil para comprender
qué partes de una imagen se identificaron como pertenecientes a una clase determinada, lo que le
permite localizar objetos en las imágenes.

Para el primer método (visualización de activación), utilizará el pequeño convnet que


entrenado desde cero en el problema de clasificación de perros versus gatos en la sección 5.2. Para
En los dos métodos siguientes, utilizará el modelo VGG16 presentado en la sección 5.3.

5.4.1 Visualización de activaciones intermedias


Visualizar activaciones intermedias consiste en mostrar los mapas de características que están
salida por varias capas de convolución y agrupación en una red, dada una cierta entrada
(La salida de una capa a menudo se llama activación, la salida de la función de activación). Esto ofrece una
vista de cómo se descompone una entrada en los diferentes filtros.
aprendido por la red. Quiere visualizar mapas de características con tres dimensiones:
ancho, alto y profundidad (canales). Cada canal codifica relativamente independiente
características, por lo que la forma adecuada de visualizar estos mapas de características es trazando de
forma independiente el contenido de cada canal como una imagen 2D . Comencemos cargando el modelo que
guardaste en la sección 5.2:

>>> desde keras.models importar load_model


>>> modelo = load_model('gatos_y_perros_pequeños_2.h5')
>>> model.summary() <1> Como recordatorio.
________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============

conv2d_5 (Conv2D) (Ninguno, 148, 148, 32) 896


________________________________________________________________
maxpooling2d_5 (MaxPooling2D) (Ninguno, 74, 74, 32) 0
________________________________________________________________
conv2d_6 (Conv2D) (Ninguno, 72, 72, 64) 18496
________________________________________________________________
maxpooling2d_6 (MaxPooling2D) (Ninguno, 36, 36, 64) 0

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 161

________________________________________________________________
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
==================================================== ===============

Parámetros totales: 3.453.121


Parámetros entrenables: 3,453,121
Parámetros no entrenables: 0

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.

Listado 5.25 Preprocesamiento de una sola imagen

img_path = '/Users/fchollet/Downloads/cats_and_dogs_small/test/cats/cat.1700.jpg'

desde keras.preprocesamiento importar imagen Preprocesa la imagen en


importar numpy como np un tensor 4D.

img = imagen.load_img(img_path, target_size=(150, 150))


img_tensor = imagen.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, eje=0)
img_tensor /= 255. Recuerde que el modelo se
entrenó con entradas que
<1> Su forma es (1, 150, 150, 3)
fueron preprocesadas de esta manera.
imprimir (img_tensor.forma)

Mostremos la imagen (ver figura 5.24).

Listado 5.26 Mostrando la imagen de prueba

importar matplotlib.pyplot como plt

plt.imshow(img_tensor[0])
plt.mostrar()

Con licencia para <nulo>


Machine Translated by Google

162 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.24 La imagen del gato de prueba

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

de modelos importados de keras

capas_salidas = [capa.salida para capa en modelo.capas[:8]] activación_modelo =


modelos.Model(entradas=modelo.entrada, salidas=capa_salidas)

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.

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 163

Listado 5.28 Ejecutando el modelo en modo predicción

activaciones = activación_modelo.predict(img_tensor) Devuelve una lista de cinco


Numpy arrays: una matriz
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:

>>> primera_capa_activación = activaciones[0]


>>> imprimir(activación_primera_capa.forma)
(1, 148, 148, 32)

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).

Listado 5.29 Visualizando el cuarto canal

importar matplotlib.pyplot como plt

plt.matshow(first_layer_activation[0, :, :, 4], cmap='viridis')

Figura 5.25 Cuarto canal de activación de la


primera capa en la imagen del gato de prueba

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.

Listado 5.30 Visualizando el séptimo canal

plt.matshow(first_layer_activation[0, :, :, 7], cmap='viridis')

Con licencia para <nulo>


Machine Translated by Google

164 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.26 Séptimo canal de activación de la


primera capa en la imagen del gato de prueba

É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.

Listado 5.31 Visualizando cada canal en cada activación intermedia

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)

imágenes_por_fila = 16 Muestra los mapas de características.

para nombre_capa, activación_capa en zip(nombres_capa, activaciones): n_features = activación_capa.forma[­1]

Número de El mapa de características tiene forma


características en el tamaño = capa_activación.forma[1] (1, tamaño, tamaño, n_features).

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,

fila * tamaño: (fila + 1) * tamaño] = imagen_canal

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')

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 165

Figura 5.27 Activación de cada canal de cada capa en la imagen del gato de prueba

Con licencia para <nulo>


Machine Translated by Google

166 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Hay algunas cosas a tener en cuenta aquí:

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.

Acabamos de evidenciar una importante característica universal de las representaciones


aprendido por redes neuronales profundas: las características extraídas por una capa se vuelven cada vez más
abstractas con la profundidad de la capa. Las activaciones de capas superiores llevan menos
y menos información sobre la entrada específica que se está viendo, y cada vez más información sobre el objetivo
(en este caso, la clase de la imagen: gato o perro). Una red neuronal profunda actúa efectivamente como un canal
de destilación de información, con datos sin procesar entrando.
(en este caso, imágenes RGB) y transformarse repetidamente para filtrar información irrelevante (por ejemplo, la
apariencia visual específica de la imagen), y
la información útil se magnifica y refina (por ejemplo, la clase de la imagen).
Esto es análogo a la forma en que los humanos y los animales perciben el mundo: después de observar una
escena durante unos segundos, un humano puede recordar qué objetos abstractos fueron
presente en él (bicicleta, árbol) pero no puedo recordar la apariencia específica de estos
objetos. De hecho, si intentaste dibujar una bicicleta genérica de memoria, es probable que logres
No pude hacerlo ni remotamente bien, a pesar de que has visto miles de bicicletas en
su vida (ver, por ejemplo, figura 5.28). Pruébelo ahora mismo: este efecto es absolutamente
real. Tu cerebro ha aprendido a abstraer completamente su información visual, a transformarla en
conceptos visuales de alto nivel mientras filtra detalles visuales irrelevantes, lo que hace tremendamente difícil
recordar cómo se ven las cosas a su alrededor.

Figura 5.28 Izquierda: intentos


de dibujar una bicicleta
de memoria. Derecha:
cómo debería ser una
bicicleta esquemática.

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 167

5.4.2 Visualización de filtros convnet


Otra forma sencilla de inspeccionar los filtros aprendidos por las convnets es mostrar el patrón visual al
que debe responder cada filtro. Esto se puede hacer con un ascenso de gradiente en el espacio de
entrada: aplicando un descenso de gradiente al valor de la imagen de entrada de un convnet para
maximizar la respuesta de un filtro específico, comenzando desde una imagen de entrada en blanco. La
imagen de entrada resultante será aquella a la que el filtro elegido responda al máximo.
El proceso es simple: creará una función de pérdida que maximice el valor de un filtro determinado
en una capa de convolución determinada y luego usará el descenso de gradiente estocástico para
ajustar los valores de la imagen de entrada para maximizar este valor de activación. .
Por ejemplo, aquí hay una pérdida por la activación del filtro 0 en la capa block3_conv1 de la red VGG16,
previamente entrenada en ImageNet.

Listado 5.32 Definiendo el tensor de pérdidas para visualización de filtros

desde keras.applications importa VGG16 desde keras


importa el backend como K

modelo = VGG16(pesos='imagenet',
incluir_top=Falso)

nombre_capa = 'block3_conv1' índice_filtro


=0

salida_capa = model.get_layer(nombre_capa).pérdida de salida =


K.mean(salida_capa[:, :, :, índice_filtro])

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.

Listado 5.33 Obtención del gradiente de la pérdida con respecto a la entrada

graduados = K.gradientes(pérdida, modelo.entrada)[0] La llamada a gradientes devuelve una lista de


tensores (de tamaño 1 en este caso). Por lo tanto,
se conserva sólo el primer elemento, que es
un tensor.

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.

Listado 5.34 Truco de normalización de gradiente

graduados /= (K.sqrt(K.media(K.cuadrado(graduados))) + 1e­5) Suma 1e–5 antes de dividir para


evitar dividir accidentalmente
entre 0.

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

Con licencia para <nulo>


Machine Translated by Google

168 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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

iterar = K.function([model.input], [pérdida, graduados])

importar numpy como np


loss_value, grads_value = iterar([np.zeros((1, 150, 150, 3))])

En este punto, puede definir un bucle de Python para realizar un descenso de gradiente estocástico.

Listado 5.36 Maximización de pérdidas mediante descenso de gradiente estocástico

Comienza desde una imagen gris


con algo de ruido.

input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.

paso = 1. para Magnitud de cada actualización de gradiente


i en el rango (40): valor_pérdida,
valor_graduación = iterar([input_img_data]) Ejecuta un ascenso
en gradiente de 40

input_img_data += valor_graduados * paso escalones.

Calcula el valor de pérdida y el Ajusta la imagen de entrada en la dirección


que maximiza la pérdida.
valor del gradiente.

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() + 1e­5) 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.

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 169

Listado 5.38 Función para generar visualizaciones de filtro

Construye una función de pérdida que maximiza Calcula el gradiente


la activación del enésimo filtro de la capa de la imagen de
considerada. entrada con respecto a
esta pérdida.
def generar_patrón(nombre_capa, índice_filtro, tamaño=150):
salida_capa = modelo.get_capa(nombre_capa).salida Truco de
pérdida = K.mean(layer_output[:, :, :, filter_index]) normalización:
normaliza el gradiente
graduados = K.gradientes(pérdida, modelo.entrada)[0]

Devuelve la pérdida
graduados /= (K.sqrt(K.media(K.cuadrado(graduados))) + 1e­5)
y los graduados dada
iterar = K.function([model.input], [pérdida, graduados]) la imagen de entrada.

input_img_data = np.random.random((1, tamaño, tamaño, 3)) * 20 + 128.

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)

Probémoslo (ver figura 5.29):

>>> plt.imshow(generate_pattern('block3_conv1', 0))

Figura 5.29 Patrón del canal cero en la capa


block3_conv1
responde al máximo

Parece que el filtro 0 en la capa block3_conv1 responde a un patrón de lunares. Ahora


La parte divertida: puedes empezar a visualizar cada filtro en cada capa. Para simplificar, necesitarás
solo mire los primeros 64 filtros en cada capa, y solo mirará la primera capa de
cada bloque de convolución (block1_conv1, block2_conv1, block3_conv1, block4_
conv1, bloque5_conv1). Organizará las salidas en una cuadrícula de 8 × 8 con patrones de filtro de
64 × 64, con algunos márgenes negros entre cada patrón de filtro (consulte las figuras 5.30–5.33).

Con licencia para <nulo>


Machine Translated by Google

170 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Listado 5.39 Generando una cuadrícula de todos los patrones de respuesta de filtro en una capa

nombre_capa = 'block1_conv1' tamaño =


Imagen vacía (negra) para
64 almacenar resultados
margen = 5

resultados = np.zeros((8 * tamaño+7* margen, 8 * tamaño+7* margen, 3))

para i en el rango(8): para j Itera sobre las filas de la cuadrícula de resultados.


en el rango(8): filter_img = Itera sobre las columnas de la cuadrícula de resultados.

generate_pattern(layer_name, i + (j * 8), tamaño=tamaño)

Genera el horizontal_start = i * tamaño + i * margen horizontal_end =


patrón para el horizontal_start + tamaño vertical_start = j * tamaño + j * margen pone el resultado
filtro i + (j * 8) en en el cuadrado (i,
vertical_end = vertical_start + tamaño resultados[horizontal_start:
nombre_capa j) de la
horizontal_end, vertical_start: vertical_end, :] = filter_img
cuadrícula de resultados

plt.figure(figsize=(20, 20)) plt.imshow(resultados)


Muestra la cuadrícula de resultados.

Figura 5.30 Patrones de filtro para la capa block1_conv1

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 171

Figura 5.31 Patrones de filtro para la capa block2_conv1

Figura 5.32 Patrones de filtro para la capa block3_conv1

Con licencia para <nulo>


Machine Translated by Google

172 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Figura 5.33 Patrones de filtro para la capa block4_conv1

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 de la primera capa del modelo (block1_conv1) codifican simples


bordes direccionales y colores (o bordes coloreados, en algunos casos).
Los filtros de block2_conv1 codifican texturas simples hechas de combinaciones de bordes y colores.

Los filtros en las capas superiores comienzan a parecerse a las texturas que se encuentran en las imágenes naturales:
plumas, ojos, hojas, etc.

5.4.3 Visualización de mapas de calor de activación de clase

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

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 173

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 “Grad­CAM: 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".

Demostraremos esta técnica nuevamente utilizando la red VGG16 previamente entrenada .

Listado 5.40 Cargando la red VGG16 con pesas previamente entrenadas

Tenga en cuenta que incluye el clasificador


desde keras.applications.vgg16 importar VGG16
densamente conectado en la parte superior;
modelo = VGG16(pesos='imagenet') en todos los casos anteriores, lo descartaste.

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.

Figura 5.34 Imagen de prueba de elefantes africanos

2
Ramprasaath R. Selvaraju et al., arXiv (2017), https://arxiv.org/abs/ 1610.02391.

Con licencia para <nulo>


Machine Translated by Google

174 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

Listado 5.41 Preprocesamiento de una imagen de entrada para VGG16

desde keras.preprocesamiento importar imagen


de keras.applications.vgg16 importar preprocess_input, decode_predictions
importar numpy como np

img_path = '/Users/fchollet/Downloads/creative_commons_elephant.jpg'

img = imagen.load_img(img_path, target_size=(224, 224))

x = imagen.img_to_array(img) float32 Matriz numerosa de formas


(224, 224, 3)
x = np.expand_dims(x, eje=0)

x = entrada_preproceso(x) Agrega una dimensión para transformar la matriz


en un lote de tamaño (1, 224, 224, 3)
Imagen de la biblioteca de imágenes de Python
Preprocesa el lote (esto realiza la
(PIL) de tamaño 224 × 224
normalización del color por canal)
Ruta local a la imagen de destino.

Ahora puede ejecutar la red previamente entrenada en la imagen y decodificar su vector de predicción
a un formato legible por humanos:

>>> preds = modelo.predecir(x)


>>> print('Predicho:', decode_predictions(preds, top=3)[0])
Predicho:', [(u'n02504458', u'African_elephant', 0.92546833),
(u'n01871265', u'tusker', 0.070257246),
(u'n02504013', u'elefante_indio', 0.0042589349)]

Las tres clases principales previstas para esta imagen son las siguientes:

Elefante africano (con 92,5% de probabilidad)


Tusker (con 7% de probabilidad)
Elefante indio (con 0,4% de probabilidad)

La red ha reconocido que la imagen contiene una cantidad indeterminada de


Elefantes africanos. La entrada en el vector de predicción que se activó al máximo es
el correspondiente a la clase “elefante africano”, en el índice 386:

>>> np.argmax(preds[0])
386

Para visualizar qué partes de la imagen se parecen más a un elefante africano, configuremos
el proceso Grad­CAM .

Listado 5.42 Configurando el algoritmo Grad­CAM

Entrada del “elefante africano” en el


vector de predicción
Mapa de características de
salida de la capa
african_e66lephant_output = modelo.salida[:, 386]
block5_conv3, la última

last_conv_layer = model.get_layer('block5_conv3') capa convolucional en VGG16

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 175

gradiente del “africano”


Vector de forma (512,), donde cada entrada es la
clase elefante” con respecto al mapa de intensidad media del gradiente sobre un canal
características de salida de de mapa de características específico
block5_conv3

graduados = K.gradients(african_elephant_output, last_conv_layer.output)[0]

pooled_grads = K.media(graduados, eje=(0, 1, 2))

iterar = K.función([modelo.entrada],
[graduados_agrupados, last_conv_layer.output[0]])

pooled_grads_value, conv_layer_output_value = iterar([x])

para i en el rango (512):


conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

mapa de calor = np.mean(conv_layer_output_value, eje=­1)

La media por canales de Multiplica cada


Valores de estas dos cantidades, como canal en el
El mapa de características
Numerosos arreglos, dada la imagen de muestra
resultante es el mapa de matriz de mapa de
de dos elefantes
calor de la activación de clase.
características por
Le permite acceder a los valores de las cantidades que "qué tan importante es este canal" con
acaba de definir: pooled_grads y el mapa de con respecto a la
características de salida de block5_conv3, dada una clase “elefante”
imagen de muestra.

Para fines de visualización, también normalizará el mapa de calor entre 0 y 1. El resultado


se muestra en la figura 5.35.

Listado 5.43 Postprocesamiento de mapas de calor

mapa de calor = np.maximum(mapa de calor, 0)


mapa de calor /= np.max(mapa de calor)
plt.matshow(mapa de calor)

0 24 6 8 10 12
0

10

12 Figura 5.35 Mapa de calor de activación de


clase de elefante africano sobre la imagen de prueba

Con licencia para <nulo>


Machine Translated by Google

176 CAPÍTULO 5 Aprendizaje profundo para la visión por computadora

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).

Listado 5.44 Superponer el mapa de calor con la imagen original

importar cv2 Cambia el tamaño del mapa de calor a


Utiliza cv2 para cargar el
ser del mismo tamaño que el
img = cv2.imread(img_path) imagen original
imagen original

mapa de calor = cv2.resize(mapa de calor, (img.shape[1], img.shape[0]))

mapa de calor = np.uint8(255 * mapa de calor) Convierte el mapa

mapa de calor = cv2.applyColorMap(mapa de calor, cv2.COLORMAP_JET) de calor a RGB

superimposed_img = mapa de calor * 0.4 + img

cv2.imwrite('/Users/fchollet/Downloads/elephant_cam.jpg', superimposed_img)

0,4 aquí es un factor de


intensidad del mapa de calor.
Guarda la imagen en el disco.

Aplica el mapa de calor a la imagen


original.

Figura 5.36 Superposición del mapa de calor de activación de clases en la imagen original

Esta técnica de visualización responde a dos preguntas importantes:

¿ 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.

Con licencia para <nulo>


Machine Translated by Google

Visualizando lo que aprenden los convnets 177

Resumen del capítulo

Las convnets son la mejor herramienta para atacar los problemas de clasificación visual.

Los convnets funcionan aprendiendo una jerarquía de patrones y conceptos modulares.


para representar el mundo 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.

Entiendes cómo utilizar el aumento de datos visuales para combatir el sobreajuste.

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.

Con licencia para <nulo>


Machine Translated by Google

Aprendizaje profundo
para texto y secuencias.

Este capítulo cubre


Preprocesar datos de texto en representaciones
útiles Trabajar con

redes neuronales recurrentes Usar convnets 1D para

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.

Las aplicaciones de estos algoritmos incluyen lo siguiente:

Clasificación de documentos y clasificación de series temporales, como la identificación de


tema de un artículo o el autor de un libro
Comparaciones de series temporales, como estimar qué tan estrechamente relacionados dos documentos
mentos o dos tickers de acciones son

178

Con licencia para <nulo>


Machine Translated by Google

179

Aprendizaje secuencia a secuencia, como decodificar una oración en inglés en


Francés

Análisis de sentimiento, como clasificar el sentimiento de tweets o reseñas de películas


como positivo o negativo.
Pronósticos de series temporales, como predecir el clima futuro en un lugar determinado.
ción, dados los datos meteorológicos recientes

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.

Con licencia para <nulo>


Machine Translated by Google

180 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

6.1 Trabajar con datos de texto El texto es

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:

Segmentar texto en palabras y transformar cada palabra en un vector.


Segmente el texto en caracteres y transforme cada carácter en un vector. Extraiga n­
gramas de palabras o caracteres y transforme cada n­grama en un vector.
Los N­gramas son grupos superpuestos de múltiples palabras o caracteres consecutivos.

En conjunto, las diferentes unidades en las que se puede dividir el texto (palabras, caracteres o n­gramas)
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 one­hot 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

un mensaje de texto "El gato se sentó en la alfombra".

Fichas
“el”, “gato”, “sat”, “on”, “el”, “mat”, “.”

Codificación vectorial de las


fichas 0,0 0,0 0,4 0,0 0,0 1,0 0,0 0,5 1,0
0,5 0,2 0,5 0,5 0,0 1,0 0,2 1,0 1,0 1,0 0,0
0,0 el gato se sentó en la alfombra.
Figura 6.1 Del texto a los
tokens y a los vectores

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 181

Comprender los n­gramas y la bolsa de palabras


Los n­gramas de palabras son grupos de N (o menos) palabras consecutivas que puedes
extraer de una oración. El mismo concepto también puede aplicarse a caracteres en lugar de palabras.

He aquí un ejemplo sencillo. Considere la frase "El gato se sentó en la alfombra". Puede
descomponerse en el siguiente conjunto de 2 gramos:

{"El", "El gato", "gato", "gato sentado", "sat",


"sentado en", "sobre", "sobre el", "el", "la estera", "estera"}

También se puede descomponer en el siguiente conjunto de 3 gramos:

{"El", "El gato", "gato", "gato sentado", "El gato sentado",


"sentado", "sentado en", "en", "gato sentado en", "en el", "el", "sentado en el", "la estera",
"estera", "en la estera"}

Un conjunto de este tipo se denomina bolsa de 2 gramos o bolsa de 3 gramos, respectivamente. El


término bolsa aquí se refiere al hecho de que se trata de un conjunto de tokens en lugar de una lista
o secuencia: los tokens no tienen un orden específico. Esta familia de métodos de tokenización se
llama bolsa de palabras.

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 n­gramas 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 n­gramas 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.

6.1.1 Codificación one­hot de palabras y caracteres

La codificación one­hot es la forma más común y básica de convertir un token en un vector.


Lo vio en acción en los ejemplos iniciales de IMDB y Reuters en el capítulo 3 (hechos con
palabras, en ese caso). Consiste en asociar un índice entero único a cada palabra y luego
convertir este índice entero i en un vector binario de tamaño N (el tamaño del vocabulario); el
vector es todo ceros excepto la entrada i, que es 1.
Por supuesto, la codificación one­hot también se puede realizar a nivel de carácter. Para
aclarar sin ambigüedades qué es la codificación one­hot y cómo implementarla, los listados 6.1 y
6.2 muestran dos ejemplos de juguetes: uno para palabras y el otro para caracteres.

Con licencia para <nulo>


Machine Translated by Google

182 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Listado 6.1 Codificación one­hot a nivel de palabra (ejemplo de juguete)

Crea un índice de todos los tokens en los datos.

Tokeniza las muestras mediante el método de


Datos iniciales: una entrada por muestra (en este división. En la vida real, también eliminarías la
ejemplo, una muestra es una oración, pero podría puntuación y los caracteres especiales de las
ser un documento completo) muestras.
importar numpy como np

samples = ['El gato se sentó en la alfombra.', 'El perro se comió mi tarea.']

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

resultados = np.zeros(forma=(len(muestras), max_length,

max(token_index.values()) + 1)) para i, muestra


en enumerar(muestras):
para j, palabra en la lista(enumerar(sample.split()))[:max_length]:
índice = token_index.get(palabra) resultados[i,
j, índice] = 1.
Aquí es donde almacena
los resultados.

Asigna un índice único a cada palabra


única. Tenga en cuenta que no atribuye el Vectoriza las muestras. Solo considerará las
índice 0 a nada.
primeras palabras de longitud máxima
en cada muestra.

Listado 6.2 Codificación one­hot a nivel de carácter (ejemplo de juguete)

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):

para j, carácter en enumerar (muestra): índice =


token_index.get (carácter) resultados [i, j, índice] = 1. Todos los caracteres
ASCII imprimibles

Tenga en cuenta que Keras tiene utilidades integradas para realizar codificación one­hot 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). ).

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 183

Listado 6.3 Uso de Keras para codificación one­hot a nivel de palabra

Crea un tokenizador, configurado


tener en cuenta sólo el
de keras.preprocessing.text importar tokenizador 1.000 palabras más comunes

samples = ['El gato se sentó en la alfombra.', 'El perro se comió mi tarea.']

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 one­hot.

Una variante de la codificación one­hot es el llamado truco de hash one­hot, 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

la dimensionalidad del espacio hash es mucho mayor que el número total de


tokens únicos que se procesan mediante hash.

Listado 6.4 Codificación one­hot a nivel de palabra con truco de hash (ejemplo de juguete)

samples = ['El gato se sentó en la alfombra.', 'El perro se comió mi tarea.']

dimensionalidad = 1000
longitud_max = 10

resultados = np.zeros((len(muestras), max_length, dimensionalidad))


para i, muestra en enumerar (muestras):
para j, palabra en la lista(enumerar(sample.split()))[:max_length]:
índice = abs(hash(palabra)) % dimensionalidad
resultados[i, j, índice] = 1.
Divide la palabra en un
Almacena las palabras como vectores de tamaño 1000. Si tiene cerca índice entero aleatorio
de 1000 palabras (o más), verá muchas colisiones de hash, lo que entre 0 y 1000.
disminuirá la precisión de este método de codificación.

Con licencia para <nulo>


Machine Translated by Google

184 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

6.1.2 Uso de incrustaciones de palabras

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 one­hot
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 one­hot, 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 one­hot 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.

Figura 6.2 Mientras que las representaciones de palabras


obtenidas a partir de codificación one­hot o hash son escasas,
Vectores de palabras únicas: Incrustaciones de palabras:
de alta dimensión y codificadas de forma rígida, las
­ Escaso ­ Denso
­ De dimensiones inferiores incrustaciones de palabras son densas,
­ Altas dimensiones
­ Codificado ­ Aprendido de los datos relativamente de baja dimensión y se aprenden a partir de datos.

Hay dos formas de obtener incrustaciones de palabras:

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.

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 185

APRENDER INTEGRACIONES DE PALABRAS CON LA CAPA DE INTEGRACIÓN

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

de transformaciones geométricas significativas son el “género”.


vectores y vectores “plurales”. Por ejemplo, agregando un vector "femenino" al vector
“rey”, obtenemos el vector “reina”. Al agregar un vector "plural", obtenemos "reyes".
Los espacios de incrustación de palabras suelen presentar miles de vectores interpretables y potencialmente útiles.

¿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.

Con licencia para <nulo>


Machine Translated by Google

186 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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 .

Listado 6.5 Creación de instancias de una capa de incrustación

de keras.layers importar Incrustación La capa de incrustación toma al menos dos


argumentos: el número de tokens posibles (aquí,
embedding_layer = Incrustación(1000, 64) 1000: 1 + índice máximo de palabras) y la
dimensionalidad de las incrustaciones (aquí, 64).

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).

índice de palabras Capa de incrustación Vector de palabra correspondiente

Figura 6.4 La capa de incrustación

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

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 187

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

desde keras.datasets importar imdb


Corta el texto después de esto.
del preprocesamiento de importación de keras Número de palabras a
considerar como características número de palabras (entre las
características_max = 10000 palabras más comunes de
maxlen = 20 max_features)

(x_train, y_train), (x_test, y_test) = imdb.load_data(


núm_palabras=max_características) Carga los datos como listas de números enteros.

x_train = preprocesamiento.sequence.pad_sequences(x_train, maxlen=maxlen


x_test = preprocesamiento.sequence.pad_sequences(x_test, maxlen=maxlen)

Convierte las listas de números enteros en


un tensor de forma entero 2D
(muestras, maxlen)

Listado 6.7 Usando una capa de incrustación y un clasificador en los datos IMDB

Especifica la longitud máxima de entrada para la capa de


incrustación para que luego pueda aplanar las entradas
incrustadas. Después de la capa de Incrustación, las activaciones
Aplana el tensor 3D de
tienen forma (muestras, maxlen, 8).
incrustaciones en un tensor

desde keras.models importar secuencial de forma 2D (muestras, maxlen * 8)

de keras.layers importar Aplanar, Denso

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()

historia = modelo.fit(x_train, y_train,


épocas = 10,
tamaño_lote = 32,
validación_split=0.2)

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.

Con licencia para <nulo>


Machine Translated by Google

188 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

USO DE INTEGRACIONES DE PALABRAS PREENTRENADAS

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 conv­nets 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

Utilizarás un modelo similar al que acabamos de ver: incrustar oraciones en


secuencias de vectores, aplanándolos y entrenando una capa Densa en la parte superior. pero lo haras
por lo que se utilizan incrustaciones de palabras previamente entrenadas; y en lugar de usar el IMDB pretokenizado
datos empaquetados en Keras, comenzará desde cero descargando los datos de texto originales.

1
Yoshua Bengio et al., Modelos de lenguaje probabilístico neuronal (Springer, 2003).

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 189

DESCARGAR LOS DATOS IMDB COMO TEXTO SIN PROCESAR

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

importar sistema operativo

imdb_dir = '/Users/fchollet/Downloads/aclImdb' train_dir = os.path.join(imdb_dir,


'tren')

etiquetas = []
textos = []

para tipo_etiqueta en ['neg', 'pos']:


dir_name = os.path.join(train_dir, label_type) para fname en os.listdir(dir_name):

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)

TOKENIZANDO LOS DATOS

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

de keras.preprocessing.text importar Tokenizer de keras.preprocessing.sequence


importar pad_sequences importar numpy como np

maxlen = 100 Elimina las reseñas después de 100 palabras

muestras_entrenamiento = 200 Trenes en 200 muestras.


muestras_validación = 10000 Valida en 10.000 muestras.
max_palabras = 10000
Considera solo las 10 000
tokenizer = Tokenizer(num_words=max_words) palabras principales del conjunto de datos
tokenizer.fit_on_texts(textos) secuencias =
tokenizer.texts_to_sequences(textos)

Con licencia para <nulo>


Machine Translated by Google

190 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

word_index = tokenizer.word_index print('Se


encontraron %s tokens únicos.' % len(word_index))

datos = pad_sequences(secuencias, maxlen=maxlen)

etiquetas = np.asarray(labels) print('Forma


del tensor de datos:', datos.forma) print('Forma del tensor de
etiquetas:', etiquetas.forma)

í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]

DESCARGAR LAS INTEGRACIONES DE PALABRAS GUANTE

Vaya a https://nlp.stanford.edu/projects/glove, y descargue las incrustaciones precalculadas de la Wikipedia


en inglés de 2014. Es un archivo zip de 822 MB llamado guante.6B.zip, que contiene vectores de
incrustación de 100 dimensiones para 400.000 palabras (o tokens que no son palabras). Descomprímelo.

PREPROCESAMIENTO DE LAS INTEGRACIONES

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).

Listado 6.10 Analizando el archivo de incrustaciones de palabras de GloVe

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:

valores = line.split() palabra =


valores[0] coefs =
np.asarray(valores[1:], dtype='float32') embeddings_index[palabra] = coefs
f.close()

print('Encontramos %s vectores de palabras.' % len(embeddings_index))

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.

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 191

Listado 6.11 Preparando la matriz de incrustaciones de palabras de GloVe

incrustación_dim = 100

embedding_matrix = np.zeros((max_words, embedding_dim)) para Word, i en


word_index.items(): si i < max_words:
embedding_vector =
embeddings_index.get(word) si embedding_vector no es Ninguno: Las palabras que no se

embedding_matrix[i] = embedding_vector encuentren en el índice de


inserción serán todas ceros.

DEFINIR UN MODELO

Utilizará la misma arquitectura de modelo que antes.

Listado 6.12 Definición del modelo

de keras.models importa Secuencial de keras.layers


importa Incrustación, Aplanamiento, Denso

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()

CARGAR LAS INTEGRACIONES DE GUANTES EN EL MODELO

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.

Listado 6.13 Cargando incrustaciones de palabras previamente entrenadas en la capa Incrustación

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.

Con licencia para <nulo>


Machine Translated by Google

192 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

FORMACIÓN Y EVALUACIÓN DEL MODELO


Compile y entrene el modelo.

Listado 6.14 Entrenamiento y evaluación

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).

Listado 6.15 Trazando los resultados

importar matplotlib.pyplot como plt

acc = historia.historia['acc'] val_acc =


historia.historia['val_acc'] pérdida = historia.historia['pérdida']
val_loss = historia.historia['val_loss']

épocas = rango(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b',


label='Validation acc') plt.title('Precisión de entrenamiento y validación') plt .leyenda()

plt.figura()

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs,


val_loss, 'b', label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación')
plt .leyenda()

plt.mostrar()

Figura 6.5 Pérdida de entrenamiento y validación cuando


se utilizan incrustaciones de palabras previamente entrenadas

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 193

Figura 6.6 Precisión del entrenamiento


y la validación cuando se utilizan incrustaciones
de palabras previamente entrenadas

El modelo rápidamente comienza a sobreajustarse, lo cual no es sorprendente dada la pequeña


cantidad de muestras de entrenamiento. La precisión de la validación tiene una gran variación por la
misma razón, pero parece alcanzar los 50.
Tenga en cuenta que su kilometraje puede variar: debido a que tiene muy pocas muestras de
entrenamiento, el rendimiento depende en gran medida de exactamente qué 200 muestras elija, y las
elige al azar. Si esto no te funciona, intenta elegir un conjunto aleatorio diferente de 200 muestras, por
el bien del ejercicio (en la vida real, no puedes elegir tus datos de entrenamiento).

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

de keras.models importa Secuencial de keras.layers


importa Incrustación, Aplanamiento, Denso

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))

Con licencia para <nulo>


Machine Translated by Google

194 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Figura 6.7 Pérdida de


entrenamiento y validación sin utilizar
incrustaciones de palabras previamente entrenadas

Figura 6.8 Precisión del entrenamiento y la


validación sin utilizar incrustaciones de
palabras previamente entrenadas

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.

Listado 6.17 Tokenización de los datos del conjunto de prueba

test_dir = os.path.join(imdb_dir, 'prueba')

etiquetas = []
textos = []

para tipo_etiqueta en ['neg', 'pos']:


nombre_dir = os.path.join(dir_prueba, tipo_etiqueta)
para fname en ordenado (os.listdir (dir_name)):
si fname[­4:] == '.txt':
f = abrir(os.path.join(dir_name, fname))
textos.append(f.read())

Con licencia para <nulo>


Machine Translated by Google

Trabajar con datos de texto 195

f.close() si
label_type == 'neg':
etiquetas.append(0)
demás:
etiquetas.append(1)

secuencias = tokenizer.texts_to_sequences(textos) x_test =


pad_sequences(secuencias, maxlen=maxlen) y_test = np.asarray(labels)

A continuación, cargue y evalúe el primer modelo.

Listado 6.18 Evaluando el modelo en el conjunto de prueba

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.

problemas de procesamiento del lenguaje

Con licencia para <nulo>


Machine Translated by Google

196 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

6.2 Comprensión de las redes neuronales recurrentes Una


característica importante de todas las redes neuronales que ha visto hasta ahora, como las
redes densamente conectadas y las convnets, es que no tienen memoria. Cada entrada que se
les muestra se procesa de forma independiente, sin que se mantenga ningún estado entre las
entradas. Con este tipo de redes, para procesar una secuencia o una serie temporal de puntos
de datos, es necesario mostrar la secuencia completa a la red a la vez: convertirla en un único
punto de datos. Por ejemplo, esto es lo que hiciste en el ejemplo de IMDB: una reseña de
película completa se transformó en un único vector grande y se procesó de una sola vez. Estas
redes se denominan redes feedforward.
Por el contrario, mientras lees la oración actual, la procesas palabra por palabra (o más bien,
sacada ojo por sacada ojo) mientras guardas recuerdos de lo que vino antes; esto le brinda una
representación fluida del significado que transmite esta oración.
La inteligencia biológica procesa la información de forma incremental mientras mantiene un
modelo interno de lo que procesa, construido a partir de información pasada y actualizado
constantemente a medida que llega nueva información.
Una red neuronal recurrente (RNN) adopta el mismo principio, aunque en una versión
extremadamente simplificada: procesa secuencias iterando a través de los elementos de la
secuencia y manteniendo un estado que contiene información
Producción
relativa a lo que ha visto hasta ahora. En efecto, una RNN es un
tipo de red neuronal que tiene un bucle interno (ver figura 6.9).
El estado del RNN se restablece entre el procesamiento de dos RNN
Conexión
secuencias diferentes e independientes (como dos revisiones recurrente

de IMDB diferentes ), por lo que aún se considera una secuencia


como un único punto de datos: una única entrada a la red. Lo Aporte

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. .

En pseudocódigo, este es el RNN.

Listado 6.19 Pseudocódigo RNN

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.

Con licencia para <nulo>


Machine Translated by Google

Comprender las redes neuronales recurrentes 197

Incluso puedes desarrollar la función f: la transformación de la entrada y el estado en un


la salida estará parametrizada por dos matrices, W y U, y un vector de polarización. Es similar a
la transformación operada por una capa densamente conectada en una red feedforward.

Listado 6.20 Pseudocódigo más detallado para el RNN

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.

Listado 6.21 Implementación numpy de un RNN simple

Número de pasos de tiempo


Dimensionalidad del espacio
en la secuencia de entrada
de características de entrada.
importar numpy como np Datos de entrada: ruido
aleatorio por el bien de
pasos de tiempo = 100 Dimensionalidad del espacio
de características de salida. el ejemplo
características_entrada = 32
características_salida = 64
Estado inicial: un
entradas = np.random.random((pasos de tiempo, características_entrada))
vector todo cero

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

secuencia_salida_final = np.concatenate(salidas_sucesivas, eje=0)

El resultado final es un tensor 2D de


Almacena esta salida en una lista.
forma (pasos de tiempo, características_de_salida).

Combina la entrada con el estado actual (la


salida anterior) para obtener la salida actual Actualiza el estado de la
red para el próximo paso de tiempo

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)

Con licencia para <nulo>


Machine Translated by Google

198 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

salida t­1 salida t salida t+1

salida_t =
activación(
... W•entrada_t + ...
Estado t U•estado_t + Estado t+1

bo)

entrada t­1 entrada t entrada t+1

Figura 6.10 Un RNN simple, desarrollado a lo largo del tiempo

NOTA En este ejemplo, el resultado final es un tensor de forma 2D (pasos de tiempo,


output_features), donde cada paso de tiempo es la salida del bucle en el tiempo t.
Cada paso de tiempo t en el tensor de salida contiene información sobre los pasos de tiempo 0
to t en la secuencia de entrada: sobre todo el pasado. Por esta razón, en muchos
En muchos casos, no necesita esta secuencia completa de salidas; sólo necesitas la última
salida (output_t al final del ciclo), porque ya contiene información sobre toda la secuencia.

6.2.1 Una capa recurrente en Keras


El proceso que acaba de implementar ingenuamente en Numpy corresponde a un Keras real
capa: la capa SimpleRNN :

de keras.layers importar SimpleRNN

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:

>>> desde keras.models importar Secuencial


>>> desde keras.layers importar Incrustación, SimpleRNN
>>> modelo = Secuencial()
>>> modelo.add(Incrustar(10000, 32))
>>> modelo.add(SimpleRNN(32))
>>> modelo.resumen()

Con licencia para <nulo>


Machine Translated by Google

Comprender las redes neuronales recurrentes 199

________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============

embedding_22 (Incrustación) (Ninguno, Ninguno, 32) 320000


________________________________________________________________
simplernn_10 (RNN simple) (Ninguno, 32) 2080
==================================================== ===============

Parámetros totales: 322.080


Parámetros entrenables: 322,080
Parámetros no entrenables: 0

El siguiente ejemplo devuelve la secuencia de estado completa:

>>> modelo = Secuencial()


>>> modelo.add(Incrustar(10000, 32))
>>> modelo.add(SimpleRNN(32, return_sequences=True))
>>> modelo.resumen()
________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============

embedding_23 (Incrustación) (Ninguno, Ninguno, 32) 320000


________________________________________________________________
simplernn_11 (RNN simple) (Ninguno, Ninguno, 32) 2080
==================================================== ===============

Parámetros totales: 322.080


Parámetros entrenables: 322,080
Parámetros no entrenables: 0

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:

>>> modelo = Secuencial()


>>> modelo.add(Incrustar(10000, 32))
>>> modelo.add(SimpleRNN(32, return_sequences=True))
>>> modelo.add(SimpleRNN(32, return_sequences=True))
>>> modelo.add(SimpleRNN(32, return_sequences=True)) La última capa solo
>>> modelo.add(SimpleRNN(32)) devuelve la última salida
>>> modelo.resumen()
________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ===============

embedding_24 (Incrustar) (Ninguno, Ninguno, 32) 320000


________________________________________________________________
simplernn_12 (RNN simple) (Ninguno, Ninguno, 32) 2080
________________________________________________________________
simplernn_13 (RNN simple) (Ninguno, Ninguno, 32) 2080
________________________________________________________________
simplernn_14 (RNN simple) (Ninguno, Ninguno, 32) 2080
________________________________________________________________
simplernn_15 (RNN simple) (Ninguno, 32) 2080
==================================================== ===============

Parámetros totales: 328,320


Parámetros entrenables: 328,320
Parámetros no entrenables: 0

Con licencia para <nulo>


Machine Translated by Google

200 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Ahora, usemos dicho modelo en el problema de clasificación de reseñas de películas de IMDB .


Primero, preprocese los datos.

Listado 6.22 Preparando los datos de IMDB

desde keras.datasets importar imdb desde


keras.preprocesamiento secuencia de importación Número de palabras a
considerar como características
max_features = 10000 maxlen =
500
Corta los textos después de esta cantidad de palabras
tamaño_lote = 32
(entre las palabras más comunes de max_features)
print('Cargando datos...') (input_train,
y_train), (input_test, y_test) = imdb.load_data(
num_words=max_features)
print(len(input_train), 'entrenar secuencias') print(len(input_test),
'prueba de secuencias')

print('Secuencias de pad (muestras x tiempo)') input_train =


secuencia.pad_sequences(input_train, maxlen=maxlen) input_test = secuencia.pad_sequences(input_test,
maxlen=maxlen) print('input_train forma:', input_train.shape) print( 'input_test forma:', input_test.shape)

Entrenemos una red recurrente simple usando una capa de incrustación y una capa SimpleRNN .

Listado 6.23 Entrenamiento del modelo con capas Embedding y SimpleRNN

de keras.layers importar Denso

modelo = Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activación='sigmoide'))

model.compile(optimizer='rmsprop', pérdida='binary_crossentropy', metrics=['acc']) historial = model.fit(input_train, y_train,


epochs=10, batch_size=128, validation_split=0.2)

Ahora, mostremos la pérdida y precisión del entrenamiento y la validación (ver figuras 6.11 y 6.12).

Listado 6.24 Trazado de resultados

importar matplotlib.pyplot como plt

acc = historia.historia['acc'] val_acc =


historia.historia['val_acc'] pérdida = historia.historia['pérdida']
val_loss = historia.historia['val_loss']

épocas = rango(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b',


label='Validation acc')

Con licencia para <nulo>


Machine Translated by Google

Comprender las redes neuronales recurrentes 201

plt.title('Precisión del entrenamiento y validación') plt.legend()

plt.figura()

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs,


val_loss, 'b', label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación')
plt .leyenda()

plt.mostrar()

Figura 6.11 Pérdida de entrenamiento y


validación en IMDB con SimpleRNN

Figura 6.12 Precisión de entrenamiento y


validación en IMDB con SimpleRNN

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.

Con licencia para <nulo>


Machine Translated by Google

202 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Otros tipos de capas recurrentes funcionan mucho mejor. Veamos algunas capas más avanzadas.

6.2.2 Comprensión de las capas LSTM y GRU


SimpleRNN no es la única capa recurrente disponible en Keras. Hay otros dos: LSTM
y GRU. En la práctica, siempre usarás uno de estos, porque SimpleRNN generalmente es demasiado
simplista para ser de utilidad real. SimpleRNN tiene un problema importante: aunque teóricamente debería
ser capaz de retener en el momento t información sobre entradas vistas muchos pasos de tiempo antes, en
En la práctica, estas dependencias a largo plazo son imposibles de aprender. Esto se debe al problema del gradiente
de desaparición, un efecto similar al que se observa con los no recurrentes.
redes (redes de retroalimentación) que tienen muchas capas de profundidad: a medida que continúa agregando capas
a una red, la red eventualmente se vuelve imposible de entrenar. Las razones teóricas para
Este efecto fue estudiado por Hochreiter, Schmidhuber y Bengio a principios de los años 1990.2
Las capas LSTM y GRU están diseñadas para resolver este problema.
Consideremos la capa LSTM . La memoria subyacente a largo plazo (LSTM)
El algoritmo fue desarrollado por Hochreiter y Schmidhuber en 1997;3 fue la culminación de su investigación sobre el
problema del gradiente evanescente.
Esta capa es una variante de la capa SimpleRNN que ya conoce; agrega una manera
para transportar información a través de muchos pasos de tiempo. Imagine una cinta transportadora que corre paralela
a la secuencia que estás procesando. La información de la secuencia puede saltar a la
cinta transportadora en cualquier punto, ser transportado a un paso de tiempo posterior y saltar, intacto,
Cuándo lo necesitas. Esto es esencialmente lo que hace LSTM : guarda información para más adelante,
evitando así que las señales más antiguas desaparezcan gradualmente durante el procesamiento.
Para entender esto en detalle, comencemos desde la celda SimpleRNN (ver figura 6.13).
Debido a que tendrá muchas matrices de peso, indexe las matrices W y U en la celda con
la letra o (Wo y Uo) para la salida.

salida t­1 salida t salida t+1

salida_t =
activación(
... Wo•entrada_t + ...
Estado t Uo•estado_t + Estado t+1

bo)

entrada t­1 entrada t entrada t+1

Figura 6.13 El punto de partida de una capa LSTM : un SimpleRNN

2
Véase, por ejemplo, Yoshua Bengio, Patrice Simard y Paolo Frasconi, “Learning Long­Term 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).

Con licencia para <nulo>


Machine Translated by Google

Comprender las redes neuronales recurrentes 203

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.

salida t­1 salida t salida t+1

c t­1 Connecticut c t+1 llevar pista

Connecticut
salida_t = Connecticut

activación(
Wo•entrada_t +
... ...
Uo•estado_t +
Estado t Estado t+1
Vo•c_t + bo)

entrada t­1 entrada t entrada t+1

Figura 6.14 Pasar de SimpleRNN a LSTM: agregar una pista de acarreo

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).

Listado 6.25 Detalles del pseudocódigo de la arquitectura LSTM (1/2)

salida_t = activación(punto(estado_t, Uo) + punto(entrada_t, Wo) + punto(C_t, Vo) + bo)

i_t = activación(punto(estado_t, Ui) + punto(entrada_t, Wi) + bi)


f_t = activación(punto(estado_t, Uf) + punto(entrada_t, Wf) + bf)
k_t = activación(punto(estado_t, Reino Unido) + punto(entrada_t, Wk) + bk)

Obtienes el nuevo estado de acarreo (el siguiente c_t) combinando i_t, f_t y k_t.

Listado 6.26 Detalles del pseudocódigo de la arquitectura LSTM (2/2)

c_t+1 = i_t * k_t + c_t * f_t

Agregue esto como se muestra en la figura 6.15. Y eso es. No es tan complicado, solo un poco
complejo.

Con licencia para <nulo>


Machine Translated by Google

204 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

salida t­1 salida t salida t+1

c t­1 Connecticut c t+1 llevar pista


Calcular Calcular
nuevo nuevo

llevar llevar

Connecticut
salida_t = Connecticut

activación(
Wo•entrada_t +
... ...
Estado t Uo•estado_t + Estado t+1
Vo•c_t + bo)

entrada t­1 entrada t entrada t+1

Figura 6.15 Anatomía de un LSTM

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.

6.2.3 Un ejemplo concreto de LSTM en Keras


Ahora pasemos a preocupaciones más prácticas: configurará un modelo usando una capa LSTM
y entrenarlo con los datos de IMDB (ver figuras 6.16 y 6.17). La red es similar a la
uno con SimpleRNN que se acaba de presentar. Solo especifica la dimensión de salida de la capa LSTM ; Deje
todos los demás argumentos (hay muchos) en Keras.

Con licencia para <nulo>


Machine Translated by Google

Comprender las redes neuronales recurrentes 205

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.

Listado 6.27 Usando la capa LSTM en Keras

de keras.layers importar LSTM

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)

Figura 6.16 Pérdida de entrenamiento y


validación en IMDB con LSTM

Figura 6.17 Precisión de entrenamiento y


validación en IMDB con LSTM

Con licencia para <nulo>


Machine Translated by Google

206 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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:

Qué son los RNN y cómo funcionan Qué


es LSTM y por qué funciona mejor en secuencias largas que un RNN ingenuo Cómo utilizar las
capas Keras RNN para procesar datos de secuencia

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.

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 207

6.3 Uso avanzado de redes neuronales recurrentes


En esta sección, revisaremos tres técnicas avanzadas para mejorar el rendimiento y el poder de generalización de
las redes neuronales recurrentes. Al final de la sección, sabrá la mayor parte de lo que hay que saber sobre el uso
de redes recurrentes con
Keras. Demostraremos los tres conceptos en un problema de pronóstico de temperatura,
donde tiene acceso a una serie temporal de puntos de datos provenientes de sensores instalados en
el techo de un edificio, como la temperatura, la presión del aire y la humedad, que utiliza
para predecir cuál será la temperatura 24 horas después del último punto de datos. Esto es un
problema bastante desafiante que ejemplifica muchas dificultades comunes encontradas
cuando se trabaja con series temporales.
Cubriremos las siguientes técnicas:

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.

6.3.1 Un problema de previsión de temperatura

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.

Tute de Biogeoquímica en Jena, Alemania.4


En este conjunto de datos, se registraron 14 cantidades diferentes (como temperatura del aire, presión
atmosférica, humedad, dirección del viento, etc.) cada 10 minutos, durante varios años. Los datos originales se
remontan a 2003, pero este ejemplo se limita a datos
de 2009 a 2016. Este conjunto de datos es perfecto para aprender a trabajar con números.
series de tiempo. Lo usará para construir un modelo que toma como entrada algunos datos de la reciente
pasado (un par de días de puntos de datos) y predice la temperatura del aire 24 horas en
el futuro.

Descargue y descomprima los datos de la siguiente manera:

cd ~/Descargas
mkdir jena_climate
cd jena_climate
wget https://s3.amazonaws.com/keras­datasets/jena_climate_2009_2016.csv.zip
descomprimir jena_climate_2009_2016.csv.zip

Miremos los datos.

4
Olaf Kolle, www.bgc­jena.mpg.de/wetter.

Con licencia para <nulo>


Machine Translated by Google

208 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Listado 6.28 Inspeccionando los datos del conjunto de datos meteorológicos de Jena

importar sistema operativo

data_dir = '/users/fchollet/Downloads/jena_climate' fname = os.path.join(data_dir,


'jena_climate_2009_2016.csv')

f = abrir(fname) datos =
f.read()
f.cerrar()

líneas = datos.split('\n') encabezado =


líneas[0].split(',') líneas = líneas[1:]

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:

["Fecha Hora", "p


(mbar)", "T
(grados C)", "Tpot
(K)", "Trocío
(grados C)", "rh (%)",
"VPmáx
(mbar)", "VPact (mbar)",
"VPdef (mbar)", "sh (g/
kg)", "H2OC (mmol/mol)",
"rho (g/m**3)", "wv
(m/s)" , "wv máx. (m/s)", "wd
(grados)"]

Ahora, convierta las 420.551 líneas de datos en una matriz Numpy.

Listado 6.29 Analizando los datos

importar numpy como np

float_data = np.zeros((len(lines), len(header) ­ 1)) para i, línea en enumerate(lines):


valores = [float(x) for x in line.split(',')[1 :]] float_data[i, :]
= valores

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.

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 209

Listado 6.30 Trazado de la serie temporal de temperatura

desde matplotlib importar pyplot como plt

temp = float_data[:, 1] <1> temperatura (en grados Celsius)


plt.plot(rango(len(temp)), temperatura)

Figura 6.18 Temperatura en


todo el rango temporal del conjunto
de datos (ºC)

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.

Listado 6.31 Trazado de los primeros 10 días de la serie temporal de temperatura

plt.plot(rango(1440), temperatura[:1440])

Figura 6.19 Temperatura durante


los primeros 10 días del conjunto
de datos (ºC)

Con licencia para <nulo>


Machine Translated by Google

210 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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.

6.3.2 Preparación de los datos


La formulación exacta del problema será la siguiente: dados los datos que se remontan a pasos de tiempo
retrospectivos (un paso de tiempo es de 10 minutos) y se tomaron muestras de cada paso de tiempo, ¿puede
predecir la temperatura en pasos de tiempo de retraso ? Utilizará los siguientes valores de parámetros:

retrospectiva = 720: las observaciones se remontarán a 5 días


atrás. pasos = 6: las observaciones se muestrearán en un punto de datos por hora.
retraso = 144: los objetivos serán 24 horas en el futuro.

Para comenzar, debes hacer dos cosas:

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.

Listado 6.32 Normalizando los datos

media = float_data[:200000].mean(axis=0) float_data ­= media


std =
float_data[:200000].std(axis=0) float_data /= std

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:

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 211

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

generador def (datos, retrospectiva, retraso, min_index, max_index, shuffle=False,


lote_size=128, paso=6): si max_index es Ninguno: max_index
= len(datos) ­ retraso ­ 1 i =
min_index + retrospección mientras 1: si barajar: filas
= np.random.randint(

índice_mínimo + retrospectiva, índice_máximo, tamaño=tamaño_lote)


demás:
si i + tamaño_lote >= índice_máximo: i =
índice_mínimo + mirada retrospectiva
filas = np.arange(i, min(i + tamaño_lote, índice_máximo)) i += len(filas)

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.

Listado 6.34 Preparando los generadores de entrenamiento, validación y pruebas

mirar hacia atrás = 1440


paso = 6
retraso = 144
tamaño_lote = 128

Con licencia para <nulo>


Machine Translated by Google

212 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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)

val_steps = (300000 ­ 200001 ­ retrospectiva) ¿Cuántos pasos extraer de


test_gen para ver el conjunto
test_steps = (len(float_data) ­ 300001 ­ retrospectiva) de pruebas completo?

6.3.3 Una línea de base de sentido común y sin aprendizaje automático

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))

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 213

Aquí está el ciclo de evaluación.

Listado 6.35 Calcular el MAE de referencia de sentido común

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.

Listado 6.36 Convirtiendo el MAE nuevamente a un error Celsius

celsius_mae = 0,29 * estándar[1]

Ese es un error absoluto promedio bastante grande. Ahora el juego consiste en utilizar tus conocimientos de
aprendizaje profundo para hacerlo mejor.

6.3.4 Un enfoque básico de aprendizaje automático


De la misma manera que es útil establecer una línea de base de sentido común antes de probar enfoques de
aprendizaje automático, es útil probar modelos de aprendizaje automático simples y baratos (como redes
pequeñas y densamente conectadas) antes de analizar enfoques complicados y computacionalmente costosos.
Modelos como RNN. Esta es la mejor manera de asegurarse de que cualquier complejidad adicional que agregue
al problema sea legítima y brinde beneficios reales.
La siguiente lista muestra un modelo completamente conectado que comienza aplanando los datos y luego
los ejecuta a través de dos capas densas . Tenga en cuenta la falta de función de activación en la última capa
Densa , lo cual es típico de un problema de regresión. Utiliza MAE como pérdida.
Debido a que evalúa exactamente con los mismos datos y con exactamente la misma métrica que hizo con el
enfoque de sentido común, los resultados serán directamente comparables.

Listado 6.37 Entrenamiento y evaluación de un modelo densamente conectado

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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))

Con licencia para <nulo>


Machine Translated by Google

214 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=20,

validation_data=val_gen,
validation_steps=val_steps)

Mostremos las curvas de pérdida para validación y entrenamiento (ver figura 6.20).

Listado 6.38 Trazado de resultados

importar matplotlib.pyplot como plt

pérdida = historia.historia['pérdida'] val_loss =


historia.historia['val_loss']

épocas = rango (1, len (pérdida) + 1)

plt.figura()

plt.plot(epochs, loss, 'bo', label='Pérdida de entrenamiento') plt.plot(epochs,


val_loss, 'b', label='Pérdida de validación') plt.title('Pérdida de entrenamiento y validación')
plt .leyenda()

plt.mostrar()

Figura 6.20 Pérdida de capacitación y


validación en la tarea de pronóstico
de temperatura de Jena con una red simple
y densamente conectada

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

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 215

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.

6.3.5 Una primera línea de base recurrente

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.

Listado 6.39 Entrenamiento y evaluación de un modelo basado en GRU

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

modelo = Sequential()
model.add(layers.GRU(32, input_shape=(Ninguno, float_data.shape[­1]))) model.add(layers.Dense(1))

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=20,

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.

Con licencia para <nulo>


Machine Translated by Google

216 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Figura 6.21 Pérdida de capacitación y


validación en la tarea de pronóstico
de temperatura de Jena con un GRU

La nueva validación MAE de ~0,265 (antes de comenzar a sobreajustar significativamente) se traduce


a un error absoluto medio de 2,35 ˚C después de la desnormalización. Esa es una ganancia sólida para el
error inicial de 2,57˚C, pero probablemente todavía tengas un pequeño margen de mejora.

6.3.6 Utilizar el abandono escolar recurrente para luchar contra el sobreajuste

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.

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 217

y recurrent_dropout, especificando la tasa de abandono de las unidades recurrentes. Agreguemos


abandono y abandono recurrente a la capa GRU y veamos cómo esto afecta el sobreajuste.
Debido a que las redes que se regularizan con abandono siempre tardan más en converger
completamente, entrenará la red durante el doble de épocas.

Listado 6.40 Entrenamiento y evaluación de un modelo basado en GRU regularizado para la deserción escolar

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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))

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=40,

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.

Figura 6.22 Pérdida de capacitación y


validación en la tarea de pronóstico
de temperatura de Jena con un GRU
regularizado por abandono

6.3.7 Apilar capas recurrentes


Debido a que ya no está sobreadaptado sino que parece haber encontrado un cuello de botella en
el rendimiento, debería considerar aumentar la capacidad de la red. Recuerde la descripción del
flujo de trabajo universal de aprendizaje automático: generalmente es una buena idea aumentar la
capacidad de su red hasta que el sobreajuste se convierta en el principal obstáculo (suponiendo que

Con licencia para <nulo>


Machine Translated by Google

218 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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.

Listado 6.41 Entrenamiento y evaluación de un modelo GRU apilado, regularizado y abandonado

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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))

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=40,

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.

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 219

Figura 6.23 Pérdida de capacitación y


validación en la tarea de
pronóstico de temperatura de Jena
con una red GRU apilada

6.3.8 Uso de RNN bidireccionales


La última técnica introducida en esta sección se denomina RNN bidireccional . Un RNN bidireccional es una variante de
RNN común que puede ofrecer un mayor rendimiento que un RNN normal en determinadas tareas. Se utiliza con frecuencia
en el procesamiento del lenguaje natural.
Podríamos llamarlo la navaja suiza del aprendizaje profundo para el procesamiento del lenguaje natural.

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

puede ser pasado por alto por un RNN unidireccional.


Sorprendentemente, el hecho de que las capas RNN en esta sección hayan procesado secuencias en
El orden cronológico (los pasos temporales más antiguos primero) puede haber sido una decisión arbitraria. Al menos,
es una decisión que no hemos intentado cuestionar hasta ahora. ¿Podrían haber actuado los RNN ?
bastante bien si procesaran secuencias de entrada en orden anticronológico, por ejemplo
(primero los nuevos pasos de tiempo)? Probemos esto en la práctica y veamos qué sucede. Todo lo que necesitas
hacer es escribir una variante del generador de datos donde las secuencias de entrada se revierten a lo largo
la dimensión de tiempo (reemplace la última línea con muestras de rendimiento [:, ::­1, :], objetivos).
Entrenando la misma red de una capa GRU que usó en el primer experimento de este
sección, se obtienen los resultados que se muestran en la figura 6.24.

Con licencia para <nulo>


Machine Translated by Google

220 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Figura 6.24 Pérdida de entrenamiento y


validación en la tarea de pronóstico
de temperatura de Jena con un GRU
entrenado en secuencias invertidas

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.

Listado 6.42 Entrenamiento y evaluación de un LSTM usando secuencias invertidas

desde keras.datasets importar imdb


de la secuencia de importación keras.preprocessing
desde keras importan capas
Número de palabras a Corta los textos después de
desde keras.models importar secuencial
considerar como esta cantidad de palabras (entre
características_max = 10000 características las palabras más comunes de
maxlen = 500 max_features)

(x_train, y_train), (x_test, y_test) = imdb.load_data(


núm_palabras=max_características)
Cargas
datos x_train = [x[::­1] para x en x_train] Invierte
x_test = [x[::­1] para x en x_test] secuencias

x_train = secuencia.pad_sequences(x_train, maxlen=maxlen) Almohadillas

x_test = secuencia.pad_sequences(x_test, maxlen=maxlen) secuencias

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'])

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 221

historial = model.fit(x_train, y_train, épocas=10,

tamaño_de_lote=128,
división_de_validación=0.2)

Obtienes un rendimiento casi idéntico al del LSTM de orden cronológico.


Sorprendentemente, en un conjunto de datos de texto de este tipo, el procesamiento de orden inverso
funciona tan bien como el procesamiento cronológico, lo que confirma la hipótesis de que, aunque el
orden de las palabras sí importa para comprender el lenguaje, el orden que se utiliza no es crucial. Es
importante destacar que un RNN entrenado en secuencias invertidas aprenderá representaciones
diferentes a las de uno entrenado en las secuencias originales, de la misma manera que usted tendría
diferentes modelos mentales si el tiempo fluyera hacia atrás en el mundo real, si viviera una vida en la
que muriera el primer día y Naciste en tu último día. En el aprendizaje automático, siempre vale la
pena explotar las representaciones que son diferentes pero útiles , y cuanto más difieran, mejor:
ofrecen un nuevo ángulo desde el cual mirar los datos, capturando aspectos de los datos que otros
enfoques pasaron por alto. y, por lo tanto, pueden ayudar a mejorar el rendimiento en una tarea. Ésta
es la intuición detrás del conjunto, un concepto que exploraremos en el capítulo 7.
Un RNN bidireccional explota esta idea para mejorar el rendimiento de los RNN de orden
cronológico. Observa su secuencia de entrada en ambos sentidos (ver figura 6.25), obteniendo
representaciones potencialmente más ricas y capturando patrones que pueden haber pasado
desapercibidos con la versión de orden cronológico únicamente.

Datos de entrada

Fusionar
(añadir, concatenar)

RNN RNN

aBCDe e, d, c, b, a

Secuencia invertido Figura 6.25 Cómo


aBCDe
cronológica secuencia funciona una capa RNN bidireccional

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 .

Listado 6.43 Entrenamiento y evaluación de un LSTM bidireccional

modelo = Sequential()
model.add(layers.Embedding(max_features, 32))
model.add(layers.Bidireccional(layers.LSTM(32))) model.add(layers.Dense(1,
activación='sigmoide') )

Con licencia para <nulo>


Machine Translated by Google

222 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

model.compile(optimizer='rmsprop', pérdida='binary_crossentropy', metrics=['acc']) historial = model.fit(x_train, y_train,


epochs=10, batch_size=128, validation_split=0.2)

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.

Ahora intentemos el mismo enfoque en la tarea de predicción de la temperatura.

Listado 6.44 Entrenamiento de un GRU bidireccional

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

modelo = Secuencial()
modelo.add(capas.Bidireccional(
capas.GRU(32), input_shape=(Ninguno, float_data.shape[­1])))
modelo.add(capas.Densa(1))

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=40,

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). ).

6.3.9 Yendo aún más lejos


Hay muchas otras cosas que puedes probar para mejorar el rendimiento en el problema de pronóstico de
temperatura:

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.

Con licencia para <nulo>


Machine Translated by Google

Uso avanzado de redes neuronales recurrentes 223

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.

Con licencia para <nulo>


Machine Translated by Google

224 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Mercados y aprendizaje automático


Algunos lectores seguramente querrán tomar las técnicas que hemos presentado aquí y
probarlas en el problema de pronosticar el precio futuro de los valores en el mercado de
valores (o los tipos de cambio de divisas, etc.). Los mercados tienen características
estadísticas muy diferentes a las de los fenómenos naturales, como los patrones climáticos.
Intentar utilizar el aprendizaje automático para ganarle a los mercados, cuando solo se tiene
acceso a datos disponibles públicamente, es una tarea difícil y es probable que se pierda
tiempo y recursos sin nada que mostrar.

Recuerde siempre que cuando se trata de mercados, el desempeño pasado no es un buen


predictor de los retornos futuros; mirar por el espejo retrovisor es una mala manera de conducir.
El aprendizaje automático, por otro lado, es aplicable a conjuntos de datos donde el pasado
es un buen predictor del futuro.

Con licencia para <nulo>


Machine Translated by Google

Procesamiento de secuencias con convnets. 225

6.4 Procesamiento de secuencias con convnets


En el capítulo 5, aprendió sobre las redes neuronales convolucionales (convnets) y cómo
Funcionan particularmente bien en problemas de visión por computadora, debido a su capacidad para
operar de forma convolucional, extrayendo características de parches de entrada locales y permitiendo
Modularidad de representación y eficiencia de datos. Las mismas propiedades que hacen que los conv­nets sobresalgan
en visión por computadora también los hacen muy relevantes para el procesamiento de secuencias.
El tiempo puede tratarse como una dimensión espacial, como la altura o el ancho de una imagen 2D .
Estas redes convnet 1D pueden ser competitivas con las RNN en ciertos procesos de procesamiento de secuencias.
problemas, generalmente a un costo computacional considerablemente más barato. Recientemente, las conv­nets 1D ,
normalmente utilizadas con núcleos dilatados, se han utilizado con gran éxito para audio.
generación y traducción automática. Además de estos éxitos específicos, desde hace tiempo
Se sabe que las pequeñas convnets 1D pueden ofrecer una alternativa rápida a las RNN para tareas simples.
como la clasificación de textos y la previsión de series temporales.

6.4.1 Comprensión de la convolución 1D para datos de secuencia


Las capas de convolución introducidas anteriormente eran convoluciones 2D , extrayendo 2D
parches de tensores de imagen y aplicando una transformación idéntica a cada parche.
De la misma manera, puedes usar convoluciones 1D , extrayendo parches 1D locales (subsecuencias) de secuencias
(ver figura 6.26).

ventana de
talla 5

Aporte
Aporte
características

Tiempo

Extraído

parche

Producto escalar
con pesas

Figura 6.26 Cómo funciona la


Funciones
convolución 1D: cada paso de tiempo
Producción
de salida de salida se obtiene de un parche
temporal en la secuencia de entrada.

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 conv­nets 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

Con licencia para <nulo>


Machine Translated by Google

226 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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.

6.4.2 Agrupación 1D para datos de secuencia

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).

6.4.3 Implementación de una conversión 1D

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.

Listado 6.45 Preparando los datos de IMDB

desde keras.datasets importar imdb desde


keras.preprocesamiento secuencia de importación

max_features = 10000 max_len


= 500

print('Cargando datos...') (x_train,


y_train), (x_test, y_test) = imdb.load_data(num_words=max_features) print(len(x_train), 'entrenar secuencias') print(len(x_test), '
secuencias de prueba')

print('Secuencias de pad (muestras x tiempo)') x_train =


secuencia.pad_sequences(x_train, maxlen=max_len) x_test = secuencia.pad_sequences(x_test,
maxlen=max_len) print('x_train forma:', x_train.shape) print( 'x_test forma:', x_test.shape)

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.

Con licencia para <nulo>


Machine Translated by Google

Procesamiento de secuencias con convnets. 227

Este es el ejemplo de convnet 1D para el conjunto de datos IMDB .

Listado 6.46 Entrenamiento y evaluación de un convnet 1D simple en los datos de IMDB

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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=1e­4),
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.

Figura 6.27 Pérdida de


entrenamiento y validación en IMDB con un
conversión 1D simple

Con licencia para <nulo>


Machine Translated by Google

228 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Figura 6.28 Precisión de


entrenamiento y validación en
IMDB con una simple conversión 1D

6.4.4 Combinación de CNN y RNN para procesar secuencias largas


Debido a que las convnets 1D procesan parches de entrada de forma independiente, no son sensibles
al orden de los pasos de tiempo (más allá de una escala local, el tamaño de las ventanas de
convolución), a diferencia de las RNN. Por supuesto, para reconocer patrones a largo plazo, se
pueden apilar muchas capas de convolución y capas de agrupación, lo que da como resultado capas
superiores que verán grandes fragmentos de las entradas originales, pero sigue siendo una forma
bastante débil de inducir sensibilidad al orden. Una forma de evidenciar esta debilidad es probar
convnets 1D en el problema de pronóstico de temperatura, donde la sensibilidad al orden es clave
para producir buenas predicciones. El siguiente ejemplo reutiliza las siguientes variables definidas
anteriormente: float_data, train_gen, val_gen y val_steps.

Listado 6.47 Entrenamiento y evaluación de un convnet 1D simple sobre los datos de Jena

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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))

model.compile(optimizer=RMSprop(), pérdida='mae') historial =


model.fit_generator(train_gen, pasos_por_epoch=500,
épocas=20,

validation_data=val_gen,
validation_steps=val_steps)

Con licencia para <nulo>


Machine Translated by Google

Procesamiento de secuencias con convnets. 229

La Figura 6.29 muestra los MAE de capacitación y validación.

Figura 6.29 Pérdida de


capacitación y validación en la
tarea de pronóstico de temperatura
de Jena con una simple conversión 1D

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 conv­net 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

la parte RNN de la red.


Esta técnica no se ve a menudo en
CNN 1D
trabajos de investigación y aplicaciones prácticas,
posiblemente porque no es muy conocido. Es eficaz y
debería ser más común. Intentemos
secuencia larga
en el conjunto de datos de pronóstico de temperatura.
Debido a que esta estrategia le permite manipular Figura 6.30 Combinando una convnet 1D y un
secuencias mucho más largas, puede RNN para procesar secuencias largas

Con licencia para <nulo>


Machine Translated by Google

230 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

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

desde keras.models importa Sequential desde keras


importa capas desde keras.optimizers
importa RMSprop

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')

Con licencia para <nulo>


Machine Translated by Google

Procesamiento de secuencias con convnets. 231

historia = model.fit_generator(train_gen,
pasos_por_epoch=500,
épocas = 20,
datos_validación=val_gen,
pasos_validación=val_pasos)

Figura 6.31 Pérdida de capacitación y


validación en la tarea de pronóstico
de temperatura de Jena con un convnet
1D seguido de un GRU

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 Max­Pooling1D ,
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 .

Con licencia para <nulo>


Machine Translated by Google

232 CAPÍTULO 6 Aprendizaje profundo para texto y secuencias

Resumen del capítulo


En este capítulo, aprendió las siguientes técnicas, que son ampliamente aplicables a
cualquier conjunto de datos de secuencia, desde texto hasta series temporales:
– Cómo tokenizar texto

– Qué son las incrustaciones de palabras y cómo usarlas –


Qué son las redes recurrentes y cómo usarlas – Cómo apilar
capas RNN y usar RNN bidireccionales para construir modelos de procesamiento de
secuencias más potentes – Cómo
usar convnets 1D para procesamiento de secuencias: cómo
combinar convnets 1D y RNN para procesar secuencias largas Puede usar RNN

para regresión de series temporales (“predecir el futuro”), clasificación de series temporales,


detección de anomalías en series temporales y etiquetado de secuencias (como identificar
nombres o fechas en oraciones). ).

De manera similar, puede usar convnets 1D para traducción automática (modelos


convolucionales de secuencia a secuencia, como SliceNet a ), clasificación de documentos
y corrección ortográfica.

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.

Si el ordenamiento global no es fundamentalmente significativo, entonces las convnets 1D


funcionarán al menos igual de bien y serán más baratas. Este suele ser el caso de los datos
de texto, donde una palabra clave que se encuentra al principio de una oración es tan
significativa como una palabra clave que se encuentra al final.

a Véase https://arxiv.org/abs/1706.03059.

Con licencia para <nulo>


Machine Translated by Google

Mejores prácticas avanzadas de


aprendizaje profundo

Este capítulo cubre


La API funcional de Keras

Usar devoluciones de llamada

de Keras Trabajar con la herramienta de visualización

TensorBoard Mejores prácticas importantes para desarrollar


modelos de última generación

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

Con licencia para <nulo>


Machine Translated by Google

234 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

7.1 Más allá del modelo secuencial: la


API funcional de Keras
Hasta ahora, todas las redes neuronales presentadas en este libro.
Producción

Se han implementado utilizando el modelo secuencial .


El modelo secuencial supone que el
Capa
La red tiene exactamente una entrada y exactamente una salida, y
que consiste en una pila lineal de capas (ver figura 7.1).
Esta es una suposición comúnmente verificada; la configuración es Capa

tan común que hemos podido cubrir muchas


Temas y aplicaciones prácticas en estas páginas hasta el momento. Capa

utilizando sólo la clase de modelo Sequential . Pero este conjunto de


supuestos es demasiado inflexible en varios casos. Alguno Secuencial
Aporte
Las redes requieren varias entradas independientes, otras
requieren múltiples salidas y algunas redes tienen ramificaciones internas Figura 7.1 Una secuencia
entre capas que las hacen parecer modelo: una pila lineal de capas
gráficos de capas en lugar de pilas lineales de capas.
Algunas tareas, por ejemplo, requieren entradas multimodales : fusionan datos provenientes de
diferentes fuentes de entrada, procesando cada tipo de datos utilizando diferentes tipos de neuronas
capas. Imagine un modelo de aprendizaje profundo que intenta predecir el precio de mercado más probable de
una prenda de vestir de segunda mano, utilizando los siguientes datos: metadatos proporcionados por el usuario
(como la marca del artículo, su antigüedad, etc.), una descripción de texto proporcionada por el usuario y una
imagen del artículo. Si solo tuviera los metadatos disponibles, podría codificarlos en caliente
y utilizar una red densamente conectada para predecir el precio. Si tuvieras solo el texto
descripción disponible, puede usar un RNN o un convnet 1D . Si solo tuviera la imagen, podría usar una
conversión 2D . ¿Pero cómo puedes utilizar los tres al mismo tiempo? A
Un enfoque ingenuo sería entrenar tres modelos separados y luego hacer un promedio ponderado de sus
predicciones. Pero esto puede ser subóptimo, porque la información
extraído por los modelos puede ser redundante. Una mejor manera es aprender conjuntamente un modelo de
datos más preciso mediante el uso de un modelo que pueda ver todas las modalidades de entrada disponibles.
simultáneamente: un modelo con tres ramas de entrada (ver figura 7.2).

Predicción de precios

Fusionando
módulo

Módulo denso Módulo RNN módulo de conversión

Metadatos Descripción del texto Imagen Figura 7.2 Un modelo de múltiples entradas

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 235

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

Figura 7.3 Un modelo de múltiples salidas (o múltiples cabezales)

Además, muchas arquitecturas neuronales desarrolladas recientemente requieren una topología


de red no lineal: redes estructuradas como gráficos acíclicos dirigidos. La familia de redes
Inception (desarrollada por Szegedy et al. en Google),1 por ejemplo, se basa en módulos
Inception, donde la entrada es procesada por varias ramas convolucionales paralelas cuyas
salidas luego se fusionan nuevamente en un solo tensor (ver figura 7.4). . También existe la
tendencia reciente de agregar conexiones residuales a un modelo, que comenzó con la familia
de redes ResNet (desarrollada por He et al. en Microsoft).2 Una conexión residual consiste en
reinyectar representaciones previas en el flujo de datos descendente mediante agregar un tensor
de salida pasado a un tensor de salida posterior (ver figura 7.5), lo que ayuda a evitar la pérdida
de información a lo largo del flujo de procesamiento de datos. Hay muchos otros ejemplos de
redes similares a gráficos.

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.

Con licencia para <nulo>


Machine Translated by Google

236 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Producción

Concatenar

Conv2D
3 × 3, zancadas = 2

Conv2D Conv2D Conv2D


3 × 3, zancadas = 2 3×3 3×3

Conv2D Conv2D AvgPool2D 3 Conv2D


1 × 1, zancadas = 2 1×1 × 3, zancadas = 2 1×1

Aporte

Figura 7.4 Un módulo Inception: un subgrafo de capas con varias ramas


convolucionales paralelas

Capa

Capa Residual
conexión

Capa

Figura 7.5 Una conexión residual: reinyección


de información previa en sentido
Capa
descendente mediante la adición de mapas de características

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.

7.1.1 Introducción a la API funcional


En la API funcional, manipulas directamente los tensores y usas capas como funciones que
toman tensores y devuelven tensores (de ahí el nombre API funcional ):
de keras import Entrada, capas

input_tensor = Entrada(forma=(32,)) un tensor

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 237

denso = capas.Denso(32, activación='relu') Una capa es una función.

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:

de keras.models importar secuencial, modelo


desde keras importan capas
de entrada de importación de keras Modelo secuencial, que
ya conoces
seq_model = Secuencial()
seq_model.add(layers.Dense(32, activación='relu', input_shape=(64,)))
seq_model.add(layers.Dense(32, activación='relu'))
seq_model.add(capas.Dense(10, activación='softmax'))

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)

modelo = Modelo(tensor_entrada, tensor_salida)


La clase Modelo convierte un tensor de
Resumen Modelo() ¡Veámoslo! entrada y un tensor de salida en un modelo.

Esto es lo que muestra la llamada a model.summary() :

_________________________________________________________________
Capa (tipo) Forma de salida Parámetro #
==================================================== ================

entrada_1 (capa de entrada) (Ninguno, 64) 0


_________________________________________________________________
denso_1 (denso) (Ninguno, 32) 2080
_________________________________________________________________
denso_2 (denso) (Ninguno, 32) 1056
_________________________________________________________________
denso_3 (denso) (Ninguno, 10) 330
==================================================== ================

Parámetros totales: 3.466


Parámetros entrenables: 3.466
Parámetros no entrenables: 0
_________________________________________________________________

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:

>>> entrada_no relacionada = Entrada(forma=(32,))


>>> bad_model = modelo = Modelo(entrada_no relacionada, tensor_salida)

Con licencia para <nulo>


Machine Translated by Google

238 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

RuntimeError: Gráfico desconectado: no se puede


obtener valor para tensor
Tensor("input_1:0", shape=(?, 64), dtype=float32) en la capa "input_1".

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))

model.fit(x_train, y_train, épocas=10, tamaño_lote=128)


Entrena el modelo
puntuación = modelo.evaluar(x_train, y_train) durante 10 épocas.
Evalúa el
modelo.
7.1.2 Modelos de entradas múltiples

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

Texto de referencia Pregunta Figura 7.6 Un modelo de preguntas y respuestas

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 239

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

de keras.models importar modelo de keras


importar capas de keras importar entrada

La entrada de texto es una secuencia

tamaño_vocabulario_texto = 10000 de números enteros de longitud variable.

tamaño_vocabulario_pregunta = 10000 Tenga en cuenta que, opcionalmente,

tamaño_vocabulario_respuesta = 500 puede nombrar las entradas.

text_input = Entrada(forma=(Ninguno,), dtype='int32', nombre='texto')

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

question_input = Entrada(forma=(Ninguno,), dtype='int32', Codifica los vectores en un solo


nombre='pregunta') vector a través de un LSTM

Mismo proceso (con diferentes instancias de


pregunta_incrustada = capas.Incrustación (32,
capa) para la pregunta
tamaño_vocabulario_pregunta)(entrada_pregunta) pregunta_codificada =
capas.LSTM(16)(pregunta_incrustada)

concatenado = capas.concatenar([texto_codificado, pregunta_codificada],


eje = ­1)
Concatena la pregunta codificada y

respuesta = capas.Dense(answer_vocabulary_size, activación='softmax') el texto codificado.


(concatenado)
Agrega un softmax
modelo = Modelo([text_input, question_input], respuesta) clasificador en la parte superior

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.

Listado 7.2 Alimentación de datos a un modelo de múltiples entradas

importar numpy como np Genera ficticio


numerosos datos
núm_muestras = 1000
longitud_máxima = 100

texto = np.random.randint(1, text_vocabulary_size, size=(num_samples,


max_length))

Con licencia para <nulo>


Machine Translated by Google

240 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Las respuestas están


pregunta = np.random.randint(1, question_vocabulary_size, codificadas one­
tamaño=(núm_muestras, longitud_máxima)) hot, no números enteros
respuestas = np.random.randint(0, 1,
tamaño=(núm_muestras, respuesta_vocabulario_tamaño))

model.fit([texto, pregunta], respuestas, épocas=10, tamaño_lote=128)

model.fit({'texto': texto, 'pregunta': pregunta}, respuestas,


épocas=10, tamaño_lote=128)

Ajuste utilizando una lista de entradas Ajuste usando un diccionario de


entradas (solo si las entradas tienen nombre)

7.1.3 Modelos de múltiples salidas

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).

Listado 7.3 Implementación de API funcional de un modelo de tres salidas

de keras importar capas de keras


importar Entrada de keras.models
importar modelo

tamaño_vocabulario = 50000
núm_grupos_ingresos = 10

posts_input = Entrada(forma=(Ninguno,), dtype='int32', nombre='publicaciones')


publicaciones_incrustadas = capas.Embedding(256, vocabulario_tamaño)(posts_input) x =
capas.Conv1D(128, 5, activación='relu ')(embedded_posts) x = capas.MaxPooling1D(5)(x) x =
capas.Conv1D(256, 5, activación='relu')(x) x
= capas.Conv1D(256, 5, activación='relu' )(x) x = capas.MaxPooling1D(5)
(x) x = capas.Conv1D(256, 5, activación='relu')(x) x = capas.Conv1D(256,
5, activación='relu') (x) x =
capas.GlobalMaxPooling1D()(x) x = capas.Dense(128, activación='relu')
(x)

Tenga en cuenta que las


capas de salida reciben nombres.
predicción_edad = capas.Dense(1, nombre='edad')(x) predicción_ingresos
= capas.Dense(núm_grupos_ingresos, activación='softmax', nombre='ingresos')
(x) predicción_género =
capas.Dense(1, activación=
'sigmoide', nombre='género')(x)

modelo = Modelo (publicaciones_entrada,


[predicción_edad, predicción_ingresos, predicción_género])

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 241

Edad Ingreso Género

Denso Denso Denso

conversión 1D

Figura 7.7 Un modelo de redes


Publicaciones en redes sociales sociales con tres cabezas

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.

Listado 7.4 Opciones de compilación de un modelo multisalida: pérdidas múltiples

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 .

Listado 7.5 Opciones de compilación de un modelo multisalida: ponderación de pérdida

model.compile(optimizador='rmsprop',
pérdida=['mse', 'categorical_crossentropy', 'binary_crossentropy'], loss_weights=[0.25, 1., 10.])

Con licencia para <nulo>


Machine Translated by Google

242 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

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.

Listado 7.6 Alimentación de datos a un modelo de múltiples salidas

model.fit(publicaciones, [objetivos_edad, objetivos_ingresos, objetivos_género], épocas=10,


tamaño_lote=64)

model.fit(publicaciones, {'edad': objetivos_edad, 'ingresos':


objetivos_ingresos, 'género': Equivalente (solo es posible si le da
objetivos_género}, nombres a las capas de salida)
épocas=10, tamaño_lote=64)

Se supone que age_targets,


Income_targets y Gender_targets son
matrices Numpy.

7.1.4 Gráficos acíclicos dirigidos de capas.


Con la API funcional , no solo puede crear modelos con múltiples entradas y múltiples salidas, sino que
también puede implementar redes con una topología interna compleja.
Las redes neuronales en Keras pueden ser gráficos de capas acíclicos dirigidos arbitrariamente . El calificativo
acíclico es importante: estos gráficos no pueden tener ciclos. Es imposible que un tensor x se convierta en la
entrada de una de las capas que generó x. Los únicos bucles de procesamiento permitidos (es decir, conexiones
recurrentes) son los internos a las capas recurrentes.
Varios componentes comunes de las redes neuronales se implementan como gráficos. Dos de ellos
notables son los módulos Inception y las conexiones residuales. Para comprender mejor cómo se puede utilizar
la API funcional para crear gráficos de capas, echemos un vistazo a cómo se pueden implementar ambas en
Keras.

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 2013­2014, 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.

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 243

características espaciales y características de canal, lo cual es más eficiente que aprenderlas


conjuntamente. También son posibles versiones más complejas de un módulo Inception, que
normalmente implican operaciones de agrupación, diferentes tamaños de convolución espacial
(por ejemplo, 5 × 5 en lugar de 3 × 3 en algunas ramas) y ramas sin convolución espacial (solo
una convolución de 1 × 1). circunvolución). En la figura 7.8 se muestra un ejemplo de un módulo
de este tipo, tomado de Inception V3.

Producción

Concatenar

Conv2D 3
× 3, zancadas = 2

Conv2D 3 Conv2D 3 Conv2D 3


× 3, zancadas = 2 ×3 ×3

Conv2D 1 Conv2D 1 AvgPool2D 3 Conv2D 1


Figura 7.8 Un módulo inicial
× 1, zancadas = 2 ×1 × 3, zancadas = 2 ×1

Aporte

El propósito de las convoluciones 1 × 1


Ya sabes que las convoluciones extraen parches espaciales alrededor de cada mosaico en
un tensor de entrada y aplican la misma transformación a cada parche. Un caso extremo es
cuando los parches extraídos constan de un solo mosaico. La operación de convolución
entonces se vuelve equivalente a ejecutar cada vector de mosaico a través de una capa
Densa : calculará características que mezclan información de los canales del tensor de
entrada, pero no mezclará información en el espacio (porque está mirando un mosaico a la
vez). tiempo). Estas convoluciones 1 × 1 (también llamadas convoluciones puntuales) se
incluyen en los módulos Inception, donde contribuyen a factorizar el aprendizaje de
características por canal y el aprendizaje de características por espacio, algo razonable si se
supone que cada canal es altamente autocorrelacionados en el espacio, pero es posible que
los diferentes canales no estén altamente correlacionados entre sí.

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:

Con licencia para <nulo>


Machine Translated by Google

244 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Cada rama tiene el mismo valor de zancada (2), que es


necesario para mantener todas las salidas de las ramas del En esta rama se produce la zancada.
mismo tamaño para poder concatenarlas. en la capa de convolución espacial.

desde keras importan capas

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)

rama_c = capas.AveragePooling2D(3, zancadas=2)(x)


Branch_c = capas.Conv2D(128, 3, activación='relu')(branch_c)

Branch_d = capas.Conv2D(128, 1, activación='relu')(x)


Branch_d = capas.Conv2D(128, 3, activación='relu')(branch_d)
Branch_d = capas.Conv2D(128, 3, activación='relu', zancadas=2)(branch_d)

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.

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 245

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:

desde keras importan capas Aplica una transformación a 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)

residual = capas.Conv2D(128, 1, zancadas=2, padding='mismo')(x)

y = capas.add([y, residual]) Agrega el tensor residual


nuevamente a las características de salida.

Cuellos de botella representacionales en el aprendizaje profundo


En un modelo secuencial , cada capa de representación sucesiva se construye sobre la
anterior, lo que significa que sólo tiene acceso a la información contenida en la activación de la
capa anterior. Si una capa es demasiado pequeña (por ejemplo, tiene características que
son de dimensiones demasiado bajas), entonces el modelo estará limitado por la cantidad de
información que se puede incluir en las activaciones de esta capa.

Con licencia para <nulo>


Machine Translated by Google

246 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

(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.

Gradientes de desaparición en el aprendizaje profundo


La retropropagación, el algoritmo maestro utilizado para entrenar redes neuronales profundas, funciona propagando
una señal de retroalimentación desde la pérdida de salida hasta las capas anteriores. Si esta señal de
retroalimentación tiene que propagarse a través de una pila profunda de capas, la señal puede volverse tenue o
incluso perderse por completo, lo que hace que la red no se pueda entrenar. Este problema se conoce como
gradientes que desaparecen.

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.

7.1.5 Peso compartido de las capas

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

Con licencia para <nulo>


Machine Translated by Google

Más allá del modelo secuencial: la API funcional de Keras 247

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 :

Crea una instancia única


de keras importar capas de keras Capa LSTM, una vez
importar Entrada de keras.models
importar modelo Construyendo la rama izquierda del
modelo: las entradas son secuencias de
lstm = capas.LSTM(32)
vectores de longitud variable de tamaño 128.
entrada_izquierda = Entrada(forma=(Ninguna, 128))
salida_izquierda = lstm(entrada_izquierda) Construyendo la rama derecha del modelo:
cuando llamas a una instancia de capa
entrada_derecha = Entrada(forma=(Ninguno, 128))
existente, reutilizas sus pesos.
salida_derecha = lstm(entrada_derecha)

fusionado = capas.concatenate([salida_izquierda, salida_derecha], eje=­1) predicciones = capas.Dense(1,


activación='sigmoide')(fusionado)

modelo = Modelo([entrada_izquierda, entrada_derecha], predicciones)


model.fit([datos_izquierda, datos_derecha], objetivos)

Construye el clasificador encima Creación de instancias y entrenamiento del modelo: cuando


entrena dicho modelo, los pesos de la capa LSTM se actualizan
en función de ambas entradas.

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.

7.1.6 Modelos como capas


Es importante destacar que en la API funcional, los modelos se pueden usar como se usarían las capas; de
hecho, puedes pensar en un modelo como una "capa más grande". Esto es cierto tanto para las clases
Sequential como para Model . Esto significa que puedes llamar a un modelo en un tensor de entrada y
recuperar un tensor de salida:

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:

y1, y2 = modelo([x1, x2])

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

Con licencia para <nulo>


Machine Translated by Google

248 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

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:

desde keras importan capas


El procesamiento de imágenes base
desde aplicaciones de importación de keras
El modelo es la red Xception.
de entrada de importación de keras
(solo base convolucional).

xception_base = aplicaciones.Xception(pesos=Ninguno,
incluir_top=Falso)

entrada_izquierda = Entrada(forma=(250, 250, 3))


Las entradas son imágenes RGB
entrada_derecha = Entrada(forma=(250, 250, 3))
de 250×250.

características_izquierdas = xception_base(entrada_izquierda) Llama dos veces al mismo


entrada_derecha = xception_base(entrada_derecha) modelo de visión.

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

Construyendo arquitecturas avanzadas de redes neuronales profundas. Ahora sabes lo siguiente:

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

topología de red interna, utilizando la API funcional de Keras

Cómo reutilizar los pesos de una capa o modelo en diferentes procesamientos


ramas, llamando a la misma capa o instancia de modelo varias veces

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 249

7.2 Inspeccionar y monitorear modelos de aprendizaje profundo utilizando


devoluciones de llamada de Keras y TensorBoard
En esta sección, revisaremos formas de obtener un mayor acceso y control sobre lo que sucede dentro de su modelo
durante el entrenamiento. Lanzar una ejecución de entrenamiento en un gran conjunto de datos durante decenas de
épocas usando model.fit() o model.fit_generator() puede ser un poco como lanzar un avión de papel: más allá del
impulso inicial, no tienes ningún control sobre su trayectoria o su lugar de aterrizaje. Si desea evitar malos resultados
(y, por lo tanto, aviones de papel desperdiciados), es más inteligente no utilizar un avión de papel, sino un dron que
pueda detectar su entorno, enviar datos a su operador y tomar automáticamente decisiones de dirección basadas en
su dirección. estado actual. Las técnicas que presentamos aquí transformarán la llamada a model.fit() de un avión de
papel a un dron inteligente y autónomo que puede autointrospeccionarse y actuar dinámicamente.

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

Con licencia para <nulo>


Machine Translated by Google

250 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

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):

Las devoluciones de llamada se pasan al modelo a través del Interrumpe el entrenamiento


argumento de devolución de llamada en forma, que toma una lista de cuando se detiene la mejora.
devoluciones de llamada. Puede pasar cualquier número de devoluciones de llamada.
Supervisa la precisión de la

importar keras validación del modelo.

lista_devoluciones de llamada = [ Interrumpe el entrenamiento cuando


keras.callbacks.EarlyStopping( la precisión ha dejado de
monitor='acc', mejorar durante más de una época (es

paciencia = 1, decir, dos épocas)

),
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

métricas=['acc']) ser parte de las métricas del modelo.

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))

LA DEVOLUCIÓN DE LLAMADA DE REDUCELRONPLATEAU

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 :

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 251

callbacks_list = Supervisa la pérdida de


[ keras.callbacks.ReduceLROnPlateau( monitor='val_loss' validación del modelo.

factor=0.1, paciencia=10,
Divide la tasa de aprendizaje por 10 cuando se activa

La devolución de llamada se activa después de que la pérdida


)
de validación haya dejado de mejorar durante 10 épocas.
]

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))

ESCRIBIR TU PROPIA DEVOLUCIÓN DE LLAMADA

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:

on_epoch_begin Llamado al comienzo de cada época.


on_epoch_end Llamado al final de cada época.

on_batch_begin Llamado justo antes de procesar cada lote.


on_batch_end Llamado inmediatamente después de procesar cada lote.

on_train_begin Llamado al inicio del entrenamiento.


on_train_end Llamado al final del 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:

self.model: la instancia del modelo desde la cual se llama la devolución de llamada.


self.validation_data: el valor de lo que se pasó para ajustarlo como datos de validación.

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

clase ActivationLogger(keras.callbacks.Callback): del entrenamiento, para informar a


la devolución de llamada qué modelo
def set_model(yo, modelo): lo llamará.
self.modelo = modelo
Layer_outputs = [layer.output para capa en model.layers] self.activations_model =
keras.models.Model(model.input,
capas_salidas)
Instancia de modelo
def on_epoch_end(self, epoch, logs=None): si self.validation_data es
que devuelve las
Ninguno: genere RuntimeError('Requiere
activaciones de
validation_data.')
cada capa.

Con licencia para <nulo>


Machine Translated by Google

252 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

muestra_validación = self.validation_data[0][0:1] activaciones =


self.activations_model.predict(validation_sample) f = open('activations_at_epoch_' + str(época) +
'.npz', 'w') np.savez(f , activaciones) f.close()

Obtiene la primera muestra de entrada


Guarda matrices en el disco
de los datos de validación.

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.

7.2.2 Introducción a TensorBoard: el


marco de visualización de TensorFlow
Para realizar una buena investigación o desarrollar buenos modelos, necesita retroalimentación rica y
frecuente sobre lo que sucede dentro de sus modelos durante sus experimentos. Ése es el objetivo
de realizar experimentos: obtener información sobre el rendimiento de un modelo: tanta información
como sea posible. Progresar es un proceso iterativo o bucle: comienzas con una idea y la expresas
como un experimento, intentando validar o invalidar tu idea.
Usted ejecuta este experimento y procesa la información que genera. Esto inspira tu próxima idea.
Cuantas más iteraciones de este ciclo puedas ejecutar, más refinadas y poderosas se volverán tus
ideas. Keras le ayuda a pasar de la idea al experimento en el menor tiempo posible, y las GPU rápidas
pueden ayudarle a pasar del experimento al resultado lo más rápido posible. Pero ¿qué pasa con el
procesamiento de los resultados del experimento? Ahí es donde entra en juego Tensor­Board.

Idea

Marco de Marco de
visualización: aprendizaje profundo:
Tablero Tensor Keras

Resultados Experimento

Infraestructura Figura 7.9 El bucle del progreso

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:

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 253

Monitorear visualmente métricas durante el


entrenamiento Visualizar la arquitectura
de su modelo Visualizar histogramas de activaciones y
gradientes Explorar incrustaciones en 3D

Demostremos estas características con un ejemplo simple. Entrenarás a un convnet 1D en la tarea de


análisis de sentimientos de IMDB .
El modelo es similar al que vio en la última sección del capítulo 6. Considerará sólo las 2000 palabras
principales del vocabulario de IMDB , para que la visualización de las incrustaciones de palabras sea más
manejable.

Listado 7.7 Modelo de clasificación de texto para usar con TensorBoard

importar keras
desde keras importar capas desde Número de palabras a

keras.datasets importar imdb desde considerar como características

keras.preprocesamiento importar secuencia Corta los textos después de esta cantidad

max_features = 2000 max_len de palabras (entre las palabras más

= 500 comunes de max_features)

(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features) x_train = secuencia.pad_sequences(x_train,


maxlen=max_len) x_test = secuencia.pad_sequences(x_test, maxlen=max_len)

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.

Listado 7.8 Creando un directorio para archivos de registro de TensorBoard

$ 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.

Con licencia para <nulo>


Machine Translated by Google

254 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Listado 7.9 Entrenando el modelo con una devolución de llamada de TensorBoard

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):

$ tablero tensor ­­logdir=my_log_dir

Luego puede navegar hasta http://localhost:6006 y ver el entrenamiento de su modelo (consulte la


figura 7.10). Además de los gráficos en vivo de las métricas de entrenamiento y validación, obtiene
acceso a la pestaña Histogramas, donde puede encontrar bonitas visualizaciones de histogramas de
valores de activación tomados por sus capas (consulte la figura 7.11).

Figura 7.10 TensorBoard: monitoreo de métricas

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 255

Figura 7.11 TensorBoard: histogramas de activación

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 (t­SNE). 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.

Con licencia para <nulo>


Machine Translated by Google

256 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Figura 7.12 TensorBoard: visualización interactiva de incrustación de palabras en 3D

La pestaña Gráficos muestra una visualización interactiva del gráfico de operaciones de


TensorFlow de bajo nivel subyacentes a su modelo Keras (consulte la figura 7.13). Como puede
ver, están sucediendo muchas más cosas de las que cabría esperar. El modelo que acaba de
crear puede parecer simple cuando se define en Keras (una pequeña pila de capas básicas),
pero en el fondo, necesita construir una estructura gráfica bastante compleja para que funcione.
Mucho de esto está relacionado con el proceso de descenso de gradiente. Esta diferencia de
complejidad entre lo que ves y lo que estás manipulando es la motivación clave para usar Keras
como tu forma de construir modelos, en lugar de trabajar con TensorFlow sin formato para definir todo desde cero.
Keras simplifica enormemente su flujo de trabajo.

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 257

Figura 7.13 TensorBoard: visualización del gráfico TensorFlow

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 pydot­ng , así como la
biblioteca Graphviz . Echemos un vistazo rápido:

desde keras.utils importar plot_model

plot_model(modelo, to_file='model.png')

Esto crea la imagen PNG que se muestra en la figura 7.14.

Con licencia para <nulo>


Machine Translated by Google

258 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Figura 7.14 Un gráfico modelo como gráfico de capas,


generado con plot_model

También tiene la opción de mostrar información de forma en el gráfico de capas. Este


ejemplo visualiza la topología del modelo usando plot_model y la opción show_shapes
(ver figura 7.15):
desde keras.utils importar plot_model

plot_model(modelo, show_shapes=True, to_file='model.png')

Con licencia para <nulo>


Machine Translated by Google

Inspeccionar y monitorear modelos de aprendizaje profundo utilizando devoluciones de llamadas de Keras y TensorBoard 259

Figura 7.15 Un gráfico modelo con información de forma

7.2.3 Conclusión
Las devoluciones de llamada de Keras proporcionan una forma sencilla de monitorear modelos durante el entrenamiento y

tomar medidas automáticamente según el estado del modelo.


Cuando usas TensorFlow, TensorBoard es una excelente manera de visualizar el modelo.

actividad en su navegador. Puede usarlo en modelos Keras a través de la devolución de llamada de


TensorBoard .

Con licencia para <nulo>


Machine Translated by Google

260 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

7.3 Aprovechar al máximo tus modelos


Probar arquitecturas a ciegas funciona bastante bien si sólo necesitas algo que funcione bien. En esta
sección, iremos más allá de "funciona bien" para llegar a "funciona muy bien y gana competencias de
aprendizaje automático" ofreciéndole una guía rápida sobre un conjunto de técnicas que debe conocer
para desarrollar un aprendizaje profundo de última generación. modelos.

7.3.1 Patrones de arquitectura avanzada


Cubrimos en detalle un patrón de diseño importante en la sección anterior: las conexiones residuales.
Hay dos patrones de diseño más que debes conocer: normalización y convolución separable en
profundidad. Estos patrones son especialmente relevantes cuando se construyen convnets profundas
de alto rendimiento, pero también se encuentran comúnmente en muchos otros tipos de arquitecturas.

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:

datos_normalizados = (datos ­ np.mean(datos, eje=...)) / np.std(datos, eje=...)

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.

Con licencia para <nulo>


Machine Translated by Google

Sacar el máximo partido a tus modelos 261

La capa BatchNormalization normalmente se usa después de una capa convolucional o


densamente conectada:

conv_model.add(layers.Conv2D(32, 3, activación='relu')) Después de una capa Conv


conv_model.add(layers.BatchNormalization())

denso_model.add(capas.Dense(32, activación='relu')) Después de una capa densa


denso_model.add(capas.BatchNormalization())

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.

Renormalización por lotes


Una mejora reciente con respecto a la normalización por lotes regular es la renormalización por
lotes, introducida por Ioffe en 2017.a Ofrece claros beneficios sobre la normalización por lotes, sin
costo aparente. Al momento de escribir este artículo, es demasiado pronto para decir si reemplazará
la normalización por lotes, pero creo que es probable. Incluso más recientemente, Klambauer et al.
después de pasar por redes introdujo b que logran mantener los datos normalizados
neuronales autonormalizadas, a través de cualquier capa Densa mediante el uso de una función de
activación específica (selu) y un inicializador específico (lecun_normal). Este esquema, aunque muy
interesante, por ahora se limita a redes densamente conectadas y su utilidad aún no se ha replicado ampliamente.

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., “Self­Normalizing Neural Networks”, Conferencia sobre sistemas de procesamiento
de información neuronal (2017), https://arxiv.org/abs/1706.02515.

CONVOLUCIÓN SEPARABLE EN PROFUNDIDAD

¿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

Con licencia para <nulo>


Machine Translated by Google

262 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

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

Figura 7.16 Convolución separable en


Canales divididos
profundidad: una convolución en
profundidad seguida de una convolución puntual

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:

de keras.models importa secuencial, modelo de keras importa capas

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.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')

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.

Con licencia para <nulo>


Machine Translated by Google

Sacar el máximo partido a tus modelos 263

convoluciones y Xception en mi artículo “Xception: Deep Learning with Depthwise


Convoluciones separables.”8

7.3.2 Optimización de hiperparámetros


Al construir un modelo de aprendizaje profundo, hay que hacer muchas cosas aparentemente arbitrarias.
Decisiones: ¿Cuántas capas deberías apilar? Cuantas unidades o filtros deben entrar
¿cada capa? ¿ Deberías usar relu como activación o una función diferente? ¿Deberías usar
¿ Normalización por lotes después de una capa determinada? ¿Cuánta deserción deberías utilizar? Y entonces
en. Estos parámetros a nivel de arquitectura se denominan hiperparámetros para distinguirlos.
a partir de los parámetros de un modelo, que se entrenan mediante retropropagación.
En la práctica, los ingenieros e investigadores experimentados en aprendizaje automático desarrollan con
el tiempo una intuición sobre lo que funciona y lo que no cuando se trata de estas opciones.

Desarrollan habilidades de ajuste de hiperparámetros. Pero no existen reglas formales. Si quieres


Para llegar al límite de lo que se puede lograr en una tarea determinada, no puedes estar contento.
con decisiones arbitrarias tomadas por un ser humano falible. Tus decisiones iniciales son casi
siempre subóptimo, incluso si tienes buena intuición. Puede refinar sus opciones
ajustarlos a mano y volver a entrenar el modelo repetidamente: eso es a lo que los ingenieros e investigadores
de aprendizaje automático dedican la mayor parte de su tiempo. Pero no debería
Su trabajo como ser humano será jugar con hiperparámetros todo el día; es mejor dejarlo en manos de un
máquina.

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:

1 Elija un conjunto de hiperparámetros (automáticamente).


2 Construya el modelo correspondiente.
3 Ajústalo a tus datos de entrenamiento y mide el rendimiento final en la plataforma de validación.
datos de ción.

4 Elija el siguiente conjunto de hiperparámetros para probar (automáticamente).


5 Repita.
6 Finalmente, mida el rendimiento con los datos de su prueba.

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

8 Véase la nota 5 anterior.

Con licencia para <nulo>


Machine Translated by Google

264 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

en la dirección correcta. Por otro lado, actualizar los hiperparámetros es un gran desafío. Considera lo
siguiente:

Calcular la señal de retroalimentación (¿este conjunto de hiperparámetros conduce a un modelo de


alto rendimiento en esta tarea?) puede ser extremadamente costoso: requiere crear y entrenar un
nuevo modelo desde cero en su conjunto de datos.
El espacio de hiperparámetros generalmente está formado por decisiones discretas y, por lo tanto,
no es continuo ni diferenciable. Por lo tanto, normalmente no se puede realizar un descenso de
gradiente en un espacio de hiperparámetros. En su lugar, debe confiar en técnicas de optimización
sin gradientes, que naturalmente son mucho menos eficientes que el descenso de gradientes.

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.

7.3.3 Conjunto de modelos


Otra técnica poderosa para obtener los mejores resultados posibles en una tarea es el ensamblaje de
modelos. El ensamblaje consiste en agrupar las predicciones de un conjunto de modelos diferentes para
producir mejores predicciones. Si observa las competiciones de aprendizaje automático, en particular en
Kaggle, verá que los ganadores utilizan conjuntos muy grandes de modelos que inevitablemente superan a
cualquier modelo, sin importar cuán bueno sea.

Con licencia para <nulo>


Machine Translated by Google

Sacar el máximo partido a tus modelos 265

El ensamblaje se basa en el supuesto de que diferentes modelos buenos entrenados de forma


independiente probablemente sean buenos por diferentes razones: cada modelo analiza aspectos ligeramente
diferentes de los datos para hacer sus predicciones, obteniendo parte de la “verdad”, pero no toda la información.
él. Quizás estés familiarizado con la antigua parábola de los ciegos y el elefante: un grupo de ciegos se
encuentra con un elefante por primera vez y trata de entender qué es el elefante tocándolo. Cada hombre toca
una parte diferente del cuerpo del elefante: sólo una parte, como el tronco o una pata. Luego los hombres se
describen unos a otros qué es un elefante: “Es como una serpiente”, “Como un pilar o un árbol”, etc.

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:

Utilice cuatro modelos diferentes para calcular las predicciones iniciales.

preds_a = model_a.predict(x_val) preds_b =


model_b.predict(x_val) preds_c = Esta nueva matriz de predicción
model_c.predict(x_val) preds_d = debería ser más precisa que

model_d.predict(x_val) cualquiera de las iniciales.

final_preds = 0,25 * (preds_a + preds_b + preds_c + preds_d)

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 Nelder­Mead:

preds_a = model_a.predict(x_val) preds_b =


model_b.predict(x_val) preds_c = Se supone que estos pesos
model_c.predict(x_val) preds_d = (0,5, 0,25, 0,1, 0,15) se aprenden
model_d.predict(x_val) empíricamente.

final_preds = 0,5 * preds_a + 0,25 * preds_b + 0,1 * preds_c + 0,15 * preds_d

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

Con licencia para <nulo>


Machine Translated by Google

266 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

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/higgs­boson) 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

Con licencia para <nulo>


Machine Translated by Google

Sacar el máximo partido a tus modelos 267

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).

Con licencia para <nulo>


Machine Translated by Google

268 CAPÍTULO 7 Mejores prácticas avanzadas de aprendizaje profundo

Resumen del capítulo


En este capítulo, aprendió lo siguiente: – Cómo
construir modelos como gráficos arbitrarios de capas, reutilizar capas (peso
compartido de capas) y usar modelos como funciones de Python (plantillas de modelos).
– Puede utilizar las devoluciones de llamada de Keras para monitorear sus modelos durante el entrenamiento
y tomar medidas según el estado del modelo.

– TensorBoard le permite visualizar métricas, histogramas de activación y


incluso incrustar espacios.

– 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.

Con licencia para <nulo>


Machine Translated by Google

Aprendizaje profundo generativo

Este capítulo cubre


Generación de texto con LSTM

Implementación de DeepDream

Realización de transferencia de estilo neuronal

Codificadores automáticos variacionales

Comprender las redes generativas de confrontación

El potencial de la inteligencia artificial para emular los procesos de pensamiento humanos


va más allá de tareas pasivas como el reconocimiento de objetos y tareas principalmente
reactivas como conducir un automóvil. Se extiende bien a las actividades creativas. Cuando
afirmé por primera vez que en un futuro no muy lejano, la mayor parte del contenido
cultural que consumimos se creará con la ayuda sustancial de las IA, me encontré con
total incredulidad, incluso por parte del aprendizaje automático de larga data. practicantes.
Eso fue en 2014. Tres años después, la incredulidad ha disminuido, a una velocidad
increíble. En el verano de 2015, nos entretuvo el algoritmo DeepDream de Google que
convertía una imagen en un desastre psicodélico de ojos de perro y artefactos pareidólicos;
En 2016, utilizamos la aplicación Prisma para convertir fotografías en pinturas de varios
estilos. En el verano de 2016, se dirigió un cortometraje experimental, Sunspring, utilizando
un guión escrito mediante un algoritmo de memoria a corto plazo (LSTM) , completo con
diálogos. Quizás hayas escuchado recientemente música generada tentativamente por una red neuronal.

269

Con licencia para <nulo>


Machine Translated by Google

270 CAPÍTULO 8 Aprendizaje profundo generativo

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

Liberado de tediosos cálculos, el compositor puede dedicarse a la generalidad.


problemas que plantea la nueva forma musical y explorar los rincones de esta
formulario mientras modifica los valores de los datos de entrada. Por ejemplo, puede probar todos
combinaciones instrumentales desde solistas hasta orquestas de cámara y grandes orquestas. Con
Con la ayuda de ordenadores electrónicos, el compositor se convierte en una especie de piloto: aprieta los botones,
introduce coordenadas y supervisa los controles de una nave cósmica que navega en el espacio
del sonido, a través de constelaciones sónicas y galaxias que antes sólo podía vislumbrar como
un sueño lejano.

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. 253­254 (1963).

Con licencia para <nulo>


Machine Translated by Google

Generación de texto con LSTM 271

8.1 Generación de texto con LSTM


En esta sección, exploraremos cómo se pueden usar las redes neuronales recurrentes para generar
datos de secuencia. Usaremos la generación de texto como ejemplo, pero exactamente las mismas técnicas se
pueden generalizar a cualquier tipo de datos de secuencia: se puede aplicar a
secuencias de notas musicales para generar nueva música, hasta series temporales de datos de pinceladas (por
ejemplo, grabadas mientras un artista pinta en un iPad) para generar
cuadros trazo a trazo, etcétera.
La generación de datos de secuencia no se limita de ninguna manera a la generación de contenido artístico. Él
se ha aplicado con éxito a la síntesis de voz y a la generación de diálogos para chatbots. La función Smart Reply
que Google lanzó en 2016, capaz de automáticamente
generar una selección de respuestas rápidas a correos electrónicos o mensajes de texto, funciona con técnicas
similares.

8.1.1 Una breve historia de las redes generativas recurrentes


A finales de 2014, pocas personas habían visto las iniciales LSTM, incluso en el mundo del aprendizaje automático.
comunidad. Las aplicaciones exitosas de generación de datos de secuencia con redes recurrentes comenzaron a
aparecer en la corriente principal en 2016. Pero estas técnicas tienen un
historia bastante larga, comenzando con el desarrollo del algoritmo LSTM en 1997.2 Este
Se utilizó un nuevo algoritmo desde el principio para generar texto carácter por carácter.
En 2002, Douglas Eck, entonces en el laboratorio de Schmidhuber en Suiza, aplicó LSTM para
generación musical por primera vez, con resultados prometedores. Eck es ahora investigador en
Google Brain, y en 2016 inició allí un nuevo grupo de investigación, llamado Magenta,
centrado en aplicar técnicas modernas de aprendizaje profundo para producir música atractiva.
A veces, las buenas ideas tardan 15 años en ponerse en marcha.
A finales de la década de 2000 y principios de la de 2010, Alex Graves realizó un importante trabajo pionero en
utilizando redes recurrentes para la generación de datos de secuencia. En particular, su trabajo de 2013.
sobre la aplicación de redes de densidad de mezcla recurrentes para generar escritura similar a la humana
Algunos consideran que el uso de series temporales de posiciones del lápiz es un punto de inflexión.3
La aplicación de redes neuronales en ese momento específico capturó para mí la
noción de máquinas que sueñan y fue una inspiración importante en la época en que
comenzó a desarrollar Keras. Graves dejó un comentario similar comentado escondido en un
Archivo LaTeX de 2013 subido al servidor de preimpresión arXiv: “generar datos secuenciales es
lo más cerca que están las computadoras de soñar”. Varios años después, damos por sentados muchos de estos
avances; pero en ese momento era difícil ver las demostraciones de Graves.
y no alejarse asombrado por las posibilidades.
Desde entonces, las redes neuronales recurrentes se han utilizado con éxito para la generación de música,
generación de diálogos, generación de imágenes, síntesis de voz y diseño de moléculas.
Incluso se utilizaron para producir el guión de una película que luego fue elegida con actores en vivo.

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.

Con licencia para <nulo>


Machine Translated by Google

272 CAPÍTULO 8 Aprendizaje profundo generativo

8.1.2 ¿Cómo se generan datos de secuencia?


La forma universal de generar datos de secuencia en el aprendizaje profundo es entrenar una red
(generalmente un RNN o un convnet) para predecir el siguiente token o los siguientes tokens en una
secuencia, utilizando los tokens anteriores como entrada. Por ejemplo, dada la entrada "el gato está
en ma", la red está entrenada para predecir el objetivo t, el siguiente carácter. Como es habitual cuando
se trabaja con datos de texto, los tokens suelen ser palabras o caracteres, y cualquier red que pueda
modelar la probabilidad del siguiente token teniendo en cuenta los anteriores se denomina modelo de
lenguaje. Un modelo de lenguaje captura el espacio latente del lenguaje: su estructura estadística.
Una vez que tenga un modelo de lenguaje entrenado, puede tomar muestras de él (generar
nuevas secuencias): le proporciona una cadena inicial de texto (llamada datos condicionantes), le pide
que genere el siguiente carácter o la siguiente palabra (incluso puede generar varios tokens a la vez),
agregue la salida generada nuevamente a los datos de entrada y repita el proceso muchas veces
(consulte la figura 8.1). Este bucle le permite generar secuencias de longitud arbitraria que reflejan la
estructura de los datos con los que se entrenó el modelo: secuencias que parecen casi oraciones
escritas por humanos. En el ejemplo que presentamos en esta sección, tomará una capa LSTM , la
alimentará con cadenas de N caracteres extraídos de un corpus de texto y la entrenará para predecir
el carácter N + 1. La salida del modelo será un softmax sobre todos personajes posibles: una
distribución de probabilidad para el siguiente personaje. Este LSTM se denomina modelo de lenguaje
neuronal a nivel de carácter.

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

8.1.3 La importancia de la estrategia de muestreo


Al generar texto, la forma en que eliges el siguiente carácter es de vital importancia. Un enfoque
ingenuo es el muestreo codicioso, que consiste en elegir siempre el siguiente personaje más probable.
Pero este enfoque da como resultado cadenas repetitivas y predecibles que no parecen un lenguaje
coherente. Un enfoque más interesante toma decisiones ligeramente más sorprendentes: introduce
aleatoriedad en el proceso de muestreo, tomando muestras de la distribución de probabilidad para el
siguiente personaje. Esto se llama muestreo estocástico (recordemos que la estocasticidad es lo que
llamamos aleatoriedad en este campo). En tal configuración, si e tiene una probabilidad de 0,3 de ser
el siguiente personaje, según el modelo, lo elegirás.

Con licencia para <nulo>


Machine Translated by Google

Generación de texto con LSTM 273

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.

Para controlar la cantidad de estocasticidad en el proceso de muestreo, introduciremos un


parámetro llamado temperatura softmax que caracteriza la entropía del
Distribución de probabilidad utilizada para el muestreo: caracteriza cuán sorprendente o predecible
será la elección del siguiente personaje. Dado un valor de temperatura , se calcula una nueva
distribución de probabilidad a partir de la original (la salida softmax del
modelo) reponderándolo de la siguiente manera.

Listado 8.1 Reponderación de una distribución de probabilidad a una temperatura diferente

importar numpy como np

def distribución_repeso(distribución_original, temperatura=0,5):


distribución = np.log(distribución_original) / temperatura
distribución = np.exp(distribución)
distribución de retorno / np.sum(distribución)

Devuelve una versión reponderada


original_distribution es una matriz 1D Numpy de de la distribución original. Es posible
valores de probabilidad que deben sumar 1. la que la suma de la distribución ya no
temperatura es un factor que cuantifica la sea 1, por lo que la divides por su
entropía de la distribución de salida. suma para obtener la nueva distribución.

Con licencia para <nulo>


Machine Translated by Google

274 CAPÍTULO 8 Aprendizaje profundo generativo

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).

temperatura = 0,01 temperatura = 0,2 temperatura = 0,4


tin
.oile
dad tasm
bre leerm
beou lP
e
d

Elementos discretos (caracteres)

temperatura = 0,6 temperatura = 0,8 temperatura = 1,0

Figura 8.2 Diferentes reponderaciones de una distribución de probabilidad. Baja temperatura =


más determinista, alta temperatura = más aleatorio.

8.1.4 Implementación de la generación de texto LSTM a nivel de caracteres

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

Comencemos descargando el corpus y convirtiéndolo a minúsculas.

Listado 8.2 Descarga y análisis del archivo de texto inicial

importar keras
importar numpy como np

ruta = keras.utils.get_file(
'nietzsche.txt',
origen ='https://s3.amazonaws.com/text­datasets/nietzsche.txt')
texto = abrir(ruta).leer().inferior()
print('Longitud del cuerpo:', len(texto))

Con licencia para <nulo>


Machine Translated by Google

Generación de texto con LSTM 275

A continuación, extraerá secuencias parcialmente superpuestas de longitud maxlen, las codificará en


caliente y las empaquetará en una matriz 3D Numpy x de forma (secuencias, maxlen, caracteres_únicos).
Simultáneamente, preparará una matriz y que contiene los objetivos correspondientes: los caracteres
codificados en caliente que vienen después de cada uno extraído.
secuencia.

Listado 8.3 Vectorización de secuencias de caracteres

Extraerás secuencias de 60
caracteres.
maxlen = 60
Probarás una nueva secuencia cada
paso = 3 tres caracteres.

oraciones = [] Contiene las secuencias extraídas.

caracteres_siguientes = []
Sostiene los objetivos (los
para i en rango(0, len(texto) ­ maxlen, paso): oraciones.append(texto[i: i + personajes de seguimiento)

maxlen]) next_chars.append(texto[i + maxlen])


Lista de personajes únicos en
el corpus.
print('Número de secuencias:', len(oraciones))
Diccionario que asigna
chars = sorted(list(set(text))) print('Caracteres caracteres únicos a su índice en
la lista "caracteres"
únicos:', len(chars)) char_indices = dict((char, chars.index(char))
para char en caracteres)

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 One­hot 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

desde keras importan capas

modelo = keras.models.Sequential()
model.add(layers.LSTM(128, input_shape=(maxlen, len(chars)))) model.add(layers.Dense(len(chars),
activación='softmax') )

Con licencia para <nulo>


Machine Translated by Google

276 CAPÍTULO 8 Aprendizaje profundo generativo

Debido a que sus objetivos están codificados en caliente, utilizará categorical_crossentropy como
pérdida para entrenar el modelo.

Listado 8.5 Configuración de compilación del modelo

optimizador = keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizador=optimizador)

ENTRENAR EL MODELO DE LENGUAJE Y MUESTREAR A PARTIR DE ÉL

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.

2 Vuelva a pesar la distribución a una temperatura determinada.


3 Muestre el siguiente carácter al azar según la distribución reponderada.
4 Añade el nuevo carácter al final del texto disponible.

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

muestra def(preds, temperatura=1.0):


preds = np.asarray(preds).astype('float64') preds = np.log(preds) /
temperatura exp_preds = np.exp(preds) preds =
exp_preds / np.sum(exp_preds) probas
= np.random.multinomial (1, preds, 1) devolver
np.argmax(probas)

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.

Listado 8.7 Bucle de generación de texto

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

print('­­­ Generando con semilla: "' + texto_generado + '"') semilla de


texto al azar

para temperatura en [0,2, 0,5, 1,0, 1,2]:


Prueba una gama de diferentes
print('­­­­­­ temperatura:', temperatura) sys.stdout.write(texto_generado)
temperaturas de muestreo

Con licencia para <nulo>


Machine Translated by Google

Generación de texto con LSTM 277

Genera 400 para i en rango(400):


One­hot codifica los
caracteres, a muestreado = np.zeros((1, maxlen, len(chars))) para t, char en
caracteres
partir del texto enumerate(generated_text):
inicial. muestreado[0, t, char_indices[char]] = 1. generados hasta el momento.

preds = model.predict(muestreado, detallado=0)[0] next_index = Muestra


el siguiente
muestra(preds, temperatura) next_char = caracteres[next_index]
personaje

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

Aquí está el resultado con temperatura = 0,5:

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

Y esto es lo que obtienes con temperatura = 1,0:

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

En la época 60, el modelo ha convergido en su mayor parte y el texto comienza a parecer


significativamente más coherente. Aquí está el resultado con temperatura = 0,2:

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

Con licencia para <nulo>


Machine Translated by Google

278 CAPÍTULO 8 Aprendizaje profundo generativo

sujeción de la sujeción de la sujeción del yo con respecto a los sentimientos en la


superioridad en la sujeción de la sujeción del espíritu no es ser un hombre del sentido de la sujeción y dicho a la
fuerza del sentido de la

Aquí está la temperatura = 0,5:

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

Y aquí está la temperatura = 1,0:

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 atu­especity"­­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.

Con licencia para <nulo>


Machine Translated by Google

Generación de texto con LSTM 279

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.

Con licencia para <nulo>


Machine Translated by Google

280 CAPÍTULO 8 Aprendizaje profundo generativo

8.2 Sueño profundo


DeepDream es una técnica artística de modificación de imágenes que utiliza las representaciones
aprendido por redes neuronales convolucionales. Fue lanzado por primera vez por Google en el verano de 2015,
como una implementación escrita utilizando la biblioteca de aprendizaje profundo Caffe (esta
fue varios meses antes del primer lanzamiento público de TensorFlow).4 Rápidamente se convirtió en
una sensación en Internet gracias a las imágenes alucinantes que podría generar (ver, por ejemplo,
figura 8.3), lleno de artefactos algorítmicos de pareidolia, plumas de pájaro y ojos de perro, un
subproducto del hecho de que DeepDream convnet fue entrenado en ImageNet, donde
las razas de perros y las especies de aves están muy sobrerrepresentadas.

Figura 8.3 Ejemplo de una imagen de salida de DeepDream

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:

Con DeepDream, intentas maximizar la activación de capas enteras en lugar de


que el de un filtro específico, mezclando así visualizaciones de un gran número de características a la vez.

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.

Con licencia para <nulo>


Machine Translated by Google

sueño profundo 281

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.

Hagamos algunos DeepDreams.

8.2.1 Implementación de DeepDream en Keras


Comenzará desde un convnet previamente entrenado en ImageNet. En Keras, muchos de estos convnets son
disponibles: VGG16, VGG19, Xception, ResNet50, etc. Puede implementar Deep­Dream con cualquiera de ellos, pero su convnet de

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 Incep­tion V3 que viene con Keras.

Listado 8.8 Cargando el modelo Inception V3 previamente entrenado

de keras.applications importa inception_v3


No entrenará el modelo, por lo que este
desde keras importa el backend como K
comando deshabilita todas las operaciones
K.set_learning_phase(0) específicas del entrenamiento.

modelo = inception_v3.InceptionV3(pesos='imagenet', Construye la red Inception V3, sin su base


convolucional.
incluir_top=Falso)
El modelo se cargará con pesos
ImageNet previamente entrenados.

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

Definitivamente quiero explorar muchas configuraciones diferentes más adelante.

Listado 8.9 Configurando la configuración de DeepDream

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().
}

Con licencia para <nulo>


Machine Translated by Google

282 CAPÍTULO 8 Aprendizaje profundo generativo

Ahora, definamos un tensor que contiene la pérdida: la suma ponderada de la norma L2 de


las activaciones de las capas del listado 8.9.

Listado 8.10 Definición de la pérdida a maximizar

Crea un diccionario que asigna nombres


de capas a instancias de capas.

Layer_dict = dict([(layer.name, Layer) para la capa en model.layers])

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

escalado = K.prod(K.cast(K.shape(activación), 'float32'))


pérdida += coeficiente * K.sum(K.square(activación[:, 2: ­2, 2: ­2, :])) / escala

Agrega la norma L2 de las características de una capa.


Recupera la salida de la capa. a la perdida. Evitas los artefactos fronterizos al
solo involucra píxeles sin borde en la pérdida.

A continuación, puede configurar el proceso de ascenso de gradiente.

Listado 8.11 Proceso de ascenso de gradiente

Este tensor contiene la


imagen generada: el sueño. Calcula los gradientes del sueño con
respecto a la pérdida.
sueño = modelo.entrada

grads = K.gradients(pérdida, sueño)[0] Normaliza los gradientes (truco


importante)
graduados /= K.máximo(K.media(K.abs(graduados)), 1e­7)

salidas = [pérdida, graduados]


fetch_loss_and_grads = K.function([sueño], salidas) Configura una función Keras
para recuperar el valor de la
def eval_loss_and_grads(x): pérdida y los gradientes,
salidas = fetch_loss_and_grads([x]) dada una imagen de entrada.
valor_pérdida = salidas[0]
grad_values = salidas[1]
devolver valor_pérdida, valores_graduación

def gradient_ascent(x, iteraciones, paso, max_loss=Ninguno):


para i en rango (iteraciones):
valor_pérdida, valores_graduación = eval_loss_and_grads(x) Esta función se ejecuta
si max_loss no es Ninguno y loss_value > max_loss: ascenso de gradiente
romper durante varias iteraciones.
print('...Valor de pérdida en', i, ':', valor_pérdida)
x += paso * valores_graduación
volver x

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).

Con licencia para <nulo>


Machine Translated by Google

sueño profundo 283

Detalle
Detalle reinyección
reinyección

Sueño Exclusivo Sueño Exclusivo Sueño

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.

Listado 8.12 Ascenso de gradiente en ejecución en diferentes escalas sucesivas

Jugar con estos hiperparámetros te permitirá Tamaño del paso de ascenso del gradiente

lograr nuevos efectos.


Número de escalas en las que ejecutar el
ascenso de gradiente
importar numpy como np
Relación de tamaño entre escalas
paso = 0,01
núm_octava = 3 Número de pasos de ascenso a
correr en cada escala.
escala_octava = 1,4
iteraciones = 20
Si la pérdida supera los 10, interrumpirá el proceso de ascenso
de gradiente para evitar artefactos desagradables.
pérdida_máxima = 10.

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)

Con licencia para <nulo>


Machine Translated by Google

284 CAPÍTULO 8 Aprendizaje profundo generativo

forma_original = img.shape[1:3] formas_sucesivas


= [forma_original] para i en rango(1, num_octava): forma =
Prepara una lista de tuplas de
tupla([int(dim / (octave_scale ** i)) para tenue en
formas que definen las diferentes
forma_original]) formas_sucesivas.append (forma) escalas en las que ejecutar
ascenso de gradiente

Invierte la lista de formas

formas_sucesivas = formas_sucesivas[::­1] para que estén en orden


creciente.
original_img = np.copy(img)
shrunk_original_img = resize_img(img, formas_sucesivas[0])
Amplia la
para formas en formas_sucesivas: print('Procesando Cambia el tamaño de la
imagen matriz Numpy de la
forma de imagen', forma) img = resize_img(img, forma) img =
del sueño imagen a la escala más pequeña
gradient_ascent(img,

Aumenta la versión más


Corre en ascenso iteraciones=iteraciones, paso=paso,
pequeña de la imagen original:
en gradiente,
estará pixelada.
alterando el sueño. max_loss=max_loss)
upscaled_shrunk_original_img = resize_img(shrunk_original_img, forma) Same_size_original =
resize_img(original_img, forma) lost_detail = mismo_tamaño_original ­
upscaled_shrunk_original_img

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.

Listado 8.13 Funciones auxiliares

importar scipy
desde keras.preprocesamiento importar imagen

def resize_img(img, tamaño): img =


np.copy(img) factores = (1,

float(tamaño[0]) / img.shape[1], float(tamaño[1]) /


img.shape[2], 1)

devolver scipy.ndimage.zoom(img, factores, orden=1)

def save_img(img, nombref):


pil_img = deprocess_image(np.copy(img)) scipy.misc.imsave(fname,
Función de utilidad para abrir, cambiar el
pil_img)
tamaño y formatear imágenes en
def imagen_preproceso(ruta_imagen): tensores que Inception V3 puede procesar

img = imagen.load_img(image_path) img =


imagen.img_to_array(img)

Con licencia para <nulo>


Machine Translated by Google

sueño profundo 285

img = np.expand_dims(img, eje=0) img =


inception_v3.preprocess_input(img) return img

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.

Figura 8.5 Ejecutando el código de DeepDream en una imagen de ejemplo

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

Con licencia para <nulo>


Machine Translated by Google

286 CAPÍTULO 8 Aprendizaje profundo generativo

Generación aleatoria de los parámetros en el diccionario Layer_Contributions para


Explore rápidamente muchas combinaciones de capas diferentes. La figura 8.6 muestra una variedad de resultados.
obtenido mediante diferentes configuraciones de capas, a partir de la imagen de un delicioso pastelito casero.

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.

Con licencia para <nulo>


Machine Translated by Google

Transferencia de estilo neuronal 287

8.3 Transferencia de estilo neuronal


Además de DeepDream, otro desarrollo importante en el ámbito del aprendizaje profundo
La modificación de imágenes es la transferencia de estilo neuronal, introducida por Leon Gatys et al. en el verano
de 2015.5 El algoritmo de transferencia de estilo neuronal ha experimentado muchas mejoras
y generó muchas variaciones desde su introducción original, y se ha abierto camino
en muchas aplicaciones de fotografía para teléfonos inteligentes. Para simplificar, esta sección se centra en la
formulación descrita en el artículo original.
La transferencia de estilo neuronal consiste en aplicar el estilo de una imagen de referencia a un objetivo
imagen conservando el contenido de la imagen de destino. La figura 8.7 muestra un ejemplo.

Objetivo de contenido Referencia de estilo Imagen combinada

Figura 8.7 Un ejemplo de transferencia de estilo

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:

pérdida = distancia (estilo (imagen_referencia) ­ estilo (imagen_generada)) +


distancia(contenido(imagen_original) ­ contenido(imagen_generada))

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.

Con licencia para <nulo>


Machine Translated by Google

288 CAPÍTULO 8 Aprendizaje profundo generativo

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.

8.3.1 La pérdida de contenido

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.

8.3.2 La pérdida de estilo

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 contenido manteniendo activaciones de capas de alto nivel similares entre


Imagen del contenido de destino y la imagen generada. El convnet debería "ver" ambos
la imagen de destino y la imagen generada contienen las mismas cosas.

Con licencia para <nulo>


Machine Translated by Google

Transferencia de estilo neuronal 289

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.

8.3.3 Transferencia de estilo neuronal en Keras

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.

3 Configure un proceso de descenso de gradiente para minimizar esta función de pérdida.

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.

Listado 8.14 Definiendo variables iniciales

desde keras.preprocessing.image importar load_img, img_to_array


Camino a la imagen que
quieres transformar
target_image_path = 'img/portrait.jpg'
style_reference_image_path = 'img/transfer_style_reference.jpg'
Ruta a la

ancho, alto = load_img(target_image_path).size img_height = 400 img_width imagen de estilo.


Dimensiones de la
= int(ancho * img_height /
alto) imagen generada.

Necesita algunas funciones auxiliares para cargar, preprocesar y posprocesar las imágenes que entran y salen del
convnet VGG19 .

Listado 8.15 Funciones auxiliares

importar numpy como np


desde keras.applications importar 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

Con licencia para <nulo>


Machine Translated by Google

290 CAPÍTULO 8 Aprendizaje profundo generativo

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

Marcador de posición que


desde keras importa el backend como K contendrá la imagen generada

target_image = K.constant(preprocess_image(target_image_path)) style_reference_image =


K.constant(preprocess_image(style_reference_image_path)) combine_image = K.placeholder((1, img_height, img_width, 3))

input_tensor = K.concatenar([imagen_objetivo,
Combina las tres
imagen_referencia_estilo,
imágenes en un solo lote.
imagen_combinación], eje=0)

modelo = vgg19.VGG19(input_tensor=input_tensor, pesos='imagenet', Construye la red VGG19 con el lote


include_top=False) de tres imágenes como entrada.
El modelo se cargará con pesos
print('Modelo cargado.') ImageNet previamente entrenados.

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.

Listado 8.17 Pérdida de contenido

def content_loss(base, combinación): return


K.sum(K.square(combinación ­ base))

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.

Listado 8.18 Pérdida de estilo

def gram_matrix(x): características


= K.batch_flatten(K.permute_dimensions(x, (2, 0, 1))) gramo = K.dot(características,
K.transpose(características)) devolver gramo

Con licencia para <nulo>


Machine Translated by Google

Transferencia de estilo neuronal 291

def style_loss(estilo, combinación): S = gram_matrix(estilo)

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.

Listado 8.19 Pérdida total por variación

def pérdida_variación_total(x): a = K.cuadrado(

x[:, :img_height ­ 1, :img_width ­ 1, :] ­ x[:, 1:, :img_width ­ 1, :])

b = K.cuadrado(
x[:, :img_height ­ 1, :img_width ­ 1, :] ­ x[:, :img_height ­ 1, 1:, :])

devolver K.sum(K.pow(a + b, 1,25))

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.

Listado 8.20 Definiendo la pérdida final que minimizarás

Diccionario que asigna nombres de


capas a tensores de activación

salidas_dict = dict([(layer.name, Layer.output) para la capa en model.layers])


content_layer = 'bloque5_conv2'
style_layers = ['block1_conv1', Capa utilizada para la pérdida de contenido.
'block2_conv1',
'block3_conv1', Capas utilizadas para perder estilo.
'block4_conv1',
'block5_conv1']
peso_variación_total = 1e­4
peso_estilo = 1. Pesos en el promedio ponderado de los
componentes de la pérdida
peso_contenido = 0,025

Con licencia para <nulo>


Machine Translated by Google

292 CAPÍTULO 8 Aprendizaje profundo generativo

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)

Finalmente, configurará el proceso de descenso de gradiente. En el original Gatys et al. En papel, la


optimización se realiza utilizando el algoritmo L­BFGS , así que eso es lo que usarás aquí.
Esta es una diferencia clave con respecto al ejemplo de DeepDream en la sección 8.2. El algoritmo L­
BFGS viene incluido con SciPy, pero existen dos pequeñas limitaciones con la implementación de SciPy:
Requiere que

pase el valor de la función de pérdida y el valor de la función de pérdida.


dientes como dos funciones separadas.
Solo se puede aplicar a vectores planos, mientras que tiene una matriz de imágenes 3D.

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.

Listado 8.21 Configurando el proceso de descenso de gradiente

grads = K.gradients(pérdida, combinación_imagen)[0]


Obtiene los

gradientes de fetch_loss_and_grads = K.function([imagen_combinación], [pérdida, graduados])


la
Función para recuperar
imagen
Evaluador de clase (objeto): los valores de la
generada con
pérdida actual y los
respecto a la
pérdida.
def __init__(self): self.loss_value gradientes actuales.
= Ninguno self.grads_values =
Ninguno

def pérdida (self, x): afirmar


self.loss_value es Ninguno x = x.reshape((1,
img_height, img_width, 3)) outs = fetch_loss_and_grads([x])

Esta clase envuelve fetch_loss_and_grads de una


manera que le permite recuperar las pérdidas y los gradientes a
través de dos llamadas a métodos separados, lo cual es requerido por el
optimizador SciPy que usará.

Con licencia para <nulo>


Machine Translated by Google

Transferencia de estilo neuronal 293

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

def graduados (yo, x):


afirmar self.loss_value no es Ninguno
valores_graduados = np.copia(self.valores_graduados)
self.loss_value = Ninguno
self.grad_values = Ninguno
devolver valores_graduados

evaluador = Evaluador()

Finalmente, puede ejecutar el proceso de ascenso de gradiente utilizando el algoritmo L­BFGS 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).

Listado 8.22 Bucle de transferencia de estilo

desde scipy.optimize importar fmin_l_bfgs_b Este es el estado inicial: la


desde scipy.misc importar imsave
imagen de destino.
tiempo de importación

Aplanas la imagen porque


prefijo_resultado = 'mi_resultado' scipy.optimize.fmin_l_bfgs_b solo
iteraciones = 20
puede procesar vectores planos.

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!

Con licencia para <nulo>


Machine Translated by Google

294 CAPÍTULO 8 Aprendizaje profundo generativo

Figura 8.8 Algunos resultados de ejemplo

Con licencia para <nulo>


Machine Translated by Google

Transferencia de estilo neuronal 295

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 entrada­salida 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.

Con licencia para <nulo>


Machine Translated by Google

296 CAPÍTULO 8 Aprendizaje profundo generativo

8.4 Generación de imágenes con codificadores automáticos variacionales


Tomar muestras de un espacio latente de imágenes para crear imágenes completamente nuevas o
editar las existentes es actualmente la aplicación más popular y exitosa de la IA creativa. En esta
sección y en la siguiente, revisaremos algunos conceptos de alto nivel relacionados con la
generación de imágenes, junto con detalles de implementación relacionados con las dos técnicas
principales en este dominio: codificadores automáticos variacionales (VAE) y redes generativas
adversarias (GAN). . Las técnicas que presentamos aquí no son específicas para imágenes (se
podrían desarrollar espacios latentes de sonido, música o incluso texto, usando GAN y VAE), pero
en la práctica, los resultados más interesantes se han obtenido con imágenes, y eso es lo que nos centramos aquí.

8.4.1 Muestreo de espacios latentes de imágenes


La idea clave de la generación de imágenes es desarrollar un espacio latente de representaciones
de baja dimensión (que naturalmente es un espacio vectorial) donde cualquier punto pueda
asignarse a una imagen de apariencia realista. El módulo capaz de realizar este mapeo, tomando
como entrada un punto latente y generando una imagen (una cuadrícula de píxeles), se denomina
generador ( en el caso de GAN) o decodificador (en el caso de VAE). Una vez que se ha desarrollado
dicho espacio latente, se pueden tomar muestras de puntos de él, ya sea de forma deliberada o
aleatoria, y, al mapearlos en el espacio de imágenes, generar imágenes que nunca antes se han
visto (ver figura 8.9).

Datos de entrenamiento

Proceso de
?
aprendizaje

Generador / Decodificador

Vector del espacio imagen

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.

Con licencia para <nulo>


Machine Translated by Google

Generando imágenes con codificadores automáticos variacionales 297

Figura 8.10 Un espacio continuo de caras generado por Tom White usando VAE

8.4.2 Vectores conceptuales para edición de imágenes

Ya insinuamos la idea de un vector conceptual cuando cubrimos las incrustaciones de palabras en


capítulo 6. La idea sigue siendo la misma: dado un espacio latente de representaciones, o un
Al incrustar el espacio, ciertas direcciones en el espacio pueden codificar interesantes ejes de variación en
los datos originales. En un espacio latente de imágenes de rostros, por ejemplo, puede haber
ser un vector de sonrisa s, tal que si el punto latente z es la representación incrustada de una cara
determinada, entonces el punto latente z + s es la representación incrustada de la misma cara,
sonriente. Una vez que haya identificado dicho vector, será posible editar imágenes.
proyectándolos en el espacio latente, moviendo su representación en un sentido significativo.
manera, y luego decodificarlos nuevamente al espacio de la imagen. Hay vectores conceptuales para
esencialmente cualquier dimensión independiente de variación en el espacio de la imagen, en el caso de
caras, puede descubrir vectores para agregar gafas de sol a una cara, quitarlas, convertir una cara masculina
en una cara femenina, etc. La figura 8.11 es un ejemplo de un vector de sonrisa, un vector conceptual
descubierto por Tom White de la Escuela de Ciencias de la Universidad de Victoria.
Diseño en Nueva Zelanda, utilizando VAE entrenados en un conjunto de datos de rostros de celebridades (el
Conjunto de datos de CelebA).

Con licencia para <nulo>


Machine Translated by Google

298 CAPÍTULO 8 Aprendizaje profundo generativo

Figura 8.11 El vector de la sonrisa

8.4.3 Autocodificadores variacionales


Los codificadores automáticos variacionales, descubiertos simultáneamente por Kingma y Welling en
diciembre de 20136 y Rezende, Mohamed y Wierstra en enero de 2014,7 son un tipo de modelo
generativo especialmente apropiado para la tarea de edición de imágenes mediante vectores
conceptuales. Son una versión moderna de los codificadores automáticos (un tipo de red que tiene
como objetivo codificar una entrada en un espacio latente de baja dimensión y luego decodificarla
nuevamente) que mezcla ideas del aprendizaje profundo con la inferencia bayesiana.
Un codificador automático de imágenes clásico toma una imagen, la asigna a un espacio vectorial
latente mediante un módulo codificador y luego la decodifica nuevamente en una salida con las mismas
dimensiones que la imagen original, mediante un módulo decodificador (consulte la figura 8.12). Luego
se entrena utilizando como datos de destino las mismas imágenes que las imágenes de entrada, lo que
significa que el codificador automático aprende a reconstruir las entradas originales. Al imponer varias
restricciones al código (la salida del codificador), puede lograr que el codificador automático aprenda
representaciones latentes de los datos más o menos interesantes. Lo más común es restringir el código
para que sea de baja dimensión y escaso (principalmente ceros), en cuyo caso el codificador actúa
como una forma de comprimir los datos de entrada en menos bits de información.

6
Diederik P. Kingma y Max Welling, “Auto­Encoding 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.

Con licencia para <nulo>


Machine Translated by Google

Generando imágenes con codificadores automáticos variacionales 299

Codificador Descifrador

Original Comprimido Reconstruido


entrada x representación entrada x

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.

Distribución sobre latente


espacio definido por z_mean
Imagen de entrada y z_log_var

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.

Con licencia para <nulo>


Machine Translated by Google

300 CAPÍTULO 8 Aprendizaje profundo generativo

En términos técnicos, así es como funciona un VAE :

1 Un módulo codificador convierte las muestras de entrada input_img en dos parámetros en


un espacio latente de representaciones, z_mean y z_log_variance.
2 Muestras aleatoriamente un punto z de la distribución normal latente que es
se supone que genera la imagen de entrada, a través de z = z_mean + exp(z_log_variance) *
épsilon, donde épsilon es un tensor aleatorio de valores pequeños.

3 Un módulo decodificador asigna este punto en el espacio latente a la entrada original


imagen.

Debido a que épsilon es aleatorio, el proceso garantiza que cada punto que esté cerca de la ubicación
latente donde codificó input_img (z­mean) 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í:

z_mean, z_log_variance = codificador(input_img)


Codifica la entrada en un
z = z_media + exp(z_log_varianza) * épsilon parámetro de media y varianza.

reconstructed_img = decodificador(z) Dibuja un punto latente usando un


decodifica
pequeño épsilon aleatorio
volver a
modelo = Modelo(input_img, reconstructed_img)
una imagen Crea una instancia del modelo de
codificador automático, que asigna una imagen
de entrada a su reconstrucción.

Luego puede entrenar el modelo utilizando la pérdida de reconstrucción y la pérdida de regularización.


La siguiente lista muestra la red de codificadores que usará, asignando imágenes al
parámetros de una distribución de probabilidad sobre el espacio latente. Es una simple conversión.
que asigna la imagen de entrada x a dos vectores, z_mean y z_log_var.

Listado 8.23 Red de codificadores VAE

importar keras
desde keras importan capas
desde keras importa el backend como K
de keras.models importar modelo
importar numpy como np

img_forma = (28, 28, 1)


tamaño_lote = 16 Dimensionalidad del espacio

atenuación_latente = 2 latente: un plano 2D

input_img = keras.Input(forma=img_shape)

Con licencia para <nulo>


Machine Translated by Google

Generando imágenes con codificadores automáticos variacionales 301

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).

Listado 8.24 Función de muestreo del espacio latente

muestreo def (argumentos):


z_mean, z_log_var = args épsilon =
K.random_normal(forma=(K.shape(z_mean)[0], latent_dim),
media=0., desvestándar=1.)
devolver z_mean + K.exp(z_log_var) * épsilon

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

decoder_input = capas.Input(K.int_shape(z)[1:]) Ingresa dónde alimentarás a z

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)

Transforma z en un mapa de características de la misma forma que el mapa de


características justo antes de la última capa Aplanar en el modelo del codificador.

Con licencia para <nulo>


Machine Translated by Google

302 CAPÍTULO 8 Aprendizaje profundo generativo

decodificador = Modelo(entrada_decodificador, x) Crea una instancia del modelo


decodificador, que convierte
z_decodificado = decodificador(z) "decoder_input" en la imagen decodificada.
Lo aplica a z para
recuperar el z decodificado.

La pérdida dual de un VAE no se ajusta a la expectativa tradicional de una función de muestra de la


forma pérdida (entrada, objetivo). Por lo tanto, configurará la pérdida escribiendo una capa personalizada
que utilice internamente el método de capa add_loss integrado para crear una pérdida arbitraria.

Listado 8.26 Capa personalizada utilizada para calcular la pérdida de VAE

clase CustomVariationalLayer (keras.layers.Layer):

def vae_loss(self, x, z_decoded):


x = K.aplanar(x)
z_decoded = K.flatten(z_decoded) xent_loss =
keras.metrics.binary_crossentropy(x, z_decoded) kl_loss = ­5e­4 * K.mean( 1 + z_log_var ­
K.square(z_mean) ­ K.exp(z_log_var), eje
=­1)
devolver K.media(xent_loss + kl_loss)

def llamada(self, entradas): x =


Implementas capas personalizadas
No utilizas esta entradas[0] z_decoded
escribiendo un método de llamada.
salida, pero la = entradas[1] pérdida =
capa debe self.vae_loss(x, z_decoded) self.add_loss(loss,
devolver algo. Llama a la capa personalizada
inputs=inputs) return x
en la entrada y la
salida decodificada para obtener
y = CustomVariationalLayer()([input_img, z_decoded]) el resultado final del modelo.

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).

Listado 8.27 Entrenamiento del VAE

desde keras.datasets importar mnist

vae = Modelo(input_img, y)
vae.compile(optimizer='rmsprop', pérdida=Ninguna) vae.summary()

(x_train, _), (x_test, y_test) = mnist.load_data()

x_train = x_train.astype('float32') / 255. x_train =


x_train.reshape(x_train.shape + (1,)) x_test = x_test.astype('float32') / 255. x_test
= x_test.reshape(x_test.shape + (1,))

vae.fit(x=x_train, y=Ninguno,
barajar = Verdadero,
épocas = 10,
tamaño_lote = tamaño_lote,
datos_validación = (x_test, Ninguno))

Con licencia para <nulo>


Machine Translated by Google

Generando imágenes con codificadores automáticos variacionales 303

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

importar matplotlib.pyplot como plt


de la norma de importación scipy.stats
Mostrará una cuadrícula de 15 × 15
norte = 15 dígitos (255 dígitos en total).

tamaño_dígito = 28 Transforma coordenadas


figura = np.zeros((tamaño_dígito * grid_x = n, tamaño_dígito * n)) linealmente espaciadas usando la
norma.ppf(np.linspace(0.05, 0.95, n)) función ppf de SciPy para producir
valores de la variable latente z (porque el
grid_y = norma.ppf(np.linspace(0.05, 0.95, n))
prior del espacio latente es gaussiano)
para i, yi en enumerar (grid_x):
Repite z varias veces para
para j, xi en enumerar (grid_y):
formar un lote completo
muestra_z = np.array([[xi, yi]])
z_sample = np.tile(z_sample, tamaño_lote).reshape(tamaño_lote, 2)
x_decodificado = decodificador.predict(z_muestra, tamaño_lote=tamaño_lote)
dígito = x_decodificado[0].reshape(tamaño_dígito, tamaño_dígito)
figura[i * tamaño_dígito: (i + 1) * tamaño_dígito,
j * tamaño_dígito: (j + 1) * tamaño_dígito] = dígito

plt.figura(tamaño de figura=(10, 10)) Reforma el primer dígito en


plt.imshow(figura, cmap='Greys_r') el lote de 28 × 28 × 1
plt.mostrar() a 28 × 28

Decodifica el lote
Imágenes de digitos

La cuadrícula de dígitos muestreados (ver


figura 8.14) muestra una distribución
completamente continua de los diferentes
clases de dígitos, con transformación de un dígito
en otro mientras sigues un camino
a través del espacio latente. Las instrucciones
específicas en este espacio tienen un significado:
por ejemplo, hay una dirección para
“cuatro”, “unicidad”, etc.
En la siguiente sección, cubriremos
detalle la otra herramienta importante para
generar imágenes artificiales: la generación
Redes adversarias (GAN).

Figura 8.14 Cuadrícula de dígitos decodificados del espacio


latente

Con licencia para <nulo>


Machine Translated by Google

304 CAPÍTULO 8 Aprendizaje profundo generativo

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 2016­2017.
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.

Con licencia para <nulo>


Machine Translated by Google

Introducción a las redes generativas adversarias 305

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.

Con licencia para <nulo>


Machine Translated by Google

306 CAPÍTULO 8 Aprendizaje profundo generativo

vector aleatorio
Generado
desde el
(descifrado)
espacio latente
imagen

Generador (decodificador)
Capacitación
comentario

Discriminado "Una verdadera falsedad"

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.

Sorprendentemente, una GAN es un sistema donde el mínimo de optimización no es fijo, a diferencia de en


cualquier otra configuración de entrenamiento que hayas encontrado en este libro. Normalmente, descenso de gradiente.
Consiste en rodar cuesta abajo en un paisaje de pérdida estática. Pero con una GAN, cada paso
bajando la colina cambia un poco todo el paisaje. Es un sistema dinámico donde
el proceso de optimización no busca un mínimo, sino un equilibrio entre dos
efectivo. Por esta razón, las GAN son notoriamente difíciles de entrenar: hacer que una GAN funcione
Requiere muchos ajustes cuidadosos de la arquitectura del modelo y los parámetros de entrenamiento.

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).

Con licencia para <nulo>


Machine Translated by Google

Introducción a las redes generativas adversarias 307

8.5.1 Una implementación esquemática de GAN


En esta sección, explicaremos cómo implementar una GAN en Keras, en su forma más básica; debido
a que las GAN son avanzadas, profundizar en los detalles técnicos estaría fuera del alcance de este
libro. La implementación específica es una GAN convolucional profunda (DCGAN): una GAN donde el
generador y el discriminador son convnets profundas. En particular, utiliza una capa Conv2DTranspose
para aumentar el muestreo de imágenes en el generador.
Entrenará la GAN con imágenes de CIFAR10, un conjunto de datos de 50 000 imágenes RGB de
32 × 32 pertenecientes a 10 clases (5000 imágenes por clase). Para facilitar las cosas, solo usarás
imágenes que pertenezcan a la clase "rana".
Esquemáticamente, la GAN se ve así: 1. Una

red generadora asigna vectores de forma (latent_dim) a imágenes de forma.


(32, 32, 3).
2 Una red discriminadora asigna imágenes de forma (32, 32, 3) a una puntuación binaria
estimar la probabilidad de que la imagen sea real.
3 Una red gan encadena al generador y al discriminador: gan(x) = discriminador(generador(x)). Por
lo tanto, esta red gan asigna vectores espaciales latentes a la evaluación del discriminador del
realismo de estos vectores latentes como
decodificado por el generador.
4 Se entrena al discriminador utilizando ejemplos de imágenes reales y falsas junto con etiquetas
"reales"/"falsas", tal como se entrena cualquier modelo de clasificación de imágenes normal.
5 Para entrenar el generador, utiliza los gradientes de los pesos del generador con respecto a la
pérdida del modelo gan . Esto significa que, en cada paso, mueve los pesos del generador en
una dirección que hace que sea más probable que el discriminador clasifique como "reales" las
imágenes decodificadas por el generador. En otras palabras, entrenas al generador para
engañar al discriminador.

8.5.2 Una bolsa de trucos


El proceso de entrenamiento de GAN y ajuste de implementaciones de GAN es notoriamente difícil.
Hay una serie de trucos conocidos que debes tener en cuenta. Como la mayoría de las cosas en el
aprendizaje profundo, es más alquimia que ciencia: estos trucos son heurísticos, no pautas respaldadas
por teoría. Están respaldados por un nivel de comprensión intuitiva del fenómeno en cuestión y se sabe
que funcionan bien empíricamente, aunque no necesariamente en todos los contextos.

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 :

Usamos tanh como última activación en el generador, en lugar de sigmoide, que es


Se encuentra más comúnmente en otros tipos de modelos.
Tomamos muestras de puntos del espacio latente usando una distribución normal (distribución
gaussiana), no una distribución uniforme.

Con licencia para <nulo>


Machine Translated by Google

308 CAPÍTULO 8 Aprendizaje profundo generativo

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.

Listado 8.29 Red generadora GAN

importar keras
desde keras importan capas
importar numpy como np

tenue_latente = 32
altura = 32
ancho = 32
canales = 3

Con licencia para <nulo>


Machine Translated by Google

Introducción a las redes generativas adversarias 309

generador_entrada = keras.Input(forma=(latent_dim,))

x = capas.Dense(128 * 16 * 16)(generator_input) x = capas.LeakyReLU()(x) x Transforma la entrada en un


= capas.Reshape((16, 16, 128))(x) mapa de características de 16
× 16 y 128 canales.

x = capas.Conv2D(256, 5, padding='mismo')(x) x = capas.LeakyReLU()


(x)

x = capas.Conv2DTranspose(256, 4, zancadas=2, padding='mismo')(x) x = capas.LeakyReLU()(x) Muestras


mejoradas a 32 × 32

x = capas.Conv2D(256, 5, padding='mismo')(x) x = capas.LeakyReLU()


(x) x = capas.Conv2D(256, 5,
padding='mismo')(x) x = capas.LeakyReLU()(x)

x = capas.Conv2D(canales, 7, activación='tanh', padding='mismo')(x) generador =


keras.models.Model(generator_input, x) generador.summary()

Produce un mapa de características de 1 canal


Crea una instancia del modelo generador, que asigna la entrada de de 32 × 32 (forma de una imagen CIFAR10)
forma (latent_dim) a una imagen de forma (32, 32, 3).

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".

Listado 8.30 La red discriminadora GAN

discriminator_input = capas.Input(forma=(alto, ancho, canales)) x = capas.Conv2D(128, 3)(discriminator_input)


x = capas.LeakyReLU()(x) x = capas.Conv2D(128, 4, zancadas =2)(x) x =
capas.LeakyReLU()(x) x =
capas.Conv2D(128, 4, zancadas=2)(x) x = capas.LeakyReLU()
(x) x = capas.Conv2D(128 , 4,
zancadas=2)(x) x = capas.LeakyReLU()(x) x = capas.Flatten()
(x) Una capa de abandono:
¡un truco importante!

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=1e­8)
Para estabilizar el entrenamiento,
discriminator.compile(optimizador=discriminator_optimizer, pérdida='binary_crossentropy')
utiliza la caída de la tasa de aprendizaje.

Con licencia para <nulo>


Machine Translated by Google

310 CAPÍTULO 8 Aprendizaje profundo generativo

8.5.5 La red adversaria


Finalmente, configurarás la GAN, que encadena el generador y el discriminador.
Cuando se entrene, este modelo moverá el generador en una dirección que mejore su capacidad
para engañar al discriminador. Este modelo convierte los puntos del espacio latente en una decisión
de clasificación ("falso" o "real") y debe entrenarse con etiquetas que siempre digan "estas son
imágenes reales". Por lo tanto, el entrenamiento gan actualizará los pesos del generador de una
manera que haga que sea más probable que el discriminador prediga "real" cuando mira imágenes falsas.
Es muy importante tener en cuenta que configuras el discriminador para que se congele durante el
entrenamiento (no entrenable): sus pesos no se actualizarán cuando entrenes gan. Si los pesos del
discriminador pudieran actualizarse durante este proceso, entonces estaría entrenando al
discriminador para que siempre prediga "real", ¡lo cual no es lo que desea!

Listado 8.31 Red adversaria

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)

gan_optimizer = keras.optimizers.RMSprop(lr=0.0004, clipvalue=1.0, decay=1e­8) gan.compile(optimizer=gan_optimizer,


loss='binary_crossentropy')

8.5.6 Cómo entrenar tu DCGAN


Ahora puedes empezar a entrenar. En resumen, así es como se ve esquemáticamente el ciclo de
entrenamiento. Para cada época, haga lo siguiente:

1 Dibuja puntos aleatorios en el espacio latente (ruido aleatorio).


2 Genere imágenes con el generador utilizando este ruido aleatorio.
3 Mezcla las imágenes generadas con las reales.
4 Entrene el discriminador utilizando estas imágenes mixtas, con los objetivos correspondientes:
ya sea “real” (para las imágenes reales) o “falso” (para las imágenes generadas).
5 Dibuja nuevos puntos aleatorios en el espacio latente.
6 Entrena usando estos vectores aleatorios, con objetivos que dicen "estas son imágenes
reales". Esto actualiza los pesos del generador (solo porque el discriminador está congelado
dentro de gan) para moverlos hacia hacer que el discriminador prediga "estas son imágenes
reales" para las imágenes generadas: esto entrena al generador para engañar al discriminador.

Implementémoslo.

Listado 8.32 Implementación del entrenamiento GAN

importar
sistema operativo desde keras.preprocesamiento importar imagen Carga datos CIFAR10

(x_train, y_train), (_, _) = keras.datasets.cifar10.load_data()

Con licencia para <nulo>


Machine Translated by Google

Introducción a las redes generativas adversarias 311

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.

Los imágenes_generadas = generador.predict(vectores_latentes_aleatorios)


decodifica Los combina con
en detener = iniciar + tamaño_por lotes imágenes reales
imágenes falsas. imágenes_real = x_train[inicio: detener]
imágenes_combinadas = np.concatenate([imagenes_generadas, imágenes_real])

Reúne etiquetas, discriminando


etiquetas = np.concatenate([np.ones((batch_size, 1)), np.zeros((batch_size, 1))])
imágenes reales de falsas.

etiquetas += 0,05 * np.random.random(etiquetas.forma)


Agrega ruido
aleatorio a las
Entrena al d_loss = discriminator.train_on_batch(combined_images, etiquetas)
etiquetas: ¡un
discriminador
vectores_latentes_aleatorios = np.random.normal(tamaño=(tamaño_lote, latent_dim)) truco importante!

Muestras de puntos
aleatorios en el
Reúne objetivos_engañosos = np.zeros((tamaño_batch, 1)) espacio latente.
etiquetas que

dicen “todas a_loss = gan.train_on_batch(vectores_latentes_aleatorios, objetivos_engañosos) Entrena el generador (a través del


estas son modelo gan, donde los pesos
imágenes discriminadores están congelados)
reales” (¡es mentira!) inicio += tamaño_lote si inicio
> len(x_train) ­ tamaño_lote:
inicio = 0 Ocasionalmente guarda y traza
(cada 100 pasos)

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)

img = image.array_to_img(generated_images[0] * 255., escala=False) img.save(os.path.join(save_dir,

'generated_frog' + str(paso) + '.png'))

img = image.array_to_img(real_images[0] * 255., escala=Falso) img.save(os.path.join(save_dir,


'real_frog' + str(step) + '.png'))

Guarda una imagen real para


comparar.

Con licencia para <nulo>


Machine Translated by Google

312 CAPÍTULO 8 Aprendizaje profundo generativo

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.

Con licencia para <nulo>


Machine Translated by Google

Introducción a las redes generativas adversarias 313

Resumen del capítulo


Con aplicaciones creativas de aprendizaje profundo, las redes profundas van más allá de
anotar contenido existente y comienzan a generar el suyo propio. Aprendiste lo
siguiente: –
Cómo generar datos de secuencia, un paso de tiempo a la vez. Esto es aplicable a la
generación de texto y también a la generación de música nota por nota o cualquier
otro tipo de datos de series temporales.
– Cómo funciona DeepDream: maximizando las activaciones de la capa convnet a través del ascenso
de gradiente en el espacio de entrada.
– Cómo realizar una transferencia de estilo, donde se combinan una imagen de contenido y una
imagen de estilo para producir resultados de apariencia interesante.
– Qué son las GAN y los VAE , cómo se pueden utilizar para crear nuevas imágenes y cómo se
pueden utilizar los vectores conceptuales de espacio latente para la edición de imágenes.

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.

Con licencia para <nulo>


Machine Translated by Google

Conclusiones

Este capítulo cubre


Conclusiones importantes de este libro
Las limitaciones del aprendizaje profundo
El futuro del aprendizaje profundo, el aprendizaje automático,
y IA

Recursos para aprender más y trabajar en


el campo

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

Con licencia para <nulo>


Machine Translated by Google

Conceptos clave en revisión 315

9.1 Repaso de conceptos clave Esta


sección sintetiza brevemente las conclusiones clave de este libro. Si alguna vez necesita
un repaso rápido que le ayude a recordar lo que ha aprendido, puede leer estas pocas páginas.

9.1.1 Diversos enfoques de la IA

En primer lugar, el aprendizaje profundo no es sinónimo de IA ni siquiera de aprendizaje automático.


La inteligencia artificial es un campo antiguo y amplio que generalmente puede definirse como “todos los
intentos de automatizar los procesos cognitivos”; en otras palabras, la automatización del pensamiento. Esto
puede variar desde lo más básico, como una hoja de cálculo de Excel, hasta lo más avanzado, como un
robot humanoide que puede caminar y hablar.
El aprendizaje automático es un subcampo específico de la IA que tiene como objetivo desarrollar
automáticamente programas (llamados modelos) únicamente a partir de la exposición a datos de
entrenamiento. Este proceso de convertir datos en un programa se llama aprendizaje. Aunque el aprendizaje
automático existe desde hace mucho tiempo, no empezó a despegar hasta la década de 1990.
El aprendizaje profundo es una de las muchas ramas del aprendizaje automático, donde los modelos
son largas cadenas de funciones geométricas, aplicadas una tras otra. Estas operaciones están estructuradas
en módulos llamados capas: los modelos de aprendizaje profundo suelen ser pilas de capas o, más
generalmente, gráficos de capas. Estas capas están parametrizadas por pesos, que son los parámetros
aprendidos durante el entrenamiento. El conocimiento de un modelo se almacena en sus pesos y el proceso
de aprendizaje consiste en encontrar buenos valores para estos pesos.

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é.

9.1.2 Qué hace que el aprendizaje profundo sea


especial dentro del campo del aprendizaje automático

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,

Con licencia para <nulo>


Machine Translated by Google

316 CAPÍTULO 9 Conclusiones

clasificación de imágenes, traducción automática muy mejorada y más. El revuelo puede


(y probablemente retrocederá), pero el impacto económico y tecnológico sostenido de
el aprendizaje permanecerá. En ese sentido, el aprendizaje profundo podría ser análogo a Internet:
Puede que se le haya dado demasiada publicidad durante algunos años, pero a largo plazo seguirá siendo un tema importante.
revolución que transformará nuestra economía y nuestras vidas.
Soy particularmente optimista sobre el aprendizaje profundo porque incluso si no hiciéramos
mayor progreso tecnológico en la próxima década, implementando algoritmos existentes para
cada problema aplicable cambiaría las reglas del juego para la mayoría de las industrias. El aprendizaje profundo
es nada menos que una revolución, y actualmente el progreso se está produciendo a un ritmo increíblemente
rápido, debido a una inversión exponencial en recursos y personal. De
En mi situación, el futuro parece brillante, aunque las expectativas a corto plazo son algo
demasiado optimista; implementar el aprendizaje profundo en todo su potencial requerirá
más de una década.

9.1.3 Cómo pensar en el aprendizaje profundo


Lo más sorprendente del aprendizaje profundo es lo sencillo que es. Hace diez años no
Uno esperaba que lográramos resultados tan sorprendentes en la percepción de las máquinas.
problemas mediante el uso de modelos paramétricos simples entrenados con descenso de gradiente. Ahora
Resulta que todo lo que necesitas son modelos paramétricos suficientemente grandes entrenados con descenso
de gradiente en un número suficiente de ejemplos. Como dijo una vez Feynman sobre el universo,
“No es complicado, es mucho.”1
En el aprendizaje profundo, todo es un vector: todo es un punto en un espacio geométrico.
Las entradas del modelo (texto, imágenes, etc.) y los objetivos se vectorizan primero: se convierten en un
espacio vectorial de entrada inicial y espacio vectorial objetivo. Cada capa en un modelo de aprendizaje profundo
opera una transformación geométrica simple en los datos que la atraviesan.
Juntas, la cadena de capas del modelo forma una transformación geométrica compleja, descompuesta en una
serie de transformaciones simples. Esta compleja transformación intenta
para asignar el espacio de entrada al espacio de destino, un punto a la vez. Esta transformación es
parametrizado por los pesos de las capas, que se actualizan iterativamente en función de cómo
Bueno, el modelo está funcionando actualmente. Una característica clave de esta transformación geométrica es
que debe ser diferenciable, lo cual es necesario para que podamos
aprenda sus parámetros a través del descenso de gradiente. Intuitivamente, esto significa que la transformación
geométrica de entradas a salidas debe ser suave y continua, una restricción significativa.
Todo el proceso de aplicar esta compleja transformación geométrica a la entrada.
Los datos se pueden visualizar en 3D imaginando a una persona intentando desenredar una bola de papel:
la bola de papel arrugada es la variedad de datos de entrada con los que comienza el modelo.
Cada movimiento realizado por la persona sobre la bola de papel es similar a una simple transformación
geométrica realizada por una capa. La secuencia completa del gesto de desarrugado.
es la compleja transformación de todo el modelo. Los modelos de aprendizaje profundo son máquinas
matemáticas para descomponer conjuntos complicados de datos de alta dimensión.

1
Richard Feynman, entrevista, El mundo desde otro punto de vista, Yorkshire Television, 1972.

Con licencia para <nulo>


Machine Translated by Google

Conceptos clave en revisión 317

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.

9.1.4 Tecnologías habilitadoras clave


La revolución tecnológica que se está desarrollando actualmente no comenzó con ningún invento revolucionario.
Más bien, como cualquier otra revolución, es producto de una vasta acumulación de factores propicios: lentamente
al principio y luego repentinamente. En el caso del aprendizaje profundo, podemos señalar los siguientes factores
clave:

Innovaciones algorítmicas incrementales, que primero se extendieron a lo largo de dos décadas


(comenzando con la propagación hacia atrás) y luego ocurrieron cada vez más rápido a medida que se
invirtieron más esfuerzos de investigación en el aprendizaje profundo después de 2012.
La disponibilidad de grandes cantidades de datos de percepción, que es un requisito para darnos cuenta
de que todo lo que necesitamos son modelos suficientemente grandes entrenados con datos
suficientemente grandes. Esto, a su vez, es un subproducto del auge de la Internet de consumo y de la
aplicación de la ley de Moore a los medios de almacenamiento.
La disponibilidad de hardware de computación rápido y altamente paralelo a bajo precio, especialmente
las GPU producidas por NVIDIA: primero GPU para juegos y luego chips diseñados desde cero para el
aprendizaje profundo. Desde el principio, el director ejecutivo de NVIDIA, Jensen Huang, tomó nota del
auge del aprendizaje profundo y decidió apostar el futuro de la empresa en él.

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.

Con licencia para <nulo>


Machine Translated by Google

318 CAPÍTULO 9 Conclusiones

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.

9.1.5 El flujo de trabajo universal de aprendizaje automático

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!

6 Refine gradualmente la arquitectura de su modelo ajustando los hiperparámetros y agregando regularización.


Realice cambios basados en el rendimiento únicamente en los datos de validación, no en los datos de
prueba ni en los datos de entrenamiento. Recuerde que debe hacer que su modelo se sobreajuste
(identificando así un nivel de capacidad del modelo que sea mayor de lo que necesita) y solo entonces
comenzar a agregar regularización o reducir el tamaño de su modelo.

Con licencia para <nulo>


Machine Translated by Google

Conceptos clave en revisión 319

7 Tenga en cuenta el sobreajuste del conjunto de validación al activar los hiperparámetros: el


hecho de que sus hiperparámetros pueden terminar sobreespecializados en el conjunto de
validación. ¡Evitar esto es el propósito de tener un equipo de prueba separado!

9.1.6 Arquitecturas de red clave


Las tres familias de arquitecturas de red con las que debería estar familiarizado son redes densamente
conectadas, redes convolucionales y redes recurrentes. Cada tipo de red está destinado a una
modalidad de entrada específica: una arquitectura de red (densa, convolucional, recurrente) codifica
suposiciones sobre la estructura de los datos: un espacio de hipótesis dentro del cual se desarrollará
la búsqueda de un buen modelo. Que una arquitectura determinada funcione en un problema
determinado depende enteramente de la coincidencia entre la estructura de los datos y los supuestos
de la arquitectura de la red.
Estos diferentes tipos de redes se pueden combinar fácilmente para lograr redes multimodales
más grandes, de la misma manera que se combinan ladrillos LEGO . En cierto modo, las capas de
aprendizaje profundo son ladrillos LEGO para el procesamiento de información. A continuación se
ofrece una descripción general rápida del mapeo entre las modalidades de

entrada y las arquitecturas de red apropiadas: Datos vectoriales :


red densamente conectada
(capas densas ). Datos de imagen: conversiones 2D. Datos de sonido (por ejemplo,
forma de onda): ya sea convnets 1D (preferido) o RNN. Datos
de texto : ya sean convnets 1D (preferido) o RNN. Datos de series
temporales : RNN (preferido) o convnets 1D . Otros tipos de datos de secuencia: ya sean RNN
o convnets 1D . Prefiera RNN si el orden de los datos es muy significativo (por ejemplo, para
series temporales, pero no para texto). Datos de vídeo : ya sea convnets 3D (si necesita
capturar efectos de movimiento) o una combinación de un convnet 2D a nivel de fotograma
para la extracción de características seguido de un RNN o un convnet 1D para procesar las secuencias resultan
Datos volumétricos: convnets 3D.

Ahora, repasemos rápidamente las especificidades de cada arquitectura de red.

REDES DENSAS CONECTADAS


Una red densamente conectada es una pila de capas densas , destinadas a procesar datos vectoriales
(lotes de vectores). Estas redes no asumen ninguna estructura específica en las características de
entrada: se denominan densamente conectadas porque las unidades de una capa Densa están
conectadas entre sí. La capa intenta mapear las relaciones entre dos entidades de entrada cualesquiera;
esto es diferente a una capa de convolución 2D , por ejemplo, que solo analiza las relaciones locales .
Las redes densamente conectadas se usan más comúnmente para datos categóricos (por ejemplo,
donde las características de entrada son listas de atributos), como el conjunto de datos Boston Housing
Price usado en el capítulo 3. También se usan como etapa final de clasificación o regresión. de la
mayoría de las redes. Por ejemplo, las redes de conversión cubiertas en el capítulo 5 normalmente
terminan con una o dos capas densas , al igual que las redes recurrentes en el capítulo 6.

Con licencia para <nulo>


Machine Translated by Google

320 CAPÍTULO 9 Conclusiones

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:

desde keras importar modelos


desde keras importar capas

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 k­hot:

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')

Con licencia para <nulo>


Machine Translated by Google

Conceptos clave en revisión 321

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:

Con licencia para <nulo>


Machine Translated by Google

322 CAPÍTULO 9 Conclusiones

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')

9.1.7 El espacio de posibilidades


¿Qué construirás con el aprendizaje profundo? Recuerde, construir modelos de aprendizaje profundo es
Es como jugar con ladrillos LEGO : las capas se pueden conectar entre sí para asignar prácticamente cualquier
cosa a cualquier cosa, siempre que tenga disponibles los datos de entrenamiento adecuados y que el
El mapeo se puede lograr mediante una transformación geométrica continua de complejidad razonable. El
espacio de posibilidades es infinito. Esta sección ofrece algunos ejemplos para
Lo inspirará a pensar más allá de las tareas básicas de clasificación y regresión que tradicionalmente han sido
el pan y la mantequilla del aprendizaje automático.
He ordenado mis aplicaciones sugeridas por modalidades de entrada y salida. Tenga en cuenta que
bastantes de ellos amplían los límites de lo que es posible, aunque se podría elaborar un modelo
capacitado en todas estas tareas, en algunos casos un modelo de este tipo probablemente no se generalizaría
lejos de sus datos de entrenamiento. Las secciones 9.2 y 9.3 abordarán cómo estas limitaciones podrían
ser levantado en el futuro.

Con licencia para <nulo>


Machine Translated by Google

Conceptos clave en revisión 323

Mapeo de datos vectoriales a datos vectoriales


– Atención médica predictiva: mapeo de registros médicos de pacientes con predicciones de resultados de
pacientes

– 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

Mapeo de datos de imagen a datos vectoriales

– 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

– IA de juegos de mesa : asignación de Go y tableros de ajedrez al siguiente movimiento del jugador.

– Ayudante de dieta : asignar imágenes de un plato a su recuento de calorías

– Predicción de edad: asignación de selfies a la edad de la persona Asignación

de datos de series temporales a datos vectoriales

– 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

– Interfaces cerebro­computadora: mapeo de series temporales de magnetoencefalograma


(MEG) datos a comandos de computadora

– 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

– Responder preguntas: asignar preguntas de conocimiento general a sus respuestas

– Resumen: asignación de un artículo extenso a un breve resumen del artículo

Mapeo de imágenes a texto


– Subtítulos: asignar imágenes a subtítulos breves que describen el contenido de
las imágenes

Mapear texto a imágenes

– Generación de imágenes condicionadas: asignación de una breve descripción de texto a imágenes que
coinciden con la descripción.

– Generación/selección de logotipo: asignar el nombre y la descripción de una empresa al logotipo de la empresa


Asignar imágenes a imágenes

– Súper resolución: asignación de imágenes reducidas a versiones de mayor resolución de las mismas imágenes

– Detección visual de profundidad : mapeo de imágenes de entornos interiores a mapas de predicciones de


profundidad

Con licencia para <nulo>


Machine Translated by Google

324 CAPÍTULO 9 Conclusiones

Mapeo de imágenes y texto a texto.


– Control de calidad visual : mapeo de imágenes y preguntas en lenguaje natural sobre el contexto.
Tiendas de imágenes para respuestas en lenguaje natural.
Mapeo de video y texto a texto
– Control de calidad de vídeos : mapeo de vídeos cortos y preguntas en lenguaje natural sobre el
contenidos de videos a respuestas en lenguaje natural

Casi todo es posible, pero no absolutamente nada. Veamos en la siguiente sección qué
No podemos hacerlo con el aprendizaje profundo.

Con licencia para <nulo>


Machine Translated by Google

Las limitaciones del aprendizaje profundo 325

9.2 Las limitaciones del aprendizaje profundo


El espacio de aplicaciones que se pueden implementar con aprendizaje profundo es casi infinito. Y, sin
embargo, muchas aplicaciones están completamente fuera del alcance de las técnicas actuales de aprendizaje
profundo, incluso con grandes cantidades de datos anotados por humanos. Digamos, por ejemplo, que se
podría reunir un conjunto de datos de cientos de miles (incluso millones) de descripciones en inglés de las
características de un producto de software, escritas por un gerente de producto, así como el código fuente
correspondiente desarrollado por un equipo de ingenieros para cumplir con estos requisitos. Incluso con estos
datos, no se podría entrenar un modelo de aprendizaje profundo para leer la descripción de un producto y
generar el código base adecuado. Ese es sólo un ejemplo entre muchos. En general, cualquier cosa que
requiera razonamiento (como programación o aplicación del método científico), planificación a largo plazo y
manipulación algorítmica de datos está fuera del alcance de los modelos de aprendizaje profundo, sin importar
cuántos datos se les arroje. Incluso aprender un algoritmo de clasificación con una red neuronal profunda es
tremendamente difícil.

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.

9.2.1 El riesgo de antropomorfizar los modelos de aprendizaje automático


Un riesgo real de la IA contemporánea es malinterpretar lo que hacen los modelos de aprendizaje profundo y
sobreestimar sus capacidades. Una característica fundamental de los humanos es nuestra teoría de la mente:
nuestra tendencia a proyectar intenciones, creencias y conocimientos sobre las cosas que nos rodean. Dibujar
una carita sonriente en una roca de repente la hace “feliz”... en nuestra mente. Aplicado al aprendizaje profundo,
esto significa que, por ejemplo, cuando somos capaces de entrenar con cierto éxito un modelo para generar
leyendas para describir imágenes, se nos hace creer que el modelo "comprende" el contenido de las imágenes
y los subtítulos que genera.
Luego nos sorprendemos cuando cualquier ligera desviación del tipo de imágenes presentes en los datos de
entrenamiento hace que el modelo genere leyendas completamente absurdas (ver figura 9.1).

Con licencia para <nulo>


Machine Translated by Google

326 CAPÍTULO 9 Conclusiones

Figura 9.1 Fallo de un sistema de subtítulos de


imágenes basado en aprendizaje profundo
El niño sostiene un bate de béisbol.

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

Panda Ejemplo adversario

Figura 9.2 Un ejemplo contradictorio: cambios imperceptibles en una imagen pueden alterar
la clasificación de la imagen por parte de un modelo.

Con licencia para <nulo>


Machine Translated by Google

Las limitaciones del aprendizaje profundo 327

En resumen, los modelos de aprendizaje profundo no comprenden sus entradas, al menos,


no en un sentido humano. Nuestra propia comprensión de las imágenes, los sonidos y el lenguaje es
basado en nuestra experiencia sensoriomotora como seres humanos. Los modelos de aprendizaje automático tienen
no tienen acceso a tales experiencias y, por lo tanto, no pueden comprender sus aportes de una manera que los humanos
puedan identificar. Al anotar una gran cantidad de ejemplos de entrenamiento para alimentar nuestros modelos, logramos
que aprendan una transformación geométrica que asigna datos a conceptos humanos en
un conjunto específico de ejemplos, pero este mapeo es un bosquejo simplista del modelo original
en nuestras mentes, el que se desarrolló a partir de nuestra experiencia como agentes encarnados. Es como un
imagen tenue en un espejo (ver figura 9.3).

Datos etiquetados
encarnado Conceptos abstractos ejemplificando Aprendizaje automático
Mundo real experiencia humana en la mente humana. estos conceptos modelo

f(x)

Puede que no No coincide con el Coincide con el


siempre se transfiera modelo mental humano datos de entrenamiento
bien al mundo real vino de

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.

9.2.2 Generalización local versus generalización extrema


Existen diferencias fundamentales entre la transformación geométrica sencilla
desde la entrada hasta la salida que hacen los modelos de aprendizaje profundo, y la forma en que los humanos piensan y
aprender. No se trata sólo del hecho de que los humanos aprenden por sí mismos a partir de experiencias encarnadas en
lugar de que se les presenten ejemplos de entrenamiento explícitos. Además de los diferentes procesos de aprendizaje,
existe una diferencia básica en la naturaleza de los procesos subyacentes.
representaciones.
Los seres humanos son capaces de hacer mucho más que mapear estímulos inmediatos en
respuestas, como lo haría una red profunda, o tal vez un insecto. Mantenemos complejos, abstractos.
modelos de nuestra situación actual, de nosotros mismos y de otras personas, y podemos utilizarlos
modelos para anticipar diferentes futuros posibles y realizar una planificación a largo plazo. Nosotros
Podemos fusionar conceptos conocidos para representar algo que nunca hemos experimentado.

Con licencia para <nulo>


Machine Translated by Google

328 CAPÍTULO 9 Conclusiones

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

Generalización local: poder Generalización extrema: poder


de generalización del de generalización logrado
mediante la abstracción y el
Figura 9.4 Generalización local
reconocimiento de patrones.
razonamiento.
versus generalización extrema

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

Con licencia para <nulo>


Machine Translated by Google

Las limitaciones del aprendizaje profundo 329

Generalización extrema, adaptación rápida a situaciones y planificación radicalmente nuevas.


para situaciones futuras a largo plazo.

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.

Con licencia para <nulo>


Machine Translated by Google

330 CAPÍTULO 9 Conclusiones

9.3 El futuro del aprendizaje profundo


Esta es una sección más especulativa destinada a abrir horizontes a las personas que quieren
Únase a un programa de investigación o comience a realizar investigaciones independientes. Dado lo que sabemos de
cómo funcionan las redes profundas, sus limitaciones y el estado actual del panorama de la investigación,
¿Podemos predecir hacia dónde se dirigen las cosas a medio plazo? A continuación se presentan algunos
pensamientos puramente personales. Tenga en cuenta que no tengo una bola de cristal, por lo que es posible que
mucho de lo que anticipo no se convierta en realidad. Estoy compartiendo estas predicciones no porque espere
que se demuestre que tienen toda la razón en el futuro, sino porque son interesantes y
procesable en el presente.
A alto nivel, estas son las principales direcciones que veo prometedoras:

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.

9.3.1 Modelos como programas


Como se señaló en la sección anterior, un desarrollo transformacional necesario que
Lo que podemos esperar en el campo del aprendizaje automático es un alejamiento de los modelos que realizan
reconocimiento puramente de patrones y sólo puede lograr una generalización local, hacia modelos capaces de
abstracción y razonamiento que pueden lograr una generalización extrema. Los programas de IA actuales que son
capaces de realizar formas básicas de razonamiento están todos codificados por humanos.
programadores: por ejemplo, software que se basa en algoritmos de búsqueda, manipulación de gráficos y lógica
formal. En AlphaGo de DeepMind, por ejemplo, la mayor parte de la inteligencia
en exhibición está diseñado y codificado por programadores expertos (como Monte Carlo
Búsqueda de árbol); el aprendizaje a partir de los datos ocurre sólo en submódulos especializados (redes de valor y
redes de políticas). Pero en el futuro, es posible que estos sistemas de IA se aprendan por completo,
sin intervención humana.

¿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:

Con licencia para <nulo>

También podría gustarte