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?
-
Simplicidad: Es fácil de entender e implementar (perfecto para principiantes)
-
Interpretabilidad: Los resultados son fáciles de explicar a cualquier audiencia
-
Base sólida: Muchos algoritmos avanzados se basan en estos conceptos
-
Efectividad: A menudo funciona mejor de lo esperado en problemas reales
-
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:
- Escribir ecuaciones de manera compacta
- Usar operaciones matriciales eficientes
- Implementar algoritmos de manera elegante
- 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