Skip to content

Commit

Permalink
basic interpretation codes
Browse files Browse the repository at this point in the history
  • Loading branch information
khairulislam committed Sep 19, 2023
1 parent 7b8ebc8 commit 331c09f
Show file tree
Hide file tree
Showing 12 changed files with 614 additions and 1,121 deletions.
390 changes: 390 additions & 0 deletions interpret.ipynb

Large diffs are not rendered by default.

101 changes: 101 additions & 0 deletions interpret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from run import *
from tint.attr import FeatureAblation
from tint.metrics import mse, mae
from tqdm import tqdm

parser = get_parser()
argv = """
--root_path ./dataset/illness/ \
--data_path national_illness.csv \
--model_id ili_36_24 \
--model Transformer \
--data custom \
--features MS \
--use_gpu \
--seq_len 36 \
--label_len 18 \
--pred_len 24 \
--e_layers 2 \
--d_layers 1 \
--factor 3 \
--enc_in 7 \
--dec_in 7 \
--c_out 7 \
--des Exp \
--itr 1
""".split()
args = parser.parse_args(argv)

set_random_seed(args.seed)
# Disable cudnn if using cuda accelerator.
# Please see https://captum.ai/docs/faq#how-can-i-resolve-cudnn-rnn-backward-error-for-rnn-or-lstm-network
args.use_gpu = False

assert args.task_name == 'long_term_forecast', "Only long_term_forecast is supported for now"

Exp = Exp_Long_Term_Forecast

setting = stringify_setting(args, 0)
exp = Exp(args) # set experiments
_, dataloader = exp._get_data('test')

exp.model.load_state_dict(
torch.load(os.path.join('checkpoints/' + setting, 'checkpoint.pth'))
)

model = exp.model
model.eval()
model.zero_grad()
explainer = FeatureAblation(model)
assert not exp.args.output_attention

if args.use_gpu:
torch.backends.cudnn.enabled = False

topk = 0.2
error_results = {
'mae':[], 'mse':[]
}

for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in tqdm(enumerate(dataloader)):
batch_x = batch_x.float().to(exp.device)
batch_y = batch_y.float().to(exp.device)

batch_x_mark = batch_x_mark.float().to(exp.device)
batch_y_mark = batch_y_mark.float().to(exp.device)

# decoder input
dec_inp = torch.zeros_like(batch_y[:, -exp.args.pred_len:, :]).float()
dec_inp = torch.cat([batch_y[:, :exp.args.label_len, :], dec_inp], dim=1).float().to(exp.device)

# batch size x pred_len x seq_len x n_features if target = None
# batch size x seq_len x n_features if target specified
score = explainer.attribute(
inputs=(batch_x), baselines=0, # target=0,
additional_forward_args=(batch_x_mark, dec_inp, batch_y_mark)
)

# batch size x seq_len x n_features
# take mean score across all output horizon
mean_score = score.reshape(
(batch_x.shape[0], args.pred_len, args.seq_len, -1)
).mean(axis=1)

mae_error = mae(
model, inputs=batch_x, topk=topk, mask_largest=True,
attributions=mean_score, baselines=0,
additional_forward_args=(batch_x_mark, dec_inp, batch_y_mark)
)

mse_error = mse(
model, inputs=batch_x, topk=topk, mask_largest=True,
attributions=mean_score, baselines=0,
additional_forward_args=(batch_x_mark, dec_inp, batch_y_mark)
)
error_results['mae'].append(mae_error)
error_results['mse'].append(mse_error)

for key in error_results.keys():
error_results[key] = np.mean(error_results[key])

