Creaci贸n de una red neuronal desde cero en Python

    Introducci贸n

    驴Alguna vez te has preguntado c贸mo los chatbots como Siri, Alexa y Cortona pueden responder a las consultas de los usuarios? 驴O c贸mo los coches aut贸nomos pueden conducirse solos sin ayuda humana? Todos estos productos sofisticados tienen una cosa en com煤n: la inteligencia artificial (IA). Es la IA la que les permite realizar tales tareas sin ser supervisados 鈥嬧媜 controlados por un humano. Pero la pregunta sigue siendo: “驴Qu茅 es la IA?” Una respuesta simple a esta pregunta es: “La IA es una combinaci贸n de algoritmos complejos de varios dominios matem谩ticos como 脕lgebra, C谩lculo y Probabilidad y Estad铆stica”.

    En este art铆culo, estudiaremos una red neuronal artificial simple, que es uno de los componentes principales de la inteligencia artificial. Existen diferentes variantes de una Red Neuronal Artificial, dedicadas a resolver un problema en particular. Por ejemplo, las redes neuronales convolucionales se usan com煤nmente para problemas de reconocimiento de im谩genes, mientras que las redes neuronales recurrentes se usan para resolver problemas de secuencia.

    Hay muchas bibliotecas de aprendizaje profundo que se pueden usar para crear una red neuronal en una sola l铆nea de c贸digo. Sin embargo, si realmente desea comprender el funcionamiento en profundidad de una red neuronal, le sugiero que aprenda a codificarla desde cero en cualquier lenguaje de programaci贸n. Realizar este ejercicio realmente aclarar谩 muchos de los conceptos para usted. Y esto es exactamente lo que haremos en este art铆culo.

    El problema

    Dado que este es un art铆culo introductorio, el problema que vamos a resolver es bastante simple. Suponga que tenemos informaci贸n sobre la obesidad, los h谩bitos de fumar y los h谩bitos de ejercicio de cinco personas. Tambi茅n sabemos si estas personas son diab茅ticas o no. Nuestro conjunto de datos se ve as铆:

    PersonSmokingObesityExerciseDiabetic

    Persona 10101
    Persona 20010
    Persona 31000
    Persona 41101
    Persona 51111

    En la tabla anterior, tenemos cinco columnas: Persona, Tabaquismo, Obesidad, Ejercicio y Diab茅tico. Aqu铆 1 se refiere a verdadero y 0 a falso. Por ejemplo, la primera persona tiene valores de 0, 1, 0, lo que significa que la persona no fuma, es obesa y no hace ejercicio. La persona tambi茅n es diab茅tica.

    Es claramente evidente a partir del conjunto de datos que la obesidad de una persona indica que es diab茅tico. Nuestra tarea es crear una red neuronal que sea capaz de predecir si una persona desconocida es diab茅tica o no dados datos sobre sus h谩bitos de ejercicio, obesidad y h谩bitos de tabaquismo. Este es un tipo de problema de aprendizaje supervisado donde se nos dan entradas y las correspondientes salidas correctas y nuestra tarea es encontrar el mapeo entre las entradas y las salidas.

    Nota : Este es solo un conjunto de datos ficticio; en la vida real, las personas obesas no siempre son necesariamente diab茅ticas.

    La soluci贸n

    Crearemos una red neuronal muy simple con una capa de entrada y una capa de salida. Antes de escribir cualquier c贸digo real, primero veamos c贸mo se ejecutar谩 nuestra red neuronal, en teor铆a.

    Teor铆a de redes neuronales

    Una red neuronal es un algoritmo de aprendizaje supervisado, lo que significa que le proporcionamos los datos de entrada que contienen las variables independientes y los datos de salida que contienen la variable dependiente. Por ejemplo, en nuestro ejemplo, nuestras variables independientes son el tabaquismo, la obesidad y el ejercicio. La variable dependiente es si una persona es diab茅tica o no.

    Al principio, la red neuronal hace algunas predicciones aleatorias, estas predicciones se comparan con la salida correcta y se calcula el error o la diferencia entre los valores predichos y los valores reales. La funci贸n que encuentra la diferencia entre el valor real y los valores propagados se llama funci贸n de costo. El costo aqu铆 se refiere al error. Nuestro objetivo es minimizar la funci贸n de costes. Entrenar una red neuronal b谩sicamente se refiere a minimizar la funci贸n de costo. Veremos c贸mo podemos realizar esta tarea.

    La red neuronal que vamos a crear tiene la siguiente representaci贸n visual.

    Una red neuronal se ejecuta en dos pasos: retroalimentaci贸n y propagaci贸n hacia atr谩s. Discutiremos estos dos pasos en detalle.

    Feed Forward

    En la parte de retroalimentaci贸n de una red neuronal, las predicciones se realizan en funci贸n de los valores en los nodes de entrada y los pesos. Si observa la red neuronal en la figura anterior, ver谩 que tenemos tres caracter铆sticas en el conjunto de datos: tabaquismo, obesidad y ejercicio, por lo tanto, tenemos tres nodes en la primera capa, tambi茅n conocida como capa de entrada. Hemos reemplazado nuestros nombres de funciones con la variable x, por generalidad en la figura anterior.

    Los pesos de una red neuronal son b谩sicamente las cadenas que tenemos que ajustar para poder predecir correctamente nuestra salida. Por ahora, recuerde que para cada funci贸n de entrada, tenemos un peso.

    Los siguientes son los pasos que se ejecutan durante la fase de retroalimentaci贸n de una red neuronal:

    Paso 1: (Calcule el producto escalar entre las entradas y los pesos)

    Los nodes de la capa de entrada est谩n conectados con la capa de salida a trav茅s de tres par谩metros de ponderaci贸n. En la capa de salida, los valores en los nodes de entrada se multiplican con sus pesos correspondientes y se suman. Finalmente, el t茅rmino de sesgo se agrega a la suma. El ben la figura anterior se refiere al t茅rmino de sesgo.

    El t茅rmino de sesgo es muy importante aqu铆. Supongamos que si tenemos una persona que no fuma, no es obesa y no hace ejercicio, la suma de los productos de los nodes de entrada y los pesos ser谩 cero. En ese caso, la salida siempre ser谩 cero sin importar cu谩nto entrenemos los algoritmos. Por lo tanto, para poder hacer predicciones, incluso si no tenemos ninguna informaci贸n distinta de cero sobre la persona, necesitamos un t茅rmino de sesgo. El t茅rmino de sesgo es necesario para crear una red neuronal robusta.

    Matem谩ticamente, en el paso 1, realizamos el siguiente c谩lculo:

    $$
    XW = x1w1 + x2w2 + x3w3 + b
    $$

    Paso 2: (Pase el resultado del paso 1 a trav茅s de una funci贸n de activaci贸n)

    El resultado del Paso 1 puede ser un conjunto de valores. Sin embargo, en nuestra salida tenemos los valores en forma de 1 y 0. Queremos que nuestra salida tenga el mismo formato. Para hacerlo, necesitamos una funci贸n de activaci贸n , que aplasta los valores de entrada entre 1 y 0. Una de esas funciones de activaci贸n es la funci贸n sigmoidea .

    La funci贸n sigmoidea devuelve 0.5 cuando la entrada es 0. Devuelve un valor cercano a 1 si la entrada es un n煤mero positivo grande. En caso de entrada negativa, la funci贸n sigmoidea genera un valor cercano a cero.

    Matem谩ticamente, la funci贸n sigmoidea se puede representar como:

    $$
    theta_ {XW} = frac {mathrm {1}} {mathrm {1} + e ^ {- XW}}
    $$

    Intentemos trazar la funci贸n sigmoidea:

    input = np.linspace(-10, 10, 100)
    
    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    from matplotlib import pyplot as plt
    plt.plot(input, sigmoid(input), c="r")
    

    En el script anterior, primero generamos aleatoriamente 100 puntos espaciados linealmente entre -10 y 10. Para hacerlo, usamos el linspacem茅todo de la biblioteca NumPy. A continuaci贸n, definimos la sigmoidfunci贸n. Finalmente, usamos la matplotlibbiblioteca para trazar los valores de entrada contra los valores devueltos por la sigmoidfunci贸n. La salida se ve as铆:

    Puede ver que si la entrada es un n煤mero negativo, la salida es cercana a cero, de lo contrario, si la entrada es positiva, la salida est谩 cerca de 1. Sin embargo, la salida siempre est谩 entre 0 y 1. Esto es lo que queremos.

    Esto resume la parte feedforward de nuestra red neuronal. Es bastante sencillo. Primero tenemos que encontrar el producto escalar de la matriz de caracter铆sticas de entrada con la matriz de ponderaci贸n. A continuaci贸n, pase el resultado de la salida a trav茅s de una funci贸n de activaci贸n, que en este caso es la funci贸n sigmoidea. El resultado de la funci贸n de activaci贸n es b谩sicamente la salida prevista para las caracter铆sticas de entrada.

    Propagaci贸n hacia atr谩s

    Al principio, antes de realizar cualquier entrenamiento, la red neuronal hace predicciones aleatorias que est谩n lejos de ser correctas.

    El principio detr谩s del funcionamiento de una red neuronal es simple. Comenzamos dejando que la red haga predicciones aleatorias sobre la salida. Luego comparamos la salida predicha de la red neuronal con la salida real. A continuaci贸n, ajustamos nuestros pesos y el sesgo de tal manera que nuestra salida predicha se acerque m谩s a la salida real, que b谩sicamente se conoce como “entrenamiento de la red neuronal”.

    En la secci贸n de propagaci贸n hacia atr谩s, entrenamos nuestro algoritmo. Echemos un vistazo a los pasos involucrados en la secci贸n de propagaci贸n hacia atr谩s.

    Paso 1: (Calcular el costo)

    El primer paso en la secci贸n de retropropagaci贸n es encontrar el “costo” de las predicciones. El costo de la predicci贸n se puede calcular simplemente encontrando la diferencia entre la salida prevista y la salida real. Cuanto mayor sea la diferencia, mayor ser谩 el costo.

    Hay varias otras formas de encontrar el costo, pero usaremos la funci贸n del costo del error cuadr谩tico medio . Una funci贸n de costo es simplemente la funci贸n que encuentra el costo de las predicciones dadas.

    La funci贸n del costo del error cuadr谩tico medio se puede representar matem谩ticamente como:

    $$
    MSE =
    frac {mathrm {1}} {mathrm {n}}
    sumnolimits_ {i = 1} ^ {n}
    (predicho – observado) ^ {2}
    $$

    Aqu铆 nest谩 el n煤mero de observaciones.

    Paso 2: (Minimizar el costo)

    Nuestro prop贸sito final es ajustar las perillas de nuestra red neuronal de tal manera que se minimice el costo. Si observa nuestra red neuronal, notar谩 que solo podemos controlar los pesos y el sesgo. Todo lo dem谩s est谩 fuera de nuestro control. No podemos controlar las entradas, no podemos controlar los productos escalares y no podemos manipular la funci贸n sigmoidea.

    Para minimizar el costo, necesitamos encontrar el peso y los valores de sesgo para los cuales la funci贸n de costo devuelve el valor m谩s peque帽o posible. Cuanto menor sea el costo, m谩s correctas ser谩n nuestras predicciones.

    Este es un problema de optimizaci贸n donde tenemos que encontrar los m铆nimos de la funci贸n .

    Para encontrar los m铆nimos de una funci贸n, podemos usar el algoritmo de degradado decente . El algoritmo de degradado decente se puede representar matem谩ticamente de la siguiente manera:

    $$
    repetir hasta la convergencia: comenzar {Bmatrix} w_j: = w_j – alpha frac {parcial} {parcial w_j} J (w_0, w_1 ……. w_n) end {Bmatrix} …….. ….. (1)
    $$

    Aqu铆, en la ecuaci贸n anterior, Jest谩 la funci贸n de costo. B谩sicamente, lo que dice la ecuaci贸n anterior es: encuentre la derivada parcial de la funci贸n de costo con respecto a cada peso y sesgo y reste el resultado de los valores de peso existentes para obtener los nuevos valores de peso.

    La derivada de una funci贸n nos da su pendiente en cualquier punto dado. Para encontrar si el costo aumenta o disminuye, dado el valor de peso, podemos encontrar la derivada de la funci贸n en ese valor de peso particular. Si el costo aumenta con el aumento de peso, la derivada devolver谩 un valor positivo que luego se restar谩 del valor existente.

    Por otro lado, si el costo disminuye con un aumento de peso, se devolver谩 un valor negativo, que se sumar谩 al valor de peso existente, ya que de negativo a negativo es positivo.

    En la Ecuaci贸n 1, podemos ver que hay un s铆mbolo alfa, que se multiplica por el gradiente. Esto se llama tasa de aprendizaje. La tasa de aprendizaje define qu茅 tan r谩pido aprende nuestro algoritmo. Para obtener m谩s detalles sobre c贸mo se puede definir la tasa de aprendizaje, consulte este art铆culo .

    Necesitamos repetir la ejecuci贸n de la Ecuaci贸n 1 para todos los pesos y sesgos hasta que el costo se minimice al nivel deseable. En otras palabras, necesitamos seguir ejecutando la Ecuaci贸n 1 hasta que obtengamos dichos valores de sesgo y ponderaciones, para lo cual la funci贸n de costo devuelve un valor cercano a cero.

    Y eso es todo. Ahora es el momento de implementar lo que hemos estudiado hasta ahora. Crearemos una red neuronal simple con una entrada y una capa de salida en Python.

    Implementaci贸n de redes neuronales en Python

    Primero creemos nuestro conjunto de caracter铆sticas y las etiquetas correspondientes. Ejecute el siguiente script:

    import numpy as np
    feature_set = np.array([[0,1,0],[0,0,1],[1,0,0],[1,1,0],[1,1,1]])
    labels = np.array([[1,0,0,1,1]])
    labels = labels.reshape(5,1)
    

    En el script anterior, creamos nuestro conjunto de funciones. Contiene cinco registros. De manera similar, creamos un labelsconjunto que contiene las etiquetas correspondientes para cada registro en el conjunto de caracter铆sticas. Las etiquetas son las respuestas que intentamos predecir con la red neuronal.

    El siguiente paso es definir hiperpar谩metros para nuestra red neuronal. Ejecute el siguiente script para hacerlo:

    np.random.seed(42)
    weights = np.random.rand(3,1)
    bias = np.random.rand(1)
    lr = 0.05
    

    En el script anterior usamos la random.seedfunci贸n para que podamos obtener los mismos valores aleatorios siempre que se ejecute el script.

    En el siguiente paso, inicializamos nuestros pesos con n煤meros aleatorios distribuidos normalmente. Como tenemos tres caracter铆sticas en la entrada, tenemos un vector de tres pesos. Luego inicializamos el valor de sesgo con otro n煤mero aleatorio. Finalmente, establecemos la tasa de aprendizaje en 0.05.

    A continuaci贸n, necesitamos definir nuestra funci贸n de activaci贸n y su derivada (explicar茅 en un momento por qu茅 necesitamos encontrar la derivada de la activaci贸n). Nuestra funci贸n de activaci贸n es la funci贸n sigmoidea, que cubrimos anteriormente.

    La siguiente secuencia de comandos de Python crea esta funci贸n:

    def sigmoid(x):
        return 1/(1+np.exp(-x))
    

    Y el m茅todo que calcula la derivada de la funci贸n sigmoidea se define de la siguiente manera:

    def sigmoid_der(x):
        return sigmoid(x)*(1-sigmoid(x))
    

    La derivada de la funci贸n sigmoidea es simple sigmoid(x) * sigmoid(1-x).

    Ahora estamos listos para entrenar nuestra red neuronal que podr谩 predecir si una persona es obesa o no.

    Mira el siguiente gui贸n:

    for epoch in range(20000):
        inputs = feature_set
    
        # feedforward step1
        XW = np.dot(feature_set, weights) + bias
    
        #feedforward step2
        z = sigmoid(XW)
    
    
        # backpropagation step 1
        error = z - labels
    
        print(error.sum())
    
        # backpropagation step 2
        dcost_dpred = error
        dpred_dz = sigmoid_der(z)
    
        z_delta = dcost_dpred * dpred_dz
    
        inputs = feature_set.T
        weights -= lr * np.dot(inputs, z_delta)
    
        for num in z_delta:
            bias -= lr * num
    

    No se deje intimidar por este c贸digo. Lo explicar茅 l铆nea por l铆nea.

    En el primer paso, definimos el n煤mero de 茅pocas. Una 茅poca es b谩sicamente la cantidad de veces que queremos entrenar el algoritmo en nuestros datos. Entrenaremos el algoritmo con nuestros datos 20.000 veces. Prob茅 este n煤mero y descubr铆 que el error se minimiza bastante despu茅s de 20,000 iteraciones. Puedes probar con un n煤mero diferente. El objetivo final es minimizar el error.

    A continuaci贸n, almacenamos los valores de la feature_seta la inputvariable. Luego ejecutamos la siguiente l铆nea:

    XW = np.dot(feature_set, weights) + bias
    

    Aqu铆 encontramos el producto escalar de la entrada y el vector de peso y le agregamos sesgo. Este es el Paso 1 de la secci贸n de feedforward.

    En esta l铆nea:

    z = sigmoid(XW)
    

    Pasamos el producto escalar a trav茅s de la funci贸n de activaci贸n sigmoidea, como se explica en el Paso 2 de la secci贸n de avance. Esto completa la parte de avance de nuestro algoritmo.

    Ahora es el momento de comenzar la propagaci贸n hacia atr谩s. La variable zcontiene las salidas previstas. El primer paso de la propagaci贸n hacia atr谩s es encontrar el error. Lo hacemos en la siguiente l铆nea:

    error = z - labels
    

    Luego imprimimos el error en la pantalla.

    Ahora es el momento de ejecutar el Paso 2 de retropropagaci贸n, que es la esencia de este c贸digo.

    Sabemos que nuestra funci贸n de costos es:

    $$
    MSE = frac {mathrm {1}} {mathrm {n}} sumnolimits_ {i = 1} ^ {n} (predicho – observado) ^ {2}
    $$

    Necesitamos diferenciar esta funci贸n con respecto a cada peso. Usaremos la regla de diferenciaci贸n de la cadena para este prop贸sito. Supongamos que “d_cost” es la derivada de nuestra funci贸n de costo con respecto al peso “w”, podemos usar la regla de la cadena para encontrar esta derivada, como se muestra a continuaci贸n:

    $$
    frac {d_cost} {dw} = frac {d_cost} {d_pred}, frac {d_pred} {dz}, frac {dz} {dw}
    $$

    Aqu铆,

    $$
    frac {d_cost} {d_pred}
    $$

    se puede calcular como:

    $$
    2 (previsto – observado)
    $$

    Aqu铆, 2 es constante y, por tanto, puede ignorarse. Este es b谩sicamente el error que ya calculamos. En el c贸digo, puede ver la l铆nea:

    dcost_dpred = error # ........ (2)
    

    A continuaci贸n tenemos que encontrar:

    $$
    frac {d_pred}{dz}
    $$

    Aqu铆 “d_pred” es simplemente la funci贸n sigmoidea y la hemos diferenciado con respecto al producto escalar de entrada “z”. En el script, esto se define como:

    dpred_dz = sigmoid_der(z) # ......... (3)
    

    Finalmente, tenemos que encontrar:

    $$
    frac {d_z} {dw}
    $$

    Lo sabemos:

    $$
    z = x1w1 + x2w2 + x3w3 + b
    $$

    Por lo tanto, la derivada con respecto a cualquier peso es simplemente la entrada correspondiente. Por lo tanto, nuestra derivada final de la funci贸n de costo con respecto a cualquier peso es:

    slope = input x dcost_dpred x dpred_dz
    

    Eche un vistazo a las siguientes tres l铆neas:

    z_delta = dcost_dpred * dpred_dz
    inputs = feature_set.T
    weights -= lr * np.dot(inputs, z_delta)
    

    Aqu铆 tenemos la z_deltavariable, que contiene el producto de dcost_dpredy dpred_dz. En lugar de recorrer cada registro y multiplicar la entrada por la correspondiente z_delta, tomamos la transposici贸n de la matriz de caracter铆sticas de entrada y la multiplicamos por z_delta. Finalmente, multiplicamos la variable de la tasa de aprendizaje lrpor la derivada para aumentar la velocidad de convergencia.

    Luego recorrimos cada valor derivado y actualizamos nuestros valores de sesgo, como se muestra en este script:

    Una vez que comience el ciclo, ver谩 que el error total comienza a disminuir como se muestra a continuaci贸n:

    0.001700995120272485
    0.001700910187124885
    0.0017008252625468727
    0.0017007403465365955
    0.00170065543909367
    0.0017005705402162556
    0.0017004856499031988
    0.0017004007681529695
    0.0017003158949647542
    0.0017002310303364868
    0.0017001461742678046
    0.0017000613267565308
    0.0016999764878018585
    0.0016998916574025129
    0.00169980683555691
    0.0016997220222637836
    0.0016996372175222992
    0.0016995524213307602
    0.0016994676336875778
    0.0016993828545920908
    0.0016992980840424554
    0.0016992133220379794
    0.0016991285685766487
    0.0016990438236577712
    0.0016989590872797753
    0.0016988743594415108
    0.0016987896401412066
    0.0016987049293782815
    

    Puede ver que el error es extremadamente peque帽o al final del entrenamiento de nuestra red neuronal. En este momento, nuestros pesos y sesgos tendr谩n valores que pueden usarse para detectar si una persona es diab茅tica o no, en funci贸n de sus h谩bitos de tabaquismo, obesidad y h谩bitos de ejercicio.

    Ahora puede intentar predecir el valor de una sola instancia. Supongamos que tenemos un registro de un paciente que ingresa, fuma, no es obeso y no hace ejercicio. Veamos si es probable que sea diab茅tico o no. La funci贸n de entrada se ver谩 as铆: [1,0,0].

    Ejecute el siguiente script:

    single_point = np.array([1,0,0])
    result = sigmoid(np.dot(single_point, weights) + bias)
    print(result)
    

    En la salida ver谩:

    [0.00707584]
    

    Puede ver que es probable que la persona no sea diab茅tica, ya que el valor est谩 mucho m谩s cerca de 0 que de 1.

    Ahora probemos a otra persona que no fuma, es obesa y no hace ejercicio. El vector de caracter铆sticas de entrada ser谩 [0,1,0]. Ejecute este script:

    single_point = np.array([0,1,0])
    result = sigmoid(np.dot(single_point, weights) + bias)
    print(result)
    

    En la salida ver谩 el siguiente valor:

    [0.99837029]
    

    Puede ver que el valor est谩 muy cerca de 1, lo que probablemente se deba a la obesidad de la persona.

    Recursos

    驴Quiere aprender m谩s sobre la creaci贸n de redes neuronales para resolver problemas complejos? Si es as铆, intente consultar otros recursos, como este curso en l铆nea:

    Deep Learning AZ: redes neuronales artificiales pr谩cticas

    Cubre las redes neuronales con mucho m谩s detalle, incluidas las redes neuronales convolucionales, las redes neuronales recurrentes y mucho m谩s.

    Conclusi贸n

    En este art铆culo creamos una red neuronal muy simple con una entrada y una capa de salida desde cero en Python. Esta red neuronal se llama simplemente perceptr贸n . Un perceptr贸n puede clasificar datos separables linealmente. Los datos linealmente separables son el tipo de datos que pueden separarse mediante un hiperplano en un espacio n-dimensional.

    Las redes neuronales artificiales de palabras reales son mucho m谩s complejas, poderosas y consisten en m煤ltiples capas ocultas y m煤ltiples nodes en la capa oculta. Estas redes neuronales pueden identificar l铆mites de decisi贸n reales no lineales. Explicar茅 c贸mo crear una red neuronal multicapa desde cero en Python en un pr贸ximo art铆culo.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *