├── .idea ├── encodings.xml ├── misc.xml ├── modules.xml └── pattern_recognize.iml ├── README.md ├── ele.sh ├── main.py ├── models ├── CNN.py ├── LSTNet.py ├── MHA_Net.py ├── MultiHeadAttention.py ├── RNN.py └── __init__.py ├── solar.sh ├── stock.sh ├── traffic.sh ├── train_eval.py └── utils.py /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/pattern_recognize.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Deep Learning for Multivariate Time Series Forecasting 3 | 4 | ### Mainly referenced paper 5 | 6 | Modeling Long- and Short-Term Temporal Patterns with Deep Neural Networks.(https://arxiv.org/abs/1703.07015) 7 | 8 | ### Environment 9 | 10 | * Python 3.6+ 11 | * Pytorch 1.0+ 12 | * numpy 13 | 14 | ### Example 15 | 16 | 1. `Exchange Rate dataset: `stock.sh 17 | 2. `Traffic dataset: ` traffic.sh 18 | 3. `Solar-Energy dataset:`solar.sh 19 | 4. ` Electricity usage dataset:` ele.sh 20 | 21 | ### Instruction 22 | 23 | main.py 24 | * --data DATA `location of the data file ` 25 | * -h --help ` show this help message and exit ` 26 | * --model DATA ` select the model: LSTNet, CNN, RNN or MHA_Net ` 27 | * --window WINDOW ` window size (history size) ` 28 | * --horizon HORIZON `forecasting horizon(step) ` 29 | * --hidRNN HIDRNN `number of RNN hidden units each layer` 30 | * --rnn_layers RNN_LAYERS ` number of RNN hidden layers` 31 | * --hidCNN HIDCNN ` number of CNN hidden units (channels)` 32 | * --CNN_kernel CNN_KERNEL `the kernel size of the CNN layers` 33 | * --highway_window HIGHWAY_WINDOW `The window size of the highway component ` 34 | * -n_head N_HEAD `num of self attention heads ` 35 | * -d_k D_K `self attention key dimension` 36 | * -d_v D_V `self attention value dimension` 37 | * --clip CLIP `gradient clipping limit ` 38 | * --epochs EPOCHS `upper epoch limit ` 39 | * --batch_size N `batch_size` 40 | * --dropout DROPOUT `dropout applied to layers (0 = no dropout)` 41 | * --seed SEED `random seed` 42 | * --log_interval N `report interval` 43 | * --save SAVE `path to save the final model'` 44 | * --cuda CUDA `whether to use cuda device` 45 | * --optim OPTIM `optimizer method ,default 'adam'` 46 | * --amsgrad AMSGRAD `whether to use amsgrad` 47 | * --lr LR `learning rate` 48 | * --skip SKIP `autoregression window size` 49 | * --hidSkip HIDSKIP `skiphidden states dimension` 50 | * --L1Loss L1LOSS `whether to use l1 loss function` 51 | * --normalize NORMALIZE `whether to normalize the data` 52 | * --output_fun OUTPUT_FUN `relu, tanh or sigmoid ` 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ele.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | python main.py --horizon 6 --data data/electricity.txt --save save/elec.pt --output_fun Linear --model LSTNet --skip 0 --batch_size 32 --hidCNN 50 --hidRNN 50 3 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | __author__ = "Guan Song Wang" 2 | 3 | import argparse 4 | import math 5 | import time 6 | import torch 7 | from torch.utils.data import Dataset, DataLoader 8 | import torch.nn as nn 9 | from models import LSTNet,MHA_Net,CNN,RNN 10 | import importlib 11 | 12 | from utils import * 13 | from train_eval import train, evaluate, makeOptimizer 14 | 15 | parser = argparse.ArgumentParser(description='PyTorch Time series forecasting') 16 | parser.add_argument('--data', type=str, required=True,help='location of the data file') 17 | parser.add_argument('--model', type=str, default='LSTNet', help='') 18 | parser.add_argument('--window', type=int, default=24 * 7,help='window size') 19 | parser.add_argument('--horizon', type=int, default=12) 20 | 21 | parser.add_argument('--hidRNN', type=int, default=100, help='number of RNN hidden units each layer') 22 | parser.add_argument('--rnn_layers', type=int, default=1, help='number of RNN hidden layers') 23 | 24 | parser.add_argument('--hidCNN', type=int, default=100, help='number of CNN hidden units (channels)') 25 | parser.add_argument('--CNN_kernel', type=int, default=6, help='the kernel size of the CNN layers') 26 | parser.add_argument('--highway_window', type=int, default=24, help='The window size of the highway component') 27 | 28 | parser.add_argument('-n_head', type=int, default=8) 29 | parser.add_argument('-d_k', type=int, default=64) 30 | parser.add_argument('-d_v', type=int, default=64) 31 | 32 | parser.add_argument('--clip', type=float, default=10.,help='gradient clipping') 33 | parser.add_argument('--epochs', type=int, default=50, help='upper epoch limit') 34 | parser.add_argument('--batch_size', type=int, default=128, metavar='N', help='batch size') 35 | parser.add_argument('--dropout', type=float, default=0.2, help='dropout applied to layers (0 = no dropout)') 36 | parser.add_argument('--seed', type=int, default=54321,help='random seed') 37 | parser.add_argument('--log_interval', type=int, default=2000, metavar='N',help='report interval') 38 | parser.add_argument('--save', type=str, default='model/model.pt', help='path to save the final model') 39 | 40 | parser.add_argument('--cuda', type=str, default=True) 41 | parser.add_argument('--optim', type=str, default='adam') 42 | parser.add_argument('--amsgrad', type=str, default=True) 43 | parser.add_argument('--lr', type=float, default=0.001) 44 | parser.add_argument('--skip', type=float, default=24) 45 | parser.add_argument('--hidSkip', type=int, default=5) 46 | parser.add_argument('--L1Loss', type=bool, default=True) 47 | parser.add_argument('--normalize', type=int, default=2) 48 | parser.add_argument('--output_fun', type=str, default='sigmoid') 49 | args = parser.parse_args() 50 | 51 | 52 | 53 | # Choose device: cpu or gpu 54 | args.cuda = torch.cuda.is_available() 55 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 56 | 57 | # Reproducibility. 58 | torch.manual_seed(args.seed) 59 | if args.cuda: 60 | torch.cuda.manual_seed(args.seed) 61 | 62 | # Load data 63 | Data = Data_utility(args.data, 0.6, 0.2, device, args) 64 | # loss function 65 | if args.L1Loss: 66 | criterion = nn.L1Loss(size_average=False) 67 | else: 68 | criterion = nn.MSELoss(size_average=False) 69 | evaluateL2 = nn.MSELoss(size_average=False) 70 | evaluateL1 = nn.L1Loss(size_average=False) 71 | if args.cuda: 72 | criterion = criterion.cuda() 73 | evaluateL1 = evaluateL1.cuda() 74 | evaluateL2 = evaluateL2.cuda() 75 | 76 | # Select model 77 | model = eval(args.model).Model(args, Data) 78 | train_method = train 79 | eval_method = evaluate 80 | nParams = sum([p.nelement() for p in model.parameters()]) 81 | print('number of parameters: %d' % nParams) 82 | if args.cuda: 83 | model = nn.DataParallel(model) 84 | 85 | 86 | best_val = 10000000 87 | 88 | optim = makeOptimizer(model.parameters(), args) 89 | 90 | # While training you can press Ctrl + C to stop it. 91 | try: 92 | print('Training start') 93 | for epoch in range(1, args.epochs + 1): 94 | epoch_start_time = time.time() 95 | 96 | train_loss = train_method(Data, Data.train[0], Data.train[1], model, criterion, optim, args) 97 | 98 | val_loss, val_rae, val_corr = eval_method(Data, Data.valid[0], Data.valid[1], model, evaluateL2, evaluateL1, args) 99 | print('| end of epoch {:3d} | time used: {:5.2f}s | train_loss {:5.4f} | valid rse {:5.4f} | valid rae {:5.4f} | valid corr {:5.4f}'. 100 | format( epoch, (time.time() - epoch_start_time), train_loss, val_loss, val_rae, val_corr)) 101 | 102 | if val_loss < best_val: 103 | with open(args.save, 'wb') as f: 104 | torch.save(model, f) 105 | best_val = val_loss 106 | if epoch % 10 == 0: 107 | test_acc, test_rae, test_corr = eval_method(Data, Data.test[0], Data.test[1], model, evaluateL2, evaluateL1,args) 108 | print("| test rse {:5.4f} | test rae {:5.4f} | test corr {:5.4f}\n".format(test_acc, test_rae, test_corr)) 109 | 110 | except KeyboardInterrupt: 111 | print('-' * 89) 112 | print('Exiting from training early') 113 | 114 | # Load the best saved model. 115 | with open(args.save, 'rb') as f: 116 | model = torch.load(f) 117 | test_acc, test_rae, test_corr = evaluate(Data, Data.test[0], Data.test[1], model, evaluateL2, evaluateL1,args) 118 | print('Best model performance:') 119 | print("| test rse {:5.4f} | test rae {:5.4f} | test corr {:5.4f}".format(test_acc, test_rae, test_corr)) 120 | -------------------------------------------------------------------------------- /models/CNN.py: -------------------------------------------------------------------------------- 1 | __author__ = "Guan Song Wang" 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | class Model(nn.Module): 8 | def __init__(self, args, data): 9 | super(Model, self).__init__() 10 | self.window = args.window 11 | self.variables = data.m 12 | self.hw=args.highway_window 13 | self.conv1 = nn.Conv1d(self.variables, 32, kernel_size=3) 14 | self.activate1=F.relu 15 | self.conv2=nn.Conv1d(32,32,kernel_size=3) 16 | self.maxpool1=nn.MaxPool1d(kernel_size=2) 17 | self.conv3=nn.Conv1d(32,16,kernel_size=3) 18 | self.maxpool2=nn.MaxPool1d(kernel_size=2) 19 | self.linear1=nn.Linear(1280,100) 20 | self.out=nn.Linear(100,self.variables) 21 | if (self.hw > 0): 22 | self.highway = nn.Linear(self.hw, 1) 23 | 24 | self.dropout = nn.Dropout(p=args.dropout) 25 | self.output = None 26 | if (args.output_fun == 'sigmoid'): 27 | self.output = F.sigmoid 28 | if (args.output_fun == 'tanh'): 29 | self.output = F.tanh 30 | 31 | def forward(self, x): 32 | c = x.permute(0,2,1).contiguous() 33 | c=self.conv1(c) 34 | c=self.activate1(c) 35 | c=self.conv2(c) 36 | c=self.activate1(c) 37 | c=self.maxpool1(c) 38 | c=self.conv3(c) 39 | c=self.activate1(c) 40 | c=c.view(c.size(0),c.size(1)*c.size(2)) 41 | c=self.dropout(c) 42 | c=self.linear1(c) 43 | c=self.dropout(c) 44 | out=self.out(c) 45 | 46 | if (self.hw > 0): 47 | 48 | z = x[:, -self.hw:, :] 49 | z = z.permute(0, 2, 1).contiguous().view(-1, self.hw) 50 | z = self.highway(z) 51 | z = z.view(-1, self.variables) 52 | out = out + z 53 | if self.output is not None: 54 | out=self.output(out) 55 | return out 56 | -------------------------------------------------------------------------------- /models/LSTNet.py: -------------------------------------------------------------------------------- 1 | __author__ = "Guan Song Wang" 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | 8 | class Model(nn.Module): 9 | def __init__(self, args, data): 10 | super(Model, self).__init__() 11 | self.P = args.window 12 | self.m = data.m 13 | self.hidR = args.hidRNN 14 | self.hidC = args.hidCNN 15 | self.hidS = args.hidSkip 16 | self.Ck = args.CNN_kernel 17 | self.skip = args.skip 18 | 19 | self.hw = args.highway_window 20 | self.conv1 = nn.Conv2d(1, self.hidC, kernel_size=(self.Ck, self.m)) 21 | self.GRU1 = nn.GRU(self.hidC, self.hidR) 22 | self.dropout = nn.Dropout(p=args.dropout) 23 | if (self.skip > 0): 24 | self.pt = (self.P - self.Ck) / self.skip 25 | self.GRUskip = nn.GRU(self.hidC, self.hidS) 26 | self.linear1 = nn.Linear(self.hidR + self.skip * self.hidS, self.m) 27 | else: 28 | self.linear1 = nn.Linear(self.hidR, self.m) 29 | if (self.hw > 0): 30 | self.highway = nn.Linear(self.hw, 1) 31 | self.output = None 32 | if (args.output_fun == 'sigmoid'): 33 | self.output = F.sigmoid 34 | if (args.output_fun == 'tanh'): 35 | self.output = F.tanh 36 | 37 | def forward(self, x): 38 | batch_size = x.size(0) 39 | 40 | # CNN 41 | c = x.view(-1, 1, self.P, self.m) 42 | c = F.relu(self.conv1(c)) 43 | c = self.dropout(c) 44 | c = torch.squeeze(c, 3) 45 | 46 | # RNN 47 | r = c.permute(2, 0, 1).contiguous() 48 | _, r = self.GRU1(r) 49 | 50 | r = self.dropout(torch.squeeze(r, 0)) 51 | 52 | 53 | 54 | # skip-rnn 55 | 56 | if (self.skip > 0): 57 | self.pt=int(self.pt) 58 | s = c[:, :, int(-self.pt * self.skip):].contiguous() 59 | 60 | s = s.view(batch_size, self.hidC, self.pt, self.skip) 61 | s = s.permute(2, 0, 3, 1).contiguous() 62 | s = s.view(self.pt, batch_size * self.skip, self.hidC) 63 | _, s = self.GRUskip(s) 64 | s = s.view(batch_size, self.skip * self.hidS) 65 | s = self.dropout(s) 66 | r = torch.cat((r, s), 1) 67 | 68 | res = self.linear1(r) 69 | 70 | # highway 71 | if (self.hw > 0): 72 | 73 | z = x[:, -self.hw:, :] 74 | z = z.permute(0, 2, 1).contiguous().view(-1, self.hw) 75 | z = self.highway(z) 76 | z = z.view(-1, self.m) 77 | res = res + z 78 | 79 | if (self.output): 80 | res = self.output(res) 81 | return res 82 | -------------------------------------------------------------------------------- /models/MHA_Net.py: -------------------------------------------------------------------------------- 1 | __author__ = "Guan Song Wang" 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | from models.MultiHeadAttention import MultiHeadAttention 7 | 8 | class Model(nn.Module): 9 | def __init__(self, args, data): 10 | super(Model, self).__init__() 11 | self.window = args.window 12 | self.variables = data.m 13 | self.hidC = args.hidCNN 14 | self.hidR = args.hidRNN 15 | self.hw=args.highway_window 16 | 17 | self.d_v=args.d_v 18 | self.d_k=args.d_k 19 | self.Ck = args.CNN_kernel 20 | self.GRU = nn.GRU(self.variables, self.hidR, num_layers=args.rnn_layers) 21 | # self.Conv1 = nn.Conv2d(1, self.hidC, kernel_size=(self.Ck, self.variables)) 22 | 23 | self.slf_attn = MultiHeadAttention(args.n_head, self.variables, self.d_k,self.d_v , dropout=args.dropout) 24 | 25 | self.dropout = nn.Dropout(p=args.dropout) 26 | self.linear_out=nn.Linear(self.hidR,self.variables) 27 | 28 | if (self.hw > 0): 29 | self.highway = nn.Linear(self.hw, 1) 30 | self.output = None 31 | if (args.output_fun == 'sigmoid'): 32 | self.output = F.sigmoid 33 | if (args.output_fun == 'tanh'): 34 | self.output = F.tanh 35 | 36 | 37 | def forward(self, x): 38 | # r = x.permute(1, 0, 2).contiguous() 39 | # out, _ = self.GRU1(r) 40 | # c = out.permute(1, 0, 2).contiguous() 41 | # c = x.view(-1,1, self.window, self.variables) 42 | # c = F.relu(self.Conv1(c)) 43 | # c = self.dropout(c) 44 | # c = torch.squeeze(c, 3) 45 | # c=c.permute(0,2,1).contiguous() 46 | 47 | attn_output, slf_attn=self.slf_attn(x,x,x,mask=None) 48 | 49 | r=attn_output.permute(1,0,2).contiguous() 50 | _,r=self.GRU(r) 51 | r = self.dropout(torch.squeeze(r[-1:, :, :], 0)) 52 | out = self.linear_out(r) 53 | 54 | if (self.hw > 0): 55 | 56 | z = x[:, -self.hw:, :] 57 | z = z.permute(0, 2, 1).contiguous().view(-1, self.hw) 58 | z = self.highway(z) 59 | z = z.view(-1, self.variables) 60 | out = out + z 61 | if self.output is not None: 62 | out=self.output(out) 63 | return out -------------------------------------------------------------------------------- /models/MultiHeadAttention.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | import torch.nn.functional as F 5 | 6 | # https://github.com/jadore801120/attention-is-all-you-need-pytorch/blob/master/transformer/SubLayers.py 7 | 8 | class ScaledDotProductAttention(nn.Module): 9 | 10 | # Scaled Dot-Product Attention 11 | 12 | def __init__(self, temperature, attn_dropout=0.1): 13 | super().__init__() 14 | self.temperature = temperature 15 | self.dropout = nn.Dropout(attn_dropout) 16 | self.softmax = nn.Softmax(dim=2) 17 | 18 | def forward(self, q, k, v, mask=None): 19 | 20 | attn = torch.bmm(q, k.transpose(1, 2)) 21 | attn = attn / self.temperature 22 | 23 | if mask is not None: 24 | attn = attn.masked_fill(mask, -np.inf) 25 | 26 | attn = self.softmax(attn) 27 | attn = self.dropout(attn) 28 | output = torch.bmm(attn, v) 29 | 30 | return output, attn 31 | 32 | 33 | class MultiHeadAttention(nn.Module): 34 | ''' Multi-Head Attention module ''' 35 | 36 | def __init__(self, n_head, d_model, d_k, d_v, dropout=0.1): 37 | super().__init__() 38 | 39 | self.n_head = n_head 40 | self.d_k = d_k 41 | self.d_v = d_v 42 | 43 | self.w_qs = nn.Linear(d_model, n_head * d_k) 44 | self.w_ks = nn.Linear(d_model, n_head * d_k) 45 | self.w_vs = nn.Linear(d_model, n_head * d_v) 46 | nn.init.normal_(self.w_qs.weight, mean=0, std=np.sqrt(2.0 / (d_model + d_k))) 47 | nn.init.normal_(self.w_ks.weight, mean=0, std=np.sqrt(2.0 / (d_model + d_k))) 48 | nn.init.normal_(self.w_vs.weight, mean=0, std=np.sqrt(2.0 / (d_model + d_v))) 49 | 50 | self.attention = ScaledDotProductAttention(temperature=np.power(d_k, 0.2)) 51 | self.layer_norm = nn.LayerNorm(d_model) 52 | 53 | self.fc = nn.Linear(n_head * d_v, d_model) 54 | nn.init.xavier_normal_(self.fc.weight) 55 | 56 | self.dropout = nn.Dropout(dropout) 57 | 58 | 59 | def forward(self, q, k, v, mask=None): 60 | 61 | d_k, d_v, n_head = self.d_k, self.d_v, self.n_head 62 | 63 | sz_b, len_q, _ = q.size() 64 | sz_b, len_k, _ = k.size() 65 | sz_b, len_v, _ = v.size() 66 | 67 | residual = q 68 | 69 | q = self.w_qs(q).view(sz_b, len_q, n_head, d_k) 70 | k = self.w_ks(k).view(sz_b, len_k, n_head, d_k) 71 | v = self.w_vs(v).view(sz_b, len_v, n_head, d_v) 72 | 73 | q = q.permute(2, 0, 1, 3).contiguous().view(-1, len_q, d_k) # (n*b) x lq x dk 74 | k = k.permute(2, 0, 1, 3).contiguous().view(-1, len_k, d_k) # (n*b) x lk x dk 75 | v = v.permute(2, 0, 1, 3).contiguous().view(-1, len_v, d_v) # (n*b) x lv x dv 76 | if mask is not None: 77 | mask = mask.repeat(n_head, 1, 1) # (n*b) x .. x .. 78 | output, attn = self.attention(q, k, v, mask=mask) 79 | 80 | output = output.view(n_head, sz_b, len_q, d_v) 81 | output = output.permute(1, 2, 0, 3).contiguous().view(sz_b, len_q, -1) # b x lq x (n*dv) 82 | 83 | output = self.dropout(self.fc(output)) 84 | output = self.layer_norm(output + residual) 85 | 86 | return output, attn 87 | 88 | class PositionwiseFeedForward(nn.Module): 89 | ''' A two-feed-forward-layer module ''' 90 | 91 | def __init__(self, d_in, d_hid, dropout=0.1): 92 | super().__init__() 93 | self.w_1 = nn.Conv1d(d_in, d_hid, 1) # position-wise 94 | self.w_2 = nn.Conv1d(d_hid, d_in, 1) # position-wise 95 | self.layer_norm = nn.LayerNorm(d_in) 96 | self.dropout = nn.Dropout(dropout) 97 | 98 | def forward(self, x): 99 | residual = x 100 | output = x.transpose(1, 2) 101 | output = self.w_2(F.relu(self.w_1(output))) 102 | output = output.transpose(1, 2) 103 | output = self.dropout(output) 104 | output = self.layer_norm(output + residual) 105 | return output 106 | -------------------------------------------------------------------------------- /models/RNN.py: -------------------------------------------------------------------------------- 1 | __author__ = "Guan Song Wang" 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | class Model(nn.Module): 8 | def __init__(self, args, data): 9 | super(Model, self).__init__() 10 | self.window = args.window 11 | self.variables = data.m 12 | self.hw=args.highway_window 13 | self.activate1=F.relu 14 | self.hidR=args.hidRNN 15 | self.rnn1=nn.GRU(self.variables,self.hidR,num_layers=args.rnn_layers) 16 | self.linear1 = nn.Linear(self.hidR, self.variables) 17 | # self.linear1=nn.Linear(1280,100) 18 | # self.out=nn.Linear(100,self.variables) 19 | if (self.hw > 0): 20 | self.highway = nn.Linear(self.hw, 1) 21 | 22 | self.dropout = nn.Dropout(p=args.dropout) 23 | self.output = None 24 | if (args.output_fun == 'sigmoid'): 25 | self.output = F.sigmoid 26 | if (args.output_fun == 'tanh'): 27 | self.output = F.tanh 28 | 29 | def forward(self, x): 30 | r= x.permute(1,0,2).contiguous() 31 | _,r=self.rnn1(r) 32 | r=self.dropout(torch.squeeze(r[-1:,:,:], 0)) 33 | out = self.linear1(r) 34 | 35 | 36 | if (self.hw > 0): 37 | 38 | z = x[:, -self.hw:, :] 39 | z = z.permute(0, 2, 1).contiguous().view(-1, self.hw) 40 | z = self.highway(z) 41 | z = z.view(-1, self.variables) 42 | out = out + z 43 | if self.output is not None: 44 | out=self.output(out) 45 | return out 46 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vsooong/multivariate-prediction/e10cb51dcaa996b71b63f19f679e042741d3807e/models/__init__.py -------------------------------------------------------------------------------- /solar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | python main.py --data data/solar_AL.txt --save save/solar_AL.pt --skip 0 --output_fun Linear 3 | -------------------------------------------------------------------------------- /stock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | python main.py --model LSTNet --data data/exchange_rate.txt --save save/exchange_rate.pt --hidCNN 50 --hidRNN 50 --L1Loss False --output_fun None 3 | -------------------------------------------------------------------------------- /traffic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | python main.py --data data/traffic.txt --save save/traffic.pt --hidSkip 10 --batch_size 32 3 | -------------------------------------------------------------------------------- /train_eval.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch.optim as optim 3 | import torch 4 | import numpy as np 5 | np.seterr(divide='ignore', invalid='ignore') 6 | 7 | def evaluate(data, X, Y, model, evaluateL2, evaluateL1, args): 8 | model.eval() 9 | total_loss = 0 10 | total_loss_l1 = 0 11 | n_samples = 0 12 | predict = None 13 | test = None 14 | 15 | for X, Y in data.get_batches(X, Y, args.batch_size, False): 16 | output = model(X) 17 | if predict is None: 18 | predict = output.clone().detach() 19 | test = Y 20 | else: 21 | predict = torch.cat((predict, output.clone().detach())) 22 | test = torch.cat((test, Y)) 23 | 24 | scale = data.scale.expand(output.size(0), data.m) 25 | total_loss += float(evaluateL2(output * scale, Y * scale).data.item()) 26 | total_loss_l1 +=float( evaluateL1(output * scale, Y * scale).data.item()) 27 | 28 | n_samples += int((output.size(0) * data.m)) 29 | 30 | rse = math.sqrt(total_loss / n_samples) / data.rse 31 | rae = (total_loss_l1 / n_samples) / data.rae 32 | 33 | predict = predict.data.cpu().numpy() 34 | Ytest = test.data.cpu().numpy() 35 | sigma_p = (predict).std(axis=0) 36 | sigma_g = (Ytest).std(axis=0) 37 | mean_p = predict.mean(axis=0) 38 | mean_g = Ytest.mean(axis=0) 39 | index = (sigma_g != 0) 40 | correlation = ((predict - mean_p) * (Ytest - mean_g)).mean(axis=0) / (sigma_p * sigma_g) 41 | correlation = (correlation[index]).mean() 42 | return rse, rae, correlation 43 | 44 | 45 | def train(data, X, Y, model, criterion, optim, args): 46 | model.train() 47 | total_loss = 0 48 | n_samples = 0 49 | for X, Y in data.get_batches(X, Y, args.batch_size, True): 50 | optim.zero_grad() 51 | output = model(X) 52 | scale = data.scale.expand(output.size(0), data.m) 53 | loss = criterion(output * scale, Y * scale) 54 | loss.backward() 55 | 56 | torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip) 57 | optim.step() 58 | total_loss += loss.data.item() 59 | n_samples +=int( (output.size(0) * data.m)) 60 | return total_loss / n_samples 61 | 62 | def makeOptimizer(params, args): 63 | if args.optim == 'sgd': 64 | optimizer = optim.SGD(params, lr=args.lr, ) 65 | elif args.optim == 'adagrad': 66 | optimizer = optim.Adagrad(params, lr=args.lr, ) 67 | elif args.optim == 'adadelta': 68 | optimizer = optim.Adadelta(params, lr=args.lr, ) 69 | elif args.optim == 'adam': 70 | optimizer = optim.Adam(params, lr=args.lr, ) 71 | else: 72 | raise RuntimeError("Invalid optim method: " + args.method) 73 | return optimizer 74 | 75 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | from torch.autograd import Variable 4 | 5 | 6 | def normal_std(x): 7 | return x.std() * np.sqrt((len(x) - 1.) / (len(x))) 8 | 9 | 10 | class Data_utility(object): 11 | # train and valid is the ratio of training set and validation set. test = 1 - train - valid 12 | def __init__(self, file_name, train, valid, device, args): 13 | self.device = device 14 | self.P = args.window 15 | self.h = args.horizon 16 | fin = open(file_name) 17 | self.rawdat = np.loadtxt(fin, delimiter=',') 18 | self.dat = np.zeros(self.rawdat.shape) 19 | self.n, self.m = self.dat.shape 20 | self.scale = np.ones(self.m) 21 | self._normalized(args.normalize) 22 | self._split(int(train * self.n), int((train + valid) * self.n), self.n) 23 | 24 | self.scale = torch.as_tensor(self.scale, device=device, dtype=torch.float) 25 | 26 | tmp = self.test[1] * self.scale.expand(self.test[1].size(0), self.m) 27 | 28 | self.scale = Variable(self.scale) 29 | 30 | self.rse = normal_std(tmp) 31 | self.rae = torch.mean(torch.abs(tmp - torch.mean(tmp))) 32 | fin.close() 33 | 34 | def _normalized(self, normalize): 35 | # normalized by the maximum value of entire matrix. 36 | 37 | if (normalize == 0): 38 | self.dat = self.rawdat 39 | 40 | if (normalize == 1): 41 | self.dat = self.rawdat / np.max(self.rawdat) 42 | 43 | # normlized by the maximum value of each row(sensor). 44 | if (normalize == 2): 45 | for i in range(self.m): 46 | self.scale[i] = np.max(np.abs(self.rawdat[:, i])) 47 | self.dat[:, i] = self.rawdat[:, i] / np.max(np.abs(self.rawdat[:, i])) 48 | 49 | def _split(self, train, valid, test): 50 | 51 | train_set = range(self.P + self.h - 1, train) 52 | valid_set = range(train, valid) 53 | test_set = range(valid, self.n) 54 | self.train = self._batchify(train_set, self.h) 55 | self.valid = self._batchify(valid_set, self.h) 56 | self.test = self._batchify(test_set, self.h) 57 | 58 | def _batchify(self, idx_set, horizon): 59 | 60 | n = len(idx_set) 61 | X = torch.zeros((n, self.P, self.m), device=self.device) 62 | Y = torch.zeros((n, self.m), device=self.device) 63 | 64 | for i in range(n): 65 | end = idx_set[i] - self.h + 1 66 | start = end - self.P 67 | X[i, :, :] = torch.as_tensor(self.dat[start:end, :], device=self.device) 68 | Y[i, :] = torch.as_tensor(self.dat[idx_set[i], :], device=self.device) 69 | 70 | return [X, Y] 71 | 72 | def get_batches(self, inputs, targets, batch_size, shuffle=True): 73 | length = len(inputs) 74 | if shuffle: 75 | index = torch.randperm(length,device=self.device) 76 | else: 77 | index = torch.as_tensor(range(length),device=self.device,dtype=torch.long) 78 | start_idx = 0 79 | while (start_idx < length): 80 | end_idx = min(length, start_idx + batch_size) 81 | excerpt = index[start_idx:end_idx] 82 | X = inputs[excerpt] 83 | Y = targets[excerpt] 84 | 85 | yield Variable(X), Variable(Y) 86 | start_idx += batch_size 87 | --------------------------------------------------------------------------------