TST

Last updated: January 16th, 20202020-01-16Project preview

LSTM Demo on Oze Dataset

In [1]:
!pip3 install -q --upgrade pip tqdm torch==1.3.1+cpu torchvision==0.4.2+cpu -f https://download.pytorch.org/whl/torch_stable.html
In [2]:
import numpy as np
from matplotlib import pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, random_split
from tqdm import tqdm
import seaborn as sns

from src.dataset import OzeDataset
from src.Benchmark import LSTM
from src.utils import compute_loss, visual_sample
In [3]:
# Training parameters
DATASET_PATH = 'dataset_sample.npz'
BATCH_SIZE = 4
NUM_WORKERS = 4
LR = 2e-4
EPOCHS = 10

# Model parameters
K = 672 # Time window length
d_model = 64 # Lattent dim
n_layer = 4 # Number of LSTM layers

d_input = 37 # From dataset
d_output = 8 # From dataset

# Config
sns.set()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"Using device {device}")
Using device cpu

Training

Load dataset

In [4]:
!wget deepnet.fr/challenge_oze/sample_dataset/{DATASET_PATH}
--2020-01-11 09:53:22--  http://deepnet.fr/challenge_oze/sample_dataset/dataset_sample.npz
Resolving deepnet.fr (deepnet.fr)... 217.160.0.45, 2001:8d8:100f:f000::2bc
Connecting to deepnet.fr (deepnet.fr)|217.160.0.45|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://deepnet.fr/challenge_oze/sample_dataset/dataset_sample.npz [following]
--2020-01-11 09:53:23--  https://deepnet.fr/challenge_oze/sample_dataset/dataset_sample.npz
Connecting to deepnet.fr (deepnet.fr)|217.160.0.45|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 34982724 (33M)
Saving to: ‘dataset_sample.npz’

dataset_sample.npz  100%[===================>]  33.36M  7.03MB/s    in 5.1s    

2020-01-11 09:53:28 (6.51 MB/s) - ‘dataset_sample.npz’ saved [34982724/34982724]

In [5]:
ozeDataset = OzeDataset(DATASET_PATH)

dataset_train, dataset_val, dataset_test = random_split(ozeDataset, (250, 125, 125))

dataloader_train = DataLoader(dataset_train,
                              batch_size=BATCH_SIZE,
                              shuffle=True,
                              num_workers=NUM_WORKERS,
                              pin_memory=False
                             )

dataloader_val = DataLoader(dataset_val,
                            batch_size=BATCH_SIZE,
                            shuffle=True,
                            num_workers=NUM_WORKERS
                           )

dataloader_test = DataLoader(dataset_test,
                             batch_size=BATCH_SIZE,
                             shuffle=False,
                             num_workers=NUM_WORKERS
                            )

Load network

In [6]:
# Load transformer with Adam optimizer and MSE loss function
net = LSTM(d_input, d_model, d_output, n_layer).to(device)
optimizer = optim.Adam(net.parameters(), lr=LR)
loss_function = nn.MSELoss()

Train

In [7]:
# Prepare loss history
hist_loss = np.zeros(EPOCHS)
hist_loss_val = np.zeros(EPOCHS)
for idx_epoch in range(EPOCHS):
    running_loss = 0
    with tqdm(total=len(dataloader_train.dataset), desc=f"[Epoch {idx_epoch+1:3d}/{EPOCHS}]") as pbar:
        for idx_batch, (x, y) in enumerate(dataloader_train):
            optimizer.zero_grad()

            # Propagate input
            netout = net(x.to(device))

            # Comupte loss
            loss = loss_function(y.to(device), netout)

            # Backpropage loss
            loss.backward()

            # Update weights
            optimizer.step()

            running_loss += loss.item()
            pbar.set_postfix({'loss': running_loss/(idx_batch+1)})
            pbar.update(x.shape[0])
        
        train_loss = running_loss/len(dataloader_train)
        val_loss = compute_loss(net, dataloader_val, loss_function, device).item()
        pbar.set_postfix({'loss': train_loss, 'val_loss': val_loss})
        
        hist_loss[idx_epoch] = train_loss
        hist_loss_val[idx_epoch] = val_loss
        
