Capítulo 1. La inteligencia artificial

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Es la primera vez que un programa informático derrota a un jugador profesional humano en el juego de Go a tamaño natural, una hazaña que antes se creía que estaba al menos a una década de distancia.

David Silver et al. (2016)

Este capítulo introduce nociones, ideas y definiciones generales del campo de la inteligencia artificial (IA) a efectos de este libro. También proporciona ejemplos prácticos de los distintos tipos de algoritmos principales de aprendizaje. En concreto, "Algoritmos" adopta una perspectiva amplia y categoriza los tipos de datos, los tipos de aprendizaje y los tipos de problemas que suelen encontrarse en un contexto de IA. Este capítulo también presenta ejemplos de aprendizaje no supervisado y de refuerzo. "Redes neuronales" salta directamente al mundo de las redes neuronales, que no sólo son fundamentales para lo que sigue en capítulos posteriores del libro, sino que también han demostrado estar entre los algoritmos más potentes que ofrece la IA en la actualidad. "La importancia de los datos" analiza la importancia del volumen y la variedad de los datos en el contexto de la IA.

Algoritmos

Esta sección presenta nociones básicas del campo de la IA relevantes para este libro. Analiza los distintos tipos de datos, aprendizaje, problemas y enfoques que pueden englobarse bajo el término general de IA. Alpaydin (2016) ofrece una introducción informal y una visión general de muchos de los temas tratados sólo brevemente en esta sección, junto con muchos ejemplos.

Tipos de datos

Los datos en general tienen dos componentes principales:

Características

Los datos de características (o datos de entrada) son datos que se dan como entrada a un algoritmo. En un contexto financiero, podrían ser, por ejemplo, los ingresos y los ahorros de un posible deudor.

Etiquetas

Los datos de etiquetas (o datos de salida) son los datos que se dan como salida relevante para ser aprendidos, por ejemplo, por un algoritmo de aprendizaje supervisado. En un contexto financiero, podría ser la solvencia de un deudor potencial.

Tipos de aprendizaje

Hay tres tipos principales de algoritmos de aprendizaje:

Aprendizaje supervisado (LS)

Son algoritmos que aprenden a partir de un conjunto de datos de muestra dado de valores de características (entrada) y etiquetas (salida). En la siguiente sección se presentan ejemplos de estos algoritmos, como la regresión por mínimos cuadrados ordinarios (MCO) y las redes neuronales. El objetivo del aprendizaje supervisado es aprender la relación entre los valores de entrada y de salida. En finanzas, estos algoritmos podrían entrenarse para predecir si un deudor potencial es solvente o no. A efectos de este libro, estos son los tipos de algoritmos más importantes.

Aprendizaje no supervisado (UL)

Son algoritmos que aprenden sólo a partir de un conjunto de datos de muestra dado de valores de características (entrada), a menudo con el objetivo de encontrar una estructura en los datos. Se supone que aprenden sobre el conjunto de datos de entrada, dados, por ejemplo, algunos parámetros orientativos. Los algoritmos de agrupación entran en esa categoría. En un contexto financiero, estos algoritmos podrían agrupar las acciones en determinados grupos.

Aprendizaje por refuerzo (RL)

Son algoritmos que aprenden por ensayo y error al recibir una recompensa por realizar una acción. Actualizan una política de acción óptima en función de las recompensas y castigos que reciben. Estos algoritmos se utilizan, por ejemplo, para entornos en los que hay que realizar acciones continuamente y las recompensas se reciben inmediatamente, como en un juego de ordenador.

Dado que el aprendizaje supervisado se aborda en la sección siguiente con cierto detalle, unos breves ejemplos ilustrarán el aprendizaje no supervisado y el aprendizaje por refuerzo.

Aprendizaje no supervisado

En pocas palabras, un algoritmo de agrupación de k-medias ordena n observaciones en k Cada observación pertenece al cluster al que su media (centro) está más próxima. El siguiente código Python genera datos de muestra para los que se agrupan los datos de las características. La Figura 1-1 visualiza los datos de muestra agrupados y también muestra que el algoritmo scikit-learn KMeans utilizado aquí ha identificado perfectamente los clusters. El color de los puntos se basa en lo que ha aprendido el algoritmo.1

In [1]: import numpy as np
        import pandas as pd
        from pylab import plt, mpl
        plt.style.use('seaborn')
        mpl.rcParams['savefig.dpi'] = 300
        mpl.rcParams['font.family'] = 'serif'
        np.set_printoptions(precision=4, suppress=True)

In [2]: from sklearn.cluster import KMeans
        from sklearn.datasets import make_blobs

In [3]: x, y = make_blobs(n_samples=100, centers=4,
                          random_state=500, cluster_std=1.25)  1

In [4]: model = KMeans(n_clusters=4, random_state=0)  2

In [5]: model.fit(x)  3
Out[5]: KMeans(n_clusters=4, random_state=0)

In [6]: y_ = model.predict(x)  4

In [7]: y_  5
Out[7]: array([3, 3, 1, 2, 1, 1, 3, 2, 1, 2, 2, 3, 2, 0, 0, 3, 2, 0, 2, 0, 0, 3,
               1, 2, 1, 1, 0, 0, 1, 3, 2, 1, 1, 0, 1, 3, 1, 3, 2, 2, 2, 1, 0, 0,
               3, 1, 2, 0, 2, 0, 3, 0, 1, 0, 1, 3, 1, 2, 0, 3, 1, 0, 3, 2, 3, 0,
               1, 1, 1, 2, 3, 1, 2, 0, 2, 3, 2, 0, 2, 2, 1, 3, 1, 3, 2, 2, 3, 2,
               0, 0, 0, 3, 3, 3, 3, 0, 3, 1, 0, 0], dtype=int32)

In [8]: plt.figure(figsize=(10, 6))
        plt.scatter(x[:, 0], x[:, 1], c=y_,  cmap='coolwarm');
1

Se crea un conjunto de datos de muestra con datos de características agrupadas.

2

Se instancia un objeto modelo KMeans, fijando el número de conglomerados.

3

El modelo se ajusta a los datos de las características.

4

Las predicciones se generan a partir del modelo ajustado.

5

