<jupyter_start><jupyter_text>[Curso de Redes Neuronales](https://rn-unison.github.io) Redes neuronales multicapa y el algoritmo de *b-prop*[**<NAME>**](http://mat.uson.mx/~juliowaissman/), 27 de febrero de 2019.En esta libreta vamos a practicar con las diferentes variaciones del método de descenso de gradiente que se utilizan en el entrenamiento de redes neuronales profundas. Esta no es una libreta tutorial (por el momento, una segunda versión puede que si sea). Así, vamos a referenciar los algoritmos a tutoriales y artículos originales. <NAME> escribio [este tutorial que me parece muy bueno](http://ruder.io/optimizing-gradient-descent/index.html). Es claro, conciso y bien referenciado por si quieres mayor detalle. Nos basaremos en este tutorial para nuestra libreta.Igualmente, vamos a aprovechar la misma libreta para hacer y revisar como funcionan los *autocodificadores*. Los autocodificadores son muy importantes porque dan la intuición necesaria para introducir las redes convolucionales, y porque muestra el poder de compartir parámetros en diferentes partes de una arquitectura distribuida.Empecemos por inicializar los modulos que vamos a requerir.<jupyter_code>%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (16,8)
plt.style.use('ggplot')<jupyter_output><empty_output><jupyter_text>1. Definiendo la red neuronal con arquitectura fijaComo la definición de red neuronal, *f-prop* y *b-prop* ya fue tratados en otra libreta, vamos a inicializar una red neuronal sencilla, la cual tenga:1. Una etapa de autoencoder (usado para dos palabras)2. Una capa oculta con activación ReLU3. Una capa de salida con una neurona logística (problema de clasificación binaria)El número de salidas del autocodificador lo vamos a denotar como $n_a$, y el número de unidades ReLU de la capa oculta como $n_h$  A contonuación se agregan celdas de código para 1. Inicialización de pesos2. Predicción (feed forward)3. El algoritmo de *b-prop* (calculo de $\delta^{(1)} y $\delta^{(2)}$)Si bien es bastante estandar algunas consideraciones se hicieron las cuales se resaltan más adelanta<jupyter_code>def inicializa_red(n_v, n_a, n_h):
    """
    Inicializa la red neuronal
    
    Parámetros
    ----------
    n_v : int con el número de palabras en el vocabulario
    n_a : int con el número de características del autocodificador
    n_h : int con el número de unidades ReLU en la capa oculta
    
    Devuelve
    --------
    W = [W_a, W_h, W_o] Lista con las matrices de pesos
    B = [b_h, b_o] Lista con los sesgos
    
    """
    
    np.random.seed(0) # Solo para efectos de reproducibilidad
    
    W_ac = np.random.randn(n_v, n_a)
    W_h = np.random.randn(n_h, 2 * n_a)
    W_o = np.random.randn(1, n_h)
    
    b_h = np.random.randn(n_h,1)
    b_o = np.sqrt(n_h) * (2 * np.random.rand() - 1.0)
    
    return [W_ac, W_h, W_o], [b_h, b_o]
    

def relu(A):   
    """
    Calcula el valor de ReLU de una matriz de activaciones A
    
    """
    return np.maximum(A, 0)

def logistica(a):
    """
    Calcula la funcion logística de a
    
    """
    return 1. / (1. + np.exp(-a))


def feedforward(X, vocab, W, b):
    """
    Calcula las activaciones de las unidades de la red neuronal
    
    Parámetros
    ----------
    X: un ndarra [-1, 2], dtype='str', con dos palabras por ejemplo
    vocab: Una lista con las palabras ordenadas del vocabulario a utilizar
    W: Lista con las matrices de pesos (ver inicializa_red para mas info)
    b: Lista con los vectores de sesgos (ver inicializa_red para mas info)
    """
    W_a, W_h, W_o = W
    b_h, b_o = b

    one_hot_1 = [vocab.index(x_i) if x_i in vocab else -1 for x_i in X[:,0]]
    one_hot_2 = [vocab.index(x_i) if x_i in vocab else -1 for x_i in X[:,1]]

    activacion_z = np.array([one_hot_1, one_hot_2])
    
    activacion_a = np.r_[W_a[one_hot_1, :].T, 
                         W_a[one_hot_2, :].T]

    activacion_h = relu(W_h @ activacion_a + b_h)
    activacion_o = logistica(W_o @ activacion_h + b_o)
    
    return [activacion_z, activacion_a, activacion_h, activacion_o]<jupyter_output><empty_output><jupyter_text>Ejercicio: Realiza un ejemplo pequeño a mano, imprime las activaciones y compruebalo con tus resultados obtenidos manualmenteSe agrega un ejemplo sin calcular manualmente. Posiblemente sea mejor establecer W y b en forma manual que faciliten los calculos y menos ejemplos.<jupyter_code>vocab = ['a', 'e', 'ei', 'ti', 'tu', 'ya', 'ye', 'toto', 'tur', 'er', 'OOV']

X = np.array([
    ['a', 'a'],
    ['e', 'tu'],
    ['ti', 'ya'],
    ['er', 'ye'],
    ['a', 'a'],
    ['e', 'tu']
])

n_v, n_a, n_h = len(vocab), 5, 7
W, b = inicializa_red(n_v, n_a, n_h)
A = feedforward(X, vocab, W, b)

print("Codificación 'one hot': \n", A[0])
print("Autocodificador: \n", A[1])
print("Activacion capa oculta:\n", A[2])
print("Salidas:\n", A[3])

assert np.all(A[0][:, 1] == A[0][:, -1]) and np.all(A[0][:, 0] == A[0][:, -2])
assert np.all(A[1][:, 1] == A[1][:, -1]) and np.all(A[1][:, 0] == A[1][:, -2])
assert np.all(A[2][:, 1] == A[2][:, -1]) and np.all(A[2][:, 0] == A[2][:, -2])
def deriv_relu(a):
    """
    Calcula la derivada de la activación de a usando ReLU
    
    """
    return np.where(a > 0.0, 1.0, 0.0)

def b_prop(A, Y, W):
    
    W_a, W_h, W_o = W
    activacion_z, activacion_a, activacion_h, activacion_o = A
    
    delta_o = Y.reshape(1, -1) - activacion_o
    delta_h = deriv_relu(activacion_h) * (W_o.T @ delta_o)
    delta_a = W_h.T @ delta_h
    
    gradiente_W_o = delta_o.T @ activacion_h
    gradiente_W_h = delta_h.T @ activacion_a
    
    gradiente_b_o = delta_o.mean(axis=0).reshape(-1, 1)
    gradiente_b_h = delta_h.mean()
    
    gradiente_W_a<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup