Ответ
Для временных рядов стандартная случайная разбивка данных недопустима, так как нарушает временную зависимость. Необходимо использовать методы, сохраняющие порядок наблюдений.
1. Разделение по времени (Time-based Hold-out) Самый простой метод — выделить последний отрезок ряда в качестве тестовой выборки.
split_index = int(len(series) * 0.8) # 80% на обучение, 20% на тест
train, test = series[:split_index], series[split_index:]
2. Перекрестная проверка с расширяющимся окном (Expanding Window CV)
Обучающая выборка постоянно увеличивается, тестовое окно сдвигается вперед. Реализуется через TimeSeriesSplit в scikit-learn.
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_index, test_index in tscv.split(series):
X_train, X_test = series.iloc[train_index], series.iloc[test_index]
# Обучить модель на X_train, оценить на X_test
3. Перекрестная проверка со скользящим окном (Sliding Window CV) Размер обучающего окна фиксирован, окно "скользит" по ряду. Полезно для моделирования, где актуальна только недавняя история.
def sliding_window_cv(series, train_size, test_size, step=1):
n_splits = (len(series) - train_size - test_size) // step + 1
for i in range(0, n_splits * step, step):
train_end = i + train_size
test_end = train_end + test_size
yield series.iloc[i:train_end], series.iloc[train_end:test_end]
# Пример: окно обучения = 100 точек, окно теста = 20 точек
for train, test in sliding_window_cv(series, train_size=100, test_size=20, step=20):
# ... обучение и оценка
Ключевые принципы:
- Сохранение порядка: Данные для обучения всегда должны предшествовать данным для тестирования.
- Учет сезонности: При разбиении границы фолдов должны проходить между сезонами, а не разрезать их.
- Многократное тестирование: Использование нескольких фолдов дает более надежную оценку устойчивости модели к разным временным периодам.
Ответ 18+ 🔞
Слушай, а вот это реально важный момент, который многие на разрыв жопы забывают. Представь, ты пытаешься предсказать завтрашнюю цену на нефть, а для обучения берёшь данные за 2023 год, а для теста — за 2020-й. Это же ёперный театр! Получится, что твоя модель, по сути, заглядывает в будущее, чтобы угадать прошлое. Пизда рулю такой модели, доверия к ней — ноль ебать. Временные ряды — они как жизнь, идут строго из прошлого в будущее, и нарушать этот порядок — себя же и обманывать.
Так что, чувак, забудь про стандартный train_test_split с shuffle=True. Тут нужны другие подходы, которые временную структуру не ломают.
1. Просто отрежь по времени (Time-based Hold-out) Самый понятный метод, его хоть сейчас в дело. Берёшь ряд, и просто, блядь, отрезаешь последний кусок под тест. Как будто последние 20% времени — это неизвестное будущее, которое надо спрогнозировать.
split_index = int(len(series) * 0.8) # 80% на обучение, 20% на тест
train, test = series[:split_index], series[split_index:]
Всё, просто и без затей. Но есть нюанс: оценка модели будет зависеть только от одного, последнего периода. А если в этот период случилась какая-нибудь ядрёна вошь вроде кризиса? Модель может показать себя хуже некуда, хотя в целом она норм.
2. Кросс-валидация с растущим окном (Expanding Window CV) Вот это уже серьёзнее. Суть: обучающая выборка с каждым новым фолдом как бы "разрастается", поглощая прошлое, а тестовое окошко сдвигается вперёд. В scikit-learn для этого есть готовый инструмент.
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_index, test_index in tscv.split(series):
X_train, X_test = series.iloc[train_index], series.iloc[test_index]
# Обучаешь модель на X_train, оцениваешь на X_test
Это как если бы ты каждый раз пересматривал всю доступную на тот момент историю, чтобы предсказать следующий шаг. Очень естественный подход, особенно для долгосрочных прогнозов.
3. Кросс-валидация со скользящим окном (Sliding Window CV) А это для прагматиков. Тут размер обучающего окна фиксированный. Окно "скользит" по ряду, и ты каждый раз учишься только на последних N наблюдениях. Идеально, если твоя модель должна реагировать на недавние тренды, а древняя история уже не актуальна.
def sliding_window_cv(series, train_size, test_size, step=1):
n_splits = (len(series) - train_size - test_size) // step + 1
for i in range(0, n_splits * step, step):
train_end = i + train_size
test_end = train_end + test_size
yield series.iloc[i:train_end], series.iloc[train_end:test_end]
# Пример: учимся на 100 точках, тестируем на 20, шаг = 20
for train, test in sliding_window_cv(series, train_size=100, test_size=20, step=20):
# ... обучение и оценка
Типа "забудь, что было год назад, смотри на последние три месяца". Часто в трейдинге так делают.
Главные правила, которые в голове держать надо:
- Порядок — святое: Обучение всегда должно быть ДО теста во времени. Никаких скачков туда-сюда. Это аксиома, ёпта.
- Сезонность не рви: Если у тебя данные с сезонностью (продажи мороженого летом), резать надо между сезонами. Нельзя разрезать июнь пополам, это пиздец как неверно. Нужно, чтобы модель училась на полных циклах.
- Тестируй много раз: Один фолд — это как один бросок костей. Сделай несколько фолдов (скользящих или расширяющихся), чтобы понять, как модель ведёт себя на разных временных отрезках. Так надёжнее, волнение ебать меньше будет, когда в продакшн запускать.