Почему ваши прогнозы временных рядов лгут
Вы обучаете LSTM, Transformer, Chronos на месяцы. Получаете MAE в 0.01 на тестовой выборке. Запускаете в продакшн - и через неделю модель предсказывает рост продаж мороженого в январе. Или падение энергопотребления ночью в дата-центре. Или всплеск трафика сайта в 3 утра.
Традиционные модели временных рядов - это слепые математики. Они видят цифры, но не понимают контекста. Им всё равно, что 1 января - это праздник, а не обычный понедельник. Что всплеск трафика в 3:00 - это не аномалия, а запланированное обновление. Что падение продаж в феврале - не кризис, а сезонность.
Chronos и другие foundation модели для временных рядов работают как универсальные переводчики. Они знают общие правила грамматики, но не понимают местных диалектов. Ваши данные - это диалект.
Retrieval-Augmented Forecasting: когда история повторяется
Представьте врача, который ставит диагноз только по текущим симптомам. Не зная истории болезни пациента. Не видя, как он реагировал на лекарства в прошлом. Не учитывая генетику.
Retrieval для временных рядов - это история болезни ваших данных. Когда нужно спрогнозировать следующий месяц, система ищет в архиве: "А когда у нас были похожие паттерны? Что происходило потом?"
Это не магия. Это RAG-подход, который уже изменил NLP, теперь приходит в анализ временных рядов. И работает на 40% точнее, чем Chronos на задачах с редкими событиями.
Архитектура: как работает поиск паттернов
Система состоит из трёх ключевых компонентов:
- Индексатор временных окон
- Поисковый движок (векторный + ключевой)
- Адаптивная модель прогнозирования
Вот как это работает на практике:
1 Подготовка архива паттернов
Вы берёте исторические данные и нарезаете их на перекрывающиеся окна. Например, для прогноза на 30 дней вперед вы используете окна по 90 дней: 60 дней контекста + 30 дней целевого периода.
Каждое окно превращается в эмбеддинг - векторное представление паттерна. Здесь есть нюанс: нельзя использовать обычные текстовые эмбеддинги. Временные ряды имеют свою геометрию.
import numpy as np
from ts2vec import TS2Vec
# Инициализация модели для эмбеддингов временных рядов
model = TS2Vec(
input_dims=1, # одномерный ряд
output_dims=128, # размерность эмбеддинга
hidden_dims=64
)
# Обучение на исторических данных
model.fit(train_data, verbose=True)
# Создание эмбеддингов для всех окон
embeddings = []
for window in sliding_windows:
emb = model.encode(window.reshape(1, -1, 1))
embeddings.append(emb.flatten())
embeddings = np.array(embeddings)
2 Гибридный поиск похожих паттернов
Когда приходит новый запрос (текущий паттерн), система ищет k наиболее похожих паттернов в архиве. Но не просто по косинусному расстоянию между эмбеддингами.
Вот ошибка, которую делают 90% разработчиков:
# КАК НЕ НАДО ДЕЛАТЬ
from sklearn.metrics.pairwise import cosine_similarity
query_embedding = model.encode(current_window)
similarities = cosine_similarity([query_embedding], embeddings)
top_k_indices = np.argsort(similarities[0])[-k:][::-1]
Почему это плохо? Потому что косинусное расстояние не учитывает временные масштабы, сдвиги и амплитуды. Паттерн "пик + спад" может быть похож на "плато + рост" по эмбеддингам, но это разные сценарии.
Правильный подход - гибридный поиск, как в гибридном поиске для RAG. Комбинируем:
- Векторный поиск по эмбеддингам (семантическое сходство паттернов)
- Метрический поиск по статистическим признакам (среднее, дисперсия, тренд)
- Временные фильтры (сезонность, день недели, праздники)
Гибридный поиск увеличивает точность на 48% по сравнению с чистым векторным поиском. Потому что временные ряды - это не просто последовательности чисел. Это последовательности контекстов.
3 Контекстное прогнозирование
Нашли 10 похожих паттернов. Что дальше? Самый наивный подход - усреднить их продолжения. Работает плохо.
Умный подход - использовать attention механизм, чтобы взвесить вклад каждого исторического паттерна. Как в трансформерах, но с временным контекстом.
import torch
import torch.nn as nn
class TemporalRetrievalPredictor(nn.Module):
def __init__(self, input_dim, hidden_dim):
super().__init__()
self.attention = nn.MultiheadAttention(
embed_dim=input_dim,
num_heads=4,
batch_first=True
)
self.decoder = nn.LSTM(
input_size=input_dim,
hidden_size=hidden_dim,
num_layers=2,
batch_first=True
)
self.forecast_head = nn.Linear(hidden_dim, 1)
def forward(self, current_pattern, retrieved_patterns, retrieved_futures):
# current_pattern: [batch, seq_len, features]
# retrieved_patterns: [batch, k, seq_len, features]
# retrieved_futures: [batch, k, forecast_len, features]
# Вычисляем attention веса между текущим паттерном и найденными
batch_size, k, seq_len, _ = retrieved_patterns.shape
# Преобразуем для attention
query = current_pattern # [batch, seq_len, features]
key = retrieved_patterns.view(batch_size, k*seq_len, -1)
value = retrieved_futures.view(batch_size, k*seq_len, -1)
# Attention механизм
attn_output, attn_weights = self.attention(query, key, value)
# Декодируем с учетом внимания
decoder_output, _ = self.decoder(attn_output)
forecast = self.forecast_head(decoder_output[:, -30:, :]) # прогноз на 30 дней
return forecast, attn_weights
Почему это работает лучше Chronos
Chronos - это foundation модель для временных рядов от Amazon. Обучена на триллионах точек данных. Знает общие закономерности. Но у неё есть фундаментальное ограничение: она не знает ваши данные.
Retrieval-подход знает. Потому что он учится на вашей собственной истории. Вот конкретные преимущества:
| Метод | Точность на редких событиях | Время обучения | Интерпретируемость | Адаптация к изменениям |
|---|---|---|---|---|
| Chronos | Низкая | 0 (pre-trained) | Чёрный ящик | Медленная |
| LSTM/Transformer | Средняя | Часы/дни | Чёрный ящик | Требует переобучения |
| Retrieval+RAG | Высокая (+40%) | Минуты | Прозрачная (видно аналоги) | Мгновенная |
Ключевое преимущество: интерпретируемость. Когда модель говорит "продажи упадут на 30%", вы можете спросить: "Почему?" И система покажет: "Потому что в прошлом году в аналогичных условиях (похожий паттерн + те же внешние факторы) продажи упали на 28%".
Это не магия. Это проверка гипотез на исторических данных.
Реальные кейсы: где retrieval выигрывает у классических подходов
Прогноз энергопотребления дата-центра
Типичная задача: предсказать нагрузку на следующие 24 часа. Классические модели видят только цифры. Они не знают, что:
- Каждую среду в 2:00 запускается бэкап
- В последний день квартала идёт финальный расчёт отчётности
- Когда температура на улице падает ниже -10°C, включается дополнительный обогрев
Retrieval-система находит в истории паттерны "среда + ночь + зимний период" и понимает: будет пик нагрузки в 2:00. Chronos такого не знает. Ему нужно заново учиться на ваших данных.
Прогноз продаж в ритейле
Чёрная пятница. Активация промокода. Изменение цены. Локальный праздник в городе.
Классические модели временных рядов обрабатывают это как шум или аномалии. Retrieval-система ищет в истории: "Когда у нас были похожие события? Как менялись продажи?"
Она находит 5 похожих активаций промокодов за последние 2 года. Видит, что в 4 случаях из 5 был всплеск продаж на 15-20% в первую неделю, затем спад на 5% на второй неделе. И даёт не просто прогноз, а распределённый прогноз с доверительными интервалами.
Технические нюансы реализации
Проблема 1: Масштабирование поиска
У вас 10 лет исторических данных с минутной гранулярностью. Это 5+ миллионов точек. Искать по 5 миллионам векторов в реальном времени - невозможно.
Решение - иерархический поиск, как в Multi-Joint RAG:
- Сначала ищем по грубым эмбеддингам низкой размерности (16-32D)
- Затем уточняем поиск на подмножестве кандидатов
- Используем бинарные индексы для ускорения, как в статье про бинарный индекс
Проблема 2: Дрейф распределения
Ваши данные 2019 года не похожи на данные 2024. Инфляция. Изменение поведения пользователей. Новые продукты.
Решение - временное взвешивание. Более свежие паттерны получают больший вес при поиске. И динамическое обновление эмбеддингов - старые паттерны перекодируются с учётом новых данных.
Проблема 3: Мультимодальность паттернов
Один и тот же временной ряд может содержать разные режимы: рабочие дни, выходные, праздники, аномалии.
Решение - кластеризация паттернов и поиск внутри кластера. Сначала определяем: "К какому режиму относится текущий паттерн?" Затем ищем аналоги внутри этого режима.
from sklearn.cluster import MiniBatchKMeans
# Кластеризуем все исторические паттерны
kmeans = MiniBatchKMeans(n_clusters=50, random_state=42)
cluster_labels = kmeans.fit_predict(embeddings)
# При поиске сначала определяем кластер запроса
query_cluster = kmeans.predict([query_embedding])[0]
# Ищем только внутри кластера
cluster_mask = cluster_labels == query_cluster
cluster_embeddings = embeddings[cluster_mask]
cluster_patterns = historical_patterns[cluster_mask]
# Поиск внутри кластера
similarities = cosine_similarity([query_embedding], cluster_embeddings)
top_k_indices = np.argsort(similarities[0])[-k:][::-1]
Интеграция с внешними контекстами
Самый мощный аспект retrieval-подхода - возможность учитывать не только числовые ряды, но и контекст.
Пример: прогноз продаж мороженого. Числовой ряд показывает сезонность. Но retrieval-система может искать в архиве:
- Паттерны с температурой выше 25°C
- Паттерны в выходные дни
- Паттерны во время школьных каникул
- Паттерны с акциями "вторая порция бесплатно"
Для этого нужно обогащать эмбеддинги временных рядов контекстной информацией. Не просто кодировать значения, а кодировать "значения + контекст".
Технически это делается через мультимодальные эмбеддинги или через конкатенацию признаков перед кодированием.
Ошибки, которые сломают вашу систему
Ошибка 1: Поиск по сырым данным без нормализации. Паттерн с амплитудой 1000 никогда не найдёт паттерн с амплитудой 10, даже если форма одинакова.
Решение: всегда нормализуйте окна перед созданием эмбеддингов. Z-score normalization или min-max scaling в пределах окна.
Ошибка 2: Использование фиксированной длины окна для всех паттернов. Сезонные колебания могут иметь разные периоды.
Решение: мультимасштабные окна. Создавайте эмбеддинги для окон разной длины: 7 дней, 30 дней, 90 дней. При поиске комбинируйте результаты.
Ошибка 3: Игнорирование временных метаданных. Паттерн "понедельник утром" и "воскресенье утром" выглядят одинаково, но это разные сущности.
Решение: добавляйте временные признаки (час, день недели, месяц, праздник) в эмбеддинг. Или используйте отдельные индексы для разных временных контекстов.
Будущее: retrieval как стандарт для временных рядов
Через 2-3 года retrieval-подход станет стандартом для прогнозирования временных рядов. Почему?
Потому что данные становятся сложнее. Потому что контекст становится важнее чисел. Потому что бизнесу нужны не просто прогнозы, а объяснимые прогнозы.
Уже сейчас появляются гибридные архитектуры, которые комбинируют retrieval с foundation моделями типа Chronos. Сначала поиск похожих паттернов. Затем дообучение Chronos на этих паттернах. Результат - точность foundation модели с адаптацией к локальным данным.
Следующий шаг - retrieval для мультимодальных временных рядов. Когда в одном индексе лежат не только числовые последовательности, но и текстовые описания событий, изображения с камер, данные с датчиков. Как в AlphaEarth Foundations, но для бизнес-аналитики.
Главный урок: временные ряды - это не просто числа. Это истории. И чтобы предсказать будущее, нужно понимать прошлое. Не статистически. Контекстуально.
Начинайте с простого: возьмите свои данные, нарежьте на окна, создайте эмбеддинги, попробуйте найти похожие паттерны. Увидите аномалии, которые не замечали. Найдёте закономерности, которые пропускали.
Retrieval - это не замена Chronos. Это его очки. Которые позволяют слепой модели увидеть контекст.