Skip to content

Commit

Permalink
Official implementation of iTransformer added
Browse files Browse the repository at this point in the history
  • Loading branch information
ZDandsomSP committed Oct 15, 2023
1 parent df10550 commit 096f5d5
Show file tree
Hide file tree
Showing 16 changed files with 1,176 additions and 26 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,4 @@ data_loader_all.py
/scripts/anomaly_detection/tmp/
/scripts/imputation/tmp/
/utils/self_tools.py
/scripts/exp_scripts/
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ We provide a neat code base to evaluate advanced deep time series models or deve

## Leaderboard for Time Series Analysis

Till February 2023, the top three models for five different tasks are:
Till October 2023, the top three models for five different tasks are:

| Model<br>Ranking | Long-term<br>Forecasting | Short-term<br>Forecasting | Imputation | Anomaly<br>Detection | Classification |
| ---------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------------------- |
| 🥇 1st | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) |
| 🥈 2nd | [DLinear](https://github.com/cure-lab/LTSF-Linear) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [FEDformer](https://github.com/MAZiqing/FEDformer) |
| 🥉 3rd | [Non-stationary<br>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [FEDformer](https://github.com/MAZiqing/FEDformer) | [Autoformer](https://github.com/thuml/Autoformer) | [Informer](https://github.com/zhouhaoyi/Informer2020) | [Autoformer](https://github.com/thuml/Autoformer) |
| Model<br>Ranking | Long-term<br>Forecasting | Short-term<br>Forecasting | Imputation | Anomaly<br>Detection | Classification |
| ---------------- |---------------------------------------------------| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------------------- |
| 🥇 1st | [iTransformer](https://arxiv.org/abs/2310.06625) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) | [TimesNet](https://arxiv.org/abs/2210.02186) |
| 🥈 2nd | [PatchTST](https://github.com/yuqinie98/PatchTST) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [Non-stationary<br/>Transformer](https://github.com/thuml/Nonstationary_Transformers) | [FEDformer](https://github.com/MAZiqing/FEDformer) |
| 🥉 3rd | [TimesNet](https://arxiv.org/abs/2210.02186) | [FEDformer](https://github.com/MAZiqing/FEDformer) | [Autoformer](https://github.com/thuml/Autoformer) | [Informer](https://github.com/zhouhaoyi/Informer2020) | [Autoformer](https://github.com/thuml/Autoformer) |

**Note: We will keep updating this leaderborad.** If you have proposed advanced and awesome models, welcome to send your paper/code link to us or raise a pull request. We will add them to this repo and update the leaderborad as soon as possible.

Expand Down
3 changes: 2 additions & 1 deletion exp/exp_basic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import torch
from models import Autoformer, Transformer, TimesNet, Nonstationary_Transformer, DLinear, FEDformer, \
Informer, LightTS, Reformer, ETSformer, Pyraformer, PatchTST, MICN, Crossformer, FiLM
Informer, LightTS, Reformer, ETSformer, Pyraformer, PatchTST, MICN, Crossformer, FiLM, iTransformer


class Exp_Basic(object):
Expand All @@ -23,6 +23,7 @@ def __init__(self, args):
'MICN': MICN,
'Crossformer': Crossformer,
'FiLM': FiLM,
'iTransformer': iTransformer,
}
self.device = self._acquire_device()
self.model = self._build_model().to(self.device)
Expand Down
12 changes: 12 additions & 0 deletions layers/Embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ def forward(self, x, x_mark):
x) + self.temporal_embedding(x_mark) + self.position_embedding(x)
return self.dropout(x)

class DataEmbedding_bnt(nn.Module):
def __init__(self, c_in, d_model, embed_type='fixed', freq='h', dropout=0.1):
super(DataEmbedding_bnt, self).__init__()
self.value_embedding = nn.Linear(c_in, d_model)
self.dropout = nn.Dropout(p=dropout)

def forward(self, x, x_mark):
if x_mark is None:
x = self.value_embedding(x)
else:
x = self.value_embedding(torch.cat([x, x_mark.permute(0,2,1)], 1))
return self.dropout(x)

class DataEmbedding_wo_pos(nn.Module):
def __init__(self, c_in, d_model, embed_type='fixed', freq='h', dropout=0.1):
Expand Down
44 changes: 38 additions & 6 deletions layers/SelfAttention_Family.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
V = torch.einsum("bhls,bshd->blhd", A, values)

if self.output_attention:
return (V.contiguous(), A)
return V.contiguous(), A
else:
return (V.contiguous(), None)
return V.contiguous(), None


class FullAttention(nn.Module):
Expand Down Expand Up @@ -70,9 +70,9 @@ def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
V = torch.einsum("bhls,bshd->blhd", A, values)

if self.output_attention:
return (V.contiguous(), A)
return V.contiguous(), A
else:
return (V.contiguous(), None)
return V.contiguous(), None


class ProbAttention(nn.Module):
Expand Down Expand Up @@ -140,9 +140,9 @@ def _update_context(self, context_in, V, scores, index, L_Q, attn_mask):
L_V).type_as(attn).to(attn.device)
attns[torch.arange(B)[:, None, None], torch.arange(H)[
None, :, None], index, :] = attn
return (context_in, attns)
return context_in, attns
else:
return (context_in, None)
return context_in, None

def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
B, L_Q, H, D = queries.shape
Expand Down Expand Up @@ -176,6 +176,38 @@ def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
return context.contiguous(), attn


class CorrAttention(nn.Module):
def __init__(self, mask_flag=True, factor=5, scale=None, attention_dropout=0.1, output_attention=False):
super(CorrAttention, self).__init__()
self.scale = scale
self.mask_flag = mask_flag
self.output_attention = output_attention
self.dropout = nn.Dropout(attention_dropout)

def forward(self, queries, keys, values, attn_mask, tau=None, delta=None):
B, L, H, E = queries.shape
_, S, _, D = values.shape
scale = self.scale or 1. / sqrt(E)

scores = torch.einsum("blhe,bshe->bhls", queries, keys)
norm_q = torch.sqrt(torch.sum(queries * queries, dim=-1)).unsqueeze(2).repeat(1,1,S,1).permute(0,3,1,2)
norm_k = torch.sqrt(torch.sum(keys * keys, dim=-1)).unsqueeze(1).repeat(1,L,1,1).permute(0,3,1,2)
scores = scores / (norm_k * norm_q + 1e-5)

if self.mask_flag:
if attn_mask is None:
attn_mask = TriangularCausalMask(B, L, device=queries.device)

scores.masked_fill_(attn_mask.mask, -np.inf)

A = self.dropout(torch.softmax(scores, dim=-1))
V = torch.einsum("bhls,bshd->blhd", A, values)

if self.output_attention:
return V.contiguous(), A
else:
return V.contiguous(), None

class AttentionLayer(nn.Module):
def __init__(self, attention, d_model, n_heads, d_keys=None,
d_values=None):
Expand Down
133 changes: 133 additions & 0 deletions models/iTransformer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from layers.Transformer_EncDec import Encoder, EncoderLayer
from layers.SelfAttention_Family import CorrAttention, AttentionLayer
from layers.Embed import DataEmbedding_bnt
import numpy as np


class Model(nn.Module):
"""
Vanilla Transformer
with O(L^2) complexity
Paper link: https://proceedings.neurips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf
"""

def __init__(self, configs):
super(Model, self).__init__()
self.task_name = configs.task_name
self.seq_len = configs.seq_len
self.pred_len = configs.pred_len
self.output_attention = configs.output_attention
# Embedding
self.enc_embedding = DataEmbedding_bnt(configs.seq_len, configs.d_model, configs.embed, configs.freq, configs.dropout)
# Encoder
self.encoder = Encoder(
[
EncoderLayer(
AttentionLayer(
CorrAttention(False, configs.factor, attention_dropout=configs.dropout,
output_attention=configs.output_attention), configs.d_model, configs.n_heads),
configs.d_model,
configs.d_ff,
dropout=configs.dropout,
activation=configs.activation
) for l in range(configs.e_layers)
],
norm_layer=torch.nn.LayerNorm(configs.d_model)
)
# Decoder
if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':
self.projection = nn.Linear(configs.d_model, configs.pred_len, bias=True)
if self.task_name == 'imputation':
self.projection = nn.Linear(configs.d_model, configs.seq_len, bias=True)
if self.task_name == 'anomaly_detection':
self.projection = nn.Linear(configs.d_model, configs.seq_len, bias=True)
if self.task_name == 'classification':
self.act = F.gelu
self.dropout = nn.Dropout(configs.dropout)
self.projection = nn.Linear(configs.d_model * configs.enc_in, configs.num_class)

def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev

_,_,N = x_enc.shape

# Embedding
enc_out = self.enc_embedding(x_enc.permute(0,2,1), x_mark_enc)
enc_out, attns = self.encoder(enc_out, attn_mask=None)

dec_out = self.projection(enc_out).permute(0,2,1)[:,:,:N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, self.pred_len, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, self.pred_len, 1))
return dec_out

def imputation(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev

_,L,N = x_enc.shape

# Embedding
enc_out = self.enc_embedding(x_enc.permute(0,2,1), x_mark_enc)
enc_out, attns = self.encoder(enc_out, attn_mask=None)

dec_out = self.projection(enc_out).permute(0,2,1)[:,:,:N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, L, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, L, 1))
return dec_out

def anomaly_detection(self, x_enc):
# Normalization from Non-stationary Transformer
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
x_enc /= stdev

_,L,N = x_enc.shape

# Embedding
enc_out = self.enc_embedding(x_enc.permute(0,2,1), None)
enc_out, attns = self.encoder(enc_out, attn_mask=None)

dec_out = self.projection(enc_out).permute(0,2,1)[:,:,:N]
# De-Normalization from Non-stationary Transformer
dec_out = dec_out * (stdev[:, 0, :].unsqueeze(1).repeat(1, L, 1))
dec_out = dec_out + (means[:, 0, :].unsqueeze(1).repeat(1, L, 1))
return dec_out
def classification(self, x_enc, x_mark_enc):
# Embedding
enc_out = self.enc_embedding(x_enc.permute(0, 2, 1), None)
enc_out, attns = self.encoder(enc_out, attn_mask=None)

# Output
output = self.act(enc_out) # the output transformer encoder/decoder embeddings don't include non-linearity
output = self.dropout(output)
output = output.reshape(output.shape[0], -1) # (batch_size, c_in * d_model)
output = self.projection(output) # (batch_size, num_classes)
return output

def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':
dec_out = self.forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len:, :] # [B, L, D]
if self.task_name == 'imputation':
dec_out = self.imputation(x_enc, x_mark_enc, x_dec, x_mark_dec, mask)
return dec_out # [B, L, D]
if self.task_name == 'anomaly_detection':
dec_out = self.anomaly_detection(x_enc)
return dec_out # [B, L, D]
if self.task_name == 'classification':
dec_out = self.classification(x_enc, x_mark_enc)
return dec_out # [B, N]
return None
20 changes: 20 additions & 0 deletions scripts/anomaly_detection/MSL/iTransformer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export CUDA_VISIBLE_DEVICES=1

python -u run.py \
--task_name anomaly_detection \
--is_training 1 \
--root_path ./dataset/MSL \
--model_id MSL \
--model iTransformer \
--data MSL \
--features M \
--seq_len 100 \
--pred_len 0 \
--d_model 128 \
--d_ff 128 \
--e_layers 3 \
--enc_in 55 \
--c_out 55 \
--anomaly_ratio 1 \
--batch_size 128 \
--train_epochs 10
Loading

0 comments on commit 096f5d5

Please sign in to comment.