Capítulo 4. Modelos de entrenamiento

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

Hasta ahora hemos tratado los modelos de aprendizaje automático y sus algoritmos de entrenamiento principalmente como cajas negras. Si has realizado algunos de los ejercicios de los capítulos anteriores, puede que te hayas sorprendido de lo mucho que puedes hacer sin saber nada de lo que hay bajo el capó: has optimizado un sistema de regresión, has mejorado un clasificador de imágenes de dígitos e incluso has construido un clasificador de spam desde cero, todo ello sin saber cómo funcionan realmente. De hecho, en muchas situaciones no necesitas conocer los detalles de la implementación.

Sin embargo, comprender bien cómo funcionan las cosas puede ayudarte a encontrar rápidamente el modelo adecuado, el algoritmo de entrenamiento correcto y un buen conjunto de hiperparámetros para tu tarea. Comprender lo que hay bajo el capó también te ayudará a depurar problemas y a realizar análisis de errores de forma más eficaz. Por último, la mayoría de los temas tratados en este capítulo serán esenciales para comprender, construir y entrenar redes neuronales (tratadas en la Parte II de este libro).

En este capítulo empezaremos examinando el modelo de regresión lineal, uno de los modelos más sencillos que existen. Discutiremos dos formas muy distintas de entrenarlo:

  • Utilizando una ecuación de "forma cerrada1 que calcula directamente los parámetros del modelo que mejor se ajustan al conjunto de entrenamiento (es decir, los parámetros del modelo que minimizan la función de coste sobre el conjunto de entrenamiento).

  • Utilizando un enfoque de optimización iterativo llamado descenso de gradiente (DG) que ajusta gradualmente los parámetros del modelo para minimizar la función de coste sobre el conjunto de entrenamiento, convergiendo finalmente al mismo conjunto de parámetros que el primer método. Veremos algunas variantes del descenso gradiente que utilizaremos una y otra vez cuando estudiemos las redes neuronales en la Parte II: GD por lotes, GD por minilotes y GD estocástico.

A continuación veremos la regresión polinómica, un modelo más complejo que puede ajustarse a conjuntos de datos no lineales. Como este modelo tiene más parámetros que la regresión lineal, es más propenso a sobreajustar los datos de entrenamiento. Exploraremos cómo detectar si éste es o no el caso utilizando curvas de aprendizaje, y luego estudiaremos varias técnicas de regularización que pueden reducir el riesgo de sobreajuste del conjunto de entrenamiento.

Por último, examinaremos otros dos modelos que se utilizan habitualmente para tareas de clasificación: la regresión logística y la regresión softmax.

Advertencia

En este capítulo habrá bastantes ecuaciones matemáticas, en las que se utilizarán nociones básicas de álgebra lineal y cálculo. Para entender estas ecuaciones, necesitarás saber qué son los vectores y las matrices; cómo transponerlos, multiplicarlos e invertirlos; y qué son las derivadas parciales. Si no estás familiarizado con estos conceptos, repasa los tutoriales introductorios de álgebra lineal y cálculo disponibles como cuadernos Jupyter en el material complementario en línea. Para los que sean realmente alérgicos a las matemáticas, deberían seguir este capítulo y simplemente saltarse las ecuaciones; con suerte, el texto será suficiente para ayudarte a entender la mayoría de los conceptos.

Regresión lineal

En el Capítulo 1 vimos un modelo de regresión simple de la satisfacción vital:

satisfacción_vital = θ0 + θ1 × PIB_per_capita

Este modelo es sólo una función lineal de la característica de entrada GDP_per_capita. θ0 y θ1 son los parámetros del modelo.

De forma más general, un modelo lineal hace una predicción simplemente calculando una suma ponderada de las características de entrada, más una constante llamada término de sesgo (también llamado término de intercepción), como se muestra en la Ecuación 4-1.

Ecuación 4-1. Predicción del modelo de regresión lineal
y ^ = θ 0 + θ 1 x 1 + θ 2 x 2 + + θ n x n

En esta ecuación:

  • ŷ es el valor previsto.

  • n es el número de características.

  • xi es el i-ésimo valor de característica.

  • θj es el j-ésimo parámetro del modelo, que incluye el término de sesgo θ0 y las ponderaciones de las características θ1, θ2, ⋯, θn.

Esto se puede escribir de forma mucho más concisa utilizando una forma vectorizada, como se muestra en la Ecuación 4-2.

Ecuación 4-2. Predicción del modelo de regresión lineal (forma vectorizada)
y^=hθ(x)=θ-x

En esta ecuación:

  • es la función de hipótesis, utilizando los parámetros del modelo θ.

  • θ es el vector de parámetros del modelo, que contiene el término de sesgo θ0 y las ponderaciones de las características θ1 a θn.

  • x es el vector de características de la instancia, que contiene de x0 a xn, siendo x0 siempre igual a 1.

  • θ - x es el producto punto de los vectores θ y x, que es igual a θ0x0 + θ1x1 + θ2x2 + ... + θnxn.

Nota

En el aprendizaje automático, los vectores suelen representarse como vectores columna, que son matrices 2D con una sola columna. Si θ y x son vectores columna, la predicción es y^=θxdonde θ es la transposición de θ (un vector fila en lugar de un vector columna) y θx es la multiplicación matricial de θ y x. Se trata, por supuesto, de la misma predicción, salvo que ahora se representa como una matriz unicelular en lugar de como un valor escalar. En este libro utilizaré esta notación para evitar cambiar entre productos por puntos y multiplicaciones matriciales.

Bien, ése es el modelo de regresión lineal, pero ¿cómo lo entrenamos? Bien, recuerda que entrenar un modelo significa ajustar sus parámetros de modo que el modelo se ajuste lo mejor posible al conjunto de entrenamiento. Para ello, primero necesitamos una medida de lo bien (o mal) que se ajusta el modelo a los datos de entrenamiento. En el Capítulo 2 vimos que la medida de rendimiento más común de un modelo de regresión es el error cuadrático medio(Ecuación 2-1). Por tanto, para entrenar un modelo de regresión lineal, necesitamos encontrar el valor de θ que minimice el RMSE. En la práctica, es más sencillo minimizar el error cuadrático medio (ECM) que el RMSE, y conduce al mismo resultado (porque el valor que minimiza una función positiva también minimiza su raíz cuadrada).

Advertencia

Los algoritmos de aprendizaje a menudo optimizarán una función de pérdida diferente durante el entrenamiento que la medida de rendimiento utilizada para evaluar el modelo final. Esto se debe generalmente a que la función es más fácil de optimizar y/o porque tiene términos adicionales necesarios sólo durante el entrenamiento (por ejemplo, para la regularización). Una buena medida de rendimiento es lo más cercana posible al objetivo comercial final. Una buena pérdida de entrenamiento es fácil de optimizar y está fuertemente correlacionada con la métrica. Por ejemplo, los clasificadores se entrenan a menudo utilizando una función de coste como la pérdida logarítmica (como verás más adelante en este capítulo), pero se evalúan utilizando la precisión/recuperación. La pérdida logarítmica es fácil de minimizar, y al hacerlo suele mejorar la precisión/recuperación.

El MSE de una hipótesis de regresión lineal sobre un conjunto de entrenamiento X se calcula mediante la ecuación 4-3.

Ecuación 4-3. Función de coste MSE para un modelo de regresión lineal
MSE ( X , h θ ) = 1 m i=1 m (θ x (i) -y (i) ) 2

La mayoría de estas notaciones se presentaron en el Capítulo 2 (véase "Notaciones"). La única diferencia es que escribimos en lugar de sólo h para dejar claro que el modelo está parametrizado por el vector θ. Para simplificar las notaciones, escribiremos simplemente MSE) en lugar de MSE(X, ).

La ecuación normal

Para encontrar el valor de θ que minimiza el MSE, existe una solución de forma cerrada, esdecir, una ecuación matemática que da el resultado directamente. Se denomina ecuación Normal(ecuación 4-4).

Ecuación 4-4. Ecuación normal
θ ^ = (X X) -1 X y

En esta ecuación:

  • θ^ es el valor de θ que minimiza la función de coste.

  • y es el vector de valores objetivo que contiene de y(1) a y(m).

Generemos algunos datos de aspecto lineal para probar esta ecuación(Figura 4-1):

import numpy as np

np.random.seed(42)  # to make this code example reproducible
m = 100  # number of instances
X = 2 * np.random.rand(m, 1)  # column vector
y = 4 + 3 * X + np.random.randn(m, 1)  # column vector
mls3 0401
Figura 4-1. Un conjunto de datos lineal generado aleatoriamente

Ahora calculemos θ^ utilizando la ecuación Normal. Utilizaremos la función inv() del módulo de álgebra lineal de NumPy (np.linalg) para calcular la inversa de una matriz, y el método dot() para la multiplicación de matrices:

from sklearn.preprocessing import add_dummy_feature

X_b = add_dummy_feature(X)  # add x0 = 1 to each instance
theta_best = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
Nota

El operador @ realiza la multiplicación de matrices. Si A y B son matrices NumPy, entonces A @ B es equivalente a np.matmul(A, B). Muchas otras bibliotecas, como TensorFlow, PyTorch y JAX, también admiten el operador @. Sin embargo, no puedes utilizar @ en matrices Python puras (es decir, listas de listas).

La función que hemos utilizado para generar los datos es y = 4 + 3x1 + ruido gaussiano. Veamos qué ha encontrado la ecuación:

>>> theta_best
array([[4.21509616],
       [2.77011339]])

Hubiéramos esperado θ0 = 4 y θ1 = 3 en lugar de θ0 = 4,215 y θ1 = 2,770. Suficientemente cerca, pero el ruido hacía imposible recuperar los parámetros exactos de la función original. Cuanto más pequeño y ruidoso es el conjunto de datos, más difícil resulta.

Ahora podemos hacer predicciones utilizando θ^:

>>> X_new = np.array([[0], [2]])
>>> X_new_b = add_dummy_feature(X_new)  # add x0 = 1 to each instance
>>> y_predict = X_new_b @ theta_best
>>> y_predict
array([[4.21509616],
       [9.75532293]])

Vamos a trazar las predicciones de este modelo(Figura 4-2):

import matplotlib.pyplot as plt

plt.plot(X_new, y_predict, "r-", label="Predictions")
plt.plot(X, y, "b.")
[...]  # beautify the figure: add labels, axis, grid, and legend
plt.show()
mls3 0402
Figura 4-2. Predicciones del modelo de regresión lineal

Realizar una regresión lineal con Scikit-Learn es relativamente sencillo:

>>> from sklearn.linear_model import LinearRegression
>>> lin_reg = LinearRegression()
>>> lin_reg.fit(X, y)
>>> lin_reg.intercept_, lin_reg.coef_
(array([4.21509616]), array([[2.77011339]]))
>>> lin_reg.predict(X_new)
array([[4.21509616],
       [9.75532293]])

Observa que Scikit-Learn separa el término de sesgo (intercept_) de los pesos de las características (coef_). La clase LinearRegression se basa en la función scipy.linalg.lstsq() (el nombre significa "mínimos cuadrados"), que podrías llamar directamente:

>>> theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
>>> theta_best_svd
array([[4.21509616],
       [2.77011339]])

Esta función calcula θ^=X+ydonde X+ es el pseudoinverso de X (concretamente, el inverso de Moore-Penrose ). Puedes utilizar np.linalg.pinv() para calcular directamente el pseudoinverso:

>>> np.linalg.pinv(X_b) @ y
array([[4.21509616],
       [2.77011339]])

El pseudoinverso propiamente dicho se calcula mediante una técnica estándar de factorización de matrices denominada descomposición de valores singulares (SVD), que puede descomponer la matriz X del conjunto de entrenamiento en la multiplicación de tres matrices U Σ V⊺ (véase numpy.linalg.svd()). El pseudoinverso se calcula como X+=VΣ+U. Para calcular la matriz Σ+, el algoritmo toma Σ y pone a cero todos los valores menores que un valor umbral minúsculo, luego sustituye todos los valores distintos de cero por su inversa y, por último, transpone la matriz resultante. Este planteamiento es más eficaz que calcular la ecuación Normal, y además maneja muy bien los casos de perímetro: de hecho, la ecuación Normal puede no funcionar si la matriz X⊺X no es invertible (es decir, singular), como si m < n o si algunas características son redundantes, pero el pseudoinverso siempre está definido.

Complejidad computacional

La ecuación Normal calcula la inversa de X⊺ X, que es una matriz(n + 1) ×(n + 1) (donde n es el número de características). La complejidad computacional de invertir una matriz de este tipo suele ser de O(n2,4) a O(n3), dependiendo de la implementación. En otras palabras, si duplicas el número de características, multiplicas el tiempo de cálculo por aproximadamente22,4 = 5,3 a23 = 8.

El enfoque SVD utilizado por la clase LinearRegression de Scikit-Learn es aproximadamente O(n2). Si duplicas el número de características, multiplicas el tiempo de cálculo por aproximadamente 4.

Advertencia

Tanto la ecuación Normal como el enfoque SVD se vuelven muy lentos cuando el número de características aumenta (por ejemplo, 100.000). Por el lado positivo, ambos son lineales con respecto al número de instancias del conjunto de entrenamiento (son O(m)), por lo que manejan conjuntos de entrenamiento grandes de forma eficiente, siempre que quepan en la memoria.

Además, una vez que has entrenado tu modelo de regresión lineal (utilizando la ecuación Normal o cualquier otro algoritmo), las predicciones son muy rápidas: la complejidad computacional es lineal con respecto tanto al número de instancias sobre las que quieres hacer predicciones como al número de características. En otras palabras, hacer predicciones sobre el doble de instancias (o el doble de características) llevará aproximadamente el doble de tiempo.

Ahora veremos una forma muy distinta de entrenar un modelo de regresión lineal, que se adapta mejor a los casos en que hay un gran número de características o demasiadas instancias de entrenamiento para que quepan en la memoria.

Descenso Gradiente

El descenso de gradiente es un algoritmo de optimización genérico capaz de encontrar soluciones óptimas a una amplia gama de problemas. La idea general del descenso gradiente es ajustar los parámetros de forma iterativa para minimizar una función de coste.

Supón que estás perdido en las montañas en medio de una densa niebla, y sólo puedes sentir la pendiente del suelo bajo tus pies. Una buena estrategia para llegar rápidamente al fondo del valle es ir cuesta abajo en la dirección de la pendiente más pronunciada. Esto es exactamente lo que hace el descenso por gradiente: mide el gradiente local de la función de error con respecto al vector de parámetros θ, y va en la dirección del gradiente descendente. Una vez que el gradiente es cero, ¡has alcanzado un mínimo!

En la práctica, empiezas llenando θ con valores aleatorios (esto se llama inicialización aleatoria). Luego lo mejoras gradualmente, dando un pasito a la vez, cada paso intentando disminuir la función de coste (por ejemplo, el MSE), hasta que el algoritmo converja a un mínimo (ver Figura 4-3).

mls3 0403
Figura 4-3. En esta representación del descenso gradiente, los parámetros del modelo se inicializan aleatoriamente y se ajustan repetidamente para minimizar la función de coste; el tamaño del paso de aprendizaje es proporcional a la pendiente de la función de coste, de modo que los pasos se hacen gradualmente más pequeños a medida que el coste se acerca al mínimo

Un parámetro importante en el descenso gradiente es el tamaño de los pasos, determinado por el hiperparámetro tasa de aprendizaje. Si la tasa de aprendizaje es demasiado pequeña, el algoritmo tendrá que pasar por muchas iteraciones para converger, lo que llevará mucho tiempo (ver Figura 4-4).

mls3 0404
Figura 4-4. Tasa de aprendizaje demasiado pequeña

Por otra parte, si la tasa de aprendizaje es demasiado alta, podrías saltar por el valle y acabar al otro lado, posiblemente incluso más arriba de lo que estabas antes. Esto podría hacer que el algoritmo divergiera, con valores cada vez mayores, sin encontrar una buena solución (ver Figura 4-5).

mls3 0405
Figura 4-5. Tasa de aprendizaje demasiado alta

Además, no todas las funciones de coste parecen cuencos bonitos y regulares. Puede haber agujeros, crestas, mesetas y todo tipo de terreno irregular, lo que dificulta la convergencia al mínimo. La Figura 4-6 muestra los dos retos principales del descenso gradiente. Si la inicialización aleatoria inicia el algoritmo por la izquierda, entonces convergerá a un mínimo local, que no es tan bueno como el mínimo global. Si empieza por la derecha, tardará mucho tiempo en cruzar la meseta. Y si se detiene demasiado pronto, nunca alcanzará el mínimo global.

mls3 0406
Figura 4-6. Escollos del descenso gradiente

Afortunadamente, la función de coste MSE para un modelo de regresión lineal resulta ser una función convexa, lo que significa que si eliges dos puntos cualesquiera de la curva, el segmento de línea que los une nunca está por debajo de la curva. Esto implica que no hay mínimos locales, sólo un mínimo global. También es una función continua con una pendiente que nunca cambia bruscamente.2 Estos dos hechos tienen una gran consecuencia: se garantiza que el descenso gradiente se aproxime arbitrariamente al mínimo global (si esperas lo suficiente y si la tasa de aprendizaje no es demasiado alta).

Aunque la función de coste tiene la forma de un cuenco, puede ser un cuenco alargado si las características tienen escalas muy diferentes. La Figura 4-7 muestra el descenso de gradiente en un conjunto de entrenamiento en el que las características 1 y 2 tienen la misma escala (a la izquierda), y en un conjunto de entrenamiento en el que la característica 1 tiene valores mucho menores que la característica 2 (a la derecha).3

mls3 0407
Figura 4-7. Descenso gradiente con (izquierda) y sin (derecha) escalado de rasgos

Como puedes ver, a la izquierda el algoritmo de descenso por gradiente va directamente hacia el mínimo, con lo que lo alcanza rápidamente, mientras que a la derecha va primero en una dirección casi ortogonal a la dirección del mínimo global, y termina con una larga marcha por un valle casi llano. Al final llegará al mínimo, pero tardará mucho tiempo.

Advertencia

Cuando utilices el descenso de gradiente, debes asegurarte de que todas las características tienen una escala similar (por ejemplo, utilizando la clase StandardScaler de Scikit-Learn), o de lo contrario tardará mucho más en converger.

Este diagrama también ilustra el hecho de que entrenar un modelo significa buscar una combinación de parámetros del modelo que minimice una función de coste (sobre el conjunto de entrenamiento). Se trata de una búsqueda en el espacio de parámetros del modelo. Cuantos más parámetros tenga un modelo, más dimensiones tendrá este espacio, y más difícil será la búsqueda: buscar una aguja en un pajar de 300 dimensiones es mucho más complicado que en 3 dimensiones. Afortunadamente, como la función de coste es convexa en el caso de la regresión lineal, la aguja está simplemente en el fondo del pajar.

Descenso gradual por lotes

Para aplicar el descenso de gradiente, necesitas calcular el gradiente de la función de coste con respecto a cada parámetro θj del modelo. En otras palabras, tienes que calcular cuánto cambiará la función de coste si cambias θj sólo un poco. Esto se llama derivada parcial. Es como preguntar: "¿Cuál es la pendiente de la montaña bajo mis pies si miro hacia el este?" y luego hacer la misma pregunta mirando hacia el norte (y así para todas las demás dimensiones, si puedes imaginar un universo con más de tres dimensiones). La ecuación 4-5 calcula la derivada parcial del MSE con respecto al parámetro θj, que se anota ∂ MSE) / ∂θj.

Ecuación 4-5. Derivadas parciales de la función de coste
θ j MSE ( θ ) = 2 m i=1 m ( θ x (i) - y (i) ) x j (i)

En lugar de calcular estas derivadas parciales individualmente, puedes utilizar la ecuación 4-6 para calcularlas todas de una vez. El vector gradiente, denominado ∇θMSE), contiene todas las derivadas parciales de la función coste (una por cada parámetro del modelo).

