MLZC25-11. Regresión Lineal: El ABC del Machine Learning



This content originally appeared on DEV Community and was authored by Jesus Oviedo Riquelme

🎯 Objetivo del Post: Dominarás la regresión lineal, el algoritmo más fundamental del Machine Learning, desde conceptos básicos hasta implementación práctica con código.

📈 ¿Qué es la Regresión Lineal?

Imagina que tienes un conjunto de puntos en una hoja de papel y quieres dibujar la línea recta que mejor los atraviese. Esa línea representa una relación entre dos variables. La regresión lineal hace exactamente eso, pero con matemáticas precisas y datos reales.

La regresión lineal es un método estadístico que:

  • 🎯 Encuentra la línea recta que mejor se ajusta a un conjunto de datos
  • 🔮 Permite predecir valores futuros basándose en esta relación
  • 🧮 Utiliza matemáticas simples pero poderosas
  • 🚀 Es la base de algoritmos más complejos

🚀 ¿Por qué Empezar con Regresión Lineal?

  1. 🧠 Simplicidad: Es fácil de entender e implementar (perfecto para principiantes)
  2. 💬 Interpretabilidad: Los resultados son fáciles de explicar a cualquier audiencia
  3. 🏗 Base sólida: Muchos algoritmos avanzados se basan en estos conceptos
  4. 🎯 Efectividad: A menudo funciona mejor de lo esperado en problemas reales
  5. ⚡ Velocidad: Es computacionalmente eficiente (milisegundos vs. minutos)

Regresión Lineal Simple: Una Variable

El Concepto Básico

En regresión lineal simple, tenemos:

  • Variable independiente (X): La característica que usamos para predecir
  • Variable dependiente (y): Lo que queremos predecir
  • Relación lineal: y = mx + b

Para nuestro proyecto de autos:

  • X: Año del auto
  • y: Precio del auto
  • Relación: Precio = m × Año + b

La Ecuación de la Línea

y = mx + b

Donde:

  • m: Pendiente (slope) – cuánto cambia y por cada unidad de x
  • b: Intercepto (intercept) – valor de y cuando x = 0
  • x: Variable independiente
  • y: Variable dependiente

Ejemplo Práctico

Si tenemos la ecuación: Precio = 1000 × Año + 5000

  • Un auto del 2020: Precio = 1000 × 2020 + 5000 = $2,025,000
  • Un auto del 2015: Precio = 1000 × 2015 + 5000 = $2,020,000
  • Un auto del 2010: Precio = 1000 × 2010 + 5000 = $2,015,000

Interpretación:

  • La pendiente (1000) significa que por cada año más nuevo, el precio aumenta $1,000
  • El intercepto (5000) sería el precio teórico de un auto del año 0 (no tiene sentido práctico)

Regresión Lineal Múltiple: Varias Variables

¿Por qué Necesitamos Múltiples Variables?

En la vida real, el precio de un auto no depende solo del año. También importan:

  • Kilometraje
  • Marca
  • Modelo
  • Tipo de transmisión
  • Estado del vehículo

La Ecuación Múltiple

y = b₀ + b₁x₁ + b₂x₂ + b₃x₃ + ... + bₙxₙ

Para nuestro proyecto de autos:

Precio = b₀ + b₁×Año + b₂×Kilometraje + b₃×Edad + b₄×Marca_BMW + b₅×Marca_Toyota + ...

Donde:

  • b₀: Intercepto (precio base)
  • b₁, b₂, b₃…: Coeficientes para cada variable
  • x₁, x₂, x₃…: Valores de cada característica

Forma Vectorial: Matemáticas Elegantes

¿Por qué Forma Vectorial?

La forma vectorial nos permite:

  1. Escribir ecuaciones de manera compacta
  2. Usar operaciones matriciales eficientes
  3. Implementar algoritmos de manera elegante
  4. Escalar a millones de datos

Representación Vectorial

En lugar de escribir:

y = b₀ + b₁x₁ + b₂x₂ + b₃x₃ + ... + bₙxₙ

Escribimos:

y = Xw

Donde:

  • X: Matriz de características (m × n)
  • w: Vector de pesos (n × 1)
  • y: Vector de predicciones (m × 1)

Ejemplo Concreto

Si tenemos 3 autos y 2 características (año, kilometraje):

X = [[2020, 25000],     # Auto 1: año 2020, 25,000 km
     [2019, 30000],     # Auto 2: año 2019, 30,000 km
     [2021, 20000]]     # Auto 3: año 2021, 20,000 km

w = [1000, -0.5]        # Pesos: año=1000, kilometraje=-0.5