Las predicciones son números del 0 al 3, cada uno de los cuales representa un conglomerado.

aiif 0101
Figura 1-1. Aprendizaje no supervisado de agrupaciones

Una vez entrenado un algoritmo como KMeans, puede, por ejemplo, predecir el conglomerado para una nueva combinación (aún no vista) de valores de características. Supongamos que dicho algoritmo se entrena con datos de características que describen a los deudores potenciales y reales de un banco. Podría aprender sobre la solvencia de los deudores potenciales generando dos clusters. Los nuevos deudores potenciales pueden clasificarse entonces en un determinado clúster: "solventes" frente a "no solventes".

Aprendizaje por refuerzo

El siguiente ejemplo se basa en un juego de lanzar monedas con una moneda que sale cara el 80% de las veces y cruz el 20%. El juego de lanzar la moneda está muy sesgado para resaltar las ventajas del aprendizaje en comparación con un algoritmo de referencia sin información. El algoritmo de referencia, que apuesta aleatoriamente y distribuye por igual a cara y cruz, consigue una recompensa total de unos 50, de media, por cada época de 100 apuestas jugadas:

In [9]: ssp = [1, 1, 1, 1, 0]  1

In [10]: asp = [1, 0]  2

In [11]: def epoch():
             tr = 0
             for _ in range(100):
                 a = np.random.choice(asp)  3
                 s = np.random.choice(ssp)  4
                 if a == s:
                     tr += 1  5
             return tr

In [12]: rl = np.array([epoch() for _ in range(15)])  6
         rl
Out[12]: array([53, 55, 50, 48, 46, 41, 51, 49, 50, 52, 46, 47, 43, 51, 52])

In [13]: rl.mean()  7
Out[13]: 48.93333333333333
1

El espacio de estados (1 = cara, 0 = cruz).

2

El espacio de acción (1 = apuesta a cara, 0 = apuesta a cruz).

3

Se elige aleatoriamente una acción del espacio de acciones.

4

Se elige aleatoriamente un estado del espacio de estados.

5

La recompensa total tr se incrementa en uno si la apuesta es correcta.

6

El juego se desarrolla durante un número de épocas; cada época tiene 100 apuestas.

7

Se calcula la recompensa total media de las épocas jugadas.

El aprendizaje por refuerzo trata de aprender a partir de lo que se observa después de realizar una acción, normalmente basada en una recompensa. Para simplificar las cosas, el siguiente algoritmo de aprendizaje sólo hace un seguimiento de los estados que se observan en cada ronda en la medida en que se añaden al objeto del espacio de acción list. De este modo, el algoritmo aprende el sesgo del juego, aunque quizá no perfectamente. Al tomar muestras aleatorias del espacio de acción actualizado, se refleja el sesgo porque, naturalmente, la apuesta será más a menudo cara. Con el tiempo, se elige cara, por término medio, alrededor del 80% de las veces. La recompensa total media de alrededor de 65 refleja la mejora del algoritmo de aprendizaje en comparación con el algoritmo de referencia no informado:

In [14]: ssp = [1, 1, 1, 1, 0]

In [15]: def epoch():
             tr = 0
             asp = [0, 1]  1
             for _ in range(100):
                 a = np.random.choice(asp)
                 s = np.random.choice(ssp)
                 if a == s:
                     tr += 1
                 asp.append(s)  2
             return tr


In [16]: rl = np.array([epoch() for _ in range(15)])
         rl
Out[16]: array([64, 65, 77, 65, 54, 64, 71, 64, 57, 62, 69, 63, 61, 66, 75])

In [17]: rl.mean()
Out[17]: 65.13333333333334
1

Restablece el espacio de acción antes de empezar (de nuevo)

2

Añade el estado observado al espacio de acción

Tipos de tareas

Según el tipo de datos de las etiquetas y el problema que se plantee, son importantes dos tipos de tareas a aprender:

Estimación

La estimación (o aproximación, regresión) se refiere a los casos en que los datos de las etiquetas son de valor real (continuos); es decir, se representan técnicamente como números de coma flotante.

Clasificación

La clasificación se refiere a los casos en que los datos de las etiquetas constan de un número finito de clases o categorías que suelen representarse mediante valores discretos (números naturales positivos), que a su vez se representan técnicamente como números enteros.

La siguiente sección proporciona ejemplos para ambos tipos de tareas.

Tipos de enfoques

Antes de terminar esta sección, tal vez convenga dar algunas definiciones más. Este libro sigue la diferenciación común entre los tres términos principales siguientes:

Inteligencia artificial (IA)

La IA engloba todos los tipos de aprendizaje (algoritmos), tal como se han definido antes, y algunos más (por ejemplo, los sistemas expertos).

Aprendizaje automático (AM)

El ML es la disciplina de aprender relaciones y otra información sobre conjuntos de datos dados basándose en un algoritmo y una medida del éxito; una medida del éxito podría ser, por ejemplo, el error cuadrático medio (MSE) dados los valores de las etiquetas y los valores de salida que hay que estimar y los valores predichos del algoritmo. El ML es un subconjunto de la IA.

Aprendizaje profundo (AD)

DL engloba todos los algoritmos basados en redes neuronales. El término profundo sólo suele utilizarse cuando la red neuronal tiene más de una capa oculta. La AD es un subconjunto del aprendizaje automático, por lo que también es un subconjunto de la IA.

La DL ha demostrado su utilidad en una serie de amplias áreas problemáticas. Es adecuada para tareas de estimación y clasificación, así como para la RL. En muchos casos, los enfoques basados en DLobtienen mejoresresultados que los algoritmos alternativos, como la regresión logística o los basados en núcleos, como las máquinas de vectores soporte.2 Por eso este libro se centra principalmente en la DL. Los enfoques de DL utilizados incluyen las redes neuronales densas (DNN), las redes neuronales recurrentes (RNN) y las redes neuronales convolucionales (CNN). Encontrarás más detalles en capítulos posteriores, sobre todo en la Parte III.

Redes neuronales

