
Hola habrozhiteli! El libro sienta las bases para un mayor dominio de la tecnología de aprendizaje profundo. Comienza con una descripción de los conceptos básicos de las redes neuronales y luego examina en detalle las capas adicionales de la arquitectura.
El libro está especialmente escrito con la intención de proporcionar el umbral de entrada más bajo posible. No necesita conocimientos de álgebra lineal, métodos numéricos, optimizaciones convexas e incluso aprendizaje automático. Todo lo que se requiere para comprender el aprendizaje profundo se aclarará a medida que avanza.
Le ofrecemos que se familiarice con el pasaje "¿Qué es un marco de aprendizaje profundo?"
Las buenas herramientas reducen los errores, aceleran el desarrollo y aumentan la velocidad de ejecución.Si lee mucho sobre el aprendizaje profundo, es probable que se encuentre con marcos tan conocidos como PyTorch, TensorFlow, Theano (recientemente declarado obsoleto), Keras, Lasagne y DyNet. En los últimos años, los marcos han evolucionado muy rápidamente, y a pesar de que todos estos marcos se distribuyen de forma gratuita y de código abierto, cada uno de ellos tiene un espíritu de competencia y camaradería.
Hasta ahora, he evitado discutir marcos, porque, antes que nada, era extremadamente importante que entendiera lo que estaba sucediendo detrás de escena, implementando los algoritmos manualmente (usando solo la biblioteca NumPy). Pero ahora comenzaremos a usar dichos marcos, porque las redes que vamos a entrenar, las redes con memoria a corto plazo a largo plazo (LSTM), son muy complejas, y el código que las implementa usando NumPy es difícil de leer, usar y depurar (gradientes en este código se encuentran en todas partes).
Es esta complejidad que los marcos de aprendizaje profundo están diseñados para abordar. El marco de aprendizaje profundo puede reducir significativamente la complejidad del código (así como reducir la cantidad de errores y aumentar la velocidad de desarrollo) y aumentar la velocidad de su ejecución, especialmente si usa un procesador de gráficos (GPU) para entrenar la red neuronal, que puede acelerar el proceso de 10 a 100 veces. Por estas razones, los marcos se usan en casi todas partes en la comunidad de investigación, y comprender las características de su trabajo será útil en su carrera como usuario e investigador de aprendizaje profundo.
Pero no nos limitaremos al marco de ningún marco en particular, ya que esto le impedirá aprender cómo funcionan todos estos modelos complejos (como el LSTM). En su lugar, crearemos nuestro propio marco ligero, siguiendo las últimas tendencias en el desarrollo de marcos. Siguiendo este camino, sabrá exactamente qué hacen los marcos cuando se crean arquitecturas complejas con su ayuda. Además, un intento de crear su propio marco pequeño lo ayudará a cambiar sin problemas al uso de marcos de aprendizaje profundo reales, porque ya conocerá los principios de organizar una interfaz de programa (API) y su funcionalidad. Este ejercicio fue muy útil para mí, y el conocimiento adquirido al crear mi propio marco resultó ser muy útil para depurar modelos problemáticos.
¿Cómo simplifica el marco el código? Hablando de manera abstracta, elimina la necesidad de escribir el mismo código una y otra vez. Específicamente, la característica más conveniente del marco de aprendizaje profundo es el soporte para la retropropagación automática y la optimización automática. Esto le permite escribir solo código de distribución directa, y el marco se encargará automáticamente de la distribución posterior y la corrección de los pesos. La mayoría de los marcos modernos incluso simplifican el código que implementa la distribución directa al ofrecer interfaces de alto nivel para definir capas típicas y funciones de pérdida.
Introducción a los tensores
Los tensores son una forma abstracta de vectores y matrices.Hasta este momento, usamos vectores y matrices como las estructuras principales. Permítame recordarle que una matriz es una lista de vectores, y un vector es una lista de escalares (números individuales). Un tensor es una forma abstracta para representar listas anidadas de números. El vector es un tensor unidimensional. La matriz es un tensor bidimensional, y las estructuras con una gran cantidad de dimensiones se denominan tensores n-dimensionales. Entonces, comencemos a crear un nuevo marco de aprendizaje profundo definiendo un tipo base, que llamaremos Tensor:
import numpy as np class Tensor (object): def __init__(self, data): self.data = np.array(data) def __add__(self, other): return Tensor(self.data + other.data) def __repr__(self): return str(self.data.__repr__()) def __str__(self): return str(self.data.__str__()) x = Tensor([1,2,3,4,5]) print(x) [1 2 3 4 5] y = x + x print(y) [2 4 6 8 10]
Esta es la primera versión de nuestra estructura de datos básica. Tenga en cuenta que almacena toda la información numérica en la matriz NumPy (self.data) y admite una sola operación de tensor (adición). Agregar operaciones adicionales no es nada difícil, solo agregue funciones adicionales con la funcionalidad correspondiente a la clase Tensor.
Introducción al cálculo automático de gradiente (autogrado)
Anteriormente, realizamos la propagación manual hacia atrás. ¡Ahora hagámoslo automático!En el capítulo 4, presentamos derivados. Desde entonces, hemos calculado manualmente estas derivadas en cada nueva red neuronal. Permítame recordarle que esto se logra mediante el movimiento inverso a través de la red neuronal: primero, se calcula el gradiente en la salida de la red, luego este resultado se usa para calcular la derivada en el componente anterior, y así sucesivamente, hasta que se determinen los gradientes correctos para todos los pesos en la arquitectura. Esta lógica para calcular gradientes también se puede agregar a la clase tensorial. Lo siguiente muestra lo que tenía en mente.
import numpy as np class Tensor (object): def __init__(self, data, creators=None, creation_op=None): self.data = np.array(data) self.creation_op = creation_op self.creators = creators self.grad = None def backward(self, grad): self.grad = grad if(self.creation_op == "add"): self.creators[0].backward(grad) self.creators[1].backward(grad) def __add__(self, other): return Tensor(self.data + other.data, creators=[self,other], creation_op="add") def __repr__(self): return str(self.data.__repr__()) def __str__(self): return str(self.data.__str__()) x = Tensor([1,2,3,4,5]) y = Tensor([2,2,2,2,2]) z = x + y z.backward(Tensor(np.array([1,1,1,1,1])))
Este método presenta dos innovaciones. Primero, cada tensor recibe dos nuevos atributos. creadores es una lista de los tensores utilizados para crear el tensor actual (el valor predeterminado es Ninguno). Es decir, si el tensor z se obtiene sumando los otros dos tensores, x e y, el atributo creador del tensor z contendrá los tensores x e y. creation_op es un atributo complementario que almacena las operaciones utilizadas en el proceso de creación de este tensor. Es decir, la instrucción z = x + y creará un gráfico computacional con tres nodos (x, y y z) y dos aristas (z -> x y z -> y). Cada borde está firmado por la operación desde creation_op, es decir, agregar. Este gráfico ayudará a organizar la propagación recursiva hacia atrás de gradientes.
La primera innovación en esta implementación es la creación automática de un gráfico durante cada operación matemática. Si tomamos z y realizamos otra operación, el gráfico continuará en una nueva variable que hace referencia a z.
La segunda innovación en esta versión de la clase Tensor es la capacidad de usar un gráfico para calcular gradientes. Si llama al método z.backward (), pasará el gradiente para x e y, teniendo en cuenta la función con la que se creó el tensor z (add). Como se muestra en el ejemplo anterior, pasamos el vector de gradiente (np.array ([1,1,1,1,1]]) a z, y ese se aplica a sus padres. Como probablemente recuerde del Capítulo 4, la propagación hacia atrás a través de la suma significa aplicar la propagación hacia atrás. En este caso, solo tenemos que agregar un degradado a x e y, por lo que lo copiamos de z a x e y:
print(x.grad) print(y.grad) print(z.creators) print(z.creation_op) [1 1 1 1 1] [1 1 1 1 1] [array([1, 2, 3, 4, 5]), array([2, 2, 2, 2, 2])] add
La característica más notable de esta forma de cálculo automático de gradiente es que funciona de forma recursiva: cada vector llama al método .backward () de todos sus padres de la lista self.creators:
»Se puede encontrar más información sobre el libro en
el sitio web del editor»
Contenidos»
ExtractoCupón de 25% de descuento para vendedores ambulantes -
Deep LearningTras el pago de la versión en papel del libro, se envía un libro electrónico por correo electrónico.