La historia del aprendizaje profundo: explorada a través de 6 fragmentos de código

En este artículo, exploraremos seis fragmentos de código que hicieron que el aprendizaje profundo sea lo que es hoy. Cubriremos a los inventores y el trasfondo de sus avances. Cada historia incluye ejemplos de código simple en FloydHub y GitHub para jugar con.

Si este es su primer encuentro con el aprendizaje profundo, sugiero leer mi Deep Learning 101 para desarrolladores .

Para ejecutar los ejemplos de código en FloydHub, instale la herramienta de línea floydcommand . Luego, clone los ejemplos de código que he proporcionado a su máquina local.

Nota: Si es nuevo en FloydHub, es posible que desee leer primero la sección de inicio de FloydHub en mi publicación anterior.

Inicie la CLI en la carpeta de proyecto de ejemplo en su máquina local. Ahora puede girar el proyecto en FloydHub con el siguiente comando:

floyd run --data emilwallner/datasets/mnist/1:mnist --tensorboard --mode jupyter

El método de mínimos cuadrados

El aprendizaje profundo comenzó con un fragmento de matemáticas.

Lo he traducido a Python:

# y = mx + b
# m is slope, b is y-intercept
def compute_error_for_line_given_points(b, m, coordinates):
    totalError = 0
    for i in range(0, len(coordinates)):
        x = coordinates[i][0]
        y = coordinates[i][1]
        totalError += (y - (m * x + b)) ** 2
    return totalError / float(len(coordinates))
# example 
compute_error_for_line_given_points(1, 2, [[3,6],[6,9],[12,18]])

Esto fue publicado por primera vez por Adrien-Marie Legendre en 1805. Era un matemático parisino que también era conocido por medir el metro.

Tenía una particular obsesión por predecir la ubicación futura de los cometas. Tenía las ubicaciones de un par de cometas pasados. Fue implacable ya que los usó en su búsqueda de un método para calcular su trayectoria.

Realmente fue uno de esos momentos de spaghetti-on-the-wall. Intentó varios métodos, luego una versión finalmente se quedó con él.

El proceso de Legendre comenzó adivinando la ubicación futura de un cometa. Luego cuadró los errores que cometió, y finalmente rehace su conjetura para reducir la suma de los errores al cuadrado. Esta fue la semilla para la regresión lineal.

Juega con el código anterior en la libreta Jupyter que he proporcionado para tener una idea. m es el coeficiente y ben la constante para su predicción, y coordinateslas ubicaciones del cometa. El objetivo es encontrar una combinación de mbdonde el error sea lo más pequeño posible.

Este es el núcleo del aprendizaje profundo:

  • Tome una entrada y una salida deseada
  • Luego busca la correlación entre los dos

Descenso de gradiente

El método de Legendre para tratar de reducir manualmente la tasa de error consumía mucho tiempo. Peter Debye fue ganador del premio Nobel de los Países Bajos. Formó una solución para este proceso un siglo después, en 1909.

Imaginemos que Legendre tiene un parámetro del que preocuparse: lo llamaremos X. El Yeje representa el valor de error para cada valor de X. Legendre estaba buscando dónde los Xresultados en el error más bajo.

En esta representación gráfica, podemos ver que el valor de Xeso minimiza el error Yes cuando X = 1.1.

Peter Debye notó que la pendiente a la izquierda del mínimo es negativa, mientras que es positiva en el otro lado. Por lo tanto, si conoce el valor de la pendiente en un Xvalor determinado , puede orientar Yhacia su valor mínimo.

Esto condujo al método de descenso de gradiente . El principio se usa en casi todos los modelos de aprendizaje profundo.

Para jugar con esto, supongamos que la función de error es Error = x⁵ -2x³-2. Para conocer la pendiente de cualquier Xvalor dado tomamos su derivada, que es 5x⁴ - 6x²​​:

Mira el video de Khan Academy si necesitas mejorar tus conocimientos sobre derivados.

La matemática de Debye traducida a Python:

current_x = 0.5 # the algorithm starts at x=0.5
learning_rate = 0.01 # step size multiplier
num_iterations = 60 # the number of times to train the function
#the derivative of the error function (x**4 = the power of 4 or x^4) 
def slope_at_given_x_value(x): 
   return 5 * x**4 - 6 * x**2
# Move X to the right or left depending on the slope of the error function
for i in range(num_iterations):
   previous_x = current_x
   current_x += -learning_rate * slope_at_given_x_value(previous_x)
   print(previous_x)