Las secciones anteriores ofrecen una visión más amplia de los algoritmos en la IA. Esta sección muestra cómo encajan las redes neuronales. Un ejemplo sencillo ilustrará lo que caracteriza a las redes neuronales en comparación con los métodos estadísticos tradicionales, como la regresión por mínimos cuadrados ordinarios (MCO). El ejemplo comienza con las matemáticas y, a continuación, utiliza la regresión lineal para la estimación (o aproximación de funciones) y, por último, aplica las redes neuronales para realizar la estimación. El enfoque adoptado aquí es un enfoque de aprendizaje supervisado en el que la tarea consiste en estimar los datos de las etiquetas basándose en los datos de las características. Esta sección también ilustra el uso de redes neuronales en el contexto de losproblemas de clasificación.

Regresión OLS

Supongamos que una función matemática viene dada como sigue:

f : , y = 2 x 2 - 1 3 x 3

Una función de este tipo transforma un valor de entrada x en un valor de salida y . O transforma una serie de valores de entrada x 1 , x 2 , ... , x N en una serie de valores de salida y 1 , y 2 , ... , y N . El siguiente código Python implementa la función matemática como una función Python y crea una serie de valores de entrada y salida. La Figura 1-2 representa los valores de salida frente a los valores de entrada:

In [18]: def f(x):
             return 2 * x ** 2 - x ** 3 / 3  1

In [19]: x = np.linspace(-2, 4, 25)  2
         x  2
Out[19]: array([-2.  , -1.75, -1.5 , -1.25, -1.  , -0.75, -0.5 , -0.25,  0.  ,
                 0.25,  0.5 ,  0.75,  1.  ,  1.25,  1.5 ,  1.75,  2.  ,  2.25,
                 2.5 ,  2.75,  3.  ,  3.25,  3.5 ,  3.75,  4.  ])

In [20]: y = f(x)  3
         y  3
Out[20]: array([10.6667,  7.9115,  5.625 ,  3.776 ,  2.3333,  1.2656,  0.5417,
                 0.1302,  0.    ,  0.1198,  0.4583,  0.9844,  1.6667,  2.474 ,
                 3.375 ,  4.3385,  5.3333,  6.3281,  7.2917,  8.1927,  9.    ,
                 9.6823, 10.2083, 10.5469, 10.6667])

In [21]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro');
1

La función matemática como función Python

2

Los valores de entrada

3

Los valores de salida

aiif 0102
Figura 1-2. Valores de salida frente a valores de entrada

Mientras que en el ejemplo matemático la función aparece en primer lugar, los datos de entrada en segundo y los de salida en tercero, la secuencia es diferente en elaprendizaje estadístico . Supongamos que se dan los valores de entrada y los valores de salida anteriores. Representan la muestra (datos). El problema en la regresión estadística es encontrar una función que se aproxime lo mejor posible a la relación funcional entre los valores de entrada (también llamados valores independientes) y los valores de salida (también llamados valores dependientes).

Supongamos una regresión lineal simple OLS. En este caso, se supone que la relación funcional entre los valores de entrada y salida es lineal, y el problema consiste en encontrar los parámetros óptimos α y β para la siguiente ecuación lineal:

f ^ : , y ^ = α + β x

Para unos valores de entrada dados x 1 , x 2 , ... , x N y los valores de salida y 1 , y 2 , ... , y N óptimos en este caso significa que minimizan el error cuadrático medio (ECM) entre los valores de salida reales y los valores de salida aproximados:

min α,β 1 N n N y n -f ^(x n ) 2

Para el caso de la regresión lineal simple, la solución ( α * , β * ) se conoce en forma cerrada, como muestra la ecuación siguiente. Las barras de las variables indican los valores medios de la muestra:

β * = Cov(x,y) Var(x) α * = y ¯ - β x ¯

El siguiente código Python calcula los valores óptimos de los parámetros, estima linealmente (aproxima) los valores de salida y traza la línea de regresión lineal junto a los datos de la muestra (ver Figura 1-3). El enfoque de regresión lineal no funciona demasiado bien aquí para aproximar la relación funcional. Así lo confirma el valor MSE relativamente alto:

In [22]: beta = np.cov(x, y, ddof=0)[0, 1] / np.var(x)  1
         beta  1
Out[22]: 1.0541666666666667

In [23]: alpha = y.mean() - beta * x.mean()  2
         alpha  2
Out[23]: 3.8625000000000003

In [24]: y_ = alpha + beta * x  3

In [25]: MSE = ((y - y_) ** 2).mean()  4
         MSE  4
Out[25]: 10.721953125

In [26]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         plt.plot(x, y_, lw=3.0, label='linear regression')
         plt.legend();
1

Cálculo del óptimo β

2

Cálculo del óptimo α

3

Cálculo de los valores de salida estimados

4

Cálculo del MSE dada la aproximación

aiif 0103
Figura 1-3. Datos de la muestra y línea de regresión lineal

¿Cómo se puede mejorar (disminuir) el valor de MSE, quizá incluso hasta 0, es decir, hasta una "estimación perfecta"? Por supuesto, la regresión MCO no se limita a una relación lineal simple. Además de la constante y los términos lineales, se pueden añadir fácilmente monomios de orden superior, por ejemplo, como funciones base. Para ello, compara los resultados de la regresión mostrados en la Figura 1-4 y el siguiente código que crea la figura. Las mejoras que se obtienen al utilizar monomios cuadráticos y cúbicos como funciones base son evidentes y también se confirman numéricamente con los valores de MSE calculados. Para las funciones base hasta el monomio cúbico incluido, la estimación es perfecta, y la relación funcional se recupera perfectamente:

In [27]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         for deg in [1, 2, 3]:
             reg = np.polyfit(x, y, deg=deg)  1
             y_ = np.polyval(reg, x)  2
             MSE = ((y - y_) ** 2).mean()  3
             print(f'deg={deg} | MSE={MSE:.5f}')
             plt.plot(x, np.polyval(reg, x), label=f'deg={deg}')
         plt.legend();
         deg=1 | MSE=10.72195
         deg=2 | MSE=2.31258
         deg=3 | MSE=0.00000

In [28]: reg  4
Out[28]: array([-0.3333,  2.    ,  0.    , -0.    ])
1

Paso de regresión

2

Paso de aproximación

3

Cálculo del MSE

4

Valores óptimos ("perfectos") de los parámetros

