Regresión lineal y logística
Ajustar una recta, medir su error y convertirla en una probabilidad
La regresión es el primer modelo que conviene entender de verdad: una recta que minimiza el error al cuadrado. Aquí la ajustas a mano, la comparas con scikit-learn, aprendes qué significan MSE, MAE y R², y das el salto a la regresión logística para clasificar.
- Ajustar una recta por mínimos cuadrados y explicar qué minimiza
- Calcular MSE, MAE y R² e interpretar cada cifra en sus unidades
- Entrenar modelos con LinearRegression y LogisticRegression de scikit-learn
- Explicar cómo la función sigmoide convierte una puntuación en probabilidad
- Leer los coeficientes de un modelo sin confundir asociación con causalidad
Antes necesitas: Fundamentos de Machine Learning · NumPy: vectores, matrices y vectorización · Estadística y análisis exploratorio
En esta lección
- ¿Qué es una regresión?
- ¿Para qué sirve?
- ¿Cómo funciona por dentro? La recta y la pérdida
- Sintaxis: dos modelos, una misma API
- Ejemplo básico: una recta que predice
- Visualización: datos, predicciones y residuos
- Ejemplo intermedio: mínimos cuadrados a mano
- Traza: un paso de gradiente descendente
- Las métricas y qué significa cada una
- Regresión logística: del número a la probabilidad
- Caso práctico: clasificar con una probabilidad
- Cómo se leen los coeficientes
- Errores comunes
- Buenas prácticas
- Ejercicios
- Soluciones explicadas
- Resumen
- Evaluación
Tienes las horas de estudio de varios estudiantes y sus notas. Quieres responder dos preguntas distintas: «¿cuánto sacará alguien que estudie 4 horas?» y «¿aprobará?». La primera pide un número; la segunda, una decisión. La regresión lineal y la logística resuelven cada una.
Las dos son primas: construyen una combinación de las entradas, w · x + b, y solo cambian lo que hacen con el resultado. Entender esa base te sirve para casi todo lo que viene después, incluidas las redes neuronales.
¿Qué es una regresión?
Una regresión es un modelo que predice una cantidad a partir de una o más características. En la lineal, la predicción es una recta (o un plano, con varias entradas): ŷ = w₁x₁ + w₂x₂ + … + b. Los pesos w dicen cuánto pesa cada característica y b es el valor base cuando todas valen cero.
La regresión logística usa la misma combinación, pero le aplica la función sigmoide para comprimir el resultado entre 0 y 1. Ese número se interpreta como la probabilidad de pertenecer a la clase positiva, así que sirve para clasificación binaria: aprobó o no, spam o no, abandona o no.
¿Para qué sirve?
- Precios y demanda: estimar cuánto costará un departamento o cuántas unidades se venderán el próximo mes.
- Riesgo de crédito: estimar la probabilidad de que un cliente no pague.
- Medicina: relacionar dosis y respuesta, o probabilidad de complicación según variables clínicas.
- Línea base de cualquier proyecto: antes de probar un modelo complejo, la regresión dice cuánto se puede explicar con una relación simple.
¿Cómo funciona por dentro? La recta y la pérdida
Con una sola entrada, el modelo es ŷ = w x + b. Para cada punto, el residuo es y - ŷ: cuánto se aleja la recta del dato real. Los mínimos cuadrados eligen w y b de modo que la suma de los residuos al cuadrado sea la menor posible. Elevar al cuadrado evita que los errores positivos y negativos se cancelen.
Con muchas entradas no hay fórmula tan cómoda y se usa gradiente descendente: calculas la pendiente de la pérdida respecto a cada peso, das un paso pequeño en dirección contraria y repites. El tamaño del paso es la tasa de aprendizaje; si es enorme, el modelo diverge; si es diminuta, tarda una eternidad.
Sintaxis: dos modelos, una misma API
scikit-learn unifica todo en tres llamadas: crear, fit y predict. Instálalo una vez en tu entorno virtual:
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
python -m pip install scikit-learnfrom sklearn.linear_model import LinearRegression, LogisticRegression
X = [[1], [2], [3], [7], [8], [9]]
notas = [2.8, 3.4, 4.1, 6.9, 8.2, 9.0] # regresión: una nota
aprobo = [0, 0, 0, 1, 1, 1] # clasificación: una categoría
lineal = LinearRegression().fit(X, notas)
logistica = LogisticRegression().fit(X, aprobo)
print("nota estimada a las 4 h:", round(lineal.predict([[4]])[0], 2))
print("clase predicha a las 8 h:", logistica.predict([[8]])[0])LinearRegression produce valores sin límite. LogisticRegression expone predict_proba, que devuelve dos columnas: probabilidad de la clase 0 y de la clase 1. El método predict aplica un umbral de 0.5 y devuelve ya la etiqueta.
Ejemplo básico: una recta que predice
Supón que ya ajustaste el modelo y obtuviste w = 0.6 y b = 2.2. Predecir es sustituir:
def predecir(horas):
return 2.2 + 0.6 * horas
for h in [1, 3, 5]:
print(h, "->", round(predecir(h), 2))La salida es 1 -> 2.8, 3 -> 4.0 y 5 -> 5.2. Cada hora adicional suma 0.6 unidades a la predicción; con cero horas, el modelo parte de 2.2. Cuando la relación es curvada, una recta se queda corta por definición, y ahí conviene añadir términos cuadráticos o cambiar de modelo.
Visualización: datos, predicciones y residuos
Estos cinco puntos tienen una relación casi lineal. La cuarta columna muestra el residuo de cada uno; fíjate en que los positivos y los negativos se compensan.
x = 1 la recta predice 2.8 y el valor real es 2: el residuo es -0.8.Recorre los pasos para ver cómo cambian las celdas.
Observa el patrón: la recta sobreestima en los extremos y subestima en el centro. Eso indica que la relación real tiene curvatura. Ningún ajuste de w y b elimina ese patrón; solo un modelo con más flexibilidad puede.
Ejemplo intermedio: mínimos cuadrados a mano
Con una entrada, los mínimos cuadrados tienen fórmula cerrada: w = Σ(xᵢ − x̄)(yᵢ − ȳ) / Σ(xᵢ − x̄)² y b = ȳ − w x̄. Aplícala a x = [1, 2, 3, 4, 5] e y = [2, 4, 5, 4, 5].
x = [1, 2, 3, 4, 5]
y = [2, 4, 5, 4, 5]
n = len(x)
media_x = sum(x) / n
media_y = sum(y) / n
sxy = sum((xi - media_x) * (yi - media_y) for xi, yi in zip(x, y))
sxx = sum((xi - media_x) ** 2 for xi in x)
pendiente = sxy / sxx
intercepto = media_y - pendiente * media_x
print("pendiente:", round(pendiente, 2))
print("intercepto:", round(intercepto, 2))La media de x es 3 y la de y es 4. El numerador vale 6, el denominador 10, así que w = 0.6. El intercepto es 4 − 0.6 · 3 = 2.2, justo los valores que usamos arriba. La salida lo confirma: pendiente: 0.6 e intercepto: 2.2.
Traza: un paso de gradiente descendente
Cuando no hay fórmula cerrada, el modelo se ajusta por pasos. Con dos puntos (x = [1, 2], y = [3, 4]), tasa 0.1 y pesos iniciales en cero, observa cómo se mueven w y b en la primera iteración:
x = [1, 2]y = [3, 4]w, b = 0.0, 0.0lr = 0.1for paso in range(2): pred = [w * xi + b for xi in x] error = [p - yi for p, yi in zip(pred, y)] grad_w = 2 * sum(e * xi for e, xi in zip(error, x)) / len(x) grad_b = 2 * sum(error) / len(x) w = w - lr * grad_w b = b - lr * grad_b print(paso, round(w, 2), round(b, 2))w = 0 y b = 0. Todas sus predicciones serán cero.—
Avanza paso a paso: observa la línea resaltada, las variables y la salida.
La recta perfecta para esos dos puntos es y = x + 2: pendiente 1, intercepto 2. El gradiente no la alcanza de golpe porque cada paso es pequeño: hacen falta unas quinientas iteraciones para llegar a w = 1.0 y b ≈ 2.0. Esa lentitud es el precio de no usar la fórmula cerrada.
Las métricas y qué significa cada una
Un error solo es útil si sabes en qué unidades está y qué castiga. Estas cuatro cifras resumen el mismo ajuste desde ángulos distintos:
| Métrica | Fórmula | Unidades | Qué castiga |
|---|---|---|---|
| MSE | media de (y − ŷ)² | unidades² | los errores grandes |
| RMSE | raíz del MSE | las de y | los errores grandes |
| MAE | media de |y − ŷ| | las de y | todos por igual |
| R² | 1 − SSE / SST | sin unidades | la proporción explicada |
x = [1, 2, 3, 4, 5]
y = [2, 4, 5, 4, 5]
w, b = 0.6, 2.2
pred = [w * xi + b for xi in x]
errores = [yi - pi for yi, pi in zip(y, pred)]
mse = sum(e ** 2 for e in errores) / len(y)
mae = sum(abs(e) for e in errores) / len(y)
sst = sum((yi - sum(y) / len(y)) ** 2 for yi in y)
r2 = 1 - sum(e ** 2 for e in errores) / sst
print("MSE:", round(mse, 2))
print("MAE:", round(mae, 2))
print("R2:", round(r2, 2))La salida es MSE: 0.48, MAE: 0.64 y R2: 0.6. La raíz del MSE vale 0.69: el error típico es de dos tercios de unidad. El R² de 0.6 dice que la recta explica el 60 % de la variación de y; el 40 % restante se debe al patrón curvo que la recta no puede seguir.
Regresión logística: del número a la probabilidad
Para clasificar, la recta sola no sirve: puede devolver -3 o 7, valores que no son probabilidades. La sigmoide los comprime: σ(z) = 1 / (1 + e^(−z)). Con z = 0 da 0.5; los valores grandes se acercan a 1 y los muy negativos, a 0.
from math import exp
def sigmoide(z):
return 1 / (1 + exp(-z))
for z in [-2, 0, 1, 2]:
print(z, "->", round(sigmoide(z), 3))La salida es -2 -> 0.119, 0 -> 0.5, 1 -> 0.731 y 2 -> 0.881. El umbral habitual es 0.5, que corresponde a z = 0: si la combinación de entradas es positiva, el modelo se inclina por la clase 1. Cambiar el umbral cambia el equilibrio entre falsos positivos y falsos negativos, y es una decisión de negocio, no del algoritmo.
Caso práctico: clasificar con una probabilidad
Seis estudiantes: tres que estudiaron 1, 2 y 3 horas no aprobaron; tres que estudiaron 7, 8 y 9 sí. El modelo debe aprender esa frontera.
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
X = [[1], [2], [3], [7], [8], [9]]
y = [0, 0, 0, 1, 1, 1]
modelo = LogisticRegression()
modelo.fit(X, y)
print("predicciones:", modelo.predict([[1], [2], [8]]))
print("exactitud:", accuracy_score(y, modelo.predict(X)))El modelo imprime predicciones: [0 0 1] y exactitud: 1.0: acierta los seis casos de entrenamiento. Esa exactitud perfecta no es un trofeo, porque los datos son trivialmente separables. La prueba real es cómo se comporta con estudiantes de 4, 5 o 6 horas, justo en la zona ambigua, y para eso necesitas un conjunto reservado.
Cómo se leen los coeficientes
En regresión lineal, un coeficiente de 0.6 significa que una unidad más de esa característica suma 0.6 a la predicción, si todo lo demás se mantiene igual. Ese «si todo lo demás se mantiene» es la parte que se olvida: si dos variables se mueven juntas, el modelo reparte el peso entre ellas de forma inestable.
En logística el coeficiente vive en escala de log-odds. Un coeficiente de 0.7 multiplica las probabilidades a favor por e^0.7 ≈ 2.01: duplica el apoyo a la clase positiva por cada unidad. La dirección sí se lee directamente: signo positivo empuja hacia la clase 1.
X = [1, 2, 3]
w, b = 0.6, 2.2
y_hat = [round(w * x + b, 2) for x in X]
print(y_hat) # [2.8, 3.4, 4.0]#include <iostream>
#include <vector>
int main() {
const std::vector<int> x = {1, 2, 3};
const double w = 0.6, b = 2.2;
for (int xi : x) {
std::cout << w * xi + b << '\n';
}
}En Python la comprensión oculta el bucle y redondea; en C++ el bucle es explícito y el tipo de cada variable está declarado. scikit-learn añade otra capa: ni siquiera escribes la multiplicación, solo llamas a `predict`.
Errores comunes
- Usar R² para comparar modelos con distinta escala: el R² depende de la varianza de
y; entre datasets distintos no es comparable. - Interpretar un coeficiente como causa: un peso alto indica asociación dentro del modelo, no que cambiar esa variable cambie el resultado.
- Ajustar sin dividir: el MSE de entrenamiento siempre es optimista.
- Ignorar la escala: una tasa de aprendizaje razonable con variables normalizadas puede divergir con variables en miles.
- Leer
predict_probacomo certeza: una probabilidad de 0.9 no significa que el caso sea positivo.
Buenas prácticas
- Grafica los residuos: un patrón en forma de curva delata que la recta no basta.
- Normaliza las entradas antes de usar gradiente descendente o regularización.
- Informa MSE y MAE juntos; el segundo es más fácil de explicar a quien decide.
- Evalúa la clasificación con exactitud, pero también con precisión y recall si las clases están desbalanceadas.
- Guarda el umbral elegido y el motivo: forma parte del modelo, no es un detalle.
Ejercicios
Evalúa una recta dada
(1, 3), (2, 4) y (3, 8), calcula las predicciones del modelo ŷ = 2x + 1, los residuos, el MSE y el MAE. Imprime los tres residuos y las dos métricas redondeadas a dos decimales.Pista
Las predicciones son 3, 5 y 7. Un residuo es y - ŷ; el MSE promedia los cuadrados y el MAE, los valores absolutos.
De la puntuación a la decisión
sigmoide(z) y clasifica con umbral 0.5 los valores z = -1, 0 y 3. Imprime la probabilidad redondeada a tres decimales y la decisión «aprueba» o «no aprueba».Pista
Compara la probabilidad con 0.5, no con 1. La función exp está en el módulo math.
errores = [yi - pi for yi, pi in zip(y, pred)] mse = sum(e ** 2 for e in errores) / mae = sum(abs(e) for e in errores) / len(errores) print("MSE:", round(, 2)) print("MAE:", round(mae, 2))
El promedio divide entre la cantidad de errores; la segunda impresión debe mostrar el MSE.
Soluciones explicadas
En el primer ejercicio, el residuo 0 del primer punto no significa que el modelo sea perfecto: solo acierta ahí. El MSE y el MAE coinciden porque ningún error supera una unidad; con un residuo de 5, el MSE subiría a (0 + 1 + 25) / 3 = 8.67 mientras el MAE quedaría en 2.0.
En el segundo, la sigmoide nunca llega a 0 ni a 1: solo se acerca. Por eso una probabilidad de 0.953 no es una promesa. La decisión final depende del umbral, y ese umbral debe elegirse mirando el coste de cada tipo de error, no copiando el 0.5 por costumbre.
Resumen
- La regresión lineal predice
ŷ = w · x + b; los mínimos cuadrados minimizan la suma de residuos al cuadrado. - El gradiente descendente actualiza los pesos en dirección contraria a la pendiente, con pasos de tamaño
lr. - El MSE está en unidades al cuadrado, el MAE y el RMSE en las de
y, y el R² no tiene unidades. - La sigmoide convierte cualquier puntuación en una probabilidad entre 0 y 1.
- En logística, un coeficiente positivo empuja hacia la clase 1 y
e^wes el factor de cambio en las probabilidades. - Un coeficiente describe asociación dentro del modelo; no demuestra causalidad.
Evaluación
¿Qué eligen los mínimos cuadrados al ajustar una recta?
Un modelo tiene MSE = 0.48 y MAE = 0.64. ¿Qué indica la diferencia?
En una regresión logística con umbral 0.5, un caso recibe probabilidad 0.62. ¿Qué ocurre?
¿Ya lo entendiste?
Márcala como completada para llevar tu progreso y desbloquear el siguiente paso de la ruta.