y = Xw = [2020×1000 + 25000×(-0.5),   # = 2007500
          2019×1000 + 30000×(-0.5),   # = 2004000
          2021×1000 + 20000×(-0.5)]   # = 2011000

Implementación Práctica

Paso 1: Preparar los Datos

import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler

# Cargar datos
df = pd.read_csv('car_data_limpio.csv')

# Preparar características numéricas
features = ['año', 'kilometraje', 'edad_auto']
X = df[features]
y = df['precio']

print("Forma de X:", X.shape)
print("Forma de y:", y.shape)
print("\nPrimeras 5 filas de X:")
print(X.head())

Paso 2: Implementación Manual de Regresión Lineal

class LinearRegressionManual:
    def __init__(self):
        self.weights = None
        self.bias = None

    def fit(self, X, y):
        """Entrenar el modelo usando la ecuación normal"""
        # Agregar columna de unos para el bias
        X_with_bias = np.column_stack([np.ones(X.shape[0]), X])

        # Calcular pesos usando ecuación normal: w = (X^T X)^-1 X^T y
        XtX = np.dot(X_with_bias.T, X_with_bias)
        XtX_inv = np.linalg.inv(XtX)
        Xty = np.dot(X_with_bias.T, y)

        weights_with_bias = np.dot(XtX_inv, Xty)

        # Separar bias y pesos
        self.bias = weights_with_bias[0]
        self.weights = weights_with_bias[1:]

        return self

    def predict(self, X):
        """Hacer predicciones"""
        return np.dot(X, self.weights) + self.bias

# Usar nuestro modelo manual
modelo_manual = LinearRegressionManual()
modelo_manual.fit(X, y)

print("Pesos aprendidos:")
for i, feature in enumerate(features):
    print(f"{feature}: {modelo_manual.weights[i]:.2f}")
print(f"Bias: {modelo_manual.bias:.2f}")

Paso 3: Usando scikit-learn

# Usar la implementación de scikit-learn
modelo_sklearn = LinearRegression()
modelo_sklearn.fit(X, y)

print("Pesos de scikit-learn:")
for i, feature in enumerate(features):
    print(f"{feature}: {modelo_sklearn.coef_[i]:.2f}")
print(f"Intercepto: {modelo_sklearn.intercept_:.2f}")

# Verificar que son iguales
print(f"\n¿Son iguales? {np.allclose(modelo_manual.weights, modelo_sklearn.coef_)}")

La Ecuación Normal: La Matemática Detrás

¿Qué es la Ecuación Normal?

La ecuación normal es una fórmula matemática que nos da la solución exacta para encontrar los mejores pesos en regresión lineal.

Fórmula: w = (X^T X)^(-1) X^T y

Donde:

  • X: Matriz de características
  • X^T: Transpuesta de X
  • y: Vector de valores objetivo
  • w: Vector de pesos óptimos

¿Por qué Funciona?

La ecuación normal encuentra los pesos que minimizan la suma de errores cuadrados:

SSE = Σ(y_i - ŷ_i)² = Σ(y_i - (w₀ + w₁x₁ + w₂x₂ + ...))²

Ventajas y Desventajas

Ventajas:

  • Solución exacta: No necesita iteraciones
  • Matemáticamente elegante: Una sola fórmula
  • Determinística: Siempre da el mismo resultado

Desventajas:

  • Computacionalmente costosa: O(n³) para matrices grandes
  • Problemas numéricos: Con matrices mal condicionadas
  • No escalable: Para datasets muy grandes

Visualización de Resultados

Paso 1: Predicciones vs Valores Reales

import matplotlib.pyplot as plt

# Hacer predicciones
y_pred = modelo_sklearn.predict(X)

# Crear gráfico de dispersión
plt.figure(figsize=(10, 6))
plt.scatter(y, y_pred, alpha=0.5, color='blue')
plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--', lw=2)
plt.xlabel('Precio Real ($)')
plt.ylabel('Precio Predicho ($)')
plt.title('Precio Real vs Predicho')
plt.grid(True, alpha=0.3)

