В каких ситуациях линейная модель неудачна?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда линейная модель неудачна
Ситуация 1: Нелинейная зависимость
Истинная зависимость между X и y не является линейной. Линейная модель даст низкий R².
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
# Данные с синусоидальной зависимостью
X = np.linspace(0, 10, 100).reshape(-1, 1)
y = np.sin(X).ravel() + np.random.normal(0, 0.1, 100)
# Линейная модель
model_linear = LinearRegression()
model_linear.fit(X, y)
print(f"Linear R²: {model_linear.score(X, y):.3f}") # ~0.002
# С полиномиальными признаками
features = PolynomialFeatures(degree=3)
X_poly = features.fit_transform(X)
model_poly = LinearRegression()
model_poly.fit(X_poly, y)
print(f"Poly R²: {model_poly.score(X_poly, y):.3f}") # ~0.99
Ситуация 2: Мультиколлинеарность
Признаки сильно коррелированы. Коэффициенты становятся нестабильными и непредсказуемыми.
import pandas as pd
from statsmodels.stats.outliers_influence import variance_inflation_factor
X = np.random.randn(100, 3)
X[:, 1] = X[:, 0] + np.random.normal(0, 0.1, 100) # Почти копия X[:, 0]
X[:, 2] = X[:, 0] * 2
df = pd.DataFrame(X, columns=['X1', 'X2', 'X3'])
vif = [variance_inflation_factor(X, i) for i in range(3)]
print(f"VIF: {vif}") # > 10 указывает на проблему
# Решение: Ridge Regression
from sklearn.linear_model import Ridge
model = Ridge(alpha=1.0)
model.fit(X, y)
Ситуация 3: Выбросы
Линейная регрессия очень чувствительна к экстремальным значениям, которые сильно влияют на наклон линии.
# С выбросом в конце
y_with_outlier = y.copy()
y_with_outlier[-1] = 100
model = LinearRegression()
model.fit(X, y_with_outlier)
# Решение: HuberRegressor
from sklearn.linear_model import HuberRegressor
model_robust = HuberRegressor(epsilon=1.35)
model_robust.fit(X, y_with_outlier)
Ситуация 4: Взаимодействие признаков
Эффект одного признака зависит от значения другого. Например, цена зависит от площади * качество локации.
# Истинная зависимость с взаимодействием
X = np.random.randn(100, 2)
y = X[:, 0] * X[:, 1] + np.random.normal(0, 0.5, 100)
model_simple = LinearRegression()
model_simple.fit(X, y)
print(f"R² без взаимодействия: {model_simple.score(X, y):.3f}")
# Добавляем interaction term
X_with_interaction = np.column_stack([X, X[:, 0] * X[:, 1]])
model_int = LinearRegression()
model_int.fit(X_with_interaction, y)
print(f"R² с взаимодействием: {model_int.score(X_with_interaction, y):.3f}")
Ситуация 5: Гетероскедастичность
Дисперсия ошибок непостоянна и зависит от X. Модель неправильно оценивает доверительные интервалы.
# Дисперсия растёт с X
y = 2 * X.ravel() + np.random.normal(0, X.ravel(), 100)
# Решение: Weighted Least Squares
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X, y)
residuals = y - model.predict(X)
# Взвешиваем по дисперсии
weights = 1 / (np.abs(residuals) + 0.01)
model_weighted = LinearRegression()
model_weighted.fit(X, y, sample_weight=weights)
Ситуация 6: Автокорреляция остатков
Особенно актуально для временных рядов. Текущая ошибка коррелирует с предыдущей.
# Временной ряд с автокоррелированными ошибками
time = np.arange(100).reshape(-1, 1)
y = 2 * time.ravel() + np.cumsum(np.random.normal(0, 1, 100))
# Решение: ARIMA для временных рядов
from statsmodels.tsa.arima.model import ARIMA
model = ARIMA(y, order=(1, 1, 1))
fitted = model.fit()
Ситуация 7: Недостаточно данных (N < D)
Количество примеров меньше количества признаков. Модель переполняется и получает идеальный training score.
# 10 примеров, 1000 признаков
X_small = np.random.randn(10, 1000)
y_small = np.random.randn(10)
model = LinearRegression()
model.fit(X_small, y_small)
print(f"Training R²: {model.score(X_small, y_small):.3f}") # 1.0 (переполнение!)
# Решение: Lasso для отбора признаков
from sklearn.linear_model import Lasso
model_lasso = Lasso(alpha=0.1)
model_lasso.fit(X_small, y_small)
Ситуация 8: Категориальные переменные без обработки
Строки нельзя передавать прямо в linear regression. Нужна кодировка.
import pandas as pd
df = pd.DataFrame({
'city': ['New York', 'Los Angeles', 'Chicago'],
'price': [500000, 600000, 400000]
})
# One-Hot Encoding
X = pd.get_dummies(df[['city']])
# city_Chicago | city_Los Angeles | city_New York
# 0 | 0 | 1
# 0 | 1 | 0
# 1 | 0 | 0
Ситуация 9: Несбалансированные классы (классификация)
При LogisticRegression с дисбалансом (99% класса 0, 1% класса 1) модель почти всё классифицирует как 0.
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=1000, weights=[0.99, 0.01])
# Без балансировки
model = LogisticRegression()
model.fit(X, y)
# Решение: class_weight='balanced'
model_balanced = LogisticRegression(class_weight='balanced')
model_balanced.fit(X, y)
Ситуация 10: Сложные нелинейные взаимодействия
y зависит от сложных комбинаций и нелинейных трансформаций X.
# Сложная нелинейность
X = np.random.randn(100, 3)
y = (X[:, 0]**2) * np.exp(X[:, 1]) + np.sin(X[:, 2]) + noise
model_linear = LinearRegression()
print(f"Linear R²: {model_linear.score(X, y):.3f}") # Низкий
# Решение: GradientBoosting
from sklearn.ensemble import GradientBoostingRegressor
model_gb = GradientBoostingRegressor()
model_gb.fit(X, y)
print(f"GBM R²: {model_gb.score(X, y):.3f}") # Намного выше
Как выявить проблемы
- Визуализация: scatter plot показывает нелинейность
- Остатки: plot остатков vs предсказания показывает паттерны
- VIF: > 10 означает мультиколлинеарность
- Durbin-Watson: < 2 означает автокорреляцию
- Q-Q plot: отклонения от нормального распределения
Практический совет
Если R² < 0.6 и данные выглядят нелинейно:
- Используй Polynomial Features или Splines
- Попробуй Tree-based модели (RandomForest, GBM)
- Используй Neural Networks для сложных зависимостей
Линейная модель — хороший baseline, но не универсальное решение.