aiif 0104
Figura 1-4. Datos de la muestra y líneas de regresión MCO

Explotar el conocimiento de la forma de la función matemática a aproximar y, en consecuencia, añadir más funciones base a la regresión conduce a una "aproximación perfecta". Es decir, la regresión MCO recupera los factores exactos de la parte cuadrática y cúbica, respectivamente, de la función original.

Estimación con redes neuronales

Sin embargo, no todas las relaciones son de este tipo. Aquí es donde, por ejemplo, pueden ayudar las redes neuronales. Sin entrar en detalles, las redes neuronales pueden aproximarse a una amplia gama de relaciones funcionales. Por lo general, no es necesario conocer la forma de la relación.

Scikit-learn

El siguiente código de Python utiliza la clase MLPRegressor de scikit-learn, que implementa una DNN para la estimación. Las DNN a veces también se denominan perceptrones multicapa (MLP).3 Los resultados no son perfectos, como ilustran la Figura 1-5 y el MSE. Sin embargo, ya son bastante buenos para la sencilla configuración utilizada:

In [29]: from sklearn.neural_network import MLPRegressor

In [30]: model = MLPRegressor(hidden_layer_sizes=3 * [256],
                              learning_rate_init=0.03,
                              max_iter=5000)  1

In [31]: model.fit(x.reshape(-1, 1), y)  2
Out[31]: MLPRegressor(hidden_layer_sizes=[256, 256, 256], learning_rate_init=0.03,
                      max_iter=5000)

In [32]: y_ = model.predict(x.reshape(-1, 1))  3

In [33]: MSE = ((y - y_) ** 2).mean()
         MSE
Out[33]: 0.021662355744355866

In [34]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         plt.plot(x, y_, lw=3.0, label='dnn estimation')
         plt.legend();
1

Instancia del objeto MLPRegressor

2

Ejecuta el paso de adaptación o aprendizaje

3

Ejecuta el paso de predicción

Basta con echar un vistazo a los resultados de la Figura 1-4 y la Figura 1-5 para suponer que, después de todo, los métodos y enfoques no son demasiado distintos. Sin embargo, hay una diferencia fundamental que merece la pena destacar. Aunque el enfoque de la regresión MCO, como se muestra explícitamente para la regresión lineal simple , se basa en el cálculo de determinadas cantidades y parámetros bien especificados, el enfoque de la red neuronal se basa en elaprendizaje incremental . Esto a su vez significa que un conjunto de parámetros, los pesos dentro de la red neuronal, se inicializan primero aleatoriamente y luego se ajustan gradualmente dadas las diferencias entre la salida de la red neuronal y los valores de salida de la muestra. Este enfoque te permite reentrenar (actualizar) una red neuronal de forma incremental.

aiif 0105
Figura 1-5. Datos de muestra y estimaciones basadas en redes neuronales

Keras

El siguiente ejemplo utiliza un modelo secuencial con el paquete de aprendizaje profundo Keras.4 El modelo se ajusta, o entrena, durante 100 épocas. El procedimiento se repite durante cinco rondas. Después de cada ronda, la aproximación de la red neuronal se actualiza y se representa gráficamente. La figura 1-6 muestra cómo la aproximación mejora gradualmente con cada ronda. Esto también se refleja en la disminución de los valores de MSE. El resultado final no es perfecto, pero, de nuevo, es bastante bueno dada la simplicidad del modelo:

In [35]: import tensorflow as tf
         tf.random.set_seed(100)

In [36]: from keras.layers import Dense
         from keras.models import Sequential
         Using TensorFlow backend.

In [37]: model = Sequential()  1
         model.add(Dense(256, activation='relu', input_dim=1)) 2
         model.add(Dense(1, activation='linear'))  3
         model.compile(loss='mse', optimizer='rmsprop')  4

In [38]: ((y - y_) ** 2).mean()
Out[38]: 0.021662355744355866

In [39]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         for _ in range(1, 6):
             model.fit(x, y, epochs=100, verbose=False)  5
             y_ =  model.predict(x)  6
             MSE = ((y - y_.flatten()) ** 2).mean()  7
             print(f'round={_} | MSE={MSE:.5f}')
             plt.plot(x, y_, '--', label=f'round={_}')  8
         plt.legend();
         round=1 | MSE=3.09714
         round=2 | MSE=0.75603
         round=3 | MSE=0.22814
         round=4 | MSE=0.11861
         round=5 | MSE=0.09029
1

Instancia del objeto modelo Sequential

2

Añade una capa oculta densamente conectada con activación de unidad lineal rectificada (ReLU)5

3

Añade la capa de salida con activación lineal

4

Compila el modelo para su uso

5

Entrena la red neuronal durante un número fijo de épocas

6

Ejecuta el paso de aproximación

7

Calcula el MSE actual

8

Traza los resultados de la aproximación actual

A grandes rasgos, se puede decir que la red neuronal hace casi tan bien la estimación como la regresión MCO, que ofrece un resultado perfecto. Por tanto, ¿por qué utilizar redes neuronales? Una respuesta más exhaustiva tendría que venir más adelante en este libro, pero un ejemplo algo diferente podría dar alguna pista.

Considera, en cambio, que el conjunto de datos de muestra anterior, generado a partir de una función matemática bien definida, es ahora un conjunto de datos de muestra aleatorio, en el que tanto las características como las etiquetas se eligen al azar. Por supuesto, este ejemplo es ilustrativo y no permite una interpretación profunda.

aiif 0106
Figura 1-6. Datos de muestra y estimaciones tras varias rondas de entrenamiento

El código siguiente genera el conjunto de datos de la muestra aleatoria y crea la estimación de regresión MCO basada en un número variable de funciones base monomiales. La Figura 1-7 visualiza los resultados. Incluso para el mayor número de monomios del ejemplo, los resultados de la estimación siguen sin ser demasiado buenos. En consecuencia, el valor MSE es relativamente alto:

In [40]: np.random.seed(0)
         x = np.linspace(-1, 1)
         y = np.random.random(len(x)) * 2 - 1

In [41]: plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         for deg in [1, 5, 9, 11, 13, 15]:
             reg = np.polyfit(x, y, deg=deg)
             y_ = np.polyval(reg, x)
             MSE = ((y - y_) ** 2).mean()
             print(f'deg={deg:2d} | MSE={MSE:.5f}')
             plt.plot(x, np.polyval(reg, x), label=f'deg={deg}')
         plt.legend();
         deg= 1 | MSE=0.28153
         deg= 5 | MSE=0.27331
         deg= 9 | MSE=0.25442
         deg=11 | MSE=0.23458
         deg=13 | MSE=0.22989
         deg=15 | MSE=0.21672

Los resultados de la regresión MCO no son demasiado sorprendentes. En este caso, la regresión MCO supone que la aproximación puede conseguirse mediante una combinación adecuada de un número finito de funciones base. Como el conjunto de datos de la muestra se ha generado aleatoriamente, la regresión MCO no funciona bien en este caso.

aiif 0107
Figura 1-7. Datos de la muestra aleatoria y líneas de regresión MCO

¿Y las redes neuronales? La aplicación es tan sencilla como antes y produce estimaciones como las que se muestran en la Figura 1-8. Aunque el resultado final no es perfecto, es evidente que la red neuronal obtiene mejores resultados que la regresión MCO en la estimación de los valores aleatorios de las etiquetas a partir de los valores aleatorios de las características. Sin embargo, dada su arquitectura, la red neuronal tiene casi 200.000 parámetros entrenables (pesos), lo que ofrece una flexibilidad relativamente alta, sobre todo si se compara con la regresión MCO, para la que se utiliza un máximo de 15 + 1 parámetros:

In [42]: model = Sequential()
         model.add(Dense(256, activation='relu', input_dim=1))
         for _ in range(3):
             model.add(Dense(256, activation='relu'))  1
         model.add(Dense(1, activation='linear'))
         model.compile(loss='mse', optimizer='rmsprop')

In [43]: model.summary()  2
         Model: "sequential_2"
         _________________________________________________________________
         Layer (type)                 Output Shape              Param #
         =================================================================
         dense_3 (Dense)              (None, 256)               512
         _________________________________________________________________
         dense_4 (Dense)              (None, 256)               65792
         _________________________________________________________________
         dense_5 (Dense)              (None, 256)               65792
         _________________________________________________________________
         dense_6 (Dense)              (None, 256)               65792
         _________________________________________________________________
         dense_7 (Dense)              (None, 1)                 257
         =================================================================
         Total params: 198,145
         Trainable params: 198,145
         Non-trainable params: 0
         _________________________________________________________________

In [44]: %%time
         plt.figure(figsize=(10, 6))
         plt.plot(x, y, 'ro', label='sample data')
         for _ in range(1, 8):
             model.fit(x, y, epochs=500, verbose=False)
             y_ =  model.predict(x)
             MSE = ((y - y_.flatten()) ** 2).mean()
             print(f'round={_} | MSE={MSE:.5f}')
             plt.plot(x, y_, '--', label=f'round={_}')
         plt.legend();
         round=1 | MSE=0.13560
         round=2 | MSE=0.08337
         round=3 | MSE=0.06281
         round=4 | MSE=0.04419
         round=5 | MSE=0.03329
         round=6 | MSE=0.07676
         round=7 | MSE=0.00431
         CPU times: user 30.4 s, sys: 4.7 s, total: 35.1 s
         Wall time: 13.6 s
1

Se añaden varias capas ocultas.

2

Se muestra la arquitectura de la red y el número de parámetros entrenables.

aiif 0108
Figura 1-8. Datos de la muestra aleatoria y estimaciones de la red neuronal

Clasificación con redes neuronales

Otra ventaja de las redes neuronales es que también pueden utilizarse fácilmente para tareas de clasificación. Considera el siguiente código Python que implementa una clasificación utilizando una red neuronal basada en Keras. Los datos de las características binarias y los datos de las etiquetas se generan aleatoriamente. El principal ajuste que hay que hacer en cuanto al modelado es cambiar la función de activación de la capa de salida de linear a sigmoid. Encontrarás más detalles al respecto en capítulos posteriores. La clasificación no es perfecta. Sin embargo, alcanzaun alto nivel de precisión.En la Figura 1-9 se muestra cómo cambia la precisión, expresada como la relación entre los resultadoscorrectos y todos los valores de etiqueta, con el número de épocas de entrenamiento. La precisión empieza siendo baja y luego mejora paso a paso, aunque nonecesariamente con cada paso:

In [45]: f = 5
         n = 10

In [46]: np.random.seed(100)

In [47]: x = np.random.randint(0, 2, (n, f))  1
         x  1
Out[47]: array([[0, 0, 1, 1, 1],
                [1, 0, 0, 0, 0],
                [0, 1, 0, 0, 0],
                [0, 1, 0, 0, 1],
                [0, 1, 0, 0, 0],
                [1, 1, 1, 0, 0],
                [1, 0, 0, 1, 1],
                [1, 1, 1, 0, 0],
                [1, 1, 1, 1, 1],
                [1, 1, 1, 0, 1]])

In [48]: y = np.random.randint(0, 2, n)  2
         y  2
Out[48]: array([1, 1, 0, 0, 1, 1, 0, 1, 0, 1])

In [49]: model = Sequential()
         model.add(Dense(256, activation='relu', input_dim=f))
         model.add(Dense(1, activation='sigmoid'))  3
         model.compile(loss='binary_crossentropy', optimizer='rmsprop',
                      metrics=['acc'])  4

In [50]: h = model.fit(x, y, epochs=50, verbose=False)
Out[50]: <keras.callbacks.callbacks.History at 0x7fde09dd1cd0>

In [51]: y_ = np.where(model.predict(x).flatten() > 0.5, 1, 0)
         y_
Out[51]: array([1, 1, 0, 0, 0, 1, 0, 1, 0, 1], dtype=int32)




In [52]: y == y_  5
Out[52]: array([ True,  True,  True,  True, False,  True,  True,  True,  True,
                 True])

In [53]: res = pd.DataFrame(h.history)  6

In [54]: res.plot(figsize=(10, 6));  6
1

Crea datos de características aleatorias

2

Crea datos de etiquetas aleatorias

3

Define la función de activación para la capa de salida como sigmoid

4