print("The local minimum occurs at %f" % current_x)

El truco aquí es el learning_rate. Al ir en la dirección opuesta a la pendiente, se acerca al mínimo. Además, cuanto más se acerca al mínimo, menor es la pendiente. Esto reduce cada paso a medida que la pendiente se aproxima a cero.

num_iterations es su tiempo estimado de iteraciones antes de llegar al mínimo. Juega con los parámetros para obtener una intuición para el descenso del gradiente.

Regresión lineal

Combinando el método de descendencia por mínimos cuadrados y gradiente obtendrás regresión lineal. En las décadas de 1950 y 1960, un grupo de economistas experimentales implementó versiones de estas ideas en computadoras antiguas. La lógica se implementó en tarjetas perforadas físicas, programas de software realmente hechos a mano. Tomó varios días preparar estas tarjetas perforadas y hasta 24 horas para ejecutar un análisis de regresión a través de la computadora.

Aquí hay un ejemplo de regresión lineal traducido a Python para que no tenga que hacerlo en tarjetas perforadas:

#Price of wheat/kg and the average price of bread
wheat_and_bread = [[0.5,5],[0.6,5.5],[0.8,6],[1.1,6.8],[1.4,7]]
def step_gradient(b_current, m_current, points, learningRate):
    b_gradient = 0
    m_gradient = 0
    N = float(len(points))
    for i in range(0, len(points)):
        x = points[i][0]
        y = points[i][1]
        b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
        m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
    new_b = b_current - (learningRate * b_gradient)
    new_m = m_current - (learningRate * m_gradient)
    return [new_b, new_m]
def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
    b = starting_b
    m = starting_m
    for i in range(num_iterations):
        b, m = step_gradient(b, m, points, learning_rate)
    return [b, m]
gradient_descent_runner(wheat_and_bread, 1, 1, 0.01, 100)

Esto no debería introducir nada nuevo. Sin embargo, puede ser un poco un alboroto para fusionar la función de error con el descenso de gradiente. Ejecute el código y juegue con este simulador de regresión lineal .

El Perceptron

Entra Frank Rosenblatt, el tipo que disecó cerebros de rata durante el día y buscó signos de vida extraterrestre por la noche. En 1958, apareció en la portada del New York Times: ” New Navy Device Learns By Doing ” con una máquina que imita a una neurona .

Si mostró a la máquina de Rosenblatt 50 juegos de dos imágenes, una con una marca a la izquierda y otra a la derecha, podría hacer la distinción sin preprogramar. El público se dejó llevar por las posibilidades de una verdadera máquina de aprendizaje.

Para cada ciclo de entrenamiento, comienzas con datos de entrada a la izquierda. Los pesos aleatorios iniciales se agregan a todos los datos de entrada. Luego se resumen. Si la suma es negativa, se traduce a 0, de lo contrario, se mapea en a 1.

Si la predicción es correcta, entonces no pasa nada a los pesos en ese ciclo. Si es incorrecto, multiplica el error con una tasa de aprendizaje. Esto ajusta los pesos en consecuencia.

Vamos a ejecutar el perceptrón con la lógica O clásica.

La máquina perceptron traducida a Python:

from random import choice 
from numpy import array, dot, random 
1_or_0 = lambda x: 0 if x < 0 else 1 
training_data = [ (array([0,0,1]), 0), 
                    (array([0,1,1]), 1), 
                    (array([1,0,1]), 1), 
                    (array([1,1,1]), 1), ] 
weights = random.rand(3) 
errors = [] 
learning_rate = 0.2 
num_iterations = 100 
for i in range(num_iterations): 
    input, truth = choice(training_data) 
    result = dot(weights, input) 
    error = truth - 1_or_0(result) 
    errors.append(error) 
    weights += learning_rate * error * input 
    
for x, _ in training_data: 
    result = dot(x, w) 
    print("{}: {} -> {}".format(input[:2], result, 1_or_0(result)))

En 1969, Marvin Minsky y Seymour Papert destruyeron la idea . En ese momento, Minsky y Papert dirigían el laboratorio de IA en el MIT. Escribieron un libro que demostraba que el perceptrón solo podía resolver problemas lineales. También desacreditaron las afirmaciones sobre el perceptrón multicapa. Tristemente, Frank Rosenblatt murió en un accidente en un bote dos años después.

En 1970, un estudiante de maestría finlandés, descubrió la teoría para resolver problemas no lineales con perceptrones multicapa. Debido a la crítica general del perceptrón, el financiamiento de AI se secó por más de una década. Esto fue conocido como el primer invierno de IA.

El poder de la crítica de Minsky y Papert fue el problema de XOR. La lógica es la misma que la lógica OR con una excepción: cuando tiene dos declaraciones verdaderas (1 y 1), devuelve False (0).

En la lógica OR, es posible dividir la verdadera combinación de las falsas. Pero como puede ver, no puede dividir la lógica XOR con una función lineal.

Redes neuronales artificiales

En 1986, varios experimentos demostraron que las redes neuronales podían resolver problemas no lineales complejos. En ese momento, las computadoras eran 10.000 veces más rápidas que cuando se desarrolló la teoría. Así es como Rumelhart presentó el legendario periódico:

Describimos un nuevo procedimiento de aprendizaje, back-propagation, para redes de unidades similares a neuronas. El procedimiento ajusta repetidamente los pesos de las conexiones en la red para minimizar una medida de la diferencia entre el vector de salida real de la red y el vector de salida deseado. Como resultado de los ajustes de peso, las unidades internas “ocultas” que no son parte de la entrada o salida vienen a representar características importantes del dominio de tareas, y las regularidades en la tarea son capturadas por las interacciones de estas unidades. La capacidad de crear nuevas funciones útiles distingue la retro-propagación de métodos más simples y anteriores, como el procedimiento de convergencia del perceptrón “-  Nature 323, 533-536 (09 de octubre de 1986)

Para comprender el núcleo de este documento, codificaremos la implementación por Andrew Trask de DeepMind. Esto no es un fragmento de código aleatorio. Se ha utilizado en el curso de aprendizaje profundo de Andrew Karpathy en el curso Udacity de Stanford y Siraj Raval. Resuelve el problema de XOR, descongelando el primer invierno de IA.

Antes de profundizar en el código, juegue con este simulador durante una o dos horas para captar la lógica central. Luego lea la publicación de blog de Trask .

Tenga en cuenta que el parámetro agregado [1]en los X_XORdatos son las neuronas de sesgo .

Tienen el mismo comportamiento que una constante en una función lineal:

import numpy as np
X_XOR = np.array([[0,0,1], [0,1,1], [1,0,1],[1,1,1]]) 
y_truth = np.array([[0],[1],[1],[0]])
np.random.seed(1)
syn_0 = 2*np.random.random((3,4)) - 1
syn_1 = 2*np.random.random((4,1)) - 1
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output
def sigmoid_output_to_derivative(output):
    return output*(1-output) 
for j in range(60000):
    layer_1 = sigmoid(np.dot(X_XOR, syn_0))
    layer_2 = sigmoid(np.dot(layer_1, syn_1))
    error = layer_2 - y_truth
    layer_2_delta = error * sigmoid_output_to_derivative(layer_2)
    layer_1_error = layer_2_delta.dot(syn_1.T)
    layer_1_delta = layer_1_error * sigmoid_output_to_derivative(layer_1)
    syn_1 -= layer_1.T.dot(layer_2_delta)
    syn_0 -= X_XOR.T.dot(layer_1_delta)
    
print("Output After Training: \n", layer_2)

La propagación hacia atrás, la multiplicación de la matriz y el descenso del gradiente combinados pueden ser difíciles de envolver. Las visualizaciones de este proceso a menudo son una simplificación de lo que ocurre detrás del capó. Concéntrese en comprender la lógica detrás de esto, pero no se preocupe demasiado por tener una imagen mental de ello.

Además, vea la conferencia de Andrew Karpathy sobre la propagación inversa, juegue con estas visualizaciones y lea el capítulo de Michael Nielsen sobre esto .

Redes neuronales profundas

Las redes neuronales profundas son redes neuronales con más de una capa entre la capa de entrada y salida. La noción fue presentada por Rina Dechter en 1986. Pero no ganó la atención de la corriente principal hasta 2012 . Esto fue poco después de la victoria Jeopardy de IBM Watson y el reconocedor de gatos de Google .

La estructura central de la red neuronal profunda se ha mantenido igual. Pero ahora se aplican a varios problemas diferentes. También ha habido una gran mejora en la regularización.

En 1963, era un conjunto de funciones matemáticas para simplificar los datos de la tierra ruidosa . Ahora se usan en redes neuronales para mejorar su capacidad de generalización .

Una gran parte de la innovación se debe a la potencia de cálculo. Esto mejoró los ciclos de innovación de los investigadores: lo que le llevó un supercomputador calcular a un año a mediados de los ochenta, lleva medio segundo con la tecnología GPU actual.

El costo reducido en informática y el desarrollo de bibliotecas de aprendizaje profundo ahora lo han hecho accesible al público en general. Veamos un ejemplo de una pila común de aprendizaje profundo, comenzando desde la capa inferior:

  • GPU> Nvidia Tesla K80. El hardware comúnmente utilizado para el procesamiento de gráficos. En comparación con las CPU, son en promedio 50-200 veces más rápidas para el aprendizaje profundo.
  • CUDA > lenguaje de programación de bajo nivel para las GPU
  • CuDNN > La biblioteca de Nvidia para optimizar CUDA
  • Tensorflow > El marco de aprendizaje profundo de Google además de CuDNN
  • TFlearn > Un marco de entrada para Tensorflow

Echemos un vistazo a la clasificación de dígitos MNIST de dígitos, el “Hola mundo” del aprendizaje profundo.

Implementado en TFlearn:

from __future__ import division, print_function, absolute_import
import tflearn
from tflearn.layers.core import dropout, fully_connected
from tensorflow.examples.tutorials.mnist import input_data
from tflearn.layers.conv import conv_2d, max_pool_2d
from tflearn.layers.normalization import local_response_normalization
from tflearn.layers.estimator import regression
# Data loading and preprocessing
mnist = input_data.read_data_sets("/data/", one_hot=True)
X, Y, testX, testY = mnist.train.images, mnist.train.labels, mnist.test.images, mnist.test.labels
X = X.reshape([-1, 28, 28, 1])
testX = testX.reshape([-1, 28, 28, 1])
# Building convolutional network
network = tflearn.input_data(shape=[None, 28, 28, 1], name='input')
network = conv_2d(network, 32, 3, activation='relu', regularizer="L2")
network = max_pool_2d(network, 2)
network = local_response_normalization(network)
network = conv_2d(network, 64, 3, activation='relu', regularizer="L2")
network = max_pool_2d(network, 2)
network = local_response_normalization(network)
network = fully_connected(network, 128, activation='tanh')
network = dropout(network, 0.8)
network = fully_connected(network, 256, activation='tanh')
network = dropout(network, 0.8)
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam', learning_rate=0.01,
                        loss='categorical_crossentropy', name='target')
# Training
model = tflearn.DNN(network, tensorboard_verbose=0)
model.fit({'input': X}, {'target': Y}, n_epoch=20,
            validation_set=({'input': testX}, {'target': testY}),
            snapshot_step=100, show_metric=True, run_id='convnet_mnist')

Hay muchos artículos geniales que explican el problema de MNIST: aquí y aquí .

Vamos a resumirlo

Como puede ver en el ejemplo de TFlearn, la lógica principal del aprendizaje profundo sigue siendo similar al perceptrón de Rosenblatt. En lugar de utilizar una función de paso binario Heaviside, las redes de hoy en día utilizan principalmente activaciones de Relu (unidad lineal de rectificador).

En la última capa de la red neuronal convolucional, la pérdida es igual a categorical_crossentropy. Esta es una evolución del menos cuadrado de Legendre, una regresión logística para múltiples categorías. El optimizador se adamorigina del trabajo del descenso de gradiente de Debye.

Noción regularización de Tikhonov Se aplica ampliamente en forma de capas de deserción y funciones de regularización, L1/L2.

Si desea una mejor intuición para las redes neuronales y cómo implementarlas, lea mi publicación anterior: Deep Learning 101 for Coders.

Gracias a Ignacio Tonoli de Maussion , Brian Young, Paal Rgd , Tomas Moška y Charlie Harrington por leer borradores de esto. Las fuentes de código están incluidas en los cuadernos Jupyter.

Acerca de Emil Wallner

Esto es parte de una serie de blogs de varias partes a medida que aprendo el aprendizaje profundo. He pasado una década explorando el aprendizaje humano. Trabajé para la escuela de negocios de Oxford, invirtió en startups de educación y construí un negocio de tecnología educativa. El año pasado, me inscribí en Ecole 42 para aplicar mi conocimiento del aprendizaje humano al aprendizaje automático.

Leave a Reply

Your email address will not be published. Required fields are marked *

Ir a la barra de herramientas