fix log dirs and plot lstm

This commit is contained in:
wassname
2020-01-27 17:19:05 +08:00
parent f862347390
commit 3b16eba5e4
4 changed files with 328 additions and 1097 deletions
+49 -37
View File
@@ -20,8 +20,8 @@
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:29.539837Z",
"start_time": "2020-01-27T08:42:27.505676Z"
"end_time": "2020-01-27T09:16:15.104531Z",
"start_time": "2020-01-27T09:16:13.059814Z"
}
},
"outputs": [],
@@ -50,8 +50,8 @@
"execution_count": 2,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:29.600991Z",
"start_time": "2020-01-27T08:42:29.544096Z"
"end_time": "2020-01-27T09:16:15.147806Z",
"start_time": "2020-01-27T09:16:15.107965Z"
}
},
"outputs": [],
@@ -66,8 +66,8 @@
"execution_count": 3,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:29.648401Z",
"start_time": "2020-01-27T08:42:29.604654Z"
"end_time": "2020-01-27T09:16:15.205163Z",
"start_time": "2020-01-27T09:16:15.151170Z"
}
},
"outputs": [],
@@ -82,8 +82,8 @@
"execution_count": 4,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:29.779009Z",
"start_time": "2020-01-27T08:42:29.651231Z"
"end_time": "2020-01-27T09:16:15.317804Z",
"start_time": "2020-01-27T09:16:15.208363Z"
}
},
"outputs": [],
@@ -100,8 +100,8 @@
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:29.837715Z",
"start_time": "2020-01-27T08:42:29.781031Z"
"end_time": "2020-01-27T09:16:15.363114Z",
"start_time": "2020-01-27T09:16:15.320595Z"
}
},
"outputs": [],
@@ -123,8 +123,8 @@
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:38.918046Z",
"start_time": "2020-01-27T08:42:29.840722Z"
"end_time": "2020-01-27T09:16:24.239293Z",
"start_time": "2020-01-27T09:16:15.365709Z"
}
},
"outputs": [],
@@ -137,15 +137,15 @@
"execution_count": 7,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:39.642785Z",
"start_time": "2020-01-27T08:42:38.920381Z"
"end_time": "2020-01-27T09:16:24.922850Z",
"start_time": "2020-01-27T09:16:24.242339Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.legend.Legend at 0x7f64f1dd80f0>"
"<matplotlib.legend.Legend at 0x7fceb80660b8>"
]
},
"execution_count": 7,
@@ -197,8 +197,8 @@
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:39.689983Z",
"start_time": "2020-01-27T08:42:39.646151Z"
"end_time": "2020-01-27T09:16:24.969589Z",
"start_time": "2020-01-27T09:16:24.926123Z"
}
},
"outputs": [
@@ -231,8 +231,8 @@
"execution_count": 9,
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T08:42:39.776781Z",
"start_time": "2020-01-27T08:42:39.693083Z"
"end_time": "2020-01-27T09:16:25.034860Z",
"start_time": "2020-01-27T09:16:24.972165Z"
}
},
"outputs": [],
@@ -283,8 +283,9 @@
" \n",
" # PyTorch Lightning will try to restore model parameters from previous trials if checkpoint\n",
" # filenames match. Therefore, the filenames for each trial must be made unique.\n",
" name = 'anp'\n",
" checkpoint_callback = pl.callbacks.ModelCheckpoint(\n",
" os.path.join(MODEL_DIR, 'trial_{}'.format(trial.number)), monitor='val_loss', mode=\"min\")\n",
" os.path.join(MODEL_DIR, name, 'version_{}'.format(trial.number), \"chk\"), monitor='val_loss', mode=\"min\")\n",
"\n",
" # The default logger in PyTorch Lightning writes to event files to be consumed by\n",
" # TensorBoard. We create a simple logger instead that holds the log in memory so that the\n",
@@ -340,7 +341,7 @@
"execution_count": null,
"metadata": {
"ExecuteTime": {
"start_time": "2020-01-27T08:42:27.600Z"
"start_time": "2020-01-27T09:16:13.200Z"
},
"scrolled": true
},
@@ -349,7 +350,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"trial 0 params {'learning_rate': 5.775195863607715e-05, 'hidden_dim': 8, 'latent_dim': 512, 'attention_layers': 2, 'n_latent_encoder_layers': 4, 'n_det_encoder_layers': 8, 'n_decoder_layers': 8, 'dropout': 0.6651734011378423, 'attention_dropout': 0.7254712115642596, 'latent_enc_self_attn_type': 'ptmultihead', 'det_enc_self_attn_type': 'multihead', 'det_enc_cross_attn_type': 'multihead', 'use_lvar': False, 'use_deterministic_path': True, 'min_std': 0.005, 'grad_clip': 40, 'num_context': 48, 'num_extra_target': 24, 'max_nb_epochs': 20, 'num_workers': 4, 'batch_size': 16, 'num_heads': 8, 'x_dim': 16, 'y_dim': 1, 'vis_i': 670}\n"
"trial 0 params {'learning_rate': 0.008760313458256718, 'hidden_dim': 256, 'latent_dim': 64, 'attention_layers': 4, 'n_latent_encoder_layers': 2, 'n_det_encoder_layers': 16, 'n_decoder_layers': 1, 'dropout': 0.3977091380558382, 'attention_dropout': 0.7191709329577967, 'latent_enc_self_attn_type': 'dot', 'det_enc_self_attn_type': 'dot', 'det_enc_cross_attn_type': 'ptmultihead', 'use_lvar': False, 'use_deterministic_path': True, 'min_std': 0.005, 'grad_clip': 40, 'num_context': 48, 'num_extra_target': 24, 'max_nb_epochs': 20, 'num_workers': 4, 'batch_size': 16, 'num_heads': 8, 'x_dim': 16, 'y_dim': 1, 'vis_i': 670}\n"
]
},
{
@@ -361,18 +362,18 @@
"INFO:root:\n",
" Name Type Params\n",
"0 model LatentModel 2 M\n",
"1 model._latent_encoder LatentEncoder 10 K\n",
"2 model._latent_encoder._input_layer NPBlockRelu2d 160 \n",
"3 model._latent_encoder._input_layer.linear Linear 144 \n",
"1 model._latent_encoder LatentEncoder 236 K\n",
"2 model._latent_encoder._input_layer NPBlockRelu2d 5 K\n",
"3 model._latent_encoder._input_layer.linear Linear 4 K\n",
"4 model._latent_encoder._input_layer.act ReLU 0 \n",
".. ... ... ...\n",
"168 model._decoder._decoder.7.act ReLU 0 \n",
"169 model._decoder._decoder.7.dropout Dropout2d 0 \n",
"170 model._decoder._decoder.7.norm BatchNorm2d 1 K\n",
"171 model._decoder._mean Linear 529 \n",
"172 model._decoder._std Linear 529 \n",
"179 model._decoder._decoder.0.act ReLU 0 \n",
"180 model._decoder._decoder.0.dropout Dropout2d 0 \n",
"181 model._decoder._decoder.0.norm BatchNorm2d 1 K\n",
"182 model._decoder._mean Linear 577 \n",
"183 model._decoder._std Linear 577 \n",
"\n",
"[173 rows x 3 columns]\n"
"[184 rows x 3 columns]\n"
]
},
{
@@ -393,14 +394,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"step 0, {'val_loss': '0.8915367126464844', 'val/kl': '1.9502738268784015e-06', 'val/std': '0.6818246841430664', 'val/mse': '0.33060529828071594'}\n",
"step 0, {'val_loss': '0.6977585554122925', 'val/kl': '8.717864147911314e-06', 'val/std': '0.6536452174186707', 'val/mse': '0.17428502440452576'}\n",
"\r"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "510857fe419e429d90eb5eca171118d3",
"model_id": "e56a48896f3a45a3b3644f280a076c53",
"version_major": 2,
"version_minor": 0
},
@@ -441,16 +442,27 @@
]
},
{
"cell_type": "code",
"execution_count": null,
"cell_type": "markdown",
"metadata": {
"ExecuteTime": {
"end_time": "2020-01-27T07:35:33.470251Z",
"start_time": "2020-01-27T07:34:47.800Z"
}
},
"outputs": [],
"source": []
"source": [
"# View\n",
"\n",
"TODO"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test\n",
"\n",
"TODO test best"
]
},
{
"cell_type": "code",
File diff suppressed because it is too large Load Diff
+1
View File
@@ -9,6 +9,7 @@ from src.plot import plot_from_loader_to_tensor
from src.utils import ObjectDict
class LatentModelPL(pl.LightningModule):
def __init__(self, hparams):
super().__init__()
+153 -82
View File
@@ -10,11 +10,17 @@ import torchvision.transforms as transforms
from argparse import ArgumentParser
import json
import pytorch_lightning as pl
from matplotlib import pyplot as plt
import torch
import io
import PIL
from torchvision.transforms import ToTensor
from src.data.smart_meter import get_smartmeter_df
from src.utils import ObjectDict
class SequenceDfDataSet(torch.utils.data.Dataset):
def __init__(self, df, hparams, label_names=None, train=True, transforms=None):
super().__init__()
@@ -22,102 +28,120 @@ class SequenceDfDataSet(torch.utils.data.Dataset):
self.hparams = hparams
self.label_names = label_names
self.train = train
self.transforms=transforms
self.transforms = transforms
def __len__(self):
return len(self.data) - +self.hparams.window_length - self.hparams.target_length
def iloc(self, idx):
k = idx+self.hparams.window_length+self.hparams.target_length
j = k-self.hparams.target_length
i = j-self.hparams.window_length
assert i>=0
assert idx<=len(self.data)
k = idx + self.hparams.window_length + self.hparams.target_length
j = k - self.hparams.target_length
i = j - self.hparams.window_length
assert i >= 0
assert idx <= len(self.data)
x_rows = self.data.iloc[i:j].copy()
y_rows = self.data.iloc[k].to_frame().T.copy()
# print(i,j,k)
# print(i,j,k)
# add seconds since start of window index
x_rows['tstp'] = (x_rows['tstp'] - x_rows['tstp'].iloc[0]).dt.total_seconds() / 86400.0
x_rows["tstp"] = (
x_rows["tstp"] - x_rows["tstp"].iloc[0]
).dt.total_seconds() / 86400.0
# TODO we could augment by removing and backfilling some
return x_rows, y_rows
def __getitem__(self, idx):
x_rows, y_rows = self.iloc(idx)
# if self.train:
# # zero and backfill some for augmentation
# drop_inds = np.random.randint(1, len(x_rows)-1, size=int(len(x_rows)*0.3))
# x_rows.iloc[drop_inds] = np.nan
# x_rows = x_rows.bfill()
# print(x_rows, y_rows)
y = y_rows[self.label_names].astype(np.float32).values
x = x_rows.astype(np.float32).values
# print(x, y)
return self.transforms(x).squeeze(0).float(), self.transforms(y[:, None,])[:, 0, 0].float()
return (
self.transforms(x).squeeze(0).float(),
self.transforms(y[:, None,])[:, 0, 0].float(),
)
class LSTM_PL(pl.LightningModule):
class LSTMNet(nn.Module):
def __init__(self, hparams):
super().__init__()
self.hparams = ObjectDict()
self.hparams.update(hparams.__dict__ if hasattr(hparams, '__dict__') else hparams)
self.hparams = hparams
self.lstm1 = nn.LSTM(
input_size=self.hparams.input_size,
hidden_size=self.hparams.hidden_size,
input_size=self.hparams.input_size,
hidden_size=self.hparams.hidden_size,
batch_first=True,
num_layers=self.hparams.lstm_layers,
bidirectional=self.hparams.bidirectional,
dropout=self.hparams.lstm_dropout,
)
self.hidden_out_size = self.hparams.hidden_size * self.hparams.lstm_layers * (self.hparams.bidirectional + 1)
self.hidden_out_size = (
self.hparams.hidden_size
* self.hparams.lstm_layers
* (self.hparams.bidirectional + 1)
)
self.linear = nn.Linear(self.hidden_out_size, 1)
self._dfs = None
def forward(self, x):
outputs, (h_out, _) = self.lstm1(x)
h_out = h_out.permute((1, 0, 2)).reshape((-1, self.hidden_out_size))
return self.linear(h_out)
class LSTM_PL(pl.LightningModule):
def __init__(self, hparams):
super().__init__()
self.hparams = ObjectDict()
self.hparams.update(
hparams.__dict__ if hasattr(hparams, "__dict__") else hparams
)
self._model = LSTMNet(self.hparams)
self._dfs = None
def forward(self, x):
return self._model(x)
def training_step(self, batch, batch_idx):
# REQUIRED
x, y = batch
y_hat = self.forward(x)
loss = F.mse_loss(y_hat, y)
tensorboard_logs = {'train_loss': loss}
return {'loss': loss, 'log': tensorboard_logs}
tensorboard_logs = {"train_loss": loss}
return {"loss": loss, "log": tensorboard_logs}
def validation_step(self, batch, batch_idx):
# OPTIONAL
x, y = batch
y_hat = self.forward(x)
loss = F.mse_loss(y_hat, y)
tensorboard_logs = {'val_loss': loss}
return {'val_loss': loss, 'log': tensorboard_logs}
tensorboard_logs = {"val_loss": loss}
return {"val_loss": loss, "log": tensorboard_logs}
def validation_end(self, outputs):
# TODO send an image to tensroboard, like in the lighting_anp.py file
# if self.hparams["vis_i"] > 0:
# self.logger.experiment.add_image('val/image', image,
if self.hparams["vis_i"] > 0:
loader = self.val_dataloader()[0]
vis_i = min(self.hparams["vis_i"], len(loader.dataset))
image = plot_from_loader_to_tensor(loader, self, vis_i=vis_i)
self.logger.experiment.add_image(
"val/image", image, self.trainer.global_step
)
# OPTIONAL
avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()
avg_loss = torch.stack([x["val_loss"] for x in outputs]).mean()
keys = outputs[0]["log"].keys()
tensorboard_logs = {k: torch.stack([x["log"][k] for x in outputs if k in x["log"]]).mean() for k in keys}
tensorboard_logs_str = {k: f'{v}' for k, v in tensorboard_logs.items()}
tensorboard_logs = {
k: torch.stack([x["log"][k] for x in outputs if k in x["log"]]).mean()
for k in keys
}
tensorboard_logs_str = {k: f"{v}" for k, v in tensorboard_logs.items()}
print(f"step {self.trainer.global_step}, {tensorboard_logs_str}")
return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}
return {"avg_val_loss": avg_loss, "log": tensorboard_logs}
def configure_optimizers(self):
# REQUIRED
# can return multiple optimizers and learning_rate schedulers
return torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate)
optim = torch.optim.Adam(self.parameters(), lr=self.hparams["learning_rate"])
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
optim, patience=2, verbose=True, min_lr=1e-5
) # note early stopping has patient 3
return [optim], [scheduler]
def _get_cache_dfs(self):
if self._dfs is None:
@@ -128,25 +152,43 @@ class LSTM_PL(pl.LightningModule):
@pl.data_loader
def train_dataloader(self):
# REQUIRED
df_train = self._get_cache_dfs()['df_train']
dset_train = SequenceDfDataSet(df_train, self.hparams, label_names=['energy(kWh/hh)'], transforms=transforms.ToTensor(), train=True)
return DataLoader(dset_train, batch_size=self.hparams.batch_size,
shuffle=True,
num_workers=self.hparams.num_workers)
df_train = self._get_cache_dfs()["df_train"]
dset_train = SequenceDfDataSet(
df_train,
self.hparams,
label_names=["energy(kWh/hh)"],
transforms=transforms.ToTensor(),
train=True,
)
return DataLoader(
dset_train,
batch_size=self.hparams.batch_size,
shuffle=True,
num_workers=self.hparams.num_workers,
)
@pl.data_loader
def val_dataloader(self):
# OPTIONAL
df_test = self._get_cache_dfs()['df_test']
dset_test = SequenceDfDataSet(df_test, self.hparams, label_names=['energy(kWh/hh)'], train=False, transforms=transforms.ToTensor())
df_test = self._get_cache_dfs()["df_test"]
dset_test = SequenceDfDataSet(
df_test,
self.hparams,
label_names=["energy(kWh/hh)"],
train=False,
transforms=transforms.ToTensor(),
)
return DataLoader(dset_test, batch_size=self.hparams.batch_size, shuffle=False)
@pl.data_loader
def test_dataloader(self):
# OPTIONAL
df_test = self._get_cache_dfs()['df_test']
dset_test = SequenceDfDataSet(df_test, self.hparams, label_names=['energy(kWh/hh)'], train=False, transforms=transforms.ToTensor())
df_test = self._get_cache_dfs()["df_test"]
dset_test = SequenceDfDataSet(
df_test,
self.hparams,
label_names=["energy(kWh/hh)"],
train=False,
transforms=transforms.ToTensor(),
)
return DataLoader(dset_test, batch_size=self.hparams.batch_size, shuffle=False)
@staticmethod
@@ -156,33 +198,62 @@ class LSTM_PL(pl.LightningModule):
"""
# MODEL specific
parser = HyperOptArgumentParser(parents=[parent_parser])
parser.add_argument('--learning_rate', default=0.02, type=float)
parser.add_argument('--batch_size', default=32, type=int)
parser.add_argument('--lstm_dropout', default=0, type=float)
parser.add_argument('--hidden_size', default=32, type=int)
parser.add_argument('--input_size', default=8, type=int)
parser.add_argument('--lstm_layers', default=4, type=int)
parser.add_argument('--bidirectional', default=False, type=bool)
parser.add_argument("--learning_rate", default=0.02, type=float)
parser.add_argument("--batch_size", default=32, type=int)
parser.add_argument("--lstm_dropout", default=0, type=float)
parser.add_argument("--hidden_size", default=32, type=int)
parser.add_argument("--input_size", default=8, type=int)
parser.add_argument("--lstm_layers", default=4, type=int)
parser.add_argument("--bidirectional", default=False, type=bool)
# training specific (for this model)
parser.add_argument('--window_length', type=int, default=12)
parser.add_argument('--target_length', type=int, default=2)
parser.add_argument('--max_nb_epochs', default=10, type=int)
parser.add_argument('--num_workers', default=4, type=int)
parser.add_argument("--window_length", type=int, default=12)
parser.add_argument("--target_length", type=int, default=2)
parser.add_argument("--max_nb_epochs", default=10, type=int)
parser.add_argument("--num_workers", default=4, type=int)
return parser
# dset_train = SequenceDfDataSet(df_train, hparams, transforms=transforms.ToTensor())
# dset_test = SequenceDfDataSet(df_test, hparams, train=False, transforms=transforms.ToTensor())
# dset_val = SequenceDfDataSet(df_test, hparams, train=False, transforms=transforms.ToTensor())
def plot_from_loader(loader, model, vis_i=670):
dset_test = loader.dataset
label_names = dset_test.label_names
y_trues = []
y_preds = []
vis_i = min(vis_i, len(dset_test))
for i in range(vis_i, vis_i + 50):
x_rows, y_rows = dset_test.iloc(i)
x, y = dset_test[i]
device = next(model.parameters()).device
x = x[None, :].to(device)
model.eval()
with torch.no_grad():
y_hat = model.forward(x)
y_hat = y_hat.cpu().numpy()
# model = LSTM_PL(hparams)
dt = y_rows.iloc[0].name
# # most basic trainer, uses good defaults
# trainer = Trainer(
# max_nb_epochs=hparams.max_nb_epochs,
# gpus=hparams.gpus,
# nb_gpu_nodes=hparams.nodes,
# )
# trainer.fit(model)
y_hat_rows = y_rows.copy()
y_hat_rows[label_names[0]] = y_hat
y_trues.append(y_rows)
y_preds.append(y_hat_rows)
plt.figure()
pd.concat(y_trues)[label_names[0]].plot(label="y_true")
pd.concat(y_preds)[label_names[0]].plot(label="y_pred")
plt.legend()
t_ahead = pd.Timedelta("30T") * model.hparams.target_length
plt.title(f"predicting {t_ahead} ahead")
# plt.show()
def plot_from_loader_to_tensor(*args, **kwargs):
plot_from_loader(*args, **kwargs)
# Send fig to tensorboard
buf = io.BytesIO()
plt.savefig(buf, format="jpeg")
buf.seek(0)
image = PIL.Image.open(buf)
image = ToTensor()(image) # .unsqueeze(0)
return image