Regresión Logística

Regresión logística para clasificación binaria: función sigmoide, frontera de decisión e implementación con scikit-learn.
Autor/a

Santi López Pavón

Palabras clave

machine learning, aprendizaje automatico, Python, algebra lineal, optimizacion, regresion lineal, clasificacion, estadistica

1 La Regresión Logística

La Regresión Logística es uno de los modelos fundamentales y más utilizados en Machine Learning para problemas de clasificación binaria. Su objetivo es calcular la probabilidad de que se dé un resultado determinado (por ejemplo, clasificar entre 1 o 0, sí o no).

Conceptualmente, la función de este modelo consiste en dibujar un objeto geométrico plano que actúe como un separador lineal para dividir nuestra nube de datos reales en dos grupos claramente definidos: los que pertenecen a la clase 1 y los que pertenecen a la clase 0. La forma exacta de este objeto dependerá exclusivamente de la cantidad de dimensiones (variables de entrada) con las que estemos trabajando:

  • En 2 Dimensiones (2 variables de entrada): El modelo genera una línea recta divisoria sobre el plano cartesiano.
  • En 3 Dimensiones (3 variables de entrada): Al añadir una variable más, la recta se transforma en un plano bidimensional divisor flotando en un espacio tridimensional.
  • En 4 o más Dimensiones (4 o más variables de entrada): Cuando entramos en el terreno de la alta dimensionalidad, el modelo genera lo que matemáticamente llamamos un hiperplano divisor.

La meta de la regresión logística es encontrar la posición exacta de este separador para que clasifique correctamente la mayor cantidad de puntos posibles, asignando probabilidades altas a la clase 1 y probabilidades bajas (cercanas a 0) a la clase 0.

2 Función hipótesis

A diferencia de la regresión lineal, la salida de la regresión logística debe ser una probabilidad acotada estrictamente entre 0 y 1. Para lograr esto, el modelo toma la misma combinación lineal del modelo lineal y la introduce dentro de una función de activación llamada función sigmoide o logística (\(g(z)\)).

La función sigmoide se define como:

\[g(z) = \frac{1}{1 + e^{-z}}\]

Donde \(z\) es nuestra conocida ecuación lineal: \(z = w_1x_1 + w_2x_2 + \dots + w_nx_n + b\). Al unirlas, construimos nuestra función hipótesis:

\[f_{w,b}(x) = g(z) = \frac{1}{1 + e^{-(w_1x_1 + w_2x_2 + \dots + w_nx_n + b)}}\]

  • Cada peso \(w_i\) controla el impacto de su característica (\(x_i\)): Un peso positivo grande empuja a \(z\) hacia el infinito positivo, lo que hace que la sigmoide se acerque a 1. Un peso negativo grande empuja a \(z\) hacia el infinito negativo, haciendo que la probabilidad caiga hacia 0.
  • El sesgo \(b\) determina la posición por defecto del separador cuando todas las características valen cero.

Computacionalmente se trabaja con vectores:

Definimos las características (\(x\)) y los pesos (\(w\)) como dos vectores columna en un espacio de \(n\) dimensiones:

\[W = \begin{bmatrix} w_1 \\ w_2 \\ \vdots \\ w_n \end{bmatrix}, \quad X = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix}\]

Realizando el producto escalar mediante la transposición del vector de pesos, la función hipótesis compacta se define entonces como:

\[f_{W,b}(X) = g(W^T X + b) = \frac{1}{1 + e^{-(W^T X + b)}}\]

Supongamos que queremos clasificar si un correo electrónico es Spam (\(Y=1\)) o No Spam (\(Y=0\)) usando tres características (\(n=3\)):

  • \(x_1\): Cantidad de veces que aparece la palabra “gratis”.
  • \(x_2\): Cantidad de enlaces en el cuerpo del correo.
  • \(x_3\): Cantidad de errores ortográficos detectados.

Supongamos que nuestro algoritmo ya entrenó con miles de correos y fijó los siguientes parámetros:

  • Vector de Pesos (\(W\)): \[W = \begin{bmatrix} w_1 \\ w_2 \\ w_3 \end{bmatrix} = \begin{bmatrix} 2.5 \\ 1.2 \\ -0.8 \end{bmatrix}\]

  • Sesgo (\(b\)): \[b = -3.0\]

Nos piden evaluar un nuevo correo con las siguientes especificaciones:

  • 2 palabras “gratis”
  • 4 enlaces
  • 1 error ortográfico

\[X = \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix} = \begin{bmatrix} 2 \\ 4 \\ 1 \end{bmatrix}\]

Primero calculamos el valor de la combinación lineal (\(z\)):

\[W^T X = \begin{bmatrix} 2.5 & 1.2 & -0.8 \end{bmatrix} \begin{bmatrix} 2 \\ 4 \\ 1 \end{bmatrix}\]

\[W^T X = (2.5 \cdot 2) + (1.2 \cdot 4) + (-0.8 \cdot 1) = 5 + 4.8 - 0.8 = 9.0\]

\[z = W^T X + b = 9.0 - 3.0 = 6.0\]

Ahora pasamos este valor por la función sigmoide para obtener la probabilidad final:

\[f_{W,b}(X) = \frac{1}{1 + e^{-6.0}} \approx \frac{1}{1 + 0.00247} \approx \mathbf{0.9975}\]

El modelo predice que hay un 99.75% de probabilidad de que este correo sea Spam. Como supera el umbral estándar de 0.5, el sistema lo clasificará formalmente como 1.

3 La función de coste en la regresión logística

En la regresión logística no podemos usar el Error Cuadrático Medio porque la introducción de la sigmoide vuelve a la función no convexa (llena de mínimos locales falsos). En su lugar, se utiliza la función de coste de Entropía Cruzada Binaria (Binary Cross-Entropy):

\[J(W,b) = -\frac{1}{m} \sum_{i=1}^{m} \left[ Y^{(i)} \log\left(f_{W,b}(X^{(i)})\right) + (1 - Y^{(i)}) \log\left(1 - f_{W,b}(X^{(i)})\right) \right]\]

  • La mecánica del coste: Si la clase real es \(Y=1\), el segundo término de la suma se anula y solo pagamos penalización si nuestra predicción se aleja de 1. Si la clase real es \(Y=0\), el primer término se anula y el coste sube si nuestra predicción se aleja de 0.
  • El signo menos inicial: Como los logaritmos de números entre 0 y 1 siempre devuelven valores negativos, este signo menos exterior convierte el resultado final en un número positivo para que podamos minimizarlo.

4 El Flujo del Modelo

El modelo recibe un vector de características (\(X\)), calcula el producto escalar (\(W^T X + b\)) y aplica la función sigmoide para generar la probabilidad estimada (\(Y_{\text{pred}}\)).

Para optimizarlo, necesitamos calcular las derivadas parciales de la función de coste respecto a los pesos (\(W\)) y al sesgo (\(b\)):

  • Para el vector de pesos (\(W\)): \[\frac{\partial J}{\partial W} = \frac{\partial J}{\partial Y_{\text{pred}}} \cdot \frac{\partial Y_{\text{pred}}}{\partial z} \cdot \frac{\partial z}{\partial W}\]

  • Para el sesgo (\(b\)): \[\frac{\partial J}{\partial b} = \frac{\partial J}{\partial Y_{\text{pred}}} \cdot \frac{\partial Y_{\text{pred}}}{\partial z} \cdot \frac{\partial z}{\partial b}\]

Gracias al diseño matemático de la combinación de la Entropía Cruzada con la función Sigmoide, al multiplicar los dos primeros eslabones de la regla de la cadena (\(\frac{\partial J}{\partial Y_{\text{pred}}} \cdot \frac{\partial Y_{\text{pred}}}{\partial z}\)), el resultado se simplifica de forma espectacular volviendo a quedar como el error puro:

  1. El error puro unificado: \[\frac{\partial J}{\partial z} = (Y_{\text{pred}} - Y)\]

  2. La derivada respecto a los pesos: Al igual que antes, la derivada de \(z = W^T X + b\) respecto a \(W\) nos devuelve la característica: \[\frac{\partial z}{\partial W} = X\]

  3. La derivada respecto al sesgo: La derivada de \(z\) respecto a \(b\) vuelve a ser simplemente uno: \[\frac{\partial z}{\partial b} = 1\]

4.1 Ejemplo Práctico Vectorial (3 Características)

Imagina que queremos predecir si un paciente está enfermo (\(Y=1\)) o sano (\(Y=0\)) en base a \(n=3\) indicadores médicos. El objetivo real (\(Y\)) del paciente es que está enfermo (\(Y = 1\)).

4.1.1 Inicialización de Vectores

Definimos nuestro vector de entrada (\(X\)) y unos parámetros iniciales aleatorios (\(W\) y \(b\)):

\[X = \begin{bmatrix} 2 \\ 3 \\ 1 \end{bmatrix}, \quad W = \begin{bmatrix} 0.1 \\ 0.1 \\ 0.1 \end{bmatrix}, \quad b = -0.5\]

4.1.2 Forward Propagation

Calculamos la combinación lineal y luego le aplicamos la sigmoide:

\[W^T X = \begin{bmatrix} 0.1 & 0.1 & 0.1 \end{bmatrix} \begin{bmatrix} 2 \\ 3 \\ 1 \end{bmatrix} = 0.2 + 0.3 + 0.1 = \mathbf{0.6}\]

\[z = 0.6 + (-0.5) = \mathbf{0.1}\]

\[Y_{\text{pred}} = g(0.1) = \frac{1}{1 + e^{-0.1}} \approx \mathbf{0.525}\]

4.1.3 Backward Propagation (Cálculo de Gradientes)

Calculamos la intensidad del error y obtenemos los gradientes:

\[\text{Error} = (Y_{\text{pred}} - Y) = 0.525 - 1 = \mathbf{-0.475}\]

\[\nabla_W J = (Y_{\text{pred}} - Y) \cdot X = -0.475 \cdot \begin{bmatrix} 2 \\ 3 \\ 1 \end{bmatrix} = \begin{bmatrix} -0.95 \\ -1.425 \\ -0.475 \end{bmatrix}\]

\[\frac{\partial J}{\partial b} = -0.475\]

4.1.4 Actualización de Parámetros

Aplicamos la regla del descenso de gradiente utilizando una tasa de aprendizaje \(\alpha = 0.1\):

\[W_{\text{nuevo}} = \begin{bmatrix} 0.1 \\ 0.1 \\ 0.1 \end{bmatrix} - 0.1 \cdot \begin{bmatrix} -0.95 \\ -1.425 \\ -0.475 \end{bmatrix} = \begin{bmatrix} 0.1 \\ 0.1 \\ 0.1 \end{bmatrix} - \begin{bmatrix} -0.095 \\ -0.1425 \\ -0.0475 \end{bmatrix} = \begin{bmatrix} \mathbf{0.195} \\ \mathbf{0.2425} \\ \mathbf{0.1475} \end{bmatrix}\]

\[b_{\text{nuevo}} = -0.5 - 0.1 \cdot (-0.475) = \mathbf{-0.4525}\]

5 Procesamiento en Lote

Trabajar en lote (batch) procesando múltiples pacientes a la vez en una sola operación matricial nos ahorra bucles lentos y exprime el potencial de procesamiento paralelo de las GPUs.

5.1 Ejemplo de cálculo

Tenemos una matriz de entrada donde cada columna representa un paciente de entrenamiento (\(m=3\)) y cada fila una característica médica (\(n=3\)):

\[X = \begin{bmatrix} 2 & 1 & 4 \\ 3 & 1 & 3 \\ 1 & 0 & 2 \end{bmatrix}\]

La matriz de pesos y el vector de objetivos reales:

\[W = \begin{bmatrix} 0.1 \\ 0.1 \\ 0.1 \end{bmatrix}, \quad Y = \begin{bmatrix} 1 & 0 & 1 \end{bmatrix}, \quad b = -0.5\]

5.1.1 Forward Propagation (\(W^T X\))

\[W^T X = \begin{bmatrix} 0.1 & 0.1 & 0.1 \end{bmatrix} \begin{bmatrix} 2 & 1 & 4 \\ 3 & 1 & 3 \\ 1 & 0 & 2 \end{bmatrix} = \begin{bmatrix} 0.6 & 0.2 & 0.9 \end{bmatrix}\]

Le sumamos el sesgo a cada elemento para obtener el vector fila \(Z\):

\[Z = \begin{bmatrix} 0.6 - 0.5 & 0.2 - 0.5 & 0.9 - 0.5 \end{bmatrix} = \begin{bmatrix} 0.1 & -0.3 & 0.4 \end{bmatrix}\]

Aplicamos la función sigmoide a cada elemento de la fila de forma independiente:

\[Y_{\text{pred}} = \begin{bmatrix} g(0.1) & g(-0.3) & g(0.4) \end{bmatrix} = \begin{bmatrix} \mathbf{0.525} & \mathbf{0.426} & \mathbf{0.599} \end{bmatrix}\]

5.1.2 Backward Propagation

Calculamos el vector fila de errores restando la realidad:

\[\text{Error} = Y_{\text{pred}} - Y = \begin{bmatrix} 0.525 & 0.426 & 0.599 \end{bmatrix} - \begin{bmatrix} 1 & 0 & 1 \end{bmatrix} = \begin{bmatrix} \mathbf{-0.475} & \mathbf{0.426} & \mathbf{-0.401} \end{bmatrix}\]

Para obtener el gradiente de los pesos, multiplicamos la matriz \(X\) por el vector de errores vuelto columna mediante la transposición, promediando entre \(m=3\):

\[(Y_{\text{pred}} - Y)^T = \begin{bmatrix} -0.475 \\ 0.426 \\ -0.401 \end{bmatrix}\]

\[X (Y_{\text{pred}} - Y)^T = \begin{bmatrix} 2 & 1 & 4 \\ 3 & 1 & 3 \\ 1 & 0 & 2 \end{bmatrix} \begin{bmatrix} -0.475 \\ 0.426 \\ -0.401 \end{bmatrix} = \begin{bmatrix} (2 \cdot -0.475) + (1 \cdot 0.426) + (4 \cdot -0.401) \\ (3 \cdot -0.475) + (1 \cdot 0.426) + (3 \cdot -0.401) \\ (1 \cdot -0.475) + (0 \cdot 0.426) + (2 \cdot -0.401) \end{bmatrix} = \begin{bmatrix} -2.128 \\ -2.202 \\ -1.277 \end{bmatrix}\]

Dividimos entre \(m=3\) para obtener el gradiente promedio definitivo del lote (\(\nabla_W J\)):

\[\nabla_W J = \begin{bmatrix} -2.128 / 3 \\ -2.202 / 3 \\ -1.277 / 3 \end{bmatrix} = \begin{bmatrix} \mathbf{-0.709} \\ \mathbf{-0.734} \\ \mathbf{-0.426} \end{bmatrix}\]

El gradiente representa la derivada de la función de coste respecto a cada uno de los componentes de \(W\).

Para el sesgo, promediamos la suma de la fila de errores:

\[\frac{\partial J}{\partial b} = \frac{1}{3} (-0.475 + 0.426 - 0.401) = \mathbf{-0.150}\]

Finalmente, aplicando nuestra tasa de aprendizaje \(\alpha = 0.1\), actualizamos todos los parámetros mediante el descenso de gradiente:

\[W_{\text{nuevo}} = \begin{bmatrix} 0.1 \\ 0.1 \\ 0.1 \end{bmatrix} - 0.1 \cdot \begin{bmatrix} -0.709 \\ -0.734 \\ -0.426 \end{bmatrix} = \begin{bmatrix} \mathbf{0.171} \\ \mathbf{0.173} \\ \mathbf{0.143} \end{bmatrix}\]

\[b_{\text{nuevo}} = -0.5 - 0.1 \cdot (-0.150) = \mathbf{-0.485}\]