├── Jupyter Scripts ├── GraphMarkovNet.ipynb └── utils.py ├── LICENSE ├── Python Scripts ├── .ipynb_checkpoints │ ├── Exp_GMN-checkpoint.py │ ├── Exp_baseline-checkpoint.py │ ├── GRUD-checkpoint.py │ └── LSTMD-checkpoint.py ├── Exp_GMN.py ├── Exp_baseline.py ├── GRUD.py ├── LSTMD.py ├── __pycache__ │ ├── models.cpython-37.pyc │ └── utils.cpython-37.pyc ├── job.sh ├── models.py └── utils.py ├── README.md ├── figures ├── GMN.png └── GMP.png └── requirements.txt /Jupyter Scripts/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.autograd import Variable 3 | import torch.utils.data as utils 4 | 5 | # import torch.utils.data as utils 6 | # import torch.nn.functional as F 7 | # import torch 8 | # import torch.nn as nn 9 | 10 | # from torch.nn.parameter import Parameter 11 | 12 | import math 13 | import numpy as np 14 | import copy 15 | import time 16 | 17 | def PrepareDataset(speed_matrix, \ 18 | BATCH_SIZE = 64, \ 19 | seq_len = 10, \ 20 | pred_len = 1, \ 21 | train_propotion = 0.7, \ 22 | valid_propotion = 0.2, \ 23 | mask_ones_proportion = 0.8, \ 24 | masking = True, \ 25 | masking_type = 'random', \ 26 | delta_last_obsv = False, \ 27 | shuffle = True, \ 28 | random_seed = 1024, \ 29 | ): 30 | """ Prepare training and testing datasets and dataloaders. 31 | 32 | Convert speed/volume/occupancy matrix to training and testing dataset. 33 | The vertical axis of speed_matrix is the time axis and the horizontal axis 34 | is the spatial axis. 35 | 36 | Args: 37 | speed_matrix: a Matrix containing spatial-temporal speed data for a network 38 | seq_len: length of input sequence 39 | pred_len: length of predicted sequence 40 | Returns: 41 | Training dataloader 42 | Testing dataloader 43 | """ 44 | 45 | print('Start Generate Data') 46 | print('\t batch_size:', BATCH_SIZE, '\t\t input_len:', seq_len, '\t\t\t pred_len:', pred_len) 47 | print('\t train_set:', train_propotion, '\t\t valid_set:', valid_propotion, '\t\t test_set:', np.around(1 - train_propotion - valid_propotion, decimals=2)) 48 | print('\t masking:', masking, '\t\t\t mask_ones_rate:', mask_ones_proportion, '\t\t delta & last_obsv:', delta_last_obsv) 49 | print('\t shuffle dataset:', shuffle, '\t\t random_seed:', random_seed) 50 | 51 | np.random.seed(random_seed) 52 | 53 | time_len = speed_matrix.shape[0] 54 | speed_matrix = speed_matrix.clip(0, 100) 55 | 56 | max_speed = speed_matrix.max().max() 57 | speed_matrix = speed_matrix / max_speed 58 | 59 | data_zero_values = np.where(speed_matrix == 0)[0].shape[0] 60 | data_zero_rate = data_zero_values / (speed_matrix.shape[0] * speed_matrix.shape[1]) 61 | print('Orignal dataset missing rate:', data_zero_rate) 62 | 63 | print('Generating input and labels...') 64 | if not masking: 65 | speed_sequences, speed_labels = [], [] 66 | for i in range(time_len - seq_len - pred_len): 67 | speed_sequences.append(speed_matrix.iloc[i:i+seq_len].values) 68 | speed_labels.append(speed_matrix.iloc[i+seq_len:i+seq_len+pred_len].values) 69 | speed_sequences, speed_labels = np.asarray(speed_sequences), np.asarray(speed_labels) 70 | print('Input sequences and labels are generated.') 71 | 72 | else: 73 | # using zero-one mask to randomly set elements to zeros 74 | # genertate mask 75 | 76 | if masking_type == 'random': 77 | Mask = np.random.choice([0,1], size=(speed_matrix.shape), p = [1 - mask_ones_proportion, mask_ones_proportion]) 78 | masked_speed_matrix = np.multiply(speed_matrix, Mask) 79 | Mask[np.where(masked_speed_matrix == 0)] = 0 80 | mask_zero_values = np.where(Mask == 0)[0].shape[0] / (Mask.shape[0] * Mask.shape[1]) 81 | print('\t Masked dataset missing rate:', np.around(mask_zero_values, decimals=4),'(mask zero rate:', np.around(1 - mask_ones_proportion, decimals=4), ')') 82 | 83 | speed_sequences, speed_labels = [], [] 84 | for i in range(time_len - seq_len - pred_len): 85 | speed_sequences.append(masked_speed_matrix.iloc[i:i+seq_len].values) 86 | speed_labels.append(speed_matrix.iloc[i+seq_len:i+seq_len+pred_len].values) 87 | speed_sequences, speed_labels = np.asarray(speed_sequences), np.asarray(speed_labels) 88 | print('Input sequences, labels, and masks are generated.') 89 | 90 | # Mask sequences 91 | Mask = np.ones_like(speed_sequences) 92 | Mask[np.where(speed_sequences == 0)] = 0 93 | 94 | if delta_last_obsv: 95 | # temporal information 96 | interval = 5 # 5 minutes 97 | S = np.zeros_like(speed_sequences) # time stamps 98 | for i in range(S.shape[1]): 99 | S[:,i,:] = interval * i 100 | 101 | Delta = np.zeros_like(speed_sequences) # time intervals 102 | for i in range(1, S.shape[1]): 103 | Delta[:,i,:] = S[:,i,:] - S[:,i-1,:] 104 | 105 | Delta = Delta / Delta.max() # normalize 106 | 107 | missing_index = np.where(speed_sequences == 0) 108 | 109 | X_last_obsv = np.copy(speed_sequences) 110 | for idx in range(missing_index[0].shape[0]): 111 | i = missing_index[0][idx] 112 | j = missing_index[1][idx] 113 | k = missing_index[2][idx] 114 | if j != 0 and j != seq_len-1: 115 | Delta[i,j+1,k] = Delta[i,j+1,k] + Delta[i,j,k] 116 | if j != 0: 117 | X_last_obsv[i,j,k] = X_last_obsv[i,j-1,k] # last observation 118 | 119 | print('Time intervals of missing values and last_observations are generated.') 120 | 121 | # shuffle and split the dataset to training and testing datasets 122 | print('Start to shuffle dataset ...') 123 | if shuffle: 124 | sample_size = speed_sequences.shape[0] 125 | index = np.arange(sample_size, dtype = int) 126 | np.random.shuffle(index) 127 | speed_sequences = speed_sequences[index] 128 | speed_labels = speed_labels[index] 129 | if masking: 130 | Mask = Mask[index] 131 | if delta_last_obsv: 132 | Delta = Delta[index] 133 | X_last_obsv = X_last_obsv[index] 134 | print('Dataset Shuffled. Start to split dataset ...') 135 | 136 | if not masking: 137 | dataset_agger = speed_sequences 138 | else: 139 | speed_sequences = np.expand_dims(speed_sequences, axis=1) 140 | Mask = np.expand_dims(Mask, axis=1) 141 | if delta_last_obsv: 142 | Delta = np.expand_dims(Delta, axis=1) 143 | X_last_obsv = np.expand_dims(X_last_obsv, axis=1) 144 | dataset_agger = np.concatenate((speed_sequences, Mask, Delta, X_last_obsv), axis = 1) 145 | else: 146 | dataset_agger = np.concatenate((speed_sequences, Mask), axis = 1) 147 | 148 | train_index = int(np.floor(sample_size * train_propotion)) 149 | valid_index = int(np.floor(sample_size * ( train_propotion + valid_propotion))) 150 | 151 | if masking: 152 | train_data, train_label = dataset_agger[:train_index], speed_labels[:train_index] 153 | valid_data, valid_label = dataset_agger[train_index:valid_index], speed_labels[train_index:valid_index] 154 | test_data, test_label = dataset_agger[valid_index:], speed_labels[valid_index:] 155 | else: 156 | train_data, train_label = speed_sequences[:train_index], speed_labels[:train_index] 157 | valid_data, valid_label = speed_sequences[train_index:valid_index], speed_labels[train_index:valid_index] 158 | test_data, test_label = speed_sequences[valid_index:], speed_labels[valid_index:] 159 | 160 | train_data, train_label = torch.Tensor(train_data), torch.Tensor(train_label) 161 | valid_data, valid_label = torch.Tensor(valid_data), torch.Tensor(valid_label) 162 | test_data, test_label = torch.Tensor(test_data), torch.Tensor(test_label) 163 | 164 | train_dataset = utils.TensorDataset(train_data, train_label) 165 | valid_dataset = utils.TensorDataset(valid_data, valid_label) 166 | test_dataset = utils.TensorDataset(test_data, test_label) 167 | 168 | train_dataloader = utils.DataLoader(train_dataset, batch_size = BATCH_SIZE, shuffle=True, drop_last = True) 169 | valid_dataloader = utils.DataLoader(valid_dataset, batch_size = BATCH_SIZE, shuffle=True, drop_last = True) 170 | test_dataloader = utils.DataLoader(test_dataset, batch_size = BATCH_SIZE, shuffle=False, drop_last = False) 171 | 172 | X_mean = np.mean(speed_sequences, axis = 0) 173 | 174 | print('Finished') 175 | 176 | return train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean 177 | 178 | 179 | 180 | def TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-5, optm = 'Adam', num_epochs = 300, patience = 10, min_delta = 0.00001): 181 | 182 | inputs, labels = next(iter(train_dataloader)) 183 | [batch_size, type_size, step_size, fea_size] = inputs.size() 184 | input_dim = fea_size 185 | hidden_dim = fea_size 186 | output_dim = fea_size 187 | 188 | model.cuda() 189 | 190 | loss_MSE = torch.nn.MSELoss() 191 | loss_L1 = torch.nn.L1Loss() 192 | 193 | learning_rate = learning_rate 194 | if optm == 'Adam': 195 | optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate) 196 | elif optm == 'Adadelta': 197 | optimizer = torch.optim.Adadelta(model.parameters(), lr = learning_rate) 198 | elif optm == 'RMSprop': 199 | optimizer = torch.optim.RMSprop(model.parameters(), lr = learning_rate) 200 | 201 | use_gpu = torch.cuda.is_available() 202 | 203 | losses_train = [] 204 | losses_valid = [] 205 | losses_epochs_train = [] 206 | losses_epochs_valid = [] 207 | 208 | train_time_list = [] 209 | valid_time_list = [] 210 | 211 | cur_time = time.time() 212 | pre_time = time.time() 213 | 214 | # Variables for Early Stopping 215 | is_best_model = 0 216 | patient_epoch = 0 217 | 218 | losses_epochs_valid_steps_l1 = [] 219 | 220 | sub_epoch = 1 221 | 222 | for epoch in range(0, num_epochs): 223 | # print('Epoch {}/{}'.format(epoch, num_epochs - 1)) 224 | # print('-' * 10) 225 | 226 | losses_epoch_train = [] 227 | losses_epoch_valid = [] 228 | 229 | train_start = time.time() 230 | 231 | for data in train_dataloader: 232 | inputs, labels = data 233 | 234 | if inputs.shape[0] != batch_size: 235 | continue 236 | 237 | if use_gpu: 238 | inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) 239 | else: 240 | inputs, labels = Variable(inputs), Variable(labels) 241 | 242 | model.zero_grad() 243 | 244 | outputs = model(inputs) 245 | 246 | if len(labels[labels==0]): 247 | label_mask = torch.ones_like(labels).cuda() 248 | label_mask = label_mask * labels 249 | label_mask[label_mask!=0] = 1 250 | loss_train = loss_MSE(outputs * label_mask, torch.squeeze(labels)) 251 | else: 252 | loss_train = loss_MSE(outputs, torch.squeeze(labels)) 253 | # print(loss_train.item()) 254 | # print([loss_train.data[0]]) 255 | # print(loss_train.data.cpu().numpy()[0]) 256 | # print(loss_train.item()) 257 | losses_epoch_train.append(loss_train.item()) 258 | 259 | optimizer.zero_grad() 260 | 261 | loss_train.backward() 262 | 263 | optimizer.step() 264 | 265 | train_end = time.time() 266 | 267 | # validation 268 | valid_start = time.time() 269 | 270 | losses_l1_allsteps_val = [] 271 | 272 | for data in valid_dataloader: 273 | 274 | inputs_val, labels_val = data 275 | 276 | if use_gpu: 277 | inputs_val, labels_val = Variable(inputs_val.cuda()), Variable(labels_val.cuda()) 278 | else: 279 | inputs_val, labels_val = Variable(inputs_val), Variable(labels_val) 280 | 281 | outputs_val= model(inputs_val) 282 | 283 | if len(labels_val[labels_val==0]): 284 | labels_val_mask = torch.ones_like(labels_val).cuda() 285 | labels_val_mask = labels_val_mask * labels_val 286 | labels_val_mask[labels_val_mask!=0] = 1 287 | 288 | loss_valid = loss_MSE(outputs_val * labels_val_mask, torch.squeeze(labels_val)) 289 | else: 290 | loss_valid = loss_MSE(outputs_val , torch.squeeze(labels_val)) 291 | 292 | 293 | # print(inputs_val[:,1:,:].shape) 294 | 295 | # print(labels_val.shape) 296 | 297 | # full_labels_val = torch.cat((inputs_val[:,1:,:], labels_val), dim = 1) 298 | 299 | # delta = torch.abs(full_labels_val - outputs_val) 300 | 301 | # delta_batch_mean = torch.mean(delta, dim = 0) 302 | 303 | # delta_batch_spatial_mean = torch.mean(delta_batch_mean, dim = 1) 304 | 305 | # losses_l1_allsteps_val.append(delta_batch_spatial_mean.data.cpu().numpy()) 306 | 307 | # if losses_l1_allsteps_val is None: 308 | # losses_l1_allsteps_val = delta_batch_spatial_mean 309 | # else: 310 | # losses_l1_allsteps_val += delta_batch_spatial_mean 311 | 312 | # # mean l1 loss for each step of the sequences of a batched sample 313 | # mean_loss_l1_allsteps = torch.mean(torch.mean(torch.abs(full_labels_val - outputs_val), dim = 0), dim = 1) 314 | 315 | # # print(mean_loss_l1_allsteps) 316 | # losses_l1_allsteps_val.append(delta_batch_spatial_mean.data.cpu().numpy(), ) 317 | 318 | losses_epoch_valid.append(loss_valid.item()) 319 | 320 | valid_end = time.time() 321 | 322 | avg_losses_epoch_train = sum(losses_epoch_train) / float(len(losses_epoch_train)) 323 | avg_losses_epoch_valid = sum(losses_epoch_valid) / float(len(losses_epoch_valid)) 324 | losses_epochs_train.append(avg_losses_epoch_train) 325 | losses_epochs_valid.append(avg_losses_epoch_valid) 326 | 327 | # print(losses_l1_allsteps_val) 328 | 329 | if losses_l1_allsteps_val: 330 | losses_l1_allsteps_val = np.asarray(losses_l1_allsteps_val) 331 | losses_l1_allsteps_mean = np.mean(losses_l1_allsteps_val, 0) 332 | losses_epochs_valid_steps_l1.append(losses_l1_allsteps_mean) 333 | # print(losses_l1_allsteps_val) 334 | # # losses_l1_allsteps_mean = (losses_l1_allsteps_mean / valid_batch_number).data.cpu().numpy() 335 | # # losses_l1_allsteps_mean = torch.mean(losses_l1_allsteps_val, dim = 0).data.cpu().numpy() 336 | # # # print(losses_l1_allsteps_mean.shape) 337 | # # # losses_epochs_valid_steps_l1.append(losses_l1_allsteps_mean) 338 | 339 | fig, ax = plt.subplots( nrows=1, ncols=1 ) # create figure & 1 axis 340 | ax.plot(losses_l1_allsteps_mean) 341 | plt.ylim((0, 0.1)) 342 | plt.title('Epoch: ' + str(epoch)) 343 | plt.ylabel('MSE') 344 | plt.xlabel('Step') 345 | fig.savefig('./Figures/' + type(model).__name__ + '_' + str(epoch) + '.png') # save the figure to file 346 | plt.close(fig) 347 | 348 | # Early Stopping 349 | if epoch == 0: 350 | is_best_model = 1 351 | best_model = copy.deepcopy(model) 352 | min_loss_epoch_valid = 10000.0 353 | if avg_losses_epoch_valid < min_loss_epoch_valid: 354 | min_loss_epoch_valid = avg_losses_epoch_valid 355 | 356 | # sub_epoch += 1 357 | else: 358 | if min_loss_epoch_valid - avg_losses_epoch_valid > min_delta: 359 | is_best_model = 1 360 | best_model = model 361 | min_loss_epoch_valid = avg_losses_epoch_valid 362 | patient_epoch = 0 363 | else: 364 | is_best_model = 0 365 | patient_epoch += 1 366 | if patient_epoch >= patience: 367 | print('Early Stopped at Epoch:', epoch) 368 | break 369 | 370 | if (epoch >= 5 and (patient_epoch == 4 or sub_epoch % 50 == 0)) and learning_rate > 1e-5: 371 | learning_rate = learning_rate / 10 372 | if optm == 'Adam': 373 | optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate) 374 | elif optm == 'Adadelta': 375 | optimizer = torch.optim.Adadelta(model.parameters(), lr = learning_rate) 376 | elif optm == 'RMSprop': 377 | optimizer = torch.optim.RMSprop(model.parameters(), lr = learning_rate) 378 | sub_epoch = 1 379 | else: 380 | sub_epoch += 1 381 | 382 | 383 | # Print training parameters 384 | cur_time = time.time() 385 | train_time = np.around([train_end - train_start] , decimals=2) 386 | train_time_list.append(train_time) 387 | valid_time = np.around([valid_end - valid_start] , decimals=2) 388 | valid_time_list.append(valid_time) 389 | 390 | print('Epoch: {}, train_loss: {}, valid_loss: {}, lr: {}, train_time: {}, valid_time: {}, best model: {}'.format( \ 391 | epoch, \ 392 | np.around(avg_losses_epoch_train, decimals=8),\ 393 | np.around(avg_losses_epoch_valid, decimals=8),\ 394 | learning_rate,\ 395 | np.around([train_end - train_start] , decimals=2),\ 396 | np.around([valid_end - valid_start] , decimals=2),\ 397 | is_best_model) ) 398 | pre_time = cur_time 399 | 400 | train_time_avg = np.mean(np.array(train_time_list)) 401 | valid_time_avg = np.mean(np.array(valid_time_list)) 402 | 403 | return best_model, [losses_train, losses_valid, losses_epochs_train, losses_epochs_valid, losses_epochs_valid_steps_l1, train_time_avg, valid_time_avg] 404 | 405 | 406 | 407 | def TestModel(model, test_dataloader, max_speed): 408 | 409 | inputs, labels = next(iter(test_dataloader)) 410 | [batch_size, step_size, step_size, fea_size] = inputs.size() 411 | 412 | cur_time = time.time() 413 | pre_time = time.time() 414 | 415 | use_gpu = torch.cuda.is_available() 416 | 417 | loss_MSE = torch.nn.MSELoss() 418 | loss_L1 = torch.nn.L1Loss() 419 | 420 | tested_batch = 0 421 | 422 | losses_mse = [] 423 | losses_l1 = [] 424 | 425 | output_list = [] 426 | label_list = [] 427 | 428 | losses_l1_allsteps = None 429 | 430 | for data in test_dataloader: 431 | inputs, labels = data 432 | 433 | if inputs.shape[0] != batch_size: 434 | continue 435 | 436 | if use_gpu: 437 | inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) 438 | else: 439 | inputs, labels = Variable(inputs), Variable(labels) 440 | 441 | outputs = model(inputs) 442 | 443 | loss_mse = loss_MSE(outputs, torch.squeeze(labels)) 444 | loss_l1 = loss_L1(outputs, torch.squeeze(labels)) 445 | 446 | losses_mse.append(loss_mse.cpu().data.numpy()) 447 | losses_l1.append(loss_l1.cpu().data.numpy()) 448 | 449 | output_list.append(torch.squeeze(outputs).cpu().data.numpy()) 450 | label_list.append(torch.squeeze(labels).cpu().data.numpy()) 451 | 452 | tested_batch += 1 453 | 454 | if tested_batch % 1000 == 0: 455 | cur_time = time.time() 456 | print('Tested #: {}, loss_l1: {}, loss_mse: {}, time: {}'.format( \ 457 | tested_batch * batch_size, \ 458 | np.around([loss_l1.data[0]], decimals=8), \ 459 | np.around([loss_mse.data[0]], decimals=8), \ 460 | np.around([cur_time - pre_time], decimals=8) ) ) 461 | pre_time = cur_time 462 | # print(losses_l1) 463 | losses_l1 = np.array(losses_l1) 464 | losses_mse = np.array(losses_mse) 465 | output_list = np.array(output_list) 466 | label_list = np.array(label_list) 467 | 468 | non_zero_index = np.nonzero(label_list) 469 | MAE = np.mean(np.absolute(output_list[non_zero_index] - label_list[non_zero_index])) * max_speed 470 | RMSE = np.sqrt(np.mean(np.square(output_list[non_zero_index]* max_speed - label_list[non_zero_index]* max_speed))) 471 | MAPE = np.mean(np.absolute(output_list[non_zero_index] - label_list[non_zero_index])/label_list[non_zero_index]) * 100 472 | MAE = np.around(MAE, decimals=3) 473 | RMSE = np.around(RMSE, decimals=3) 474 | MAPE = np.around(MAPE, decimals=3) 475 | print('Tested: MAE: {}, RMSE : {}, MAPE : {} %, '.format( MAE, RMSE, MAPE)) 476 | return [losses_l1, losses_mse, None, MAE, RMSE, MAPE, losses_l1_allsteps] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zhiyong Cui (UW) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Python Scripts/.ipynb_checkpoints/Exp_GMN-checkpoint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import torch.utils.data as utils 6 | import torch.nn.functional as F 7 | import torch 8 | import torch.nn as nn 9 | from torch.autograd import Variable 10 | from torch.nn.parameter import Parameter 11 | import math 12 | import numpy as np 13 | import pandas as pd 14 | import time 15 | import pickle 16 | import os 17 | 18 | import matplotlib.pyplot as plt 19 | import matplotlib.patches as patches 20 | # %matplotlib inline 21 | # plt.ioff() 22 | # plt.ion() 23 | 24 | import sys 25 | 26 | if not sys.warnoptions: 27 | import warnings 28 | warnings.simplefilter("ignore") 29 | 30 | sys.path.append('../') 31 | 32 | 33 | import models 34 | import utils 35 | import importlib 36 | 37 | import argparse 38 | 39 | dtype = torch.float32 40 | if torch.cuda.is_available(): 41 | device = torch.device("cuda:1") 42 | else: 43 | device = torch.device("cpu") 44 | 45 | torch.cuda.set_device(1) 46 | 47 | 48 | 49 | # Load data 50 | 51 | def loadDataset(dataset = None): 52 | if dataset == 'PEMS-BAY': 53 | speed_matrix = pd.read_hdf('../Data/PEMS-BAY/pems-bay.h5') 54 | A = pd.read_pickle('../Data/PEMS-BAY/adj_mx_bay.pkl') 55 | A = A[2] 56 | A[np.where(A != 0)] = 1 57 | for i in range(0, A.shape[0]): 58 | for j in range(i, A.shape[0]): 59 | if A[i,j] == 1: 60 | A[j,i] = 1 61 | elif dataset == 'METR-LA': 62 | speed_matrix = pd.read_hdf('../Data/METR-LA/metr-la.h5') 63 | A = pd.read_pickle('../Data/METR-LA/adj_mx.pkl') 64 | A = A[2] 65 | A[np.where(A != 0)] = 1 66 | for i in range(0, A.shape[0]): 67 | for j in range(i, A.shape[0]): 68 | if A[i,j] == 1: 69 | A[j,i] = 1 70 | elif dataset == 'LOOP-SEA': 71 | speed_matrix = pd.read_pickle('../Data/LOOP-SEA/speed_matrix_2015_1mph') 72 | A = np.load('../Data/LOOP-SEA/Loop_Seattle_2015_A.npy') 73 | elif dataset == 'INRIX-SEA': 74 | speed_matrix = pd.read_pickle('../Data/INRIX-SEA/INRIX_Seattle_Speed_Matrix__2012_v2.pkl') 75 | A = np.load('../Data/INRIX-SEA/INRIX_Seattle_Adjacency_matrix_2012_v2.npy') 76 | else: 77 | print('Dataset not found.') 78 | return None, None 79 | print('Dataset loaded.') 80 | return speed_matrix, A 81 | 82 | 83 | # def StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed = 1024, save_model=True): 84 | # result_dict[model_name] = {} 85 | # result_dict[model_name]['train_loss'] = train_result[2] 86 | # result_dict[model_name]['valid_loss'] = train_result[3] 87 | # result_dict[model_name]['MAE'] = test_result[3] 88 | # result_dict[model_name]['RMSE'] = test_result[4] 89 | # result_dict[model_name]['MAPE'] = test_result[5] 90 | # f = open(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl', "wb") 91 | # pickle.dump(result_dict,f) 92 | # f.close() 93 | # if save_model: 94 | # torch.save(model.state_dict(), directory + '/' + model_name) 95 | 96 | def StoreData(model_name, train_result, test_result, directory, model, random_seed = 1024, save_model=True): 97 | 98 | if os.path.isfile(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl'): 99 | f = open(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl', "rb") 100 | result_dict = pickle.load(f) 101 | f.close() 102 | else: 103 | result_dict = {} 104 | 105 | result_dict[model_name] = {} 106 | result_dict[model_name]['train_loss'] = train_result[2] 107 | result_dict[model_name]['valid_loss'] = train_result[3] 108 | result_dict[model_name]['train_time'] = train_result[5] 109 | result_dict[model_name]['valid_time'] = train_result[6] 110 | result_dict[model_name]['MAE'] = test_result[3] 111 | result_dict[model_name]['RMSE'] = test_result[4] 112 | result_dict[model_name]['MAPE'] = test_result[5] 113 | f = open(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl', "wb") 114 | pickle.dump(result_dict,f) 115 | f.close() 116 | if save_model: 117 | torch.save(model.state_dict(), directory + '/' + model_name) 118 | 119 | 120 | 121 | if __name__ == "__main__": 122 | 123 | # dataset == 'PEMS-BAY' 124 | # missing_rate = 0.2 125 | 126 | parser = argparse.ArgumentParser() 127 | 128 | parser.add_argument('-d', '-dataset', default='PEMS-BAY', type=str, required=True, help="specify dataset") 129 | parser.add_argument('-m', '-missingrate', default=0.2, type=float, required=True, help="specify missing rate") 130 | parser.add_argument('-o', '-optm', default='Adam', type=str, required=False, help="specify training optimizer") 131 | parser.add_argument('-l', '-learningrate', default=0.001, type=float, required=False, help="specify initial learning rate") 132 | parser.add_argument('-g', '-gamma', default=0.9, type=float, required=False, help="specify gamma for GMN") 133 | parser.add_argument('-t', '-maskingtype', default='random', type=str, required=False, help="specify masking type") 134 | parser.add_argument('-r', '-randomseed', default=1024, type=int, required=False, help="specify random seed") 135 | parser.add_argument('-s', '-savemodel', default=1, type=int, required=False, help="specify whether save model") 136 | args = parser.parse_args() 137 | 138 | dataset = args.d 139 | missing_rate = args.m 140 | optm = args.o 141 | learning_rate = args.l 142 | gamma = args.g 143 | random_seed = args.r 144 | masking_type = args.t 145 | save_model = args.s 146 | 147 | np.random.seed(random_seed) 148 | 149 | print('Exp: baseline models') 150 | print('\tDataset:', dataset) 151 | print('\tMissing rate:', missing_rate) 152 | print('\tOptimizer:', optm) 153 | print('\tLearnig ate:', learning_rate) 154 | print('\tGamma:', gamma) 155 | print('\tMasking type:', masking_type) 156 | print('\tRandom seed:', random_seed) 157 | if save_model==1: 158 | print('\tSave model:', 'True') 159 | else: 160 | print('\tSave model:', 'False') 161 | 162 | 163 | directory = './Masking_' + masking_type + '/GMN_' + str(dataset) + '_MR=' + str(missing_rate) + '_gamma=' + str(gamma) 164 | 165 | if not os.path.exists(directory): 166 | os.makedirs(directory) 167 | 168 | speed_matrix, A = loadDataset(dataset = dataset) 169 | 170 | 171 | importlib.reload(utils) 172 | from utils import PrepareDataset, TrainModel, TestModel 173 | 174 | 175 | importlib.reload(utils) 176 | from utils import PrepareDataset 177 | mask_ones_proportion = 1 - missing_rate 178 | train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean = PrepareDataset(speed_matrix, BATCH_SIZE = 64, seq_len = 10, pred_len = 1, \ 179 | train_propotion = 0.6, valid_propotion = 0.2, \ 180 | mask_ones_proportion = mask_ones_proportion, \ 181 | masking = True, masking_type = masking_type, delta_last_obsv = False, \ 182 | shuffle = True, random_seed = random_seed) 183 | 184 | 185 | inputs, labels = next(iter(train_dataloader)) 186 | [batch_size, type_size, step_size, fea_size] = inputs.size() 187 | 188 | 189 | importlib.reload(utils) 190 | from utils import TrainModel, TestModel 191 | 192 | # SGNN-6 193 | importlib.reload(models) 194 | from models import SGNN 195 | importlib.reload(utils) 196 | from utils import TrainModel, TestModel 197 | model_name = 'SGMN6' 198 | print(model_name) 199 | model = SGNN(A, layer = 6, gamma = gamma) 200 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 201 | test_result = TestModel(model, test_dataloader, max_speed) 202 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 203 | 204 | # SGNN-8 205 | importlib.reload(models) 206 | from models import SGNN 207 | importlib.reload(utils) 208 | from utils import TrainModel, TestModel 209 | model_name = 'SGMN8' 210 | print(model_name) 211 | model = SGNN(A, layer = 8, gamma = gamma) 212 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 213 | test_result = TestModel(model, test_dataloader, max_speed) 214 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 215 | 216 | # SGNN-10 217 | importlib.reload(models) 218 | from models import SGNN 219 | importlib.reload(utils) 220 | from utils import TrainModel, TestModel 221 | model_name = 'SGMN10' 222 | print(model_name) 223 | model = SGNN(A, layer = 10, gamma = gamma) 224 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 225 | test_result = TestModel(model, test_dataloader, max_speed) 226 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 227 | 228 | # GNN-6 229 | importlib.reload(models) 230 | from models import GNN 231 | importlib.reload(utils) 232 | from utils import TrainModel, TestModel 233 | model_name = 'GMN6' 234 | print(model_name) 235 | model = GNN(A, layer = 6, gamma = gamma) 236 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 237 | test_result = TestModel(model, test_dataloader, max_speed) 238 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 239 | 240 | # GNN-8 241 | importlib.reload(models) 242 | from models import GNN 243 | importlib.reload(utils) 244 | from utils import TrainModel, TestModel 245 | model_name = 'GMN8' 246 | print(model_name) 247 | model = GNN(A, layer = 8, gamma = gamma) 248 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 249 | test_result = TestModel(model, test_dataloader, max_speed) 250 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 251 | 252 | # GNN-10 253 | importlib.reload(models) 254 | from models import GNN 255 | importlib.reload(utils) 256 | from utils import TrainModel, TestModel 257 | model_name = 'GMN10' 258 | print(model_name) 259 | model = GNN(A, layer = 10, gamma = gamma) 260 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 261 | test_result = TestModel(model, test_dataloader, max_speed) 262 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 263 | 264 | 265 | 266 | # # ANN 267 | # importlib.reload(models) 268 | # from models import ANN 269 | # importlib.reload(utils) 270 | # from utils import TrainModel, TestModel 271 | # model_name = 'FCMN' 272 | # print(model_name) 273 | # model = ANN(A, layer = 10, gamma = 0.9) 274 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 275 | # test_result = TestModel(model, test_dataloader, max_speed) 276 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 277 | 278 | # # LSTM 279 | # importlib.reload(models) 280 | # from models import LSTM 281 | # importlib.reload(utils) 282 | # from utils import TrainModel, TestModel 283 | # model_name = 'LSTM' 284 | # model = LSTM(A.shape[0]) 285 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 286 | # test_result = TestModel(model, test_dataloader, max_speed) 287 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 288 | 289 | # # LSTM-I 290 | # importlib.reload(models) 291 | # from models import LSTM 292 | # importlib.reload(utils) 293 | # from utils import TrainModel, TestModel 294 | # model_name = 'LSTMI' 295 | # model = LSTM(A.shape[0], imputation = True) 296 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 297 | # test_result = TestModel(model, test_dataloader, max_speed) 298 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 299 | 300 | # # LSTM-D 301 | # import LSTMD 302 | # importlib.reload(LSTMD) 303 | # from LSTMD import LSTMD 304 | # importlib.reload(utils) 305 | # from utils import TrainModel, TestModel 306 | # model_name = 'LSTMD' 307 | # print(model_name) 308 | # model = LSTMD(fea_size, fea_size, fea_size, X_mean) 309 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 310 | # test_result = TestModel(model, test_dataloader, max_speed) 311 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 312 | 313 | # # GRU 314 | # importlib.reload(models) 315 | # from models import GRU 316 | # importlib.reload(utils) 317 | # from utils import TrainModel, TestModel 318 | # model_name = 'GRU' 319 | # print(model_name) 320 | # gru = GRU(A.shape[0]) 321 | # gru, train_result = TrainModel(gru, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 322 | # test_result = TestModel(gru, test_dataloader, max_speed) 323 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 324 | 325 | # # GRU-I 326 | # importlib.reload(models) 327 | # from models import GRU 328 | # importlib.reload(utils) 329 | # from utils import TrainModel, TestModel 330 | # model_name = 'GRUI' 331 | # print(model_name) 332 | # model = GRU(A.shape[0], imputation = True) 333 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 334 | # test_result = TestModel(model, test_dataloader, max_speed) 335 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 336 | 337 | # # GRU-D 338 | # import GRUD 339 | # importlib.reload(GRUD) 340 | # from GRUD import GRUD 341 | # importlib.reload(utils) 342 | # from utils import TrainModel, TestModel 343 | # model_name = 'GRUD' 344 | # print(model_name) 345 | # model = GRUD(fea_size, fea_size, fea_size, X_mean) 346 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 347 | # test_result = TestModel(model, test_dataloader, max_speed) 348 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 349 | 350 | # # Graph GRU-D 351 | # import GGRUD 352 | # importlib.reload(GGRUD) 353 | # from GGRUD import GGRUD 354 | # importlib.reload(utils) 355 | # from utils import TrainModel, TestModel 356 | # model_name = 'GGRUD' 357 | # print(model_name) 358 | # model = GGRUD(A, fea_size, fea_size, fea_size, X_mean) 359 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-3, patience = 5) 360 | # test_result = TestModel(model, test_dataloader, max_speed) 361 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 362 | 363 | # # GCLSTM 364 | # importlib.reload(models) 365 | # from models import GCLSTM 366 | # importlib.reload(utils) 367 | # from utils import TrainModel, TestModel 368 | # model_name = 'GCLSTM' 369 | # print(model_name) 370 | # model = GCLSTM(A) 371 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-2, patience = 5) 372 | # test_result = TestModel(model, test_dataloader, max_speed) 373 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 374 | 375 | 376 | 377 | 378 | -------------------------------------------------------------------------------- /Python Scripts/.ipynb_checkpoints/Exp_baseline-checkpoint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import torch.utils.data as utils 6 | import torch.nn.functional as F 7 | import torch 8 | import torch.nn as nn 9 | from torch.autograd import Variable 10 | from torch.nn.parameter import Parameter 11 | import math 12 | import numpy as np 13 | import pandas as pd 14 | import time 15 | import pickle 16 | import os 17 | 18 | import matplotlib.pyplot as plt 19 | import matplotlib.patches as patches 20 | # %matplotlib inline 21 | # plt.ioff() 22 | # plt.ion() 23 | 24 | 25 | import sys 26 | 27 | if not sys.warnoptions: 28 | import warnings 29 | warnings.simplefilter("ignore") 30 | 31 | sys.path.append('../') 32 | 33 | 34 | import models 35 | import utils 36 | import importlib 37 | 38 | import argparse 39 | 40 | 41 | dtype = torch.float32 42 | if torch.cuda.is_available(): 43 | device = torch.device("cuda:1") 44 | else: 45 | device = torch.device("cpu") 46 | 47 | torch.cuda.set_device(1) 48 | 49 | 50 | 51 | # Load data 52 | 53 | def loadDataset(dataset = None): 54 | if dataset == 'PEMS-BAY': 55 | speed_matrix = pd.read_hdf('../Data/PEMS-BAY/pems-bay.h5') 56 | A = pd.read_pickle('../Data/PEMS-BAY/adj_mx_bay.pkl') 57 | A = A[2] 58 | A[np.where(A != 0)] = 1 59 | for i in range(0, A.shape[0]): 60 | for j in range(i, A.shape[0]): 61 | if A[i,j] == 1: 62 | A[j,i] = 1 63 | elif dataset == 'METR-LA': 64 | speed_matrix = pd.read_hdf('../Data/METR-LA/metr-la.h5') 65 | A = pd.read_pickle('../Data/METR-LA/adj_mx.pkl') 66 | A = A[2] 67 | A[np.where(A != 0)] = 1 68 | for i in range(0, A.shape[0]): 69 | for j in range(i, A.shape[0]): 70 | if A[i,j] == 1: 71 | A[j,i] = 1 72 | elif dataset == 'LOOP-SEA': 73 | speed_matrix = pd.read_pickle('../Data/LOOP-SEA/speed_matrix_2015_1mph') 74 | A = np.load('../Data/LOOP-SEA/Loop_Seattle_2015_A.npy') 75 | elif dataset == 'INRIX-SEA': 76 | speed_matrix = pd.read_pickle('../Data/INRIX-SEA/INRIX_Seattle_Speed_Matrix__2012_v2.pkl') 77 | A = np.load('../Data/INRIX-SEA/INRIX_Seattle_Adjacency_matrix_2012_v2.npy') 78 | else: 79 | print('Dataset not found.') 80 | return None, None 81 | print('Dataset loaded.') 82 | return speed_matrix, A 83 | 84 | 85 | def StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed = 1024, save_model=True): 86 | result_dict[model_name] = {} 87 | result_dict[model_name]['train_loss'] = train_result[2] 88 | result_dict[model_name]['valid_loss'] = train_result[3] 89 | result_dict[model_name]['MAE'] = test_result[3] 90 | result_dict[model_name]['RMSE'] = test_result[4] 91 | result_dict[model_name]['MAPE'] = test_result[5] 92 | f = open(directory + '/log_rs_' + str(random_seed) + '.pkl', "wb") 93 | pickle.dump(result_dict,f) 94 | f.close() 95 | if save_model: 96 | torch.save(model.state_dict(), directory + '/' + model_name) 97 | 98 | 99 | 100 | if __name__ == "__main__": 101 | 102 | parser = argparse.ArgumentParser() 103 | 104 | parser.add_argument('-d', '-dataset', default='PEMS-BAY', type=str, required=True, help="specify dataset") 105 | parser.add_argument('-m', '-missingrate', default=0.2, type=float, required=True, help="specify missing rate") 106 | parser.add_argument('-o', '-optm', default='Adam', type=str, required=False, help="specify training optimizer") 107 | parser.add_argument('-l', '-learningrate', default=0.001, type=float, required=False, help="specify initial learning rate") 108 | parser.add_argument('-t', '-maskingtype', default='random', type=str, required=False, help="specify masking type") 109 | parser.add_argument('-r', '-randomseed', default=1024, type=int, required=False, help="specify random seed") 110 | parser.add_argument('-s', '-savemodel', default=1, type=int, required=False, help="specify whether save model") 111 | args = parser.parse_args() 112 | 113 | dataset = args.d 114 | missing_rate = args.m 115 | optm = args.o 116 | learning_rate = args.l 117 | random_seed = args.r 118 | masking_type = args.t 119 | save_model = args.s 120 | 121 | np.random.seed(random_seed) 122 | 123 | print('Exp: baseline models') 124 | print('\tDataset:', dataset) 125 | print('\tMissing rate:', missing_rate) 126 | print('\tOptimizer:', optm) 127 | print('\tLearnig ate:', learning_rate) 128 | print('\tMasking type:', masking_type) 129 | print('\tRandom seed:', random_seed) 130 | 131 | if save_model==1: 132 | print('\tSave model:', 'True') 133 | else: 134 | print('\tSave model:', 'False') 135 | 136 | directory = './Masking_' + masking_type + '/' + str(dataset) + '_MR=' + str(missing_rate) 137 | 138 | if not os.path.exists(directory): 139 | os.makedirs(directory) 140 | 141 | result_dict = {} 142 | 143 | 144 | speed_matrix, A = loadDataset(dataset = dataset) 145 | 146 | 147 | importlib.reload(utils) 148 | from utils import PrepareDataset, TrainModel, TestModel 149 | 150 | 151 | importlib.reload(utils) 152 | from utils import PrepareDataset 153 | mask_ones_proportion = 1 - missing_rate 154 | train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean = PrepareDataset(speed_matrix, BATCH_SIZE = 64, seq_len = 10, pred_len = 1, \ 155 | train_propotion = 0.6, valid_propotion = 0.2, \ 156 | mask_ones_proportion = mask_ones_proportion, \ 157 | masking = True, masking_type = masking_type, delta_last_obsv = True, \ 158 | shuffle = True, random_seed = random_seed) 159 | 160 | 161 | inputs, labels = next(iter(train_dataloader)) 162 | [batch_size, type_size, step_size, fea_size] = inputs.size() 163 | 164 | 165 | importlib.reload(utils) 166 | from utils import TrainModel, TestModel 167 | 168 | 169 | # # ANN 170 | # importlib.reload(models) 171 | # from models import ANN 172 | # importlib.reload(utils) 173 | # from utils import TrainModel, TestModel 174 | # model_name = 'FCMN' 175 | # print(model_name) 176 | # model = ANN(A, layer = 7, gamma = 0.9) 177 | # model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-2, patience = 5) 178 | # test_result = TestModel(model, test_dataloader, max_speed) 179 | # StoreData(result_dict, model_name, train_result, test_result, directory, model) 180 | 181 | # LSTM 182 | importlib.reload(models) 183 | from models import LSTM 184 | importlib.reload(utils) 185 | from utils import TrainModel, TestModel 186 | model_name = 'LSTM' 187 | print(model_name) 188 | model = LSTM(A.shape[0]) 189 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 190 | test_result = TestModel(model, test_dataloader, max_speed) 191 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 192 | 193 | # LSTM-I 194 | importlib.reload(models) 195 | from models import LSTM 196 | importlib.reload(utils) 197 | from utils import TrainModel, TestModel 198 | model_name = 'LSTMI' 199 | print(model_name) 200 | model = LSTM(A.shape[0], imputation = True) 201 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 202 | test_result = TestModel(model, test_dataloader, max_speed) 203 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 204 | 205 | # LSTM-D 206 | import LSTMD 207 | importlib.reload(LSTMD) 208 | from LSTMD import LSTMD 209 | importlib.reload(utils) 210 | from utils import TrainModel, TestModel 211 | model_name = 'LSTMD' 212 | print(model_name) 213 | model = LSTMD(fea_size, fea_size, fea_size, X_mean) 214 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 215 | test_result = TestModel(model, test_dataloader, max_speed) 216 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 217 | 218 | # GRU 219 | importlib.reload(models) 220 | from models import GRU 221 | importlib.reload(utils) 222 | from utils import TrainModel, TestModel 223 | model_name = 'GRU' 224 | print(model_name) 225 | gru = GRU(A.shape[0]) 226 | gru, train_result = TrainModel(gru, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 227 | test_result = TestModel(gru, test_dataloader, max_speed) 228 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 229 | 230 | # GRU-I 231 | importlib.reload(models) 232 | from models import GRU 233 | importlib.reload(utils) 234 | from utils import TrainModel, TestModel 235 | model_name = 'GRUI' 236 | print(model_name) 237 | model = GRU(A.shape[0], imputation = True) 238 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 239 | test_result = TestModel(model, test_dataloader, max_speed) 240 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 241 | 242 | # GRU-D 243 | import GRUD 244 | importlib.reload(GRUD) 245 | from GRUD import GRUD 246 | importlib.reload(utils) 247 | from utils import TrainModel, TestModel 248 | model_name = 'GRUD' 249 | print(model_name) 250 | model = GRUD(fea_size, fea_size, fea_size, X_mean) 251 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 252 | test_result = TestModel(model, test_dataloader, max_speed) 253 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 254 | 255 | # Graph GRU-D 256 | import GGRUD 257 | importlib.reload(GGRUD) 258 | from GGRUD import GGRUD 259 | importlib.reload(utils) 260 | from utils import TrainModel, TestModel 261 | model_name = 'GGRUD' 262 | print(model_name) 263 | model = GGRUD(A, fea_size, fea_size, fea_size, X_mean) 264 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 265 | test_result = TestModel(model, test_dataloader, max_speed) 266 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 267 | 268 | # GCLSTM 269 | importlib.reload(models) 270 | from models import GCLSTM 271 | importlib.reload(utils) 272 | from utils import TrainModel, TestModel 273 | model_name = 'GCLSTM' 274 | print(model_name) 275 | model = GCLSTM(A) 276 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 277 | test_result = TestModel(model, test_dataloader, max_speed) 278 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /Python Scripts/.ipynb_checkpoints/GRUD-checkpoint.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 12 16:48:54 2018 4 | 5 | @author: Zhiyong 6 | """ 7 | 8 | import torch.utils.data as utils 9 | import torch.nn.functional as F 10 | import torch 11 | import torch.nn as nn 12 | from torch.autograd import Variable 13 | from torch.nn.parameter import Parameter 14 | import math 15 | import numpy as np 16 | import pandas as pd 17 | import time 18 | 19 | class FilterLinear(nn.Module): 20 | def __init__(self, in_features, out_features, filter_square_matrix, bias=True): 21 | ''' 22 | filter_square_matrix : filter square matrix, whose each elements is 0 or 1. 23 | ''' 24 | super(FilterLinear, self).__init__() 25 | self.in_features = in_features 26 | self.out_features = out_features 27 | 28 | use_gpu = torch.cuda.is_available() 29 | self.filter_square_matrix = None 30 | if use_gpu: 31 | self.filter_square_matrix = Variable(filter_square_matrix.cuda(), requires_grad=False) 32 | else: 33 | self.filter_square_matrix = Variable(filter_square_matrix, requires_grad=False) 34 | 35 | self.weight = Parameter(torch.Tensor(out_features, in_features)) 36 | if bias: 37 | self.bias = Parameter(torch.Tensor(out_features)) 38 | else: 39 | self.register_parameter('bias', None) 40 | self.reset_parameters() 41 | 42 | def reset_parameters(self): 43 | stdv = 1. / math.sqrt(self.weight.size(1)) 44 | self.weight.data.uniform_(-stdv, stdv) 45 | if self.bias is not None: 46 | self.bias.data.uniform_(-stdv, stdv) 47 | # print(self.weight.data) 48 | # print(self.bias.data) 49 | 50 | def forward(self, input): 51 | # print(self.filter_square_matrix.mul(self.weight)) 52 | return F.linear(input, self.filter_square_matrix.mul(self.weight), self.bias) 53 | 54 | def __repr__(self): 55 | return self.__class__.__name__ + '(' \ 56 | + 'in_features=' + str(self.in_features) \ 57 | + ', out_features=' + str(self.out_features) \ 58 | + ', bias=' + str(self.bias is not None) + ')' 59 | 60 | class GRUD(nn.Module): 61 | def __init__(self, input_size, cell_size, hidden_size, X_mean, output_last = False): 62 | """ 63 | Recurrent Neural Networks for Multivariate Times Series with Missing Values 64 | GRU-D: GRU exploit two representations of informative missingness patterns, i.e., masking and time interval. 65 | cell_size is the size of cell_state. 66 | 67 | Implemented based on the paper: 68 | @article{che2018recurrent, 69 | title={Recurrent neural networks for multivariate time series with missing values}, 70 | author={Che, Zhengping and Purushotham, Sanjay and Cho, Kyunghyun and Sontag, David and Liu, Yan}, 71 | journal={Scientific reports}, 72 | volume={8}, 73 | number={1}, 74 | pages={6085}, 75 | year={2018}, 76 | publisher={Nature Publishing Group} 77 | } 78 | 79 | GRU-D: 80 | input_size: variable dimension of each time 81 | hidden_size: dimension of hidden_state 82 | mask_size: dimension of masking vector 83 | X_mean: the mean of the historical input data 84 | """ 85 | 86 | super(GRUD, self).__init__() 87 | 88 | self.hidden_size = hidden_size 89 | self.delta_size = input_size 90 | self.mask_size = input_size 91 | 92 | use_gpu = torch.cuda.is_available() 93 | if use_gpu: 94 | self.identity = torch.eye(input_size).cuda() 95 | self.zeros = Variable(torch.zeros(input_size).cuda()) 96 | self.X_mean = Variable(torch.Tensor(X_mean).cuda()) 97 | else: 98 | self.identity = torch.eye(input_size) 99 | self.zeros = Variable(torch.zeros(input_size)) 100 | self.X_mean = Variable(torch.Tensor(X_mean)) 101 | 102 | self.zl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 103 | self.rl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 104 | self.hl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 105 | 106 | self.gamma_x_l = FilterLinear(self.delta_size, self.delta_size, self.identity) 107 | 108 | self.gamma_h_l = nn.Linear(self.delta_size, self.delta_size) 109 | 110 | self.output_last = output_last 111 | 112 | def step(self, x, x_last_obsv, x_mean, h, mask, delta): 113 | 114 | batch_size = x.shape[0] 115 | dim_size = x.shape[1] 116 | 117 | delta_x = torch.exp(-torch.max(self.zeros, self.gamma_x_l(delta))) 118 | delta_h = torch.exp(-torch.max(self.zeros, self.gamma_h_l(delta))) 119 | 120 | x = mask * x + (1 - mask) * (delta_x * x_last_obsv + (1 - delta_x) * x_mean) 121 | h = delta_h * h 122 | 123 | combined = torch.cat((x, h, mask), 1) 124 | z = F.sigmoid(self.zl(combined)) 125 | r = F.sigmoid(self.rl(combined)) 126 | combined_r = torch.cat((x, r * h, mask), 1) 127 | h_tilde = F.tanh(self.hl(combined_r)) 128 | h = (1 - z) * h + z * h_tilde 129 | 130 | return h 131 | 132 | def forward(self, input): 133 | batch_size = input.size(0) 134 | type_size = input.size(1) 135 | step_size = input.size(2) 136 | spatial_size = input.size(3) 137 | 138 | Hidden_State = self.initHidden(batch_size) 139 | X = torch.squeeze(input[:,0,:,:]) 140 | Mask = torch.squeeze(input[:,1,:,:]) 141 | Delta = torch.squeeze(input[:,2,:,:]) 142 | X_last_obsv = torch.squeeze(input[:,3,:,:]) 143 | 144 | 145 | for i in range(step_size): 146 | Hidden_State = self.step(torch.squeeze(X[:,i:i+1,:])\ 147 | , torch.squeeze(X_last_obsv[:,i:i+1,:])\ 148 | , torch.squeeze(self.X_mean[:,i:i+1,:])\ 149 | , Hidden_State\ 150 | , torch.squeeze(Mask[:,i:i+1,:])\ 151 | , torch.squeeze(Delta[:,i:i+1,:])) 152 | 153 | outputs = Hidden_State.unsqueeze(1) 154 | return outputs 155 | 156 | def initHidden(self, batch_size): 157 | use_gpu = torch.cuda.is_available() 158 | if use_gpu: 159 | Hidden_State = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 160 | return Hidden_State 161 | else: 162 | Hidden_State = Variable(torch.zeros(batch_size, self.hidden_size)) 163 | return Hidden_State 164 | 165 | 166 | -------------------------------------------------------------------------------- /Python Scripts/.ipynb_checkpoints/LSTMD-checkpoint.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 12 16:48:54 2018 4 | 5 | @author: Zhiyong 6 | """ 7 | 8 | import torch.utils.data as utils 9 | import torch.nn.functional as F 10 | import torch 11 | import torch.nn as nn 12 | from torch.autograd import Variable 13 | from torch.nn.parameter import Parameter 14 | import math 15 | import numpy as np 16 | import pandas as pd 17 | import time 18 | 19 | class FilterLinear(nn.Module): 20 | def __init__(self, in_features, out_features, filter_square_matrix, bias=True): 21 | ''' 22 | filter_square_matrix : filter square matrix, whose each elements is 0 or 1. 23 | ''' 24 | super(FilterLinear, self).__init__() 25 | self.in_features = in_features 26 | self.out_features = out_features 27 | 28 | use_gpu = torch.cuda.is_available() 29 | self.filter_square_matrix = None 30 | if use_gpu: 31 | self.filter_square_matrix = Variable(filter_square_matrix.cuda(), requires_grad=False) 32 | else: 33 | self.filter_square_matrix = Variable(filter_square_matrix, requires_grad=False) 34 | 35 | self.weight = Parameter(torch.Tensor(out_features, in_features)) 36 | if bias: 37 | self.bias = Parameter(torch.Tensor(out_features)) 38 | else: 39 | self.register_parameter('bias', None) 40 | self.reset_parameters() 41 | 42 | def reset_parameters(self): 43 | stdv = 1. / math.sqrt(self.weight.size(1)) 44 | self.weight.data.uniform_(-stdv, stdv) 45 | if self.bias is not None: 46 | self.bias.data.uniform_(-stdv, stdv) 47 | # print(self.weight.data) 48 | # print(self.bias.data) 49 | 50 | def forward(self, input): 51 | # print(self.filter_square_matrix.mul(self.weight)) 52 | return F.linear(input, self.filter_square_matrix.mul(self.weight), self.bias) 53 | 54 | def __repr__(self): 55 | return self.__class__.__name__ + '(' \ 56 | + 'in_features=' + str(self.in_features) \ 57 | + ', out_features=' + str(self.out_features) \ 58 | + ', bias=' + str(self.bias is not None) + ')' 59 | 60 | class LSTMD(nn.Module): 61 | def __init__(self, input_size, cell_size, hidden_size, X_mean, output_last = False): 62 | 63 | super(LSTMD, self).__init__() 64 | 65 | self.hidden_size = hidden_size 66 | self.delta_size = input_size 67 | self.mask_size = input_size 68 | 69 | use_gpu = torch.cuda.is_available() 70 | if use_gpu: 71 | self.identity = torch.eye(input_size).cuda() 72 | self.zeros = Variable(torch.zeros(input_size).cuda()) 73 | self.X_mean = Variable(torch.Tensor(X_mean).cuda()) 74 | else: 75 | self.identity = torch.eye(input_size) 76 | self.zeros = Variable(torch.zeros(input_size)) 77 | self.X_mean = Variable(torch.Tensor(X_mean)) 78 | 79 | # self.zl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 80 | # self.rl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 81 | # self.hl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 82 | 83 | self.fl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 84 | self.il = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 85 | self.ol = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 86 | self.Cl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 87 | 88 | self.gamma_x_l = FilterLinear(self.delta_size, self.delta_size, self.identity) 89 | 90 | self.gamma_h_l = nn.Linear(self.delta_size, self.delta_size) 91 | 92 | self.output_last = output_last 93 | 94 | def step(self, x, x_last_obsv, x_mean, h, mask, delta, C): 95 | 96 | batch_size = x.shape[0] 97 | dim_size = x.shape[1] 98 | 99 | delta_x = torch.exp(-torch.max(self.zeros, self.gamma_x_l(delta))) 100 | delta_h = torch.exp(-torch.max(self.zeros, self.gamma_h_l(delta))) 101 | 102 | x = mask * x + (1 - mask) * (delta_x * x_last_obsv + (1 - delta_x) * x_mean) 103 | h = delta_h * h 104 | 105 | xhm = torch.cat((x, h, mask), 1) 106 | 107 | f = F.sigmoid(self.fl(xhm)) 108 | i = F.sigmoid(self.il(xhm)) 109 | o = F.sigmoid(self.ol(xhm)) 110 | C_tilde = F.tanh(self.Cl(xhm)) 111 | C = f * C + i * C_tilde 112 | h = o * F.tanh(C) 113 | 114 | return h, C 115 | 116 | def forward(self, input): 117 | batch_size = input.size(0) 118 | type_size = input.size(1) 119 | step_size = input.size(2) 120 | spatial_size = input.size(3) 121 | 122 | X = torch.squeeze(input[:,0,:,:]) 123 | Mask = torch.squeeze(input[:,1,:,:]) 124 | Delta = torch.squeeze(input[:,2,:,:]) 125 | X_last_obsv = torch.squeeze(input[:,3,:,:]) 126 | 127 | h, C = self.initHidden(batch_size) 128 | 129 | for i in range(step_size): 130 | h, C = self.step(torch.squeeze(X[:,i:i+1,:])\ 131 | , torch.squeeze(X_last_obsv[:,i:i+1,:])\ 132 | , torch.squeeze(self.X_mean[:,i:i+1,:])\ 133 | , h\ 134 | , torch.squeeze(Mask[:,i:i+1,:])\ 135 | , torch.squeeze(Delta[:,i:i+1,:])\ 136 | , C) 137 | return h 138 | 139 | def initHidden(self, batch_size): 140 | h = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 141 | C = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 142 | return h, C 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Python Scripts/Exp_GMN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import torch.utils.data as utils 6 | import torch.nn.functional as F 7 | import torch 8 | import torch.nn as nn 9 | from torch.autograd import Variable 10 | from torch.nn.parameter import Parameter 11 | import math 12 | import numpy as np 13 | import pandas as pd 14 | import time 15 | import pickle 16 | import os 17 | 18 | import matplotlib.pyplot as plt 19 | import matplotlib.patches as patches 20 | # %matplotlib inline 21 | # plt.ioff() 22 | # plt.ion() 23 | 24 | import sys 25 | 26 | if not sys.warnoptions: 27 | import warnings 28 | warnings.simplefilter("ignore") 29 | 30 | sys.path.append('../') 31 | 32 | 33 | import models 34 | import utils 35 | import importlib 36 | 37 | import argparse 38 | 39 | dtype = torch.float32 40 | if torch.cuda.is_available(): 41 | device = torch.device("cuda:0") 42 | else: 43 | device = torch.device("cpu") 44 | 45 | torch.cuda.set_device(0) 46 | 47 | 48 | 49 | # Load data 50 | 51 | def loadDataset(dataset = None): 52 | if dataset == 'PEMS-BAY': 53 | speed_matrix = pd.read_hdf('../Data/PEMS-BAY/pems-bay.h5') 54 | A = pd.read_pickle('../Data/PEMS-BAY/adj_mx_bay.pkl') 55 | A = A[2] 56 | A[np.where(A != 0)] = 1 57 | for i in range(0, A.shape[0]): 58 | for j in range(i, A.shape[0]): 59 | if A[i,j] == 1: 60 | A[j,i] = 1 61 | elif dataset == 'METR-LA': 62 | speed_matrix = pd.read_hdf('../Data/METR-LA/metr-la.h5') 63 | A = pd.read_pickle('../Data/METR-LA/adj_mx.pkl') 64 | A = A[2] 65 | A[np.where(A != 0)] = 1 66 | for i in range(0, A.shape[0]): 67 | for j in range(i, A.shape[0]): 68 | if A[i,j] == 1: 69 | A[j,i] = 1 70 | elif dataset == 'LOOP-SEA': 71 | speed_matrix = pd.read_pickle('../Data/LOOP-SEA/speed_matrix_2015_1mph') 72 | A = np.load('../Data/LOOP-SEA/Loop_Seattle_2015_A.npy') 73 | elif dataset == 'INRIX-SEA': 74 | speed_matrix = pd.read_pickle('../Data/INRIX-SEA/INRIX_Seattle_Speed_Matrix__2012_v2.pkl') 75 | A = np.load('../Data/INRIX-SEA/INRIX_Seattle_Adjacency_matrix_2012_v2.npy') 76 | else: 77 | print('Dataset not found.') 78 | return None, None 79 | print('Dataset loaded.') 80 | return speed_matrix, A 81 | 82 | def StoreData(model_name, train_result, test_result, directory, model, random_seed = 1024, save_model=True): 83 | 84 | if os.path.isfile(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl'): 85 | f = open(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl', "rb") 86 | result_dict = pickle.load(f) 87 | f.close() 88 | else: 89 | result_dict = {} 90 | 91 | result_dict[model_name] = {} 92 | result_dict[model_name]['train_loss'] = train_result[2] 93 | result_dict[model_name]['valid_loss'] = train_result[3] 94 | result_dict[model_name]['train_time'] = train_result[5] 95 | result_dict[model_name]['valid_time'] = train_result[6] 96 | result_dict[model_name]['MAE'] = test_result[3] 97 | result_dict[model_name]['RMSE'] = test_result[4] 98 | result_dict[model_name]['MAPE'] = test_result[5] 99 | f = open(directory + '/gmn_log_rs_' + str(random_seed) + '.pkl', "wb") 100 | pickle.dump(result_dict,f) 101 | f.close() 102 | if save_model: 103 | torch.save(model.state_dict(), directory + '/' + model_name) 104 | 105 | 106 | 107 | if __name__ == "__main__": 108 | 109 | parser = argparse.ArgumentParser() 110 | 111 | parser.add_argument('-d', '-dataset', default='PEMS-BAY', type=str, required=True, help="specify dataset") 112 | parser.add_argument('-m', '-missingrate', default=0.2, type=float, required=True, help="specify missing rate") 113 | parser.add_argument('-o', '-optm', default='Adam', type=str, required=False, help="specify training optimizer") 114 | parser.add_argument('-l', '-learningrate', default=0.001, type=float, required=False, help="specify initial learning rate") 115 | parser.add_argument('-g', '-gamma', default=0.9, type=float, required=False, help="specify gamma for GMN") 116 | parser.add_argument('-t', '-maskingtype', default='random', type=str, required=False, help="specify masking type") 117 | parser.add_argument('-r', '-randomseed', default=1024, type=int, required=False, help="specify random seed") 118 | parser.add_argument('-s', '-savemodel', default=1, type=int, required=False, help="specify whether save model") 119 | args = parser.parse_args() 120 | 121 | dataset = args.d 122 | missing_rate = args.m 123 | optm = args.o 124 | learning_rate = args.l 125 | gamma = args.g 126 | random_seed = args.r 127 | masking_type = args.t 128 | save_model = args.s 129 | 130 | np.random.seed(random_seed) 131 | 132 | print('Exp: baseline models') 133 | print('\tDataset:', dataset) 134 | print('\tMissing rate:', missing_rate) 135 | print('\tOptimizer:', optm) 136 | print('\tLearnig ate:', learning_rate) 137 | print('\tGamma:', gamma) 138 | print('\tMasking type:', masking_type) 139 | print('\tRandom seed:', random_seed) 140 | if save_model==1: 141 | print('\tSave model:', 'True') 142 | else: 143 | print('\tSave model:', 'False') 144 | 145 | 146 | directory = './Masking_' + masking_type + '/GMN_' + str(dataset) + '_MR=' + str(missing_rate) + '_gamma=' + str(gamma) 147 | 148 | if not os.path.exists(directory): 149 | os.makedirs(directory) 150 | 151 | speed_matrix, A = loadDataset(dataset = dataset) 152 | 153 | 154 | importlib.reload(utils) 155 | from utils import PrepareDataset, TrainModel, TestModel 156 | 157 | 158 | importlib.reload(utils) 159 | from utils import PrepareDataset 160 | mask_ones_proportion = 1 - missing_rate 161 | train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean = PrepareDataset(speed_matrix, BATCH_SIZE = 64, seq_len = 10, pred_len = 1, \ 162 | train_propotion = 0.6, valid_propotion = 0.2, \ 163 | mask_ones_proportion = mask_ones_proportion, \ 164 | masking = True, masking_type = masking_type, delta_last_obsv = False, \ 165 | shuffle = True, random_seed = random_seed) 166 | 167 | 168 | inputs, labels = next(iter(train_dataloader)) 169 | [batch_size, type_size, step_size, fea_size] = inputs.size() 170 | 171 | 172 | importlib.reload(utils) 173 | from utils import TrainModel, TestModel 174 | 175 | # SGNN-6 176 | importlib.reload(models) 177 | from models import SGNN 178 | importlib.reload(utils) 179 | from utils import TrainModel, TestModel 180 | model_name = 'SGMN6' 181 | print(model_name) 182 | model = SGNN(A, layer = 6, gamma = gamma) 183 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 184 | test_result = TestModel(model, test_dataloader, max_speed) 185 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 186 | 187 | # SGNN-8 188 | importlib.reload(models) 189 | from models import SGNN 190 | importlib.reload(utils) 191 | from utils import TrainModel, TestModel 192 | model_name = 'SGMN8' 193 | print(model_name) 194 | model = SGNN(A, layer = 8, gamma = gamma) 195 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 196 | test_result = TestModel(model, test_dataloader, max_speed) 197 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 198 | 199 | # SGNN-10 200 | importlib.reload(models) 201 | from models import SGNN 202 | importlib.reload(utils) 203 | from utils import TrainModel, TestModel 204 | model_name = 'SGMN10' 205 | print(model_name) 206 | model = SGNN(A, layer = 10, gamma = gamma) 207 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 208 | test_result = TestModel(model, test_dataloader, max_speed) 209 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 210 | 211 | # GNN-6 212 | importlib.reload(models) 213 | from models import GNN 214 | importlib.reload(utils) 215 | from utils import TrainModel, TestModel 216 | model_name = 'GMN6' 217 | print(model_name) 218 | model = GNN(A, layer = 6, gamma = gamma) 219 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 220 | test_result = TestModel(model, test_dataloader, max_speed) 221 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 222 | 223 | # GNN-8 224 | importlib.reload(models) 225 | from models import GNN 226 | importlib.reload(utils) 227 | from utils import TrainModel, TestModel 228 | model_name = 'GMN8' 229 | print(model_name) 230 | model = GNN(A, layer = 8, gamma = gamma) 231 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 232 | test_result = TestModel(model, test_dataloader, max_speed) 233 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) 234 | 235 | # GNN-10 236 | importlib.reload(models) 237 | from models import GNN 238 | importlib.reload(utils) 239 | from utils import TrainModel, TestModel 240 | model_name = 'GMN10' 241 | print(model_name) 242 | model = GNN(A, layer = 10, gamma = gamma) 243 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, optm = optm, learning_rate = learning_rate, patience = 5) 244 | test_result = TestModel(model, test_dataloader, max_speed) 245 | StoreData(model_name, train_result, test_result, directory, model, random_seed, save_model) -------------------------------------------------------------------------------- /Python Scripts/Exp_baseline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | import torch.utils.data as utils 6 | import torch.nn.functional as F 7 | import torch 8 | import torch.nn as nn 9 | from torch.autograd import Variable 10 | from torch.nn.parameter import Parameter 11 | import math 12 | import numpy as np 13 | import pandas as pd 14 | import time 15 | import pickle 16 | import os 17 | 18 | import matplotlib.pyplot as plt 19 | import matplotlib.patches as patches 20 | # %matplotlib inline 21 | # plt.ioff() 22 | # plt.ion() 23 | 24 | 25 | import sys 26 | 27 | if not sys.warnoptions: 28 | import warnings 29 | warnings.simplefilter("ignore") 30 | 31 | sys.path.append('../') 32 | 33 | 34 | import models 35 | import utils 36 | import importlib 37 | 38 | import argparse 39 | 40 | 41 | dtype = torch.float32 42 | if torch.cuda.is_available(): 43 | device = torch.device("cuda:0") 44 | else: 45 | device = torch.device("cpu") 46 | 47 | torch.cuda.set_device(0) 48 | 49 | 50 | 51 | # Load data 52 | 53 | def loadDataset(dataset = None): 54 | if dataset == 'PEMS-BAY': 55 | speed_matrix = pd.read_hdf('../Data/PEMS-BAY/pems-bay.h5') 56 | A = pd.read_pickle('../Data/PEMS-BAY/adj_mx_bay.pkl') 57 | A = A[2] 58 | A[np.where(A != 0)] = 1 59 | for i in range(0, A.shape[0]): 60 | for j in range(i, A.shape[0]): 61 | if A[i,j] == 1: 62 | A[j,i] = 1 63 | elif dataset == 'METR-LA': 64 | speed_matrix = pd.read_hdf('../Data/METR-LA/metr-la.h5') 65 | A = pd.read_pickle('../Data/METR-LA/adj_mx.pkl') 66 | A = A[2] 67 | A[np.where(A != 0)] = 1 68 | for i in range(0, A.shape[0]): 69 | for j in range(i, A.shape[0]): 70 | if A[i,j] == 1: 71 | A[j,i] = 1 72 | elif dataset == 'LOOP-SEA': 73 | speed_matrix = pd.read_pickle('../Data/LOOP-SEA/speed_matrix_2015_1mph') 74 | A = np.load('../Data/LOOP-SEA/Loop_Seattle_2015_A.npy') 75 | elif dataset == 'INRIX-SEA': 76 | speed_matrix = pd.read_pickle('../Data/INRIX-SEA/INRIX_Seattle_Speed_Matrix__2012_v2.pkl') 77 | A = np.load('../Data/INRIX-SEA/INRIX_Seattle_Adjacency_matrix_2012_v2.npy') 78 | else: 79 | print('Dataset not found.') 80 | return None, None 81 | print('Dataset loaded.') 82 | return speed_matrix, A 83 | 84 | 85 | def StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed = 1024, save_model=True): 86 | result_dict[model_name] = {} 87 | result_dict[model_name]['train_loss'] = train_result[2] 88 | result_dict[model_name]['valid_loss'] = train_result[3] 89 | result_dict[model_name]['MAE'] = test_result[3] 90 | result_dict[model_name]['RMSE'] = test_result[4] 91 | result_dict[model_name]['MAPE'] = test_result[5] 92 | f = open(directory + '/log_rs_' + str(random_seed) + '.pkl', "wb") 93 | pickle.dump(result_dict,f) 94 | f.close() 95 | if save_model: 96 | torch.save(model.state_dict(), directory + '/' + model_name) 97 | 98 | 99 | 100 | if __name__ == "__main__": 101 | 102 | parser = argparse.ArgumentParser() 103 | 104 | parser.add_argument('-d', '-dataset', default='PEMS-BAY', type=str, required=True, help="specify dataset") 105 | parser.add_argument('-m', '-missingrate', default=0.2, type=float, required=True, help="specify missing rate") 106 | parser.add_argument('-o', '-optm', default='Adam', type=str, required=False, help="specify training optimizer") 107 | parser.add_argument('-l', '-learningrate', default=0.001, type=float, required=False, help="specify initial learning rate") 108 | parser.add_argument('-t', '-maskingtype', default='random', type=str, required=False, help="specify masking type") 109 | parser.add_argument('-r', '-randomseed', default=1024, type=int, required=False, help="specify random seed") 110 | parser.add_argument('-s', '-savemodel', default=1, type=int, required=False, help="specify whether save model") 111 | args = parser.parse_args() 112 | 113 | dataset = args.d 114 | missing_rate = args.m 115 | optm = args.o 116 | learning_rate = args.l 117 | random_seed = args.r 118 | masking_type = args.t 119 | save_model = args.s 120 | 121 | np.random.seed(random_seed) 122 | 123 | print('Exp: baseline models') 124 | print('\tDataset:', dataset) 125 | print('\tMissing rate:', missing_rate) 126 | print('\tOptimizer:', optm) 127 | print('\tLearnig ate:', learning_rate) 128 | print('\tMasking type:', masking_type) 129 | print('\tRandom seed:', random_seed) 130 | 131 | if save_model==1: 132 | print('\tSave model:', 'True') 133 | else: 134 | print('\tSave model:', 'False') 135 | 136 | directory = './Masking_' + masking_type + '/' + str(dataset) + '_MR=' + str(missing_rate) 137 | 138 | if not os.path.exists(directory): 139 | os.makedirs(directory) 140 | 141 | result_dict = {} 142 | 143 | 144 | speed_matrix, A = loadDataset(dataset = dataset) 145 | 146 | 147 | importlib.reload(utils) 148 | from utils import PrepareDataset, TrainModel, TestModel 149 | 150 | 151 | importlib.reload(utils) 152 | from utils import PrepareDataset 153 | mask_ones_proportion = 1 - missing_rate 154 | train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean = PrepareDataset(speed_matrix, BATCH_SIZE = 64, seq_len = 10, pred_len = 1, \ 155 | train_propotion = 0.6, valid_propotion = 0.2, \ 156 | mask_ones_proportion = mask_ones_proportion, \ 157 | masking = True, masking_type = masking_type, delta_last_obsv = True, \ 158 | shuffle = True, random_seed = random_seed) 159 | 160 | 161 | inputs, labels = next(iter(train_dataloader)) 162 | [batch_size, type_size, step_size, fea_size] = inputs.size() 163 | 164 | 165 | importlib.reload(utils) 166 | from utils import TrainModel, TestModel 167 | 168 | 169 | # LSTM 170 | importlib.reload(models) 171 | from models import LSTM 172 | importlib.reload(utils) 173 | from utils import TrainModel, TestModel 174 | model_name = 'LSTM' 175 | print(model_name) 176 | model = LSTM(A.shape[0]) 177 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 178 | test_result = TestModel(model, test_dataloader, max_speed) 179 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 180 | 181 | # LSTM-I 182 | importlib.reload(models) 183 | from models import LSTM 184 | importlib.reload(utils) 185 | from utils import TrainModel, TestModel 186 | model_name = 'LSTMI' 187 | print(model_name) 188 | model = LSTM(A.shape[0], imputation = True) 189 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 190 | test_result = TestModel(model, test_dataloader, max_speed) 191 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 192 | 193 | # LSTM-D 194 | import LSTMD 195 | importlib.reload(LSTMD) 196 | from LSTMD import LSTMD 197 | importlib.reload(utils) 198 | from utils import TrainModel, TestModel 199 | model_name = 'LSTMD' 200 | print(model_name) 201 | model = LSTMD(fea_size, fea_size, fea_size, X_mean) 202 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 203 | test_result = TestModel(model, test_dataloader, max_speed) 204 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 205 | 206 | # GRU 207 | importlib.reload(models) 208 | from models import GRU 209 | importlib.reload(utils) 210 | from utils import TrainModel, TestModel 211 | model_name = 'GRU' 212 | print(model_name) 213 | gru = GRU(A.shape[0]) 214 | gru, train_result = TrainModel(gru, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 215 | test_result = TestModel(gru, test_dataloader, max_speed) 216 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 217 | 218 | # GRU-I 219 | importlib.reload(models) 220 | from models import GRU 221 | importlib.reload(utils) 222 | from utils import TrainModel, TestModel 223 | model_name = 'GRUI' 224 | print(model_name) 225 | model = GRU(A.shape[0], imputation = True) 226 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 227 | test_result = TestModel(model, test_dataloader, max_speed) 228 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 229 | 230 | # GRU-D 231 | import GRUD 232 | importlib.reload(GRUD) 233 | from GRUD import GRUD 234 | importlib.reload(utils) 235 | from utils import TrainModel, TestModel 236 | model_name = 'GRUD' 237 | print(model_name) 238 | model = GRUD(fea_size, fea_size, fea_size, X_mean) 239 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 240 | test_result = TestModel(model, test_dataloader, max_speed) 241 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) 242 | 243 | # Graph GRU-D 244 | import GGRUD 245 | importlib.reload(GGRUD) 246 | from GGRUD import GGRUD 247 | importlib.reload(utils) 248 | from utils import TrainModel, TestModel 249 | model_name = 'GGRUD' 250 | print(model_name) 251 | model = GGRUD(A, fea_size, fea_size, fea_size, X_mean) 252 | model, train_result = TrainModel(model, train_dataloader, valid_dataloader, learning_rate = learning_rate, patience = 5) 253 | test_result = TestModel(model, test_dataloader, max_speed) 254 | StoreData(result_dict, model_name, train_result, test_result, directory, model, random_seed, save_model) -------------------------------------------------------------------------------- /Python Scripts/GRUD.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 12 16:48:54 2018 4 | 5 | @author: Zhiyong 6 | """ 7 | 8 | import torch.utils.data as utils 9 | import torch.nn.functional as F 10 | import torch 11 | import torch.nn as nn 12 | from torch.autograd import Variable 13 | from torch.nn.parameter import Parameter 14 | import math 15 | import numpy as np 16 | import pandas as pd 17 | import time 18 | 19 | class FilterLinear(nn.Module): 20 | def __init__(self, in_features, out_features, filter_square_matrix, bias=True): 21 | ''' 22 | filter_square_matrix : filter square matrix, whose each elements is 0 or 1. 23 | ''' 24 | super(FilterLinear, self).__init__() 25 | self.in_features = in_features 26 | self.out_features = out_features 27 | 28 | use_gpu = torch.cuda.is_available() 29 | self.filter_square_matrix = None 30 | if use_gpu: 31 | self.filter_square_matrix = Variable(filter_square_matrix.cuda(), requires_grad=False) 32 | else: 33 | self.filter_square_matrix = Variable(filter_square_matrix, requires_grad=False) 34 | 35 | self.weight = Parameter(torch.Tensor(out_features, in_features)) 36 | if bias: 37 | self.bias = Parameter(torch.Tensor(out_features)) 38 | else: 39 | self.register_parameter('bias', None) 40 | self.reset_parameters() 41 | 42 | def reset_parameters(self): 43 | stdv = 1. / math.sqrt(self.weight.size(1)) 44 | self.weight.data.uniform_(-stdv, stdv) 45 | if self.bias is not None: 46 | self.bias.data.uniform_(-stdv, stdv) 47 | # print(self.weight.data) 48 | # print(self.bias.data) 49 | 50 | def forward(self, input): 51 | # print(self.filter_square_matrix.mul(self.weight)) 52 | return F.linear(input, self.filter_square_matrix.mul(self.weight), self.bias) 53 | 54 | def __repr__(self): 55 | return self.__class__.__name__ + '(' \ 56 | + 'in_features=' + str(self.in_features) \ 57 | + ', out_features=' + str(self.out_features) \ 58 | + ', bias=' + str(self.bias is not None) + ')' 59 | 60 | class GRUD(nn.Module): 61 | def __init__(self, input_size, cell_size, hidden_size, X_mean, output_last = False): 62 | """ 63 | Recurrent Neural Networks for Multivariate Times Series with Missing Values 64 | GRU-D: GRU exploit two representations of informative missingness patterns, i.e., masking and time interval. 65 | cell_size is the size of cell_state. 66 | 67 | Implemented based on the paper: 68 | @article{che2018recurrent, 69 | title={Recurrent neural networks for multivariate time series with missing values}, 70 | author={Che, Zhengping and Purushotham, Sanjay and Cho, Kyunghyun and Sontag, David and Liu, Yan}, 71 | journal={Scientific reports}, 72 | volume={8}, 73 | number={1}, 74 | pages={6085}, 75 | year={2018}, 76 | publisher={Nature Publishing Group} 77 | } 78 | 79 | GRU-D: 80 | input_size: variable dimension of each time 81 | hidden_size: dimension of hidden_state 82 | mask_size: dimension of masking vector 83 | X_mean: the mean of the historical input data 84 | """ 85 | 86 | super(GRUD, self).__init__() 87 | 88 | self.hidden_size = hidden_size 89 | self.delta_size = input_size 90 | self.mask_size = input_size 91 | 92 | use_gpu = torch.cuda.is_available() 93 | if use_gpu: 94 | self.identity = torch.eye(input_size).cuda() 95 | self.zeros = Variable(torch.zeros(input_size).cuda()) 96 | self.X_mean = Variable(torch.Tensor(X_mean).cuda()) 97 | else: 98 | self.identity = torch.eye(input_size) 99 | self.zeros = Variable(torch.zeros(input_size)) 100 | self.X_mean = Variable(torch.Tensor(X_mean)) 101 | 102 | self.zl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 103 | self.rl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 104 | self.hl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 105 | 106 | self.gamma_x_l = FilterLinear(self.delta_size, self.delta_size, self.identity) 107 | 108 | self.gamma_h_l = nn.Linear(self.delta_size, self.delta_size) 109 | 110 | self.output_last = output_last 111 | 112 | def step(self, x, x_last_obsv, x_mean, h, mask, delta): 113 | 114 | batch_size = x.shape[0] 115 | dim_size = x.shape[1] 116 | 117 | delta_x = torch.exp(-torch.max(self.zeros, self.gamma_x_l(delta))) 118 | delta_h = torch.exp(-torch.max(self.zeros, self.gamma_h_l(delta))) 119 | 120 | x = mask * x + (1 - mask) * (delta_x * x_last_obsv + (1 - delta_x) * x_mean) 121 | h = delta_h * h 122 | 123 | combined = torch.cat((x, h, mask), 1) 124 | z = F.sigmoid(self.zl(combined)) 125 | r = F.sigmoid(self.rl(combined)) 126 | combined_r = torch.cat((x, r * h, mask), 1) 127 | h_tilde = F.tanh(self.hl(combined_r)) 128 | h = (1 - z) * h + z * h_tilde 129 | 130 | return h 131 | 132 | def forward(self, input): 133 | batch_size = input.size(0) 134 | type_size = input.size(1) 135 | step_size = input.size(2) 136 | spatial_size = input.size(3) 137 | 138 | Hidden_State = self.initHidden(batch_size) 139 | X = torch.squeeze(input[:,0,:,:]) 140 | Mask = torch.squeeze(input[:,1,:,:]) 141 | Delta = torch.squeeze(input[:,2,:,:]) 142 | X_last_obsv = torch.squeeze(input[:,3,:,:]) 143 | 144 | 145 | for i in range(step_size): 146 | Hidden_State = self.step(torch.squeeze(X[:,i:i+1,:])\ 147 | , torch.squeeze(X_last_obsv[:,i:i+1,:])\ 148 | , torch.squeeze(self.X_mean[:,i:i+1,:])\ 149 | , Hidden_State\ 150 | , torch.squeeze(Mask[:,i:i+1,:])\ 151 | , torch.squeeze(Delta[:,i:i+1,:])) 152 | 153 | outputs = Hidden_State.unsqueeze(1) 154 | return outputs 155 | 156 | def initHidden(self, batch_size): 157 | use_gpu = torch.cuda.is_available() 158 | if use_gpu: 159 | Hidden_State = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 160 | return Hidden_State 161 | else: 162 | Hidden_State = Variable(torch.zeros(batch_size, self.hidden_size)) 163 | return Hidden_State 164 | 165 | 166 | -------------------------------------------------------------------------------- /Python Scripts/LSTMD.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat May 12 16:48:54 2018 4 | 5 | @author: Zhiyong 6 | """ 7 | 8 | import torch.utils.data as utils 9 | import torch.nn.functional as F 10 | import torch 11 | import torch.nn as nn 12 | from torch.autograd import Variable 13 | from torch.nn.parameter import Parameter 14 | import math 15 | import numpy as np 16 | import pandas as pd 17 | import time 18 | 19 | class FilterLinear(nn.Module): 20 | def __init__(self, in_features, out_features, filter_square_matrix, bias=True): 21 | ''' 22 | filter_square_matrix : filter square matrix, whose each elements is 0 or 1. 23 | ''' 24 | super(FilterLinear, self).__init__() 25 | self.in_features = in_features 26 | self.out_features = out_features 27 | 28 | use_gpu = torch.cuda.is_available() 29 | self.filter_square_matrix = None 30 | if use_gpu: 31 | self.filter_square_matrix = Variable(filter_square_matrix.cuda(), requires_grad=False) 32 | else: 33 | self.filter_square_matrix = Variable(filter_square_matrix, requires_grad=False) 34 | 35 | self.weight = Parameter(torch.Tensor(out_features, in_features)) 36 | if bias: 37 | self.bias = Parameter(torch.Tensor(out_features)) 38 | else: 39 | self.register_parameter('bias', None) 40 | self.reset_parameters() 41 | 42 | def reset_parameters(self): 43 | stdv = 1. / math.sqrt(self.weight.size(1)) 44 | self.weight.data.uniform_(-stdv, stdv) 45 | if self.bias is not None: 46 | self.bias.data.uniform_(-stdv, stdv) 47 | # print(self.weight.data) 48 | # print(self.bias.data) 49 | 50 | def forward(self, input): 51 | # print(self.filter_square_matrix.mul(self.weight)) 52 | return F.linear(input, self.filter_square_matrix.mul(self.weight), self.bias) 53 | 54 | def __repr__(self): 55 | return self.__class__.__name__ + '(' \ 56 | + 'in_features=' + str(self.in_features) \ 57 | + ', out_features=' + str(self.out_features) \ 58 | + ', bias=' + str(self.bias is not None) + ')' 59 | 60 | class LSTMD(nn.Module): 61 | def __init__(self, input_size, cell_size, hidden_size, X_mean, output_last = False): 62 | 63 | super(LSTMD, self).__init__() 64 | 65 | self.hidden_size = hidden_size 66 | self.delta_size = input_size 67 | self.mask_size = input_size 68 | 69 | use_gpu = torch.cuda.is_available() 70 | if use_gpu: 71 | self.identity = torch.eye(input_size).cuda() 72 | self.zeros = Variable(torch.zeros(input_size).cuda()) 73 | self.X_mean = Variable(torch.Tensor(X_mean).cuda()) 74 | else: 75 | self.identity = torch.eye(input_size) 76 | self.zeros = Variable(torch.zeros(input_size)) 77 | self.X_mean = Variable(torch.Tensor(X_mean)) 78 | 79 | # self.zl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 80 | # self.rl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 81 | # self.hl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 82 | 83 | self.fl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 84 | self.il = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 85 | self.ol = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 86 | self.Cl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size) 87 | 88 | self.gamma_x_l = FilterLinear(self.delta_size, self.delta_size, self.identity) 89 | 90 | self.gamma_h_l = nn.Linear(self.delta_size, self.delta_size) 91 | 92 | self.output_last = output_last 93 | 94 | def step(self, x, x_last_obsv, x_mean, h, mask, delta, C): 95 | 96 | batch_size = x.shape[0] 97 | dim_size = x.shape[1] 98 | 99 | delta_x = torch.exp(-torch.max(self.zeros, self.gamma_x_l(delta))) 100 | delta_h = torch.exp(-torch.max(self.zeros, self.gamma_h_l(delta))) 101 | 102 | x = mask * x + (1 - mask) * (delta_x * x_last_obsv + (1 - delta_x) * x_mean) 103 | h = delta_h * h 104 | 105 | xhm = torch.cat((x, h, mask), 1) 106 | 107 | f = F.sigmoid(self.fl(xhm)) 108 | i = F.sigmoid(self.il(xhm)) 109 | o = F.sigmoid(self.ol(xhm)) 110 | C_tilde = F.tanh(self.Cl(xhm)) 111 | C = f * C + i * C_tilde 112 | h = o * F.tanh(C) 113 | 114 | return h, C 115 | 116 | def forward(self, input): 117 | batch_size = input.size(0) 118 | type_size = input.size(1) 119 | step_size = input.size(2) 120 | spatial_size = input.size(3) 121 | 122 | X = torch.squeeze(input[:,0,:,:]) 123 | Mask = torch.squeeze(input[:,1,:,:]) 124 | Delta = torch.squeeze(input[:,2,:,:]) 125 | X_last_obsv = torch.squeeze(input[:,3,:,:]) 126 | 127 | h, C = self.initHidden(batch_size) 128 | 129 | for i in range(step_size): 130 | h, C = self.step(torch.squeeze(X[:,i:i+1,:])\ 131 | , torch.squeeze(X_last_obsv[:,i:i+1,:])\ 132 | , torch.squeeze(self.X_mean[:,i:i+1,:])\ 133 | , h\ 134 | , torch.squeeze(Mask[:,i:i+1,:])\ 135 | , torch.squeeze(Delta[:,i:i+1,:])\ 136 | , C) 137 | return h 138 | 139 | def initHidden(self, batch_size): 140 | h = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 141 | C = Variable(torch.zeros(batch_size, self.hidden_size).cuda()) 142 | return h, C 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Python Scripts/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyongc/Graph-Markov-Network/8e29e58c8fe7879e53605485615d09356b990a85/Python Scripts/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /Python Scripts/__pycache__/utils.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyongc/Graph-Markov-Network/8e29e58c8fe7879e53605485615d09356b990a85/Python Scripts/__pycache__/utils.cpython-37.pyc -------------------------------------------------------------------------------- /Python Scripts/job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | for random_seed in 1 2 3 4 5 4 | do 5 | for dataset in PEMS-BAY METR-LA INRIX-SEA 6 | do 7 | for missing_rate in 0.1 0.2 0.4 8 | do 9 | python Exp_GMN.py -d $dataset -m $missing_rate -o Adam -l 0.001 -r $random_seed -s 0 -t random 10 | done 11 | done 12 | done 13 | 14 | for random_seed in 1 2 3 4 5 15 | do 16 | for dataset in PEMS-BAY METR-LA INRIX-SEA 17 | do 18 | for missing_rate in 0.1 0.2 0.4 19 | do 20 | python Exp_baseline.py -d $dataset -m $missing_rate -o Adam -l 0.001 -r $random_seed -s 0 -t random 21 | done 22 | done 23 | done 24 | -------------------------------------------------------------------------------- /Python Scripts/models.py: -------------------------------------------------------------------------------- 1 | import torch.utils.data as utils 2 | import torch.nn.functional as F 3 | import torch 4 | import torch.nn as nn 5 | from torch.autograd import Variable 6 | from torch.nn.parameter import Parameter 7 | 8 | import math 9 | 10 | 11 | dtype = torch.float32 12 | if torch.cuda.is_available(): 13 | device = torch.device("cuda:0") 14 | else: 15 | device = torch.device("cpu") 16 | 17 | 18 | class GMN(nn.Module): 19 | ''' 20 | The new version of GMN, which is idendical to the one in the Jupyter Script version 21 | ''' 22 | def __init__(self, A, dtype, device, num_layer = 6, gamma = 0.9): 23 | super(GMN, self).__init__() 24 | self.gamma = gamma 25 | self.n = A.shape[0] 26 | self.num_layer = num_layer 27 | self.dtype = dtype 28 | self.device = device 29 | 30 | self.A = torch.tensor(A, dtype = self.dtype, device = self.device) 31 | self.A = torch.clamp(self.A, min=0, max=1) 32 | self.A1 = self.A 33 | 34 | self.A_list = torch.empty(self.num_layer, self.n, self.n, dtype = self.dtype, device = self.device) 35 | for i in range(self.num_layer): 36 | if i == 0: 37 | self.A_list[i] = self.A 38 | else: 39 | self.A_list[i] = torch.clamp(torch.matmul(self.A_list[i-1], self.A), min=0, max=1) 40 | W_list = torch.empty(self.num_layer, self.n, self.n, dtype = self.dtype, device = self.device) 41 | nn.init.uniform_(W_list) 42 | self.W_list = torch.nn.Parameter(W_list) 43 | 44 | 45 | def forward(self, input): 46 | batch_size = input.size(0) 47 | type_size = input.size(1) 48 | step_size = input.size(2) 49 | spatial_size = input.size(3) 50 | 51 | X = torch.squeeze(input[:,0,:,:]) 52 | M = torch.squeeze(input[:,1,:,:]) 53 | 54 | Y_hat = None 55 | for i in range(0, self.num_layer): 56 | if i == 0: 57 | Y_hat = self.gamma * (torch.mm(X[:,-1,:], self.A_list[0] * self.W_list[0])) 58 | elif i == 1: 59 | Y_hat += self.gamma**2 * (torch.mm(X[:,-2,:] * (1-M[:,-1,:]), self.A_list[1] * self.W_list[1])) 60 | else: 61 | # i >= 2 62 | NonMissing = (1-M[:,-1,:]) 63 | for j in range(1, i): 64 | NonMissing = NonMissing * (1-M[:,-(j+1),:]) 65 | Y_hat += self.gamma**(i+1) * (torch.mm(X[:,-(i+1),:] * NonMissing, self.A_list[i] * self.W_list[i])) 66 | return Y_hat 67 | 68 | 69 | class GNN(nn.Module): 70 | ''' 71 | The hard code version of GMN, which was used in the experimental testing stage. 72 | ''' 73 | def __init__(self, A, layer = 5, gamma = 0.9): 74 | 75 | super(GNN, self).__init__() 76 | 77 | self.gamma = gamma 78 | 79 | self.n = A.shape[0] 80 | 81 | self.layer = layer 82 | 83 | self.A = torch.tensor(A, dtype = dtype, device = device) 84 | self.A = torch.clamp(self.A, min=0, max=1) 85 | self.A1 = self.A 86 | self.W1 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 87 | 88 | self.A2 = torch.matmul(self.A1, self.A) 89 | self.A2 = torch.clamp(self.A2, min=0, max=1) 90 | self.W2 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 91 | 92 | self.A3 = torch.matmul(self.A2, self.A) 93 | self.A3 = torch.clamp(self.A3, min=0, max=1) 94 | self.W3 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 95 | 96 | self.A4 = torch.matmul(self.A3, self.A) 97 | self.A4 = torch.clamp(self.A4, min=0, max=1) 98 | self.W4 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 99 | 100 | self.A5 = torch.matmul(self.A4, self.A) 101 | self.A5 = torch.clamp(self.A5, min=0, max=1) 102 | self.W5 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 103 | 104 | self.A6 = torch.matmul(self.A5, self.A) 105 | self.A6 = torch.clamp(self.A6, min=0, max=1) 106 | self.W6 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 107 | 108 | self.A7 = torch.matmul(self.A6, self.A) 109 | self.A7 = torch.clamp(self.A7, min=0, max=1) 110 | self.W7 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 111 | 112 | self.A8 = torch.matmul(self.A7, self.A) 113 | self.A8 = torch.clamp(self.A8, min=0, max=1) 114 | self.W8 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 115 | 116 | self.A9 = torch.matmul(self.A8, self.A) 117 | self.A9 = torch.clamp(self.A9, min=0, max=1) 118 | self.W9 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 119 | 120 | self.A10 = torch.matmul(self.A9, self.A) 121 | self.A10 = torch.clamp(self.A10, min=0, max=1) 122 | self.W10 = torch.nn.Parameter(torch.randn([self.n, self.n],dtype = dtype, device = device)) 123 | 124 | 125 | def forward(self, input): 126 | 127 | batch_size = input.size(0) 128 | type_size = input.size(1) 129 | step_size = input.size(2) 130 | spatial_size = input.size(3) 131 | 132 | X = torch.squeeze(input[:,0,:,:]) 133 | M = torch.squeeze(input[:,1,:,:]) 134 | 135 | Y_hat = self.gamma * (torch.mm(X[:,-1,:], self.A1 * self.W1)) 136 | Y_hat += self.gamma**2 * (torch.mm(X[:,-2,:] * (1-M[:,-1,:]), self.A2 * self.W2)) 137 | if self.layer >=3: 138 | Y_hat += self.gamma**3 * (torch.mm(X[:,-3,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]), self.A3 * self.W3)) 139 | if self.layer >=4: 140 | Y_hat += self.gamma**4 * (torch.mm(X[:,-4,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]), self.A4 * self.W4)) 141 | if self.layer >=5: 142 | Y_hat += self.gamma**5 * (torch.mm(X[:,-5,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]), self.A5 * self.W5)) 143 | if self.layer >=6: 144 | Y_hat += self.gamma**6 * (torch.mm(X[:,-6,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]), self.A6 * self.W6)) 145 | if self.layer >=7: 146 | Y_hat += self.gamma**7 * (torch.mm(X[:,-7,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]), self.A7 * self.W7)) 147 | if self.layer >=8: 148 | Y_hat += self.gamma**8 * (torch.mm(X[:,-8,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]), self.A8 * self.W8)) 149 | if self.layer >=9: 150 | Y_hat += self.gamma**9 * (torch.mm(X[:,-9,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]) * (1-M[:,-8,:]), self.A9 * self.W9)) 151 | if self.layer >=10: 152 | Y_hat += self.gamma**10 * (torch.mm(X[:,-10,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]) * (1-M[:,-8,:])* (1-M[:,-9,:]), self.A10 * self.W10)) 153 | 154 | return Y_hat 155 | 156 | 157 | 158 | class SGNN(nn.Module): 159 | def __init__(self, A, layer = 5, gamma = 0.9): 160 | 161 | super(SGNN, self).__init__() 162 | 163 | self.gamma = gamma 164 | 165 | self.layer = layer 166 | 167 | self.n = A.shape[0] 168 | A = torch.tensor(A, dtype = dtype, device = device) 169 | D_rsqrt = torch.tensor(torch.diag(torch.sum(A, dim=0).pow(-0.5)), dtype = dtype, device = device) 170 | I = torch.tensor(torch.eye(self.n, self.n), dtype = dtype, device = device) 171 | self.L = I - D_rsqrt.mm(A).mm(D_rsqrt) 172 | self.e, self.v = torch.symeig(self.L, eigenvectors=True) 173 | 174 | self.F_x_param = Parameter(torch.Tensor(self.n)) 175 | self.F_h_param = Parameter(torch.Tensor(self.n)) 176 | 177 | self.A1 = Parameter(torch.Tensor(self.n)) 178 | self.A2 = Parameter(torch.Tensor(self.n)) 179 | self.A3 = Parameter(torch.Tensor(self.n)) 180 | self.A4 = Parameter(torch.Tensor(self.n)) 181 | self.A5 = Parameter(torch.Tensor(self.n)) 182 | self.A6 = Parameter(torch.Tensor(self.n)) 183 | self.A7 = Parameter(torch.Tensor(self.n)) 184 | self.A8 = Parameter(torch.Tensor(self.n)) 185 | self.A9 = Parameter(torch.Tensor(self.n)) 186 | self.A10 = Parameter(torch.Tensor(self.n)) 187 | 188 | self.reset_parameters() 189 | 190 | def forward(self, input): 191 | 192 | batch_size = input.size(0) 193 | type_size = input.size(1) 194 | step_size = input.size(2) 195 | spatial_size = input.size(3) 196 | 197 | X = torch.squeeze(input[:,0,:,:]) 198 | M = torch.squeeze(input[:,1,:,:]) 199 | 200 | A1_diag = torch.diag(self.A1) 201 | A2_diag = torch.diag(self.A2) 202 | A3_diag = torch.diag(self.A3) 203 | A4_diag = torch.diag(self.A4) 204 | A5_diag = torch.diag(self.A5) 205 | A6_diag = torch.diag(self.A6) 206 | A7_diag = torch.diag(self.A7) 207 | A8_diag = torch.diag(self.A8) 208 | A9_diag = torch.diag(self.A9) 209 | A10_diag = torch.diag(self.A10) 210 | 211 | 212 | Y_hat = self.gamma * (torch.mm(X[:,-1,:], self.v.matmul(A1_diag).matmul(torch.t(self.v)))) 213 | Y_hat += self.gamma**2 * (torch.mm(X[:,-2,:] * (1-M[:,-1,:]), self.v.matmul(A2_diag).matmul(torch.t(self.v)))) 214 | Y_hat += self.gamma**3 * (torch.mm(X[:,-3,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]), self.v.matmul(A3_diag).matmul(torch.t(self.v)))) 215 | Y_hat += self.gamma**4 * (torch.mm(X[:,-4,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]), self.v.matmul(A4_diag).matmul(torch.t(self.v)))) 216 | if self.layer >=5: 217 | Y_hat += self.gamma**5 * (torch.mm(X[:,-5,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]), self.v.matmul(A5_diag).matmul(torch.t(self.v)))) 218 | if self.layer >=6: 219 | Y_hat += self.gamma**6 * (torch.mm(X[:,-6,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]), self.v.matmul(A6_diag).matmul(torch.t(self.v)))) 220 | if self.layer >=7: 221 | Y_hat += self.gamma**7 * (torch.mm(X[:,-7,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]), self.v.matmul(A7_diag).matmul(torch.t(self.v)))) 222 | if self.layer >=8: 223 | Y_hat += self.gamma**8 * (torch.mm(X[:,-8,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]), self.v.matmul(A8_diag).matmul(torch.t(self.v)))) 224 | if self.layer >=9: 225 | Y_hat += self.gamma**9 * (torch.mm(X[:,-9,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]) * (1-M[:,-8,:]), self.v.matmul(A9_diag).matmul(torch.t(self.v)))) 226 | if self.layer >=10: 227 | Y_hat += self.gamma**10 * (torch.mm(X[:,-10,:] * (1-M[:,-1,:]) * (1-M[:,-2,:]) * (1-M[:,-3,:]) * (1-M[:,-4,:]) * (1-M[:,-5,:]) * (1-M[:,-6,:]) * (1-M[:,-7,:]) * (1-M[:,-8,:])* (1-M[:,-9,:]), self.v.matmul(A10_diag).matmul(torch.t(self.v)))) 228 | 229 | return Y_hat 230 | 231 | def reset_parameters(self): 232 | stdv = 1. / math.sqrt(self.n) 233 | self.A1.data.uniform_(-stdv, stdv) 234 | self.A2.data.uniform_(-stdv, stdv) 235 | self.A3.data.uniform_(-stdv, stdv) 236 | self.A4.data.uniform_(-stdv, stdv) 237 | self.A5.data.uniform_(-stdv, stdv) 238 | self.A6.data.uniform_(-stdv, stdv) 239 | self.A7.data.uniform_(-stdv, stdv) 240 | self.A8.data.uniform_(-stdv, stdv) 241 | self.A9.data.uniform_(-stdv, stdv) 242 | self.A10.data.uniform_(-stdv, stdv) 243 | 244 | 245 | 246 | class LSTM(nn.Module): 247 | def __init__(self, n, imputation = False): 248 | """ 249 | cell_size is the size of cell_state. 250 | hidden_size is the size of hidden_state, or say the output_state of each step 251 | """ 252 | super(LSTM, self).__init__() 253 | 254 | self.n = n 255 | self.fl = nn.Linear(2 * n, n) 256 | self.il = nn.Linear(2 * n, n) 257 | self.ol = nn.Linear(2 * n, n) 258 | self.Cl = nn.Linear(2 * n, n) 259 | 260 | self.imputation = imputation 261 | 262 | def step(self, x, h, C): 263 | xh = torch.cat((x, h), 1) 264 | f = F.sigmoid(self.fl(xh)) 265 | i = F.sigmoid(self.il(xh)) 266 | o = F.sigmoid(self.ol(xh)) 267 | C_tilde = F.tanh(self.Cl(xh)) 268 | C = f * C + i * C_tilde 269 | h = o * F.tanh(C) 270 | 271 | return h, C 272 | 273 | def forward(self, input): 274 | batch_size = input.size(0) 275 | type_size = input.size(1) 276 | step_size = input.size(2) 277 | spatial_size = input.size(3) 278 | 279 | X = torch.squeeze(input[:,0,:,:]) 280 | M = torch.squeeze(input[:,1,:,:]) 281 | # Delta = torch.squeeze(input[:,2,:,:]) 282 | # X_last_obsv = torch.squeeze(input[:,3,:,:]) 283 | 284 | h, C = self.initHidden(batch_size, spatial_size) 285 | 286 | for i in range(step_size): 287 | if self.imputation: 288 | x = X[:,i,:] * M[:,i,:] + h * (1 - M[:,i,:]) 289 | else: 290 | x = X[:,i,:] 291 | h, C = self.step(x, h, C) 292 | return h 293 | 294 | def initHidden(self, batch_size, spatial_size): 295 | Hidden_State = torch.zeros((batch_size, spatial_size), dtype = dtype, device = device) 296 | Cell_State = torch.zeros((batch_size, spatial_size), dtype = dtype, device = device) 297 | return Hidden_State, Cell_State 298 | 299 | 300 | class GRU(nn.Module): 301 | def __init__(self, n, imputation = False): 302 | """ 303 | cell_size is the size of cell_state. 304 | hidden_size is the size of hidden_state, or say the output_state of each step 305 | """ 306 | super(GRU, self).__init__() 307 | 308 | self.n = n 309 | self.zl = nn.Linear(2 * n, n) 310 | self.rl = nn.Linear(2 * n, n) 311 | self.hl = nn.Linear(2 * n, n) 312 | 313 | self.imputation = imputation 314 | 315 | def step(self, x, h): 316 | xh = torch.cat((x, h), 1) 317 | z = F.sigmoid(self.zl(xh)) 318 | r = F.sigmoid(self.rl(xh)) 319 | combined_r = torch.cat((x, r * h), 1) 320 | h_tilde = F.tanh(self.hl(combined_r)) 321 | h = (1 - z) * h + z * h_tilde 322 | return h 323 | 324 | def forward(self, input): 325 | batch_size = input.size(0) 326 | type_size = input.size(1) 327 | step_size = input.size(2) 328 | spatial_size = input.size(3) 329 | 330 | X = torch.squeeze(input[:,0,:,:]) 331 | M = torch.squeeze(input[:,1,:,:]) 332 | # Delta = torch.squeeze(input[:,2,:,:]) 333 | # X_last_obsv = torch.squeeze(input[:,3,:,:]) 334 | 335 | h = self.initHidden(batch_size, spatial_size) 336 | 337 | for i in range(step_size): 338 | if self.imputation: 339 | x = X[:,i,:] * M[:,i,:] + h * (1 - M[:,i,:]) 340 | else: 341 | x = X[:,i,:] 342 | h = self.step(x, h) 343 | return h 344 | 345 | def initHidden(self, batch_size, spatial_size): 346 | Hidden_State = torch.zeros((batch_size, spatial_size), dtype = dtype, device = device) 347 | return Hidden_State -------------------------------------------------------------------------------- /Python Scripts/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.autograd import Variable 3 | import torch.utils.data as utils 4 | 5 | # import torch.utils.data as utils 6 | # import torch.nn.functional as F 7 | # import torch 8 | # import torch.nn as nn 9 | 10 | # from torch.nn.parameter import Parameter 11 | 12 | import math 13 | import numpy as np 14 | import copy 15 | import time 16 | 17 | def PrepareDataset(speed_matrix, \ 18 | BATCH_SIZE = 64, \ 19 | seq_len = 10, \ 20 | pred_len = 1, \ 21 | train_propotion = 0.7, \ 22 | valid_propotion = 0.2, \ 23 | mask_ones_proportion = 0.8, \ 24 | masking = True, \ 25 | masking_type = 'random', \ 26 | delta_last_obsv = False, \ 27 | shuffle = True, \ 28 | random_seed = 1024, \ 29 | ): 30 | """ Prepare training and testing datasets and dataloaders. 31 | 32 | Convert speed/volume/occupancy matrix to training and testing dataset. 33 | The vertical axis of speed_matrix is the time axis and the horizontal axis 34 | is the spatial axis. 35 | 36 | Args: 37 | speed_matrix: a Matrix containing spatial-temporal speed data for a network 38 | seq_len: length of input sequence 39 | pred_len: length of predicted sequence 40 | Returns: 41 | Training dataloader 42 | Testing dataloader 43 | """ 44 | 45 | print('Start Generate Data') 46 | print('\t batch_size:', BATCH_SIZE, '\t\t input_len:', seq_len, '\t\t\t pred_len:', pred_len) 47 | print('\t train_set:', train_propotion, '\t\t valid_set:', valid_propotion, '\t\t test_set:', np.around(1 - train_propotion - valid_propotion, decimals=2)) 48 | print('\t masking:', masking, '\t\t\t mask_ones_rate:', mask_ones_proportion, '\t\t delta & last_obsv:', delta_last_obsv) 49 | print('\t shuffle dataset:', shuffle, '\t\t random_seed:', random_seed) 50 | 51 | np.random.seed(random_seed) 52 | 53 | time_len = speed_matrix.shape[0] 54 | speed_matrix = speed_matrix.clip(0, 100) 55 | 56 | max_speed = speed_matrix.max().max() 57 | speed_matrix = speed_matrix / max_speed 58 | 59 | data_zero_values = np.where(speed_matrix == 0)[0].shape[0] 60 | data_zero_rate = data_zero_values / (speed_matrix.shape[0] * speed_matrix.shape[1]) 61 | print('Orignal dataset missing rate:', data_zero_rate) 62 | 63 | print('Generating input and labels...') 64 | if not masking: 65 | speed_sequences, speed_labels = [], [] 66 | for i in range(time_len - seq_len - pred_len): 67 | speed_sequences.append(speed_matrix.iloc[i:i+seq_len].values) 68 | speed_labels.append(speed_matrix.iloc[i+seq_len:i+seq_len+pred_len].values) 69 | speed_sequences, speed_labels = np.asarray(speed_sequences), np.asarray(speed_labels) 70 | print('Input sequences and labels are generated.') 71 | 72 | else: 73 | # using zero-one mask to randomly set elements to zeros 74 | # genertate mask 75 | 76 | if masking_type == 'random': 77 | Mask = np.random.choice([0,1], size=(speed_matrix.shape), p = [1 - mask_ones_proportion, mask_ones_proportion]) 78 | masked_speed_matrix = np.multiply(speed_matrix, Mask) 79 | Mask[np.where(masked_speed_matrix == 0)] = 0 80 | mask_zero_values = np.where(Mask == 0)[0].shape[0] / (Mask.shape[0] * Mask.shape[1]) 81 | print('\t Masked dataset missing rate:', np.around(mask_zero_values, decimals=4),'(mask zero rate:', np.around(1 - mask_ones_proportion, decimals=4), ')') 82 | 83 | speed_sequences, speed_labels = [], [] 84 | for i in range(time_len - seq_len - pred_len): 85 | speed_sequences.append(masked_speed_matrix.iloc[i:i+seq_len].values) 86 | speed_labels.append(speed_matrix.iloc[i+seq_len:i+seq_len+pred_len].values) 87 | speed_sequences, speed_labels = np.asarray(speed_sequences), np.asarray(speed_labels) 88 | print('Input sequences, labels, and masks are generated.') 89 | 90 | # Mask sequences 91 | Mask = np.ones_like(speed_sequences) 92 | Mask[np.where(speed_sequences == 0)] = 0 93 | 94 | if delta_last_obsv: 95 | # temporal information 96 | interval = 5 # 5 minutes 97 | S = np.zeros_like(speed_sequences) # time stamps 98 | for i in range(S.shape[1]): 99 | S[:,i,:] = interval * i 100 | 101 | Delta = np.zeros_like(speed_sequences) # time intervals 102 | for i in range(1, S.shape[1]): 103 | Delta[:,i,:] = S[:,i,:] - S[:,i-1,:] 104 | 105 | Delta = Delta / Delta.max() # normalize 106 | 107 | missing_index = np.where(speed_sequences == 0) 108 | 109 | X_last_obsv = np.copy(speed_sequences) 110 | for idx in range(missing_index[0].shape[0]): 111 | i = missing_index[0][idx] 112 | j = missing_index[1][idx] 113 | k = missing_index[2][idx] 114 | if j != 0 and j != seq_len-1: 115 | Delta[i,j+1,k] = Delta[i,j+1,k] + Delta[i,j,k] 116 | if j != 0: 117 | X_last_obsv[i,j,k] = X_last_obsv[i,j-1,k] # last observation 118 | 119 | print('Time intervals of missing values and last_observations are generated.') 120 | 121 | # shuffle and split the dataset to training and testing datasets 122 | print('Start to shuffle dataset ...') 123 | if shuffle: 124 | sample_size = speed_sequences.shape[0] 125 | index = np.arange(sample_size, dtype = int) 126 | np.random.shuffle(index) 127 | speed_sequences = speed_sequences[index] 128 | speed_labels = speed_labels[index] 129 | if masking: 130 | Mask = Mask[index] 131 | if delta_last_obsv: 132 | Delta = Delta[index] 133 | X_last_obsv = X_last_obsv[index] 134 | print('Dataset Shuffled. Start to split dataset ...') 135 | 136 | if not masking: 137 | dataset_agger = speed_sequences 138 | else: 139 | speed_sequences = np.expand_dims(speed_sequences, axis=1) 140 | Mask = np.expand_dims(Mask, axis=1) 141 | if delta_last_obsv: 142 | Delta = np.expand_dims(Delta, axis=1) 143 | X_last_obsv = np.expand_dims(X_last_obsv, axis=1) 144 | dataset_agger = np.concatenate((speed_sequences, Mask, Delta, X_last_obsv), axis = 1) 145 | else: 146 | dataset_agger = np.concatenate((speed_sequences, Mask), axis = 1) 147 | 148 | train_index = int(np.floor(sample_size * train_propotion)) 149 | valid_index = int(np.floor(sample_size * ( train_propotion + valid_propotion))) 150 | 151 | if masking: 152 | train_data, train_label = dataset_agger[:train_index], speed_labels[:train_index] 153 | valid_data, valid_label = dataset_agger[train_index:valid_index], speed_labels[train_index:valid_index] 154 | test_data, test_label = dataset_agger[valid_index:], speed_labels[valid_index:] 155 | else: 156 | train_data, train_label = speed_sequences[:train_index], speed_labels[:train_index] 157 | valid_data, valid_label = speed_sequences[train_index:valid_index], speed_labels[train_index:valid_index] 158 | test_data, test_label = speed_sequences[valid_index:], speed_labels[valid_index:] 159 | 160 | train_data, train_label = torch.Tensor(train_data), torch.Tensor(train_label) 161 | valid_data, valid_label = torch.Tensor(valid_data), torch.Tensor(valid_label) 162 | test_data, test_label = torch.Tensor(test_data), torch.Tensor(test_label) 163 | 164 | train_dataset = utils.TensorDataset(train_data, train_label) 165 | valid_dataset = utils.TensorDataset(valid_data, valid_label) 166 | test_dataset = utils.TensorDataset(test_data, test_label) 167 | 168 | train_dataloader = utils.DataLoader(train_dataset, batch_size = BATCH_SIZE, shuffle=True, drop_last = True) 169 | valid_dataloader = utils.DataLoader(valid_dataset, batch_size = BATCH_SIZE, shuffle=True, drop_last = True) 170 | test_dataloader = utils.DataLoader(test_dataset, batch_size = BATCH_SIZE, shuffle=False, drop_last = False) 171 | 172 | X_mean = np.mean(speed_sequences, axis = 0) 173 | 174 | print('Finished') 175 | 176 | return train_dataloader, valid_dataloader, test_dataloader, max_speed, X_mean 177 | 178 | 179 | 180 | def TrainModel(model, train_dataloader, valid_dataloader, learning_rate = 1e-5, optm = 'Adam', num_epochs = 300, patience = 10, min_delta = 0.00001): 181 | 182 | inputs, labels = next(iter(train_dataloader)) 183 | [batch_size, type_size, step_size, fea_size] = inputs.size() 184 | input_dim = fea_size 185 | hidden_dim = fea_size 186 | output_dim = fea_size 187 | 188 | model.cuda() 189 | 190 | loss_MSE = torch.nn.MSELoss() 191 | loss_L1 = torch.nn.L1Loss() 192 | 193 | learning_rate = learning_rate 194 | if optm == 'Adam': 195 | optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate) 196 | elif optm == 'Adadelta': 197 | optimizer = torch.optim.Adadelta(model.parameters(), lr = learning_rate) 198 | elif optm == 'RMSprop': 199 | optimizer = torch.optim.RMSprop(model.parameters(), lr = learning_rate) 200 | 201 | use_gpu = torch.cuda.is_available() 202 | 203 | losses_train = [] 204 | losses_valid = [] 205 | losses_epochs_train = [] 206 | losses_epochs_valid = [] 207 | 208 | train_time_list = [] 209 | valid_time_list = [] 210 | 211 | cur_time = time.time() 212 | pre_time = time.time() 213 | 214 | # Variables for Early Stopping 215 | is_best_model = 0 216 | patient_epoch = 0 217 | 218 | losses_epochs_valid_steps_l1 = [] 219 | 220 | sub_epoch = 1 221 | 222 | for epoch in range(0, num_epochs): 223 | # print('Epoch {}/{}'.format(epoch, num_epochs - 1)) 224 | # print('-' * 10) 225 | 226 | losses_epoch_train = [] 227 | losses_epoch_valid = [] 228 | 229 | train_start = time.time() 230 | 231 | for data in train_dataloader: 232 | inputs, labels = data 233 | 234 | if inputs.shape[0] != batch_size: 235 | continue 236 | 237 | if use_gpu: 238 | inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) 239 | else: 240 | inputs, labels = Variable(inputs), Variable(labels) 241 | 242 | model.zero_grad() 243 | 244 | outputs = model(inputs) 245 | 246 | if len(labels[labels==0]): 247 | label_mask = torch.ones_like(labels).cuda() 248 | label_mask = label_mask * labels 249 | label_mask[label_mask!=0] = 1 250 | loss_train = loss_MSE(outputs * label_mask, torch.squeeze(labels)) 251 | else: 252 | loss_train = loss_MSE(outputs, torch.squeeze(labels)) 253 | # print(loss_train.item()) 254 | # print([loss_train.data[0]]) 255 | # print(loss_train.data.cpu().numpy()[0]) 256 | # print(loss_train.item()) 257 | losses_epoch_train.append(loss_train.item()) 258 | 259 | optimizer.zero_grad() 260 | 261 | loss_train.backward() 262 | 263 | optimizer.step() 264 | 265 | train_end = time.time() 266 | 267 | # validation 268 | valid_start = time.time() 269 | 270 | losses_l1_allsteps_val = [] 271 | 272 | for data in valid_dataloader: 273 | 274 | inputs_val, labels_val = data 275 | 276 | if use_gpu: 277 | inputs_val, labels_val = Variable(inputs_val.cuda()), Variable(labels_val.cuda()) 278 | else: 279 | inputs_val, labels_val = Variable(inputs_val), Variable(labels_val) 280 | 281 | outputs_val= model(inputs_val) 282 | 283 | if len(labels_val[labels_val==0]): 284 | labels_val_mask = torch.ones_like(labels_val).cuda() 285 | labels_val_mask = labels_val_mask * labels_val 286 | labels_val_mask[labels_val_mask!=0] = 1 287 | 288 | loss_valid = loss_MSE(outputs_val * labels_val_mask, torch.squeeze(labels_val)) 289 | else: 290 | loss_valid = loss_MSE(outputs_val , torch.squeeze(labels_val)) 291 | 292 | 293 | # print(inputs_val[:,1:,:].shape) 294 | 295 | # print(labels_val.shape) 296 | 297 | # full_labels_val = torch.cat((inputs_val[:,1:,:], labels_val), dim = 1) 298 | 299 | # delta = torch.abs(full_labels_val - outputs_val) 300 | 301 | # delta_batch_mean = torch.mean(delta, dim = 0) 302 | 303 | # delta_batch_spatial_mean = torch.mean(delta_batch_mean, dim = 1) 304 | 305 | # losses_l1_allsteps_val.append(delta_batch_spatial_mean.data.cpu().numpy()) 306 | 307 | # if losses_l1_allsteps_val is None: 308 | # losses_l1_allsteps_val = delta_batch_spatial_mean 309 | # else: 310 | # losses_l1_allsteps_val += delta_batch_spatial_mean 311 | 312 | # # mean l1 loss for each step of the sequences of a batched sample 313 | # mean_loss_l1_allsteps = torch.mean(torch.mean(torch.abs(full_labels_val - outputs_val), dim = 0), dim = 1) 314 | 315 | # # print(mean_loss_l1_allsteps) 316 | # losses_l1_allsteps_val.append(delta_batch_spatial_mean.data.cpu().numpy(), ) 317 | 318 | losses_epoch_valid.append(loss_valid.item()) 319 | 320 | valid_end = time.time() 321 | 322 | avg_losses_epoch_train = sum(losses_epoch_train) / float(len(losses_epoch_train)) 323 | avg_losses_epoch_valid = sum(losses_epoch_valid) / float(len(losses_epoch_valid)) 324 | losses_epochs_train.append(avg_losses_epoch_train) 325 | losses_epochs_valid.append(avg_losses_epoch_valid) 326 | 327 | # print(losses_l1_allsteps_val) 328 | 329 | if losses_l1_allsteps_val: 330 | losses_l1_allsteps_val = np.asarray(losses_l1_allsteps_val) 331 | losses_l1_allsteps_mean = np.mean(losses_l1_allsteps_val, 0) 332 | losses_epochs_valid_steps_l1.append(losses_l1_allsteps_mean) 333 | # print(losses_l1_allsteps_val) 334 | # # losses_l1_allsteps_mean = (losses_l1_allsteps_mean / valid_batch_number).data.cpu().numpy() 335 | # # losses_l1_allsteps_mean = torch.mean(losses_l1_allsteps_val, dim = 0).data.cpu().numpy() 336 | # # # print(losses_l1_allsteps_mean.shape) 337 | # # # losses_epochs_valid_steps_l1.append(losses_l1_allsteps_mean) 338 | 339 | fig, ax = plt.subplots( nrows=1, ncols=1 ) # create figure & 1 axis 340 | ax.plot(losses_l1_allsteps_mean) 341 | plt.ylim((0, 0.1)) 342 | plt.title('Epoch: ' + str(epoch)) 343 | plt.ylabel('MSE') 344 | plt.xlabel('Step') 345 | fig.savefig('./Figures/' + type(model).__name__ + '_' + str(epoch) + '.png') # save the figure to file 346 | plt.close(fig) 347 | 348 | # Early Stopping 349 | if epoch == 0: 350 | is_best_model = 1 351 | best_model = copy.deepcopy(model) 352 | min_loss_epoch_valid = 10000.0 353 | if avg_losses_epoch_valid < min_loss_epoch_valid: 354 | min_loss_epoch_valid = avg_losses_epoch_valid 355 | 356 | # sub_epoch += 1 357 | else: 358 | if min_loss_epoch_valid - avg_losses_epoch_valid > min_delta: 359 | is_best_model = 1 360 | best_model = model 361 | min_loss_epoch_valid = avg_losses_epoch_valid 362 | patient_epoch = 0 363 | else: 364 | is_best_model = 0 365 | patient_epoch += 1 366 | if patient_epoch >= patience: 367 | print('Early Stopped at Epoch:', epoch) 368 | break 369 | 370 | if (epoch >= 5 and (patient_epoch == 4 or sub_epoch % 50 == 0)) and learning_rate > 1e-5: 371 | learning_rate = learning_rate / 10 372 | if optm == 'Adam': 373 | optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate) 374 | elif optm == 'Adadelta': 375 | optimizer = torch.optim.Adadelta(model.parameters(), lr = learning_rate) 376 | elif optm == 'RMSprop': 377 | optimizer = torch.optim.RMSprop(model.parameters(), lr = learning_rate) 378 | sub_epoch = 1 379 | else: 380 | sub_epoch += 1 381 | 382 | 383 | # Print training parameters 384 | cur_time = time.time() 385 | train_time = np.around([train_end - train_start] , decimals=2) 386 | train_time_list.append(train_time) 387 | valid_time = np.around([valid_end - valid_start] , decimals=2) 388 | valid_time_list.append(valid_time) 389 | 390 | print('Epoch: {}, train_loss: {}, valid_loss: {}, lr: {}, train_time: {}, valid_time: {}, best model: {}'.format( \ 391 | epoch, \ 392 | np.around(avg_losses_epoch_train, decimals=8),\ 393 | np.around(avg_losses_epoch_valid, decimals=8),\ 394 | learning_rate,\ 395 | np.around([train_end - train_start] , decimals=2),\ 396 | np.around([valid_end - valid_start] , decimals=2),\ 397 | is_best_model) ) 398 | pre_time = cur_time 399 | 400 | train_time_avg = np.mean(np.array(train_time_list)) 401 | valid_time_avg = np.mean(np.array(valid_time_list)) 402 | 403 | return best_model, [losses_train, losses_valid, losses_epochs_train, losses_epochs_valid, losses_epochs_valid_steps_l1, train_time_avg, valid_time_avg] 404 | 405 | 406 | 407 | def TestModel(model, test_dataloader, max_speed): 408 | 409 | inputs, labels = next(iter(test_dataloader)) 410 | [batch_size, step_size, step_size, fea_size] = inputs.size() 411 | 412 | cur_time = time.time() 413 | pre_time = time.time() 414 | 415 | use_gpu = torch.cuda.is_available() 416 | 417 | loss_MSE = torch.nn.MSELoss() 418 | loss_L1 = torch.nn.L1Loss() 419 | 420 | tested_batch = 0 421 | 422 | losses_mse = [] 423 | losses_l1 = [] 424 | 425 | output_list = [] 426 | label_list = [] 427 | 428 | losses_l1_allsteps = None 429 | 430 | for data in test_dataloader: 431 | inputs, labels = data 432 | 433 | if inputs.shape[0] != batch_size: 434 | continue 435 | 436 | if use_gpu: 437 | inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda()) 438 | else: 439 | inputs, labels = Variable(inputs), Variable(labels) 440 | 441 | outputs = model(inputs) 442 | 443 | loss_mse = loss_MSE(outputs, torch.squeeze(labels)) 444 | loss_l1 = loss_L1(outputs, torch.squeeze(labels)) 445 | 446 | losses_mse.append(loss_mse.cpu().data.numpy()) 447 | losses_l1.append(loss_l1.cpu().data.numpy()) 448 | 449 | output_list.append(torch.squeeze(outputs).cpu().data.numpy()) 450 | label_list.append(torch.squeeze(labels).cpu().data.numpy()) 451 | 452 | tested_batch += 1 453 | 454 | if tested_batch % 1000 == 0: 455 | cur_time = time.time() 456 | print('Tested #: {}, loss_l1: {}, loss_mse: {}, time: {}'.format( \ 457 | tested_batch * batch_size, \ 458 | np.around([loss_l1.data[0]], decimals=8), \ 459 | np.around([loss_mse.data[0]], decimals=8), \ 460 | np.around([cur_time - pre_time], decimals=8) ) ) 461 | pre_time = cur_time 462 | # print(losses_l1) 463 | losses_l1 = np.array(losses_l1) 464 | losses_mse = np.array(losses_mse) 465 | output_list = np.array(output_list) 466 | label_list = np.array(label_list) 467 | 468 | non_zero_index = np.nonzero(label_list) 469 | MAE = np.mean(np.absolute(output_list[non_zero_index] - label_list[non_zero_index])) * max_speed 470 | RMSE = np.sqrt(np.mean(np.square(output_list[non_zero_index]* max_speed - label_list[non_zero_index]* max_speed))) 471 | MAPE = np.mean(np.absolute(output_list[non_zero_index] - label_list[non_zero_index])/label_list[non_zero_index]) * 100 472 | MAE = np.around(MAE, decimals=3) 473 | RMSE = np.around(RMSE, decimals=3) 474 | MAPE = np.around(MAPE, decimals=3) 475 | print('Tested: MAE: {}, RMSE : {}, MAPE : {} %, '.format( MAE, RMSE, MAPE)) 476 | return [losses_l1, losses_mse, None, MAE, RMSE, MAPE, losses_l1_allsteps] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Graph Markov Network for Traffic Forecasting with Missing Data 2 | > Code for this post: [Graph Markov Network for Traffic Forecasting with Missing Data](https://zhiyongcui.com/blog/2020/07/16/graph-markov-network.html) 3 | ##### Table of Contents 4 | * [Introduction](#introduction) 5 | * [Usage](#usage) 6 | * [Requirements](#requirements) 7 | * [Data Preparation](#data-preparation) 8 | * [Run Jupyter Scripts for Demonstration](#run-jupyter-scripts-for-demonstration) 9 | * [Run Python Scripts for Experiments](#run-python-scripts-for-experiments) 10 | * [Reference](#reference) 11 | * [Citation](#citation) 12 | 13 | 16 | 17 | ## Introduction 18 | This is the github repo for sharing the code for implementing the Graph Markov Network (GMN) proposed in [1]. The GMN is proposed to solve the traffic forecasting problems while the traffic data has missing values. The Graph Markov Model is designed based on the Graph Markov Process (GMP), which provides a new perspective to model the transition process of the spatial-temporal data. 19 | 20 | The idea of GMN is very simple and easy to be implemented. The structure of GMN is similar to the autoregressive model and recurrent neural networks. The difference is that GMN takes the spatial structure of the data (network-wide traffic states) as a graph and attempts to infer missing values from the values of neighboring nodes in the graph. The following figure demonstrates the GMP structure. The gray-colored nodes in the left demonstrate the nodes with missing values. Vectors on the right side represent the traffic states. The traffic states at time _t_ are numbered to match the graph and the vector. The future state (in red color) can be inferred from their neighbors at previous time steps. 21 | 22 |

