Мотивация
Преобразователи стали популярным выбором для прогнозирования временных рядов из-за их способности фиксировать сложные временные отношения. Однако недавние исследования показали, что простые модели глубокой нейронной сети (DNN) превосходят модели Transformer, такие как FEDFormer и Informer, в определенных задачах прогнозирования временных рядов [1]. Я также написал статью, объясняющую результаты эксперимента.
Для решения этой проблемы в недавней статье ВРЕМЕННОЙ РЯД СТОИТ 64 СЛОВ: ДОЛГОСРОЧНОЕ ПРОГНОЗИРОВАНИЕ С ТРАНСФОРМАТОРАМИ [2] была представлена новая архитектура Transformer под названием PatchTST. Модель PatchTST включает в себя две ключевые концепции прогнозирования временных рядов: независимость канала и исправление.
Независимость от канала включает в себя разложение многоканальных последовательностей на отдельные каналы перед вводом в модель, что обеспечивает большую гибкость и масштабируемость при обработке различных типов данных. Патч делит входную последовательность на более мелкие части или патчи, позволяя модели сосредоточиться на локальных шаблонах и корреляциях.
В исходной статье PatchTST авторы оценили эффективность модели в задачах прогнозирования многоканальных временных рядов, где как входные, так и выходные данные являются многоканальными. Чтобы продемонстрировать превосходство PatchTST над другими моделями, авторы сравнили ее производительность с производительностью простой модели DNN, DLinear, которая превзошла модели Transformer в предыдущем исследовании.
Вдохновленный этим, я расширил применение PatchTST до одноканальной задачи прогнозирования временных рядов с многоканальными входными данными и одноканальными выходными данными. В этой статье я рассмотрю архитектуру PatchTST и реализацию независимости каналов и исправлений. Я также расскажу о результатах своих экспериментов и сравню производительность PatchTST с простыми моделями DNN. Мои результаты показывают, что PatchTST превосходит эти модели, еще больше подчеркивая его потенциал для широкого спектра приложений прогнозирования временных рядов.
Ключевые понятия PatchTST
В основном, в модели PatchTST есть две важные концепции: «исправление» и «независимость канала».
Исправление
Исправление — это способ облегчить вычислительную нагрузку, связанную с самостоятельным вниманием. Вместо того, чтобы следить за каждой позицией в последовательности, входная последовательность разбивается на более мелкие подпоследовательности, известные как патчи. Затем между патчами вычисляется самовнимание. Этот подход позволяет модели обрабатывать более длинные последовательности, избегая при этом ограничений памяти и способствуя более быстрому выводу. Кроме того, исправление позволяет захватывать локализованную семантическую информацию, которая не может быть доступна при использовании отдельных токенов ввода.
Независимость канала
Независимость канала, с другой стороны, является свойством модели PatchTST, которое позволяет независимо обрабатывать различные каналы ввода. В традиционных преобразователях для всех каналов используется один и тот же набор весов внимания, что ограничивает способность модели фиксировать детализированную информацию в каждом канале. Напротив, модель PatchTST применяет весовые коэффициенты внимания отдельно к каждому каналу, что позволяет лучше фиксировать уникальные функции и шаблоны в каждом канале.
В совокупности исправление и независимость от каналов делают модель PatchTST мощным инструментом для обработки длинных последовательностей с несколькими каналами, таких как задачи прогнозирования длинных временных рядов. Разделяя входные данные на патчи и обрабатывая каждый канал независимо, модель может эффективно фиксировать сложные шаблоны и взаимосвязи во всей последовательности.
Результаты в оригинальной статье
В исходной статье авторы провели обширный эксперимент для оценки производительности модели PatchTST на нескольких наборах данных. Они экспериментировали с четырьмя шаблонами длин выходных последовательностей и сравнивали результаты с DLinear.
Результаты показали, что модель PatchTST значительно превосходит модель DLinear с точки зрения точности. Кроме того, было обнаружено, что PatchTST превосходит другие модели Transformer. Авторы заметили, что способность модели PatchTST фиксировать долгосрочные зависимости и ее свойство независимости от канала сыграли решающую роль в достижении лучших результатов. Эти результаты показывают, что PatchTST является многообещающей моделью для задач прогнозирования временных рядов, и ее применение может привести к значительному повышению точности прогнозирования.
Мои эксперименты с одним выходным каналом
В этой части я расскажу о своих экспериментах с PatchTST. Для этого я выбрал набор данных ETT [3] и сосредоточился на одном выходном канале, а именно на столбце Температура масла. В отличие от оригинальной статьи, где выходные последовательности были многоканальными, я реконструировал PatchTST для вывода одноканальной последовательности. Реконструкция была простым процессом и включала изменение последнего полносвязного слоя.
# Implementation of the original final layer class Flatten_Head(torch.nn.Module): def __init__(self, n_vars, nf, target_window, head_dropout=0): super().__init__() self.n_vars = n_vars self.flatten = torch.nn.Flatten(start_dim=-2) self.linear = torch.nn.Linear(nf, target_window) self.dropout = torch.nn.Dropout(head_dropout) def forward(self, x): # x: [bs x nvars x d_model x patch_num] x = self.flatten(x) # x: [bs x nvars x d_model *patch_num] x = self.linear(x) # x: [bs x nvars x target_window] x = self.dropout(x) return x # Implementation for single channel output class Flatten_Head_For_Single_Output(torch.nn.Module): def __init__(self, n_vars, nf, target_window, head_dropout=0): super().__init__() self.n_vars = n_vars self.flatten = torch.nn.Flatten(start_dim=-3) self.linear = torch.nn.Linear(nf * n_vars, target_window) self.dropout = torch.nn.Dropout(head_dropout) def forward(self, x): # x: [bs x nvars x d_model x patch_num] x = self.flatten(x) # x: [bs x nvars * d_model * patch_num] x = self.linear(x) # x: [bs x target_window] x = self.dropout(x) return x #Implementation of PatchTST class PatchTST(torch.nn.Module): def __init__(self, c_in, context_window, target_window, patch_len, stride, max_seq_len=1024, n_layers=3, d_model=16, n_heads=4, d_k=None, d_v=None, d_ff=128, attn_dropout=0.0, dropout=0.3, key_padding_mask="auto", padding_var=None, attn_mask=None, res_attention=True, pre_norm=False, store_attn=False, head_dropout = 0.0, padding_patch = "end", revin = True, affine = False, subtract_last = False, verbose=False, target_idx=-1, **kwargs): super().__init__() self.revin = revin if revin: self.revin_layer = RevIN(c_in, affine=affine, subtract_last=subtract_last, target_idx=target_idx) self.patch_len = patch_len self.stride = stride self.padding_patch = padding_patch patch_num = int((context_window - patch_len)/stride + 1) if padding_patch == "end": self.padding_patch_layer = torch.nn.ReplicationPad1d((0, stride)) patch_num += 1 self.backbone = TSTiEncoder(c_in, patch_num=patch_num, patch_len=patch_len, max_seq_len=max_seq_len, n_layers=n_layers, d_model=d_model, n_heads=n_heads, d_k=d_k, d_v=d_v, d_ff=d_ff, attn_dropout=attn_dropout, dropout=dropout, key_padding_mask=key_padding_mask, padding_var=padding_var, attn_mask=attn_mask, res_attention=res_attention, pre_norm=pre_norm, store_attn=store_attn, verbose=verbose, **kwargs) self.head_nf = d_model * patch_num self.n_vars = c_in self.head = Flatten_Head_For_Single_Output(self.n_vars, self.head_nf, target_window, head_dropout=head_dropout) def forward(self, z): # z: [bs x seq_len × nvars] # instance norm if self.revin: z = self.revin_layer(z, "norm") z = z.permute(0,2,1) # z: [bs x nvars × seq_len] # patching if self.padding_patch == "end": z = self.padding_patch_layer(z) z = z.unfold(dimension=-1, size=self.patch_len, step=self.stride) # z: [bs x nvars x patch_num x patch_len] z = z.permute(0,1,3,2) # z: [bs x nvars x patch_len x patch_num] # model z = self.backbone(z) # z: [bs x nvars x d_model x patch_num] z = self.head(z) # z: [bs x target_window] # denorm if self.revin: z = self.revin_layer(z, "denorm") return z
Чтобы оценить производительность одноканальной модели PatchTST, я сравнил ее как с Linear, так и с DLinear, которые реализованы следующим образом.
#Linear model class Linear(torch.nn.Module): def __init__(self, c_in, context_window, target_window): super().__init__() self.c_in = c_in self.context_winsoq = context_window self.target_window = target_window self.flatten = torch.nn.Flatten(start_dim=-2) self.linear = torch.nn.Linear(c_in * context_window, target_window) def forward(self, x): # x: [bs x seq_len × nvars] x = self.flatten(x) # x: [bs x seq_len * nvars] x = self.linear(x) # x: [bs x target_window] return x class moving_avg(torch.nn.Module): def __init__(self, kernel_size, stride): super().__init__() self.kernel_size = kernel_size self.avg = torch.nn.AvgPool1d(kernel_size=kernel_size, stride=stride, padding=0) def forward(self, x): # padding on the both ends of time series front = x[:, 0:1, :].repeat(1, (self.kernel_size - 1) // 2, 1) end = x[:, -1:, :].repeat(1, (self.kernel_size - 1) // 2, 1) x = torch.cat([front, x, end], dim=1) x = self.avg(x.permute(0, 2, 1)) x = x.permute(0, 2, 1) return x class series_decomp(torch.nn.Module): def __init__(self, kernel_size): super().__init__() self.moving_avg = moving_avg(kernel_size, stride=1) def forward(self, x): moving_mean = self.moving_avg(x) res = x - moving_mean return res, moving_mean #DLinear model class DLinear(torch.nn.Module): def __init__(self, c_in, context_window, target_window): super().__init__() # Decompsition Kernel Size kernel_size = 25 self.decompsition = series_decomp(kernel_size) self.flatten_Seasonal = torch.nn.Flatten(start_dim=-2) self.flatten_Trend = torch.nn.Flatten(start_dim=-2) self.Linear_Seasonal = torch.nn.Linear(c_in * context_window, target_window) self.Linear_Trend = torch.nn.Linear(c_in * context_window, target_window) def forward(self, x): # x: [Batch, Input length, Channel] seasonal_init, trend_init = self.decompsition(x) seasonal_init = self.flatten_Seasonal(x) trend_init = self.flatten_Trend(x) seasonal_output = self.Linear_Seasonal(seasonal_init) trend_output = self.Linear_Trend(trend_init) x = seasonal_output + trend_output return x
Хотя я не выполнял обширную настройку гиперпараметров, я искал оптимальную скорость обучения для каждой модели.
Результаты моих экспериментов показали, что даже в задаче временных рядов с одним выходом одноканальная модель PatchTST превосходила модели Linear и DLinear.
Помимо сравнения производительности одноканальных PatchTST, Linear и DLinear в наборе данных ETT, я также проанализировал процесс обучения каждой модели. Используя набор данных для проверки, я построил график точности каждой модели в течение эпох обучения.
График показывает, что PatchTST не только более точен, но и более стабилен в процессе обучения. Это указывает на то, что PatchTST может учиться на данных более эффективно и последовательно, чем Linear и DLinear. В целом, эти результаты показывают, что PatchTST является мощным и надежным инструментом для задач прогнозирования временных рядов, даже при применении к одноканальным последовательностям.
Заключение
В заключение, модель PatchTST предлагает многообещающий подход к задачам прогнозирования временных рядов. Используя концепции исправления и независимости каналов, он достигает лучших результатов, чем простые модели DNN.
В этой статье я рассмотрел ключевые концепции PatchTST и его превосходство в задачах многоканального прогнозирования временных рядов. Кроме того, я представил результаты оригинальной статьи и свои собственные эксперименты с набором данных ETT, демонстрируя эффективность PatchTST даже в задачах временных рядов с одним выходом.
В целом, PatchTST может произвести революцию в прогнозировании временных рядов, и ее следует рассматривать как альтернативу традиционным моделям DNN и другим моделям на основе преобразователей. Дальнейшие исследования и эксперименты могут продолжить изучение его возможностей и приложений в реальных сценариях.
Весь код, использованный в этих экспериментах, показан ниже, и я призываю всех попробовать его.
Рекомендации
[1] А. Цзэн, М. Чен, Л. Чжан и К. Сюй, «Эффективны ли преобразователи для прогнозирования временных рядов?», Материалы конференции AAAI по искусственному интеллекту, 2023 г.
[2] Ю. Ни, Н. Х. Нгуен, П. Синтонг и Дж. Каланьянам, «Временной ряд стоит 64 слов: долгосрочное прогнозирование с помощью преобразователей», в материалах Международной конференции по обучающим представлениям, 2023 г.
[3] Х. Чжоу, С. Чжан, Дж. Пэн, С. Чжан, Дж. Ли, Х. Сюн и В. Чжан, «Информатор: помимо эффективного преобразователя для прогнозирования временных рядов с длинной последовательностью», в материалах 35-я конференция AAAI по искусственному интеллекту, AAAI 2021, виртуальная конференция, том. 35, нет. 12, стр. 11106–11115, 2021.