Define que la función de pérdida sea binary_crossentropy6

5

Compara los valores predichos con los datos de las etiquetas

6

Traza la función de pérdida y los valores de precisión para cada paso de entrenamiento

aiif 0109
Figura 1-9. Precisión y pérdida de clasificación frente al número de épocas

Los ejemplos de esta sección ilustran algunas características fundamentales de las redes neuronales en comparación con la regresión MCO:

Diagnóstico de problemas

El enfoque de redes neuronales es agnóstico cuando se trata de estimar y clasificar valores de etiquetas, dado un conjunto de valores de características. Los métodos estadísticos, como la regresión OLS, pueden funcionar bien para un conjunto reducido de problemas, pero no demasiado bien o nada en absoluto para otros.

Aprendizaje incremental

Los pesos óptimos dentro de una red neuronal, dada una medida objetivo de éxito, se aprenden de forma incremental basándose en una inicialización aleatoria y en mejoras incrementales. Estas mejoras incrementales se consiguen considerando las diferencias entre los valores predichos y los valores de la etiqueta de muestra y retropropagando las actualizaciones de los pesos a través de la red neuronal.

Aproximación universal

Existen sólidos teoremas matemáticos que demuestran que las redes neuronales (incluso con una sola capa oculta) pueden aproximar casi cualquier función.7

Estas características podrían justificar por qué este libro sitúa a las redes neuronales en el centro en cuanto a los algoritmos utilizados. En el Capítulo 2 se exponen más buenas razones.

Redes neuronales

Las redes neuronales son buenas aprendiendo relaciones entre datos de entrada y de salida. Pueden aplicarse a varios tipos de problemas, como la estimación en presencia de relaciones complejas o la clasificación, para los que los métodos estadísticos tradicionales no son muy adecuados.

Importancia de los datos

El ejemplo del final de la sección anterior demuestra que las redes neuronales son capaces de resolver bastante bien los problemas de clasificación. La red neuronal con una capa oculta alcanza un alto grado de precisión en el conjunto de datos dado, o en la muestra. Sin embargo, ¿qué ocurre con el poder predictivo de una red neuronal? Esto depende en gran medida del volumen y la variedad de los datos disponibles para entrenar la red neuronal. Otro ejemplo numérico, basado en conjuntos de datos más grandes, ilustrará este punto.

Pequeño conjunto de datos

Considera un conjunto de datos de muestra aleatoria similar al utilizado antes en el ejemplo de clasificación, pero con más características y más muestras. La mayoría de los algoritmos utilizados en IA tratan sobre el reconocimiento de patrones. En el siguiente código Python, el número de características binarias define el número de patrones posibles sobre los que el algoritmo puede aprender algo. Dado que los datos de las etiquetas también son binarios, el algoritmo intenta aprender si es más probable un 0 o un 1 dado un determinado patrón, digamos [0, 0, 1, 1, 1, 1, 0, 0, 0, 0]. Como todos los números se eligen aleatoriamente con la misma probabilidad, no hay mucho que aprender más allá del hecho de que las etiquetas 0 y 1 son igual de probables independientemente del patrón (aleatorio) que se observe. Por lo tanto, un algoritmo de predicción de referencia debería acertar aproximadamente el 50% de las veces, independientemente del patrón (aleatorio) que se lepresente:

In [55]: f = 10
         n = 250

In [56]: np.random.seed(100)

In [57]: x = np.random.randint(0, 2, (n, f))  1
         x[:4]  1