print(error_results)
5 changes: 4 additions & 1 deletion models/DLinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, configs, individual=False):
individual: Bool, whether shared model among different variates.
"""
super(Model, self).__init__()
self.configs = configs
self.task_name = configs.task_name
self.seq_len = configs.seq_len
if self.task_name == 'classification':
Expand Down Expand Up @@ -91,7 +92,9 @@ def classification(self, x_enc):
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == 'long_term_forecast':
dec_out = self.forecast(x_enc)
return dec_out[:, -self.pred_len:, :] # [B, L, D]

f_dim = -1 if self.configs.features == 'MS' else 0
return dec_out[:, -self.pred_len:, f_dim:] # [B, L, D]
if self.task_name == 'classification':
dec_out = self.classification(x_enc)
return dec_out # [B, N]
Expand Down
5 changes: 4 additions & 1 deletion models/Transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Model(nn.Module):

def __init__(self, configs):
super(Model, self).__init__()
self.configs = configs
self.task_name = configs.task_name
self.pred_len = configs.pred_len
self.output_attention = configs.output_attention
Expand Down Expand Up @@ -93,7 +94,9 @@ def classification(self, x_enc, x_mark_enc):
def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):
if self.task_name == 'long_term_forecast':
dec_out = self.forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)
return dec_out[:, -self.pred_len:, :] # [B, L, D]

f_dim = -1 if self.configs.features == 'MS' else 0
return dec_out[:, -self.pred_len:, f_dim:] # [B, L, D]
if self.task_name == 'classification':
dec_out = self.classification(x_enc, x_mark_enc)
return dec_out # [B, N]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pyunpack
SALib
shutil
scikit-learn
tensorflow-gpu==2.9.1
time-interpret==0.3.0
torch==1.13.1+cu116
tqdm
wget
169 changes: 84 additions & 85 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,81 @@
import random
import numpy as np

if __name__ == '__main__':
fix_seed = 2021
random.seed(fix_seed)
torch.manual_seed(fix_seed)
np.random.seed(fix_seed)
def main(args):
set_random_seed(args.seed)
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.devices = 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)

if args.task_name == 'classification':
Exp = Exp_Classification
else:
Exp = Exp_Long_Term_Forecast

parser = argparse.ArgumentParser(description='Run Timeseries')
if args.is_training:
for ii in range(args.itr):
# setting record of experiments
setting = stringify_setting(args, ii)

exp = Exp(args) # set experiments
print('>>>>>>>start training : {}>>>>>>>>>>>>>>>>>>>>>>>>>>'.format(setting))
exp.train(setting)

print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
exp.test(setting)
else:
setting = stringify_setting(args, 0)

exp = Exp(args) # set experiments
print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
exp.test(setting, test=1)

torch.cuda.empty_cache()


def stringify_setting(args, iteration):
setting = '{}_{}_{}_{}_ft{}_sl{}_ll{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_fc{}_eb{}_dt{}_{}_{}'.format(
args.task_name,
args.model_id,
args.model,
args.data,
args.features,
args.seq_len,
args.label_len,
args.pred_len,
args.d_model,
args.n_heads,
args.e_layers,
args.d_layers,
args.d_ff,
args.factor,
args.embed,
args.distil,
args.des, iteration
)
return setting

def get_parser():
parser = argparse.ArgumentParser(
description='Run Timeseries',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

# basic config
parser.add_argument('--task_name', type=str, required=True, default='long_term_forecast',
help='task name, options:[long_term_forecast, short_term_forecast, imputation, classification, anomaly_detection]')
parser.add_argument('--is_training', type=int, required=True, default=1, help='status')
parser.add_argument('--task_name', type=str, default='long_term_forecast',
choices=['long_term_forecast', 'classification'], help='task name')
parser.add_argument('--is_training', action='store_true', help='status')
parser.add_argument('--model_id', type=str, required=True, default='test', help='model id')
parser.add_argument('--model', type=str, required=True, default='Transformer',
choices=['Transformer', 'DLinear'], help='model name')
parser.add_argument('--seed', default=7, help='random seed')

# data loader
parser.add_argument('--data', type=str, required=True, default='ETTm1', help='dataset type')
Expand All @@ -33,7 +93,7 @@
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')
parser.add_argument('--no-scale', action='store_true', help='do not scale the dataset')
parser.add_argument('--group-id', type=str, default=None, help='Group identifier id for multiple timeseries')
parser.add_argument('--group-id', type=str, default=None, help='group identifier id for multiple timeseries')

# forecasting task
parser.add_argument('--seq_len', type=int, default=96, help='input sequence length')
Expand All @@ -44,9 +104,9 @@
# model define
parser.add_argument('--top_k', type=int, default=5, help='for TimesBlock')
parser.add_argument('--num_kernels', type=int, default=6, help='for Inception')
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('--enc_in', type=int, default=7, help='encoder input size, equal to number of input fetures.')
parser.add_argument('--dec_in', type=int, default=7, help='decoder input size, same as enc_in')
parser.add_argument('--c_out', type=int, default=7, help='output size, same as enc_in')
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')
Expand All @@ -64,7 +124,7 @@
parser.add_argument('--output_attention', action='store_true', help='whether to output attention in ecoder')

# optimization
parser.add_argument('--num_workers', type=int, default=10, help='data loader num workers')
parser.add_argument('--num_workers', type=int, default=0, help='data loader num workers')
parser.add_argument('--itr', type=int, default=1, help='experiments times')
parser.add_argument('--train_epochs', type=int, default=10, help='train epochs')
parser.add_argument('--batch_size', type=int, default=32, help='batch size of train input data')
Expand All @@ -76,7 +136,7 @@
parser.add_argument('--use_amp', action='store_true', help='use automatic mixed precision training', default=False)

# GPU
parser.add_argument('--use_gpu', type=bool, default=True, help='use gpu')
parser.add_argument('--use_gpu', action='store_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')
Expand All @@ -85,76 +145,15 @@
parser.add_argument('--p_hidden_dims', type=int, nargs='+', default=[128, 128],
help='hidden layer dimensions of projector (List)')
parser.add_argument('--p_hidden_layers', type=int, default=2, help='number of hidden layers in projector')

return parser

def set_random_seed(seed):
random.seed(seed)
torch.manual_seed(seed)
np.random.seed(seed)

if __name__ == '__main__':
parser = get_parser()
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.devices = 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)

if args.task_name == 'classification':
Exp = Exp_Classification
else:
Exp = Exp_Long_Term_Forecast

if args.is_training:
for ii in range(args.itr):
# setting record of experiments
setting = '{}_{}_{}_{}_ft{}_sl{}_ll{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_fc{}_eb{}_dt{}_{}_{}'.format(
args.task_name,
args.model_id,
args.model,
args.data,
args.features,
args.seq_len,
args.label_len,
args.pred_len,
args.d_model,
args.n_heads,
args.e_layers,
args.d_layers,
args.d_ff,
args.factor,
args.embed,
args.distil,
args.des, ii)

exp = Exp(args) # set experiments
print('>>>>>>>start training : {}>>>>>>>>>>>>>>>>>>>>>>>>>>'.format(setting))
exp.train(setting)

print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
exp.test(setting)
torch.cuda.empty_cache()
else:
ii = 0
setting = '{}_{}_{}_{}_ft{}_sl{}_ll{}_pl{}_dm{}_nh{}_el{}_dl{}_df{}_fc{}_eb{}_dt{}_{}_{}'.format(
args.task_name,
args.model_id,
args.model,
args.data,
args.features,
args.seq_len,
args.label_len,
args.pred_len,
args.d_model,
args.n_heads,
args.e_layers,
args.d_layers,
args.d_ff,
args.factor,
args.embed,
args.distil,
args.des, ii)

exp = Exp(args) # set experiments
print('>>>>>>>testing : {}<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<'.format(setting))
exp.test(setting, test=1)
torch.cuda.empty_cache()
main(args)
2 changes: 1 addition & 1 deletion scripts/Covid/Transformer.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
python run.py --is_training 1 --root_path ./dataset/covid/ --data_path Top_20.csv --target Cases --model_id covid_14_14 --model Transformer --data covid --features MS --seq_len 14 --label_len 7 --pred_len 14 --e_layers 2 --d_layers 1 --factor 3 --enc_in 10 --dec_in 10 --c_out 10 --des 'Exp' --freq d --group-id 'FIPS' --train_epochs 2 --itr 1 --task_name long_term_forecast
python run.py --is_training --root_path ./dataset/covid/ --data_path Top_20.csv --target Cases --model_id covid_14_14 --model Transformer --data covid --features MS --seq_len 14 --label_len 7 --pred_len 14 --e_layers 2 --d_layers 1 --factor 3 --enc_in 10 --dec_in 10 --c_out 10 --des Exp --freq d --group-id 'FIPS' --train_epochs 2 --itr 1 --task_name long_term_forecast
Loading

0 comments on commit 331c09f

Please sign in to comment.