23 | 24 | For most details, you can refer to the paper \[[TR Part C](https://www.sciencedirect.com/science/article/pii/S0968090X20305866)\] or \[[arXiv](https://arxiv.org/abs/1912.05457)\]. 25 | 26 | The ideas and the methodologies of GMP and GMN are detailed in this **[Post](https://zhiyongcui.com/blog/2020/07/16/graph-markov-network.html)**. 27 | 28 | ## Usage 29 | 30 | ### Requirements 31 | 32 | * PyTorch >= 1.1.0 33 | * NumPy 34 | * Pandas 35 | 36 | The requirements.txt listed the required packages. 37 | 38 | ### Data Preparation 39 | 40 | Three datasets are used to test the GMN in the experiments 41 | 46 | The PEMS-BAY and METR-LA data is acquired from the [DCRNN Repo](https://github.com/liyaguang/DCRNN). 47 | 48 | All three ready-to-use datasets can be download from this **[LINK](https://drive.google.com/drive/folders/1Y0wBUF0vBS9KeG5VjRAJy_gySBJqmA-P?usp=sharing)**. After the datasets are downloaded, the data path should be changed accordingly to run the codes. 49 | 50 | ### Run Jupyter Scripts for Demonstration 51 | 52 | The Jupyter Scripts is used for quick demonstrations. Two files are included in the Jupyter Scripts folder: 53 | * [GraphMarkovNet.ipynb](https://github.com/zhiyongc/GraphMarkovNetwork/blob/master/Jupyter%20Scripts/GraphMarkovNet.ipynb): The GMN and its spectral version (SGMN) are implemented, trained, and tested in this Jupyter Notebook document. 54 | * [utils.py](https://github.com/zhiyongc/GraphMarkovNetwork/blob/master/Jupyter%20Scripts/utils.py): This script includes data loading, training, and testing functions. 55 | 56 | To quickly run and test GMN/SGMN, the **Jupyter Notebook version** is recommended. 57 | 58 | ### Run Python Scripts for Experiments 59 | 60 | The Python Scripts contains the implementation of the GMN and baseline models for conducting the experiments described in [1]. The Python Scripts folder contains several files: 61 | * [Exp_GMN.py](https://github.com/zhiyongc/GraphMarkovNetwork/blob/master/Python%20Scripts/Exp_GMN.py): codes for testing GMN models 62 | * [Exp_baseline.py](https://github.com/zhiyongc/GraphMarkovNetwork/blob/master/Python%20Scripts/Exp_baseline.py): codes for testing baseline models 63 | * [job.sh](https://github.com/zhiyongc/GraphMarkovNetwork/blob/master/Python%20Scripts/job.sh): bash file for testing models with different parameters 64 | * models.py: GMN/SGMN and baseline models 65 | * utils.py: utils functions 66 | * GRUD.py: GRU-D model 67 | * LSTMD.py: LSTM-D model 68 | 69 | To run and test GMN/SGMN with specific parameters, you can optionally change the Exp_GMN.py file and run 70 | 71 | ``` 72 | python Exp_GMN.py -d $dataset -m $missing_rate -o $optimizer -l $learning_rate -r $random_seed -s 0 -t $masking_type 73 | ``` 74 | 75 | ## Reference 76 | [1] Cui, Zhiyong, Longfei Lin, Ziyuan Pu, and Yinhai Wang. "[Graph markov network for traffic forecasting with missing data.](https://www.sciencedirect.com/science/article/pii/S0968090X20305866)" Transportation Research Part C: Emerging Technologies 117 (2020): 102671. \[[arXiv](https://arxiv.org/abs/1912.05457)\] 77 | 78 | ## Citation 79 | If you find this repository, e.g., the code and the datasets, useful in your research, please cite the following paper: 80 | ``` 81 | @article{cui2020graph, 82 | title={Graph markov network for traffic forecasting with missing data}, 83 | author={Cui, Zhiyong and Lin, Longfei and Pu, Ziyuan and Wang, Yinhai}, 84 | journal={Transportation Research Part C: Emerging Technologies}, 85 | volume={117}, 86 | pages={102671}, 87 | year={2020}, 88 | publisher={Elsevier} 89 | } 90 | ``` 91 | -------------------------------------------------------------------------------- /figures/GMN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyongc/Graph-Markov-Network/8e29e58c8fe7879e53605485615d09356b990a85/figures/GMN.png -------------------------------------------------------------------------------- /figures/GMP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyongc/Graph-Markov-Network/8e29e58c8fe7879e53605485615d09356b990a85/figures/GMP.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytorch=1.4.0 2 | numpy=1.16.5 3 | pandas=0.25.1 4 | matplotlib=3.2.1 5 | 6 | --------------------------------------------------------------------------------