# Calcular R²
from sklearn.metrics import r2_score
r2 = r2_score(y, y_pred)
plt.text(0.05, 0.95, f'R² = {r2:.3f}', transform=plt.gca().transAxes, 
         bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
plt.show()

Paso 2: Residuos (Errores)

# Calcular residuos
residuos = y - y_pred

# Gráfico de residuos
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.scatter(y_pred, residuos, alpha=0.5, color='green')
plt.axhline(y=0, color='r', linestyle='--')
plt.xlabel('Precio Predicho ($)')
plt.ylabel('Residuos ($)')
plt.title('Gráfico de Residuos')
plt.grid(True, alpha=0.3)

plt.subplot(1, 2, 2)
plt.hist(residuos, bins=30, alpha=0.7, color='orange')
plt.xlabel('Residuos ($)')
plt.ylabel('Frecuencia')
plt.title('Distribución de Residuos')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

Interpretación de Resultados

Coeficientes y su Significado

# Crear DataFrame con coeficientes
coef_df = pd.DataFrame({
    'Característica': features,
    'Coeficiente': modelo_sklearn.coef_,
    'Significado': [
        'Por cada año más nuevo, el precio aumenta $' + f'{modelo_sklearn.coef_[0]:,.0f}',
        'Por cada km adicional, el precio disminuye $' + f'{abs(modelo_sklearn.coef_[1]):.2f}',
        'Por cada año de edad, el precio disminuye $' + f'{abs(modelo_sklearn.coef_[2]):,.0f}'
    ]
})

print("Interpretación de Coeficientes:")
print(coef_df)

Ejemplo de Predicción

# Ejemplo: Predecir precio de un auto específico
auto_ejemplo = {
    'año': 2020,
    'kilometraje': 25000,
    'edad_auto': 4
}

# Convertir a array
X_ejemplo = np.array([list(auto_ejemplo.values())])

# Hacer predicción
precio_predicho = modelo_sklearn.predict(X_ejemplo)[0]

print(f"\nEjemplo de Predicción:")
print(f"Auto: Año {auto_ejemplo['año']}, {auto_ejemplo['kilometraje']:,} km")
print(f"Precio predicho: ${precio_predicho:,.2f}")

# Mostrar contribución de cada característica
contribuciones = modelo_sklearn.coef_ * X_ejemplo[0]
print(f"\nContribuciones:")
for i, feature in enumerate(features):
    print(f"{feature}: ${contribuciones[i]:,.2f}")
print(f"Intercepto: ${modelo_sklearn.intercept_:,.2f}")
print(f"Total: ${modelo_sklearn.intercept_ + contribuciones.sum():,.2f}")

Limitaciones de la Regresión Lineal

1. Asume Relación Lineal

  • No puede capturar relaciones no lineales
  • Ejemplo: La depreciación de autos no es lineal

2. Sensible a Outliers

  • Un auto muy caro puede sesgar toda la línea
  • Necesita detección y manejo de outliers

3. Multicolinealidad

  • Si dos características están muy correlacionadas, los coeficientes pueden ser inestables
  • Ejemplo: Año y edad del auto están perfectamente correlacionadas

4. No Maneja Variables Categóricas Directamente

  • Necesita codificación (one-hot encoding)
  • Ejemplo: Marca = [“Toyota”, “BMW”, “Honda”]

Mejores Prácticas

1. Verificar Supuestos

# Verificar linealidad
plt.figure(figsize=(15, 5))
for i, feature in enumerate(features):
    plt.subplot(1, 3, i+1)
    plt.scatter(X[feature], y, alpha=0.5)
    plt.xlabel(feature)
    plt.ylabel('Precio')
    plt.title(f'Precio vs {feature}')
plt.tight_layout()
plt.show()

2. Normalizar Variables

# Normalizar características
scaler = StandardScaler()
X_normalizado = scaler.fit_transform(X)

# Entrenar modelo con datos normalizados
modelo_normalizado = LinearRegression()
modelo_normalizado.fit(X_normalizado, y)

print("Coeficientes normalizados:")
print(modelo_normalizado.coef_)

3. Validación Cruzada

from sklearn.model_selection import cross_val_score

# Validación cruzada
scores = cross_val_score(modelo_sklearn, X, y, cv=5, scoring='r2')
print(f"R² promedio: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")

Conclusión

La regresión lineal es el fundamento de muchos algoritmos de Machine Learning. Aunque simple, es poderosa y nos enseña conceptos importantes:

  • Interpretabilidad: Cada coeficiente tiene significado
  • Eficiencia: Rápida de entrenar y predecir
  • Base sólida: Para algoritmos más complejos

Puntos clave:

  • La forma vectorial hace el código más elegante
  • La ecuación normal da la solución exacta
  • Los coeficientes son interpretables
  • Siempre verifica los supuestos del modelo

En el siguiente post, exploraremos cómo evaluar qué tan bien funciona nuestro modelo usando métricas como RMSE y cómo crear un modelo baseline para comparar.

¿Has usado regresión lineal antes? ¿Qué variables crees que serían más importantes para predecir el precio de un auto?


This content originally appeared on DEV Community and was authored by Jesus Oviedo Riquelme