← Назад к вопросам
Какой метод перекрёстной проверки вы бы использовали для набора данных временных рядов?
1.7 Middle🔥 161 комментариев
#Временные ряды#Метрики и оценка моделей
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Какой метод перекрёстной проверки вы бы использовали для набора данных временных рядов?
Обычная k-fold кросс-валидация категорически запрещена для временных рядов (time series), так как она нарушает временной порядок данных и приводит к информационной утечке из будущего в прошлое (data leakage). Вместо этого используются специализированные методы, которые уважают временную последовательность.
Почему обычная k-fold не работает для временных рядов
# НЕПРАВИЛЬНО: k-fold нарушает временной порядок
train_indices = [0, 2, 4, 6, 8] # Случайная выборка
test_indices = [1, 3, 5, 7, 9]
# На обучении модель видит будущие значения (5, 6, 7, 8, 9)
# На тесте проверяем на прошлые значения (1, 3, 5)
# Это нереалистично и приводит к завышению точности!
1. Walk-Forward Validation (Временный порядок)
Самый важный и правильный метод. Обучаются на прошлых данных, тестируются на будущих.
# Визуально:
Fold 1: [Train: 0-20] [Test: 21-25]
Fold 2: [Train: 0-25] [Test: 26-30]
Fold 3: [Train: 0-30] [Test: 31-35]
Тренировочное окно расширяется со временем
Реализация:
from sklearn.model_selection import TimeSeriesSplit
from sklearn.linear_model import LinearRegression
import numpy as np
# Генерируем временной ряд
np.random.seed(42)
X = np.arange(100).reshape(-1, 1)
y = np.sin(X.ravel() / 10) + np.random.normal(0, 0.1, 100)
# TimeSeriesSplit — встроенный метод для walk-forward
tscv = TimeSeriesSplit(n_splits=5)
for fold, (train_idx, test_idx) in enumerate(tscv.split(X)):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
model = LinearRegression()
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
print(f"Fold {fold+1}: Train={train_idx}, Test={test_idx}, Score={score:.4f}")
# Результат:
# Fold 1: Train=[ 0 ... 19], Test=[20 ... 39], Score=0.9234
# Fold 2: Train=[ 0 ... 39], Test=[40 ... 59], Score=0.9156
# Fold 3: Train=[ 0 ... 59], Test=[60 ... 79], Score=0.8925
# Fold 4: Train=[ 0 ... 79], Test=[80 ... 99], Score=0.8112
2. Expanding Window (Растущее окно)
Тренировочное окно растёт, тестовое остаётся фиксированным размером.
# Визуально:
Fold 1: [Train: 0-10] [Test: 11-20]
Fold 2: [Train: 0-20] [Test: 21-30]
Fold 3: [Train: 0-30] [Test: 31-40]
Fold 4: [Train: 0-40] [Test: 41-50]
# Реализация
def expanding_window_cv(X, y, window_size=10, test_size=10):
for start in range(len(X) - window_size - test_size):
train_end = start + window_size
test_end = train_end + test_size
X_train = X[start:train_end]
y_train = y[start:train_end]
X_test = X[train_end:test_end]
y_test = y[train_end:test_end]
yield X_train, X_test, y_train, y_test
# Использование
for fold, (X_train, X_test, y_train, y_test) in enumerate(expanding_window_cv(X, y)):
model = LinearRegression()
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
print(f"Fold {fold+1}: Score={score:.4f}")
3. Sliding Window (Скользящее окно)
Оба окна (тренировка и тест) движутся по времени с фиксированным размером.
# Визуально:
Fold 1: [Train: 0-20] [Test: 21-30]
Fold 2: [Train: 11-30] [Test: 31-40]
Fold 3: [Train: 21-40] [Test: 41-50]
# Реализация
def sliding_window_cv(X, y, train_size=20, test_size=10):
for start in range(0, len(X) - train_size - test_size, 5): # шаг=5
train_end = start + train_size
test_end = train_end + test_size
X_train = X[start:train_end]
y_train = y[start:train_end]
X_test = X[train_end:test_end]
y_test = y[train_end:test_end]
yield X_train, X_test, y_train, y_test
# Использование
for fold, (X_train, X_test, y_train, y_test) in enumerate(sliding_window_cv(X, y)):
model = LinearRegression()
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
print(f"Fold {fold+1}: Score={score:.4f}")
4. Backtesting (для финансовых данных)
Специализированный метод для торговых стратегий.
# Визуально:
Fold 1: [Train: 0-252] [Test: 253-281]
Fold 2: [Train: 0-281] [Test: 282-310]
Fold 3: [Train: 0-310] [Test: 311-339]
# где 252 = кол-во торговых дней в году
def backtesting_cv(data, train_years=3, test_months=1):
train_size = train_years * 252 # дней в году
test_size = test_months * 21 # торговых дней в месяце
for start in range(0, len(data) - train_size - test_size, test_size):
train_end = start + train_size
test_end = train_end + test_size
yield (data.iloc[start:train_end], data.iloc[train_end:test_end])
Сравнение методов
| Метод | Применение | Размер Train | Размер Test | Примеры |
|---|---|---|---|---|
| Walk-Forward | Общий случай | Растёт | Фиксирован | ARIMA, XGBoost |
| Expanding | Модели нуждаются больше истории | Растёт максимально | Фиксирован | LSTM, Prophet |
| Sliding | Когда история должна быть ограничена | Фиксирован | Фиксирован | Автотрейдинг |
| Backtesting | Финансовые предсказания | Фиксирован (годы) | Фиксирован (месяцы) | Прогноз акций |
Практический пример с scikit-learn
from sklearn.model_selection import TimeSeriesSplit
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error
import numpy as np
# Данные (100 наблюдений)
X = np.arange(100).reshape(-1, 1)
y = np.sin(X.ravel() / 10) * 10 + np.random.normal(0, 1, 100)
# 5-fold time series cross-validation
tscv = TimeSeriesSplit(n_splits=5)
scores = []
for train_idx, test_idx in tscv.split(X):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
# Обучить модель
model = RandomForestRegressor(n_estimators=10, random_state=42)
model.fit(X_train, y_train)
# Оценить
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
scores.append({'MSE': mse, 'MAE': mae})
print(f"MSE: {mse:.4f}, MAE: {mae:.4f}")
# Средние результаты
avg_mse = np.mean([s['MSE'] for s in scores])
avg_mae = np.mean([s['MAE'] for s in scores])
print(f"\nAverage MSE: {avg_mse:.4f}, Average MAE: {avg_mae:.4f}")
КРИТИЧЕСКАЯ РЕКОМЕНДАЦИЯ
# НЕПРАВИЛЬНО для временных рядов:
from sklearn.model_selection import KFold, cross_val_score
kf = KFold(n_splits=5, shuffle=True) # ✗ shuffle=True нарушает порядок!
# ПРАВИЛЬНО:
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5) # ✓ Соблюдает временной порядок