Out[57]: array([[0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
                [0, 1, 0, 0, 0, 0, 1, 0, 0, 1],
                [0, 1, 0, 0, 0, 1, 1, 1, 0, 0],
                [1, 0, 0, 1, 1, 1, 1, 1, 0, 0]])

In [58]: y = np.random.randint(0, 2, n)  2
         y[:4]  2
Out[58]: array([0, 1, 0, 0])

In [59]: 2 ** f  3
Out[59]: 1024
1

Características

2

Datos de las etiquetas

3

Número de patrones

Para proceder, los datos brutos se introducen en un objeto pandas DataFrame , lo que simplifica determinadas operaciones y análisis:

In [60]: fcols = [f'f{_}' for _ in range(f)]  1
         fcols  1
Out[60]: ['f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9']

In [61]: data = pd.DataFrame(x, columns=fcols)  2
         data['l'] = y  3

In [62]: data.info()  4
         <class 'pandas.core.frame.DataFrame'>
         RangeIndex: 250 entries, 0 to 249
         Data columns (total 11 columns):
          #   Column  Non-Null Count  Dtype
         ---  ------  --------------  -----
          0   f0      250 non-null    int64
          1   f1      250 non-null    int64
          2   f2      250 non-null    int64
          3   f3      250 non-null    int64
          4   f4      250 non-null    int64
          5   f5      250 non-null    int64
          6   f6      250 non-null    int64
          7   f7      250 non-null    int64
          8   f8      250 non-null    int64
          9   f9      250 non-null    int64
          10  l       250 non-null    int64
         dtypes: int64(11)
         memory usage: 21.6 KB
1

Define los nombres de las columnas para los datos de las características

2

Introduce los datos de las características en un objeto DataFrame

3

Coloca los datos de las etiquetas en el mismo objeto DataFrame

4

Muestra la metainformación del conjunto de datos

Los resultados de la ejecución del siguiente código Python permiten identificar dos problemas principales. En primer lugar, no todos los patrones están en el conjunto de datos de la muestra. En segundo lugar, el tamaño de la muestra es demasiado pequeño por patrón observado. Incluso sin profundizar, está claroque ningún algoritmo de clasificación puede realmente aprender sobre todos los patrones posibles deforma significativa:

In [63]: grouped = data.groupby(list(data.columns))  1

In [64]: freq = grouped['l'].size().unstack(fill_value=0)  2

In [65]: freq['sum'] = freq[0] + freq[1]  3

In [66]: freq.head(10)  4
Out[66]: l                              0  1  sum
         f0 f1 f2 f3 f4 f5 f6 f7 f8 f9
         0  0  0  0  0  0  0  1  1  1   0  1    1
                           1  0  1  0   1  1    2
                                    1   0  1    1
                        1  0  0  0  0   1  0    1
                                    1   0  1    1
                              1  1  1   0  1    1
                           1  0  0  0   0  1    1
                                 1  0   0  1    1
                     1  0  0  0  1  1   1  0    1
                           1  1  0  0   1  0    1

In [67]: freq['sum'].describe().astype(int)  5
Out[67]: count    227
         mean       1
         std        0
         min        1
         25%        1
         50%        1
         75%        1
         max        2
         Name: sum, dtype: int64
1

Agrupa los datos en todas las columnas

2

Desapila los datos agrupados de la columna etiquetas

3

Suma la frecuencia de un 0 y un 1

4

Muestra las frecuencias de un 0 y un 1 dado un determinado patrón

5

Proporciona estadísticas para la suma de las frecuencias

El siguiente código Python utiliza el modelo MLPClassifier de scikit-learn.8 El modelo se entrena con todo el conjunto de datos. ¿Qué hay de la capacidadde una red neuronalpara aprender sobre las relaciones dentro de un conjunto de datos determinado? La capacidad es bastante alta, como muestra la puntuación de precisión en la muestra. De hecho, se acerca al 100%, un resultado impulsado en gran medida por la capacidad relativamente alta de la red neuronal dado elconjunto de datos relativamente pequeño:

In [68]: from sklearn.neural_network import MLPClassifier
         from sklearn.metrics import accuracy_score

In [69]: model = MLPClassifier(hidden_layer_sizes=[128, 128, 128],
                               max_iter=1000, random_state=100)

In [70]: model.fit(data[fcols], data['l'])
Out[70]: MLPClassifier(hidden_layer_sizes=[128, 128, 128], max_iter=1000,
                       random_state=100)

In [71]: accuracy_score(data['l'], model.predict(data[fcols]))
Out[71]: 0.952

Pero, ¿qué ocurre con el poder predictivo de una red neuronal entrenada? Para ello, el conjunto de datos dado puede dividirse en un subconjunto de datos de entrenamiento y otro de prueba. El modelo se entrena sólo en el subconjunto de datos de entrenamiento y luego se comprueba su poder predictivo en el conjunto de datos de prueba. Como antes, la precisión de la red neuronal entrenada es bastante alta en la muestra (es decir, en el conjunto de datos de entrenamiento). Sin embargo, es más de 10 puntos porcentuales peor que un algoritmo de referencia desinformado en el conjunto de datos de prueba:

In [72]: split = int(len(data) * 0.7)  1

In [73]: train = data[:split]  1
         test = data[split:]  1

In [74]: model.fit(train[fcols], train['l'])  2
Out[74]: MLPClassifier(hidden_layer_sizes=[128, 128, 128], max_iter=1000,
                       random_state=100)

In [75]: accuracy_score(train['l'], model.predict(train[fcols]))  3
Out[75]: 0.9714285714285714

In [76]: accuracy_score(test['l'], model.predict(test[fcols]))  4
Out[76]: 0.38666666666666666
1

Divide los datos en subconjuntos de datos train y test

2

Entrena el modelo sólo con el conjunto de datos de entrenamiento

3

Informa de la precisión en la muestra (conjunto de datos de entrenamiento)

4

Informa de la precisión fuera de la muestra (conjunto de datos de prueba)

A grandes rasgos, la red neuronal, entrenada sólo con un pequeño conjunto de datos, aprende relaciones erróneas debido a las dos principales áreas problemáticas identificadas. Los problemas no son realmente relevantes en el contexto del aprendizaje de relaciones en la muestra. Al contrario, cuanto más pequeño es un conjunto de datos, más fácilmente se pueden aprender las relaciones en la muestra en general. Sin embargo, las áreas problemáticas son muy relevantes cuando se utiliza la red neuronal entrenada para generar predicciones fuera de la muestra.

Conjunto de datos más amplio

Afortunadamente, a menudo hay una salida clara a esta situación problemática: un conjunto de datos mayor. Enfrentados a problemas del mundo real, esta idea teórica podría ser igualmente correcta. Sin embargo, desde un punto de vista práctico, esos conjuntos de datos más grandes no siempre están disponibles, ni a menudo pueden generarse tan fácilmente. Sin embargo, en el contexto del ejemplo de esta sección, sí que es fácil crear un conjunto de datos mayor.

El siguiente código Python aumenta significativamente el número de muestras del conjunto de datos de muestra inicial. El resultado es que la precisión de la predicción de lared neuronal entrenada aumenta en más de 10 puntos porcentuales, hasta un nivel de aproximadamente el 50%, lo que era de esperar dada la naturaleza de los datos de las etiquetas. Ahora está en línea con un algoritmo de referencia no informado:

In [77]: factor = 50

In [78]: big = pd.DataFrame(np.random.randint(0, 2, (factor * n, f)),
                            columns=fcols)

In [79]: big['l'] = np.random.randint(0, 2, factor * n)

In [80]: train = big[:split]
         test = big[split:]

In [81]: model.fit(train[fcols], train['l'])
Out[81]: MLPClassifier(hidden_layer_sizes=[128, 128, 128], max_iter=1000,
                       random_state=100)

In [82]: accuracy_score(train['l'], model.predict(train[fcols]))  1
Out[82]: 0.9657142857142857

In [83]: accuracy_score(test['l'], model.predict(test[fcols]))  2
Out[83]: 0.5043407707910751
1

Precisión de la predicción en la muestra (conjunto de datos de entrenamiento)

2

Precisión de predicción fuera de muestra (conjunto de datos de prueba)

Un rápido análisis de los datos disponibles, como se muestra a continuación, explica el aumento de la precisión de la predicción. En primer lugar, ahora todos los patrones posibles están representados en el conjunto de datos. En segundo lugar, todos los patrones tienen una frecuencia media superior a 10 en el conjunto de datos. En otras palabras, la red neuronal ve básicamente todos los patrones varias veces. Esto permite a la red neuronal "aprender" que tanto las etiquetas 0 como 1 son igualmente probables para todos los patrones posibles. Por supuesto, es una forma bastante complicada de aprender esto, pero es una buena ilustración del hecho de que un conjunto de datos relativamente pequeño puede ser a menudo demasiado pequeño en el contexto de las redes neuronales:

In [84]: grouped = big.groupby(list(data.columns))

In [85]: freq = grouped['l'].size().unstack(fill_value=0)

In [86]: freq['sum'] = freq[0] + freq[1]  1

In [87]: freq.head(6)
Out[87]: l                               0  1  sum
         f0 f1 f2 f3 f4 f5 f6 f7 f8 f9
         0  0  0  0  0  0  0  0  0  0   10  9   19
                                    1    5  4    9
                                 1  0    2  5    7
                                    1    6  6   12
                              1  0  0    9  8   17
                                    1    7  4   11

In [88]: freq['sum'].describe().astype(int)  2
Out[88]: count    1024
         mean       12
         std         3
         min         2
         25%        10
         50%        12
         75%        15
         max        26
         Name: sum, dtype: int64
1

Añade la frecuencia de los valores 0 y 1

2

Muestra estadísticas resumidas de los valores de la suma

Volumen y variedad

En el contexto de las redes neuronales que realizan tareas de predicción, el volumen y la variedad de los datos disponibles utilizados para entrenar la red neuronal son decisivos para su rendimiento de predicción. Los ejemplos numéricos e hipotéticos de esta sección muestran que la misma red neuronal entrenada en un conjunto de datos relativamente pequeño y no tan variado tiene un rendimiento inferior al de su homóloga entrenada en un conjunto de datos relativamente grande y variado en más de 10 puntos porcentuales. Esta diferencia puede considerarse enorme, dado que los profesionales y las empresas de IA suelen luchar por mejoras tan pequeñas como una décima de punto porcentual.

Grandes datos

¿Cuál es la diferencia entre un gran conjunto de datos y un conjunto de big data? El término big data se utiliza desde hace más de una década para referirse a varias cosas. A efectos de este libro, se podría decir que un conjunto de grandes datos es lo suficientemente grande -en términos de volumen, variedad y quizá también de velocidad- como para que un algoritmo de IA se entrene adecuadamente de modo que el algoritmo obtenga mejores resultados en una tarea de predicción que un algoritmo de referencia.

El conjunto de datos más grande utilizado anteriormente sigue siendo pequeño en términos prácticos. Sin embargo, es lo suficientemente grande como para cumplir el objetivo especificado. El volumen y la variedad necesarios del conjunto de datos dependen principalmente de la estructura y las características de los datos de rasgos y etiquetas.

En este contexto, supongamos que un banco minorista aplica un enfoque de clasificación basado en redes neuronales para la puntuación crediticia. Dados los datos internos, el científico de datos responsable diseña 25 características categóricas, cada una de las cuales puede adoptar 8 valores diferentes. El número de patrones resultante es astronómicamente grande:

In [89]: 8 ** 25
Out[89]: 37778931862957161709568

Está claro que ningún conjunto de datos puede proporcionar a una red neuronal exposición a cada uno de estos patrones.9 Afortunadamente, en la práctica esto no es necesario para que la red neuronal aprenda sobre la solvencia basándose en datos de deudores regulares, morosos y/o rechazados. Tampoco es necesario, en general, generar "buenas" predicciones con respecto a la solvencia de cada deudor potencial.

Esto se debe a varias razones. Por nombrar sólo algunas, en primer lugar, no todos los patrones serán relevantes en la práctica: algunos patrones podrían simplemente no existir, podrían ser imposibles, etc. En segundo lugar, puede que no todas las características sean igual de importantes, lo que reduciría el número de características relevantes y, por tanto, el número de modelos posibles. En tercer lugar, un valor de 4 o 5 para la característica 7, por ejemplo, podría no suponer ninguna diferencia, lo que reduciría aún más el número de modelos relevantes.

Conclusiones

Para este libro, la inteligencia artificial, o IA, abarca métodos, técnicas, algoritmos, etc., capaces de aprender relaciones, reglas, probabilidades y más a partir de los datos. La atención se centra en los algoritmos de aprendizaje supervisado, como los de estimación y clasificación. En cuanto a los algoritmos, las redes neuronales y los enfoques de aprendizaje profundo ocupan un lugar central.

El tema central de este libro es la aplicación de las redes neuronales a uno de los problemas centrales de las finanzas: la predicción de los movimientos futuros del mercado. Más concretamente, el problema puede ser predecir la dirección del movimiento de un índice bursátil o el tipo de cambio de un par de divisas. La predicción de la dirección futura del mercado (es decir, si un nivel o precio objetivo sube o baja) es un problema que puede trasladarse fácilmente a un entorno de clasificación.

Antes de profundizar en el tema central propiamente dicho, en el siguiente capítulo se tratan primero algunos temas relacionados con lo que se denomina superinteligencia y singularidad tecnológica. Ese debate proporcionará unos antecedentes útiles para los capítulos siguientes, que se centran en las finanzas y la aplicación de la IA al ámbito financiero.

Referencias

Libros y artículos citados en este capítulo:

1 Para más detalles, véase sklearn.cluster.KMeans y VanderPlas (2017, cap. 5).

2 Para más detalles, véase VanderPlas (2017, cap. 5).

3 Para más detalles, consulta sklearn.neural_network.MLPRegressor. Para más antecedentes, véase Goodfellow et al. (2016, cap. 6).

4 Para más detalles, véase Chollet (2017, cap. 3).

5 Para más detalles sobre las funciones de activación con Keras, consulta https://keras.io/activations.

6 La función de pérdida calcula el error de predicción de la red neuronal (u otros algoritmos ML). La entropía cruzada binaria es una función de pérdida apropiada para los problemas de clasificación binaria, mientras que el error cuadrático medio (ECM ) es, por ejemplo, apropiado para los problemas de estimación. Para más detalles sobre las funciones de pérdida con Keras, consulta https://keras.io/losses.

7 Véase, por ejemplo, Kratsios (2019).

8 Para más detalles, consulta sklearn.neural_network.MLPClassifier.

9 La tecnología informática actual tampoco permitiría modelar y entrenar una red neuronal basada en un conjunto de datos de este tipo, si estuviera disponible. En este contexto, el siguiente capítulo trata de la importancia del hardware para la IA.

Get Inteligencia Artificial en Finanzas now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.