first commit

This commit is contained in:
gorold
2022-06-20 15:15:36 +08:00
commit dcc5a96275
36 changed files with 2682 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# Comment line immediately above ownership line is reserved for related other information. Please be careful while editing.
#ECCN:Open Source
+105
View File
@@ -0,0 +1,105 @@
# Salesforce Open Source Community Code of Conduct
## About the Code of Conduct
Equality is a core value at Salesforce. We believe a diverse and inclusive
community fosters innovation and creativity, and are committed to building a
culture where everyone feels included.
Salesforce open-source projects are committed to providing a friendly, safe, and
welcoming environment for all, regardless of gender identity and expression,
sexual orientation, disability, physical appearance, body size, ethnicity, nationality,
race, age, religion, level of experience, education, socioeconomic status, or
other similar personal characteristics.
The goal of this code of conduct is to specify a baseline standard of behavior so
that people with different social values and communication styles can work
together effectively, productively, and respectfully in our open source community.
It also establishes a mechanism for reporting issues and resolving conflicts.
All questions and reports of abusive, harassing, or otherwise unacceptable behavior
in a Salesforce open-source project may be reported by contacting the Salesforce
Open Source Conduct Committee at ossconduct@salesforce.com.
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of gender
identity and expression, sexual orientation, disability, physical appearance,
body size, ethnicity, nationality, race, age, religion, level of experience, education,
socioeconomic status, or other similar personal characteristics.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy toward other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Personal attacks, insulting/derogatory comments, or trolling
* Public or private harassment
* Publishing, or threatening to publish, others' private information—such as
a physical or electronic address—without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Advocating for or encouraging any of the above behaviors
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned with this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project email
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the Salesforce Open Source Conduct Committee
at ossconduct@salesforce.com. All complaints will be reviewed and investigated
and will result in a response that is deemed necessary and appropriate to the
circumstances. The committee is obligated to maintain confidentiality with
regard to the reporter of an incident. Further details of specific enforcement
policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership and the Salesforce Open Source Conduct
Committee.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home],
version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html.
It includes adaptions and additions from [Go Community Code of Conduct][golang-coc],
[CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc].
This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us].
[contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/)
[golang-coc]: https://golang.org/conduct
[cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
[microsoft-coc]: https://opensource.microsoft.com/codeofconduct/
[cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/
+12
View File
@@ -0,0 +1,12 @@
Copyright (c) 2022, Salesforce.com, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+33
View File
@@ -0,0 +1,33 @@
# ETSformer: Exponential Smoothing Transformers for Time-series Forecasting
## Requirements
Official PyTorch code repository for the [ETSformer paper](https://arxiv.org/abs/2202.01381). Required dependencies can be installed by:
```bash
pip install -r requirements.txt
```
## Data
* Pre-processed datasets can be downloaded from the following links, [Tsinghua Cloud](https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/) or [Google Drive](https://drive.google.com/drive/folders/1ZOYpTUa82_jCcxIdTmyr0LXQfvaM9vIy?usp=sharing), as obtained from [Autoformer's](https://github.com/thuml/Autoformer) GitHub repository.
* Place the downloaded datasets into the `dataset/` folder, e.g. `dataset/ETT-small/ETTm2.csv`.
## Usage
1. Install Python 3.8, and the required dependencies.
2. Download data as above, and place them in the folder, `dataset/`.
3. Train the model. We provide the experiment scripts of all benchmarks under the folder `./scripts`, e.g. `./scripts/ETTm2.sh`. You might have to change permissions on the script files by running`chmod u+x scripts/*`.
4. The script for grid search is also provided, and can be run by `./grid_search.sh`.
## Acknowledgements
The implementation of ETSformer relies on resources from the following codebases and repositories, we thank the original authors for open-sourcing their work.
* https://github.com/thuml/Autoformer
* https://github.com/zhouhaoyi/Informer2020
## Citation
Please consider citing if you find this code useful to your research.
<pre>@article{woo2022etsformer,
title={ETSformer: Exponential Smoothing Transformers for Time-series Forecasting},
author={Gerald Woo and Chenghao Liu and Doyen Sahoo and Akshat Kumar and Steven C. H. Hoi},
year={2022},
url={https://arxiv.org/abs/2202.01381},
}</pre>
+7
View File
@@ -0,0 +1,7 @@
## Security
Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com)
as soon as it is discovered. This library limits its runtime dependencies in
order to reduce the total cost of ownership as much as can be, but all consumers
should remain vigilant and have their security stakeholders review all third-party
products (3PP) like this one and their dependencies.
View File
+1
View File
@@ -0,0 +1 @@
+51
View File
@@ -0,0 +1,51 @@
from data_provider.data_loader import Dataset_ETT_hour, Dataset_ETT_minute, Dataset_Custom, Dataset_Pred
from torch.utils.data import DataLoader
data_dict = {
'ETTh1': Dataset_ETT_hour,
'ETTh2': Dataset_ETT_hour,
'ETTm1': Dataset_ETT_minute,
'ETTm2': Dataset_ETT_minute,
'custom': Dataset_Custom,
}
def data_provider(args, flag):
Data = data_dict[args.data]
timeenc = 0 if args.embed != 'timeF' else 1
if flag == 'test':
shuffle_flag = False
drop_last = True
batch_size = args.batch_size
freq = args.freq
elif flag == 'pred':
shuffle_flag = False
drop_last = False
batch_size = 1
freq = args.freq
Data = Dataset_Pred
else:
shuffle_flag = True
drop_last = True
batch_size = args.batch_size
freq = args.freq
data_set = Data(
root_path=args.root_path,
data_path=args.data_path,
flag=flag,
size=[args.seq_len, args.label_len, args.pred_len],
features=args.features,
target=args.target,
timeenc=timeenc,
freq=freq
)
print(flag, len(data_set))
data_loader = DataLoader(
data_set,
batch_size=batch_size,
shuffle=shuffle_flag,
num_workers=args.num_workers,
drop_last=drop_last)
return data_set, data_loader
+394
View File
@@ -0,0 +1,394 @@
import os
import numpy as np
import pandas as pd
import os
import torch
from torch.utils.data import Dataset, DataLoader
from sklearn.preprocessing import StandardScaler
from utils.timefeatures import time_features
import warnings
warnings.filterwarnings('ignore')
class Dataset_ETT_hour(Dataset):
def __init__(self, root_path, flag='train', size=None,
features='S', data_path='ETTh1.csv',
target='OT', scale=True, timeenc=0, freq='h'):
# size [seq_len, label_len, pred_len]
# info
if size == None:
self.seq_len = 24 * 4 * 4
self.label_len = 24 * 4
self.pred_len = 24 * 4
else:
self.seq_len = size[0]
self.label_len = size[1]
self.pred_len = size[2]
# init
assert flag in ['train', 'test', 'val']
type_map = {'train': 0, 'val': 1, 'test': 2}
self.set_type = type_map[flag]
self.features = features
self.target = target
self.scale = scale
self.timeenc = timeenc
self.freq = freq
self.root_path = root_path
self.data_path = data_path
self.__read_data__()
def __read_data__(self):
self.scaler = StandardScaler()
df_raw = pd.read_csv(os.path.join(self.root_path,
self.data_path))
border1s = [0, 12 * 30 * 24 - self.seq_len, 12 * 30 * 24 + 4 * 30 * 24 - self.seq_len]
border2s = [12 * 30 * 24, 12 * 30 * 24 + 4 * 30 * 24, 12 * 30 * 24 + 8 * 30 * 24]
border1 = border1s[self.set_type]
border2 = border2s[self.set_type]
if self.features == 'M' or self.features == 'MS':
cols_data = df_raw.columns[1:]
df_data = df_raw[cols_data]
elif self.features == 'S':
df_data = df_raw[[self.target]]
if self.scale:
train_data = df_data[border1s[0]:border2s[0]]
self.scaler.fit(train_data.values)
data = self.scaler.transform(df_data.values)
else:
data = df_data.values
df_stamp = df_raw[['date']][border1:border2]
df_stamp['date'] = pd.to_datetime(df_stamp.date)
if self.timeenc == 0:
df_stamp['month'] = df_stamp.date.apply(lambda row: row.month, 1)
df_stamp['day'] = df_stamp.date.apply(lambda row: row.day, 1)
df_stamp['weekday'] = df_stamp.date.apply(lambda row: row.weekday(), 1)
df_stamp['hour'] = df_stamp.date.apply(lambda row: row.hour, 1)
data_stamp = df_stamp.drop(['date'], 1).values
elif self.timeenc == 1:
data_stamp = time_features(pd.to_datetime(df_stamp['date'].values), freq=self.freq)
data_stamp = data_stamp.transpose(1, 0)
self.data_x = data[border1:border2]
self.data_y = data[border1:border2]
self.data_stamp = data_stamp
def __getitem__(self, index):
s_begin = index
s_end = s_begin + self.seq_len
r_begin = s_end - self.label_len
r_end = r_begin + self.label_len + self.pred_len
seq_x = self.data_x[s_begin:s_end]
seq_y = self.data_y[r_begin:r_end]
seq_x_mark = self.data_stamp[s_begin:s_end]
seq_y_mark = self.data_stamp[r_begin:r_end]
return seq_x, seq_y, seq_x_mark, seq_y_mark
def __len__(self):
return len(self.data_x) - self.seq_len - self.pred_len + 1
def inverse_transform(self, data):
return self.scaler.inverse_transform(data)
class Dataset_ETT_minute(Dataset):
def __init__(self, root_path, flag='train', size=None,
features='S', data_path='ETTm1.csv',
target='OT', scale=True, timeenc=0, freq='t'):
# size [seq_len, label_len, pred_len]
# info
if size == None:
self.seq_len = 24 * 4 * 4
self.label_len = 24 * 4
self.pred_len = 24 * 4
else:
self.seq_len = size[0]
self.label_len = size[1]
self.pred_len = size[2]
# init
assert flag in ['train', 'test', 'val']
type_map = {'train': 0, 'val': 1, 'test': 2}
self.set_type = type_map[flag]
self.features = features
self.target = target
self.scale = scale
self.timeenc = timeenc
self.freq = freq
self.root_path = root_path
self.data_path = data_path
self.__read_data__()
def __read_data__(self):
self.scaler = StandardScaler()
df_raw = pd.read_csv(os.path.join(self.root_path,
self.data_path))
border1s = [0, 12 * 30 * 24 * 4 - self.seq_len, 12 * 30 * 24 * 4 + 4 * 30 * 24 * 4 - self.seq_len]
border2s = [12 * 30 * 24 * 4, 12 * 30 * 24 * 4 + 4 * 30 * 24 * 4, 12 * 30 * 24 * 4 + 8 * 30 * 24 * 4]
border1 = border1s[self.set_type]
border2 = border2s[self.set_type]
if self.features == 'M' or self.features == 'MS':
cols_data = df_raw.columns[1:]
df_data = df_raw[cols_data]
elif self.features == 'S':
df_data = df_raw[[self.target]]
if self.scale:
train_data = df_data[border1s[0]:border2s[0]]
self.scaler.fit(train_data.values)
data = self.scaler.transform(df_data.values)
else:
data = df_data.values
df_stamp = df_raw[['date']][border1:border2]
df_stamp['date'] = pd.to_datetime(df_stamp.date)
if self.timeenc == 0:
df_stamp['month'] = df_stamp.date.apply(lambda row: row.month, 1)
df_stamp['day'] = df_stamp.date.apply(lambda row: row.day, 1)
df_stamp['weekday'] = df_stamp.date.apply(lambda row: row.weekday(), 1)
df_stamp['hour'] = df_stamp.date.apply(lambda row: row.hour, 1)
df_stamp['minute'] = df_stamp.date.apply(lambda row: row.minute, 1)
df_stamp['minute'] = df_stamp.minute.map(lambda x: x // 15)
data_stamp = df_stamp.drop(['date'], 1).values
elif self.timeenc == 1:
data_stamp = time_features(pd.to_datetime(df_stamp['date'].values), freq=self.freq)
data_stamp = data_stamp.transpose(1, 0)
self.data_x = data[border1:border2]
self.data_y = data[border1:border2]
self.data_stamp = data_stamp
def __getitem__(self, index):
s_begin = index
s_end = s_begin + self.seq_len
r_begin = s_end - self.label_len
r_end = r_begin + self.label_len + self.pred_len
seq_x = self.data_x[s_begin:s_end]
seq_y = self.data_y[r_begin:r_end]
seq_x_mark = self.data_stamp[s_begin:s_end]
seq_y_mark = self.data_stamp[r_begin:r_end]
return seq_x, seq_y, seq_x_mark, seq_y_mark
def __len__(self):
return len(self.data_x) - self.seq_len - self.pred_len + 1
def inverse_transform(self, data):
return self.scaler.inverse_transform(data)
class Dataset_Custom(Dataset):
def __init__(self, root_path, flag='train', size=None,
features='S', data_path='ETTh1.csv',
target='OT', scale=True, timeenc=0, freq='h'):
# size [seq_len, label_len, pred_len]
# info
if size == None:
self.seq_len = 24 * 4 * 4
self.label_len = 24 * 4
self.pred_len = 24 * 4
else:
self.seq_len = size[0]
self.label_len = size[1]
self.pred_len = size[2]
# init
assert flag in ['train', 'test', 'val']
type_map = {'train': 0, 'val': 1, 'test': 2}
self.set_type = type_map[flag]
self.features = features
self.target = target
self.scale = scale
self.timeenc = timeenc
self.freq = freq
self.root_path = root_path
self.data_path = data_path
self.__read_data__()
def __read_data__(self):
self.scaler = StandardScaler()
df_raw = pd.read_csv(os.path.join(self.root_path,
self.data_path))
'''
df_raw.columns: ['date', ...(other features), target feature]
'''
cols = list(df_raw.columns)
cols.remove(self.target)
cols.remove('date')
df_raw = df_raw[['date'] + cols + [self.target]]
# print(cols)
num_train = int(len(df_raw) * 0.7)
num_test = int(len(df_raw) * 0.2)
num_vali = len(df_raw) - num_train - num_test
border1s = [0, num_train - self.seq_len, len(df_raw) - num_test - self.seq_len]
border2s = [num_train, num_train + num_vali, len(df_raw)]
border1 = border1s[self.set_type]
border2 = border2s[self.set_type]
if self.features == 'M' or self.features == 'MS':
cols_data = df_raw.columns[1:]
df_data = df_raw[cols_data]
elif self.features == 'S':
df_data = df_raw[[self.target]]
if self.scale:
train_data = df_data[border1s[0]:border2s[0]]
self.scaler.fit(train_data.values)
data = self.scaler.transform(df_data.values)
else:
data = df_data.values
df_stamp = df_raw[['date']][border1:border2]
df_stamp['date'] = pd.to_datetime(df_stamp.date)
if self.timeenc == 0:
df_stamp['month'] = df_stamp.date.apply(lambda row: row.month, 1)
df_stamp['day'] = df_stamp.date.apply(lambda row: row.day, 1)
df_stamp['weekday'] = df_stamp.date.apply(lambda row: row.weekday(), 1)
df_stamp['hour'] = df_stamp.date.apply(lambda row: row.hour, 1)
data_stamp = df_stamp.drop(['date'], 1).values
elif self.timeenc == 1:
data_stamp = time_features(pd.to_datetime(df_stamp['date'].values), freq=self.freq)
data_stamp = data_stamp.transpose(1, 0)
self.data_x = data[border1:border2]
self.data_y = data[border1:border2]
self.data_stamp = data_stamp
def __getitem__(self, index):
s_begin = index
s_end = s_begin + self.seq_len
r_begin = s_end - self.label_len
r_end = r_begin + self.label_len + self.pred_len
seq_x = self.data_x[s_begin:s_end]
seq_y = self.data_y[r_begin:r_end]
seq_x_mark = self.data_stamp[s_begin:s_end]
seq_y_mark = self.data_stamp[r_begin:r_end]
return seq_x, seq_y, seq_x_mark, seq_y_mark
def __len__(self):
return len(self.data_x) - self.seq_len - self.pred_len + 1
def inverse_transform(self, data):
return self.scaler.inverse_transform(data)
class Dataset_Pred(Dataset):
def __init__(self, root_path, flag='pred', size=None,
features='S', data_path='ETTh1.csv',
target='OT', scale=True, inverse=False, timeenc=0, freq='15min', cols=None):
# size [seq_len, label_len, pred_len]
# info
if size == None:
self.seq_len = 24 * 4 * 4
self.label_len = 24 * 4
self.pred_len = 24 * 4
else:
self.seq_len = size[0]
self.label_len = size[1]
self.pred_len = size[2]
# init
assert flag in ['pred']
self.features = features
self.target = target
self.scale = scale
self.inverse = inverse
self.timeenc = timeenc
self.freq = freq
self.cols = cols
self.root_path = root_path
self.data_path = data_path
self.__read_data__()
def __read_data__(self):
self.scaler = StandardScaler()
df_raw = pd.read_csv(os.path.join(self.root_path,
self.data_path))
'''
df_raw.columns: ['date', ...(other features), target feature]
'''
if self.cols:
cols = self.cols.copy()
cols.remove(self.target)
else:
cols = list(df_raw.columns)
cols.remove(self.target)
cols.remove('date')
df_raw = df_raw[['date'] + cols + [self.target]]
border1 = len(df_raw) - self.seq_len
border2 = len(df_raw)
if self.features == 'M' or self.features == 'MS':
cols_data = df_raw.columns[1:]
df_data = df_raw[cols_data]
elif self.features == 'S':
df_data = df_raw[[self.target]]
if self.scale:
self.scaler.fit(df_data.values)
data = self.scaler.transform(df_data.values)
else:
data = df_data.values
tmp_stamp = df_raw[['date']][border1:border2]
tmp_stamp['date'] = pd.to_datetime(tmp_stamp.date)
pred_dates = pd.date_range(tmp_stamp.date.values[-1], periods=self.pred_len + 1, freq=self.freq)
df_stamp = pd.DataFrame(columns=['date'])
df_stamp.date = list(tmp_stamp.date.values) + list(pred_dates[1:])
if self.timeenc == 0:
df_stamp['month'] = df_stamp.date.apply(lambda row: row.month, 1)
df_stamp['day'] = df_stamp.date.apply(lambda row: row.day, 1)
df_stamp['weekday'] = df_stamp.date.apply(lambda row: row.weekday(), 1)
df_stamp['hour'] = df_stamp.date.apply(lambda row: row.hour, 1)
df_stamp['minute'] = df_stamp.date.apply(lambda row: row.minute, 1)
df_stamp['minute'] = df_stamp.minute.map(lambda x: x // 15)
data_stamp = df_stamp.drop(['date'], 1).values
elif self.timeenc == 1:
data_stamp = time_features(pd.to_datetime(df_stamp['date'].values), freq=self.freq)
data_stamp = data_stamp.transpose(1, 0)
self.data_x = data[border1:border2]
if self.inverse:
self.data_y = df_data.values[border1:border2]
else:
self.data_y = data[border1:border2]
self.data_stamp = data_stamp
def __getitem__(self, index):
s_begin = index
s_end = s_begin + self.seq_len
r_begin = s_end - self.label_len
r_end = r_begin + self.label_len + self.pred_len
seq_x = self.data_x[s_begin:s_end]
if self.inverse:
seq_y = self.data_x[r_begin:r_begin + self.label_len]
else:
seq_y = self.data_y[r_begin:r_begin + self.label_len]
seq_x_mark = self.data_stamp[s_begin:s_end]
seq_y_mark = self.data_stamp[r_begin:r_end]
return seq_x, seq_y, seq_x_mark, seq_y_mark
def __len__(self):
return len(self.data_x) - self.seq_len + 1
def inverse_transform(self, data):
return self.scaler.inverse_transform(data)
View File
+37
View File
@@ -0,0 +1,37 @@
import os
import torch
import numpy as np
class Exp_Basic(object):
def __init__(self, args):
self.args = args
self.device = self._acquire_device()
self.model = self._build_model().to(self.device)
def _build_model(self):
raise NotImplementedError
return None
def _acquire_device(self):
if self.args.use_gpu:
os.environ["CUDA_VISIBLE_DEVICES"] = str(
self.args.gpu) if not self.args.use_multi_gpu else self.args.devices
device = torch.device('cuda:{}'.format(self.args.gpu))
print('Use GPU: cuda:{}'.format(self.args.gpu))
else:
device = torch.device('cpu')
print('Use CPU')
return device
def _get_data(self):
pass
def vali(self):
pass
def train(self):
pass
def test(self):
pass
+240
View File
@@ -0,0 +1,240 @@
from models.ETSformer.model import ETSformer
from data_provider.data_factory import data_provider
from exp.exp_basic import Exp_Basic
from utils.tools import EarlyStopping, adjust_learning_rate
from utils.metrics import metric
from utils.Adam import Adam
import numpy as np
import torch
import torch.nn as nn
import os
import time
import warnings
import numpy as np
warnings.filterwarnings('ignore')
class Exp_Main(Exp_Basic):
def __init__(self, args):
super(Exp_Main, self).__init__(args)
def _build_model(self):
model_dict = {
'ETSformer': ETSformer,
}
model = model_dict[self.args.model](self.args).float()
if self.args.use_multi_gpu and self.args.use_gpu:
model = nn.DataParallel(model, device_ids=self.args.device_ids)
return model
def _get_data(self, flag):
data_set, data_loader = data_provider(self.args, flag)
return data_set, data_loader
def _select_optimizer(self):
if 'warmup' in self.args.lradj:
lr = self.args.min_lr
else:
lr = self.args.learning_rate
if self.args.smoothing_learning_rate > 0:
smoothing_lr = self.args.smoothing_learning_rate
else:
smoothing_lr = 100 * self.args.learning_rate
if self.args.damping_learning_rate > 0:
damping_lr = self.args.damping_learning_rate
else:
damping_lr = 100 * self.args.learning_rate
nn_params = []
smoothing_params = []
damping_params = []
for k, v in self.model.named_parameters():
if k[-len('_smoothing_weight'):] == '_smoothing_weight':
smoothing_params.append(v)
elif k[-len('_damping_factor'):] == '_damping_factor':
damping_params.append(v)
else:
nn_params.append(v)
model_optim = Adam([
{'params': nn_params, 'lr': lr, 'name': 'nn'},
{'params': smoothing_params, 'lr': smoothing_lr, 'name': 'smoothing'},
{'params': damping_params, 'lr': damping_lr, 'name': 'damping'},
])
return model_optim
def _select_criterion(self):
criterion = nn.MSELoss()
return criterion
def vali(self, vali_data, vali_loader, criterion):
total_loss = []
self.model.eval()
with torch.no_grad():
for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(vali_loader):
batch_x = batch_x.float().to(self.device)
batch_y = batch_y.float()
batch_x_mark = batch_x_mark.float().to(self.device)
batch_y_mark = batch_y_mark.float().to(self.device)
# decoder input
dec_inp = torch.zeros_like(batch_y[:, -self.args.pred_len:, :]).float()
dec_inp = torch.cat([batch_y[:, :self.args.label_len, :], dec_inp], dim=1).float().to(self.device)
# encoder - decoder
outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)
f_dim = -1 if self.args.features == 'MS' else 0
batch_y = batch_y[:, -self.args.pred_len:, f_dim:].to(self.device)
pred = outputs.detach().cpu()
true = batch_y.detach().cpu()
loss = criterion(pred, true)
total_loss.append(loss)
total_loss = np.average(total_loss)
self.model.train()
return total_loss
def train(self, setting):
train_data, train_loader = self._get_data(flag='train')
vali_data, vali_loader = self._get_data(flag='val')
test_data, test_loader = self._get_data(flag='test')
path = os.path.join(self.args.checkpoints, setting)
if not os.path.exists(path):
os.makedirs(path)
time_now = time.time()
train_steps = len(train_loader)
early_stopping = EarlyStopping(patience=self.args.patience, verbose=True)
model_optim = self._select_optimizer()
criterion = self._select_criterion()
for epoch in range(self.args.train_epochs):
iter_count = 0
train_loss = []
self.model.train()
epoch_time = time.time()
for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(train_loader):
iter_count += 1
model_optim.zero_grad()
batch_x = batch_x.float().to(self.device)
batch_y = batch_y.float().to(self.device)
batch_x_mark = batch_x_mark.float().to(self.device)
batch_y_mark = batch_y_mark.float().to(self.device)
# decoder input
dec_inp = torch.zeros_like(batch_y[:, -self.args.pred_len:, :]).float()
dec_inp = torch.cat([batch_y[:, :self.args.label_len, :], dec_inp], dim=1).float().to(self.device)
# encoder - decoder
outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)
f_dim = -1 if self.args.features == 'MS' else 0
batch_y = batch_y[:, -self.args.pred_len:, f_dim:].to(self.device)
loss = criterion(outputs, batch_y)
train_loss.append(loss.item())
if (i + 1) % 100 == 0:
print("\titers: {0}, epoch: {1} | loss: {2:.7f}".format(i + 1, epoch + 1, loss.item()))
speed = (time.time() - time_now) / iter_count
left_time = speed * ((self.args.train_epochs - epoch) * train_steps - i)
print('\tspeed: {:.4f}s/iter; left time: {:.4f}s'.format(speed, left_time))
iter_count = 0
time_now = time.time()
loss.backward()
torch.nn.utils.clip_grad_norm(self.model.parameters(), 1.0)
model_optim.step()
print("Epoch: {} cost time: {}".format(epoch + 1, time.time() - epoch_time))
train_loss = np.average(train_loss)
vali_loss = self.vali(vali_data, vali_loader, criterion)
test_loss = self.vali(test_data, test_loader, criterion)
print("Epoch: {0}, Steps: {1} | Train Loss: {2:.7f} Vali Loss: {3:.7f} Test Loss: {4:.7f}".format(
epoch + 1, train_steps, train_loss, vali_loss, test_loss))
early_stopping(vali_loss, self.model, path)
if early_stopping.early_stop:
print("Early stopping")
break
adjust_learning_rate(model_optim, epoch + 1, self.args)
best_model_path = path + '/' + 'checkpoint.pth'
self.model.load_state_dict(torch.load(best_model_path))
return self.model
def test(self, setting, data, save_vals=False):
"""data - 'val' or 'test' """
test_data, test_loader = self._get_data(flag=data)
print('loading model')
self.model.load_state_dict(torch.load(os.path.join('./checkpoints/' + setting, 'checkpoint.pth')))
preds = []
trues = []
self.model.eval()
with torch.no_grad():
for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(test_loader):
batch_x = batch_x.float().to(self.device)
batch_y = batch_y.float().to(self.device)
batch_x_mark = batch_x_mark.float().to(self.device)
batch_y_mark = batch_y_mark.float().to(self.device)
# decoder input
dec_inp = torch.zeros_like(batch_y[:, -self.args.pred_len:, :]).float()
dec_inp = torch.cat([batch_y[:, :self.args.label_len, :], dec_inp], dim=1).float().to(self.device)
# encoder - decoder
outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)
f_dim = -1 if self.args.features == 'MS' else 0
outputs = outputs[:, -self.args.pred_len:, f_dim:]
batch_y = batch_y[:, -self.args.pred_len:, f_dim:].to(self.device)
outputs = outputs.detach().cpu().numpy()
batch_y = batch_y.detach().cpu().numpy()
pred = outputs # outputs.detach().cpu().numpy() # .squeeze()
true = batch_y # batch_y.detach().cpu().numpy() # .squeeze()
preds.append(pred)
trues.append(true)
preds = np.array(preds)
trues = np.array(trues)
print('test shape:', preds.shape, trues.shape)
preds = preds.reshape(-1, preds.shape[-2], preds.shape[-1])
trues = trues.reshape(-1, trues.shape[-2], trues.shape[-1])
print('test shape:', preds.shape, trues.shape)
# result save
folder_path = './results/' + setting + '/'
if not os.path.exists(folder_path):
os.makedirs(folder_path)
mae, mse, rmse, mape, mspe = metric(preds, trues)
print('mse:{}, mae:{}'.format(mse, mae))
np.save(folder_path + f'{data}_metrics.npy', np.array([mae, mse, rmse, mape, mspe]))
if save_vals:
np.save(folder_path + 'pred.npy', preds)
np.save(folder_path + 'true.npy', trues)
return
View File
+74
View File
@@ -0,0 +1,74 @@
import torch
import torch.nn as nn
from einops import rearrange, reduce, repeat
class DampingLayer(nn.Module):
def __init__(self, d_model, pred_len, nhead, dropout=0.1):
super().__init__()
self.pred_len = pred_len
self.nhead = nhead
self._damping_factor = nn.Parameter(torch.randn(1, nhead))
self.dropout = nn.Dropout(dropout)
def forward(self, x):
x = repeat(x, 'B () D -> B T D', T=self.pred_len)
B, T, D = x.shape
powers = torch.arange(self.pred_len).to(self._damping_factor.device) + 1
powers = powers.view(self.pred_len, 1)
damping_factors = self.damping_factor ** powers
damping_factors = damping_factors.cumsum(dim=0)
x = x.view(B, T, self.nhead, -1)
x = self.dropout(x) * damping_factors.unsqueeze(-1)
return x.view(B, T, D)
@property
def damping_factor(self):
return torch.sigmoid(self._damping_factor)
class DecoderLayer(nn.Module):
def __init__(self, d_model, nhead, c_out, pred_len, dropout=0.1):
super().__init__()
self.d_model = d_model
self.nhead = nhead
self.c_out = c_out
self.pred_len = pred_len
self.growth_damping = DampingLayer(d_model, pred_len, nhead, dropout=dropout)
self.dropout1 = nn.Dropout(dropout)
def forward(self, growth, season):
growth_horizon = self.growth_damping(growth[:, -1:])
growth_horizon = self.dropout1(growth_horizon)
seasonal_horizon = season[:, -self.pred_len:]
return growth_horizon, seasonal_horizon
class Decoder(nn.Module):
def __init__(self, layers):
super().__init__()
self.d_model = layers[0].d_model
self.c_out = layers[0].c_out
self.pred_len = layers[0].pred_len
self.nhead = layers[0].nhead
self.layers = nn.ModuleList(layers)
self.pred = nn.Linear(self.d_model, self.c_out)
def forward(self, growths, seasons):
growth_repr = []
season_repr = []
for idx, layer in enumerate(self.layers):
growth_horizon, season_horizon = layer(growths[idx], seasons[idx])
growth_repr.append(growth_horizon)
season_repr.append(season_horizon)
growth_repr = sum(growth_repr)
season_repr = sum(season_repr)
return self.pred(growth_repr), self.pred(season_repr)
+172
View File
@@ -0,0 +1,172 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.fft as fft
from einops import rearrange, reduce, repeat
import math, random
from .modules import Feedforward
from .exponential_smoothing import ExponentialSmoothing
class GrowthLayer(nn.Module):
def __init__(self, d_model, nhead, d_head=None, dropout=0.1):
super().__init__()
self.d_head = d_head or (d_model // nhead)
self.d_model = d_model
self.nhead = nhead
self.z0 = nn.Parameter(torch.randn(self.nhead, self.d_head))
self.in_proj = nn.Linear(self.d_model, self.d_head * self.nhead)
self.es = ExponentialSmoothing(self.d_head, self.nhead, dropout=dropout)
self.out_proj = nn.Linear(self.d_head * self.nhead, self.d_model)
assert self.d_head * self.nhead == self.d_model, "d_model must be divisible by nhead"
def forward(self, inputs):
"""
:param inputs: shape: (batch, seq_len, dim)
:return: shape: (batch, seq_len, dim)
"""
B, T, D = inputs.shape
values = self.in_proj(inputs).view(B, T, self.nhead, -1)
values = torch.cat([repeat(self.z0, 'H D -> B () H D', B=B), values], dim=1)
values = values[:, 1:] - values[:, :-1]
out = self.es(values)
out = torch.cat([repeat(self.es.v0, 'H D -> B () H D', B=B), out], dim=1)
out = rearrange(out, 'B T H D -> B T (H D)')
return self.out_proj(out)
class FourierLayer(nn.Module):
def __init__(self, d_model, pred_len, k=None, low_freq=1):
super().__init__()
self.d_model = d_model
self.pred_len = pred_len
self.k = k
self.low_freq = low_freq
def forward(self, x):
"""x: (B T D)"""
B, T, D = x.shape
x_freq = fft.rfft(x, dim=1)
if T % 2 == 0:
x_freq = x_freq[:, self.low_freq:-1]
f = fft.rfftfreq(T)[self.low_freq:-1]
else:
x_freq = x_freq[:, self.low_freq:]
f = fft.rfftfreq(T)[self.low_freq:]
x_freq, index_tuple = self.topk_freq(x_freq)
f = repeat(f, 'F -> B F D', B=x_freq.size(0), D=x_freq.size(2))
f = rearrange(f[index_tuple], 'B F D -> B F () D').to(x_freq.device)
return self.extrapolate(x_freq, f, T)
def extrapolate(self, x_freq, f, T):
x_freq = torch.cat([x_freq, x_freq.conj()], dim=1)
f = torch.cat([f, -f], dim=1)
t = rearrange(torch.arange(T + self.pred_len, dtype=torch.float),
'T -> () () T ()').to(x_freq.device)
amp = rearrange(x_freq.abs() / T, 'B F D -> B F () D')
phase = rearrange(x_freq.angle(), 'B F D -> B F () D')
x_time = amp * torch.cos(2 * math.pi * f * t + phase)
return reduce(x_time, 'B F T D -> B T D', 'sum')
def topk_freq(self, x_freq):
values, indices = torch.topk(x_freq.abs(), self.k, dim=1, largest=True, sorted=True)
mesh_a, mesh_b = torch.meshgrid(torch.arange(x_freq.size(0)), torch.arange(x_freq.size(2)))
index_tuple = (mesh_a.unsqueeze(1), indices, mesh_b.unsqueeze(1))
x_freq = x_freq[index_tuple]
return x_freq, index_tuple
class LevelLayer(nn.Module):
def __init__(self, d_model, c_out, dropout=0.1):
super().__init__()
self.d_model = d_model
self.c_out = c_out
self.es = ExponentialSmoothing(1, self.c_out, dropout=dropout, aux=True)
self.growth_pred = nn.Linear(self.d_model, self.c_out)
self.season_pred = nn.Linear(self.d_model, self.c_out)
def forward(self, level, growth, season):
B, T, _ = level.shape
growth = self.growth_pred(growth).view(B, T, self.c_out, 1)
season = self.season_pred(season).view(B, T, self.c_out, 1)
growth = growth.view(B, T, self.c_out, 1)
season = season.view(B, T, self.c_out, 1)
level = level.view(B, T, self.c_out, 1)
out = self.es(level - season, aux_values=growth)
out = rearrange(out, 'B T H D -> B T (H D)')
return out
class EncoderLayer(nn.Module):
def __init__(self, d_model, nhead, c_out, pred_len, k, dim_feedforward=None, dropout=0.1,
activation='sigmoid', layer_norm_eps=1e-5):
super().__init__()
self.d_model = d_model
self.nhead = nhead
self.c_out = c_out
self.pred_len = pred_len
dim_feedforward = dim_feedforward or 4 * d_model
self.dim_feedforward = dim_feedforward
self.growth_layer = GrowthLayer(d_model, nhead, dropout=dropout)
self.seasonal_layer = FourierLayer(d_model, pred_len, k=k)
self.level_layer = LevelLayer(d_model, c_out, dropout=dropout)
# Implementation of Feedforward model
self.ff = Feedforward(d_model, dim_feedforward, dropout=dropout, activation=activation)
self.norm1 = nn.LayerNorm(d_model, eps=layer_norm_eps)
self.norm2 = nn.LayerNorm(d_model, eps=layer_norm_eps)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
def forward(self, res, level, attn_mask=None):
season = self._season_block(res)
res = res - season[:, :-self.pred_len]
growth = self._growth_block(res)
res = self.norm1(res - growth[:, 1:])
res = self.norm2(res + self.ff(res))
level = self.level_layer(level, growth[:, :-1], season[:, :-self.pred_len])
return res, level, growth, season
def _growth_block(self, x):
x = self.growth_layer(x)
return self.dropout1(x)
def _season_block(self, x):
x = self.seasonal_layer(x)
return self.dropout2(x)
class Encoder(nn.Module):
def __init__(self, layers):
super().__init__()
self.layers = nn.ModuleList(layers)
def forward(self, res, level, attn_mask=None):
growths = []
seasons = []
for layer in self.layers:
res, level, growth, season = layer(res, level, attn_mask=None)
growths.append(growth)
seasons.append(season)
return level, growths, seasons
+67
View File
@@ -0,0 +1,67 @@
import torch
import torch.nn as nn
import torch.fft as fft
from einops import rearrange, reduce, repeat
from scipy.fftpack import next_fast_len
def conv1d_fft(f, g, dim=-1):
N = f.size(dim)
M = g.size(dim)
fast_len = next_fast_len(N + M - 1)
F_f = fft.rfft(f, fast_len, dim=dim)
F_g = fft.rfft(g, fast_len, dim=dim)
F_fg = F_f * F_g.conj()
out = fft.irfft(F_fg, fast_len, dim=dim)
out = out.roll((-1,), dims=(dim,))
idx = torch.as_tensor(range(fast_len - N, fast_len)).to(out.device)
out = out.index_select(dim, idx)
return out
class ExponentialSmoothing(nn.Module):
def __init__(self, dim, nhead, dropout=0.1, aux=False):
super().__init__()
self._smoothing_weight = nn.Parameter(torch.randn(nhead, 1))
self.v0 = nn.Parameter(torch.randn(nhead, dim))
self.dropout = nn.Dropout(dropout)
if aux:
self.aux_dropout = nn.Dropout(dropout)
def forward(self, values, aux_values=None):
B, T, H, D = values.shape
init_weight, weight = self.get_exponential_weight(T)
output = conv1d_fft(self.dropout(values), weight, dim=1)
v0 = repeat(self.v0, 'H D -> () () H D')
output = init_weight * v0 + output
if aux_values is not None:
aux_weight = weight / (1 - self.weight) * self.weight
aux_output = conv1d_fft(self.aux_dropout(aux_values), aux_weight)
output = output + aux_output
return output
def get_exponential_weight(self, T):
# Generate array [0, 1, ..., T-1]
powers = torch.arange(T, dtype=torch.float, device=self.weight.device)
# (1 - \alpha) * \alpha^t, for all t = T-1, T-2, ..., 0]
weight = (1 - self.weight) * (self.weight ** torch.flip(powers, dims=(0,)))
# \alpha^t for all t = 1, 2, ..., T
init_weight = self.weight ** (powers + 1)
return rearrange(init_weight, 'H T -> () T H ()'), \
rearrange(weight, 'H T -> () T H ()')
@property
def weight(self):
return torch.sigmoid(self._smoothing_weight)
+76
View File
@@ -0,0 +1,76 @@
import torch
import torch.nn as nn
from .modules import ETSEmbedding
from .encoder import EncoderLayer, Encoder
from .decoder import DecoderLayer, Decoder
class Transform:
def __init__(self, sigma):
self.sigma = sigma
@torch.no_grad()
def transform(self, x):
return self.jitter(self.shift(self.scale(x)))
def jitter(self, x):
return x + (torch.randn(x.shape).to(x.device) * self.sigma)
def scale(self, x):
return x * (torch.randn(x.size(-1)).to(x.device) * self.sigma + 1)
def shift(self, x):
return x + (torch.randn(x.size(-1)).to(x.device) * self.sigma)
class ETSformer(nn.Module):
def __init__(self, configs):
super().__init__()
self.seq_len = configs.seq_len
self.label_len = configs.label_len
self.pred_len = configs.pred_len
self.configs = configs
assert configs.d_layers == configs.e_layers
# Embedding
self.enc_embedding = ETSEmbedding(configs.enc_in, configs.d_model, dropout=configs.dropout)
# Encoder
self.encoder = Encoder(
[
EncoderLayer(
configs.d_model, configs.n_heads, configs.c_out, configs.pred_len, configs.K,
dim_feedforward=configs.d_ff,
dropout=configs.dropout,
activation=configs.activation,
) for _ in range(configs.e_layers)
]
)
# Decoder
self.decoder = Decoder(
[
DecoderLayer(
configs.d_model, configs.n_heads, configs.c_out, configs.pred_len,
dropout=configs.dropout,
) for _ in range(configs.d_layers)
],
)
self.transform = Transform(sigma=self.configs.std)
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec,
enc_self_mask=None, dec_self_mask=None, dec_enc_mask=None):
with torch.no_grad():
if self.training:
x_enc = self.transform.transform(x_enc)
res = self.enc_embedding(x_enc)
level, growths, seasons = self.encoder(res, x_enc, attn_mask=enc_self_mask)
growth, season = self.decoder(growths, seasons)
preds = level[:, -1:] + growth + season
return preds
+30
View File
@@ -0,0 +1,30 @@
import torch.nn as nn
import torch.nn.functional as F
class ETSEmbedding(nn.Module):
def __init__(self, c_in, d_model, dropout=0.1):
super().__init__()
self.conv = nn.Conv1d(in_channels=c_in, out_channels=d_model,
kernel_size=3, padding=2, bias=False)
self.dropout = nn.Dropout(p=dropout)
nn.init.kaiming_normal_(self.conv.weight)
def forward(self, x,):
x = self.conv(x.permute(0,2,1))[..., :-2]
return self.dropout(x.transpose(1,2))
class Feedforward(nn.Module):
def __init__(self, d_model, dim_feedforward, dropout=0.1, activation='sigmoid'):
# Implementation of Feedforward model
super().__init__()
self.linear1 = nn.Linear(d_model, dim_feedforward, bias=False)
self.dropout1 = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model, bias=False)
self.dropout2 = nn.Dropout(dropout)
self.activation = getattr(F, activation)
def forward(self, x):
x = self.linear2(self.dropout1(self.activation(self.linear1(x))))
return self.dropout2(x)
View File
+8
View File
@@ -0,0 +1,8 @@
matplotlib==3.5.1
numpy ==1.19.2
pandas==1.4.2
scikit-learn==1.0.2
scipy==1.7.3
torch==1.11.0
tqdm==4.62.3
einops==0.4.1
+123
View File
@@ -0,0 +1,123 @@
import argparse
import os
import torch
from exp.exp_main import Exp_Main
import random
import numpy as np
def set_seed(seed):
random.seed(seed)
seed += 1
np.random.seed(seed)
seed += 1
torch.manual_seed(seed)
parser = argparse.ArgumentParser(description='ETSformer: Exponential Smoothing Transformers for Time-series Forecasting')
# basic config
parser.add_argument('--model_id', type=str, required=True, default='test', help='model id')
parser.add_argument('--model', type=str, required=True, default='ETSformer',
help='model name, options: [ETSformer]')
# data loader
parser.add_argument('--data', type=str, required=True, default='ETTm1', help='dataset type')
parser.add_argument('--root_path', type=str, default='./data/ETT/', help='root path of the data file')
parser.add_argument('--data_path', type=str, default='ETTh1.csv', help='data file')
parser.add_argument('--features', type=str, default='M',
help='forecasting task, options:[M, S, MS]; M:multivariate predict multivariate, S:univariate predict univariate, MS:multivariate predict univariate')
parser.add_argument('--target', type=str, default='OT', help='target feature in S or MS task')
parser.add_argument('--freq', type=str, default='h',
help='freq for time features encoding, options:[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly], you can also use more detailed freq like 15min or 3h')
parser.add_argument('--checkpoints', type=str, default='./checkpoints/', help='location of model checkpoints')
# forecasting task
parser.add_argument('--seq_len', type=int, required=True, help='input sequence length')
parser.add_argument('--label_len', type=int, default=0, help='start token length')
parser.add_argument('--pred_len', type=int, required=True, help='prediction sequence length')
# model define
parser.add_argument('--enc_in', type=int, default=7, help='encoder input size')
parser.add_argument('--dec_in', type=int, default=7, help='decoder input size')
parser.add_argument('--c_out', type=int, default=7, help='output size')
parser.add_argument('--d_model', type=int, default=512, help='dimension of model')
parser.add_argument('--n_heads', type=int, default=8, help='num of heads')
parser.add_argument('--e_layers', type=int, default=2, help='num of encoder layers')
parser.add_argument('--d_layers', type=int, default=1, help='num of decoder layers')
parser.add_argument('--d_ff', type=int, default=2048, help='dimension of fcn')
parser.add_argument('--K', type=int, default=1, help='Top-K Fourier bases')
parser.add_argument('--dropout', type=float, default=0.2, help='dropout')
parser.add_argument('--embed', type=str, default='timeF',
help='time features encoding, options:[timeF, fixed, learned]')
parser.add_argument('--activation', type=str, default='sigmoid', help='activation')
parser.add_argument('--min_lr', type=float, default=1e-30)
parser.add_argument('--warmup_epochs', type=int, default=3)
parser.add_argument('--std', type=float, default=0.2)
parser.add_argument('--smoothing_learning_rate', type=float, default=0, help='optimizer learning rate')
parser.add_argument('--damping_learning_rate', type=float, default=0, help='optimizer learning rate')
# optimization
parser.add_argument('--optim', type=str, default='adam', help='optimizer')
parser.add_argument('--num_workers', type=int, default=10, help='data loader num workers')
parser.add_argument('--itr', type=int, default=1, help='experiments times')
parser.add_argument('--train_epochs', type=int, default=15, help='train epochs')
parser.add_argument('--batch_size', type=int, default=32, help='batch size of train input data')
parser.add_argument('--patience', type=int, default=5, help='early stopping patience')
parser.add_argument('--learning_rate', type=float, default=1e-4, help='optimizer learning rate')
parser.add_argument('--des', type=str, default='test', help='exp description')
parser.add_argument('--lradj', type=str, default='exponential_with_warmup', help='adjust learning rate')
# GPU
parser.add_argument('--use_gpu', type=bool, default=True, help='use gpu')
parser.add_argument('--gpu', type=int, default=0, help='gpu')
parser.add_argument('--use_multi_gpu', action='store_true', help='use multiple gpus', default=False)
parser.add_argument('--devices', type=str, default='0,1,2,3', help='device ids of multile gpus')
args = parser.parse_args()
args.use_gpu = True if torch.cuda.is_available() and args.use_gpu else False
if args.use_gpu and args.use_multi_gpu:
args.dvices = args.devices.replace(' ', '')
device_ids = args.devices.split(',')
args.device_ids = [int(id_) for id_ in device_ids]
args.gpu = args.device_ids[0]
print('Args in experiment:')
print(args)
Exp = Exp_Main
for ii in range(args.itr):
set_seed(ii)
# setting record of experiments
setting = '{}_{}_{}_ft{}_sl{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_K{}_lr{}_{}_{}'.format(
args.model_id,
args.model,
args.data,
args.features,
args.seq_len,
args.pred_len,
args.d_model,
args.n_heads,
args.e_layers,
args.d_layers,
args.d_ff,
args.K,
args.learning_rate,
args.des, ii)
if os.path.exists(os.path.join(args.checkpoints, setting)):
continue
exp = Exp(args) # set experiments
print('>>>>>>>start training : {}>>>>>>>>>>>>>>>>>>>>>>>>>>'.format(setting))
exp.train(setting)
print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
exp.test(setting, data='val')
exp.test(setting, data='test')
torch.cuda.empty_cache()
+77
View File
@@ -0,0 +1,77 @@
python -u run.py \
--root_path ./dataset/electricity/ \
--data_path electricity.csv \
--model_id ECL \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 321 \
--dec_in 321 \
--c_out 321 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--root_path ./dataset/electricity/ \
--data_path electricity.csv \
--model_id ECL \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 321 \
--dec_in 321 \
--c_out 321 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--is_training 1 \
--root_path ./dataset/electricity/ \
--data_path electricity.csv \
--model_id ECL \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 321 \
--dec_in 321 \
--c_out 321 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--is_training 1 \
--root_path ./dataset/electricity/ \
--data_path electricity.csv \
--model_id ECL \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 321 \
--dec_in 321 \
--c_out 321 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-4 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features M \
--seq_len 96 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-5 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features M \
--seq_len 96 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-5 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features M \
--seq_len 96 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-5 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features M \
--seq_len 96 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-5 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features S \
--seq_len 96 \
--pred_len $96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 2 \
--learning_rate 1e-5 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features S \
--seq_len 96 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 2 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features S \
--seq_len 96 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features S \
--seq_len 720 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 8 \
--dec_in 8 \
--c_out 8 \
--des 'Exp' \
--K 0 \
--learning_rate 3e-5 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features M \
--seq_len 720 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 8 \
--dec_in 8 \
--c_out 8 \
--des 'Exp' \
--K 0 \
--learning_rate 1e-4 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 8 \
--dec_in 8 \
--c_out 8 \
--des 'Exp' \
--K 0 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features M \
--seq_len 192 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 8 \
--dec_in 8 \
--c_out 8 \
--des 'Exp' \
--K 0 \
--learning_rate 1e-3 \
--itr 1
+76
View File
@@ -0,0 +1,76 @@
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features S \
--seq_len 96 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 0 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features S \
--seq_len 96 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 0 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features S \
--seq_len 192 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 1 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features S \
--seq_len 720 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K 1 \
--learning_rate 3e-5 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili \
--model ETSformer \
--data custom \
--features M \
--seq_len 48 \
--pred_len 24 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili \
--model ETSformer \
--data custom \
--features M \
--seq_len 60 \
--pred_len 36 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili \
--model ETSformer \
--data custom \
--features M \
--seq_len 60 \
--pred_len 48 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili \
--model ETSformer \
--data custom \
--features M \
--seq_len 60 \
--pred_len 60 \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/traffic/ \
--data_path traffic.csv \
--model_id traffic \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 862 \
--dec_in 862 \
--c_out 862 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/traffic/ \
--data_path traffic.csv \
--model_id traffic \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 862 \
--dec_in 862 \
--c_out 862 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/traffic/ \
--data_path traffic.csv \
--model_id traffic \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 862 \
--dec_in 862 \
--c_out 862 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/traffic/ \
--data_path traffic.csv \
--model_id traffic \
--model ETSformer \
--data custom \
--features M \
--seq_len 336 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 862 \
--dec_in 862 \
--c_out 862 \
--des 'Exp' \
--K 3 \
--learning_rate 1e-3 \
--itr 1
+75
View File
@@ -0,0 +1,75 @@
python -u run.py \
--root_path ./dataset/weather/ \
--data_path weather.csv \
--model_id weather \
--model ETSformer \
--data custom \
--features M \
--seq_len 192 \
--pred_len 96 \
--e_layers 2 \
--d_layers 2 \
--enc_in 21 \
--dec_in 21 \
--c_out 21 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/weather/ \
--data_path weather.csv \
--model_id weather \
--model ETSformer \
--data custom \
--features M \
--seq_len 192 \
--pred_len 192 \
--e_layers 2 \
--d_layers 2 \
--enc_in 21 \
--dec_in 21 \
--c_out 21 \
--des 'Exp' \
--K 1 \
--learning_rate 1e-3 \
--itr 1
python -u run.py \
--root_path ./dataset/weather/ \
--data_path weather.csv \
--model_id weather \
--model ETSformer \
--data custom \
--features M \
--seq_len 720 \
--pred_len 336 \
--e_layers 2 \
--d_layers 2 \
--enc_in 21 \
--dec_in 21 \
--c_out 21 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
python -u run.py \
--root_path ./dataset/weather/ \
--data_path weather.csv \
--model_id weather \
--model ETSformer \
--data custom \
--features M \
--seq_len 720 \
--pred_len 720 \
--e_layers 2 \
--d_layers 2 \
--enc_in 21 \
--dec_in 21 \
--c_out 21 \
--des 'Exp' \
--K 3 \
--learning_rate 3e-4 \
--itr 1
+171
View File
@@ -0,0 +1,171 @@
for lr in 1e-5 3e-5 1e-4 3e-4 1e-3; do
for k in 0 1 2 3; do
for pl in 96 192 336 720; do
for sl in 96 192 336 720; do
# ETTm2
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# ETTm2 univar
python -u run.py \
--root_path ./dataset/ETT-small/ \
--data_path ETTm2.csv \
--model_id ETTm2 \
--model ETSformer \
--data ETTm2 \
--features S \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# ECL
python -u run.py \
--root_path ./dataset/electricity/ \
--data_path electricity.csv \
--model_id ECL \
--model ETSformer \
--data custom \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 321 \
--dec_in 321 \
--c_out 321 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# Traffic
python -u run.py \
--root_path ./dataset/traffic/ \
--data_path traffic.csv \
--model_id traffic \
--model ETSformer \
--data custom \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 862 \
--dec_in 862 \
--c_out 862 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# Weather
python -u run.py \
--root_path ./dataset/weather/ \
--data_path weather.csv \
--model_id weather \
--model ETSformer \
--data custom \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 21 \
--dec_in 21 \
--c_out 21 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# Exchange
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 8 \
--dec_in 8 \
--c_out 8 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
# Exchange univar
python -u run.py \
--root_path ./dataset/exchange_rate/ \
--data_path exchange_rate.csv \
--model_id Exchange \
--model ETSformer \
--data custom \
--features S \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 1 \
--dec_in 1 \
--c_out 1 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
done
done
# ILI
for pl in 24 36 48 60; do
for sl in 24 36 48 60; do
python -u run.py \
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili \
--model ETSformer \
--data custom \
--features M \
--seq_len ${sl} \
--pred_len ${pl} \
--e_layers 2 \
--d_layers 2 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des 'Exp' \
--K ${k} \
--learning_rate ${lr} \
--itr 3
done
done
done
done
+163
View File
@@ -0,0 +1,163 @@
import math
import torch
from torch import Tensor
from typing import List, Optional
from torch.optim.optimizer import Optimizer
def adam(params: List[Tensor],
grads: List[Tensor],
exp_avgs: List[Tensor],
exp_avg_sqs: List[Tensor],
max_exp_avg_sqs: List[Tensor],
state_steps: List[int],
*,
amsgrad: bool,
beta1: float,
beta2: float,
lr: float,
weight_decay: float,
eps: float):
r"""Functional API that performs Adam algorithm computation.
See :class:`~torch.optim.Adam` for details.
"""
for i, param in enumerate(params):
grad = grads[i]
exp_avg = exp_avgs[i]
exp_avg_sq = exp_avg_sqs[i]
step = state_steps[i]
bias_correction1 = 1 - beta1 ** step
bias_correction2 = 1 - beta2 ** step
if weight_decay != 0:
grad = grad.add(param, alpha=weight_decay)
# Decay the first and second moment running average coefficient
exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
exp_avg_sq.mul_(beta2).addcmul_(grad, grad.conj(), value=1 - beta2)
if amsgrad:
# Maintains the maximum of all 2nd moment running avg. till now
torch.maximum(max_exp_avg_sqs[i], exp_avg_sq, out=max_exp_avg_sqs[i])
# Use the max. for normalizing running avg. of gradient
denom = (max_exp_avg_sqs[i].sqrt() / math.sqrt(bias_correction2)).add_(eps)
else:
denom = (exp_avg_sq.sqrt() / math.sqrt(bias_correction2)).add_(eps)
step_size = lr / bias_correction1
param.addcdiv_(exp_avg, denom, value=-step_size)
class Adam(Optimizer):
r"""Implements Adam algorithm.
It has been proposed in `Adam: A Method for Stochastic Optimization`_.
The implementation of the L2 penalty follows changes proposed in
`Decoupled Weight Decay Regularization`_.
Args:
params (iterable): iterable of parameters to optimize or dicts defining
parameter groups
lr (float, optional): learning rate (default: 1e-3)
betas (Tuple[float, float], optional): coefficients used for computing
running averages of gradient and its square (default: (0.9, 0.999))
eps (float, optional): term added to the denominator to improve
numerical stability (default: 1e-8)
weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
amsgrad (boolean, optional): whether to use the AMSGrad variant of this
algorithm from the paper `On the Convergence of Adam and Beyond`_
(default: False)
.. _Adam\: A Method for Stochastic Optimization:
https://arxiv.org/abs/1412.6980
.. _Decoupled Weight Decay Regularization:
https://arxiv.org/abs/1711.05101
.. _On the Convergence of Adam and Beyond:
https://openreview.net/forum?id=ryQu7f-RZ
"""
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
weight_decay=0, amsgrad=False):
if not 0.0 <= lr:
raise ValueError("Invalid learning rate: {}".format(lr))
if not 0.0 <= eps:
raise ValueError("Invalid epsilon value: {}".format(eps))
if not 0.0 <= betas[0] < 1.0:
raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
if not 0.0 <= betas[1] < 1.0:
raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
if not 0.0 <= weight_decay:
raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
defaults = dict(lr=lr, betas=betas, eps=eps,
weight_decay=weight_decay, amsgrad=amsgrad)
super(Adam, self).__init__(params, defaults)
def __setstate__(self, state):
super(Adam, self).__setstate__(state)
for group in self.param_groups:
group.setdefault('amsgrad', False)
@torch.no_grad()
def step(self, closure=None):
"""Performs a single optimization step.
Args:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
"""
loss = None
if closure is not None:
with torch.enable_grad():
loss = closure()
for group in self.param_groups:
params_with_grad = []
grads = []
exp_avgs = []
exp_avg_sqs = []
max_exp_avg_sqs = []
state_steps = []
beta1, beta2 = group['betas']
for p in group['params']:
if p.grad is not None:
params_with_grad.append(p)
if p.grad.is_sparse:
raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
grads.append(p.grad)
state = self.state[p]
# Lazy state initialization
if len(state) == 0:
state['step'] = 0
# Exponential moving average of gradient values
state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
# Exponential moving average of squared gradient values
state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
if group['amsgrad']:
# Maintains max of all exp. moving avg. of sq. grad. values
state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
exp_avgs.append(state['exp_avg'])
exp_avg_sqs.append(state['exp_avg_sq'])
if group['amsgrad']:
max_exp_avg_sqs.append(state['max_exp_avg_sq'])
# update the steps for each param group update
state['step'] += 1
# record the step after step update
state_steps.append(state['step'])
adam(params_with_grad,
grads,
exp_avgs,
exp_avg_sqs,
max_exp_avg_sqs,
state_steps,
amsgrad=group['amsgrad'],
beta1=beta1,
beta2=beta2,
lr=group['lr'],
weight_decay=group['weight_decay'],
eps=group['eps'])
return loss
View File
+26
View File
@@ -0,0 +1,26 @@
import torch
class TriangularCausalMask():
def __init__(self, B, L, device="cpu"):
mask_shape = [B, 1, L, L]
with torch.no_grad():
self._mask = torch.triu(torch.ones(mask_shape, dtype=torch.bool), diagonal=1).to(device)
@property
def mask(self):
return self._mask
class ProbMask():
def __init__(self, B, H, L, index, scores, device="cpu"):
_mask = torch.ones(L, scores.shape[-1], dtype=torch.bool).to(device).triu(1)
_mask_ex = _mask[None, None, :].expand(B, H, L, scores.shape[-1])
indicator = _mask_ex[torch.arange(B)[:, None, None],
torch.arange(H)[None, :, None],
index, :].to(device)
self._mask = indicator.view(scores.shape).to(device)
@property
def mask(self):
return self._mask
+41
View File
@@ -0,0 +1,41 @@
import numpy as np
def RSE(pred, true):
return np.sqrt(np.sum((true - pred) ** 2)) / np.sqrt(np.sum((true - true.mean()) ** 2))
def CORR(pred, true):
u = ((true - true.mean(0)) * (pred - pred.mean(0))).sum(0)
d = np.sqrt(((true - true.mean(0)) ** 2 * (pred - pred.mean(0)) ** 2).sum(0))
return (u / d).mean(-1)
def MAE(pred, true):
return np.mean(np.abs(pred - true))
def MSE(pred, true):
return np.mean((pred - true) ** 2)
def RMSE(pred, true):
return np.sqrt(MSE(pred, true))
def MAPE(pred, true):
return np.mean(np.abs((pred - true) / true))
def MSPE(pred, true):
return np.mean(np.square((pred - true) / true))
def metric(pred, true):
mae = MAE(pred, true)
mse = MSE(pred, true)
rmse = RMSE(pred, true)
mape = MAPE(pred, true)
mspe = MSPE(pred, true)
return mae, mse, rmse, mape, mspe
+134
View File
@@ -0,0 +1,134 @@
from typing import List
import numpy as np
import pandas as pd
from pandas.tseries import offsets
from pandas.tseries.frequencies import to_offset
class TimeFeature:
def __init__(self):
pass
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
pass
def __repr__(self):
return self.__class__.__name__ + "()"
class SecondOfMinute(TimeFeature):
"""Minute of hour encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return index.second / 59.0 - 0.5
class MinuteOfHour(TimeFeature):
"""Minute of hour encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return index.minute / 59.0 - 0.5
class HourOfDay(TimeFeature):
"""Hour of day encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return index.hour / 23.0 - 0.5
class DayOfWeek(TimeFeature):
"""Hour of day encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return index.dayofweek / 6.0 - 0.5
class DayOfMonth(TimeFeature):
"""Day of month encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return (index.day - 1) / 30.0 - 0.5
class DayOfYear(TimeFeature):
"""Day of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return (index.dayofyear - 1) / 365.0 - 0.5
class MonthOfYear(TimeFeature):
"""Month of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return (index.month - 1) / 11.0 - 0.5
class WeekOfYear(TimeFeature):
"""Week of year encoded as value between [-0.5, 0.5]"""
def __call__(self, index: pd.DatetimeIndex) -> np.ndarray:
return (index.isocalendar().week - 1) / 52.0 - 0.5
def time_features_from_frequency_str(freq_str: str) -> List[TimeFeature]:
"""
Returns a list of time features that will be appropriate for the given frequency string.
Parameters
----------
freq_str
Frequency string of the form [multiple][granularity] such as "12H", "5min", "1D" etc.
"""
features_by_offsets = {
offsets.YearEnd: [],
offsets.QuarterEnd: [MonthOfYear],
offsets.MonthEnd: [MonthOfYear],
offsets.Week: [DayOfMonth, WeekOfYear],
offsets.Day: [DayOfWeek, DayOfMonth, DayOfYear],
offsets.BusinessDay: [DayOfWeek, DayOfMonth, DayOfYear],
offsets.Hour: [HourOfDay, DayOfWeek, DayOfMonth, DayOfYear],
offsets.Minute: [
MinuteOfHour,
HourOfDay,
DayOfWeek,
DayOfMonth,
DayOfYear,
],
offsets.Second: [
SecondOfMinute,
MinuteOfHour,
HourOfDay,
DayOfWeek,
DayOfMonth,
DayOfYear,
],
}
offset = to_offset(freq_str)
for offset_type, feature_classes in features_by_offsets.items():
if isinstance(offset, offset_type):
return [cls() for cls in feature_classes]
supported_freq_msg = f"""
Unsupported frequency {freq_str}
The following frequencies are supported:
Y - yearly
alias: A
M - monthly
W - weekly
D - daily
B - business days
H - hourly
T - minutely
alias: min
S - secondly
"""
raise RuntimeError(supported_freq_msg)
def time_features(dates, freq='h'):
return np.vstack([feat(dates) for feat in time_features_from_frequency_str(freq)])
+112
View File
@@ -0,0 +1,112 @@
import numpy as np
import torch
import matplotlib.pyplot as plt
import math
plt.switch_backend('agg')
def adjust_learning_rate(optimizer, epoch, args):
for param_group in optimizer.param_groups:
if param_group['name'] == 'smoothing':
continue
elif param_group['name'] == 'damping':
continue
else:
learning_rate = args.learning_rate
if args.lradj == 'exponential':
lr_adjust = {epoch: learning_rate * (0.5 ** ((epoch - 1) // 1))}
elif args.lradj == 'schedule':
lr_adjust = {
2: 5e-5, 4: 1e-5, 6: 5e-6, 8: 1e-6,
10: 5e-7, 15: 1e-7, 20: 5e-8
}
elif args.lradj == 'cos':
lr_adjust = {epoch: learning_rate * 0.5 * (1. + math.cos(math.pi * epoch / args.train_epochs))}
elif args.lradj == 'cos_with_warmup':
if epoch <= args.warmup_epochs:
lr = args.min_lr + (learning_rate - args.min_lr) * (epoch / (args.warmup_epochs + 1))
else:
curr_epoch = epoch - args.warmup_epochs
total_epochs = args.train_epochs - args.warmup_epochs
lr = learning_rate * 0.5 * (1. + math.cos(math.pi * curr_epoch / total_epochs))
lr_adjust = {epoch: lr}
elif args.lradj == 'exponential_with_warmup':
if epoch <= args.warmup_epochs:
lr = args.min_lr + (learning_rate - args.min_lr) * (epoch / (args.warmup_epochs + 1))
else:
curr_epoch = epoch - args.warmup_epochs
lr = learning_rate * (0.5 ** ((curr_epoch - 1) // 1))
lr_adjust = {epoch: lr}
else:
raise NotImplementedError
if epoch in lr_adjust.keys():
lr = lr_adjust[epoch]
for param_group in optimizer.param_groups:
param_group['lr'] = lr
print('Updating learning rate to {}'.format(lr))
class EarlyStopping:
def __init__(self, patience=7, verbose=False, delta=0):
self.patience = patience
self.verbose = verbose
self.counter = 0
self.best_score = None
self.early_stop = False
self.val_loss_min = np.Inf
self.delta = delta
def __call__(self, val_loss, model, path):
score = -val_loss
if self.best_score is None:
self.best_score = score
self.save_checkpoint(val_loss, model, path)
elif score < self.best_score + self.delta:
self.counter += 1
print(f'EarlyStopping counter: {self.counter} out of {self.patience}')
if self.counter >= self.patience:
self.early_stop = True
else:
self.best_score = score
self.save_checkpoint(val_loss, model, path)
self.counter = 0
def save_checkpoint(self, val_loss, model, path):
if self.verbose:
print(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}). Saving model ...')
torch.save(model.state_dict(), path + '/' + 'checkpoint.pth')
self.val_loss_min = val_loss
class dotdict(dict):
"""dot.notation access to dictionary attributes"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
class StandardScaler():
def __init__(self, mean, std):
self.mean = mean
self.std = std
def transform(self, data):
return (data - self.mean) / self.std
def inverse_transform(self, data):
return (data * self.std) + self.mean
def visual(true, preds=None, name='./pic/test.pdf'):
"""
Results visualization
"""
plt.figure()
plt.plot(true, label='GroundTruth', linewidth=2)
if preds is not None:
plt.plot(preds, label='Prediction', linewidth=2)
plt.legend()
plt.savefig(name, bbox_inches='tight')