plt.plot(hist_loss, 'o-', label='train')
plt.plot(hist_loss_val, 'o-', label='val')
_ = plt.legend()
[Epoch   1/10]: 100%|██████████| 250/250 [00:12<00:00, 20.51it/s, loss=0.156, val_loss=0.113]
[Epoch   2/10]: 100%|██████████| 250/250 [00:11<00:00, 20.94it/s, loss=0.0915, val_loss=0.0837]
[Epoch   3/10]: 100%|██████████| 250/250 [00:11<00:00, 21.08it/s, loss=0.0665, val_loss=0.0527]
[Epoch   4/10]: 100%|██████████| 250/250 [00:11<00:00, 21.01it/s, loss=0.0451, val_loss=0.0396]
[Epoch   5/10]: 100%|██████████| 250/250 [00:12<00:00, 20.54it/s, loss=0.0329, val_loss=0.0288]
[Epoch   6/10]: 100%|██████████| 250/250 [00:12<00:00, 20.12it/s, loss=0.0248, val_loss=0.0234]
[Epoch   7/10]: 100%|██████████| 250/250 [00:12<00:00, 19.38it/s, loss=0.0213, val_loss=0.0214]
[Epoch   8/10]: 100%|██████████| 250/250 [00:19<00:00, 12.98it/s, loss=0.0202, val_loss=0.0204]
[Epoch   9/10]: 100%|██████████| 250/250 [00:19<00:00, 13.10it/s, loss=0.0193, val_loss=0.0199]
[Epoch  10/10]: 100%|██████████| 250/250 [00:18<00:00, 13.74it/s, loss=0.0188, val_loss=0.019]

Validation

In [8]:
_ = net.eval()

Plot results sample

In [9]:
visual_sample(dataloader_test, net, device)
plt.savefig("fig")

Evaluate on the test dataset

In [10]:
predictions = np.empty(shape=(len(dataloader_test.dataset), 672, 8))

idx_prediction = 0
with torch.no_grad():
    for x, y in tqdm(dataloader_test, total=len(dataloader_test)):
        netout = net(x.to(device)).cpu().numpy()
        predictions[idx_prediction:idx_prediction+x.shape[0]] = netout
        idx_prediction += x.shape[0]
100%|██████████| 32/32 [00:02<00:00, 15.31it/s]
In [11]:
fig, axes = plt.subplots(8, 1)
fig.set_figwidth(20)
fig.set_figheight(40)
plt.subplots_adjust(bottom=0.05)

occupancy = (dataloader_test.dataset.dataset._x.numpy()[..., dataloader_test.dataset.dataset.labels["Z"].index("occupancy")].mean(axis=0)>0.5).astype(float)
y_true_full = dataloader_test.dataset.dataset._y[dataloader_test.dataset.indices].numpy()
                                                 
for idx_label, (label, ax) in enumerate(zip(dataloader_test.dataset.dataset.labels['X'], axes)):
    # Select output to plot
    y_true = y_true_full[..., idx_label]
    y_pred = predictions[..., idx_label]
    
    # Rescale
    y_true = dataloader_test.dataset.dataset.rescale(y_true, idx_label)
    y_pred = dataloader_test.dataset.dataset.rescale(y_pred, idx_label)
    
    # Compute delta, mean and std
    delta = np.abs(y_true - y_pred)
    
    mean = delta.mean(axis=0)
    std = delta.std(axis=0)
    
    # Plot
    # Labels for consumption and temperature
    if label.startswith('Q_'):
        y_label_unit = 'kW'
    else:
        y_label_unit = '°C'
    
    # Occupancy
    occupancy_idxes = np.where(np.diff(occupancy) != 0)[0]
    for idx in range(0, len(occupancy_idxes), 2):
        ax.axvspan(occupancy_idxes[idx], occupancy_idxes[idx+1], facecolor='green', alpha=.15)
    
    # Std
    ax.fill_between(np.arange(mean.shape[0]), (mean - std), (mean + std), alpha=.4, label='std')
    
    # Mean
    ax.plot(mean, label='mean')
    
    # Title and labels
    ax.set_title(label)
    ax.set_xlabel('time', fontsize=16)
    ax.set_ylabel(y_label_unit, fontsize=16)
    ax.legend()
    
plt.savefig('error_mean_std')