Ecuación 4-6. Vector gradiente de la función de coste
θ MSE ( θ ) = θ 0 MSE ( θ ) θ 1 MSE ( θ ) θ n MSE ( θ ) = 2 m X ( X θ - y )
Advertencia

Observa que esta fórmula implica cálculos sobre todo el conjunto de entrenamiento X, ¡en cada paso del descenso gradiente! Por eso el algoritmo se llama descenso de gradiente por lotes: utiliza todo el lote de datos de entrenamiento en cada paso (en realidad, descenso de gradiente completo sería probablemente un nombre mejor). Como resultado, es terriblemente lento en conjuntos de entrenamiento muy grandes (en breve veremos algunos algoritmos de descenso de gradiente mucho más rápidos). Sin embargo, el descenso de gradiente se escala bien con el número de características; entrenar un modelo de regresión lineal cuando hay cientos de miles de características es mucho más rápido utilizando el descenso de gradiente que utilizando la ecuación Normal o la descomposición SVD.

Una vez que tienes el vector gradiente, que apunta cuesta arriba, basta con ir en sentido contrario para ir cuesta abajo. Esto significa restar ∇θMSE) de θ. Aquí es donde entra en juego la tasa de aprendizaje η.4 multiplica el vector gradiente por η para determinar el tamaño del paso cuesta abajo(ecuación 4-7).

Ecuación 4-7. Paso de descenso gradiente
θ(siguiente paso)=θ-ηθMSE(θ)

Veamos una aplicación rápida de este algoritmo:

eta = 0.1  # learning rate
n_epochs = 1000
m = len(X_b)  # number of instances

np.random.seed(42)
theta = np.random.randn(2, 1)  # randomly initialized model parameters

for epoch in range(n_epochs):
    gradients = 2 / m * X_b.T @ (X_b @ theta - y)
    theta = theta - eta * gradients

¡No ha sido tan difícil! Cada iteración sobre el conjunto de entrenamiento se denomina época. Veamos el resultado theta:

>>> theta
array([[4.21509616],
       [2.77011339]])

Oye, ¡eso es exactamente lo que encontró la ecuación Normal! El descenso gradiente funcionó perfectamente. Pero, ¿y si hubieras utilizado una tasa de aprendizaje diferente (eta)? La Figura 4-8 muestra los 20 primeros pasos del descenso gradiente utilizando tres velocidades de aprendizaje diferentes. La línea de la parte inferior de cada gráfico representa el punto de partida aleatorio, luego cada época se representa con una línea cada vez más oscura.

mls3 0408
Figura 4-8. Descenso gradual con distintos ritmos de aprendizaje

A la izquierda, la tasa de aprendizaje es demasiado baja: el algoritmo acabará llegando a la solución, pero tardará mucho tiempo. En el medio, la tasa de aprendizaje parece bastante buena: en pocas épocas, ya ha convergido a la solución. A la derecha, la tasa de aprendizaje es demasiado alta: el algoritmo diverge, saltando por todas partes y alejándose cada vez más de la solución a cada paso.

Para encontrar una buena tasa de aprendizaje, puedes utilizar la búsqueda en cuadrícula (ver Capítulo 2). Sin embargo, tal vez quieras limitar el número de épocas para que la búsqueda en cuadrícula pueda eliminar los modelos que tardan demasiado en converger.

Puede que te preguntes cómo establecer el número de épocas. Si es demasiado bajo, aún estarás lejos de la solución óptima cuando el algoritmo se detenga; pero si es demasiado alto, perderás tiempo mientras los parámetros del modelo ya no cambian. Una solución sencilla es fijar un número de épocas muy grande, pero interrumpir el algoritmo cuando el vector gradiente se vuelva diminuto -es decir, cuando su norma sea menor que un número minúsculo ϵ (llamado tolerancia)-, porque esto ocurre cuando el descenso gradiente ha alcanzado (casi) el mínimo.

Descenso Gradiente Estocástico

El principal problema del descenso de gradiente por lotes es que utiliza todo el conjunto de entrenamiento para calcular los gradientes en cada paso, lo que lo hace muy lento cuando el conjunto de entrenamiento es grande. En el extremo opuesto, el descenso de gradiente estocástico elige una instancia aleatoria del conjunto de entrenamiento en cada paso y calcula los gradientes basándose sólo en esa única instancia. Obviamente, trabajar con un único caso cada vez hace que el algoritmo sea mucho más rápido, porque tiene muy pocos datos que manipular en cada iteración. También permite entrenar conjuntos de entrenamiento enormes, ya que sólo es necesario que haya una instancia en memoria en cada iteración (la DG estocástica puede implementarse como un algoritmo fuera del núcleo; véase el Capítulo 1).

Por otra parte, debido a su naturaleza estocástica (es decir, aleatoria), este algoritmo es mucho menos regular que el descenso gradiente por lotes: en lugar de disminuir suavemente hasta alcanzar el mínimo, la función de coste rebotará hacia arriba y hacia abajo, disminuyendo sólo de media. Con el tiempo acabará muy cerca del mínimo, pero una vez allí seguirá rebotando, sin estabilizarse nunca (ver Figura 4-9). Cuando el algoritmo se detenga, los valores finales de los parámetros serán buenos, pero no óptimos.

mls3 0409
Figura 4-9. Con el descenso de gradiente estocástico, cada paso de entrenamiento es mucho más rápido, pero también mucho más estocástico que cuando se utiliza el descenso de gradiente por lotes

Cuando la función de coste es muy irregular (como en la Figura 4-6), esto puede ayudar al algoritmo a salir de los mínimos locales, por lo que el descenso de gradiente estocástico tiene más posibilidades de encontrar el mínimo global que el descenso de gradiente por lotes.

Por tanto, la aleatoriedad es buena para escapar de los óptimos locales, pero mala porque significa que el algoritmo nunca puede establecerse en el mínimo. Una solución a este dilema es reducir gradualmente el ritmo de aprendizaje. Los pasos empiezan siendo grandes (lo que ayuda a progresar rápidamente y escapar de los mínimos locales), y luego se van haciendo cada vez más pequeños, lo que permite al algoritmo asentarse en el mínimo global. Este proceso es similar al recocido simulado, un algoritmo inspirado en el proceso metalúrgico del recocido, en el que el metal fundido se enfría lentamente. La función que determina la tasa de aprendizaje en cada iteración se llama programa de aprendizaje. Si la tasa de aprendizaje se reduce demasiado deprisa, puedes quedarte atascado en un mínimo local, o incluso acabar congelado a medio camino del mínimo. Si la tasa de aprendizaje se reduce demasiado lentamente, puedes saltar alrededor del mínimo durante mucho tiempo y acabar con una solución subóptima si detienes el entrenamiento demasiado pronto.

Este código implementa el descenso de gradiente estocástico utilizando un programa de aprendizaje sencillo:

n_epochs = 50
t0, t1 = 5, 50  # learning schedule hyperparameters

def learning_schedule(t):
    return t0 / (t + t1)

np.random.seed(42)
theta = np.random.randn(2, 1)  # random initialization

for epoch in range(n_epochs):
    for iteration in range(m):
        random_index = np.random.randint(m)
        xi = X_b[random_index : random_index + 1]
        yi = y[random_index : random_index + 1]
        gradients = 2 * xi.T @ (xi @ theta - yi)  # for SGD, do not divide by m
        eta = learning_schedule(epoch * m + iteration)
        theta = theta - eta * gradients

Por convención, iteramos por rondas de m iteraciones; cada ronda se denomina época, como antes. Mientras que el código de descenso de gradiente por lotes itera 1.000 veces por todo el conjunto de entrenamiento, este código recorre el conjunto de entrenamiento sólo 50 veces y alcanza una solución bastante buena:

>>> theta
array([[4.21076011],
       [2.74856079]])

La Figura 4-10 muestra los 20 primeros pasos del entrenamiento (fíjate en lo irregulares que son los pasos).

Ten en cuenta que, como las instancias se eligen aleatoriamente, algunas pueden elegirse varias veces por época, mientras que otras pueden no elegirse en absoluto. Si quieres asegurarte de que el algoritmo pasa por todas las instancias en cada epoch, otro método es barajar el conjunto de entrenamiento (asegurándote de barajar las características de entrada y las etiquetas conjuntamente), luego pasarlo instancia por instancia, luego barajarlo de nuevo, y así sucesivamente. Sin embargo, este enfoque es más complejo y, por lo general, no mejora el resultado.

mls3 0410
Figura 4-10. Los 20 primeros pasos del descenso de gradiente estocástico
Advertencia

Cuando se utiliza el descenso de gradiente estocástico, las instancias de entrenamiento deben ser independientes e idénticamente distribuidas (IID) para garantizar que los parámetros se dirijan hacia el óptimo global, por término medio. Una forma sencilla de garantizarlo es barajar las instancias durante el entrenamiento (por ejemplo, elegir cada instancia aleatoriamente, o barajar el conjunto de entrenamiento al principio de cada época). Si no barajas las instancias -por ejemplo, si las instancias están ordenadas por etiquetas-, el SGD empezará optimizando para una etiqueta, luego para la siguiente, y así sucesivamente, y no se asentará cerca del mínimo global.

Para realizar una regresión lineal utilizando GD estocástico con Scikit-Learn, puedes utilizar la clase SGDRegressor, que por defecto optimiza la función de coste MSE. El siguiente código se ejecuta durante un máximo de 1.000 épocas (max_iter) o hasta que la pérdida sea inferior a 10-5 (tol) durante 100 épocas (n_iter_no_change). Comienza con una tasa de aprendizaje de 0,01 (eta0), utilizando el programa de aprendizaje por defecto (diferente del que hemos utilizado nosotros). Por último, no utiliza ninguna regularización (penalty=None; más detalles al respecto en breve):

from sklearn.linear_model import SGDRegressor

sgd_reg = SGDRegressor(max_iter=1000, tol=1e-5, penalty=None, eta0=0.01,
                       n_iter_no_change=100, random_state=42)
sgd_reg.fit(X, y.ravel())  # y.ravel() because fit() expects 1D targets

Una vez más, encuentras una solución bastante próxima a la que devuelve la ecuación Normal:

>>> sgd_reg.intercept_, sgd_reg.coef_
(array([4.21278812]), array([2.77270267]))
Consejo

Todos los estimadores de Scikit-Learn pueden entrenarse utilizando el método fit(), pero algunos estimadores también tienen un método partial_fit() al que puedes llamar para ejecutar una única ronda de entrenamiento en una o más instancias (ignora hiperparámetros como max_iter o tol). Si llamas repetidamente a partial_fit(), el modelo se entrenará gradualmente. Esto es útil cuando necesitas más control sobre el proceso de entrenamiento. Otros modelos tienen en su lugar un hiperparámetro warm_start (y algunos tienen ambos): si estableces warm_start=True, llamar al método fit() en un modelo entrenado no reiniciará el modelo; simplemente continuará el entrenamiento donde lo dejó, respetando hiperparámetros como max_iter y tol. Ten en cuenta que fit() reinicia el contador de iteraciones utilizado por el programa de aprendizaje, mientras que partial_fit() no lo hace.

Mini-lote de descenso gradiente

El último algoritmo de descenso de gradiente que vamos a estudiar se llama descenso de gradiente por minilotes. Es sencillo una vez que conoces el descenso de gradiente por lotes y estocástico: en cada paso, en lugar de calcular los gradientes basándose en el conjunto de entrenamiento completo (como en la DG por lotes) o basándose en una sola instancia (como en la DG estocástica), la DG por minilotes calcula los gradientes en pequeños conjuntos aleatorios de instancias llamados minilotes. La principal ventaja de la GD minilotes sobre la GD estocástica es que puedes obtener un aumento de rendimiento gracias a la optimización por hardware de las operaciones matriciales, especialmente cuando se utilizan GPU.

El progreso del algoritmo en el espacio de parámetros es menos errático que con la DG estocástica, especialmente con minilotes bastante grandes. Como resultado, la DG de minilotes acabará caminando un poco más cerca del mínimo que la DG estocástica, pero puede resultarle más difícil escapar de los mínimos locales (en el caso de los problemas que sufren mínimos locales, a diferencia de la regresión lineal con la función de coste MSE). La Figura 4-11 muestra las trayectorias que siguen los tres algoritmos de descenso gradiente en el espacio de parámetros durante el entrenamiento. Todos acaban cerca del mínimo, pero el camino de la DG por lotes se detiene realmente en el mínimo, mientras que tanto la DG estocástica como la mini DG por lotes siguen dando vueltas. Sin embargo, no olvides que el GD por lotes tarda mucho tiempo en dar cada paso, y que el GD estocástico y el GD minilotes también alcanzarían el mínimo si utilizaras un buen programa de aprendizaje.

mls3 0411
Figura 4-11. Trayectorias de descenso gradiente en el espacio de parámetros

La Tabla 4-1 compara los algoritmos que hemos discutido hasta ahora para la regresión lineal5 (recuerda que m es el número de instancias de entrenamiento y n es el número de características).

Tabla 4-1. Comparación de algoritmos para la regresión lineal
Algoritmo Gran m Soporte fuera del núcleo Grande n Hiperparámetros Escalado necesario Scikit-Learn

Ecuación normal

Rápido

No

Lento

0

No

N/A

SVD

Rápido

No

Lento

0

No

LinearRegression

Lote GD

Lento

No

Rápido

2

N/A

DG Estocástica

Rápido

Rápido

≥2

SGDRegressor

Minilotes GD

Rápido

Rápido

≥2

N/A

No hay casi ninguna diferencia después del entrenamiento: todos estos algoritmos acaban con modelos muy parecidos y hacen predicciones exactamente de la misma manera.

Regresión polinómica

¿Y si tus datos son más complejos que una línea recta? Sorprendentemente, puedes utilizar un modelo lineal para ajustar datos no lineales. Una forma sencilla de hacerlo es añadir potencias de cada característica como nuevas características y, a continuación, entrenar un modelo lineal con este conjunto ampliado de características. Esta técnica se denomina regresión polinómica.

Veamos un ejemplo. En primer lugar, generaremos algunos datos no lineales (véase la Figura 4-12), basados en una ecuación cuadráticasimple -esdecir, una ecuación de la forma y = ax²+ bx + c- másalgo de ruido:

np.random.seed(42)
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X ** 2 + X + 2 + np.random.randn(m, 1)
mls3 0412
Figura 4-12. Conjunto de datos generado no lineal y ruidoso

Está claro que una línea recta nunca se ajustará correctamente a estos datos. Así que vamos a utilizar la clase PolynomialFeatures de Scikit-Learn para transformar nuestros datos de entrenamiento, añadiendo el cuadrado (polinomio de segundo grado) de cada característica del conjunto de entrenamiento como una nueva característica (en este caso sólo hay una característica):

>>> from sklearn.preprocessing import PolynomialFeatures
>>> poly_features = PolynomialFeatures(degree=2, include_bias=False)
>>> X_poly = poly_features.fit_transform(X)
>>> X[0]
array([-0.75275929])
>>> X_poly[0]
array([-0.75275929,  0.56664654])

X_poly contiene ahora la característica original de X más el cuadrado de esta característica. Ahora podemos ajustar un modelo LinearRegression a estos datos de entrenamiento ampliados(Figura 4-13):

>>> lin_reg = LinearRegression()
>>> lin_reg.fit(X_poly, y)
>>> lin_reg.intercept_, lin_reg.coef_
(array([1.78134581]), array([[0.93366893, 0.56456263]]))
mls3 0413
Figura 4-13. Predicciones del modelo de regresión polinómica

No está mal: el modelo estima y ^ = 0.56 x 1 2 + 0.93 x 1 + 1.78 cuando en realidad la función original era y = 0.5 x 1 2 + 1.0 x 1 + 2.0 + Ruido gaussiano .

Observa que cuando hay múltiples características, la regresión polinómica es capaz de encontrar relaciones entre características, algo que no puede hacer un modelo de regresión lineal simple. Esto es posible gracias a que PolynomialFeatures también suma todas las combinaciones de características hasta el grado dado. Por ejemplo, si hubiera dos características a y b, PolynomialFeatures con degree=3 no sólo sumaría las características a2, a3, b2 y b3, sino también las combinaciones ab, a2b y ab2.

Advertencia

PolynomialFeatures(degree=d) ¡transforma una matriz que contiene n características en una matriz que contiene(n + d)! / d!n! características, donde n! es el factorial de n, igual a 1 × 2 × 3 × ⋯ × n. ¡Cuidado con la explosión combinatoria del número de características!

Curvas de aprendizaje

Si realizas una regresión polinómica de alto grado, probablemente ajustarás los datos de entrenamiento mucho mejor que con una regresión lineal pura. Por ejemplo, la Figura 4-14 aplica un modelo polinómico de 300 grados a los datos de entrenamiento anteriores, y compara el resultado con un modelo lineal puro y un modelo cuadrático (polinómico de segundo grado). Observa cómo el modelo polinómico de 300 grados se contonea para acercarse lo más posible a las instancias de entrenamiento.

mls3 0414
Figura 4-14. Regresión polinómica de alto grado

Este modelo de regresión polinómica de alto grado está sobreajustando gravemente los datos de entrenamiento, mientras que el modelo lineal los está infraajustando. El modelo que mejor generalizará en este caso es el modelo cuadrático, lo que tiene sentido porque los datos se generaron utilizando un modelo cuadrático. Pero en general no sabrás qué función generó los datos, así que ¿cómo puedes decidir lo complejo que debe ser tu modelo? ¿Cómo puedes saber si tu modelo se ajusta demasiado o demasiado poco a los datos?

En el Capítulo 2 utilizaste la validación cruzada para obtener una estimación del rendimiento de generalización de un modelo. Si un modelo funciona bien en los datos de entrenamiento, pero generaliza mal según las métricas de validación cruzada, entonces tu modelo está sobreajustado. Si su rendimiento es bajo en ambos casos, entonces está infraadaptado. Ésta es una forma de saber si un modelo es demasiado simple o demasiado complejo.

Otra forma de saberlo es mirar las curvas de aprendizaje, que son gráficos del error de entrenamiento y del error de validación del modelo en función de la iteración de entrenamiento: sólo tienes que evaluar el modelo a intervalos regulares durante el entrenamiento, tanto en el conjunto de entrenamiento como en el conjunto de validación, y trazar los resultados. Si el modelo no puede entrenarse de forma incremental (es decir, si no admite partial_fit() o warm_start), deberás entrenarlo varias veces en subconjuntos gradualmente mayores del conjunto de entrenamiento.

Scikit-Learn dispone de una útil función learning_curve() para ayudarte con esto: entrena y evalúa el modelo utilizando la validación cruzada. Por defecto, vuelve a entrenar el modelo en subconjuntos crecientes del conjunto de entrenamiento, pero si el modelo admite el aprendizaje incremental, puedes establecer exploit_incremental_learning=True al llamar a learning_curve() y, en su lugar, entrenará el modelo de forma incremental. La función devuelve los tamaños del conjunto de entrenamiento con los que evaluó el modelo, y las puntuaciones de entrenamiento y validación que midió para cada tamaño y para cada pliegue de validación cruzada. Utilicemos esta función para ver las curvas de aprendizaje del modelo de regresión lineal simple (ver Figura 4-15):

from sklearn.model_selection import learning_curve

train_sizes, train_scores, valid_scores = learning_curve(
    LinearRegression(), X, y, train_sizes=np.linspace(0.01, 1.0, 40), cv=5,
    scoring="neg_root_mean_squared_error")
train_errors = -train_scores.mean(axis=1)
valid_errors = -valid_scores.mean(axis=1)

plt.plot(train_sizes, train_errors, "r-+", linewidth=2, label="train")
plt.plot(train_sizes, valid_errors, "b-", linewidth=3, label="valid")
[...]  # beautify the figure: add labels, axis, grid, and legend
plt.show()
mls3 0415
Figura 4-15. Curvas de aprendizaje

Este modelo está infraajustado. Para ver por qué, veamos primero el error de entrenamiento. Cuando sólo hay una o dos instancias en el conjunto de entrenamiento, el modelo puede ajustarse perfectamente a ellas, por eso la curva empieza en cero. Pero a medida que se añaden nuevos casos al conjunto de entrenamiento, resulta imposible que el modelo se ajuste perfectamente a los datos de entrenamiento, tanto porque los datos son ruidosos como porque no son lineales en absoluto. Así que el error en los datos de entrenamiento aumenta hasta que llega a una meseta, momento en el que añadir nuevas instancias al conjunto de entrenamiento no mejora ni empeora mucho el error medio. Veamos ahora el error de validación. Cuando el modelo se entrena con muy pocas instancias de entrenamiento, es incapaz de generalizar adecuadamente, por eso el error de validación es inicialmente bastante grande. Luego, a medida que el modelo recibe más ejemplos de entrenamiento, aprende, y por eso el error de validación disminuye lentamente. Sin embargo, una vez más, una línea recta no puede modelizar bien los datos, por lo que el error acaba en una meseta, muy cerca de la otra curva.

Estas curvas de aprendizaje son típicas de un modelo que no se ajusta bien. Ambas curvas han alcanzado una meseta; están próximas y son bastante altas.

Consejo

Si tu modelo no se ajusta a los datos de entrenamiento, añadir más ejemplos de entrenamiento no te ayudará. Tienes que utilizar un modelo mejor o idear características mejores.

Veamos ahora las curvas de aprendizaje de un modelo polinómico de 10º grado sobre los mismos datos(Figura 4-16):

from sklearn.pipeline import make_pipeline

polynomial_regression = make_pipeline(
    PolynomialFeatures(degree=10, include_bias=False),
    LinearRegression())

train_sizes, train_scores, valid_scores = learning_curve(
    polynomial_regression, X, y, train_sizes=np.linspace(0.01, 1.0, 40), cv=5,
    scoring="neg_root_mean_squared_error")
[...]  # same as earlier
mls3 0416
Figura 4-16. Curvas de aprendizaje del modelo polinómico de 10º grado

Estas curvas de aprendizaje se parecen un poco a las anteriores, pero hay dos diferencias muy importantes:

  • El error en los datos de entrenamiento es mucho menor que antes.

  • Hay un hueco entre las curvas. Esto significa que el modelo funciona significativamente mejor con los datos de entrenamiento que con los datos de validación, lo que es el sello distintivo de un modelo sobreajustado. Sin embargo, si utilizaras un conjunto de entrenamiento mucho mayor, las dos curvas seguirían acercándose.

Consejo

Una forma de mejorar un modelo sobreajustado es alimentarlo con más datos de entrenamiento hasta que el error de validación alcance el error de entrenamiento.

Modelos lineales regularizados

Como has visto en los Capítulos 1 y 2, una buena forma de reducir el sobreajuste es regularizar el modelo (es decir, restringirlo): cuantos menos grados de libertad tenga, más difícil será que sobreajuste los datos. Una forma sencilla de regularizar un modelo polinómico es reducir el número de grados polinómicos.

Para un modelo lineal, la regularización se consigue normalmente restringiendo los pesos del modelo. A continuación veremos la regresión de cresta, la regresión de lazo y la regresión de red elástica, que aplican tres formas distintas de restringir los pesos.

Regresión Ridge

La regresión Ridge (también llamada regularización de Tikhonov) es una versión regularizada de la regresión lineal: un término de regularización igual a αmi=1nθi2 se añade al MSE. Esto obliga al algoritmo de aprendizaje no sólo a ajustarse a los datos, sino también a mantener las ponderaciones del modelo lo más pequeñas posible. Ten en cuenta que el término de regularización sólo debe añadirse a la función de costes durante el entrenamiento. Una vez entrenado el modelo, querrás utilizar el MSE no regularizado (o el RMSE) para evaluar el rendimiento del modelo.

El hiperparámetro α controla cuánto quieres regularizar el modelo. Si α = 0, la regresión de cresta es simplemente una regresión lineal. Si α es muy grande, todas las ponderaciones acaban muy cerca de cero y el resultado es una línea plana que pasa por la media de los datos. La ecuación 4-8 presenta la función de coste de la regresión de cresta.7

Ecuación 4-8. Función de coste de la regresión Ridge
J(θ)=MSE(θ)+αmi=1nθi2

Observa que el término de sesgo θ0 no está regularizado (la suma empieza en i = 1, no en 0). Si definimos w como el vector de pesos de las características(θ1 a θn), entonces el término de regularización es igual a α(∥ w ∥2)2 / m, donde ∥ w ∥2 representa la norma ℓ2 del vector de pesos.8 Para el descenso de gradiente por lotes, basta con añadir 2αw / m a la parte del vector de gradiente MSE que corresponde a los pesos de las características, sin añadir nada al gradiente del término de sesgo (véase la ecuación 4-6).

Advertencia

Es importante escalar los datos (por ejemplo, utilizando un StandardScaler) antes de realizar la regresión ridge, ya que es sensible a la escala de las características de entrada. Esto ocurre con la mayoría de los modelos regularizados.

La Figura 4-17 muestra varios modelos de cresta que se entrenaron con algunos datos lineales muy ruidosos utilizando diferentes valores α. A la izquierda, se utilizan modelos de cresta simples, que conducen a predicciones lineales. A la derecha, los datos se expanden primero utilizandoPolynomialFeatures(degree=10), luego se escalan utilizando StandardScaler, y finalmente se aplican los modelos de cresta a las características resultantes: esto es regresión polinómica con regularización de cresta. Observa cómo el aumento de α conduce a predicciones más planas (es decir, menos extremas, más razonables), reduciendo así la varianza del modelo pero aumentando su sesgo.

mls3 0417
Figura 4-17. Modelos lineal (izquierda) y polinómico (derecha), ambos con varios niveles de regularización de cresta

Al igual que con la regresión lineal, podemos realizar la regresión de cresta calculando una ecuación de forma cerrada o realizando el descenso de gradiente. Los pros y los contras son los mismos. La ecuación 4-9 muestra la solución de forma cerrada, donde A es la matriz de identidad(n + 1) ×(n + 1).9 excepto con un 0 en la celda superior izquierda, que corresponde al término de sesgo.

Ecuación 4-9. Solución de forma cerrada de la regresión Ridge
θ ^ = (X X+αA) -1 X y

He aquí cómo realizar la regresión de cresta con Scikit-Learn utilizando una solución de forma cerrada (una variante de la ecuación 4-9 que utiliza una técnica de factorización de matrices de André-Louis Cholesky):

>>> from sklearn.linear_model import Ridge
>>> ridge_reg = Ridge(alpha=0.1, solver="cholesky")
>>> ridge_reg.fit(X, y)
>>> ridge_reg.predict([[1.5]])
array([[1.55325833]])

Y utilizando el descenso de gradiente estocástico.10

>>> sgd_reg = SGDRegressor(penalty="l2", alpha=0.1 / m, tol=None,
...                        max_iter=1000, eta0=0.01, random_state=42)
...
>>> sgd_reg.fit(X, y.ravel())  # y.ravel() because fit() expects 1D targets
>>> sgd_reg.predict([[1.5]])
array([1.55302613])

El hiperparámetro penalty establece el tipo de término de regularización a utilizar. Especificar "l2" indica que quieres que SGD añada un término de regularización a la función de coste MSE igual a alpha veces el cuadrado de la norma ℓ2 del vector de pesos. Esto es igual que la regresión ridge, salvo que en este caso no hay división por m; por eso pasamos alpha=0.1 / m, para obtener el mismo resultado que Ridge(alpha=0.1).

Consejo

La clase RidgeCV también realiza regresión de cresta, pero ajusta automáticamente los hiperparámetros mediante validación cruzada. Es más o menos equivalente a utilizar GridSearchCV, pero está optimizado para la regresión de cresta y se ejecuta mucho más rápido. Otros estimadores (la mayoría lineales) también tienen variantes CV eficientes, como LassoCV yElasticNetCV.

Regresión Lasso

La regresión por mínimos absolutos y operador de selección (normalmente llamada simplemente regresión lasso) es otra versión regularizada de la regresión lineal: igual que la regresión ridge, añade un término de regularización a la función de costes, pero utiliza la norma ℓ1 del vector de pesos en lugar del cuadrado de la norma ℓ2 (véase la ecuación 4-10). Observa que la norma ℓ1 se multiplica por , mientras que la norma ℓ2 se multiplicaba por α / m en la regresión de cresta. Estos factores se eligieron para garantizar que el valor óptimo de α es independiente del tamaño del conjunto de entrenamiento: normas diferentes conducen a factores diferentes (para más detalles, consulta el tema #15657 de Scikit-Learn ).

Ecuación 4-10. Función de coste de regresión Lasso
J(θ)=MSE(θ)+2αi=1nθi

La Figura 4-18 muestra lo mismo que la Figura 4-17, pero sustituye los modelos de cresta por modelos de lazo y utiliza valores α diferentes.

mls3 0418
Figura 4-18. Modelos lineal (izquierda) y polinómico (derecha), ambos con distintos niveles de regularización del lazo

Una característica importante de la regresión lasso es que tiende a eliminar los pesos de las características menos importantes (es decir, a ponerlos a cero). Por ejemplo, la línea discontinua del gráfico de la derecha de la Figura 4-18 (con α = 0,01) tiene un aspecto aproximadamente cúbico: todas las ponderaciones de las características polinómicas de alto grado son iguales a cero. En otras palabras, la regresión lasso realiza automáticamente la selección de características y produce un modelo disperso con pocas ponderaciones de características distintas de cero.

Puedes hacerte una idea de por qué es así observando la Figura 4-19: los ejes representan dos parámetros del modelo, y los contornos del fondo representan diferentes funciones de pérdida. En el gráfico superior izquierdo, los contornos representan la pérdida ℓ1 (|θ1|+ |θ2|), que disminuye linealmente a medida que te acercas a cualquier eje. Por ejemplo, si inicializas los parámetros del modelo a θ1 = 2 y θ2 = 0,5, al ejecutar el descenso por gradiente disminuirán ambos parámetros por igual (como representa la línea amarilla discontinua); por tanto, θ2 llegará primero a 0 (ya que estaba más cerca de 0 al principio). Después, el descenso gradiente rodará por el canalón hasta llegar a θ1 = 0 (con un poco de rebote, ya que los gradientes de ℓ1 nunca se acercan a 0: son -1 ó 1 para cada parámetro). En el gráficosuperior derecho, los contornos representan la función de coste de la regresión lasso (es decir, una función de coste MSE más una pérdida ℓ1 ). Los pequeños círculos blancos muestran el camino que sigue el descenso gradiente para optimizar unos parámetros del modelo que se inicializaron en torno aθ1= 0,25 y θ2= -1: observa una vez más cómo el camino alcanza rápidamente θ2 = 0, luego rueda por el canalón y acaba rebotando alrededor del óptimo global (representado por el cuadrado rojo). Si aumentáramos α, el óptimo global se desplazaría hacia la izquierda a lo largo de la línea amarilla discontinua, mientras que si disminuyéramos α, el óptimo global se desplazaría hacia la derecha (en este ejemplo, los parámetros óptimos para el MSE no regularizado son θ1 = 2 y θ2 = 0,5).

mls3 0419
Figura 4-19. Lasso frente a regularización de cresta

Los dos gráficos inferiores muestran lo mismo pero con una penalización ℓ2 en su lugar. En el gráfico inferior izquierdo, puedes ver que la pérdida ℓ2 disminuye a medida que nos acercamos al origen, por lo que el descenso por gradiente sigue un camino recto hacia ese punto. En el gráfico inferior derecho, los contornos representan la función de coste de la regresión de cresta (es decir, una función de coste MSE más una pérdida ℓ2 ). Como puedes ver, los gradientes se hacen más pequeños a medida que los parámetros se acercan al óptimo global, por lo que el descenso de gradiente se ralentiza de forma natural. Esto limita el rebote, lo que ayuda a que la cresta converja más rápidamente que la regresión lazo. Observa también que los parámetros óptimos (representados por el cuadrado rojo) se acercan cada vez más al origen cuando aumentas α, pero nunca se eliminan por completo.

Consejo

Para evitar que el descenso gradiente rebote alrededor del óptimo al final cuando se utiliza la regresión lazo, tienes que reducir gradualmente la velocidad de aprendizaje durante el entrenamiento. Seguirá rebotando alrededor del óptimo, pero los pasos serán cada vez más pequeños, por lo que convergerá.

La función de coste lazo no es diferenciable en θi = 0 (para i = 1, 2, ⋯, n), pero el descenso gradiente sigue funcionando si utilizas en su lugar un vector subgradiente g11 cuando cualquier θi = 0. La ecuación 4-11 muestra una ecuación de vector subgradiente que puedes utilizar para el descenso gradiente con la función de coste lazo.

Ecuación 4-11. Vector subgradiente de regresión lasso
g(θ,J)=θMSE(θ)+2αfirma(θ1)firma(θ2)firma(θn) donde firma(θi)=-1si θi<00si θi=0+1si θi>0

He aquí un pequeño ejemplo de Scikit-Learn utilizando la clase Lasso:

>>> from sklearn.linear_model import Lasso
>>> lasso_reg = Lasso(alpha=0.1)
>>> lasso_reg.fit(X, y)
>>> lasso_reg.predict([[1.5]])
array([1.53788174])

Ten en cuenta que podrías utilizar en su lugar SGDRegressor(penalty="l1", alpha=0.1).

Regresión de la red elástica

La regresión de red elástica es un término medio entre la regresión de cresta y la regresión de lazo. El término de regularización es una suma ponderada de los términos de regularización de la cresta y del lazo, y puedes controlar la relación de mezcla r. Cuando r = 0, la red elástica es equivalente a la regresión de la cresta, y cuando r = 1, es equivalente a la regresión del lazo(ecuación 4-12).

Ecuación 4-12. Función de coste neto elástico
J(θ)=MSE(θ)+r2αi=1nθi+(1-r)αmi=1nθi2

Entonces, ¿cuándo debes utilizar la regresión de red elástica, o la de cresta, la de lazo o la regresión lineal simple (es decir, sin ninguna regularización)? Casi siempre es preferible tener al menos un poco de regularización, así que en general debes evitar la regresión lineal simple. La cresta es una buena opción por defecto, pero si sospechas que sólo unas pocas características son útiles, deberías preferir el lazo o la red elástica, porque tienden a reducir a cero los pesos de las características inútiles, como ya se ha dicho. En general, se prefiere la red elástica al lasso porque éste puede comportarse de forma errática cuando el número de características es mayor que el número de instancias de entrenamiento o cuando varias características están fuertemente correlacionadas.

He aquí un breve ejemplo que utiliza ElasticNet de Scikit-Learn (l1_ratio corresponde a la relación de mezcla r):

>>> from sklearn.linear_model import ElasticNet
>>> elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
>>> elastic_net.fit(X, y)
>>> elastic_net.predict([[1.5]])
array([1.54333232])

Parada anticipada

Una forma muy distinta de regularizar los algoritmos de aprendizaje iterativo, como el descenso de gradiente, es detener el entrenamiento en cuanto el error de validación alcance un mínimo. Esto se denomina parada anticipada. La Figura 4-20 muestra un modelo complejo (en este caso, un modelo de regresión polinómica de alto grado) que se entrena con el descenso de gradiente por lotes en el conjunto de datos cuadrático que hemos utilizado antes. A medida que pasan las épocas, el algoritmo aprende, y su error de predicción (RMSE) en el conjunto de entrenamiento disminuye, junto con su error de predicción en el conjunto de validación. Sin embargo, al cabo de un tiempo, el error de validación deja de disminuir y empieza a aumentar. Esto indica que el modelo ha empezado a sobreajustar los datos de entrenamiento. Con la parada anticipada sólo tienes que dejar de entrenar en cuanto el error de validación alcance el mínimo. Se trata de una técnica de regularización tan sencilla y eficaz que Geoffrey Hinton la denominó "hermoso almuerzo gratis".

mls3 0420
Figura 4-20. Regularización de parada anticipada
Consejo

Con el descenso de gradiente estocástico y por mini lotes, las curvas no son tan suaves, y puede ser difícil saber si has alcanzado el mínimo o no. Una solución es parar sólo después de que el error de validación haya estado por encima del mínimo durante algún tiempo (cuando estés seguro de que el modelo no lo hará mejor), y luego retroceder los parámetros del modelo hasta el punto en que el error de validación estaba en el mínimo.

Aquí tienes una aplicación básica de la parada anticipada:

from copy import deepcopy
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler

X_train, y_train, X_valid, y_valid = [...]  # split the quadratic dataset

preprocessing = make_pipeline(PolynomialFeatures(degree=90, include_bias=False),
                              StandardScaler())
X_train_prep = preprocessing.fit_transform(X_train)
X_valid_prep = preprocessing.transform(X_valid)
sgd_reg = SGDRegressor(penalty=None, eta0=0.002, random_state=42)
n_epochs = 500
best_valid_rmse = float('inf')

for epoch in range(n_epochs):
    sgd_reg.partial_fit(X_train_prep, y_train)
    y_valid_predict = sgd_reg.predict(X_valid_prep)
    val_error = mean_squared_error(y_valid, y_valid_predict, squared=False)
    if val_error < best_valid_rmse:
        best_valid_rmse = val_error
        best_model = deepcopy(sgd_reg)

Este código añade primero las características polinómicas y escala todas las características de entrada, tanto para el conjunto de entrenamiento como para el conjunto de validación (el código supone que has dividido el conjunto de entrenamiento original en un conjunto de entrenamiento más pequeño y un conjunto de validación). A continuación, crea un modelo SGDRegressor sin regularización y con una tasa de aprendizaje pequeña. En el bucle de entrenamiento, llama a partial_fit() en lugar de a fit(), para realizar un aprendizaje incremental. En cada época, mide el RMSE en el conjunto de validación. Si es inferior al RMSE más bajo observado hasta el momento, guarda una copia del modelo en la variable best_model. Esta implementación no detiene realmente el entrenamiento, pero te permite volver al mejor modelo después del entrenamiento. Observa que el modelo se copia utilizando copy.deepcopy(), porque copia tanto los hiperparámetros del modelo como los parámetros aprendidos. En cambio, sklearn.base.clone() sólo copia los hiperparámetros del modelo.

Regresión logística

Como se comentó en el Capítulo 1, algunos algoritmos de regresión pueden utilizarse para la clasificación (y viceversa). La regresión logística (también llamada regresión logit) se utiliza habitualmente para estimar la probabilidad de que una instancia pertenezca a una clase determinada (por ejemplo, ¿cuál es la probabilidad de que este correo electrónico sea spam?). Si la probabilidad estimada es superior a un umbral determinado (normalmente el 50%), el modelo predice que la instancia pertenece a esa clase (denominada clase positiva, etiquetada como "1"), y en caso contrario predice que no (es decir, que pertenece a la clase negativa, etiquetada como "0"). Esto lo convierte en un clasificador binario.

Estimación de probabilidades

¿Cómo funciona la regresión logística? Al igual que un modelo de regresión lineal, un modelo de regresión logística calcula una suma ponderada de las características de entrada (más un término de sesgo), pero en lugar de emitir el resultado directamente como hace el modelo de regresión lineal, emite la logística de este resultado (véase la ecuación 4-13).

Ecuación 4-13. Probabilidad estimada del modelo de regresión logística (forma vectorizada)
p ^ = h θ ( x ) = σ ( θ x )

La logística -notada σ(-)- es una función sigmoidea (es decir, con forma de S) que da como resultado un número entre 0 y 1. Se define como se muestra en la Ecuación 4-14 y en la Figura 4-21.

Ecuación 4-14. Función logística
σ ( t ) = 1 1+exp(-t)
mls3 0421
Figura 4-21. Función logística

Una vez que el modelo de regresión logística ha estimado la probabilidad p^ = (x) de que una instancia x pertenezca a la clase positiva, puede hacer su predicción ŷ fácilmente (véase la ecuación 4-15).

Ecuación 4-15. Predicción del modelo de regresión logística utilizando un umbral de probabilidad del 50
y ^ = 0 si p ^ < 0.5 1 si p ^ 0.5

Observa que σ(t) < 0,5 cuando t < 0, y σ(t) ≥ 0,5 cuando t ≥ 0, por lo que un modelo de regresión logística que utilice el umbral por defecto del 50% de probabilidad predice 1 si θ⊺ x es positivo y 0 si es negativo.

Nota

La puntuación t suele denominarse logit. El nombre proviene del hecho de que la función logit, definida como logit(p) = log(p / (1 - p)), es la inversa de la función logística. En efecto, si calculas el logit de la probabilidad estimada p, verás que el resultado es t. El logit también se llama log-odds, ya que es el logaritmo de la relación entre la probabilidad estimada para la clase positiva y la probabilidad estimada para la clase negativa.

Función de formación y costes

Ahora ya sabes cómo un modelo de regresión logística estima probabilidades y hace predicciones. Pero, ¿cómo se entrena? El objetivo del entrenamiento es establecer el vector de parámetros θ de modo que el modelo estime probabilidades altas para los casos positivos(y = 1) y probabilidades bajas para los casos negativos(y = 0). Esta idea se capta mediante la función de coste que se muestra en la ecuación 4-16 para una única instancia de entrenamiento x.

Ecuación 4-16. Función de coste de una única instancia de entrenamiento
c(θ)=-registro(p^)si y=1-registro(1-p^)si y=0

Esta función de coste tiene sentido porque -log(t) crece mucho cuando t se acerca a 0, por lo que el coste será grande si el modelo estima una probabilidad cercana a 0 para una instancia positiva, y también será grande si el modelo estima una probabilidad cercana a 1 para una instancia negativa. Por otra parte, -log(t) se aproxima a 0 cuando t se aproxima a 1, por lo que el coste se aproximará a 0 si la probabilidad estimada se aproxima a 0 para una instancia negativa o se aproxima a 1 para una instancia positiva, que es precisamente lo que queremos.

La función de coste sobre todo el conjunto de entrenamiento es el coste medio sobre todas las instancias de entrenamiento. Se puede escribir en una única expresión denominada pérdida logarítmica, que se muestra en la Ecuación 4-17.

Ecuación 4-17. Función de coste de la regresión logística (pérdida logarítmica)
J(θ)=-1mi=1my(i)logp^(i)+(1-y(i))log1-p^(i)
Advertencia

La pérdida logarítmica no se sacó simplemente de un sombrero. Se puede demostrar matemáticamente (utilizando la inferencia bayesiana) que la minimización de esta pérdida dará como resultado el modelo con la máxima probabilidad de ser óptimo, suponiendo que las instancias siguen una distribución gaussiana en torno a la media de su clase. Cuando utilizas la pérdida logarítmica, ésta es la suposición implícita que estás haciendo. Cuanto más errónea sea esta suposición, más sesgado será el modelo. Del mismo modo, cuando utilizamos el MSE para entrenar modelos de regresión lineal, estábamos suponiendo implícitamente que los datos eran puramente lineales, más algo de ruido gaussiano. Por tanto, si los datos no son lineales (por ejemplo, si son cuadráticos) o si el ruido no es gaussiano (por ejemplo, si los valores atípicos no son exponencialmente raros), el modelo estará sesgado.

La mala noticia es que no se conoce ninguna ecuación de forma cerrada para calcular el valor de θ que minimiza esta función de coste (no existe un equivalente de la ecuación Normal). Pero la buena noticia es que esta función de coste es convexa, por lo que el descenso gradiente (o cualquier otro algoritmo de optimización) tiene garantizado encontrar el mínimo global (si la tasa de aprendizaje no es demasiado grande y esperas lo suficiente). Las derivadas parciales de la función de coste con respecto al j-ésimo parámetro del modelo θj vienen dadas por la ecuación 4-18.

Ecuación 4-18. Derivadas parciales de la función de coste logístico
θ j J ( θ ) = 1 m i=1 m σ ( θ x (i) ) - y (i) x j (i)

Esta ecuación se parece mucho a la ecuación 4-5: para cada instancia calcula el error de predicción y lo multiplica por el j-ésimo valor de la característica, y luego calcula la media de todas las instancias de entrenamiento. Una vez que tengas el vector gradiente que contiene todas las derivadas parciales, puedes utilizarlo en el algoritmo de descenso de gradiente por lotes. Eso es todo: ahora ya sabes cómo entrenar un modelo de regresión logística. Para la DG estocástica tomarías una instancia cada vez, y para la DG por mini lotes utilizarías un mini lote cada vez.

Límites de decisión

Podemos utilizar el conjunto de datos del iris para ilustrar la regresión logística. Se trata de un famoso conjunto de datos que contiene la longitud y anchura de los sépalos y pétalos de 150 flores de iris de tres especies diferentes: Iris setosa, Iris versicolor e Iris virginica (ver Figura 4-22).

mls3 0422
Figura 4-22. Flores de tres especies de iris12

Intentemos construir un clasificador para detectar el tipo de Iris virginica basándonos sólo en la característica de anchura de los pétalos. El primer paso es cargar los datos y echar un vistazo rápido:

>>> from sklearn.datasets import load_iris
>>> iris = load_iris(as_frame=True)
>>> list(iris)
['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names',
 'filename', 'data_module']
>>> iris.data.head(3)
   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
>>> iris.target.head(3)  # note that the instances are not shuffled
0    0
1    0
2    0
Name: target, dtype: int64
>>> iris.target_names
array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

A continuación, dividiremos los datos y entrenaremos un modelo de regresión logística en el conjunto de entrenamiento:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X = iris.data[["petal width (cm)"]].values
y = iris.target_names[iris.target] == 'virginica'
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

log_reg = LogisticRegression(random_state=42)
log_reg.fit(X_train, y_train)

Veamos las probabilidades estimadas del modelo para flores con anchuras de pétalo que varían de 0 cm a 3 cm(Figura 4-23).13

X_new = np.linspace(0, 3, 1000).reshape(-1, 1)  # reshape to get a column vector
y_proba = log_reg.predict_proba(X_new)
decision_boundary = X_new[y_proba[:, 1] >= 0.5][0, 0]

plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2,
         label="Not Iris virginica proba")
plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica proba")
plt.plot([decision_boundary, decision_boundary], [0, 1], "k:", linewidth=2,
         label="Decision boundary")
[...] # beautify the figure: add grid, labels, axis, legend, arrows, and samples
plt.show()
mls3 0423
Figura 4-23. Probabilidades estimadas y límite de decisión

La anchura de los pétalos de las flores de Iris virginica (representadas como triángulos) oscila entre 1,4 cm y 2,5 cm, mientras que las demás flores de iris (representadas por cuadrados) suelen tener una anchura de pétalo menor, que oscila entre 0,1 cm y 1,8 cm. Observa que hay un poco de solapamiento. Por encima de unos 2 cm, el clasificador está muy seguro de que la flor es una Iris virginica (emite una probabilidad alta para esa clase), mientras que por debajo de 1 cm está muy seguro de que no es una Iris virginica (probabilidad alta para la clase "No Iris virginica"). Entre estos extremos, el clasificador no está seguro. Sin embargo, si le pides que prediga la clase (utilizando el método predict() en lugar del método predict_proba() ), te devolverá la clase que sea más probable. Por tanto, hay un límite de decisión en torno a 1,6 cm en el que ambas probabilidades son iguales al 50%: si la anchura del pétalo es superior a 1,6 cm, el clasificador predecirá que la flor es una Iris virginica, y en caso contrario predecirá que no lo es (aunque no esté muy seguro):

>>> decision_boundary
1.6516516516516517
>>> log_reg.predict([[1.7], [1.5]])
array([ True, False])

La Figura 4-24 muestra el mismo conjunto de datos, pero esta vez con dos características: la anchura y la longitud de los pétalos. Una vez entrenado, el clasificador de regresión logística puede, basándose en estas dos características, estimar la probabilidad de que una flor nueva sea una Iris virginica. La línea discontinua representa los puntos en los que el modelo estima una probabilidad del 50%: éste es el límite de decisión del modelo. Observa que se trata de un límite lineal.14 Cada línea paralela representa los puntos en los que el modelo da una probabilidad específica, desde el 15% (abajo a la izquierda) hasta el 90% (arriba a la derecha). Todas las flores situadas más allá de la línea superior derecha tienen más de un 90% de probabilidades de ser Iris virginica, según el modelo.

mls3 0424
Figura 4-24. Límite de decisión lineal
Nota

El hiperparámetro que controla la fuerza de regularización de un modelo Scikit-Learn LogisticRegression no es alpha (como en otros modelos lineales), sino su inverso: C. Cuanto mayor sea el valor de C, menos regularizado estará el modelo.

Al igual que los demás modelos lineales, los modelos de regresión logística se pueden regularizar mediante penalizaciones ℓ1 o ℓ2. De hecho, Scikit-Learn añade una penalización ℓ2 por defecto.

Regresión Softmax

El modelo de regresión logística puede generalizarse para admitir varias clases directamente, sin tener que entrenar y combinar varios clasificadores binarios (como se explica en el Capítulo 3). Esto se denomina regresión softmax o regresión logística multinomial.

La idea es sencilla: cuando se da una instancia x, el modelo de regresión softmax calcula primero una puntuaciónsk(x) para cada clase k, y luego estima la probabilidad de cada clase aplicando la función softmax (también llamada exponencial normalizada) a las puntuaciones. La ecuación para calcularsk(x) debería resultarte familiar, ya que es igual que la ecuación para la predicción por regresión lineal (ver Ecuación 4-19).

Ecuación 4-19. Puntuación Softmax para la clase k
s k ( x ) = (θ (k) ) x

Observa que cada clase tiene su propio vector de parámetros dedicado θ(k). Todos estos vectores suelen almacenarse como filas de una matriz de parámetros Θ.

Una vez calculada la puntuación de cada clase para la instancia x, puedes estimar la probabilidad p^k de que la instancia pertenezca a la clase k pasando las puntuaciones por la función softmax(Ecuación 4-20). La función calcula la exponencial de cada puntuación, y luego las normaliza (dividiéndolas por la suma de todas las exponenciales). Las puntuaciones suelen denominarse logits o log-odds (aunque en realidad son log-odds sin normalizar).

Ecuación 4-20. Función Softmax
p ^ k = σ s(x) k = exps k (x) j=1 K exps j (x)

En esta ecuación:

  • K es el número de clases.

  • s(x) es un vector que contiene las puntuaciones de cada clase para la instancia x.

  • σ(s(x))k es la probabilidad estimada de que la instancia x pertenezca a la clase k, dadas las puntuaciones de cada clase para esa instancia.

Al igual que el clasificador de regresión logística, por defecto el clasificador de regresión softmax predice la clase con la probabilidad estimada más alta (que es simplemente la clase con la puntuación más alta), como se muestra en la ecuación 4-21.

Ecuación 4-21. Predicción del clasificador de regresión Softmax
y ^ = argmax k σ s(x) k = argmax k s k ( x ) = argmax k (θ (k) ) x

El operador argmax devuelve el valor de una variable que maximiza una función. En esta ecuación, devuelve el valor de k que maximiza la probabilidad estimada σ(s(x))k.

Consejo

El clasificador de regresión softmax sólo predice una clase a la vez (es decir, es multiclase, no multisalida), por lo que sólo debe utilizarse con clases mutuamente excluyentes, como diferentes especies de plantas. No puedes utilizarlo para reconocer a varias personas en una misma foto.

Ahora que sabes cómo el modelo estima probabilidades y hace predicciones, echemos un vistazo al entrenamiento. El objetivo es tener un modelo que estime una probabilidad alta para la clase objetivo (y, en consecuencia, una probabilidad baja para las demás clases). Minimizar la función de coste mostrada en la ecuación 4-22, llamada entropía cruzada, debería conducir a este objetivo, porque penaliza al modelo cuando estima una probabilidad baja para una clase objetivo. La entropía cruzada se utiliza con frecuencia para medir lo bien que coincide un conjunto de probabilidades de clase estimadas con las clases objetivo.

Ecuación 4-22. Función de coste de entropía cruzada
J(Θ)=-1mi=1mk=1Kyk(i)registrop^k(i)

En esta ecuación yk(i) es la probabilidad objetivo de que la i-ésima instancia pertenezca a la clase k. En general, es igual a 1 o a 0, según que la instancia pertenezca o no a la clase.

Observa que cuando sólo hay dos clases(K = 2), esta función de coste es equivalente a la función de coste de regresión logística (pérdida logarítmica; véase la ecuación 4-17).

El vector gradiente de esta función de coste con respecto a θ(k) viene dado por la ecuación 4-23.

Ecuación 4-23. Vector gradiente de entropía cruzada para la clase k
θ (k) J ( Θ ) = 1 m i=1 m p ^ k (i) - y k (i) x (i)

Ahora puedes calcular el vector gradiente para cada clase, y luego utilizar el descenso de gradiente (o cualquier otro algoritmo de optimización) para encontrar la matriz de parámetros Θ que minimice la función de coste.

Utilicemos la regresión softmax para clasificar las plantas de iris en las tres clases. El clasificador LogisticRegression de Scikit-Learn utiliza automáticamente la regresión softmax cuando lo entrenas en más de dos clases (suponiendo que utilices solver="lbfgs", que es el valor por defecto). También aplica la regularización ℓ2 por defecto, que puedes controlar mediante el hiperparámetro C, como ya se ha mencionado:

X = iris.data[["petal length (cm)", "petal width (cm)"]].values
y = iris["target"]
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

softmax_reg = LogisticRegression(C=30, random_state=42)
softmax_reg.fit(X_train, y_train)

Así, la próxima vez que encuentres un iris con pétalos de 5 cm de largo y 2 cm de ancho, puedes pedirle a tu modelo que te diga qué tipo de iris es, y te responderá Iris virginica (clase 2) con un 96% de probabilidad (o Iris versicolor con un 4% de probabilidad):

>>> softmax_reg.predict([[5, 2]])
array([2])
>>> softmax_reg.predict_proba([[5, 2]]).round(2)
array([[0.  , 0.04, 0.96]])

La Figura 4-25 muestra los límites de decisión resultantes, representados por los colores de fondo. Observa que los límites de decisión entre dos clases cualesquiera son lineales. La figura también muestra las probabilidades de la clase Iris versicolor, representadas por las líneas curvas (por ejemplo, la línea marcada con 0,30 representa el límite de probabilidad del 30%). Observa que el modelo puede predecir una clase que tenga una probabilidad estimada inferior al 50%. Por ejemplo, en el punto donde se encuentran todos los límites de decisión, todas las clases tienen una probabilidad estimada igual del 33%.

mls3 0425
Figura 4-25. Límites de decisión de la regresión Softmax

En este capítulo has aprendido varias formas de entrenar modelos lineales, tanto de regresión como de clasificación. Utilizaste una ecuación de forma cerrada para resolver la regresión lineal, así como el descenso de gradiente, y aprendiste cómo se pueden añadir diversas penalizaciones a la función de coste durante el entrenamiento para regularizar el modelo. Por el camino, también aprendiste a trazar curvas de aprendizaje y a analizarlas, así como a aplicar la parada anticipada. Por último, aprendiste cómo funcionan la regresión logística y la regresión softmax. ¡Hemos abierto las primeras cajas negras del aprendizaje automático! En los próximos capítulos abriremos muchas más, empezando por las máquinas de vectores soporte.

Ejercicios

  1. ¿Qué algoritmo de entrenamiento de regresión lineal puedes utilizar si tienes un conjunto de entrenamiento con millones de características?

  2. Supón que las características de tu conjunto de entrenamiento tienen escalas muy diferentes. ¿Qué algoritmos podrían sufrir por ello, y cómo? ¿Qué puedes hacer al respecto?

  3. ¿Puede el descenso de gradiente quedarse atascado en un mínimo local al entrenar un modelo de regresión logística?

  4. ¿Todos los algoritmos de descenso gradiente conducen al mismo modelo, siempre que los dejes funcionar el tiempo suficiente?

  5. Supón que utilizas el descenso de gradiente por lotes y trazas el error de validación en cada época. Si observas que el error de validación aumenta sistemáticamente, ¿qué puede estar pasando? ¿Cómo puedes solucionarlo?

  6. ¿Es una buena idea detener inmediatamente el descenso gradiente por mini lotes cuando sube el error de validación?

  7. ¿Qué algoritmo de descenso gradiente (de entre los que hemos discutido) alcanzará más rápidamente la proximidad de la solución óptima? ¿Cuál convergerá realmente? ¿Cómo puedes hacer que los demás también converjan?

  8. Supongamos que utilizas la regresión polinómica. Trazas las curvas de aprendizaje y observas que hay una gran diferencia entre el error de entrenamiento y el error de validación. ¿Qué ocurre? ¿Cuáles son las tres formas de solucionarlo?

  9. Supongamos que utilizas la regresión ridge y observas que el error de entrenamiento y el error de validación son casi iguales y bastante elevados. ¿Dirías que el modelo sufre un sesgo elevado o una varianza elevada? ¿Deberías aumentar el hiperparámetro de regularización α o reducirlo?

  10. ¿Por qué querrías utilizar:

    1. ¿Regresión Ridge en lugar de regresión lineal simple (es decir, sin ningunaregularización)?

    2. ¿Lazo en lugar de regresión de cresta?

    3. ¿Red elástica en lugar de regresión lasso?

  11. Supongamos que quieres clasificar imágenes como exteriores/interiores y diurnas/nocturnas. ¿Deberías implementar dos clasificadores de regresión logística o un clasificador de regresión softmax?

  12. Implementa el descenso de gradiente por lotes con parada temprana para la regresión softmax sin utilizar Scikit-Learn, sólo NumPy. Utilízalo en una tarea de clasificación como el conjunto de datos del iris.

Las soluciones a estos ejercicios están disponibles al final del cuaderno de este capítulo, en https://homl.info/colab3.

1 Una ecuación de forma cerrada sólo se compone de un número finito de constantes, variables y operaciones estándar: por ejemplo, a = sen(b - c). No hay sumas infinitas, ni límites, ni integrales, etc.

2 Técnicamente hablando, su derivada es continua de Lipschitz.

3 Como el rasgo 1 es más pequeño, se necesita un cambio mayor en θ1 para afectar a la función de coste, por lo que el cuenco se alarga a lo largo del eje θ1.

4 Eta) es la séptima letra del alfabeto griego.

5 Mientras que la ecuación Normal sólo puede realizar una regresión lineal, los algoritmos de descenso gradiente pueden utilizarse para entrenar muchos otros modelos, como verás.

6 Esta noción de sesgo no debe confundirse con el término de sesgo de los modelos lineales.

7 Es habitual utilizar la notación J) para las funciones de coste que no tienen un nombre abreviado; a menudo utilizaré esta notación a lo largo del resto de este libro. El contexto dejará claro de qué función de coste se está hablando.

8 Las normas se tratan en el Capítulo 2.

9 Una matriz cuadrada llena de 0s excepto por 1s en la diagonal principal (de arriba a la izquierda hasta abajo a la derecha).

10 Como alternativa, puedes utilizar la clase Ridge con el solucionador "sag". El GD de promedio estocástico es una variante del GD estocástico. Para más detalles, consulta la presentación "Minimizing Finite Sums with the Stochastic Average Gradient Algorithm" de Mark Schmidt et al. de la Universidad de Columbia Británica.

11 Puedes pensar en un vector subgradiente en un punto no diferenciable como un vector intermedio entre los vectores gradiente alrededor de ese punto.

12 Fotos reproducidas de las correspondientes páginas de Wikipedia. Foto de Iris virginica de Frank Mayfield(Creative Commons BY-SA2.0), foto de Iris versicolor de D. Gordon E. Robertson(Creative Commons BY-SA 3.0), foto de Iris setosa de dominio público.

13 La función reshape() de NumPy permite que una dimensión sea -1, lo que significa "automático": el valor se deduce de la longitud de la matriz y de las dimensiones restantes.

14 Es el conjunto de puntos x tales que θ0 + θ1x1 + θ2x2 = 0, que define una recta.

Get Aprendizaje automático práctico con Scikit-Learn, Keras y TensorFlow, 3ª edición 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.